Clones - An NES Emulator
Table of Contents
- 1 Links
- 2 Overview
- 3 ROM Parsing
- 4 Mapper Interface
- 5 Memory Interface
- 6 Opcode Data
- 7 Disassembler
- 8
CPU
Core - 9 Picture Processing Unit
- 10 The Rendering Logic
- 11 Input Handling
- 12 Debugging Utilities
[in package CLONES.DOCS]
1 Links
Here are links to the current website and git repo.
2 Overview
Clones is an early-stage NES emulator written in Common Lisp.
It is inspired by long standing beliefs about the power of computers for experiential learning. See: Research Goals.
Why another emulator?
Reading things teaches people how to write. Analogous, if we are to place programming at the same fundamental level, using a program should teach how it works. But we don't see this.
- Tony Garnock-Jones, @leastfixedpoint
Many NES emulators already exist on the web, the desktop, and elsewhere. Clones is intended to be a readable, tested, and compact code base sufficient for mostly accurate emulation of many but not all popular Nintendo titles.
However, my goal is not to be able to relive childhood nostalgia since that need is thoroughly solved. Clones exists to support curious programmers learning about how the system and its titles worked rather than being a vehicle for reliving the past. Running the games is just a prerequisite.
The codebase strives to be accessible for learning about emulation, sacrificing total accuracy and performance for clarity and ease of modification. Once some key games (Super Mario Bros, Mega Man 2) are playable, focus will shift towards building out debugging and reverse engineering tools.
In addition to the usual tools to disassemble memory or view VRAM, we hope to support building a directed graph of blocks and jumps as games are played. Afterwards, we'll provide tools for users to visualize and annotate the graph with notes about the code. This will move us towards our overall goal of making the high-level structure of programs "discoverable" through using them, calling back to the Tony Garnock-Jones quote above.
Current Status
Work has just begun so nothing is playable yet. Hang in there.
ROM Support: ✅
NROM Mapper: ✅
CPU Opcodes: ✅
PPU Registers: ✅
Rendering - Timing: ✅
Rendering - Backgrounds: ✅
Rendering - Sprites: ⌛
Rendering - Scrolling: ⌛
Input Handling: ✅
MMC1 Mapper: ✅
MMC3 Mapper: ❌
Audio Support: ❌
3 ROM Parsing
[in package CLONES.ROM]
[function] PARSE-ROM PATHNAME
Attempt to parse the file at
PATHNAME
as an Nintendo ROM, returning a property list suitable for passing toMAKE-INSTANCE
for an appropriateMAPPER
. AnINVALID-ROM
condition will be signalled if the header does not conform to the iNES format.
-
Signalled when the file passed to
PARSE-ROM
does not correspond to the iNES format.
4 Mapper Interface
[in package CLONES.MAPPERS]
-
A Mapper is a virtual representation of a game cartridge, referenced by the PPU for purposes of accessing graphical data (
CHR
) and by the CPU for purposes of accessing program code (PRG
).
[function] LOAD-ROM &OPTIONAL (PATHNAME (
ASDF/SYSTEM:SYSTEM-RELATIVE-PATHNAME
:CLONES
"roms/nestest.nes"))Given a
PATHNAME
to a valid Nintendo ROM, process the file usingPARSE-ROM
and return an appropriate instance ofMAPPER
for the cartridge type of the game. AnUNIMPLEMENTED-MAPPER
condition will be signalled if the cartridge type is not yet supported by clones. If noPATHNAME
is supplied, the NEStest ROM will be used.
[condition] UNIMPLEMENTED-MAPPER
Signalled when no subclass of
MAPPER
implements the cartridge for the file supplied toLOAD-ROM
.
- [generic-function] MAPPER-PATHNAME OBJECT
[generic-function] GET-PRG MAPPER ADDRESS
Retrieve the value at
ADDRESS
from thePRG
bank ofMAPPER
.
[generic-function] SET-PRG MAPPER ADDRESS VALUE
Set
ADDRESS
in thePRG
bank ofMAPPER
toVALUE
.
[generic-function] GET-CHR MAPPER ADDRESS
Retrive the value at
ADDRESS
from theCHR
bank ofMAPPER
.
[generic-function] SET-CHR MAPPER ADDRESS VALUE
Set
ADDRESS
in theCHR
bank ofMAPPER
toVALUE
.
- [accessor] MIRRORING MAPPER (:MIRRORING)
5 Memory Interface
[in package CLONES.MEMORY]
- [function] MAKE-MEMORY &KEY (RAM (
MAKE-OCTET-VECTOR
2048)) (PPU (MAKE-PPU
)) (CONTROLLER (MAKE-CONTROLLER
)) (CART (LOAD-ROM
))
- [function] FETCH MEMORY ADDRESS
- [function] STORE MEMORY ADDRESS VALUE
- [function] FETCH-WORD MEMORY ADDRESS
- [function] FETCH-INDIRECT MEMORY START
- [reader] MEMORY-PPU MEMORY (:PPU)
- [reader] MEMORY-CART MEMORY (:CART)
- [reader] MEMORY-CONTROLLER MEMORY (:CONTROLLER)
- [accessor] MEMORY-DMA? MEMORY (= NIL)
- [function] SWAP-CART MEMORY RELATIVE-PATH
6 Opcode Data
[in package CLONES.OPCODES]
[function] FIND-OPCODE BYTE
Find the
OPCODE
encoded asBYTE
.
- [structure-accessor] OPCODE-NAME OPCODE
- [structure-accessor] OPCODE-CODE OPCODE
- [structure-accessor] OPCODE-SIZE OPCODE
- [structure-accessor] OPCODE-TIME OPCODE
7 Disassembler
[in package CLONES.DISASSEMBLER]
[function] DISASM MEMORY START END
Loop through
MEMORY
fromSTART
toEND
printing disassembly for each instruction found in the specified range. An error will be thrown if illegal instructions are present or if the start index is not the beginning of a 6502 instruction.
[function] DISASSEMBLE-INSTRUCTION MEMORY INDEX &KEY (STREAM
T
)Disassemble a single instruction from
MEMORY
beginning atINDEX
.STREAM
is theFORMAT
destination of the disassembly output.
8 CPU
Core
[in package CLONES.CPU]
- [function] MAKE-CPU &KEY (MEMORY (
MAKE-MEMORY
))
- [accessor] CPU-MEMORY CPU (:MEMORY)
- [accessor] CPU-CYCLES CPU (= 0)
-
Step the
CPU
over the current instruction.
- [function] RESET CPU
[function] NOW CPU &KEY (STREAM
T
)Disassemble the current instruction pointed to by the
CPU
's program counter.STREAM
is theFORMAT
destination for the disassembly.
- [function] NMI CPU
- [function] CHANGE-GAME CPU RELATIVE-PATH
9 Picture Processing Unit
[in package CLONES.PPU]
- [function] MAKE-PPU &KEY (NAME-TABLE (
MAKE-OCTET-VECTOR
4096)) (CART (LOAD-ROM
))
- [function] READ-PPU PPU ADDRESS
- [function] WRITE-PPU PPU ADDRESS VALUE
- [function] SET-VBLANK! PPU VALUE
- [function] READ-PALETTE PPU ADDRESS
- [function] MAKE-SPRITE PPU NUMBER
- [function] EVALUATE-SPRITES PPU SCANLINE
- [function] SET-SPRITE-OVERFLOW! PPU VALUE
- [function] SET-SPRITE-ZERO-HIT! PPU VALUE
-
See: https://www.nesdev.org/wiki/PPU_scrolling#Tile_and_attribute_fetching
[function] FETCH-SCANLINE-BYTES PPU TILE-DESCRIPTOR
Fetch the low and high bytes of the pattern matching
TILE-DESCRIPTOR
that are appropriate for display on the current scanline ofPPU
.
[function] FETCH-TILE-BYTES PPU TILE-DESCRIPTOR
Fetch all 16 bytes of the the pattern corresponding to
TILE-DESCRIPTOR
.
[generic-function] FLIP-X? TILE-DESCRIPTOR
Check whether the X-axis of
TILE-DESCRIPTOR
should be flipped.
[generic-function] FLIP-Y? TILE-DESCRIPTOR
Check whether the Y-axis of
TILE-DESCRIPTOR
should be flipped.
[generic-function] COMPUTE-X-OFFSET PPU TILE-DESCRIPTOR
Compute the X offset for the tile currently being rendered by
PPU
.
- [function] PALETTE-LOW-BITS LOW-BYTE HIGH-BYTE INDEX
[generic-function] PALETTE-HIGH-BITS PPU TILE-DESCRIPTOR
Determine the 2 high bits of the palette index for
TILE-DESCRIPTOR
.
[function] FINE-SCROLL-VERTICAL! PPU
A scroll operation that conceptually occurs at the end of each scanline.
[function] COARSE-SCROLL-HORIZONTAL! PPU
A scroll operation that conceptually occurs at the end of each 8-pixel tile.
10 The Rendering Logic
[in package CLONES.RENDERER]
- [function] MAKE-RENDERER &KEY (PPU (
MAKE-PPU
)) (ON-NMI (CONSTANTLY
NIL
)) ON-FRAME
[generic-function] SYNC RENDERER CPU FRAMEBUFFER
Synchronize the renderer to the
CPU
and return the next scanline.
- [function] RENDER-PIXEL FRAMEBUFFER X Y COLOR-INDEX
11 Input Handling
[in package CLONES.INPUT]
- [function] READ-CONTROLLER CONTROLLER
- [function] RESET-CONTROLLER CONTROLLER
- [function] UPDATE-BUTTON CONTROLLER BUTTON VALUE
12 Debugging Utilities
[in package CLONES.DEBUG]
- [function] FOR-SPRITES PPU CALLBACK
- [function] FOR-BACKGROUND PPU CALLBACK &KEY (NAME-TABLE 0)
- [function] DUMP-GRAPHICS BUFFER PPU &KEY ITERATOR MARGIN