

                                 ACME

         ...the ACME Crossassembler for Multiple Environments

                        --- pseudo opcodes ---


This is a list of all the pseudo opcodes currently implemented.
Stuff in square brackets is optional, stuff followed by "*" may be
given more than once. This list is not sorted alphabetically, the
pseudo opcodes are grouped together according to their usage.


----------------------------------------------------------------------
Section:   How to insert values
----------------------------------------------------------------------

Call:		!8 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 8-bit values.
Parameters:	EXPRESSION: Any formula the value parser accepts.
		"list" objects will be treated as if all their items
		were given separately.
Aliases:	"!by", "!byt", "!byte", "!08"
Examples:	!8 127, symbol, -128	; output some values
		!by 14, $3d, %0110, &304, <*, 'c'
		!byte 3 - 4, symbol1 XOR symbol2, 2 ^ tz, (3+4)*7
		!byte [2, 3, 5, 7], other_list	; iterate over items


Call:		!16 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 16-bit values in chosen CPU's byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	"!wo", "!word" (and because all currently supported
		CPUs are little-endian, "!le16" is in fact another
		alias)
Examples:	!16 65535, symbol, -32768	; output some values
		!wo 14, $4f35, %100101010010110, &36304, *, 'c'
		!word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!word [2, 3, 5, 7], other_list	; iterate over items

Call:		!le16 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 16-bit values in little-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None (but because all currently supported CPUs are
		little-endian, "!16/!wo/!word" are in fact aliases)
Examples:	!le16 65535, symbol, -32768	; output some values
		!le16 14, $4f35, %100101010010110, &36304, *, 'c'
		!le16 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!le16 [2, 3, 5, 7], other_list	; iterate over items

Call:		!be16 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 16-bit values in big-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None
Examples:	!be16 65535, symbol, -32768	; output some values
		!be16 14, $4f35, %100101010010110, &36304, *, 'c'
		!be16 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!be16 [2, 3, 5, 7], other_list	; iterate over items


Call:		!24 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 24-bit values in chosen CPU's byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None (but because all currently supported CPUs are
		little-endian, "!le24" is in fact an alias)
Examples:	!24 16777215, symbol, -8388608, 14, $6a4f35
		!24 %10010110100101010010110, &47336304, *, 'c'
		!24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!24 [2, 3, 5, 7], other_list	; iterate over items

Call:		!le24 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 24-bit values in little-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None (but because all currently supported CPUs are
		little-endian, "!24" is in fact an alias)
Examples:	!le24 16777215, symbol, -8388608, 14, $6a4f35
		!le24 %10010110100101010010110, &47336304, *, 'c'
		!le24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!le24 [2, 3, 5, 7], other_list	; iterate over items

Call:		!be24 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 24-bit values in big-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None
Examples:	!be24 16777215, symbol, -8388608, 14, $6a4f35
		!be24 %10010110100101010010110, &47336304, *, 'c'
		!be24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!be24 [2, 3, 5, 7], other_list	; iterate over items


Call:		!32 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 32-bit values in chosen CPU's byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None (but because all currently supported CPUs are
		little-endian, "!le32" is in fact an alias)
Examples:	!32 $7fffffff, symbol, -$80000000, 14, $46a4f35
		!32 %1001011010010101001011010010, &4733630435, *, 'c'
		!32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!32 [2, 3, 5, 7], other_list	; iterate over items

Call:		!le32 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 32-bit values in little-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None (but because all currently supported CPUs are
		little-endian, "!32" is in fact an alias)
Examples:	!le32 $7fffffff, symbol, -$80000000, 14, $46a4f35
		!le32 %1001011010010101001011010010, &4733630435, *, 'c'
		!le32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!le32 [2, 3, 5, 7], other_list	; iterate over items

Call:		!be32 EXPRESSION [, EXPRESSION]*
Purpose:	Insert 32-bit values in big-endian byte order.
Parameters:	EXPRESSION: Any formula the value parser accepts.
Aliases:	None
Examples:	!be32 $7fffffff, symbol, -$80000000, 14, $46a4f35
		!be32 %1001011010010101001011010010, &4733630435, *, 'c'
		!be32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
		!be32 [2, 3, 5, 7], other_list	; iterate over items


Call:		!hex PAIRS_OF_HEX_DIGITS
Purpose:	Insert byte values with a minimum of additional
		syntax. This pseudo opcode was added for easier
		writing of external source code generator tools.
Parameters:	PAIRS_OF_HEX_DIGITS: Just hexadecimal digits, without
		any "0x" or "$" prefix. Spaces and TABs are allowed,
		but not needed to separate the byte values.
Aliases:	"!h"
Examples:	!h f0 f1 f2 f3 f4 f5 f6 f7	; insert values 0xf0..0xf7
		!h f0f1f2f3 f4f5f6f7		; insert values 0xf0..0xf7
		!h f0f1f2f3f4f5f6f7		; insert values 0xf0..0xf7
		!h f0f 1f2	; ERROR: space inside pair!
		!h 0x00, $00	; ERROR: "0x", "," and "$" are forbidden!
		!h SOME_SYMBOL	; ERROR: symbols are forbidden!
		!h ABCD	; insert value 0xAB, then 0xCD (CAUTION, big-endian)


Call:		!fill AMOUNT [, VALUE]
Purpose:	Fill amount of memory with value.
Parameters:	AMOUNT: Any formula the value parser accepts, but it
		must be solvable even in the first pass.
		VALUE: Any formula the value parser accepts. If
		omitted, a default value is used (currently zero).
Aliases:	"!fi"
Examples:	!fi 256, $ff	; reserve 256 bytes
		!fill 2		; reserve two bytes


Call:		!skip AMOUNT
Purpose:	Advance in output buffer without starting a new
		segment.
Parameters:	AMOUNT: Any formula the value parser accepts, but it
		must be solvable even in the first pass (this
		limitation will hopefully be lifted in a future
		release).
Aliases:	None
Examples:	!skip BUFSIZE	; reserve some bytes
		!skip 5		; reserve five bytes


Call:		!align ANDVALUE, EQUALVALUE [, FILLVALUE]
Purpose:	Fill memory until a matching address is reached. ACME
		outputs FILLVALUE until "program counter AND ANDVALUE"
		equals EQUALVALUE.
Parameters:	ANDVALUE: Any formula the value parser accepts, but it
		must be solvable even in the first pass.
		EQUALVALUE: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		FILLVALUE: Any formula the value parser accepts. If it
		is omitted, a default value is used (currently 234,
		that's the opcode of the 6502 CPU's NOP instruction).
Examples:	!align 255, 0	; align to page (256 bytes)
		!align 63, 0	; align to C64 sprite block (64 bytes)


----------------------------------------------------------------------
Section:   How to insert text strings
----------------------------------------------------------------------

Call:		!convtab KEYWORD [ { BLOCK } ]
or:		!convtab FILENAME [ { BLOCK } ]
Purpose:	Choose text conversion table.
Parameters:	KEYWORD: Name of conversion table. Valid names are:
			pet	converts to PetSCII
			raw	doesn't convert at all
			scr	converts to C64 screencode
		FILENAME: File name of conversion table, given in
		"..." quoting (load from current directory) or in
		<...> quoting (load from library). The file must hold
		exactly 256 bytes.
		BLOCK: A block of assembler statements
		Before encountering this PO, ACME defaults to "raw".
		This PO supersedes the now deprecated "!cbm".
Aliases:	"!ct"
Examples:	!convtab raw
		!text "Test"	; outputs $54 $65 $73 $74
		!ct pet
		!tx "Test"	; outputs $d4 $45 $53 $54
		!ct scr {
			!tx "Test"	; outputs $54 $05 $13 $14
			!ct "my_own_table_file"
			!tx "abcdefg"	; whatever... :)
		}
		!tx "Test"	; outputs $d4 $45 $53 $54 again
Hint: If you don't want to fiddle with a hex editor to create a
conversion table file, try using ACME:
		!to "asc2pet.ct", plain	; no load address
		* = 0			; pc = table index
		; first create "as-is" table
		!for i, 0, 255 {!byte i}
		; now exchange upper and lower case characters
		* = 65, overlay
		!for i, 1, 26 {!byte * + 128}
		* = 97, overlay
		!for i, 1, 26 {!byte * - 32}
The resulting file can be used as a conversion table to convert to
PetSCII (which is useless, because ACME can do so anyway. But you get
the idea).


Call:		!text STRING_VALUE [, STRING_VALUE]*
Purpose:	Output the given string(s) using the current
		conversion table.
Parameters:	STRING_VALUE: Can be either a string given in double
		quotes or any formula the value parser accepts.
		Please note that formula results won't be converted,
		but single characters involved in calculations will.
Aliases:	"!tx", "!txt"
Examples:	!text "Loading...", Char_NewLine, "Filename:", 0
		!tx "Offset character is ", offset - 1 + 'a', 0


Call:		!pet STRING_VALUE [, STRING_VALUE]*
Purpose:	Output the given string(s) using the PetSCII
		conversion table (This means to exchange the upper-
		and lowercase characters; useful for C64 programs).
Parameters:	STRING_VALUE: Can be either a string given in double
		quotes or any formula the value parser accepts.
		Please note that formula results won't be converted,
		but single characters involved in calculations will.
Examples:	!pet "Loading...", Char_NewLine, "Filename:", 0
		!pet "Offset character is ", offset - 1 + 'a', 0


Call:		!raw STRING_VALUE [, STRING_VALUE]*
Purpose:	Output the given string(s) without any conversion at
		all.
Parameters:	STRING_VALUE: Can be either a string given in double
		quotes or any formula the value parser accepts.
Examples:	!raw "Loading...", Char_NewLine, "Filename:", 0
		!raw "Offset character is ", offset - 1 + 'a', 0


Call:		!scr STRING_VALUE [, STRING_VALUE]*
Purpose:	Output the given string(s) using the C64 screen code
		conversion table (useful for C64 programs, as you will
		have guessed).
Parameters:	STRING_VALUE: Can be either a string given in double
		quotes or any formula the value parser accepts.
		Please note that formula results won't be converted,
		but single characters involved in calculations will.
Examples:	!scr "Loading...", Char_NewLine, "Filename:", 0
		!scr "Offset character is ", offset - 1 + 'a', 0


Call:		!scrxor XOR_VALUE, STRING_VALUE [, STRING_VALUE]*
Purpose:	Output the given string(s) using the C64 screen code
		conversion table and exclusive-OR-ing the results with
		the given value (useful for C64 programs when inverse
		video is needed, or EBC mode, etc.).
Parameters:	XOR_VALUE: Any formula the value parser accepts.
		STRING_VALUE: Can be either a string given in double
		quotes or any formula the value parser accepts.
		Please note that formula results will be neither
		converted nor exclusive-OR-d.
		Single characters involved in calculations will be
		converted, but not exclusive-OR-d.
Examples:	!scrxor $80, "Loading..."
		!scrxor $a0, "Offset char is ", (offset-1+'a') XOR $a0


----------------------------------------------------------------------
Section:   File stuff
----------------------------------------------------------------------

Call:		!to FILENAME, FILEFORMAT
Purpose:	Define the output file name and file type. If this
		opcode isn't used, ACME still fully processes the
		source code - as the resulting binary isn't stored,
		this only serves to check for errors. Instead of using
		this pseudo opcode, you can also use the command line
		options "--outfile" and "--format".
Parameters:	FILENAME: A file name given in "..." quoting.
		FILEFORMAT: Name of file format. Valid names are:
			cbm	with load address (Commodore format)
			plain	without load address
			apple	with load address and length (Apple II)
		If FILEFORMAT is omitted, ACME gives a warning and
		then defaults to "cbm" (this can be changed using the
		command line option "--format").
Examples:	!to "eprom.p", plain	; don't add a load address
		!to "demo.o", cbm	; add c64-style load address


Call:		!source FILENAME
Purpose:	Assemble another source code file. After having
		processed the new file, ACME continues processing the
		old one.
Parameters:	FILENAME: A file name given in "..." quoting (load
		from current directory) or in <...> quoting (load from
		library).
Aliases:	"!src"
Examples:	!source <6502/std.a>	; Read library file
		!src "Macros.a"		; Read file from current dir


Call:		!binary FILENAME [, [SIZE] [, [SKIP]]]
Purpose:	Insert binary file directly into output file.
Parameters:	FILENAME: A file name given in "..." quoting (load
		from current directory) or in <...> quoting (load from
		library).
		SIZE: Any formula the value parser accepts, but it
		must be solvable even in the first pass. If SIZE is
		given, it is used: If the file is longer, only SIZE
		bytes are read; if it is shorter, ACME will use
		padding until SIZE is reached. If SIZE is omitted,
		ACME will include the file until EOF.
		SKIP: Any formula the value parser accepts. If SKIP is
		omitted, it defaults to zero. ACME will start loading
		the file from file offset SKIP. So C64 coders wanting
		to include C64 files without their load addresses
		should use a SKIP value of 2.
Aliases:	"!bin"
Examples:	!binary <Own/menudata.b>	; insert library file
		!bin "asc2pet.b", 256, 2	; insert 256 bytes
						; from file offset 2.
		!bin "table", 2, 9	; insert 2 bytes from offset 9
		!bin "list",, 9		; insert from offset 9 to EOF


----------------------------------------------------------------------
Section:   Symbols
----------------------------------------------------------------------

Call:		!zone [TITLE] [ { BLOCK } ]
Purpose:	Switch to new zone of local symbols. Zones can either
		be nested or used sequentially.
Parameters:	TITLE: May consist of letters and digits. Its only
		purpose is to be displayed in error messages, so it'll
		be omitted in most cases.
		BLOCK: A block of assembler statements
		If no block is given, the previous zone is terminated
		and the new zone is started.
		If a block is given, the old zone continues after the
		block.
Aliases:	"!zn"
Examples:	.backgroundcolor = 0	; some local symbol
		!zone File_IO		; new zone begins here, so
		.backgroundcolor = 1	; this is a different symbol
		!zn LinkedList_Init
		.backgroundcolor = 2
		!zone LinkedList {	; start of nested zone
			; imagine some code here...
			!zone LinkedList_Init
			; imagine some more code here...
			!zone LinkedList_Body {
				; imagine yet some more code here...
				!zone LinkedList_SecondPart
				; imagine still some more code here...
			}
			!zone LinkedList_End
			; you know what to imagine here...
		}
		.backgroundcolor = 3	; -> "Symbol already defined."


Call:		!symbollist FILENAME
Purpose:	Write a symbol list to the given file after assembly
		is finished. The list will contain all global symbols.
		This table could be loaded during another assembly
		session using the "!source" pseudo opcode.
Parameters:	FILENAME: A file name given in "..." quoting.
Aliases:	"!sl"
Examples:	!sl "Symbols.a"	; produce symbol list after assembly
		!sl "global"	; produce symbol list after assembly


----------------------------------------------------------------------
Section:   Flow control
----------------------------------------------------------------------

Call:		!if CONDITION { BLOCK } [ else { BLOCK } ]
Purpose:	Conditional assembly. If the given condition is true,
		the matching block of statements will be parsed;
		if no condition is true, the ELSE block (if present)
		will be parsed.
Parameters:	CONDITION: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		BLOCK: A block of assembler statements.
Examples:	; Choose word according to "country" symbol:
		!if country == uk {
			!text "Grey"
		} else if country == fr {
			!text "Gris"
		} else if country == de {
			!text "Grau"
		} else {
			!text "Gray"
		}

		; Insert debug code depending on symbol "debug":
		!if debug { lda #'z':jsr char_output }


Call:		!ifdef SYMBOL { BLOCK } [ else { BLOCK } ]
or:		!ifdef SYMBOL STATEMENT
Call:		!ifndef SYMBOL { BLOCK } [ else { BLOCK } ]
or:		!ifndef SYMBOL STATEMENT
Purpose:	Conditional assembly, depending on whether a symbol is
		already defined or not.
		With "ifdef", if the symbol is defined, the first
		block of statements will be parsed; if it isn't, the
		second block will be parsed instead (if present).
		With "ifndef", it's the other way around: If the
		symbol isn't defined, the first block of statements
		will be parsed; if it is defined, the second block
		will be parsed instead (if present).
		CAUTION: These opcodes were added to speed up parsing
		of library files (see example below). They can be used
		to tell passes apart, therefore only use them in your
		own files if you're sure you *really* know what you
		are doing - using them in the wrong way will result in
		loads of error messages.
Parameters:	SYMBOL: Any valid symbol name.
		BLOCK: A block of assembler statements.
		STATEMENT: Any assembler statement.
Examples:	; this was taken from <6502/std.a>:
		!ifdef Lib_6502_std_a !eof	; in later passes,
		Lib_6502_std_a = 1		; skip this file.
		; During the first pass, the symbol is not defined,
		; therefore the file will get parsed. During all
		; further passes, the symbol is already defined,
		; therefore the file will be skipped.

		; if the following code gets included several times,
		; only assemble it at the first location:
		!ifndef my_label {my_label} ; only define if undefined
		!if * == my_label {
			; imagine some code here...
			; this block will only be assembled at the
			; first location where it is included. all
			; further instances will be skipped.
		}

		; include at most one driver source code:
		!ifdef RAM_REU {
			!src "driver_reu.a"
		} else ifdef RAM_GEORAM {
			!src "driver_georam.a"
		} else ifdef RAM_VDCRAM {
			!src "driver_vdcram.a"
		} else ifdef RAM_SUPERRAM {
			!src "driver_superram.a"
		} else {
			!src "driver_noram.a"
		}


Call:		!for SYMBOL, START, END { BLOCK }
or:		!for SYMBOL in ITERABLE { BLOCK }
Purpose:	Looping assembly. The block of statements will be
		parsed a fixed number of times, as specified by the
		arguments:
		When using the first syntax, SYMBOL will simply count
		from START to END.
		When using the second syntax, SYMBOL will iterate over
		the contents of the ITERABLE, which must be a string
		or a list.
		For more flexible loop constructs, have a look at
		"!do" and "!while" below.
Parameters:	SYMBOL: Any valid symbol name.
		START: Any formula the value parser accepts, but it
		must be solvable even in the first pass. SYMBOL will 
		have this value during the first loop cycle.
		END: Any formula the value parser accepts, but it must
		be solvable even in the first pass. SYMBOL will have
		this value during the last loop cycle.
		ITERABLE: This must be a string or a list, but its
		length must be defined even in the first pass (and of
		course it should stay the same during all subsequent
		passes).
			If ITERABLE is a list, its _items_ are allowed
			to be undefined.
			If ITERABLE is a string, SYMBOL will be set to
			each of its character codes in turn, using the
			currently chosen conversion table.
		BLOCK: A block of assembler statements.
		If START or END are floats, they will be converted to
		integers (never use floats for loop counters). If
		START is less than or equal to END, SYMBOL will get
		incremented at the end of each cycle; if START is
		greater than END, SYMBOL will get decremented at the
		end of each cycle. So after leaving the loop, SYMBOL
		will have an "illegal" value (END + 1 if counting up,
		END - 1 if counting down).
		Please note that it is impossible to change the number
		of loop cycles "inside" the loop by fiddling with the
		counter using the "!set" pseudo opcode: The "!for"
		routine keeps its own copy of the counter value and
		only sets the symbol value, it never reads it back.
		This was done to eliminate a possibility to hang ACME.
Examples:
	int2BCD ; conversion table: integer to BCD
		!for Outer, 0, 9 {
			!for Inner, 0, 9 {
				!byte (Outer << 4) OR Inner
			}
		}
		!fill 156, $ff	; values above 99 give 255 (invalid)

	BCD2int ; conversion table: BCD to integer
		!for Outer, 0, 9 {
			!for Inner, 0, 9 {
				!byte 10 * Outer + Inner
			}
			!fill 6, $ff	; invalid BCD values give 255
		}
		!fill 96, $ff		; invalid BCD values give 255

	quickclear ; generate speedcode to clear C64 screen
		lda #' '
		!for i, 0, 999 {
			sta $0400 + i
		}

	split_table_lo ; generate two tables from one list
		!for h in my_handler_list {
			!by <h
		}
	split_table_hi
		!for h in my_handler_list {
			!by >h
		}

	hidden_string ; "encrypt" a string by XORing with address
		!ct scr {	; use screen codes
			!for c in "very secret message" {
				!by c XOR <*
			}
		}

Miscellaneous:	The old syntax
			!for SYMBOL, END { BLOCK }
		where START was always implied to be 1 is still fully
		supported, but gives a warning to get people to change
		to the new syntax.
		You can disable this warning using the "--dialect" or
		the "-Wno-old-for" switches, but then you will get
		warnings for using the *new* syntax.
		When migrating your sources to the current syntax,
		bear in mind that it is no longer possible to skip the
		block completely by specifying a loop count of zero.
		Also note that with the new algorithm, SYMBOL has a
		different value after the block than during the last
		loop cycle, while the old algorithm kept that last
		value.


Call:		!set SYMBOL = VALUE
Purpose:	Assign given value to symbol even if the symbol
		already has a different value. Needed for loop
		counters when using "!do"or "!while", for example.
		Only use this opcode for something else if you're sure
		you *really* know what you are doing... :)
Parameters:	SYMBOL: Any valid symbol name.
		VALUE: Any formula the value parser accepts.
Example:	see "!do" below


Call:		!do [KEYWORD CONDITION] { BLOCK } [KEYWORD CONDITION]
Purpose:	Looping assembly. The block of statements can be
		parsed several times, depending on the given
		condition(s).
		Conditions may be placed before or after the block (or
		even at both places), they are then parsed in every
		repetition before or after the block respectively. If
		there is a condition before the block and it isn't
		met when first checked, the block will be skipped.
Parameters:	KEYWORD: Either "until" or "while" (without quotes).
		CONDITION: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		BLOCK: A block of assembler statements.
Examples:	; a loop with conditions at both start and end
		!set a = 0			; init loop counter
		!do while loop_flag == TRUE {
			lda #a
			sta label + a
			!set a = a + 1
		} until a > 6

		; a loop with a condition at the start
		!do while * < $c000 { nop }

		; a loop with a condition at the end
		!do { !wo * + base } while * < base + 345

		; a never ending loop - this will cause an error
		!do while 3 < 4 { nop } until 3 == 4

		; an empty loop - this will hang ACME
		!do until 3 == 4 {     } while 3 < 4


Call:		!while [CONDITION] { BLOCK }
Purpose:	Looping assembly. The block of statements can be
		parsed several times, depending on the given
		condition.
		The condition is parsed in every repetition before the
		actual block. If it isn't met when first checked, the
		block will be skipped.
Parameters:	CONDITION: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		BLOCK: A block of assembler statements.
Examples:	; a loop with a counter
		!set a = 0			; init loop counter
		!while a < 6 {
			lda #a
			sta label + a
			!set a = a + 1
		}

		; a loop depending on program counter
		!while * < $c000 { nop }

		; a never ending loop - this will cause an error
		!while 3 < 4 { nop }

		; an empty loop - this will hang ACME
		!while 3 != 4 {     }


Call:		!endoffile
Purpose:	Stop processing the current source file. Using this
		pseudo opcode you can add explanatory text inside your
		source file without having to comment out every single
		line of it.
Aliases:	"!eof"
Example:	rts	; some assembler mnemonic
		!eof
		Though this text isn't preceded by a semicolon, it is
		treated as if it were a comment. In fact, ACME doesn't
		even parse this anymore - the file gets closed when
		"!eof" is reached.


Call:		!warn VALUE [, VALUE]*
Purpose:	Show a warning during assembly.
Parameters:	VALUE: Can be either a string given in double quotes
		or any formula the value parser accepts.
		Integer numbers will be output in both decimal _and_
		hex formats.
Example:	!if * > $a000 {
			!warn "Program reached ROM: ", * - $a000, " bytes overlap."
		}


Call:		!error VALUE [, VALUE]*
Purpose:	Generate an error during assembly (therefore, no
		output file will be generated).
Parameters:	VALUE: Can be either a string given in double quotes
		or any formula the value parser accepts.
		Integer numbers will be output in both decimal _and_
		hex formats.
Example:	rts	; end of some function
	start	!source "colors.a"
	end	!if end - start > 256 {
			!error "Color strings are ", end - start - 256, " bytes too long."
		}


Call:		!serious VALUE [, VALUE]*
Purpose:	Generate a serious error, immediately stopping
		assembly.
Parameters:	VALUE: Can be either a string given in double quotes
		or any formula the value parser accepts.
		Integer numbers will be output in both decimal _and_
		hex formats.
Example:	!source "part1.a"	; sets part1_version
		!source "part2.a"	; sets part2_version
		!if part1_version != part2_version {
			!serious "part1.a and part2.a don't match!"
		}


----------------------------------------------------------------------
Section:   Macro usage
----------------------------------------------------------------------

Call:		!macro TITLE [[~]SYMBOL [, [~]SYMBOL]*] { BLOCK }
Purpose:	Define a macro.
Parameters:	TITLE: The macro's desired name (same rules as for
		symbol names). If the title's first character is a dot
		("."), the macro will be local (though why anyone
		could want this is beyond me).
		SYMBOL: The desired name for the parameter value at
		call time. Normally, these parameter symbols should be
		local (first character a '.' or a '@'), as different
		macro calls will almost for sure have different
		parameter values.
		If you prefix SYMBOL with a '~' character, it will be
		called by reference, not by value: Changing the value
		inside the macro will result in the "outer" symbol to
		be changed as well.
		BLOCK: A block of assembler statements.
Examples:	; far branch, as defined in <6502/std.a>
		!macro bne .target {
			beq * + 5
			jmp .target
		}

		; increase 16-bit counters
		!macro dinc .target {
			inc .target
			bne +	; "bne * + 5" would not work in zp
			inc .target + 1
		+
		}
		; Yes, anonymous label references can be used with
		; macros (unlike several other assemblers). That's
		; because ACME's macros are implemented more like
		; real functions.

		; load A and X
		!macro ldax .target {
			lda .target
			ldx .target + 1
		}

		; store A and X
		!macro stax .target {
			sta .target
			stx .target + 1
		}

		; use call-by-reference for return value
		!macro reserve ~.address, .amount {
			.address = external_pc
			!set external_pc = external_pc + .amount
		}

		; define a pixel row of a C64 hardware sprite
		!macro SpriteLine .v {
			!by .v >> 16, (.v >> 8) & 255, .v & 255
		}


Call:		+TITLE [ARGUMENT [, ARGUMENT]*]
Purpose:	Call a macro, using the given parameter values.
Parameters:	TITLE: The macro's name as given in its definition.
		ARGUMENT: This is either any formula the value parser
		accepts, or (new in release 0.86) a '~' character
		followed by a symbol name. The '~'-prefix indicates
		call-by-reference semantics, which means that when the
		macro changes the symbol's value, the caller's
		symbol's value will change as well.
Examples:	inc label
		bne mark	; "near" branch
		inc label2
		+bne mark2	; "far" branch

		inc $fa		; increase  8-bit counter
		+dinc $fb	; increase 16-bit counter

		ldy label	; get byte
		+ldax label2	; get two bytes

		; using macro calls in a macro definition
		!macro cp16 .source, .target {
			+ldax .source
			+stax .target
		}

		; use call-by-reference for return value
		!set external_pc = $0400
		+reserve ~.line_buffer, 80
		+reserve ~.in_buffer, 256
		+reserve ~.out_buffer, 256
		+reserve ~.byte_var, 1

		; define a C64 hardware sprite
		;            765432107654321076543210
		+SpriteLine %........................
		+SpriteLine %.#......................
		+SpriteLine %.##.....................
		+SpriteLine %.###....................
		+SpriteLine %.####...................
		+SpriteLine %.#####..................
		+SpriteLine %.######.................
		+SpriteLine %.#######................
		+SpriteLine %.########...............
		+SpriteLine %.#########..............
		+SpriteLine %.########...............
		+SpriteLine %.######.................
		+SpriteLine %.######.................
		+SpriteLine %.##..##.................
		+SpriteLine %.#....##................
		+SpriteLine %......##................
		+SpriteLine %.......##...............
		+SpriteLine %.......##...............
		+SpriteLine %........##..............
		+SpriteLine %........##..............
		+SpriteLine %........................
		!byte 0	; pad to 64-byte block

Since release 0.86, different macros are allowed to have the same name
as long as their parameter lists differ in size (number of arguments)
or type (call-by-value vs. call-by-reference). So
		!macro process_bytes b1, b2 {...whatever...}
		!macro process_bytes b1, b2, b3 {...whatever...}
		!macro process_bytes b1, b2, ~b3 {...whatever...}
can *all* be used at the same time without any name clash.

Since release 0.97, lists are supported. This is useful for macros if
you want an arbitrary number of arguments: Just pass a list or string
as one of the arguments and have the macro iterate over its contents.
Examples:	; a macro to store any number of arguments as 16-bit values:
		!macro iter_example @iterable {
			!for @item in @iterable {
				!16 @item
			}
		}
		+iter_example [2, 3, 5, 7]	; iterate over list items
		+iter_example "string"		; iterate over characters

		; a macro to "encrypt" each argument with its index:
		!macro scramble @iterable {
			@length = len(@iterable)
			!for @index, 0, @length - 1 {
				!byte @iterable[@index] xor @index
			}
		}
		+scramble [2, 3, 5, 7]	; iterate over list items
		+scramble "string"	; iterate over characters


----------------------------------------------------------------------
Section:   Segment assembly
----------------------------------------------------------------------

Call:		* = EXPRESSION [, MODIFIER]*
Purpose:	Set program counter to given value and start new
		segment. This opcode must be given at least once
		(or the command line option "--setpc" must be used).
		If segments overlap each other, warnings will be
		issued. Because some people do this overlapping
		on purpose, the warnings can be suppressed using
		modifier keywords.
		Using the "--strict-segments" CLI switch, these
		warnings can be turned onto errors. Future versions of
		ACME may do that by default - so if needed, use the
		modifier keywords.
Parameters:	EXPRESSION: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		MODIFIER: "overlay" or "invisible" (without quotes):
		"overlay" suppresses the warning "Segment starts
		inside another one, overwriting it".
		"invisible" makes the new segment invisible, so that
		_other_ segments will never raise the warning "Segment
		reached another one, overwriting it".
Examples:	!to "TinyDemo", cbm	; define output file + format
		* = $0801		; start at C64 BASIC start
		!src "basicmacros.a"	; include macro definitions
		+basic_header		; call program header macro
		!src "main.a"		; include main program
		* = $1000		; jump to new segment
		!bin "music.b"		; load music to $1000
		* = $8000		; jump to new segment
		!bin "pic.b"		; load graphics to $8000
		* = $8010, overlay, invisible	; go back and patch
				; the graphics, suppressing warnings
		; After assembly, ACME will save everything from $0801
		; up to the highest address written to. The resulting
		; file will contain some big unused areas (zero'd),
		; but demos will get compressed anyway... :)


Call:		!initmem EXPRESSION
Purpose:	Define "unchanged" memory. ACME will fill its output
		buffer completely with the given value before storing
		the assembled code. So gaps between segments will
		contain the desired byte when writing the output file.
		Instead of using this pseudo opcode, you can also use
		the "--initmem" command line option. If neither is
		used, the buffer is cleared.
Parameters:	EXPRESSION: Any formula the value parser accepts, but
		it must be solvable even in the first pass (because
		this opcode will be ignored in all later passes).
Examples:	!to "TinyDemo", cbm	; define output file + format
		!initmem $ea		; default memory content $ea.
		* = $0801		; start at C64 BASIC start
		!src "basicmacros.a"	; include macro definitions
		+basic_header		; call program header macro
		!src "main.a"		; include main program
		* = $1000		; jump to new segment
		!bin "music.b"		; load music to $1000
		* = $8000		; jump to new segment
		!bin "pic.b"		; load graphics to $8000
		* = $8010, overlay, invisible	; go back and patch
				; the graphics, suppressing warnings
		; This is the same example as before, but now the big
		; unused areas will contain the value $ea instead of
		; zero.

		!initmem $ff	; Default memory content is now $ff.
		; Useful if you want to store your code in an EPROM.


Call:		!xor EXPRESSION [ { BLOCK } ]
Purpose:	Change the value to XOR all output bytes with (the
		value defaults to zero on startup). This "encryption"
		facility was added to compensate for the shortcomings
		of the "!scrxor" pseudo opcode, which only XORs
		strings and characters, but not numbers.
		When used with block syntax, the previously chosen
		value is restored afterwards.
Parameters:	EXPRESSION: Any formula the value parser accepts.
		BLOCK: A block of assembler statements.
Examples:	; first as normal screencodes:
		!scr "Hello everybody...", GROUPLOGOCHAR
		; and now as inverted screencodes:
		!xor $80 {
		!scr "Hello everybody...", GROUPLOGOCHAR
		}
		

----------------------------------------------------------------------
Section:   Offset assembly
----------------------------------------------------------------------

Call:		!pseudopc EXPRESSION { BLOCK }
Purpose:	Assemble code as if the program counter had the given
		value, effectively producing a program that has to be
		copied to a different address space before being run.
		After having processed the block of statements with
		the new program counter, the updated (!) old program
		counter is used again.
		Thanks to the block syntax, offset assembly can now be
		nested. Then the old program counter would not
		necessarily be the *real* program counter, but could
		be a pseudopc as well. ;)
Parameters:	EXPRESSION: Any formula the value parser accepts, but
		it must be solvable even in the first pass.
		BLOCK: A block of assembler statements.
Examples:	ldx #.shifted_end - .shifted_start
	-		lda .shifted_start - 1, x
			sta .target - 1, x
			dex
			bne -
		jmp .target
	.shifted_start
		!pseudopc $0400 {
	.target	; imagine some code here...
		; it should be copied to $0400 and executed *there*
		}
	.shifted_end

Miscellaneous:	If you need to convert a label or the program counter
		from its "pseudopc" to its "real" value, you can do
		that using the "&" operator. Given the example above,
		the symbol ".target" will evaluate to the value $0400,
		but "&.target" will evaluate to the same value as
		".shifted_start" will.


----------------------------------------------------------------------
Section:   CPU support pseudo opcodes (especially 65816 support)
----------------------------------------------------------------------

Call:		!cpu KEYWORD [ { BLOCK } ]
Purpose:	Select the processor to produce code for. If this PO
		isn't used, ACME defaults to the 6502 CPU (or to the
		one selected by the "--cpu" command line option).
		ACME will give errors if you try to assemble
		instructions the chosen CPU does not support. You can
		change the chosen CPU at any time. When used with
		block syntax, the previously chosen CPU value is
		restored afterwards.
Parameters:	KEYWORD: Currently valid keywords are:
		6502		for the original MOS 6502
		nmos6502	6502 plus undocumented opcodes
		6510		(alias for "nmos6502")
		65c02		6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB
		r65c02		65c02 plus BBRx, BBSx, RMBx, SMBx
		w65c02		r65c02 plus STP/WAI
		65816		65c02 plus 16/24-bit extensions
		65ce02		r65c02 plus Z reg, long branches, ...
		4502		65ce02 with MAP instead of AUG
		m65		4502 plus 32-bit extensions
		c64dtv2		6502 plus BRA/SAC/SIR plus some of the
				undocumented opcodes
			See "docs/cputypes/all.txt" for more info.
		BLOCK: A block of assembler statements.
Examples:	!if cputype == $65c02 {
			!cpu 65c02 {	; temporarily allow 65c02 stuff
				stz .todelete
			}
		} else {
			pha
			lda #0
			sta .todelete
			pla
		}
		rts
		!cpu 65816	; now allow instructions of 65816 cpu


Call:		!al [ { BLOCK } ]
Purpose:	Assume long (16 bits) accumulator. Only allowed when
		producing code for the 65816 CPU. When used with block
		syntax, the previous configuration is restored
		afterwards.


Call:		!as [ { BLOCK } ]
Purpose:	Assume short (8 bits) accumulator. Only needed when
		producing code for the 65816 CPU. When used with block
		syntax, the previous configuration is restored
		afterwards. Short accumulator is the default in every
		pass.


Call:		!rl [ { BLOCK } ]
Purpose:	Assume long (16 bits) index registers. Only allowed
		when producing code for the 65816 CPU. When used with
		block syntax, the previous configuration is restored
		afterwards.


Call:		!rs [ { BLOCK } ]
Purpose:	Assume short (8 bits) index registers. Only needed
		when producing code for the 65816 CPU. When used with
		block syntax, the previous configuration is restored
		afterwards. Short registers are the default in every
		pass.


----------------------------------------------------------------------
Section:   Type system
----------------------------------------------------------------------

Call:		!address [ { BLOCK } ]
or:		!address SYMBOL = VALUE
Purpose:	Mark a block or a statement as "explicitly defined
		symbols are holding addresses".
Parameters:	BLOCK: A block of assembler statements
		Everything inside the block will be parsed as usual,
		but explicitly defined symbols will be marked as
		referencing memory.
		If no block is given, only the current statement will
		be affected, which should then be an explicit symbol
		definition.
		To make use of this feature, you need to use the
		"-Wtype-mismatch" CLI switch.
Aliases:	"!addr"
Examples:	!addr	k_chrout = $ffd2	; this is an address
			CLEAR = 147		; but this is not
		!addr {
			; these are addresses:
			sid_v1_control	= $d404
			sid_v2_control	= $d40b
			sid_v3_control	= $d412
		}
		; these are not:
		sid_VOICECONTROL_NOISE		= %#.......
		sid_VOICECONTROL_RECTANGLE	= %.#......
		sid_VOICECONTROL_SAWTOOTH	= %..#.....
		sid_VOICECONTROL_TRIANGLE	= %...#....


----------------------------------------------------------------------
Section:   Obsolete pseudo opcodes (they will throw errors if used)
----------------------------------------------------------------------

Call:		!cbm
Purpose:	Use PetSCII as the text conversion table. Now
		superseded by the "!convtab" pseudo opcode.
Old usage:	!cbm		; gives "use !ct pet instead" error
Now use:	!convtab pet	; does the same without error
		If you just want to assemble an old source code
		without touching it, use the "--dialect" CLI switch:
		Using "--dialect 0.94.6" or earlier will assemble this
		pseudo opcode without throwing an error.


Call:		!subzone [TITLE] { BLOCK }
Purpose:	Allows nesting of zones. Now superseded by "!zone"
		because that allows nesting as well.
Parameters:	TITLE: May consist of letters and digits.
		BLOCK: A block of assembler statements.
Aliases:	"!sz"
Old usage:	!subzone graphics {
			!source "graphics.a"
		}
Now use:	!zone graphics {
			!source "graphics.a"
		}
		If you just want to assemble an old source code
		without touching it, use the "--dialect" CLI switch:
		Using "--dialect 0.94.6" or earlier will assemble this
		pseudo opcode without throwing an error.


Call:		!realpc
Purpose:	Restore the program counter to its real value,
		therefore finishing offset assembly. Because
		"!pseudopc" now knows block syntax and can be nested,
		there's no reason to use "!realpc" any more.
Old usage:	!pseudopc $0400
		; imagine some code here...
		!realpc
Now use:	!pseudopc $0400 {
			; imagine some code here...
		}
		If you just want to assemble an old source code
		without touching it, use the "--dialect" CLI switch:
		Using "--dialect 0.94.6" or earlier will assemble this
		pseudo opcode without throwing an error.
		Using "--dialect 0.85", not even a warning is thrown.
