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...
- the Apple II,
- the original NES,
- the Commodore 64,
- the BBC Micro,
- and Michael Steil's phenomenal talk at 27C3.
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:
- Check out the docs for the cl-6502 package or have a look on quickdocs.
- Play around at the REPL!
- Use it to create your own wacky code artifacts.
- There is also a lower-level 6502 package if you really want to get your hands dirty. NOTE: The 6502 package shadows
ANDso you likely don't want to
:useit in your own packages.
A simple example:
- Load cl-6502 and switch to the
- Write some 6502 code and run it through
(asm "brk")) to get a bytevector to execute.
- Load it into memory with
(setf (get-range 0) *my-bytevector*).
- Set the program counter to 0 with
(setf (6502:cpu-pc *cpu*) 0).
- Run it with
(run *cpu*)or manually step through it with
(step-cpu *cpu* (get-byte (cpu-pc *cpu*))).
(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-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" |
- Using Quicklisp: For local development, git clone this repository into the
local-projectssubdirectory of quicklisp.
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.