AMOS BASIC tokenized file
(→AMOS source code) |
|||
Line 43: | Line 43: | ||
|- | |- | ||
|colspan="2"| Header identifying which version of AMOS saved the file | |colspan="2"| Header identifying which version of AMOS saved the file | ||
− | *"<samp>AMOS Pro111v | + | *"<samp>AMOS Pro111V</samp>" and 4 more bytes (AMOS Professional, source tested) |
− | *"<samp>AMOS Pro101V | + | *"<samp>AMOS Pro111v</samp>" and 4 more bytes (AMOS Professional, source not tested) |
− | *"<samp>AMOS Pro101v | + | *"<samp>AMOS Pro101V</samp>" and 4 more bytes (AMOS Professional, source tested) |
+ | *"<samp>AMOS Pro101v</samp>" and 4 more bytes (AMOS Professional, source not tested) | ||
*"<samp>AMOS Basic V134 </samp>" (AMOS Pro compatible with AMOS 1.3, source tested) | *"<samp>AMOS Basic V134 </samp>" (AMOS Pro compatible with AMOS 1.3, source tested) | ||
*"<samp>AMOS Basic v134 </samp>" (AMOS Pro compatible with AMOS 1.3, source not tested) | *"<samp>AMOS Basic v134 </samp>" (AMOS Pro compatible with AMOS 1.3, source not tested) | ||
*"<samp>AMOS Basic V1.3 </samp>" (AMOS The Creator v1.3, source tested) | *"<samp>AMOS Basic V1.3 </samp>" (AMOS The Creator v1.3, source tested) | ||
*"<samp>AMOS Basic v1.3 </samp>" (AMOS The Creator v1.3, source not tested) | *"<samp>AMOS Basic v1.3 </samp>" (AMOS The Creator v1.3, source not tested) | ||
− | *"<samp>AMOS Basic V1.23</samp>" | + | *"<samp>AMOS Basic V1.23</samp>" (AMOS The Creator v1.2, source tested) |
− | *"<samp>AMOS Basic V1.00</samp>" (AMOS The Creator v1.0 – v1. | + | *"<samp>AMOS Basic v1.23</samp>" (AMOS The Creator v1.2, source not tested) |
− | *"<samp>AMOS Basic v1.00</samp>" (AMOS The Creator v1.0 – v1. | + | *"<samp>AMOS Basic V1.00</samp>" (AMOS The Creator v1.0 – v1.1, source tested) |
+ | *"<samp>AMOS Basic v1.00</samp>" (AMOS The Creator v1.0 – v1.1, source not tested) | ||
|16 bytes | |16 bytes | ||
|- | |- | ||
Line 93: | Line 95: | ||
Each token starts with a signed 16-bit number. Token values between 0x0000 and 0x004E have special printing and size rules, all other tokens are a signed offset into AMOS's internal token table. The instruction name in the internal token table is what should be printed. | Each token starts with a signed 16-bit number. Token values between 0x0000 and 0x004E have special printing and size rules, all other tokens are a signed offset into AMOS's internal token table. The instruction name in the internal token table is what should be printed. | ||
− | + | === Specially printed tokens === | |
{|class="wikitable" | {|class="wikitable" | ||
!Token!!Type!!Interpretation | !Token!!Type!!Interpretation | ||
Line 99: | Line 101: | ||
|0x0000 | |0x0000 | ||
|null token | |null token | ||
− | |Marks the end of line. Always 2 bytes long | + | |Marks the end of line. Always 2 bytes long. |
|- | |- | ||
|0x0006 | |0x0006 | ||
Line 110: | Line 112: | ||
**bit 1 set: this is a floating point reference, e.g. <code>XYZ#</code> | **bit 1 set: this is a floating point reference, e.g. <code>XYZ#</code> | ||
**bit 2 set: this is a string reference, e.g. <code>XYZ$</code> | **bit 2 set: this is a string reference, e.g. <code>XYZ$</code> | ||
− | * variable length: ISO-8859-1 string, with the above given length. The string is null terminated and its length is rounded up to a multiple of two | + | * variable length: ISO-8859-1 string, with the above given length. The string is null terminated and its length is rounded up to a multiple of two bytes. |
|- | |- | ||
|0x000C | |0x000C | ||
Line 126: | Line 128: | ||
* 2 bytes: token | * 2 bytes: token | ||
* 2 bytes: length of ISO-8859-1 string to follow | * 2 bytes: length of ISO-8859-1 string to follow | ||
− | * variable length: ISO-8859-1 string, with the above given length | + | * variable length: ISO-8859-1 string, with the above given length, rounded up to a multiple of two. The string should be null-terminated, but sometimes isn't. |
|- | |- | ||
|0x002E | |0x002E | ||
Line 147: | Line 149: | ||
| | | | ||
* 2 bytes: token | * 2 bytes: token | ||
− | * 4 bytes: | + | * 4 bytes: a single-precision floating point value |
− | **bits | + | ** bits 31-8: mantissa (24 bits), most significant bit is always set |
− | **bit 7: sign bit | + | ** bit 7: ignore (should be sign bit, but all AMOS floats are positive) |
− | **bits | + | ** bits 6-0: exponent (from 0x00 to 0x7F) |
− | An exponent of 0 means 0.0, regardless of mantissa. | + | An exponent of 0 means 0.0, regardless of mantissa. |
+ | |||
+ | Each set bit in the mantissa has the value 2<sup>''m''+''e''-88</sup> where ''m'' is from 23 (MSB) to 0 (LSB) and ''e'' is the exponent | ||
+ | |- | ||
+ | |0x2B6A | ||
+ | |Double-precision float, e.g. <code>'''3.1415926543'''</code> | ||
+ | | | ||
+ | * 2 bytes: token | ||
+ | * 8 bytes: a double-precision floating point value | ||
+ | ** bit 63: ignore (should be sign bit, but all AMOS floats are positive) | ||
+ | ** bits 62-51: exponent (from 0x000 to 0x7FF) | ||
+ | ** bits 50-0: mantissa (52 bits), most significant bit is not stored but implicitly set | ||
+ | |||
+ | An exponent of 0 means 0.0, regardless of mantissa. | ||
+ | |||
+ | Each set bit in the mantissa has the value 2<sup>''m''+''e''-1074</sup> where ''m'' is from 51 (implicit MSB) to 0 (LSB) and ''e'' is the exponent | ||
|- | |- | ||
|0x004E | |0x004E | ||
Line 163: | Line 180: | ||
|} | |} | ||
− | + | === Specially sized tokens === | |
{|class="wikitable" | {|class="wikitable" | ||
!Token!!Type!!Interpretation | !Token!!Type!!Interpretation | ||
Line 173: | Line 190: | ||
* 1 byte: unused | * 1 byte: unused | ||
* 1 byte: length of ISO-8859-1 string to follow | * 1 byte: length of ISO-8859-1 string to follow | ||
− | * variable length: ISO-8859-1 string, with the above-given length. The string is null terminated and its length is rounded up to a multiple of two. The string should be printed after the remark token | + | * variable length: ISO-8859-1 string, with the above-given length. The string is null terminated and its length is rounded up to a multiple of two. The string should be printed after the remark token |
|- | |- | ||
|0x0652 | |0x0652 | ||
Line 180: | Line 197: | ||
|0x023C | |0x023C | ||
|<code>For</code> | |<code>For</code> | ||
− | | rowspan=" | + | | rowspan="8" | |
* 2 bytes: token | * 2 bytes: token | ||
* 2 bytes: unknown purpose | * 2 bytes: unknown purpose | ||
Line 195: | Line 212: | ||
|- | |- | ||
|0x0404||<code>Data</code> | |0x0404||<code>Data</code> | ||
+ | |- | ||
+ | |0x25A4||<code>Else If</code> | ||
|- | |- | ||
|0x0290 | |0x0290 | ||
Line 218: | Line 237: | ||
** bit 4: if set, procedure contains compiled code and not tokens | ** bit 4: if set, procedure contains compiled code and not tokens | ||
* 1 byte: part of seed for encryption | * 1 byte: part of seed for encryption | ||
+ | |- | ||
+ | |0x2A40||<code>Equ</code> | ||
+ | | rowspan="4"| | ||
+ | * 2 bytes: token | ||
+ | * 4 bytes: stored equate value | ||
+ | * 1 byte: equate type (0-7) | ||
+ | * 1 byte: unknown purpose | ||
+ | |- | ||
+ | |0x2A40||<code>Lvo</code> | ||
+ | |- | ||
+ | |0x2A54||<code>Struc</code> | ||
+ | |- | ||
+ | |0x2A64||<code>Struct</code> | ||
|} | |} | ||
Line 223: | Line 255: | ||
If you should find a procedure (0x0376) token with the "is encrypted" bit set, run this C function on the code and it will decrypt the contents of the procedure. | If you should find a procedure (0x0376) token with the "is encrypted" bit set, run this C function on the code and it will decrypt the contents of the procedure. | ||
− | void | + | /* read 16-bit big-endian word from unsigned char[] */ |
+ | #define amos_deek(a) ((((a)[0])<<8)|((a)[1])) | ||
+ | /* read 32-bit big-endian word from unsigned char[] */ | ||
+ | #define amos_leek(a) ((((a)[0])<<24)|(((a)[1])<<16)|(((a)[2])<<8)|((a)[3])) | ||
+ | |||
+ | void AMOS_decrypt_procedure(unsigned char *src) { | ||
unsigned char *line, *next, *endline; | unsigned char *line, *next, *endline; | ||
unsigned int key, key2, key3, size; | unsigned int key, key2, key3, size; | ||
− | /* | + | /* src should be a pointer to a line with the PROCEDURE token on it */ |
− | if (src[2] != | + | if (amos_deek(&src[2]) != 0x0376) return; |
/* do not operate on compiled procedures */ | /* do not operate on compiled procedures */ | ||
if (src[10] & 0x10) return; | if (src[10] & 0x10) return; | ||
− | + | size = amos_leek(&src[4]); | |
− | size = (src[4] | + | line = next = &src[src[0] * 2]; /* the line after PROCEDURE */ |
− | endline = &src[size + 8 + 6]; | + | endline = &src[size + 8 + 6]; /* the start of the line after END PROC */ |
− | + | ||
− | /* initialise | + | /* initialise keys */ |
key = (size << 8) | src[11]; | key = (size << 8) | src[11]; | ||
key2 = 1; | key2 = 1; | ||
− | key3 = (src[8] | + | key3 = amos_deek(&src[8]); |
while (line < endline) { | while (line < endline) { | ||
− | line = next; | + | line = next; next = &line[line[0] * 2]; |
− | + | ||
− | + | ||
− | + | ||
for (line += 4; line < next;) { | for (line += 4; line < next;) { | ||
*line++ ^= (key >> 8) & 0xFF; | *line++ ^= (key >> 8) & 0xFF; | ||
*line++ ^= key & 0xFF; | *line++ ^= key & 0xFF; | ||
− | key | + | key += key2; |
key2 += key3; | key2 += key3; | ||
− | key = (key >> 1) | (key << 31); | + | key = (key >> 1) | (key << 31); /* rotate right one bit */ |
} | } | ||
} | } |
Revision as of 02:28, 18 January 2019
AMOS BASIC is a family of BASIC dialects for the Amiga computer. They were written by François Lionet, who also wrote AMOS's predecessor, STOS BASIC for the Atari ST (see STOS memory bank).
There are several versions of AMOS published: AMOS The Creator, Easy AMOS and AMOS Professional.
AMOS has its own integrated development environment, and it uses its own custom file formats for everything, from source code to graphics and sound.
Contents |
Overview
AMOS is an interpreted BASIC dialect where code is edited and run in an integrated development environment. Every time the programmer finishes editing a line of code, it is immediately parsed into tokens. For example, typing procedure foobar
and pressing the return key will change the line into Procedure FOOBAR
.
Before a program can run, it will be tested to ensure it is free of syntax errors. Source code can be saved even if it is untested or fails testing, but AMOS includes a tested flag in the saved file. This is used by external software, for example the AMOS Compiler will refuse to compile an untested source code file.
Extensions
AMOS tokens are split between instructions in the core language and instructions in extensions.
Extensions are external files, written in 68000 assembler, which begin with a token table listing all the instructions they add to the language.
Each extension is intended to be loaded into a specific slot. AMOS has 25 slots for extensions. The configuration of extensions and their slots are saved in AMOS's global config file.
To load other people's source code, AMOS needs to be configured with the same versions of the same extensions that they used, in the same slots.
Banks
In order to work with multimedia such as pictures and music, AMOS has the concept of a bank or memory bank (see AMOS Memory Bank#Disambiguation for a note about terminology). An AMOS program can have up to 15 banks. For example, you can load several pieces of music into different banks, and then identify which one you want to play by a number: Track Play 5
will play music in bank 5. Or you could load a packed picture into bank 4 and say Unpack 4 to 0
, which will unpack the picture onto screen 0.
While you can load anything into any bank, some instructions can only take their data from specific bank numbers. Bank 1 is used for Sprites, which are controlled with instructions beginning Sprite
or Bob
. Bank 2 is for Icons, which are controlled with instructions beginning Icon
. Bank 3 is used for music in AMOS's native music format.
If banks are in use while saving source code, the contents of the banks are included in the saved source code. This makes it easy to bundle code with the data it works on. The exception to this rule is banks created using the Reserve As Work
instruction.
AMOS source code
All multi-byte integer values are in big-endian (Motorola) format.
AMOS source code is stored in a file with the extension .AMOS. It has the following structure:
Section | Length | |
---|---|---|
Header identifying which version of AMOS saved the file
|
16 bytes | |
Length in bytes of tokenized BASIC code to follow | 4 bytes | |
Tokenized BASIC code | varies | |
AMOS AmBs segment | ASCII identifier "AmBs" | 4 bytes |
Count of AMOS banks to follow (0–16) | 2 bytes | |
AMOS banks. Each bank's length must be individually determined. | varies |
Tokenised BASIC code
Tokenised BASIC code is a sequence of tokenised lines. Each tokenised line has the following format:
Field | Length |
---|---|
Length of this line in words (2 bytes), including this byte. To get the length of the line in bytes, double this value | 1 byte |
Indent level of this line. Prefix indent level + 1 spaces at the beginning of the line, or no spaces if the value is less than 2 | 1 byte |
Sequence of tokens. Each token is at least two bytes, and all tokens are rounded to to a multiple of two bytes. Each token is individually sized. The tokens always end with a compulsory null token | varies |
Some tokens have special size rules, but most are exactly 2 bytes in size.
Each token starts with a signed 16-bit number. Token values between 0x0000 and 0x004E have special printing and size rules, all other tokens are a signed offset into AMOS's internal token table. The instruction name in the internal token table is what should be printed.
Specially printed tokens
Token | Type | Interpretation |
---|---|---|
0x0000 | null token | Marks the end of line. Always 2 bytes long. |
0x0006 | Variable reference, e.g. Print XYZ
|
|
0x000C | Label, e.g. XYZ: or 190 at the start of a line
| |
0x0012 | Procedure call reference, e.g. XYZ["hello"]
| |
0x0018 | Label reference, e.g. Goto XYZ
| |
0x0026 | String with double quotes, e.g. "XYZ"
|
|
0x002E | String with single quotes, e.g. 'XYZ'
| |
0x001E | Binary integer value, e.g. %100101
|
|
0x0036 | Hexidecimal integer value, e.g. $80FAA010
| |
0x003E | Decimal integer value, e.g. 1234567890
| |
0x0046 | Floating point value, e.g. 3.1452
|
An exponent of 0 means 0.0, regardless of mantissa. Each set bit in the mantissa has the value 2m+e-88 where m is from 23 (MSB) to 0 (LSB) and e is the exponent |
0x2B6A | Double-precision float, e.g. 3.1415926543
|
An exponent of 0 means 0.0, regardless of mantissa. Each set bit in the mantissa has the value 2m+e-1074 where m is from 51 (implicit MSB) to 0 (LSB) and e is the exponent |
0x004E | Extension instruction |
|
Specially sized tokens
Token | Type | Interpretation |
---|---|---|
0x064A | Rem
|
|
0x0652 | '
| |
0x023C | For
|
|
0x0250 | Repeat
| |
0x0268 | While
| |
0x027E | Do
| |
0x02BE | If
| |
0x02D0 | Else
| |
0x0404 | Data
| |
0x25A4 | Else If
| |
0x0290 | Exit If
|
|
0x029E | Exit
| |
0x0316 | On
| |
0x0376 | Procedure |
|
0x2A40 | Equ
|
|
0x2A40 | Lvo
| |
0x2A54 | Struc
| |
0x2A64 | Struct
|
Encrypted procedures
If you should find a procedure (0x0376) token with the "is encrypted" bit set, run this C function on the code and it will decrypt the contents of the procedure.
/* read 16-bit big-endian word from unsigned char[] */ #define amos_deek(a) ((((a)[0])<<8)|((a)[1])) /* read 32-bit big-endian word from unsigned char[] */ #define amos_leek(a) ((((a)[0])<<24)|(((a)[1])<<16)|(((a)[2])<<8)|((a)[3])) void AMOS_decrypt_procedure(unsigned char *src) { unsigned char *line, *next, *endline; unsigned int key, key2, key3, size; /* src should be a pointer to a line with the PROCEDURE token on it */ if (amos_deek(&src[2]) != 0x0376) return; /* do not operate on compiled procedures */ if (src[10] & 0x10) return; size = amos_leek(&src[4]); line = next = &src[src[0] * 2]; /* the line after PROCEDURE */ endline = &src[size + 8 + 6]; /* the start of the line after END PROC */ /* initialise keys */ key = (size << 8) | src[11]; key2 = 1; key3 = amos_deek(&src[8]); while (line < endline) { line = next; next = &line[line[0] * 2]; for (line += 4; line < next;) { *line++ ^= (key >> 8) & 0xFF; *line++ ^= key & 0xFF; key += key2; key2 += key3; key = (key >> 1) | (key << 31); /* rotate right one bit */ } } src[10] ^= 0x20; /* toggle "is encrypted" bit */ }
AMOS Banks
AMOS banks can be found either included with AMOS source code, saved individually on disk, where they typically have the file extension .ABK.
AMOS allows for 15 banks in an program. Each bank can be located in "chip" memory, which is accessible to the Amiga's custom graphics and sound processors, or it can be located in "fast" memory, which is only accessible to the CPU.
Banks are identified by their first four bytes. They are either Sprite/Icon banks (using the ASCII identifier AmSp or AmIc), or they are "normal" memory banks, which covers all other possible bank formats (using the identifier AmBk).
AMOS Sprite/Icon Bank format
Refer to AMOS Sprite Bank. See also AMOS Icon Bank.
AMOS Memory Bank format
Refer to the main article: AMOS Memory Bank.