XASM(1)

NAME
xasm - 6502 cross-assembler

SYNOPSIS

xasm [OPTIONS] SOURCE_FILE

DESCRIPTION

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). 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.

OPTIONS

/c
    Specifies that lines skipped due to a false condition should be included in the listing file.

/d:LABEL=VALUE
    Defines a label. LABEL should be a valid label name. VALUE may be any expression (may reference to labels defined in source files). You may use several /d options to define many labels from the command line.

/i
    Excludes included files from the listing file.

/l[:LISTING_FILE]
    Generates listing file. If LISTING_FILE is omitted, the listing filename is SOURCE_FILE with the extension changed to .lst.

/M
    Prints a rule for use in a Makefile. 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 directives) as dependencies. The second line contains the command line with OBJECT_FILE replaced by the make macro $@ and SOURCE_FILE replaced by the macro $<. Dollars in the command line are doubled. Your make or shell may require further escaping.

/o:OBJECT_FILE
    Sets output file name. The default is SOURCE_FILE with the extension changed to .obx.

/p
    Prints absolute paths in listing and error messages.

/q
    Quiet mode. Prevents xasm from printing its banner and compilation summary.

/t[:LABEL_FILE]
    Generates label table. If LABEL_FILE is omitted then the table is appended at the end of the listing.

/u
    Issues a warning message for each label whose value is unused.

Alternatively, you may use Unix-style options, for example:

    xasm -i -d DEBUG=1 -l listing.lst source.asx

SYNTAX

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-specific characters with their integer codes. You also have to update all OPT directives, but usually you can simply remove them.

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 (i.e. the address of the compiled instruction), unless you use it with the EQU directive where it is assigned the value of the EQU argument.

Instructions and directives must be preceded with some whitespace. Without leading whitespace they are treated as label names. For example:

     nop

is a 6502 instruction, whereas

    nop

(without leading space) defines a label called nop.

Whole-line comments must start with a semicolon, an asterisk or a pipe, with 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,8,4,2,1

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 a 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 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 #0

is allowed because #0 is treated as a comment for tax.

EXPRESSIONS

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"

    • origin counter: *

    • a hardware register (see below), e.g. ^4e

    • an opcode (see below), e.g. {lda #0} is $a9

    • 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. 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)

The operator precedence is following:

    • first: [] (brackets)

    • + - ~ < > (unary)

    • * / % & << >> (binary)

    • + - | ^ (binary)

    • = == <> != < > <= >= (binary)

    • ! (unary)

    • && (binary)

    • last: || (binary)

    Note

    Although the operators are similar to those used in C, C++ and Java, their priorities are different.

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. "Arithmetic overflow" error signals overflow of the 32-bit range.

DIRECTIVES

EQU - assign value of expression to label
    Examples:

        five equ 5
        here equ *

OPT - set assembler options
    Six options are available:

        • F - fill the space between memory areas with $FF

        • G - Atari 5200 mode for hardware register abbreviations

        • H - generate Atari executable headers

        • L - write to the listing

        • O - write to the object file

        • U - warn of unused labels

    You can turn any of these on or off. The default (if no OPT specified) is opt f-g-h+l+o+u+. Examples:

         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

ORG - change value of the origin counter
    If Atari executable headers are enabled, 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).

    Examples:

         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).

    Starting with version 2.6.0, xasm supports code that is relocated at run time. Let’s say you want your code to be located on page zero. 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. You can only calculate it:

        where_am_i equ *-code_zpage+code_loaded

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:

            sin(center,amp,period,first,last)

        where:

            • 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.

        Example:

             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. Examples:

         icl 'macros.asx'
         icl 'lib/fileio'

        Note
        for portability, use only relative paths and slash as the separator. This way your sources will compile under Windows and Linux.

END - end assembling file
    May be used if the source file ends with something which shouldn’t be read by xasm (e.g. your notes).

INS - insert contents of file
    Copies every byte of the specified file into the object file and updates the origin counter, as if these bytes were written using 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. Examples:

         ins 'picture.raw'
         ins 'file',-256  insert last 256 bytes of file
         ins 'file',10,10 insert bytes 10..19 of file

RUN - set run address in the Atari executable format

         run main

    is equivalent to:

         org $2e0
         dta a(main)

INI - set init address in the Atari executable format
    Example:

         ini showloadingpic

ERT - generate error if expression evaluates to true
    Examples:

         ert *>$c000
         ert len1>$ff||len2>$ff

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. Example:

        noscr equ 1
        widescr equ 1
         ift noscr
         lda #0
         eli widescr
         lda #$23
         els
         lda #$22
         eif
         sta $22f

        Note
        The above example may be rewritten 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

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 and SBC.

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. Example:

         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
         inx
         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. Example:

         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, 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. Example:

         inw dest

    is equivalent to:

         inc dest
         sne:inc dest+1

MVA, MVX, MVY - move byte using 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

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 commands. For example, mwx #1 dest expands to:

         ldx #1
         stx dest
         dex
         stx dest+1

ADDRESSING MODES

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).

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.

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

HISTORY

  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 /u unused label warnings (suggested by Marek Pavlik)

    • if the file to be included cannot be opened, report error in the ICL line (suggested by Peter Dell)

    • removed duplicate filenames for /M

    • implemented /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 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 D programming language - Linux version is now available and DOS is no longer supported

    • no limits for line length, number of ICLs, ORGs,IFTs and labels

    • Unix-style command-line options are supported

    • /e option is no longer supported

    • 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
         eif
         eif

    (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

    • support for code relocated at runtime

    • 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 (such as floppy not ready) 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, very similar to the previous one, e.g.

         ift 0
        :label nop
         eif

    reported "Label not defined before" error for the repeat count

    • OPT F+ causes ORG to fill the space between the old and the new location with $FFs

    • 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, e.g.

         ift 0
        label
         eif

    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 .EXE (.COM caused problems with DJGPP make due to a bug in the DJGPP runtime)

    • the assembler executable is not compressed (so it occupies less space in the ZIP)

    • improved command-line parsing: options may be used before source file name, tab character 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, which is equivalent to =, but more natural for C/C++/Java programmers

    • added /d:label=value option: define a label

    • added /p option: print full paths in listing and error messages

    • added /q option: quiet mode

    • added /u option: warn of unused labels

    • writing to the object file may be suppressed with OPT O-

    • added ELI (else if) directive

    • MWX and MWY may use INX/DEX and INY/DEY

  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

    • unary operators

    • DTA implied byte mode

    • 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 origin counter until it’s used

    • 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 source is newer than object file

    • opcode extracting

    • repeat line

    • two instructions in line

    • conditional repeat and skip pseudo commands

    • (),0+ and (),0- pseudo addressing modes

  Version 2.0 (1998-11-12)

    • fixed: name of object file 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

AUTHOR

Piotr Fusik <fox@scene.pl>

SEE ALSO

Website: https://github.com/pfusik/xasm