X-Assembler version 2.3

by Fox/Taquart


TABLE OF CONTENTS


INTRODUCTION

The X-Assembler is a cross-assembler generating code for the 6502 processor. It has been designed to be easy to use for Quick Assembler programmers, therefore its syntax is an extension of QA's one.

Changes

Version 2.3

Version 2.2

Version 2.0

Version 1.2


USAGE

System requirements

Source code requirements

Converting Quick Assembler files

Because of possible editor-associated problems you had better convert an Atari text file to a regular PC text file, i.e. EOLs from $9b to $0d/$0a and ATASCII specific characters to their integer representation.
You have to change all OPT directives, but usually you only need to remove them.

Assembling a source program

The common syntax of invoking command line is following:
XASM source [options]
source is the name of source file. If no extension is given, the .ASX is implied.

Default action on invoking without options is to compile writing to a file with the .OBX extension.

Assembling options

Following options are supported:
/c
Enable listing false conditionals.
By default lines skipped due to false condition are not listed.

/e
Enable setting environment variables pointing to the error location.
With this option, X-Asm sets two environment variables: ERRFILE and ERRLINE. They may be used in a batch file to locate error and set editor's insertion point on it. For example, in my batch file I use such a construction:
XASM %1 /e
IF NOT ERRORLEVEL 1 GOTO ok
NCE +%ERRLINE% %ERRFILE%
NCE stands for Norton Classic Editor.

If there was no error, variables point at last issued warning. If no warning occured, they are removed from the environment.

/i
Disable listing included source. Only main source file will be listed.

/l[:fname]
Enable generating listing. If no fname given, listing is written to source.lst.

/n
Check source and object modification times and assemble only if source is newer than object file. X-Asm does NOT check included nor inserted files but only main source, so be careful with this option.

/o:fname
Specify object name. Default is source.obx.

/s
Disable converting spaces to tabs in listing. Using tabs makes listing file shorter.
Tab stops are assumed to be every 8 characters.

/t[:fname]
List label table. If no fname given, table is written at the end of listing or to source.tab.

If source is incorrect, X-Asm stops on first encountered error.

Exit codes

Meaning of exit codes returned by X-Asm:
3 = bad parameters, assembling not started
2 = error occured
1 = warning(s) only
0 = no errors, no warnings

Listing structure

Line of listing includes:

Label table structure

Line of label table includes:

SYNTAX

Fields

Source code is line-oriented. Every line of source consists of fields. Same sequence of characters used in different fields has completely different meaning. Fields are separated with one or more blank characters. There can't be any space within a field excluding strings and comments.

There are following types of fields:

Every line of source must match one of two general forms: In the first form, both fields are optional. Blank lines are ignored of course. Comment must here start with one of these characters: ; | or * (semicolon, pipe or asterisk). Any information in the line following such character is ignored.

In the latter form, you must use a minimum of an instruction field. It depends on the instruction, how many operands it takes. Every instruction takes constant number of operands, therefore there's no need to use a special character at the start of a comment, because after succesfully taking operands, X-Asm discards the remaining part of line.

Label field

This field is optional. It must start at first character in line, without any blank characters before. The purpose of using label field is to define a label.
Label is a symbol representing an integer of range -$ffff..$ffff.
Name of a label can contain letters, digits and underscores (_). Digit can not be label's first character. Name of a label can be as long as you want and all the characters are meaningful. In Quick Assembler only 6 leading characters were recognized and some sources may not compile under X-Asm for this reason.
Defining a label without using EQU makes it equal to current value of the origin counter.
Label can't be redefined.

Repeat count

Repeating means assembling single line several times as if there were several identical lines. Note it is not just duplicating bytes written to object.
Repeat count, which can be any valid expression, has to be preceded with a colon.
Examples:
:4 asl @
:2 dta a(*)
In the latter example each DTA has different operand value.
If repeat count equals zero, remaining part of line is not assembled. This allows conditional assembly on single line to be more compact.

Instruction field

If this is the first field in a line, the line must start with at least one blank character. Instruction field consists of one or two instructions. The latter case is called instructions pairing, because a pair of instructions have shared operand. You separate instructions with a colon.
Example:
 adc:sta $80
is equivalent to
 adc $80
 sta $80
Note that
 lda:tax $80
is correct, because $80 is a comment for TAX.

Single instruction always consists of 3 letters. It can be:

  1. 6502 command - standard mnemonics are used
  2. directive
  3. pseudo-command

Operand field

Some instructions don't need any operand, other need two operands.
6502 commands require operand with proper
addressing mode.

Comment

Comment in a statement does not start from any special character like ; for example. Comment field is implied when appropriate number of operands was taken.


Expressions

Expressions are numbers combined with operators and brackets. You should use square brackets, because parentheses are reserved for 6502 indirect addressing.

Numbers

Numbers are 32-bit signed integers, in the range of -$7fffffff..$7fffffff. A number can be:
  • a decimal number
-12345
  • a hexadecimal number
$abcd
  • a binary number
%10100101
  • an ASCII character
'a' or "a"
  • an origin counter
*
  • a hardware register
^31
An abbreviation of Atari hardware register:
^0x means $d00x
^1x means $d01x
^2x means $d20x
^3x means $d30x
^4x means $d40x
where x is a hexadecimal digit.
  • an op-code
{lda #0}
Byte op-code of instruction inside braces. Operand is discarded and is necessary only for identifying addressing mode. Instruction should begin right after left brace and right brace should immediately follow the operand or the command in implied addressing mode.
You can skip the operand, if the addressing mode is fixed. Examples:
{lda #}, {jsr}, {bne}, {jmp ()}, {sta a:,x}.

Operators

Binary operators:

+ Addition
- Subtraction
* Multiplication
/ Division
% Remainder
& Bitwise and
| Bitwise or
^ Bitwise xor
<< Arithmetic shift left
>> Arithmetic shift right
= Equal
<> Not equal
!= Not equal (same as <>)
< Less than
> Greater than
<= Less or equal
>= Greater or equal
&& Logical and
|| Logical or

Unary operators:

+Plus (does nothing)
-Minus (changes sign)
~Bitwise not (complements all bits)
!Logical not (changes true to false and vice versa)
<Low (extracts low byte)
>High (extracts high byte)

Operator precedence:

first[](brackets)
+ - ~ < >(unary)
* / % & << >>(binary)
+ - | ^(binary)
= <> != < > <= >=(binary)
!(unary)
&&(binary)
last ||(binary)

Operators are similar to used in C language, but their priorities are different than in C.

Compare and logical operators assume that zero is false and non-zero is true. They return 1 for true.

While calculating expression, signed 32-bit arithmetic is used. When range of 32 bits is exceeded, 'Arithmetic overflow' error is generated.


Directives

EQU - assign a value of an expression to the label
Note that label represents a number, not a text macro.
Examples:
five equ 5
here equ *
OPT - set assembling options
Currently there are two options: generating listing and generating headers. You can turn any of these on or off.
Default (if no OPT specified) is opt l+h+.
Examples:
 opt l-    listing off
 opt h-    headers off
 opt l+h-  listing on, headers off
ORG - set new origin counter
You can set some options applied to new header (if headers are on):
  • a: tells X-Asm to always make a header, even it is unnecessary, like in ORG *.
  • f: works same as a:, but additionally tells to generate a $ff,$ff prefix before header. X-Asm adds it at the beginning of file by default, so use this option only if you want the $ff's somewhere inside.
Examples:
 org $600
 org f:$700
table org *+100
In the latter example table points to 100 bytes of uninitialized data (label is assigned to * before ORG directive is executed).

DTA - define data
There are various data types:
  • integers
    • bytes: b(200) or simply 200
    • words: a(10000)
    • low bytes of words: l(511) defines byte 255
    • high bytes of words: h(511) defines 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 define a sinus table. Syntax is:
    sin(centre,amp,size,first,last)
    where:
    • centre is a number which is added to every sinus value
    • amp is the amplitude of sinus
    • size is the period of sinus
    • first,last define range of values in the table. They are optional. Default are 0,size-1.
    Example: dta a(sin(0,1000,256,0,63)) defines table of 64 words representing a quarter of sinus with amplitude of 1000.

  • real numbers: r(-1.23456e12)
    Real numbers are written in 6-byte Atari Floating-Point format. You can't combine reals with operators, as you can integers.

  • text strings
    • ASCII strings: c'Text' or c"Text"
    • ANTIC strings: d'Text' or d"Text"
    A character string consists of any of characters surrounded by quotation marks. Within a string, a single quotation mark character is represented by two succesive quotation marks.
    Placing a * character after a string inverts high bit in every byte of 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 'c:\atari\xasm\fileio'
END - end assembling file
Remaining part of the file is not assembled. If this statement does not occur, assembler stops assembling when encounters end of file.
Example:
 end
INS - insert contents of file
Copies every byte of specified file into object file and moves origin counter, as if these bytes were defined with DTA.
You may specify range of inserted file. Syntax is following:
 ins 'file'[,offset[,length]]
First byte in file has offset 0.
If offset is negative, it is counted from the end of 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 - generate run address
The Atari executable program should have run address specified. A program may be loaded in many areas of memory and started from any address.
 run addr
is equivalent to:
 org $2e0
 dta a(addr)
Examples:
 run start
 run program
INI - generate init address
The Atari executable program may have some routines which are executed during loading process. There may be many init blocks in one file.
Examples:
 ini init
 ini showpic
ERT - generate error if expression is true
Examples:
 ert *>$c000
 ert len1>$ff||len2>$ff
IFT - assemble if expression is true
ELS - else
EIF - end if
With these directives you can construct fragments which are assembled when a condition is met. Conditional constructions can be nested.
Example:
noscr equ 1
 ift noscr
 lda #0
 els
 lda #$22
 eif
 sta $22f
Above example can be rewritten using line repeating feature:
noscr equ 1
:noscr lda #0
:!noscr lda #$22
 sta $22f

Pseudo-commands

Pseudo-commands are built-in macros. They are not illegal instructions and work on typical 6502.

ADD - addition without carry
If you have ever programmed 6502, you must have noticed that you had to use a CLC before ADC for every simple addition.
X-Asm can do it for you. ADD replaces two instructions: CLC and ADC.

SUB - subtraction
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 previously assembled instruction.
Example:
 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
SCC, SCS, SEQ, SMI, SNE, SPL, SVC, SVS - conditional skip
These are branches over the next instructions. No operand is required, because the target is the address of instruction following the next instruction.
Example:
 lda #40
 add:sta $80
 scc:inc $81
In the above example word-sized variable $80 is incremented by 40.
Nor conditional repeat nor skip pseudo-commands require operand, thus they can be paired with any other command.

JCC, JCS, JEQ, JMI, JNE, JPL, JVC, JVS - conditional jumps
They are a kind of 'long' branches. While standard branches (BNE, BEQ) have range of -128..+127, these jumps have range of all 64 kB.
Example:
 jne dest
is equivalent to:
 seq:jmp dest
INW - increment word
It is a 16-bit memory increment command.
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 MV*'s: one to move low byte, and the other to move high byte.
You can't use indirect nor pseudo addressing mode with MW*. Destination must be absolute address (optionally indexed).
When source is also absolute, a MW* SOURCE DEST will be:
 mv* source   dest
 mv* source+1 dest+1
When source is an immediate, a MW* #IMMED DEST will be:
 mv* <immed dest
 mv* >immed dest+1
When <immed equals >immed and immed is not forward-referenced, X-Asm uses optimization:
 mv* <immed dest
 st* dest+1

Addressing modes

All addressing modes are entered in standard convention except the accumulator addressing mode, which should be marked with a @ character (as in Quick Assembler).

There are two extra immediate addressing modes: < and >, which use low/high byte of word rather than byte value. They are for Quick Assembler compatibility. You can use traditional #< and #>. Note lda >$ff+5 loads 1 (>$104), while lda #>$ff+5 loads 5 (0+5) to accumulator, because unary operator > has higher priority than plus has.

In absolute addressing modes, X-Asm examines expression and uses zero-page addressing mode if it supposes it is possible. You may override it with a: and z: prefixes.

Examples:

 nop
 asl @
 lda >$1234	assembles to lda #$12
 lda $100,x
 lda a:0	generates 16-bit address
 jmp ($0a)
 lda ($80),y
There are also 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 excluding MWA, MWX and MWY only:
 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

FAQ