▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒
▒▒▒▒▒▒ ▒▒▒ ▒▒▒ ▒▒▒▒ ▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒
▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒ ▒▒▒ ▒▒▒ ▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒
▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒▒▒
▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
I wrote a Chip8 virtual machine. Chip8 was implemented by a number of computers in the 70s and HP calculators in the 80s. Instead of using actual microprocessor opcodes, it was always designed to be a virtual language and be interpreted at runtime.
(A high quality video version of the gif is found here.)
You'll need a Linux computer with a compiler that supports C++17 and make
installed.
This project has no third-party dependencies and renders on the terminal.
To compile:
make
To run:
./play path/to/rom.ch8
Compile and run the sanity tests (mostly for CI):
make test
To clean all object and executable files:
make clean
The keys along with their descriptions are listed on the right panel of the UI. You can press each [B]racketed key to send an input.
Chip8's hex keypad has been mapped to the keyboard in the following way by default:
+---+---+---+---+ +---+---+---+---+
| 1 | 2 | 3 | C | | 1 | 2 | 3 | 4 |
+---+---+---+---+ +---+---+---+---+
| 4 | 5 | 6 | D | | q | w | e | r |
+---+---+---+---+ --> +---+---+---+---+
| 7 | 8 | 9 | E | | a | s | d | f |
+---+---+---+---+ +---+---+---+---+
| A | 0 | B | F | | z | x | c | v |
+---+---+---+---+ +---+---+---+---+
You can edit the keys in keypad.hpp.
Each rom (.ch8
file in roms
directory) is accompanied by a .cfg
file.
A .cfg
file is optional and describes each control key and sets the frequency
each rom will run at. Chip8 games used to run on different machines over the
decades so each one runs on a different frequency. More detailed description of
.cfg
files can be found at roms/README.md
.
- CPU and renderer.
- UI including controls and debugger view including pause, exit, and stepping key.
- .cfg file for each ROM with editable presets (such as emulation frequency) and key descriptions for each rom. Found at
roms/*.cfg
. - Togglable quirks [1] (XO-CHIP and SCHIP1.1 quirks are offered together).
- Configurable keys.
- CI.
- Sound; probably never going to implement this.
<--- Main memory --->
Read Registers
<- only -> Stack 0 1 15
+-----------+-------+ +-----------+ +--+ +--+ +--+
| | | | | | | | | | | | . . . | |
| Instr/s | Fonts | +-----------+ +--+ +--+ +--+
| | | 0x18 ^ ^
+-----------+-------+ ^ | |
0xFFF ^ 0x200 0x0 | 2 bytes 1 byte
| ^ |
| | |
Program ------+ | Delay Sound
start | | timer time
+-----------------------|------+ +---+ +---+
Program | Stack | | | | | |
counter | pointer | Index register +---+ +---+
(PC) | (SP) | (I) 1 byte 1 byte
+----+ | +----+ | +----+
| |----+ | |------+ | |
+----+ +----+ +----+
2 bytes 2 bytes 2 bytes
Monochrome renderer
+----------------------+ ^
| | |
| | 32
| | |
+----------------------+ v
<-------- 64 ---------->
Here is the idea of this implementation. Details are omitted.
+-------+
| start |
+-------+
|
+------>---------------------+
| |
+------>--------+ |
| | |
| +~~~~~~~~~+ +~~~~~~~~~~+
+---------+ | Delay & | | Key |
| Set PC | | sound | | listener |
+---------+ | thread | | thread |
| +~~~~~~~~~+ +~~~~~~~~~~+
v | |
+-----<-------+ | |
| | +-->--+--<---+
+-------+ | |
| Fetch | ^ x
+-------+ | / \
| | / \
instruction | / \
v +---x End x----------+
+--------+ n \ loop/ y |
| Decode | \ ? / |
+--------+ \ / |
| x v
opcode | |
v | |
+---------+ | +--------------+
| Execute | | | Join threads |
+---------+ | +--------------+
| | |
v ^ v
| | |
+--------+ | |
| Render | | |
+--------+ | |
| | |
+--------->-----------+ |
|
+------------------------------------+
|
v
+-----+
| End |
+-----+
The emulator uses 3 threads in total. More detailed comments on how the fetch-
decode-execute cycle works are found in the source file chip8.cpp
.
Instructions are implemented in a neat but non-optimised instruction table.
This suffices since we typically run on a 300 to 2000 instruction per second
clock. More comprehensive techninal resources are found in the reference
section.
As mentioned in the features, togglable XO-CHIP and SCHIP1.1 quirks are offered together. The most noticeable quirk is the sprite wrapping; if any pixel of a sprite meets the right border, it's wrapped around. If quirks are off, then the sprite is clipped till the origin crosses the right wall. This is demonstrated below during a Brix gameplay.
- Get rid of excessive screen flicker (f27bd6d)
- Several race conditions fixed - thanks to @Skeeto.