Original written for DOS by Marek Matula of Taboo, then ported to ansi C by BigFoot/Breeze, and finally added 65816 support, CPU64, DTV, illegal opcodes, optimizations, multi pass compile and a lot of features by Soci/Singular. More fixing and "Force Long" operand mode added by Tom-Cat/Nostalgia.
Compile it with "make", if argp is missing or search.h then try to add -DWIN32 to CFLAGS. (that workarounds these, but there's a small performance loss)
On non-GNU systems try "make -f Makefile2" to compile.
Syntax is the same as the well known Turbo Assembler on c64, so you can port your sources easy by only replacing the CR at the end of each line.
Maintainer: soci at c64.rulez.org
64tass is a command line compiler, the source can be written in any text editor. As a minimum the source filename must be given on command line.
64tass 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.prg
This way demo.prg is recreated whenever source.asm, makros.asm, pic.drp or music.bin had changed. For cross development with VICE here's another Makefile example:
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 killall x64 || true x64 demo.prg
Of course it's not much harder to create something similar for win32 (make.bat):
64tass.exe -C -a -B -i source.asm -o demo.tmp pucrunch.exe -ffast -x 2048 demo.tmp >demo.prg x64.exe demo.prg
Another useful thing is 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 ss,2005 .null $9e,^start;will be sys 4096 ss .word 0 *=$1000 start rts
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 --
These references are also useful in macros, but this can create some nice traps, as macros are copied into the code, with the internal references.
bne + #somemakro ;let's hope that this macro does + nop ;not contain forward references...
For writing short code (4k intro, whatever) there are some special pseudo instructions. These are automatically compiled
as relative branches when the distance is short and as jumps when longer. (GEQ
, GNE
, GCC
, GCS
, GPL
, GMI
, GVC
, GVS
)
There's one more called GRA
for CPUs supporting BRA
, which is expanded to BRL
(if available) or JMP
(otherwise).
lda #3 gne label ;compiled as bne, or jmp nop label
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
. This have to be enabled on the command line using the "--long-branch" option.
bcc veryfar ;compiled as "bcc veryfar" or ; bcs sk ; jmp veryfar ;sk
There are some other tips below in the descriptions.
Normally no conversion takes place, this is for backwards compatibility with a DOS based Turbo Assembler editor, which could create petscii files for 6502tass. (with control characters also of course)
Using this option will convert 'a'-'z' and 'A'-'Z' into the correct petscii range of $41-$5A and $C1-$DA, which is more suitable for an ascii editor.
64tass a.asm lda #"a" -> result: $A9, $61 .text "1aA" -> result: $31, $61, $41 64tass --ascii a.asm lda #"a" -> result: $A9, $41 .text "1aA" -> result: $31, $41, $C1
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.
64tass a.asm label nop Label nop ;double defined... 64tass --case-sensitive a.asm label nop Label nop ;Ok, it's a different label...
64tass -D ii=2 a.asm lda #ii ;result: $a9, $02
64tass --nonlinear a.asm *=$1000 lda #2 *=$2000 nop result: $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)
64tass a.asm -o a.prg
64tass --no-warn a.asm
64tass --wordstart --m65816 a.asm
.cpu
directive in the source.
64tass --m65c02 a.asm stz $d020 ;65c02 instruction
64tass --m6502 a.asm lax $14 ;illegal instruction
64tass --m65xx a.asm lda $14 ;regular instructions
64tass --m65dtv02 a.asm sac #$00
64tass --m65816 a.asm lda $123456,x
64tass --mcpu64 a.asm ldx $123456
64tass -l labels.txt a.asm *=$1000 label result (labels.txt): label = $1000 ; *** unused
64tass -L list.txt a.asm *=$1000 ldx #0 loop dex bne loop rts result (list.txt): ;6502/65C02/65816/CPU64/DTV Turbo Assembler V1.4x 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
64tass --no-monitor -L list.txt a.asm result (list.txt): ;6502/65C02/65816/CPU64/DTV Turbo Assembler V1.4x 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
64tass --no-source -L list.txt a.asm result (list.txt): ;6502/65C02/65816/CPU64/DTV Turbo Assembler V1.4x 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
( )
lda #(4+2)*3
<
– lower byte>
– higher byte`
– bank bytelda #<label ldy #>label jsr $ab1e
=
– equal<
– less>
– more!=
– non equal>=
– more or equal<=
– less or equal.if ntsc=1 nop .fi
+
– add-
– substract*
– multiply/
– divide//
– modulo|
– or^
– xor&
– and<<
– shift left>>
– shift right-
– negate~
– invert!
– notlda #((bitmap & $2000) >> 10) | ((screen & $3c00) >> 6) sta $d018
*=$1000
.text
.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)
.char -33, 57
.text
, but the last byte is "shifted". Characters
in range $c0-$df are converted to $60-$7f, $ff is converted to
$7e, and everything else >=$80 will cause an error. No conversion
if screen encoding is done, >=$80 will always cause an error.
ldx #0 loop lda txt,x php and #$7f jsr $ffd2 inx plp bpl loop rts txt .shift "some text"
.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
lda #0 asl tax lda rets+1,x pha lda rets,x pha rts rets .rta $fce2
.word $2342, $4555
.int -533, 4433
.long $123456
.offs 100
name .macro lda #\1 ;first parameter .endm #name 23 ;call macro
.for ue=0,ue<10,ue=ue+1 .byte ue .next
.rept
or .for
)
.if oeu=1 nop .else lda #1 .fi
.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
.rept 100 nop .next
.include macros.asm
.binary stuffz.bin ;simple include .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.dmc",2 ;strip load address
.comment lda #1 ;this won't be compiled sta $d020 .endc
.page table .byte 0,1,2,3,4,5,6,7 .endp
.logical $300 drive lda #$80 sta $00 jmp drive ;jmp $300 rts .here
.al lda #$4322
.xl ldx #$1000
.error "Unfinished here..."
.warn "FIXME: handle negative values too!"
.cerror *>$1200,"Program too long!"
.cwarn *>$1200,"This may not work!"
ize .proc nop cucc nop .pend jsr ize jmp ize.cuccIf "ize" is not referenced then the code won't be compiled at all! All labels inside are local.
.databank $10 ;$10xxxx
.dpage $400
.repeat
!
.fill $100 ;no fill, just skip $100 bytes .fill $4000,0 ;16384 bytes of 0
.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
.enc screen ;screencode mode .text "text with screencodes" cmp #"u" ;compare screencode .enc none ;normal again mode cmp #"u" ;compare ascii
.cpu 6502 ;standard 65xx .cpu 65c02 ;CMOS 65C02 .cpu 6502i ;NMOS 65xx .cpu 65816 ;W65C816 .cpu cpu64 ;CPU64 .cpu 65dtv02 ;65dtv02 .cpu default ;cpu set on commandline
opcode | hex | |
---|---|---|
ANC | $0b | |
ANE (XAA) | $8b | |
ARR | $6b | |
ASR (ALR) | $4b | |
DCP (DCM) | $c3, $c7, $cf, $d3, $d7, $db, $df | |
ISB (INS, ISC) | $e3, $e7, $ef, $f3, $f7, $fb, $ff | |
JAM | $02 | |
LAE (LAS, LDS) | $bb | |
LAX | $a3, $a7, $ab, $af, $b3, $b7, $bf | |
LXA (LAX #) | $ab | |
RLA | $23, $27, $2f, $33, $37, $3b, $3f | |
RRA | $63, $67, $6f, $73, $77, $7b, $7f | |
SAX | $83, $87, $8f, $97 | |
SBX (AXS) | $cb | |
SHA (AHX) | $93, $9f | |
SHS (TAS) | $9b | |
SHX | $9e | |
SHY | $9c | |
SLO | $03, $07, $0f, $13, $17, $1b, $1f | |
SRE | $43, $47, $4f, $53, $57, $5b, $5f |
opcode | hex | |
---|---|---|
BRA | $80 | |
DEA | $3a | |
INA | $1a | |
PHX | $da | |
PHY | $5a | |
PLX | $fa | |
PLY | $7a | |
TRB | $14, $1c | |
TSB | $04, $0c | |
STZ | $64, $74, $9c, $9e |
opcode | hex | |
---|---|---|
BRA | $12 | |
SAC | $32 | |
SIR | $42 | |
ANE (XAA) | $8b | |
ARR | $6b | |
ASR (ALR) | $4b | |
DCP (DCM) | $c3, $c7, $cf, $d3, $d7, $db, $df | |
ISB (INS, ISC) | $e3, $e7, $ef, $f3, $f7, $fb, $ff | |
LAX | $a3, $a7, $ab, $af, $b3, $b7, $bf | |
LXA (LAX #) | $ab | |
RLA | $23, $27, $2f, $33, $37, $3b, $3f | |
RRA | $63, $67, $6f, $73, $77, $7b, $7f | |
SAX | $83, $87, $8f, $97 | |
SLO | $03, $07, $0f, $13, $17, $1b, $1f | |
SRE | $43, $47, $4f, $53, $57, $5b, $5f |