git clone ''

(ql:quickload :cl-6502)

cl-6502 - A Readable CPU Emulator

cl-6502 is a Common Lisp emulator, assembler and disassembler for the MOS 6502 processor. In case that sounds weird to you, the MOS 6502 is famous for its use in…

I gave a talk on cl-6502 called ‘On Programmer Archaeology’. You can watch it on Vimeo or grab the slides. A few notes on why I'm writing it are here and minor notes on the design are here.


Inspired by Luke Gorrie's call for Readable Programs, there is a readable PDF book of the source. You can also produce it from the git repo with: cd repo/src && make book. You'll need make, pandoc, and some latex packages (texlive-luatex, texlive-xetex, and texlive-latex-extra on debian) installed to build it yourself.


You are strongly encouraged to use this library via Quicklisp. Simply start your lisp and run: (ql:quickload 'cl-6502).

Getting Started

In particular, asm, disasm, execute, step-cpu, and reset are likely of interest.

A simple example:

  1. Load cl-6502 and switch to the cl-6502 package.
  2. Write some 6502 code and run it through asm (e.g. (asm "brk")) to get a bytevector to execute.
  3. Load it into memory with (setf (get-range 0) *my-bytevector*).
  4. Set the program counter to 0 with (setf (6502:cpu-pc *cpu*) 0).
  5. Run it with (run *cpu*) or manually step through it with (step-cpu *cpu* (get-byte (cpu-pc *cpu*))).
  6. (reset) the CPU as necessary and keep hacking! :)

Supported Assembler Syntax

There are sexp-based and string-based assemblers, both invoked via asm. The string-based assembler expects statements to be separated by newlines. The sexp-based assembler expects each statement to be in its own list. Disassembling to both formats is supported via disasm and disasm-to-list. Semicolons are treated as “comment to end-of-line” in the string assembler.

| Addressing Mode | SEXP-based format | String format  |
|   Implied       |  (:brk)           | "brk"          |
|   Immediate     |  (:lda :#$00)     | "lda #$00"     |
|   Accumulator   |  (:rol :a)        | "rol a"        |
|   Zero-page     |  (:lda :$03)      | "lda $03"      |
|   Zero-page, X  |  (:lda :$03.x)    | "lda $03, x"   |
|   Zero-page, Y  |  (:ldx :$03.y)    | "ldx $03, y"   |
|   Absolute      |  (:sbc :$0001)    | "sbc $0001"    |
|   Absolute, X   |  (:lda :$1234.x)  | "lda $1234, x" |
|   Absolute, Y   |  (:lda :$1234.y)  | "lda $1234, y" |
|   Indirect      |  (:jmp :@1234)    | "jmp ($1234)   |
|   Indirect, X   |  (:lda :@12.x)    | "lda ($12), x" |
|   Indirect, Y   |  (:lda :@34.y)    | "lda ($34), y" |
|   Relative      |  (:bne :&fd)      | "bne &fd"      |


To run the tests, after you've loaded cl-6502 just run (asdf:oos 'asdf:test-op 'cl-6502). You may need to (ql:quickload 'cl-6502-tests) to ensure that the fiveam dependency is satisfied first.


The code is under a BSD license except for docs/6502.txt and tests/6502_functional_test.a65 which are only present by ‘mere aggregation’ and not strictly part of my sources.