6asm
6502 cross-assembler. NMOS official and undocumented opcodes, 65C02, macros, conditionals and multi-pass addressing. Accepts source written for tmpx, ACME and DASM. Outputs PRG, D64, CRT, Intel HEX and S-Record. MIT licence.
| Linux x64 | 6asm-2026.11.06-linux-x64.tar.gz | 5.5 MB |
| Windows x64 | 6asm-2026.11.06-win-x64.zip | 5.3 MB |
Unpack and put 6asm on your PATH. The Windows binary is unsigned, so SmartScreen may warn.
Full manual: every directive, operator and option.
Usage
6asm [options] input.asm
$ 6asm demo.asm
Assembled 127 bytes to demo.prg
Address range: $0801-$0880
Memory ranges:
$0801-$087F (127 bytes)
Passes: 3
Symbols: 230
$ 6asm --list --vice part2.asm
Listing file: part2.lst
VICE symbols: part2.lbl
Assembled 832 bytes to part2.prg
Address range: $2000-$2340
Memory ranges:
$2000-$233F (832 bytes)
Passes: 3
Symbols: 240
$ ls
ball.spr
bars.bas
demo.asm
demo.prg
mandel.kla
part2.asm
part2.lbl
part2.lst
part2.prg
| Flag | |
|---|---|
-o FILE |
output file |
-f bin\|hex\|srec\|d64\|crt |
format (default bin: PRG) |
-s |
symbol file |
--list |
listing with symbols, cross-reference and memory map |
--vice |
VICE monitor labels, breakpoints and watchpoints |
--analyze |
peephole suggestions |
-fillbyte=0xNN |
fill byte for gaps |
An example
This is the sprite mover from the sample demo. .for and sin() build the tables at assembly time, .sysline writes the BASIC stub, and the names for hardware (CHROUT, EXTCOL, colours) are predefined.
*= $0801
.sysline 10, start
sine_x .for i = 0, 256
.byte sin(i * 2 * PI / 256) * 110 + 130
.endfor

Cycle budgets
.cycles adds a timing column to the listing. .rasterbudget warns when a block is over.
$ cat budget.asm
*= $1000
.rasterbudget 63
.cycles
loop lda #$01
sta $d020
ldx #$08
wait dex
bne wait
.endcycles
rts
$ 6asm -q --list budget.asm
$ sed -n '/Line Addr/,/ rts$/p' budget.lst
Line Addr Code Cy Source
----- ----- -------------------------- --- ---------------------------------
; --- budget.asm --------------------------------------------------------------
1 *= $1000
2 .rasterbudget 63
3 .cycles
4 1000: A9 01 2 loop lda #$01
5 1002: 8D 20 D0 4 sta $d020
6 1005: A2 08 2 ldx #$08
7 1007: CA 2 wait dex
8 1008: D0 FD 2 bne wait
9 .endcycles
;>>> 12/63 cycles (51 free)
10 100A: 60 rts
Peephole suggestions
$ cat dup.asm
*= $1000
loop lda $d012
sta $d020
lda $d020
lda #$00
lda #$00
jmp loop
$ 6asm --analyze dup.asm
Optimization suggestions (3):
dup.asm:3-4: STA then LDA same address — LDA is redundant (A already holds value, saves 3B, 4 cycles)
dup.asm:4-5: LDA immediately followed by LDA — first load is dead code (saves 3B, 4 cycles)
dup.asm:5-6: LDA immediately followed by LDA — first load is dead code (saves 2B, 2 cycles)
Total potential savings: 8B, 10 cycles
Assembled 16 bytes to dup.prg
Address range: $1000-$1010
Memory ranges:
$1000-$100F (16 bytes)
Passes: 1
Symbols: 223
Checks for C64 code
.bank $35warns when code touches memory that bank does not map..spritealign,.charsetalign,.bitmapalignand similar align, then warn if the data overruns the block..assert * < $C000, "...".breakand.watchgo into the VICE label file.
Compatibility limits
Not supported from ACME: { } block bodies and anonymous +/- labels. !for takes 6asm's .for syntax. !pseudopc and !convtab are accepted but do nothing. ^ is XOR and & is AND, as in most assemblers, not ACME's power and octal.