xasm - 6502 cross-assembler
xasm source [options]
xasm is a cross-assembler which generates code for the 6502 processor.
source is the name of the source file. If no filename extension is given, .asx is appended. The default action (when invoked without options) is to assembly source, writing the result to a file with the .obx extension.
Alternatively, you may use Unix-style options, for example:
xasm -i -d DEBUG=1 -l listing.lst source.asx
Source files should be plain ASCII files. LF, CR, CR/LF and Atari line terminators are supported. Labels and instructions are case-insensitive.
xasm is backward compatible with Quick Assembler. To compile QA sources with xasm, simply replace ATASCII-specific characters with their integer codes. You also have to change all OPT directives, but usually you can simply remove them.
A label is a symbol that represents a 32-bit signed integer. You can define a label by putting its name at the beginning of a line (with no spaces before). Unless you use the EQU directive, the label is assigned the current value of the origin counter.
Instructions and directives must be preceded with some whitespace. Note that in xasm you can use instruction and directive names as label names. For example
nop
defines a label called nop, whereas
nop
is a 6502 instruction.
Full comment lines must start with a semicolon, a pipe or an asterisk, with optional label definition and spaces before. Here are examples of full comment lines:
; this is a comment * so it is label | and this too
Lines with instructions (and some directives) may be repeated. A single line may be assembled several times, for example:
:4 asl @ table :32*5 dta 5
In lines with instructions or directives, a comment starts immediately after the instruction/directive has been successfully parsed. That is, xasm does not require a special character to start a comment.
lda foo ; this is a comment sta bar so it is tax #0 tax has no operand, therefore #0 starts this comment
You may put two instructions in one line so they share the operand. For example:
eor:sta foo
is equivalent to
eor foo sta foo
Note that
lda:tax #0
is allowed because #0 is treated as a comment for tax.
Expressions are numbers combined with operators and brackets. You should use square brackets, because parentheses are reserved for 6502 indirect addressing.
Numbers can be expressed as:
Abbreviations of Atari hardware registers are provided to save you the trouble of typing two extra characters (^4e vs $d40e) and to ease porting software between Atari 8-bit computers and the Atari 5200 console. These are very similar machines, one of the biggest differences is the location of hardware registers.
Syntax | Chip | Value in the Atari 8-bit computer mode (opt g-) |
Value in the Atari 5200 game console mode (opt g+) |
---|---|---|---|
^0x | GTIA | $D00x | $C00x |
^1x | GTIA | $D01x | $C01x |
^2x | POKEY | $D20x | $E80x |
^3x | PIA | $D30x | error (there's no PIA chip) |
^4x | ANTIC | $D40x | $D40x |
An op-code is the single-byte op-code of the instruction inside braces. The operand of the instruction is discarded and is necessary only to recognize the addressing mode. The instruction should begin right after the left brace and the right brace should immediately follow the operand or the instruction. You can skip the operand if the addressing mode is fixed. Examples: {lda #}, {jsr}, {bne}, {jmp ()}, {sta a:,x}.
You can use the line repeat counter (#) in the repeated lines. It counts the iterations starting from zero. Examples:
:3 dta # ; generates three bytes: 0, 1, 2. line_lo :192 dta l(screen+40*#) line_hi :192 dta h(screen+40*#) dl :59 dta $4f,a(screen+40*#),0,$4f,a(screen+40*#),0
The follownig binary operators are supported:
The following unary operators are supported:
The operator precedence is following:
first | [] | (brackets) |
+ - ~ < > | (unary) | |
* / % & << >> | (binary) | |
+ - | ^ | (binary) | |
= == <> != < > <= >= | (binary) | |
! | (unary) | |
&& | (binary) | |
last | || | (binary) |
Note that although the operators are similar to those used in C, C++ and Java, their priorities are different than in these languages.
Compare and logical operators assume that zero is false and a non-zero is true. They return 1 for true.
While calculating an expression, signed 32-bit arithmetic is used. When range of 32 bits is exceeded, an 'Arithmetic overflow' error occurs.
five equ 5 here equ *
opt l- listing off opt l+o- listing on, object file off opt f+g+h- useful for Atari 5200 cartridges - raw output format, 5200 hw regs
org $600 org f:$700 table org *+100In the latter example table points to 100 bytes of uninitialized data (label is assigned to * before the ORG directive is executed).
Starting with version 2.6.0, xasm supports code that is relocated in the memory at runtime. Let's say you want your code to be located on the zero page. You can't normally load it directly into this place, so you load it at a different address and then move in your program. org r: changes the address that it used for code generation but not the address used for generating Atari executable headers. Example:
org $8000 ldx #code_length-1 mva:rpl code_loaded,x z:code_zpage,x- jmp code_zpage code_loaded org r:$30 code_zpage jmp * ; ... or something more sensible code_length equ *-code_zpage
Note that both * and label definitions use the counter used for code generation. There is no direct access to the other counter, because I think this is not useful. If you really need it, you can always type something like:
where_am_i equ *-code_zpage+code_loaded
dta b(1,2),3,a(1000,-1),l(12345,sin(0,127,256)) dta d"ANTIC"*,c'It''s a string',$9b
icl 'macros.asx' icl 'lib/fileio'Note: for portability, you should use only relative paths and slash as the separator. This assures that your sources will compile under Windows and Linux.
end
ins 'file'[,offset[,length]]The first byte in a file has the offset of zero.
ins 'picture.raw' ins 'file',-256 insert last 256 bytes of file ins 'file',10,10 insert bytes 10..19 of file
run addris equivalent to:
org $2e0 dta a(addr)Example:
run main
ini showpic
ert *>$c000 ert len1>$ff||len2>$ff
noscr equ 1 widescr equ 1 ift noscr lda #0 eli widescr lda #$23 els lda #$22 eif sta $22fThe above example can be rewritten using the line repeating feature:
noscr equ 1 widescr equ 1 :noscr lda #0 :!noscr&&widescr lda #$23 :!noscr&&!widescr lda #$22 sta $22f
Pseudo-commands are built-in macros.
ldx #0 mva:rne $500,x $600,x+The example code copies memory $500-$5ff to $600-$6ff. Here is the same written with standard 6502 commands only:
ldx #0 loop lda $500,x sta $600,x inx bne loop
lda #40 add:sta $80 scc:inc $81In the above example the word-sized variable $80 is incremented by 40.
jne destis equivalent to:
seq:jmp dest
inw destis equivalent to:
inc dest sne:inc dest+1
mva source dest = lda source : sta dest mvx source dest = ldx source : stx dest mvy source dest = ldy source : sty dest
mv* source dest mv* source+1 dest+1When source is an immediate value, an mw* #immed dest expands to:
mv* <immed dest mv* >immed dest+1When <immed equals >immed and immed is not forward-referenced, xasm skips the second LD*:
mv* <immed dest st* dest+1If possible, MWX and MWY use increment/decrement commands. For example, mwx #1 dest expands to:
ldx #1 stx dest dex stx dest+1
All addressing modes are entered in the standard 6502 convention except for the accumulator addressing mode, which should be marked with the @ character (as in Quick Assembler).
There are two extra immediate addressing modes: < and >, which use the low/high byte of a 16-bit word constant. They are for Quick Assembler compatibility. You can use traditional #< and #>. Note that lda >$ff+5 loads 1 (>$104), while lda #>$ff+5 loads 5 (0+5) to the accumulator, because the unary operator > has a higher priority than the binary plus.
You can explicitly choose absolute (a:) and zero-page (z:) addressing modes.
Examples:
nop asl @ lda >$1234 assembles to lda #$12 lda $100,x lda 0 zero-page (8-bit address) lda a:0 absolute (16-bit address) jmp ($0a) lda ($80),y
There are pseudo addressing modes, which are similar to pseudo-commands. You may use them just like standard addressing modes in all 6502 commands and pseudo-commands, except for MWA, MWX and MWY:
cmd a,x+ = cmd a,x : inx cmd a,x- = cmd a,x : dex cmd a,y+ = cmd a,y : iny cmd a,y- = cmd a,y : dey cmd (z),y+ = cmd (z),y : iny cmd (z),y- = cmd (z),y : dey cmd (z,0) = ldx #0 : cmd (z,x) cmd (z),0 = ldy #0 : cmd (z),y cmd (z),0+ = ldy #0 : cmd (z),y : iny cmd (z),0- = ldy #0 : cmd (z),y : dey
xasm -i -d DEBUG=1 -l listing.lst source.asx
ift 0 foo equ 1 ift foo eif eif(bug found by Adrian Matoga)
ift 0 :label nop eifreported Label not defined before error for the repeat count
ift 0 label eifreported No ORG specified error for the label definition
Piotr Fusik (fox@scene.pl)
xasm home page (http://xasm.atari.org)