64tass v1.5x r484 manual This is the manual for 64tass, the multi pass optimizing macro assembler for the 65xx series of processors. Key features: * Open source, mostly portable C * Familiar syntax to Omicron TASS and TASM. * Supports 6502, 65C02, R65C02, W65C02, 65CE02, 65816, DTV, 65EL02 * Integer, floating point, string and array arithmetic available * Handles UTF-8, UTF-16 and 8 bit RAW encoded sources and strings * Built in `linker' with section support * CPU or flat address space for creating huge binaries (e.g. cartridges) * Conditional compilation, macros, struct/union structures, scopes. This is a development version, features or syntax may change over time. Not everything is backwards compatible. Project page: http://sourceforge.net/projects/tass64/ ------------------------------------------------------------------------------- Usage tips 64tass is a command line assembler, the source can be written in any text editor. As a minimum the source filename must be given on the command line. The `-a' parameter is highly recommended if the source is Unicode or ASCII. 64tass -a src.asm There are also some useful parameters which are described later. For comfortable compiling I use such `Makefile's (for make): demo.prg: source.asm makros.asm pic.drp music.bin 64tass -C -a -B -i source.asm -o demo.tmp pucrunch -ffast -x 2048 demo.tmp >demo.prg This way `demo.prg' is recreated by compiling `source.asm' whenever `source.asm', `makros.asm', `pic.drp' or `music.bin' had changed. Of course it's not much harder to create something similar for win32 (make.bat), however this will always compile and compress: 64tass.exe -C -a -B -i source.asm -o demo.tmp pucrunch.exe -ffast -x 2048 demo.tmp >demo.prg Here's a slightly more advanced Makefile example with default action as testing in VICE, clean target for removal of temporary files and compressing using an intermediate temporary file: all: demo.prg x64 -autostartprgmode 1 -autostart-warp +truedrive +cart $< demo.prg: demo.tmp pucrunch -ffast -x 2048 $< >$@ demo.tmp: source.asm makros.asm pic.drp music.bin 64tass -C -a -B -i $< -o $@ .INTERMEDIATE: demo.tmp .PHONY: all clean clean: $(RM) demo.prg demo.tmp It's useful to add a basic header to your source files like the one below, so that the resulting file is directly runnable without additional compression: *= $0801 .word (+), 2005 ;pointer, line number .null $9e, ^start;will be sys 4096 + .word 0 ;basic line end *= $1000 start rts A frequently comming up question is, how to automatically allocate memory, without hacks like *=*+1? Sure there's .byte and friends for variables with initial values but what about zero page, or RAM outside of program area? The solution is to not use an initial value by using '?' or not giving a fill byte value to .fill. *= $02 p1 .word ? ;a zero page pointer temp .fill 10 ;a 10 byte temporary area Space allocated this way is not saved in the output as there's no data to save at those addresses. What about some code running on zero page for speed? It needs to be relocated, and the length must be known to copy it there. Here's an example: ldx #size(zpcode)-1;calculate length - lda zpcode,x sta wrbyte,x dex ;install to zeropage bpl - jsr wrbyte rts ;code continues here but is compiled to run from $02 zpcode .logical $02 wrbyte sta $ffff ;quick byte writer at $02 inc wrbyte+1 bne + inc wrbyte+2 + rts .here The assembler supports lists and tuples, which does not seems interesting at first as it sound like something which is only useful when heavy scripting is involved. But as normal arithmetic operations also apply on all their elements at once, this could spare quite some typing and repetition. Let's take a simple example of a low/high byte jump table of return addresses, this usually involves some unnecessary copy/pasting to create a pair of tables with constructs like >(label-1). jumpcmd lda hibytes,x ; selected routine in X register pha lda lobytes,x ; push address to stack pha rts ; jump, rts will increase pc by one! ; Build an anonymous list of jump addresses minus 1 - = (cmd_p, cmd_c, cmd_m, cmd_s, cmd_r, cmd_l, cmd_e)-1 lobytes .byte <(-) ; low bytes of jump addresses hibytes .byte >(-) ; high bytes There are some other tips below in the descriptions. ------------------------------------------------------------------------------- Expressions and data types Integer constants Integer constants can be entered as decimal ([0-9]+), hexadecimal ($[0-9a-f]*) or binary (%[01]*). The following operations are accepted: Integer operators and functions x + y add x to y 2 + 2 is 4 x - y substract y from x 4 - 1 is 3 x * y multiply x with y 2 * 3 is 6 x / y integer divide x by y 7 / 2 is 3 x % y integer modulo of x divided by y 5 % 2 is 1 x ** y x raised t power of y 2 ** 4 is 16 -x negated value -2 is -2 +x unchanged +2 is 2 < lower byte <$1234 is $34 > higher byte >$1234 is $12 ` bank byte `$123456 is $12 <> lower word <>$123456 is $3456 >` higher word <`$123456 is $1234 >< lower byte swapped word ><$123456 is $5634 x <=> y x compares to y 2 <=> 5 is -1 x == y x equals to y 2 == 3 is false x != y x does not equal to y 2 != 3 is true x < y x is less than y 2 < 3 is true x > y x is more than y 2 > 3 is false x >= y x is more than y or equals 2 >= 3 is false x <= y x is less than y or equals 2 <= 3 is true x | y bitwise or 2 | 6 is 6 x ^ y bitwise xor 2 ^ 6 is 4 x & y bitwise and 2 & 6 is 2 x << y logical shift left 1 << 3 is 8 x >> y arithmetic shift right -8 >> 3 is -1 ~x invert bits ~%101 is %010 .. concatenate bits $a..$b is $ab x[n] extract bit(s) $a[1] is 0 x[s] slice bits $1234[4:8] is $3 len(a) length in bits len($034) is 12 float(a) convert to floating point float(1) is 1.0 An integer has a truth value of true if it's non-zero. The true value is the same as 1. Length of a numeric constants are defined in bits and is calculated from the number of digits used for hexadecimal (4 each) and binary (1 each) definitions. It's also set when slicing, bit (1), byte (8) or word (16) extraction is used. Integers are automatically promoted to float as necessary in expressions. The precision is limited to 32 bits in this version. This might change in the future, so don't rely on clipping of higher bits. .byte 23 ; decimal .byte $33 ; hex .byte %00011111 ; binary lda #<label ldy #>label jsr $ab1e ldx #<>source ; word extraction ldy #<>dest lda #size(source)-1 mvn `source, `dest; bank extraction lda #((bitmap & $2000) >> 10) | ((screen & $3c00) >> 6) sta $d018 lda $d015 and #~%00100000 sta $d015 Floating point constants Floating point constants have a radix point in them and optionally an exponent. A decimal exponent is `e' while a binary one is `p'. The following operations can be used: Floating point operators and functions x + y add x to y 2.2 + 2.2 is 4.4 x - y substract y from x 4.1 - 1.1 is 3.0 x * y multiply x with y 1.5 * 3 is 4.5 x / y integer divide x by y 7.0 / 2.0 is 3.5 x % y integer modulo of x divided by y 5.0 % 2.0 is 1.0 x ** y x raised t power of y 2.0 ** -1 is 0.5 -x negated value -2.0 is -2.0 +x unchanged +2.0 is 2.0 x <=> y x compares to y 5.0 <=> 3.0 is 1 x == y x equals to y 2.0 == 3.0 is false x != y x does not equal to y 2.0 != 3.0 is true x < y x is less than y 2.0 < 3.0 is true x > y x is more than y 2.0 > 3.0 is false x >= y x is more than y or equals 2.0 >= 3.0 is false x <= y x is less than y or equals 2.0 <= 3.0 is true x | y bitwise or 2.5 | 6.5 is 6.5 x ^ y bitwise xor 2.5 ^ 6.5 is 4.0 x & y bitwise and 2.5 & 6.5 is 2.5 x << y logical shift left 1.0 << 3.0 is 8.0 x >> y arithmetic shift right -8.0 >> 4 is -0.5 ~x almost ?x ~2.1 is almost -2.1 abs(a) absolute value abs(-1.0) is 1.0 sign(a) sign value (?1, 0, 1) sign(-4.0) is -1 floor(a) round down floor(-4.8) is -5.0 round(a) round to nearest away from zero floor(4.8) is 5.0 ceil(a) round up ceil(1.1) is 2.0 trunc(a) round down towards zero trunc(-1.9) is -1 frac(a) fractional part frac(1.1) is 0.1 sqrt(a) square root sqrt(16.0) is 4.0 cbrt(a) cube root cbrt(27.0) is 3.0 log10(a) common logarithm log10(100.0) is 2.0 log(a) natural logarithm log(1) is 0.0 exp(a) exponential exp(0) is 1.0 pow(a, b) a raised to power of b pow(2.0, 3.0) is 8.0 sin(a) sine sin(0.0) is 0.0 asin(a) arc sine asin(0.0) is 0.0 sinh(a) hyperbolic sine sinh(0.0) is 0.0 cos(a) cosine cos(0.0) is 1.0 acos(a) arc cosine acos(1.0) is 0.0 cosh(a) hyperbolic cosine cosh(0.0) is 1.0 tan(a) tangent tan(0.0) is 0.0 atan(a) arc tangent atan(0.0) is 0.0 tanh(a) hyperbolic tangent tanh(0.0) is 0.0 rad(a) degrees to radian rad(0.0) is 0.0 deg(a) radian to degrees deg(0.0) is 0.0 hypot(y, x) polar distance hypot(4.0, 3.0) is 5.0 atan2(y, x) polar angle atan2(0.0, 3.0) is 0.0 int(a) convert to integer int(3.8) is 3 A floating point number has a truth value of true if it's non-zero. As usual comparing floating point numbers for (non) equality is a bad idea due to rounding errors. There are no predefined floating point constants, define them as necessary. Hint: pi is rad(180) and e is exp(1). Floating point numbers are automatically truncated to integer as necessary. Fixed point conversion can be done by using the shift operators for example a 8.16 fixed point number can be calculated as (3.14 << 16) & $ffffff. The binary operators operate like if the floating point number would be a fixed point one. This is the reason for the strange definiton of inversion. .byte 3.66e1 ; 36.6, truncated to 36 .byte $1.8p4 ; 4:4 fixed point number (1.5) .int 12.2p8 ; 8:8 fixed point number (12.2) Character string constants Strings are enclosed in single or double quotes and can hold any unicode character. Operations like indexing or slicing are always done on the original representation. The current encoding is only applied when it's used in expressions as numeric constants or in context of text data directives. Doubling the quotes inside the strings escapes them. String operators and functions .. concatenate strings "a".."b" is "ab" in is substring of "b" in "abc" is true % string formatting "%02x" % (12,) is "0c" x repeat "ab" x 3 is "ababab" x[i] character from start "abc"[1] is "b" x[i] character from end "abc"[-1] is "c" x[s] no change "abc"[:] is "abc" x[s] cut off start "abc"[1:] is "bc" x[s] cut off end "abc"[:-1] is "ab" x[s] reverse "abc"[::-1] is "cba" len(x) number of characters len("abc") is 3 A string has a truth value of true if it contains at least one character. Strings are converted to numeric constants as necessary using the current encoding and escape rules, for example when using a sane encoding "z" - "a" is 25. Indexing characters with positive integers start with zero. Negative indexes are translated internally by adding the number of characters to them, therefore ?1 can be used to access the last character. Indexing with list of integers is possible as well so "abc"[(-1, 0, 1)] is "cab". Slicing is an operation when parts of string are extracted from a start position to an end position with a step value. These parameters are separated with colons enclosed in square brackets and are all optional. Their default values are [start:maximum:step=1]. Negative start and end characters are converted to positive internally by adding the length of string to them. Negative step operates in reverse direction, non single steps will jump over characters. mystr = "oeU" ; text .text 'it''s' ; text: it's .word "ab"+1 ; character, results in "bb" usually .text "text"[:2] ; "te" .text "text"[2:] ; "xt" .text "text"[:-1] ; "tex" .text "reverse"[::-1]; "esrever" String formatting can interpret a list of values and convert them to a string. The converted values are inserted at the % sign which is followed by conversion flags, minimum field length, and conversion type. The these flags can be used: Formatting flags # alternate form ($a, %10, 10.) * width/precision from list . precision 0 pad with zeros - left adjusted (default right) blank when positive or minus sign + sign even if positive The following conversions are implemented: Formatting conversions a A hexadecimal floating point (uppercase) b binary c unicode character d decimal e E exponential float (uppercase) f F floating point (uppercase) g G exponential/floating point s string x X hexadecimal (uppercase) % percent sign .text "%#04x bytes left" % (1000,) ; $03e8 bytes left Byte string constants Byte strings are like strings, but hold bytes instead of characters. They can be created by prefixing quoted strings with a `b', this converts the string using the current encoding to bytes. Byte string operators and functions .. concatenate strings b"a"..b"b" is b"ab" in is substring of b"b" in b"abc" is true x repeat b"ab" x 3 is b"ababab" x[i] byte from start b"abc"[1] is b"b" x[i] byte from end b"abc"[-1] is b"c" x[s] no change b"abc"[:] is b"abc" x[s] cut off start b"abc"[1:] is b"bc" x[s] cut off end b"abc"[:-1] is b"ab" x[s] reverse b"abc"[::-1] is b"cba" len(x) number of bytes len(b"abc") is 3 A byte string has a truth value of true if it contains at least one byte. Indexing and slicing works as with character strings. .enc screen ;use screen encoding mystr = b"oeU" ;convert text to bytes .enc none ;normal encoding .text mystr ;text as originally encoded List and tuples Lists and tuples can hold a collection of values. Lists are defined from values separated by comma between square brackets [1, 2, 3], an empty list is []. Tuples are similar but are enclosed in parentheses instead. An empty tuple is (), a single element tuple is (4,) to differentiate from normal numeric expression parentheses. When nested they function similar to an array. Currently both types are immutable. List and tuple operators and functions .. concatenate lists [1]..[2] is [1, 2] in is member of list 2 in [1, 2, 3] is true x repeat [1, 2] x 2 is [1, 2, 1, 2] x[i] element from start ("1", 2)[1] is 2 x[i] element from end ("1", 2, 3)[-1] is 3 x[s] no change (1, 2, 3)[:] is (1, 2, 3) x[s] cut off start (1, 2, 3)[1:] is (2, 3) x[s] cut off end (1, 2.0, 3)[:-1] is (1, 2.0) x[s] reverse (1, 2, 3)[::-1] is (3, 2, 1) len(x) number of elements len([1, 2, 3]) is 3 range(s,e,t) create a list with values from a range(3) is [0,1,2] range A list or tuple has a truth value of true if it contains at least one element. Arithmetic operations are applied on the all elements recursively, therefore [1, 2] + 1 is [2, 3], and abs([1, -1]) is [1, 1]. Arithmetic operations between lists are applied one by one on their elements, so [1, 2] + [3, 4] is [4, 6]. When lists form an array and columns/rows are missing the smaller array is stretched to fill in the gaps if possible, so [[1],[2]] * [3, 4] is [[3, 4], [6, 8]]. Indexing elements with positive integers start with zero. Negative indexes are transformed to positive by adding the number of elements to them, therefor ?1 is the last element. Indexing with list of integers is possible as well so [1, 2, 3][(-1, 0, 1)] is [3, 1, 2]. Slicing is an operation when parts of list or tuple are extracted from a start position to an end position with a step value. These parameters are separated with colons enclosed in square brackets and are all optional. Their default values are [start:maximum:step=1]. Negative start and end elements are converted to positive internally by adding the number of elements to them. Negative step operates in reverse direction, non single steps will jump over elements. mylist = [1, 2, "whatever"] mytuple = (cmd_e, cmd_g) mylist = ("e", cmd_e, "g", cmd_g, "i", cmd_i) keys .text mylist[::2] ; keys ("e", "g", "i") call_l .byte <mylist[1::2]-1; routines (<cmd_e-1, <cmd_g-1, <cmd_i-1) call_h .byte >mylist[1::2]-1; routines (>cmd_e-1, >cmd_g-1, >cmd_i-1) The range(start, end, step) built in function can be used to create lists of integers in a range with a given step value. At least the end must be given, the start defaults to 0 and the step to 1. Sounds not very useful, so here are a few examples: ;Bitmask table, 8 bits from left to right .byte %10000000 >> range(8) ;Classic 256 byte single period sinus table with values of 0-255. .byte 128.5 + 127 * sin(range(256) * rad(360.0/256)) ;Screen row address tables - = $400 + range(0, 1000, 40) scrlo .byte <(-) scrhi .byte >(-) Dictionaries Dictionaries are unsorted lists holding key and value pairs. Definition is done by collecting key:value pairs separated by comma between braces {1:"a", "a":1}. An empty dictionary is {}. Currently this type is immutable. Numeric and string keys are accepted, the value can be anything. Dictionary operators and functions x[i] value lookup {"1":2}["1"] is 2 in is a key 1 in {1:2} is true len(x) number of elements len({1:2, 3:4]) is 2 A dictionary has a truth value of true if it contains at least one key value pair. .text {1:"one", 2:"two"}[2]; "two" Labels Normal labels can be defined at the start of each line. Each of them is uniq and can't be redefined. In arithmetic operations they represent the numeric addresses of a memory location. Additionally they also hold the compiled code and data definitions in binary format. A label represents by default only the single line it is found on unless block directives are used where it's extended till the end of block. The content is not forward referencable, only the address of label. Usually the size of a single element is a byte, but this can be more for data definitions. Trying to `overwrite' the same memory locations later does not affect the content anymore. Indexing and slicing of labels to access the compiled content might be implemented differently in future releases. Use this feature at your own risk for now, you might need to update your code later. Label operators and functions . member label.locallabel x[i] element from start label[1] x[i] element from end label[-1] x[s] copy as tuple label[:] x[s] cut off start, as tuple label[1:] x[s] cut off end, as tuple label[:-1] x[s] reverse, as tuple label[::-1] len(x) number of elements len(label) size(a) size in bytes size(label) A label has a truth value of true when it's address is non-zero. mydata .word 1, 4, 3 mycode .block local lda #0 .bend ldx #size(mydata) ;6 bytes (3*2) ldx #len(mydata) ;3 elements ldx #mycode[0] ;lda instruction, $a9 ldx #mydata[1] ;2nd element, 4 jmp mycode.local ;address of local label The assembler supports anonymous labels, also called as forward (+) and backward (-) references. `-' means one backward, `--' means two backward, etc. also the same for forward, but with `+'. ldy #4 - ldx #0 - txa cmp #3 bcc + adc #44 + sta $400,x inx bne - dey bne -- Excessive nesting or long distance references create a poorly readable code. It's also very easy to insert a few new references in a way to break the old ones around by mistake. These references are also useful in segments, but this can create a nice traps, as segments are copied into the code, with the internal references. bne + #somemakro ;let's hope that this segment does + nop ;not contain forward references... Constant and variable references References can reference labels, results from expressions or other references. Constant references can be created with the equal sign. These are not redefinable. Forward referencing to them is allowed as they retain the reference to constant objects over compilation passes. border = $d020 ;a constant reference f .block g .block n nop ;jump here .bend .bend inc border ;inc $d020 jsr labelref.n labelref = f.g Redefinable references can be created by the .var directive. As it's redefinable it can only be used in code after it's definition. Even tricks like using constant references on them will not help with forward referencing. They simply don't carry their last reference over from the previous pass. variabl .var 1 .rept 10 .byte variabl variabl .var variabl+1 .next Uninitialized memory There's a special value for uninitialized memory, it's represented by a question mark. Whenever it's used to generate data it creates a `hole' where the previous content of memory is visible. Uninitialized memory holes without previous content are not saved unless it's really necessary for the output format, in that case it's replaced with zeros. It's not just data generation statements (e.g. .byte) that can create uninitialized memory, but filling, alignment or address manipulation as well. *= $200 ;bytes as necessary .word ? ;2 bytes .fill 10 ;10 bytes .align 64 ;bytes as necessary .offs 16 ;16 bytes Conditional expressions Boolean conditional operators give false (0) or true (1) or one of the operands as the result. True is defined as a non-zero number, a non-empty string/tuple/ list, anything else is false. The ternary operator (?:) gives the first (x) result if c is true or the second (y) if c is false. Logical and conditional operators x || y if x is true then x otherwise y x ^^ y if both false or true then false otherwise x || y x && y if x is true then y otherwise x !x if x is true then false otherwise true !!x if x is true then true otherwise false c ? x : y if c is true then x otherwise y ;Silly example for 1=>"simple", 2=>"advanced", else "normal" .text MODE == 1 && "simple" || MODE == 2 && "advanced" || "normal" .text MODE == 1 ? "simple" : MODE == 2 ? "advanced" : "normal" Expressions Parenthesis (( )) can be used to override operator precedence. Don't forget that they also denote indirect addressing mode for certain opcodes. lda #(4+2)*3 Built in functions are identifiers followed by parentheses. They accept variable number of parameters separated by comma. Other built in functions min(a, b, ...) Minimum of values max(a, b, ...) Maximum of values repr(a) Text representation of value Special addressing mode forcing operators in front of an expression can be used to make sure the expected addressing mode is used. Address size forcing @b to force 8 bit address @w to force 16 bit address @l to force 24 bit address (65816) lda @w$0000 ------------------------------------------------------------------------------- Compiler directives: Controlling the compile offset and program counter Two counters are used while assembling. The compile offset is where the data and code ends up in memory, while the program counter is what labels will be set or what the special star label gets when referenced. *= <expression> The compile offset is moved so that the program counter will match the one in the expression. .offs <expression> Add an offset to the compile offset (create a gap). The program counter stays the same as before. .logical <expression> .here Changes the program counter, the compile offset is not changed. Can be nested. .align <modulo>[, <fill>] Align code to a dividable program counter address by gap or filler bytes *= $1000 ;set program counter (and offset) .offs 100 ;gap of 100, PC still the same .logical $300 ;set PC to $300 drive lda #$80 sta $00 jmp drive ;it's jmp $300 rts .here .align $100 irq inc $d019 ;this will be on a page boundary, after skipping bytes .align 4, $ea loop adc #1 ;padding with "nop" for DTV burst mode Here's an example how .logical and *= works together: *= $0800 ;Compile: $0800, PC: $0800 .logical $1000 ;Compile: $0800, PC: $1000 *= $1200 ;Compile: $0a00, PC: $1200 .here ;Compile: $0a00, PC: $0a00 Dumping data Storing numeric data Multi byte numeric data is stored in the little endian order, which is the natural byte order for 65xx processors. Numeric ranges are enforced depending on the directives used. When using lists or tuples their values will be used one by one. Uninitialized data creates holes of different sizes. Small string constants are converted using the current encoding. .byte <expression>[, <expression>, ...] Create bytes from 8 bit unsigned constants (0 .. 255) .char <expression>[, <expression>, ...] Create bytes from 8 bit signed constants (-128 .. 127) .byte 255 ; $ff .byte ? ; reserve 1 byte of space ;Compact computed jumps using self modifying code lda jumps,x sta smod+1 smod bne * jumps .char (routine1, routine2)-smod-2 ;Routines nearby (-128 .. 127 bytes) .word <expression>[, <expression>, ...] Create bytes from 16 bit unsigned constants (0 .. 65535) .int <expression>[, <expression>, ...] Create bytes from 16 bit signed constants (-32768 .. 32767) .word $2342, $4555 .word ? ; reserve 2 bytes of space .int -533, 4433 ;Computed jumps with jump table lda jumps,x sta ind lda jumps+1,x sta ind+1 jmp (ind) ;Computed jumps with jump table (65C02) jmp (jumps,x) jumps .word routine1, routine2 ;On 65816 substract the current program bank jumps2 .word (routine1, routine2) - (* & ~$ffff) .rta <expression>[, <expression>, ...] Create return address constants, which are 16 bit unsigned constants (usually addresses) but with one substracted. ;Computed jumps by using stack asl tax lda rets+1,x pha lda rets,x pha rts rets .rta $fce2, routine1, routine2 ;On 65816 substract the current program bank rets2 .rta ($fce2, routine1, routine2) - (* & ~$ffff) .long <expression>[, <expression>, ...] Create bytes from 24 bit unsigned constants (0 .. 16777215) .lint <expression>[, <expression>, ...] Create bytes from 24 bit signed constants (-8388608 .. 8388607) .long $123456 .long ? ; reserve 3 bytes of space .lint -533, 4433 ;Computed long jumps with jump table (65816) lda jumps,x sta ind lda jumps+1,x sta ind+1 lda jumps+2,x sta ind+2 jmp [ind] jumps .long routine1, routine2 ;Store 8.16 signed fixed point constants .lint (-3.44, 3.4, 3.52) * (1 << 16) .dword <expression>[, <expression>, ...] Create bytes from 32 bit constants (0 .. 4294967295) .dint <expression>[, <expression>, ...] Create bytes from 32 bit signed constants (-2147483648 .. 2147483647) .dword $12345678 .dword ? ; reserve 4 bytes of space ;Store 8.24 signed fixed point constants .dint (-3.44, 3.4, 3.52) * (1 << 24) .fill <length>[, <fill>] Skip bytes (using uninitialized data), or fill with repeated bytes. For multi byte patterns use .rept! .fill $100 ;no fill, just reserve $100 bytes .fill $4000, 0 ;16384 bytes of 0 Storing text data Texts are stored as a string of bytes. Small numeric constants can be mixed in to represent control characters. .text <expression>[, <expression>, ...] Assemble strings and small constants into bytes: .text "oeU" ; text, "" means $22 .text 'oeU' ; text, '' means $27 .text 23, $33 ; bytes .text %00011111 ; more bytes .text ^OEU ; the decimal value as string (^23 is $32,$33) .shift <expression>[, <expression>, ...] Same as .text, but the last byte will have the highest bit set. Any character which already has the most significiant bit set will cause an error. ldx #0 loop lda txt,x php and #$7f jsr $ffd2 inx plp bpl loop rts txt .shift "some text" .shiftl <expression>[, <expression>, ...] Same as .text, but all bytes are shifted to left, and the last character gets the lowest bit set. Any character which already has the most significiant bit set will cause an error as this would be cut off. ldx #0 loop lda txt,x lsr sta $400,x ;screen memory inx bcc loop rts .enc screen txt .shiftl "some text" .enc none .null <expression>[, <expression>, ...] Same as .text, but adds a null at the end, null in string is an error. txt .text "lot of stuff" .null "to write" lda #<txt ldy #>txt jsr $ab1e .ptext <expression>[, <expression>, ...] Same as .text, but prepend the number of bytes in front of the string (pascal style string). Longer than 255 bytes are not allowed. lda #<txt ldx #>txt jsr print rts print sta $fb stx $fc ldy #0 lda ($fb),y beq null tax - iny lda ($fb),y jsr $ffd2 dex bne - null rts txt .ptext "note" Text encoding 64tass supports sources written in utf8, utf16 (be/le) and raw 8-bit encoding. To take advantage of this capability custom encodings can be defined to map unicode characters to 8 bit values in strings. .enc <name> Selects text encoding, predefined encodings are `none' and `screen' (screen code), anything else is user defined. All user encodings start without any character or escape definitions, add some as required. .enc screen ;screencode mode .text "text with screencodes" cmp #"u" ;compare screencode .enc none ;normal mode again cmp #"u" ;compare ascii .cdef <start>, <end>, <coded> [, <start>, <end>, <coded>, ...] .cdef "<start><end>", <coded> [, "<start><end>", <coded>, ...] Defines a character range, and assigns all characters one by one from the 8 bit value. The start and end positions are unicode character codes either by numbers or by typing them. .edef "<escapetext>", <code> [, "<escapetext>", <code>, ...] Defines an escape sequence, and assigns it to a 8 bit value. When using common prefixes the longest match wins. Useful for defining non-typeable control code aliases. .enc petscii ;define an ascii->petscii encoding .cdef " @", 32 ;characters .cdef "AZ", $c1 .cdef "az", $41 .cdef "[[", $5b .cdef "??", $5c .cdef "]]", $5d .cdef "??", $5e .cdef $2190, $2190, $1f;left arrow .edef "\n", 13 ;escape sequences .edef "{clr}", 147 .text "{clr}Text in PETSCII\n" Structured data Structures and unions can be defined to create complex data types. The offset of fields are available by using the definition's name. The fields themselves by using the instance name. The initialization method is very similar to macro parameters, the difference is that unset parameters always return uninitialized data (`?') instead of an error. Structure Structures are for organizing sequential data, so the length of a structure is the sum of lengths of all items. .struct [<name>][=<default>]][,[<name>][=<default>] ...] .ends [<result>][,<result> ...] Structure definition, with named parameters and default values .dstruct <name>[,<initialization values>] .<name> [<initialization values>] Create instance of structure with initialization values .struct ;anonymous struct x .byte 0 ;labels are visible y .byte 0 ;content compiled here .ends ;useful inside unions nn_s .struct col,row ;named struct x .byte \col ;labels are not visible y .byte \row ;no content is compiled here .ends ;it's just a definition nn .dstruct nn_s,1,2;struct instance, content here lda nn.x ;direct field access ldy #nn_s.x ;get offset of field lda nn,y ;and use it indirectly Union Unions can be used for overlapping data as the compile offset and program counter remains the same on each line. Therefore the length of a union is the length of it's longest item. .union [<name>][=<default>]][,[<name>][=<default>] ...] .endu Union definition, with named parameters and default values .dunion <name>[,<initialization values>] .<name> [<initialization values>] Create instance of union with initialization values .union ;anonymous union x .byte 0 ;labels are visible y .word 0 ;content compiled here .endu nn_u .union ;named union x .byte ? ;labels are not visible y .word \1 ;no content is compiled here .endu ;it's just a definition nn .dunion nn_u,1 ;union instance here lda nn.x ;direct field access ldy #nn_u.x ;get offset of field lda nn,y ;and use it indirectly Combined use of structures and unions The example below shows how to define structure to a binary include. .union .binary "pic.drp",2 .struct color .fill 1024 screen .fill 1024 bitmap .fill 8000 backg .byte ? .ends .endu Anonymous structures and unions in combination with sections are useful for overlapping memory assignment. The example below shares zeropage allocations for two separate parts of a bigger program. The common subroutine variables are assigned after in the `zp' section. *= $02 .union ;spare some memory .struct .dsection zp1 ;declare zp1 section .ends .struct .dsection zp2 ;declare zp2 section .ends .endu .dsection zp ;declare zp section Macros Macros can be used to reduce typing of frequently used source lines. Each invocation is a copy of the macro's content with parameter references replaced by the parameter texts. .segment [<name>][=<default>]][,[<name>][=<default>] ...] .endm [<result>][,<result> ...] Copies the code segment as it is, so symbols can be used from outside, but this also means multiple use will result in double defines unless anonymous labels are used. .macro [<name>][=<default>]][,[<name>][=<default>] ...] .endm [<result>][,<result> ...] The code is enclosed in it's own block so symbols inside are non-accessible, unless a label is prefixed at the place of use, then local labels can be accessed through that label. #<name> [<param>][[,][<param>] ...] .<name> [<param>][[,][<param>] ...] Invoke the macro after `#' or `.' with the parameters. Normally the name of the macro is used, but it can be any expression. ;A simple macro copy .macro ldx #size(\1) lp lda \1,x sta \2,x dex bpl lp .endm #copy label, $500 ;Use macro as an assembler directive lohi .macro lo .byte <(\@) hi .byte >(\@) .endm var .lohi 1234, 5678 lda var.lo,y ldx var.hi,y Parameter references The first 9 parameters can be referenced by \1...\9. The entire parameter list including separators is \@. name .macro lda #\1 ;first parameter 23+1 .endm #name 23+1 ;call macro Parameters can be named, and it's possible to set a default value after an equal sign which is used as a replacement when the parameter is missing. These named parameters can be referenced by \name or \{name}. Names must match completely, if unsure use the quoted name reference syntax. name .macro first,b=2,,last lda #\first ;first parameter lda #\b ;second parameter lda #\3 ;third parameter lda #\last ;fourth parameter .endm #name 1, , 3, 4 ;call macro Text references In the original turbo assembler normal references are passed by value and can only appear in place of one. Text references on the other hand can appear everywhere and will work in place of eg quoted text or opcodes and labels. The first 9 parameters can be referenced as text by @1...@9. name .macro jsr print .null "Hello @1!";first parameter .endm #name "wth?" ;call macro Custom functions Beyond the built in functions mentioned earlier it's possible to define custom ones for frequently used calculations. .function <name>[=<default>]][,<name>[=<default>] ...] .endf [<result>][,<result> ...] Defines a user function #<name> [<param>][[,][<param>] ...] .<name> [<param>][[,][<param>] ...] <name> [<param>][[,][<param>] ...] Invoke a function like a macro, directive or pseudo instruction. Parameters are assigned to constant references. It's possible to use less parameters if the missing ones have a default value, but more parameters are not accepted. Multiple values are returned as a tuple. Functions can span multiple lines but unlike macros they can't create new code. Only those external variables and functions are available which were accessible at the place of definition, but not those at the place of invocation. wpack .function a,b=0 .endf a+b*256 .word wpack(1), wpack(2,3) If a function is used as macro, directive or pseudo instruction and there's a label in front then the returned value is assigned to it. If nothing is returned then it's used as regular label. Of course when used like this it can create code and access local variables. mva .function s,d lda s sta d .endf mva #1, label Conditional assembly To prevent parts of source from compiling conditional constructs can be used. This is useful when multiple slightly different versions needs to be compiled from the same source. If, elsif, else .if <expression> Compile, if result is true (not zero) .elsif <expression> Compile if the previous conditions were all skipped and the result is true (not zero) .else Compile if the previous conditions were all skipped .fi .endif End of conditional compile .ifne <value> Compile, if value is not zero (or true) .ifeq <value> Compile, if value is zero (or false) .ifpl <value> Compile, if value is greater or equal zero .ifmi <value> Compile, if value is less than zero The .ifne, .ifeq, .ifpl and .ifmi directives exists for compatibility only, in practice it's better to use comparison operators instead. .if wait==2 ;2 cycles nop .elsif wait==3 ;3 cycles bit $ea .elsif wait==4 ;4 cycles bit $eaea .else ;else 5 cycles inc $2 .fi Switch, case, default Similar to the .if/.elsif/.else construct, but the compared value needs to be written only once in the switch statement. .switch <expression> Evaluate expression and remember it .case <expression>[,<expression> ...] Compile if the previous conditions were all skipped and one of the values equals .default Compile if the previous conditions were all skipped .endswitch End of conditional compile .switch wait .case 2 ;2 cycles nop .case 3 ;3 cycles bit $ea .case 4 ;4 cycles bit $eaea .default ;else 5 cycles inc $2 .endswitch Repetitions .for <variable>=<expression>,<expression>,<variable>=<expression> .next Compile loop, only anonymous references are allowed as labels inside ldx #0 lda #32 lp .for ue = 0, ue < $400, ue=ue+$100 sta ue,x .next dex bne lp .rept <expression> .next Repeated compile, only anonymous references are allowed as labels inside .rept 100 nop .next .lbl Creates a special jump label that can be referenced by .goto .goto <labelname> Causes assembler to continue assembling from the jump label. No forward references of course, handle with care. Typically used in classic TASM sources for creating loops. i .var 100 loop .lbl nop i .var i - 1 .ifne i .goto loop ;generates 100 nops .fi Including files Longer sources are usually separated into multiple files for easier handling. Precomputed binary data can also be included directly without converting it into source code first. Search path is relative to the location of current source file. If it's not found there the include search path is consulted for further possible locations. To make your sources portable please always use forward slashes (/) as a directory separator and use lower/uppercase consistently in filenames! .include <filename> Include source file here. .binclude <filename> Include source file here in it's local block. If the directive is prefixed with a label then all labels are local and are accessible through that label only, otherwise not reachable at all. .include "macros.asm" ;include macros menu .binclude "menu.asm" ;include in a block jmp menu.start .binary <filename>[, <offset>[, <length>]] Include raw binary data from file. By using offset and length it's possible to break out chunks of data from a file separately, like bitmap and colors for example. .binary "stuffz.bin" ;simple include, all bytes .binary "stuffz.bin",2 ;skip start address .binary "stuffz.bin",2,1000 ;skip start address, 1000 bytes max *= $1000 ;load music to $1000 and .binary "music.sid",$7e ;strip SID header Blocks .proc .pend Procedure start and end of procedure. If it's label is not used then the code won't be compiled at all! All labels inside are local and are accessible through the prefixed label. Useful for building libraries. ize .proc nop cucc nop .pend jsr ize jmp ize.cucc .block .bend Block start and block end. All labels inside a block are local. If prefixed with a label local variables are accessible through that label, otherwise not at all. .block inc count + 1 count ldx #0 .bend .comment .endc Comment block start and comment block end. .comment lda #1 ;this won't be compiled sta $d020 .endc Sections Sections can be used to collect data or code into separate memory areas without moving source code lines around. This is achieved by having separate compile offset and program counters for each defined section. .section <name> .send [<name>] Defines a section fragment. The name at .send must match but it's optional. .dsection <name> Collect the section fragments here. All .section fragments are compiled to the memory area allocated by the .dsection directive. Compilation happens as the code appears, this directive only assigns enough space to hold all the content in the section fragments. The space used by section fragments is calculated from the difference of starting compile offset and the maximum compile offset reached. It is possible to manipulate the compile offset in fragments, but putting code before the start of .dsection is not allowed. *= $02 .dsection zp ;declare zeropage section .cerror *>$30,"Too many zeropage variables" *= $334 .dsection bss ;declare uninitialized variable section .cerror *>$400,"Too many variables" *= $0801 .dsection code ;declare code section .cerror *>$1000,"Program too long!" *= $1000 .dsection data ;declare data section .cerror *>$2000,"Data too long!" ;-------------------- .section code .word ss, 2005 .null $9e, ^start ss .word 0 start sei .section zp ;declare some new zeropage variables p2 .word ? ;a pointer .send zp .section bss ;new variables buffer .fill 10 ;temporary area .send bss lda (p2),y lda #<label ldy #>label jsr print .section data ;some data label .null "message" .send data jmp error .section zp ;declare some more zeropage variables p3 .word ? ;a pointer .send zp .send code The compiled code will look like: >0801 0b 08 d5 07 .word ss, 2005 >0805 9e 32 30 36 31 00 .null $9e, ^start >080b 00 00 ss .word 0 .080d 78 start sei >0002 p2 .word ? ;a pointer >0334 buffer .fill 10 ;temporary area .080e b1 02 lda (p2),y .0810 a9 00 lda #<label .0812 a0 10 ldy #>label .0814 20 1e ab jsr print >1000 6d 65 73 73 61 67 65 00 label .null "message" .0817 4c e2 fc jmp error >0004 p2 .word ? ;a pointer Sections can form a hierarchy by nesting a .dsection into another section. The section names must only be unique within a section but can be reused otherwise. Parent section names are visible for children, siblings can be reached through parents. In the following example the included sources don't have to know which `code' and `data' sections they use, while the `bss' section is shared for all banks. ;First 8K bank at the beginning, PC at $8000 *= $0000 .logical $8000 .dsection bank1 .cerror *>$a000, "Bank1 too long" .here bank1 .block ;Make all symbols local .section bank1 .dsection code ;Code and data sections in bank1 .dsection data .section code ;Pre-open code section .include "code.asm"; see below .include "iter.asm" .send code .send bank1 .bend ;Second 8K bank at $2000, PC at $8000 *= $2000 .logical $8000 .dsection bank2 .cerror *>$a000, "Bank2 too long" .here bank2 .block ;Make all symbols local .section bank2 .dsection code ;Code and data sections in bank2 .dsection data .section code ;Pre-open code section .include "scr.asm" .send code .send bank2 .bend ;Common data, avoid initialized variables here! *= $c000 .dsection bss .cerror *>$d000, "Too much common data" ;------------- The following is in "code.asm" code sei .section bss ;Common data section buffer .fill 10 .send bss .section data ;Data section (in bank1) routine .word print .send bss 65816 related .as .al Select short (8 bit) or long (16 bit) accumulator immediate constants. .al lda #$4322 .xs .xl Select short (8 bit) or long (16 bit) index register immediate constants. .xl ldx #$1000 .databank <expression> Set databank (65816). Absolute addressing is used only for symbols in this bank, anything else (except direct page) is using long addressing. .databank $10 ;$10xxxx .dpage <expression> Set directpage. Direct or zero page addressing is only used for addresses in the following 256 byte range, anything else is using absolute or long addressing. .dpage $400 Controlling errors .page .endp Gives an error on page boundary crossing, eg. for timing sensitive code. .page table .byte 0,1,2,3,4,5,6,7 .endp .option allow_branch_across_page Switches error generation on page boundary crossing during relative branch. Such a condition on 6502 adds 1 extra cycle to the execution time, which can ruin the timing of a carefuly cycle counted code. .option allow_branch_across_page = 0 ldx #3 ;now this will execute in - dex ;16 cycles for sure bne - .option allow_branch_across_page = 1 .error <message> [, <message>, ...] .cerror <condition>, <message> [, <message>, ...] Exit with error or conditionally exit with error .error "Unfinished here..." .cerror *>$1200, "Program too long by ", *-$1200, " bytes" .warn <message> [, <message>, ...] .cwarn <condition>, <message> [, <message>, ...] Display a warning message always or depending on a condition .warn "FIXME: handle negative values too!" .cwarn *>$1200, "This may not work!" Target .cpu <cpuname> Selects cpu .cpu 6502 ;standard 65xx .cpu 65c02 ;CMOS 65C02 .cpu 65ce02 ;CSG 65CE02 .cpu 6502i ;NMOS 65xx .cpu 65816 ;W65C816 .cpu 65dtv02 ;65dtv02 .cpu 65el02 ;65el02 .cpu r65c02 ;R65C02 .cpu w65c02 ;W65C02 .cpu default ;cpu set on commandline Misc .end Terminate assembly. Any content after this directive is ignored. .eor <expression> Eor output with some 8 bit value. Useful for reverse screencode text for example, or for silly `encryption'. .var <expression> Defines a variable identified by the label preceeding, which is set to the value of expression or reference of variable. .assert .check Do not use these, the syntax will change in next version! Printer control .pron .proff Turn on or off source listing on part of the file. .proff ;Don't put filler bytes into listing *= $8000 .fill $2000, $ff ;Pre-fill ROM area .pron *= $8000 .word reset, restore .text "CBM80" reset cld .hidemac .showmac Ignored for compatibility ------------------------------------------------------------------------------- Pseudo instructions For writing short code there are some special pseudo instructions for always taken branches. These are automatically compiled as relative branches when the jump distance is short enough and as JMP or BRL when longer. The names are derived from conditional branches and are: GEQ, GNE, GCC, GCS, GPL, GMI, GVC, and GVS. There's one more called GRA for CPUs supporting BRA, which is expanded to BRL (if available) or JMP. .0000 a9 03 lda #$03 in1 lda #3 .0002 d0 02 gne $0006 gne at ;branch always .0004 a9 02 lda #$02 in2 lda #2 .0006 4c 00 10 gne $1000 at gne $1000 ;branch further If the branch would skip only one byte then the opposite condition is compiled and only the first byte is emitted. This is now a never executed jump, and the relative distance byte after the opcode is the jumped over byte. If the branch would not skip anything at all then no code is generated. .0009 geq $0009 geq in3 ;zero length "branch" .0009 18 clc in3 clc .000a b0 gcc $000c gcc at2 ;one byte skip, as bcs .000b 38 sec in4 sec ;sec is skipped! .000c 20 0f 00 jsr $000f at2 jsr func .000f func Please note that expressions like Gxx *+2 or Gxx *+3 are not allowed as the compiler can't figure out if it has to create no code at all, the 1 byte variant or the 2 byte one. Therefore use normal or anonymous labels defined after the jump instruction when jumping forward! To avoid branch too long errors the assembler also supports long branches, it can automatically convert conditional relative branches to it's opposite and a JMP or BRL. This can be enabled on the command line using the `--long-branch' option. .0000 ea nop nop .0001 b0 03 4c 00 10 bcc $1000 bcc $1000 ;long branch .0006 ea nop nop Please note that forward jump expressions like Bxx *+130, Bxx *+131 and Bxx *+132 are not allowed as the compiler can't decide between a short/long branch. Of course these destinations can be used, but only with normal or anonymous labels defined after the jump instruction. ------------------------------------------------------------------------------- Original turbo assembler compatibility How to convert source code for use with 64tass Currently there are two options, either use `TMPview' by Style to convert the sourcefile directly, or do the following: * load turbo assembler, start (by sys9*4096 or sys8*4096 depending on version) * <- (arrow left) then l to load a sourcefile * <- (arrow left) then w to write a sourcefile in petscii format * convert the result to ASCII using petcat (from the vice package) The resulting file should then (with the restrictions below) assemble using the following commandline: 64tass -C -T -a -W -i source.asm -o outfile.prg Differences to the original turbo ass macro on the C64 64tass is nearly 100% compatible with the original `Turbo Assembler', and supports most of the features of the original `Turbo Assembler Macro'. The remaining notable differences are listed here. Labels The original turbo assembler uses case sensitive labels, use the -C, --case-sensitive option to enable this behaviour. Another thing worth noting is that the original turbo assembler lets you create an interesting ambiguous construct using a label called `a'. lsr a ; uses accu ! (or does it really?) a jmp a ; uses the label address .word a ; uses the label address If you get a warning like `warning: Possibly incorrectly used A "lsr a"', then there is such an ambiguous situation in your code and you should fix it (by renaming the label). Expression evaluation There are a few differences which can be worked around by the -T, --tasm-compatible option. These are: The original expression parser has no operator precedence, but 64tass has. That means that you will have to fix expressions using braces accordingly, for example 1+2*3 becomes (1+2)*3. The following operators used by the original Turbo Assembler are different: TASM Operator differences . bitwise or, now | : bitwise eor, now ^ ! force 16 bit address, now @w The default expression evaluation is not limited to 16 bit unsigned numbers anymore. Macros Macro parameters are referenced by \1...\9 instead of using the pound sign. Parameters are always copied as text into the macro and not passed by value as the original turbo assembler does, which sometimes may lead to unexpected behaviour. You may need to make use of braces around arguments and/or references to fix this. Bugs Some versions of the original turbo assembler had bugs that are not reproduced by 64tass, you will have to fix the code instead. In some versions labels used in the first .block are globally available. If you get a related error move the respective label out of the .block ------------------------------------------------------------------------------- Command line options Output options -o <filename> Place output into <filename>. The default output filename is `a.out'. This option changes it. 64tass a.asm -o a.prg -b, --nostart Strip starting address. Strips the 2 or 3 byte starting address before the resulting binary. Useful for creating small ROM images. -f, --flat Flat output mode. Output the plain binary image from offset 0. The image size can be much larger than the processor address space. Useful for creating huge multi bank ROM files. -n, --nonlinear Generate nonlinear output file. Generates non-linear output for linkers. 64tass --nonlinear a.asm *= $1000 lda #2 *= $2000 nop Result of compilation $02, $00 little endian length, 2 bytes $00, $10 little endian start $1000 $a9, $02 code $01, $00 little endian length, 1 byte $00, $20 little endian start $2000 $ea code $00, $00 end marker (length=0) -W, --wordstart Force 2 byte start address. If 16 MiB address space is used for a 65816, then the starting address of file will be 3 bytes long. This option makes it 2 bytes long. 64tass --wordstart --m65816 a.asm Operation options -a, --ascii Use ASCII/Unicode text encoding instead of raw 8-bit Normally no conversion takes place, this is for backwards compatibility with a DOS based Turbo Assembler editor, which could create PETSCII files for 6502tass. (including control characters of course) Using this option will change the default `none' and `screen' encodings to map 'a'-'z' and 'A'-'Z' into the correct PETSCII range of $41-$5A and $C1-$DA, which is more suitable for an ASCII editor. It also adds predefined petcat style PETASCII literals to the default encodings. For writing sources in utf8/utf16 encodings this option is required! The symbol names are still limited to ASCII, but custom string encodings can take advantage of the full unicode set. 64tass a.asm .0000 a9 61 lda #$61 lda #"a" >0002 31 61 41 .text "1aA" >0005 7b 63 6c 65 61 72 7d 74 .text "{clear}text{return}more" >000e 65 78 74 7b 72 65 74 75 >0016 72 6e 7d 6d 6f 72 65 64tass --ascii a.asm .0000 a9 41 lda #$41 lda #"a" >0002 31 41 c1 .text "1aA" >0005 93 54 45 58 54 0d 4d 4f .text "{clear}text{return}more" >000e 52 45 -B, --long-branch Automatic BXX *+5 JMP xxx. Branch too long messages can be annoying sometimes, usually they'll need to be rewritten to BXX *+5 JMP xxx. 64tass can do this automatically if this option is used. But BRA is not converted. 64tass a.asm *= $1000 bcc $1233 ;error... 64tass a.asm *= $1000 bcs *+5 ;opposite condition jmp $1233 ;as simple workaround 64tass --long-branch a.asm *= $1000 bcc $1233 ;no error, automatically converted to the above one. -C, --case-sensitive Case sensitive labels. Labels are non case sensitive by default, this option changes that. 64tass a.asm label nop Label nop ;double defined... 64tass --case-sensitive a.asm label nop Label nop ;Ok, it's a different label... -D <label>=<value> Define <label> to <value>. Defines a label to a value. Same syntax is allowed as in source files. Be careful with string quoting, the shell might eat some of the characters. 64tass -D ii=2 a.asm lda #ii ;result: $a9, $02 -w, --no-warn Suppress warnings. Disables warnings during compile. 64tass --no-warn a.asm -q, --quiet Suppress messages. Disables header and summary messages. 64tass --quiet a.asm -T, --tasm-compatible Enable TASM compatible operators and precedence Switches the expression evaluator into compatibility mode. This enables `.', `:' and `!' operators and disables 64tass specific extensions, disables precedence handling and forces 16 bit unsigned evaluation (see `differences to original Turbo Assembler' below) -I <path> Specify include search path If an included source or binary file can't be found in the directory of the source file then this path is tried. More than one directories can be specified by repeating this option. If multiple matches exist the first one is used. Target selection on command line These options will select the default architecture. It can be overridden by using the .cpu directive in the source. --m65xx Standard 65xx (default). For writing compatible code, no extra codes. This is the default. 64tass --m65xx a.asm lda $14 ;regular instructions -c, --m65c02 CMOS 65C02. Enables extra opcodes and addressing modes specific to this CPU. 64tass --m65c02 a.asm stz $d020 ;65c02 instruction -c, --m65ce02 CSG 65CE02. Enables extra opcodes and addressing modes specific to this CPU. 64tass --m65ce02 a.asm inz -i, --m6502 NMOS 65xx. Enables extra illegal opcodes. Useful for demo coding for C64, disk drive code, etc. 64tass --m6502 a.asm lax $14 ;illegal instruction -t, --m65dtv02 65DTV02. Enables extra opcodes specific to DTV. 64tass --m65dtv02 a.asm sac #$00 -x, --m65816 W65C816. Enables extra opcodes, and full 16 MiB address space. Useful for SuperCPU projects. Don't forget to use `--word-start' for small ones ;) 64tass --m65816 a.asm lda $123456,x -e, --m65el02 65EL02. Enables extra opcodes, useful RedPower CPU projects. Probably you'll need `--nostart' as well. 64tass --m65el02 a.asm lda 0,r --mr65c02 R65C02. Enables extra opcodes and addressing modes specific to this CPU. 64tass --mr65c02 a.asm rmb 7,$20 --mw65c02 W65C02. Enables extra opcodes and addressing modes specific to this CPU. 64tass --mw65c02 a.asm wai Source listing options -l <file> List labels into <file>. List global used labels to a file. 64tass -l labels.txt a.asm *= $1000 label jmp label result (labels.txt): label = $1000 -L <file> List into <file>. Dumps source code and compiled code into file. Useful for debugging, it's much easier to identify the code in memory within the source files. 64tass -L list.txt a.asm *= $1000 ldx #0 loop dex bne loop rts result (list.txt): ;64tass Turbo Assembler Macro V1.5x listing file of "a.asm" ;done on Fri Dec 9 19:08:55 2005 .1000 a2 00 ldx #$00 ldx #0 .1002 ca dex loop dex .1003 d0 fd bne $1002 bne loop .1005 60 rts rts ;****** end of code -m, --no-monitor Don't put monitor code into listing. There won't be any monitor listing in the list file. 64tass --no-monitor -L list.txt a.asm result (list.txt): ;64tass Turbo Assembler Macro V1.5x listing file of "a.asm" ;done on Fri Dec 9 19:11:43 2005 .1000 a2 00 ldx #0 .1002 ca loop dex .1003 d0 fd bne loop .1005 60 rts ;****** end of code -s, --no-source Don't put source code into listing. There won't be any source listing in the list file. 64tass --no-source -L list.txt a.asm result (list.txt): ;64tass Turbo Assembler Macro V1.5x listing file of "a.asm" ;done on Fri Dec 9 19:13:25 2005 .1000 a2 00 ldx #$00 .1002 ca dex .1003 d0 fd bne $1002 .1005 60 rts ;****** end of code Other options -?, --help Give this help list. Prints help about command line options. --usage Give a short usage message. Prints short help about command line options. -V, --version Print program version ------------------------------------------------------------------------------- Messages Faults and warnings encountered are sent to standard error for logging. The format of messages is the following: <filename>:<line>:<character>: <severity>: <message> * filename: The name and path of source file where the error happened. * line: Line number of file, starts from 1. * character: Character in line, starts from 1. Tabs are not expanded. * severity: Note, warning, error or fatal. * message: The fault message itself. a.asm:4:8: error: page error at $0007d0 Warnings Top of memory excedeed compile continues at the bottom ($0000) Possibly incorrectly used A do not use `a' as label Memory bank excedeed compile continues in the next 64 KiB bank, however execution may not Possible jmp ($xxff) bug yet another 65xx feature... Long branch used Branch too long, so long branch was used (bxx *+5 jmp) Directive ignored An assembler directive was ignored for compatibility reasons. Errors Double defined double use of label/macro Not defined not defined label Extra characters on line there's some garbage on the end of line Constant too large the number was too big General syntax can't do anything with this X expected X may be missing Expression syntax syntax error Branch too far can't relative branch that far Missing argument no argument given Illegal operand can't be used here Requirements not met: Not all features are provided, at least one is missing Conflict: at least one feature is provided, which shouldn't be there Division by zero Cannot calculate value Fatal errors Can't locate file cannot open file Out of memory won't happen ;) Can't write object file: cannot write the result Can't write listing file: cannot write the list file Can't write label file: cannot write the label file File recursion wrong use of .include Macro recursion too deep wrong use of nested macros Unknown CPU CPU type not known Ooops! Too many passes... With a carefuly crafted source file it's possible to create unresolvable situations. Fix your code. ------------------------------------------------------------------------------- Credits Original written for DOS by Marek Matula of Taboo, then ported to ansi C by BigFoot/Breeze, and finally added 65816 support, DTV, illegal opcodes, optimizations, multi pass compile and a lot of features by Soci/Singular. Improved TASS compatibility, PETSCII codes by Groepaz. Additional code: my_getopt command-line argument parser by Benjamin Sittler, avl tree code by Franck Bui-Huu, ternary tree code by Daniel Berlin, snprintf Alain Magloire, Amiga OS4 support files by Janne Per?aho. Main developer and maintainer: soci at c64.rulez.org ------------------------------------------------------------------------------- Default translation and escape sequences Raw 8-bit source By default raw 8-bit encoding is used and nothing is translated or escaped. This mode is for compiling sources which are already PETSCII. The `none' encoding for raw 8-bit Does no translation at all, no translation table, no escape sequences. The `screen' encoding for raw 8-bit The following translation table applies, no escape sequences. Built in PETSCII to PETSCII screen code translation table Input Byte Input Byte 00...1F 80...9F 20...3F 20...3F 40...5F 00...1F 60...7F 40...5F 80...9F 80...9F A0...BF 60...7F C0...FE 40...7E FF 5E Unicode and ASCII source Unicode encoding is used when the `-a' option is given on the command line. The `none' encoding for Unicode This is a Unicode to PETSCII mapping, including escape sequences for control codes. Built in Unicode to PETSCII translation table Glyph Unicode Byte Glyph Unicode Byte ...@ U+0020...U+0040 20...40 A...Z U+0041...U+005A C1...DA [ U+005B 5B ] U+005D 5D a...z U+0061...U+007A 41...5A ? U+00A3 5C ? U+03C0 FF ? U+2190 5F ? U+2191 5E ? U+2500 C0 ? U+2502 DD ? U+250C B0 ? U+2510 AE ? U+2514 AD ? U+2518 BD ? U+251C AB ? U+2524 B3 ? U+252C B2 ? U+2534 B1 ? U+253C DB ? U+256D D5 ? U+256E C9 ? U+256F CB ? U+2570 CA ? U+2571 CE ? U+2572 CD ? U+2573 D6 ? U+2581 A4 ? U+2582 AF ? U+2583 B9 ? U+2584 A2 ? U+258C A1 ? U+258D B5 ? U+258E B4 ? U+258F A5 ? U+2592 A6 ? U+2594 A3 ? U+2595 A7 ? U+2596 BB ? U+2597 AC ? U+2598 BE ? U+259A BF ? U+259D BC ? U+25CB D7 ? U+25CF D1 ? U+25E4 A9 ? U+25E5 DF ? U+2660 C1 ? U+2663 D8 ? U+2665 D3 ? U+2666 DA ? U+2713 BA Built in PETSCII escape sequences Escape Byte Escape Byte Escape Byte {bell} 07 {black} 90 {blk} 90 {blue} 1F {blu} 1F {brn} 95 {brown} 95 {cbm-*} DF {cbm-+} A6 {cbm--} DC {cbm-0} 30 {cbm-1} 81 {cbm-2} 95 {cbm-3} 96 {cbm-4} 97 {cbm-5} 98 {cbm-6} 99 {cbm-7} 9A {cbm-8} 9B {cbm-9} 29 {cbm-@} A4 {cbm-^} DE {cbm-a} B0 {cbm-b} BF {cbm-c} BC {cbm-d} AC {cbm-e} B1 {cbm-f} BB {cbm-g} A5 {cbm-h} B4 {cbm-i} A2 {cbm-j} B5 {cbm-k} A1 {cbm-l} B6 {cbm-m} A7 {cbm-n} AA {cbm-o} B9 {cbm-pound} A8 {cbm-p} AF {cbm-q} AB {cbm-r} B2 {cbm-s} AE {cbm-t} A3 {cbm-up arrow} DE {cbm-u} B8 {cbm-v} BE {cbm-w} B3 {cbm-x} BD {cbm-y} B7 {cbm-z} AD {clear} 93 {clr} 93 {control-0} 92 {control-1} 90 {control-2} 05 {control-3} 1C {control-4} 9F {control-5} 9C {control-6} 1E {control-7} 1F {control-8} 9E {control-9} 12 {control-:} 1B {control-;} 1D {control-=} 1F {control-@} 00 {control-a} 01 {control-b} 02 {control-c} 03 {control-d} 04 {control-e} 05 {control-f} 06 {control-g} 07 {control-h} 08 {control-i} 09 {control-j} 0A {control-k} 0B {control-left arrow} 06 {control-l} 0C {control-m} 0D {control-n} 0E {control-o} 0F {control-pound} 1C {control-p} 10 {control-q} 11 {control-r} 12 {control-s} 13 {control-t} 14 {control-up arrow} 1E {control-u} 15 {control-v} 16 {control-w} 17 {control-x} 18 {control-y} 19 {control-z} 1A {cr} 0D {cyan} 9F {cyn} 9F {delete} 14 {del} 14 {dish} 08 {down} 11 {ensh} 09 {esc} 1B {f10} 82 {f11} 84 {f12} 8F {f1} 85 {f2} 89 {f3} 86 {f4} 8A {f5} 87 {f6} 8B {f7} 88 {f8} 8C {f9} 80 {gray1} 97 {gray2} 98 {gray3} 9B {green} 1E {grey1} 97 {grey2} 98 {grey3} 9B {grn} 1E {gry1} 97 {gry2} 98 {gry3} 9B {help} 84 {home} 13 {insert} 94 {inst} 94 {lblu} 9A {left arrow} 5F {left} 9D {lf} 0A {lgrn} 99 {lower case} 0E {lred} 96 {lt blue} 9A {lt green} 99 {lt red} 96 {orange} 81 {orng} 81 {pi} FF {pound} 5C {purple} 9C {pur} 9C {red} 1C {return} 0D {reverse off} 92 {reverse on} 12 {rght} 1D {right} 1D {run} 83 {rvof} 92 {rvon} 12 {rvs off} 92 {rvs on} 12 {shift return} 8D {shift-*} C0 {shift-+} DB {shift-,} 3C {shift--} DD {shift-.} 3E {shift-/} 3F {shift-0} 30 {shift-1} 21 {shift-2} 22 {shift-3} 23 {shift-4} 24 {shift-5} 25 {shift-6} 26 {shift-7} 27 {shift-8} 28 {shift-9} 29 {shift-:} 5B {shift-;} 5D {shift-@} BA {shift-^} DE {shift-a} C1 {shift-b} C2 {shift-c} C3 {shift-d} C4 {shift-e} C5 {shift-f} C6 {shift-g} C7 {shift-h} C8 {shift-i} C9 {shift-j} CA {shift-k} CB {shift-l} CC {shift-m} CD {shift-n} CE {shift-o} CF {shift-pound} A9 {shift-p} D0 {shift-q} D1 {shift-r} D2 {shift-space} A0 {shift-s} D3 {shift-t} D4 {shift-up arrow} DE {shift-u} D5 {shift-v} D6 {shift-w} D7 {shift-x} D8 {shift-y} D9 {shift-z} DA {space} 20 {sret} 8D {stop} 03 {swlc} 0E {swuc} 8E {tab} 09 {up arrow} 5E {up/lo lock off} 09 {up/lo lock on} 08 {upper case} 8E {up} 91 {white} 05 {wht} 05 {yellow} 9E {yel} 9E The `screen' encoding for Unicode This is a Unicode to PETSCII screen code mapping, including escape sequences for control code screen codes. Built in Unicode to PETSCII screen code translation table Glyph Unicode Translated Glyph Unicode Translated ...? U+0020...U+003F 20...3F @ U+0040 00 A...Z U+0041...U+005A 41...5A [ U+005B 1B ] U+005D 1D a...z U+0061...U+007A 01...1A ? U+00A3 1C ? U+03C0 5E ? U+2190 1F ? U+2191 1E ? U+2500 40 ? U+2502 5D ? U+250C 70 ? U+2510 6E ? U+2514 6D ? U+2518 7D ? U+251C 6B ? U+2524 73 ? U+252C 72 ? U+2534 71 ? U+253C 5B ? U+256D 55 ? U+256E 49 ? U+256F 4B ? U+2570 4A ? U+2571 4E ? U+2572 4D ? U+2573 56 ? U+2581 64 ? U+2582 6F ? U+2583 79 ? U+2584 62 ? U+258C 61 ? U+258D 75 ? U+258E 74 ? U+258F 65 ? U+2592 66 ? U+2594 63 ? U+2595 67 ? U+2596 7B ? U+2597 6C ? U+2598 7E ? U+259A 7F ? U+259D 7C ? U+25CB 57 ? U+25CF 51 ? U+25E4 69 ? U+25E5 5F ? U+2660 41 ? U+2663 58 ? U+2665 53 ? U+2666 5A ? U+2713 7A Built in PETSCII screen code escape sequences Escape Byte Escape Byte Escape Byte {cbm-*} 5F {cbm-+} 66 {cbm--} 5C {cbm-0} 30 {cbm-9} 29 {cbm-@} 64 {cbm-^} 5E {cbm-a} 70 {cbm-b} 7F {cbm-c} 7C {cbm-d} 6C {cbm-e} 71 {cbm-f} 7B {cbm-g} 65 {cbm-h} 74 {cbm-i} 62 {cbm-j} 75 {cbm-k} 61 {cbm-l} 76 {cbm-m} 67 {cbm-n} 6A {cbm-o} 79 {cbm-pound} 68 {cbm-p} 6F {cbm-q} 6B {cbm-r} 72 {cbm-s} 6E {cbm-t} 63 {cbm-up arrow} 5E {cbm-u} 78 {cbm-v} 7E {cbm-w} 73 {cbm-x} 7D {cbm-y} 77 {cbm-z} 6D {left arrow} 1F {pi} 5E {pound} 1C {shift-*} 40 {shift-+} 5B {shift-,} 3C {shift--} 5D {shift-.} 3E {shift-/} 3F {shift-0} 30 {shift-1} 21 {shift-2} 22 {shift-3} 23 {shift-4} 24 {shift-5} 25 {shift-6} 26 {shift-7} 27 {shift-8} 28 {shift-9} 29 {shift-:} 1B {shift-;} 1D {shift-@} 7A {shift-^} 5E {shift-a} 41 {shift-b} 42 {shift-c} 43 {shift-d} 44 {shift-e} 45 {shift-f} 46 {shift-g} 47 {shift-h} 48 {shift-i} 49 {shift-j} 4A {shift-k} 4B {shift-l} 4C {shift-m} 4D {shift-n} 4E {shift-o} 4F {shift-pound} 69 {shift-p} 50 {shift-q} 51 {shift-r} 52 {shift-space} 60 {shift-s} 53 {shift-t} 54 {shift-up arrow} 5E {shift-u} 55 {shift-v} 56 {shift-w} 57 {shift-x} 58 {shift-y} 59 {shift-z} 5A {space} 20 {up arrow} 1E ------------------------------------------------------------------------------- Opcodes Standard 6502 opcodes The standard 6502 opcodes ADC 61 65 69 6D 71 75 79 7D AND 21 25 29 2D 31 35 39 3D ASL 06 0A 0E 16 1E BCC 90 BCS B0 BEQ F0 BIT 24 2C BMI 30 BNE D0 BPL 10 BRK 00 BVC 50 BVS 70 CLC 18 CLD D8 CLI 58 CLV B8 CMP C1 C5 C9 CD D1 D5 D9 DD CPX E0 E4 EC CPY C0 C4 CC DEC C6 CE D6 DE DEX CA DEY 88 EOR 41 45 49 4D 51 55 59 5D INC E6 EE F6 FE INX E8 INY C8 JMP 4C 6C JSR 20 LDA A1 A5 A9 AD B1 B5 B9 BD LDX A2 A6 AE B6 BE LDY A0 A4 AC B4 BC LSR 46 4A 4E 56 5E NOP EA ORA 01 05 09 0D 11 15 19 1D PHA 48 PHP 08 PLA 68 PLP 28 ROL 26 2A 2E 36 3E ROR 66 6A 6E 76 7E RTI 40 RTS 60 SBC E1 E5 E9 ED F1 F5 F9 FD SEC 38 SED F8 SEI 78 STA 81 85 8D 91 95 99 9D STX 86 8E 96 STY 84 8C 94 TAX AA TAY A8 TSX BA TXA 8A TXS 9A TYA 98 Aliases, pseudo instructions ASL 0A BGE B0 BLT 90 GCC 4C 90 GCS 4C B0 GEQ 4C F0 GGE 4C B0 GLT 4C 90 GMI 30 4C GNE 4C D0 GPL 10 4C GVC 4C 50 GVS 4C 70 LSR 4A ROL 2A ROR 6A SHL 06 0A 0E 16 1E SHR 46 4A 4E 56 5E ------------------------------------------------------------------------------- 6502 illegal opcodes This processor is a standard 6502 with the NMOS illegal opcodes. Additional opcodes ANC 0B ANE 8B ARR 6B ASR 4B DCP C3 C7 CF D3 D7 DB DF ISB E3 E7 EF F3 F7 FB FF JAM 02 LAX A3 A7 AB AF B3 B7 BF LDS BB NOP 04 0C 14 1C 80 RLA 23 27 2F 33 37 3B 3F RRA 63 67 6F 73 77 7B 7F SAX 83 87 8F 97 SBX CB SHA 93 9F SHS 9B SHX 9E SHY 9C SLO 03 07 0F 13 17 1B 1F SRE 43 47 4F 53 57 5B 5F Additional aliases AHX 93 9F ALR 4B AXS CB DCM C3 C7 CF D3 D7 DB DF INS E3 E7 EF F3 F7 FB FF ISC E3 E7 EF F3 F7 FB FF LAE BB LAS BB LXA AB TAS 9B XAA 8B ------------------------------------------------------------------------------- 65DTV02 opcodes This processor is an enhanced version of standard 6502 with some illegal opcodes. Additionally to 6502 illegal opcodes BRA 12 SAC 32 SIR 42 Additional pseudo instruction GRA 12 4C These illegal opcodes are not valid ANC 0B JAM 02 LDS BB NOP 04 0C 14 1C 80 SBX CB SHA 93 9F SHS 9B SHX 9E SHY 9C These aliases are not valid AHX 93 9F AXS CB LAE BB LAS BB TAS 9B ------------------------------------------------------------------------------- Standard 65C02 opcodes This processor is an enhanced version of standard 6502. Additional opcodes ADC 72 AND 32 BIT 34 3C 89 BRA 80 CMP D2 DEC 3A EOR 52 INC 1A JMP 7C LDA B2 ORA 12 PHX DA PHY 5A PLX FA PLY 7A SBC F2 STA 92 STZ 64 74 9C 9E TRB 14 1C TSB 04 0C Additional aliases and pseudo instructions CLR 64 74 9C 9E DEA 3A GRA 4C 80 INA 1A ------------------------------------------------------------------------------- R65C02 opcodes This processor is an enhanced version of standard 65C02. Additional opcodes BBR 0F 1F 2F 3F 4F 5F 6F 7F BBS 8F 9F AF BF CF DF EF FF RMB 07 17 27 37 47 57 67 77 SMB 87 97 A7 B7 C7 D7 E7 F7 ------------------------------------------------------------------------------- W65C02 opcodes This processor is an enhanced version of R65C02. Additional opcodes STP DB WAI CB Additional aliases HLT DB ------------------------------------------------------------------------------- W65816 opcodes This processor is an enhanced version of W65C02. Additional opcodes ADC 63 67 6F 73 77 7F AND 23 27 2F 33 37 3F BRL 82 CMP C3 C7 CF D3 D7 DF COP 02 EOR 43 47 4F 53 57 5F JMP 5C DC JSL 22 JSR FC LDA A3 A7 AF B3 B7 BF MVN 54 MVP 44 ORA 03 07 0F 13 17 1F PEA F4 PEI D4 PER 62 PHB 8B PHD 0B PHK 4B PLB AB PLD 2B REP C2 RTL 6B SBC E3 E7 EF F3 F7 FF SEP E2 STA 83 87 8F 93 97 9F TCD 5B TCS 1B TDC 7B TSC 3B TXY 9B TYX BB XBA EB XCE FB Additional aliases CSP 02 CLP C2 JML 5C DC SWA EB TAD 5B TAS 1B TDA 7B TSA 3B ------------------------------------------------------------------------------- 65EL02 opcodes This processor is an enhanced version of standard 65C02. Additional opcodes ADC 63 67 73 77 AND 23 27 33 37 CMP C3 C7 D3 D7 DIV 4F 5F 6F 7F ENT 22 EOR 43 47 53 57 JSR FC LDA A3 A7 B3 B7 MMU EF MUL 0F 1F 2F 3F NXA 42 NXT 02 ORA 03 07 13 17 PEA F4 PEI D4 PER 62 PHD DF PLD CF REA 44 REI 54 REP C2 RER 82 RHA 4B RHI 0B RHX 1B RHY 5B RLA 6B RLI 2B RLX 3B RLY 7B SBC E3 E7 F3 F7 SEA 9F SEP E2 STA 83 87 93 97 STP DB SWA EB TAD BF TDA AF TIX DC TRX AB TXI 5C TXR 8B TXY 9B TYX BB WAI CB XBA EB XCE FB ZEA 8F Additional aliases CLP C2 HLT DB ------------------------------------------------------------------------------- 65CE02 opcodes This processor is an enhanced version of R65C02. Additional opcodes ASR 43 44 54 ASW CB BCC 93 BCS B3 BEQ F3 BMI 33 BNE D3 BPL 13 BRA 83 BSR 63 BVC 53 BVS 73 CLE 02 CPZ C2 D4 DC DEW C3 DEZ 3B INW E3 INZ 1B JSR 22 23 LDA E2 LDZ A3 AB BB NEG 42 PHW F4 FC PHZ DB PLZ FB ROW EB RTS 62 SEE 03 STA 82 STX 9B STY 8B TAB 5B TAZ 4B TSY 0B TYS 2B TZA 6B Additional aliases ASR 43 BGE B3 BLT 93 NEG 42 RTN 62 This alias is not valid CLR 64 74 9C 9E