mirror of https://github.com/pfusik/xasm.git synced 2024-05-29 11:41:37 +00:00
2022-12-08 11:49:40 +01:00

926 lines
27 KiB

XASM (1)
:doctype: manpage
xasm - 6502 cross-assembler
*xasm* is a cross-assembler for the 6502 family processors.
'SOURCE_FILE' is the name of the source file
(you may omit the default `.asx` extension).
Using '-' as 'SOURCE_FILE' makes *xasm* read from standard input.
When invoked without any options, *xasm* assembles 'SOURCE_FILE'
and writes the result to an object file named 'SOURCE_FILE'
with the extension changed to `.obx`.
The listing should include conditionally skipped lines.
[[new_deflabel]]*-d* 'LABEL'='VALUE'::
Defines a label.
'LABEL' should be a valid label name.
'VALUE' may be any expression (potentially referencing labels defined in source files).
You may use several *-d* options to define many labels on the command line.
The listing file should exclude included files.
*-l* '[LISTING_FILE]'::
Generates a listing file.
Using '-' as 'LISTING_FILE' makes *xasm* write to standard output.
If 'LISTING_FILE' is omitted, the listing filename
is 'SOURCE_FILE' with the extension changed to `.lst`.
Prints a `Makefile` rule.
First line of the rule lists 'OBJECT_FILE' as the target of the rule
and all source files (including the ones specified with `icl` and `ins`) as dependencies.
The second line contains the command line with 'OBJECT_FILE'
replaced by the *make* macro `$@` and 'SOURCE_FILE' replaced by the macro `$<`.
Dollar signs in the command line are doubled.
Your *make* or shell may require further escaping.
*-o* 'OBJECT_FILE'::
Sets output file name.
Using '-' as 'OBJECT_FILE' makes *xasm* write to standard output.
The default is 'SOURCE_FILE' with the extension changed to `.obx`, or
standard output if standard input was specified as source.
Prints absolute paths in listing and error messages.
Quiet mode. Prevents *xasm* from printing its banner and compilation summary.
*-t* '[LABEL_FILE]'::
Generates a label table.
Using '-' as 'LABEL_FILE' makes *xasm* write to standard output.
If 'LABEL_FILE' is omitted then the table is appended at the end of the listing.
Issues warnings for unreferenced labels.
Alternatively, you may use DOS-style options, for example:
xasm /i /d:DEBUG=1 /l:listing.lst source.asx
These are deprecated because they are incompatible with https://www.msys2.org[MSYS2].
Source files should be plain ASCII files.
LF, CR, CR/LF and Atari ($9b) 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 characters
in string literals with the corresponding integers.
Also update all `OPT` directives, but often you can omit them.
A 'label' is a symbol that represents a signed 32-bit integer.
You define a label by putting its name at the beginning of a line
(with no spaces before).
The label will be assigned the current value of the 'origin counter'
(that is, the address of the compiled instruction),
unless you use it with the `EQU` directive to assign the specified value.
Any label name starting with a `?` (question mark) is a 'local label'.
It is implicitly prefixed with the name of the most recently defined
'global label' (that is, a label without any `?` in name),
and remains visible until another global label is defined.
It is still possible to access a local label from anywhere in the source
by specifying its full name.
Local labels provide a way to reuse common, short label names while keeping
them unique.
foo ldy #0
?loop lda data,y ; full label name is FOO?LOOP
beq ?ret
jsr sendByte
iny:bne ?loop
?ret rts
bar lda baz
beq foo?ret ; ok
bne ?loop ; ERROR: Undeclared label: BAR?LOOP
'Instructions' and 'directives' must be preceded with some whitespace.
Without leading whitespace they are treated as label names.
For example:
is a 6502 instruction, whereas
(without leading space) defines a label called `nop`.
Whole-line comments must start with a semicolon, an asterisk or a pipe,
with an optional label definition and spaces before.
Here are examples of whole-line comments:
; this is a comment
* so it is
label | and this too
Lines with instructions (and selected directives) may be 'repeated'.
To assemble a single line several times,
precede the repeat count with a colon, for example:
:4 asl @
mask_lookup :32 dta $80,$40,$20,$10,$08,$04,$02,$01
In lines with instructions or directives, a comment starts immediately
after the instruction/directive has been successfully parsed.
That is, in such lines *xasm* does 'not' require any special character
to start a comment.
lda foo ; this is a comment
sta bar this too
tax #0 tax has no operand, therefore #0 starts this comment
You may put two or more instructions in one line so they share their operand.
For example:
eor:sta foo
is equivalent to
eor foo
sta foo
Note that
lda:tax:tay #0
is allowed because `#0` is treated as a comment for `tax` and `tay`.
Expressions are numbers combined with operators and brackets.
You should use square brackets, because parentheses are reserved
for 6502 indirect addressing.
A number is:
- a 32-bit decimal integer, e.g. `12345`
- a 32-bit hexadecimal integer, e.g. `$abcd`
- a 32-bit binary integer, e.g. `%10100101`
- an ASCII character, e.g. `'a'` or `"a"`
- the current value of the origin counter: `*`
- a hardware register (see below), e.g. `^4e`
- [[new_opcode]]an opcode (see below), e.g. `{lda #0}` is `$a9`
- [[new_linecnt]]the current value of the line repeat counter (see below): `#`
Abbreviations of Atari hardware registers are provided
to save two characters (`$d40e` vs `^4e`)
and to facilitate 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|Value in Atari 5200 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`
The opcode syntax represents the opcode byte of the instruction inside braces.
The operand of the instruction is discarded and is needed 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.
[[new_op_op]]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 following 'binary operators' are supported:
- `+` Addition
- `-` Subtraction
- `*` Multiplication
- `/` Division
- `%` Remainder
- `&` Bitwise AND
- `|` Bitwise OR
- `^` Bitwise XOR
- `<<` Arithmetic shift left
- `>>` Arithmetic shift right
- `==` Equal
- `=` Equal (same as `==`)
- `!=` Not equal
- `<>` Not equal (same as `!=`)
- `<` Less than
- `>` Greater than
- `<=` Less or equal
- `>=` Greater or equal
- `&&` Logical AND
- `||` Logical OR
The following 'unary operators' are supported:
- `+` Plus (no operation)
- `-` Minus (changes the sign)
- `~` Bitwise NOT (complements all bits)
- `!` Logical NOT (changes true to false and vice versa)
- `<` Low (extracts the low byte)
- `>` High (extracts the high byte)
Although the operators are similar to those used in C, C++, C# and Java,
their precedence is different:
- first: `[]` (brackets)
- `+ - ~ < >` (unary)
- `* / % & << >>` (binary)
- `+ - | ^` (binary)
- `= == <> != < > <= >=` (binary)
- `!` (unary)
- `&&` (binary)
- last: `||` (binary)
The compare and logical operators assume that zero is false
and a non-zero is true. They return 1 for true.
Expressions are calculated in signed 32-bit arithmetic.
An overflow is signalled with an "Arithmetic overflow" error.
*EQU* - assign the value of an expression to a label::
five equ 5
here equ *
[[new_opt]]*OPT* - set assembler options::
Six options are available:
- `F` - fill the space between noncontiguous memory areas with `$FF` bytes
- `G` - Atari 5200 mode for hardware register abbreviations (`^xx`)
- `H` - generate Atari executable headers
- `L` - write the listing
- `O` - write the object file
- `U` - warn of unused labels
You can turn any of these on or off.
The default (before the first `OPT`) is `opt f-g-h+l+o+u+`.
For compatibility with MADS, `opt ?+` is accepted and ignored.
opt l- listing off
opt l+o- listing on, object file off
opt f+g+h- useful for Atari 5200 cartridges - raw output, 5200 hw regs
opt ?+ MADS compatibility, no effect
*ORG* - set the origin counter::
If Atari executable headers are enabled (`opt h+`), you can include an operand prefix:
- `a:` starts a new block even if it's superfluous
because the new address equals the current address.
- `f:` is same as `a:`, but additionally generates a double `$FF` prefix
before the new header. This prefix is automatically generated
at the beginning of the file (no need to include `f:` in the first `ORG`).
org $600
org f:$700
table org *+100
In the latter example `table` points to 100 bytes
of uninitialized data (label is assigned with `*`
before the `ORG` directive is executed).
[[new_orgr]]*xasm* supports code that is relocated at run time.
Let's say you want your code to be located on page zero for best performance.
You can't safely 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.
org $8000
ldx #code_length-1
mva:rpl code_loaded,x z:code_zpage,x-
jmp code_zpage
org r:$30
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.
You can only calculate it:
where_am_i equ *-code_zpage+code_loaded
[[new_dta]]*DTA* - define data::
- integers
* bytes: `b(200)` or simply `200`
* words: `a(10000)`
* low bytes of words: `l(511)` (byte 255)
* high bytes of words: `h(511)` (byte 1)
You may enter many expressions in parentheses and combine different types
of data in single line, separating things with commas.
You may also build a sine lookup table. The syntax is:
* `center` is an integer which is added to every sine value
* `amp` is the sine amplitude
* `period` is the number of values per sine period
* `first,last` define the range of sine arguments.
They are optional. The default are `0,period-1`.
dta a(sin(0,1000,256,0,63))
defines a table of 64 words representing a quarter of sine with the amplitude of 1000.
- real numbers: `r(-1.23456e12)`
Real numbers are stored in the 6-byte Atari Floating-Point format.
- text strings
* ASCII strings: `c'Text'` or `c"Text"`
* ANTIC strings: `d'Text'` or `d"Text"`
A character string consists of any number of characters surrounded by quotation
marks. You can include the quotation marks in the string by doubling them.
Placing a `*` character after a string inverts
the highest bit in every byte of the string.
Examples of `DTA`:
dta b(1,2),3,a(1000,-1),l(12345,sin(0,127,256))
dta d"ANTIC"*,c'It''s a string',$9b
*ICL* - include another source file::
Specifies another file to be included in the assembly as if the contents
of the referenced file appeared in place of the `ICL` statement.
The included file may contain other `ICL` statements.
The `.asx` extension is added if none given.
icl 'macros.asx'
icl 'lib/fileio'
NOTE: For Windows/macOS/Linux portability use relative paths
and slashes as path separators.
*END* - end this source file::
May be used if the source file ends with something which shouldn't
be read by *xasm* (e.g. your notes).
*INS* - insert binary file contents::
Copies every byte of the specified file into the object file and updates
the origin counter, as if these bytes were specified in a `DTA`.
You may specify a range of the file to insert. The syntax is:
ins 'file'[,offset[,length]]
The first byte in a file has the offset of zero.
If the offset is negative, it counts from the end of the file.
ins 'picture.raw'
ins 'file',-256 ; insert last 256 bytes of file
ins 'file',10,10 ; insert bytes 10..19 of file
*RUN* - set the Atari executable run address::
run main
is equivalent to:
org $2e0
dta a(main)
*INI* - set the Atari executable init address::
ini showloadingpic
*ERT* - abort the assembly with an error if an expression is true::
ert *>$c000
ert len1>$ff||len2>$ff
[[new_eli]]*IFT* - assemble if expression is true::
*ELI* - else if::
*ELS* - else::
*EIF* - end if::
With these directives you can construct fragments which
are assembled only when a condition is met.
Conditional constructions can be nested.
noscr equ 1
widescr equ 1
ift noscr
lda #0
eli widescr
lda #$23
lda #$22
sta $22f
NOTE: Alternatively, the above example can be written using the 'repeat line' 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. There are 'no' user-defined macros in *xasm*.
*ADD* - add without carry::
If you have ever programmed a 6502, you must have noticed that you had
to use a `CLC` before `ADC` for every simple addition.
*xasm* can do it for you. `ADD` replaces two instructions: `CLC` and `ADC`.
*SUB* - subtract::
It is `SEC` followed by `SBC`.
[[new_repskip]]*RCC, RCS, REQ, RMI, RNE, RPL, RVC, RVS* - conditional repeat::
These are branches to the previous instruction.
They take no operand, because the branch target is the address
of the previously assembled instruction or pseudo command.
ldx #0
mva:rne $500,x $600,x+
The above code copies a 256-byte memory block from $500 to $600.
Here is the same written with standard 6502 commands only:
ldx #0
copy_loop lda $500,x
sta $600,x
bne copy_loop
*SCC, SCS, SEQ, SMI, SNE, SPL, SVC, SVS* - conditional skip::
These are branches over the next instruction. No operand is required,
because the target is the address of the instruction following
the next instruction.
lda #40
add:sta ptr
scc:inc ptr+1
In the above example the 16-bit variable `ptr` is incremented by 40.
*JCC, JCS, JEQ, JMI, JNE, JPL, JVC, JVS* - conditional jump::
These are long branches. While standard branches (such as `BNE`)
have range of -128..+127 bytes, these jumps have range of 64 KB.
For example:
jne dest
is equivalent to:
seq:jmp dest
*INW* - increment word::
Increments a 16-bit word in the memory.
inw dest
is equivalent to:
inc dest
sne:inc dest+1
*MVA, MVX, MVY* - move a byte using the accumulator, X or Y::
Each of these pseudo commands requires two operands
and substitutes two commands:
mva source dest = lda source : sta dest
mvx source dest = ldx source : stx dest
mvy source dest = ldy source : sty dest
[[new_mwinde]]*MWA, MWX, MWY* - move word using accumulator, X or Y::
These pseudo commands require two operands and are combinations of two `MVA`/`MVX`/`MWY`:
one to move the low byte, and the other to move the high byte.
You can't use indirect nor pseudo addressing mode with `MWA`/`MWX`/`MWY`.
Destination must be an absolute or zeropage address, optionally indexed.
When source is also an absolute or zeropage address, an `mwa source dest` expands to:
mva source dest
mva source+1 dest+1
When source is an immediate value, an `mwa #immed dest` expands to:
mva <immed dest
mva >immed dest+1
When `<immed` equals `>immed` and `immed` is not forward-referenced,
*xasm* skips the second `LDA`, generating the following code:
mva <immed dest
sta dest+1
If possible, `MWX` and `MWY` use increment/decrement instructions.
For example, `mwx #1 dest` expands to:
ldx #1
stx dest
stx dest+1
Addressing modes are entered in the standard 6502 convention.
An exception is the accumulator mode marked with the `@` character
for compatibility with Quick Assembler.
Also for Quick Assembler compatibility there are two extra immediate
addressing modes: `<` and `>`, which use the low/high byte of a 16-bit word constant.
Unlike in Quick Assembler, you can alternatively use
the more common syntax: `#<` and `#>`.
Note the difference:
lda >$ff+5 ; loads 1 (>$104)
lda #>$ff+5 ; loads 5 (0+5)
You can explicitly specify absolute (`a:`) and zero-page (`z:`) addressing modes.
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
Version 3.2.1 (2022-12-08)
- source can optionally be read from standard input, and object can be
written to standard output instead of files (by Adrian Matoga)
- <<new_pairing,any number of instructions in one line>> (by Adrian Matoga)
Version 3.2.0 (2021-06-22)
- <<new_locallabel,local labels>> (contributed by Adrian Matoga)
- fixed emitted bytes not shown in the listing after `OPT L+` (by Adrian Matoga)
- emit shorter code for moving a negative immediate value with `MWX`/`MWY`
- Windows binary signed
- macOS binary signed, notarized, installed in `/usr/local/bin`
Version 3.1.1 (2019-11-20)
- remove partial object file on error (requested by Bill Kendrick)
- report label name in the unused label warning (requested by Peter Dell)
- support `a:`/`f:` modifiers for `RUN`/`INI`
- improved performance with network drives (reported by Marek Pavlik)
Version 3.1.0 (2014-07-20)
- OS X, Ubuntu and Fedora distributions
- `INS` can be repeated (suggested by Marek Pavlik) and taken "opcode" of
- `OPT U-` disables <<new_unlabels,*/u*>> unused label warnings
(suggested by Marek Pavlik)
- if the file to be included cannot be opened, report an error in the `ICL` line
(suggested by Peter Dell)
- removed duplicate filenames for <<new_makefile,*/M*>>
- implemented <<new_fullpaths,*/p*>> outside Windows
- source code updated from D1 to D2
- project moved to GitHub
Version 3.0.2 (2009-10-17)
- fixed "Branch out of range" error message - was overstated by 256 bytes
for backward branches
- <<new_makefile,new command-line option */M* prints a Makefile rule>>
- command-line options are now case-insensitive
- on Windows, error messages are printed in red, warnings in yellow
Version 3.0.1 (2007-04-22)
- fixed a bug in `OPT H-` mode
- made *xasm* compilable with the latest D compiler v1.010
(there were incompatible changes in the D language and library)
Version 3.0.0 (2005-05-22)
- rewritten from the x86 assembly language to the
http://dlang.org/[D programming language] - Linux version
is now available and DOS is no longer supported
- no limits on line length, number of `ICLs`, `ORGs`,`IFTs` and labels
- Unix-style command-line options are supported
- the */e* option is removed
- the label table is now sorted alphabetically
Version 2.6.1 (2005-05-21)
- no more "Arithmetic overflow" and "Division by zero" errors for correct
use of forward-referenced labels (bug found by Marcin Lewandowski)
- an error was reported in the following correct code:
ift 0
foo equ 1
ift foo
(bug found by Adrian Matoga)
- errors for non-existing `INC @` and `DEC @`
- negative numbers fixed in the listing
Version 2.6.0 (2005-02-07)
- long file names are supported under Windows
- <<new_orgr,support for code relocated at runtime>>
- <<new_linecnt,line repeat counter>>
- label values are now 32-bit, not just 17-bit
- command-line options */n* and */s* are no longer supported
- fatal I/O errors no longer print the annoying "Abort, Retry, Ignore" message
Version 2.5.2 (2002-10-03)
- version 2.5.1 broke Unix EOLs - fixed
- version 2.5.1 omitted all blank/comment/label lines, unless */c* was used
Version 2.5.1 (2002-08-21)
- fixed assembling sources with Atari EOLs
- blank/comment/label lines in false conditionals are now correctly omitted
in listing
Version 2.5 (2002-07-08)
- fixed another bug similar to the previous one, for example:
ift 0
:label nop
reported "Label not defined before" error for the repeat count
- <<new_opt,`OPT F+` causes `ORG` to fill the space
between the old and the new location with `$FFs`>>
- <<new_opt,`OPT G+` enables Atari 5200 mode for hardware
register abbreviations>>
Version 2.4.1 (2002-06-27)
- fixed a bug related to label definitions in conditionally skipped code,
ift 0
reported "No ORG specified" error for the label definition
Version 2.4 (2002-05-22)
- fixed incorrect unary operator precedence
- fixed wrong label value after a skip pseudo command
- the assembler is an .EXE (.COM caused problems with DJGPP *make* due
to a bug in the DJGPP runtime)
- the assembler executable is no longer compressed
- improved command-line parsing: options may be used before the source file name,
tab is a valid separator, slash may be used as a directory separator
- error and warning messages are written to stderr, not stdout
- added `==` (equals) operator, equivalent to `=`, but familiar to C/C++/Java programmers
- <<new_deflabel,added `/d:label=value` option: define a label>>
- <<new_fullpaths,added `/p` option: print full paths
in listing and error messages>>
- <<new_quiet,added `/q` option: quiet mode>>
- <<new_unlabels,added `/u` option: warn of unused labels>>
- <<new_opt,writing to the object file may be suppressed with `OPT O-`>>
- <<new_eli,added `ELI` (else if) directive>>
- <<new_mwinde,`MWX` and `MWY` may use `INX`/`DEX` and `INY`/`DEY`,
respectively, for generating shorter code>>
Version 2.3 (2002-02-10)
- fixed double skip (e.g. `SCC:SNE`)
- fixed real numbers with two-digit exponent
- trailing spaces are trimmed from listing lines
- label definitions allowed in blank, comment and repeated lines
- <<new_unary,unary operators>>
- <<new_dta,`DTA` implied byte mode>>
- <<new_op_op,operand can be skipped for some opcodes>>
Version 2.2 (1999-09-10)
- fixed invalid opcodes of absolute `CPX` and `CPY`
- fixed: addressing mode not checked for branch commands
- fixed `ICL` in last line
- fixed `OPT H-H+`
- fixed first `ORG *`
- no need to set the origin counter until it's needed
- allow Unix, Macintosh and Atari EOLs
- value of 'true' changed to 1
- command-line option to set environment variables on error
- commane-line option to assemble only if the source is newer than the object file
- <<new_opcode,opcode extracting>>
- <<new_linerep,repeat line>>
- <<new_pairing,two instructions in line>>
- <<new_repskip,conditional repeat and skip pseudo commands>>
- <<new_adrmodes,`(),0+` and `(),0-` pseudo addressing modes>>
Version 2.0 (1998-11-12)
- fixed: object filename was truncated
- fixed forward references in `EQU` and `DTA`
- fixed hex numbers
- `.OBX` is now the default extension for the object file
- options (command-line switches and `OPT`)
- listing
- label table
- conditional assembly
- user errors (`ERT`)
- warnings
- 6 new pseudo commands (memory-to-memory move)
- 8 pseudo addressing modes
- indirect conditional jumps
- Atari floating-point numbers
- object file headers optimization
- improved expressions - 19 operators and brackets, 32-bit arithmetic
- improved signed numbers
- improved `INS`: inserting specified part of file
Version 1.2 (1998-08-14)
- first release
Piotr Fusik <fox@scene.pl>
Website: https://github.com/pfusik/xasm[]