Oliver VieBrooks

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 x646asm-2026.11.06-linux-x64.tar.gz5.5 MB
Windows x646asm-2026.11.06-win-x64.zip5.3 MB

Built 2026-11-06 from 923e1de. Self-contained: no .NET install needed. SHA256SUMS

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
The sample running in VICE: 6asm built it, 6pack linked and compressed it.
The sample running in VICE: 6asm built it, 6pack linked and compressed it.

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

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.