Common Lisp Package: CL-6502

Homepage: <a href="http://github.com/redline6561/cl-6502">Github</a>

README:

cl-6502, or The 6502...IBM 704 edition ;)

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 and related ideas which is online here. The slides are available separately here. A few notes on why I'm writing it are here and minor notes on the design are here.

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

Getting Started

  • 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 BIT and AND so you likely don't want to :use it in your own packages.

In particular, asm, disasm, execute, 6502-step, 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 and run it with (execute *cpu* *my-bytevector*) OR
  4. Load it with (setf (get-range 0) *my-bytevector*)
  5. Set the program counter to 0 with (setf (6502:cpu-pc *cpu*) 0)
  6. Manually step through it with (6502-step *cpu* (get-byte (6502:immediate *cpu*)))
  7. (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 :&amp;fd) | "bne &amp;fd" |

Hacking

  • Using Quicklisp: For local development, git clone this repository into the local-projects subdirectory 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.

License

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

FUNCTION

Public

CURRENT-INSTRUCTION (CPU &OPTIONAL PRINT-P)

Return a list representing the current instruction. If PRINT-P is non-nil, print the current address and instruction and return NIL.

DISASM (START END)

Disassemble memory from START to END.

DISASM-TO-LIST (START END)

Disassemble a given region of memory into a sexp-based format.

DISASM-TO-STR (START END)

Call DISASM with the provided args and return its output as a string.

GET-BYTE (ADDRESS)

Get a byte from RAM at the given address.

SETFGET-BYTE (NEW-VAL ADDRESS)

Set ADDRESS in *ram* to NEW-VAL.

GET-RANGE (START &OPTIONAL END)

Get a range of bytes from RAM, starting from START and stopping at END if provided.

SETFGET-RANGE (BYTEVECTOR START)

Replace the contents of RAM, starting from START with BYTEVECTOR.

GET-WORD (ADDRESS &OPTIONAL WRAP-P)

Get a word from RAM starting at the given address.

SETFGET-WORD (NEW-VAL ADDRESS)

Set ADDRESS and (1+ ADDRESS) in *ram* to NEW-VAL, little endian ordering.

JIT-EXECUTE (CPU)

Execute the CPU with the dynamic recompiler.

JIT-STEP (CPU PC)

If the current block has been JIT compiled, run it, otherwise JIT compile it.

GENERIC-FUNCTION

Public

6502-STEP (CPU OPCODE)

Step the CPU through the next instruction, returning the CPU or :done.

ASM (SOURCE)

Assemble SOURCE into a bytevector and return it.

EXECUTE (CPU &OPTIONAL PROGRAM)

Step the CPU until a BRK. If a PROGRAM bytevector is supplied, it will be placed at the beginning of *ram* and the PC will be set to 0.

NMI (OBJ)

Generate a non-maskable interrupt. Used for vblanking in NES.

RESET (OBJ)

Reset the OBJ to an initial state.

VARIABLE

Public

*CPU*

The 6502 instance used by opcodes in the package.

CLASS

Public

CPU

A 6502 CPU with an extra slot for tracking the cycle count/clock ticks.