mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-11-18 23:07:53 +00:00
782 lines
27 KiB
Plaintext
782 lines
27 KiB
Plaintext
|
||
|
||
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.
|
||
Aliases: "!08", "!by", "!byte"
|
||
Examples: !08 127, label, -128 ; output some values
|
||
!by 14, $3d, %0110, &304, <*, "c"
|
||
!byte 3 - 4, label1 EOR label2, 2 ^ tz, (3+4)*7
|
||
|
||
|
||
Call: !16 EXPRESSION [, EXPRESSION]*
|
||
Purpose: Insert 16-bit values.
|
||
Parameters: EXPRESSION: Any formula the value parser accepts.
|
||
Aliases: "!wo", "!word"
|
||
Examples: !16 65535, label, -32768 ; output some values
|
||
!wo 14, $4f35, %100101010010110, &36304, *, "c"
|
||
!word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
|
||
|
||
|
||
Call: !24 EXPRESSION [, EXPRESSION]*
|
||
Purpose: Insert 24-bit values.
|
||
Parameters: EXPRESSION: Any formula the value parser accepts.
|
||
Examples: !24 16777215, label, -8388608, 14, $6a4f35
|
||
!24 %10010110100101010010110, &47336304, *, "c"
|
||
!24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
|
||
|
||
|
||
Call: !32 EXPRESSION [, EXPRESSION]*
|
||
Purpose: Insert 32-bit values.
|
||
Parameters: EXPRESSION: Any formula the value parser accepts.
|
||
Examples: !32 $7fffffff, label, -$80000000, 14, $46a4f35
|
||
!32 %1001011010010101001011010010, &4733630435, *, "c"
|
||
!32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
|
||
|
||
|
||
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: !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 6502 CPU's NOP command).
|
||
Examples: ; eliminate the 6502's JMP()-Bug:
|
||
!align 1, 0 ; wait for even address
|
||
Label !word Pointer
|
||
|
||
; align code to page border for speed increase
|
||
!align 255, 0
|
||
|
||
|
||
----------------------------------------------------------------------
|
||
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 "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" ; 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, 256 {!byte i-1}
|
||
; now exchange upper and lower case characters
|
||
*=65
|
||
!for i, 91-65 {!byte *+128}
|
||
*=97
|
||
!for i, 123-97 {!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"
|
||
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') EOR $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
|
||
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: Labels
|
||
----------------------------------------------------------------------
|
||
|
||
Call: !zone [TITLE] [ { BLOCK } ]
|
||
Purpose: Switch to new zone of local labels. 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 label
|
||
!zone File_IO ; new zone begins here
|
||
.backgroundcolor = 1 ; so this is a different label
|
||
!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 ; => "Label already defined."
|
||
|
||
|
||
Call: !sl FILENAME
|
||
Purpose: Save all the global labels to the given file after the
|
||
assembly is finished. This table could be loaded
|
||
during another assembly session using the "!source"
|
||
pseudo opcode.
|
||
Parameters: FILENAME: A file name given in "..." quoting.
|
||
Examples: !sl "Labels.a" ; produce label dump after assembly
|
||
!sl "global" ; produce label dump after assembly
|
||
|
||
|
||
----------------------------------------------------------------------
|
||
Section: Flow control
|
||
----------------------------------------------------------------------
|
||
|
||
Call: !if CONDITION { BLOCK } [ else { BLOCK } ]
|
||
Purpose: Conditional assembly. If the given condition is true,
|
||
the first block of statements will be parsed;
|
||
if it isn't, the second block will be parsed instead
|
||
(if present).
|
||
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: !text "Black", 0 ; Choose wording according to
|
||
!if country = uk { ; content of "country" label.
|
||
!text "Grey"
|
||
} else {
|
||
!text "Gray"
|
||
}
|
||
!byte 0
|
||
!text "White", 0
|
||
|
||
; Insert debug commands if label "debug" is not zero:
|
||
!if debug { lda #"z":jsr char_output }
|
||
|
||
|
||
Call: !ifdef LABEL { BLOCK } [ else { BLOCK } ]
|
||
or: !ifdef LABEL STATEMENT
|
||
Purpose: Conditional assembly, depending on whether a label is
|
||
already defined or not. If it is defined, the first
|
||
block of statements will be parsed; if it isn't, the
|
||
second block will be parsed instead (if present). This
|
||
opcode was only added to speed up parsing of library
|
||
files.
|
||
Only use it in your own files if you're sure you
|
||
*really* know what you are doing - using it in the
|
||
wrong place will result in loads of error messages.
|
||
Parameters: LABEL: Any valid label name.
|
||
BLOCK: A block of assembler statements.
|
||
STATEMENT: Any assembler statement.
|
||
Example: ; this was taken straight from <6502/std.a>:
|
||
!ifdef Lib_6502_std_a !eof ; parse this file once
|
||
Lib_6502_std_a = 1
|
||
|
||
|
||
Call: !for LABEL, TIMES { BLOCK }
|
||
Purpose: Looping assembly. The block of statements will be
|
||
parsed TIMES times. For a more flexible possibility,
|
||
have a look at "!do" below.
|
||
Parameters: LABEL: Any valid label name. The label's value will
|
||
show the number of the current loop cycle:
|
||
In the first cycle it will have the value 1, in the
|
||
last cycle it will have the value TIMES.
|
||
TIMES: Any formula the value parser accepts, but it
|
||
must be solvable even in the first pass. Negative
|
||
values are forbidden, zero causes the block to be
|
||
skipped.
|
||
BLOCK: A block of assembler statements.
|
||
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 label value, it never reads it back.
|
||
This was done to eliminate a possibility to hang ACME.
|
||
Examples: ; conversion table: integer to BCD
|
||
int2BCD !for Outer, 10 {
|
||
!for Inner, 10 {
|
||
!byte ((Outer-1) << 4) OR (Inner-1)
|
||
}
|
||
}
|
||
!fill 156, $ff ; values above 99 give 255 (invalid)
|
||
|
||
; conversion table: BCD to integer
|
||
BCD2int !for Outer, 10 {
|
||
!for Inner, 10 {
|
||
!byte 10 * (Outer-1) + (Inner-1)
|
||
}
|
||
!fill 6, $ff ; invalid BCD values give 255
|
||
}
|
||
!fill 96, $ff ; invalid BCD values give 255
|
||
|
||
|
||
Call: !set LABEL = VALUE
|
||
Purpose: Assign given value to label even if the label already
|
||
has a different value. Needed for loop counters when
|
||
using "!do", for example. Only use this opcode for
|
||
something else if you're sure you *really* know what
|
||
you are doing... :)
|
||
Parameters: LABEL: Any valid label 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: !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 STRING_VALUE
|
||
Purpose: Show a warning during assembly.
|
||
Parameters: STRING_VALUE: A string given in double quotes.
|
||
Example: !if * > $a000 {
|
||
!warn "Program reached ROM area."
|
||
}
|
||
|
||
|
||
Call: !error STRING_VALUE
|
||
Purpose: Generate an error during assembly (therefore, no
|
||
output file will be generated).
|
||
Parameters: STRING_VALUE: A string given in double quotes.
|
||
Example: rts ; end of some function
|
||
start !source "colors.a"
|
||
end !if end-start > 256 {
|
||
!error "Color strings exceed 256 chars!"
|
||
}
|
||
|
||
|
||
Call: !serious STRING_VALUE
|
||
Purpose: Generate a serious error, immediately stopping
|
||
assembly.
|
||
Parameters: STRING_VALUE: A string given in double quotes.
|
||
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 [[~]LABEL [, [~]LABEL]*] { BLOCK }
|
||
Purpose: Define a macro.
|
||
Parameters: TITLE: The macro's desired name (same rules as for
|
||
label names). If the title's first character is a dot
|
||
("."), the macro will be local (though why anyone
|
||
could want this is beyond me).
|
||
LABEL: The desired name for the parameter value at
|
||
call time. Normally, these parameter labels should be
|
||
local (first character a dot), as different macro
|
||
calls will almost for sure have different parameter
|
||
values.
|
||
If you prefix LABEL with a '~' character, it will be
|
||
called by reference, not by value: Changing the value
|
||
inside the macro will result in the "outer" label 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 label name. The '~'-prefix indicates
|
||
call-by-reference semantics, which means that when the
|
||
macro changes the label's value, the "outer" label
|
||
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.
|
||
|
||
|
||
----------------------------------------------------------------------
|
||
Section: Segment assembly
|
||
----------------------------------------------------------------------
|
||
|
||
Call: *= EXPRESSION
|
||
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 (not errors, as some people do this overlapping
|
||
on purpose).
|
||
Parameters: EXPRESSION: Any formula the value parser accepts, but
|
||
it must be solvable even in the first pass.
|
||
Examples: !to "TinyDemo", cbm ; define output file + format
|
||
*= $0801 ; Start at C64 BASIC start
|
||
+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
|
||
; 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
|
||
+basic_header ; Call macro to create program header
|
||
!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
|
||
; 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.
|
||
|
||
|
||
----------------------------------------------------------------------
|
||
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
|
||
|
||
|
||
----------------------------------------------------------------------
|
||
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 commands
|
||
the chosen CPU does not have. 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 allows official mnemonics and addressing modes
|
||
6510 adds mnemonics for some undocumented opcodes
|
||
(but includes all the official 6502 stuff)
|
||
65c02 allows official 65c02 stuff (includes 6502)
|
||
65816 allows official 65816 stuff (includes 65c02)
|
||
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 ; allow 65816 commands from here on
|
||
|
||
|
||
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: Deprecated pseudo opcodes (they still work at the moment)
|
||
----------------------------------------------------------------------
|
||
|
||
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" warning
|
||
Now use: !convtab pet ; does the same without warning
|
||
|
||
|
||
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. Its only
|
||
purpose is to be displayed in error messages, so it'll
|
||
be omitted in most cases.
|
||
BLOCK: A block of assembler statements.
|
||
Aliases: "!sz"
|
||
Old usage: !subzone graphics {
|
||
!source "graphics.a"
|
||
}
|
||
Now use: !zone graphics {
|
||
!source "graphics.a"
|
||
}
|
||
|
||
|
||
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...
|
||
}
|