Company: School of Physics and Astronomy, Cardiff University
Author: Dr Richard James Lewis
NI Product(s) Used: LabVIEW 2015
Industry: Higher Education
Overview
There are numerous CHIP-8 emulators implemented in almost every programming language. To my knowledge, this is the world’s first LabVIEW implementation. In this application note I summarise the history of the CHIP-8, my implementation of the CHIP-8 virtual machine in LabVIEW, and the positive educational use to which this project has been put. The source code for this fun project is included.
The CHIP-8 Virtual Machine
CHIP-8 is an interpreted programming language (virtual machine, VM) designed by Joseph Weisbecker in the mid 1970s. CHIP-8 was intended to make the programming of video games easier for early 8-bit microcomputers such as the COSMAC VIP and Telmac 1800. Rather than coding in the machine language of the host computer, a developer would instead code in the simple assembly language of the CHIP-8 VM.
Even by the standards of the day, CHIP-8 was primitive; the 8-bit VM had 4kB of RAM, a simple hexadecimal keypad for input, a monochrome (1 bit) display with a resolution of 64x32, and a beeper that could only play one note. The VM is limited to drawing a single sprite at a time, and collision detection is performed by a simple XOR of any new sprite to the display.
Two important later extensions to (and essentially supersets of) the CHIP-8 specification are (i.) HiRes CHIP-8, which provides an improved resolution of 64x64, and (ii.) S-CHIP8 (also called “Super CHIP-8” or “SCHIP”) which supports a resolution of 128x64 and full-screen scrolling. All three share the restrictive keypad input, single sprite handling, and rudimentary sound support.
Despite these limitations, it was possible to implement simple versions of early video games such as Pong, Breakout, and Lunar Lander. Over the years, inventive programmers have been able to code credible ports of classic video games such as Tetris, Pac-Man, and Joust. There are even demos available for the CHIP-8, such as the excellent SuperTrip8.
The following screenshots show the LabVIEW CHIP-8 emulator in action:
LabVIEW CHIP-8 emulator system requirements
The emulator is written using the base installation of LabVIEW 2015. No special packages are required.
LabVIEW CHIP-8 emulator specifications
The LabVIEW CHIP-8 emulator supports three variants of the CHIP-8 specification:
Graphical output is provided by a fixed aspect ratio display and the CHIP-8’s hexadecimal keypad is mapped to the PC keypad.
There are a couple of issues, mostly applicable to running early (i.e. 1970s and 1980s) code:
Known bugs
Emulating the CHIP-8 VM in LabVIEW
The architecture of CHIP-8 is very simple. The internal state of the CHIP-8 VM is completely defined by a single type-defined cluster, which contains:
Element |
Implementation |
Main memory (4kB) |
1D array of U8, 4096 elements |
Current opcode (fixed-length, 2 bytes) |
U16, representing two U8 |
CPU registers (0x0 to 0xF) |
1D array of U8, 16 elements |
Index register (2 bytes) |
U16, CHIP-8 addresses are 16-bit |
Program counter (2 bytes) |
U16, CHIP-8 addresses are 16-bit |
Graphics display (64x32, 64x64, or 128x64 for CHIP-8, HiRes CHIP-8 and S-CHIP8 modes) |
2D array of U8, sized accordingly |
Delay timer (0x0 to 0xFF) |
U8, decremented at 60Hz |
Sound timer (0x0 to 0xFF) |
U8, decremented at 60Hz |
Stack (32 bytes, representing 16 levels) |
1D array of U16, 16 elements |
Stack pointer |
U16, CHIP-8 addresses are 16-bit |
Keypad state (16 bytes, each representing the pressed state of |
1D array of U8, 16 elements |
Display mode |
Type defined enum (CHIP-8, HiRes CHIP-8, S-CHIP8 modes) |
The standard CHIP-8 specification has 35 opcodes, which are all fixed length (2 bytes), stored big-endian. The HiRes CHIP-8 specification adds 1 opcode to specify that the display should switch to 64x64 resolution. The S-CHIP8 1specification significantly expands the base opcode set with an additional 9 opcodes that add full-screen scrolling, high resolution font support and extra flag operations.
Architecturally, the emulator is very simple and implements the producer-consumer (events) state machine pattern:
There are 11 states for the CHIP-8 emulator, which are enumerated in a type-defined enum control:
State |
Purpose |
0: initialise |
Loads the default ROMs into memory. The CHIP-8 font set (80 bytes) is loaded into memory beginning at 0x0, the S-CHIP8 font set (160 bytes) into memory beginning at 0x50, and the splash screen ROM into memory beginning at 0x200. The program counter is set to 0x200. |
1: decode |
The opcode specified by the opcode pointer is decoded, updating the internal state of the CHIP-8 VM as required. The opcode pointer is incremented by 2 bytes. |
2: change mapping |
Currently not implemented: intended for future code to allow changing the mapping of the PC keypad to the CHIP-8 hexadecimal keypad. |
3: change theme |
Currently not implemented: intended for future code to allow changing the colours of the main display. |
4: soft reset |
Clears the display and sets the program pointer to 0x200 (where all CHIP-8 ROMs begin). |
5: hard reset |
Pauses the VM and starts a dialog to select a new ROM file. If a valid ROM file is selected, the VM internal state is re-initialised, the new ROM loaded into memory beginning 0x200, the program pointer set to 0x200, and the VM restarts. If the dialog is cancelled, the VM is unpaused and continues as before. |
6: shutdown |
Shuts down the emulator. Stops the consumer loop by sending a true constant to the conditional terminal, and stops the producer loop by signalling a value change to the Boolean control “stop”. |
7: update timers |
Decrements the sound timer and delay timer. |
8: update display |
Updates the display. The 2D array “Graphics display U8” (an element of the CHIP-8 VM internal state cluster) is written to the front panel indicator “display” (a simple intensity graph). |
9: poll keypad |
The defined mapping is scanned and the appropriate keypad states are set or reset depending on whether the key is pressed or not pressed. |
10: play sound |
Currently not implemented: intended for future code to play a sound when required. |
When run, the reference to the PC keypad is created, the CHIP-8 internal state is initialised; the value at all memory, locations, flags, counters, registers and pointers is set to zero. The “0: initialise” and “1: decode” states are then enqueued.
The emulator runs at an arbitrary rate of 1kHz (opcode decoding rate), with a 60Hz refresh rate for the display, keypad polling and timer updates. When run, the emulator proceeds through the following states provided the user does not interact with the front panel:
State |
Time per state |
Total time elapsed, notes |
0: initialise |
Unknown, <1ms |
Assumed 0ms, very small on the scale of ms |
1: decode (15 executions) |
1ms |
15ms |
9: poll keypad |
Unknown, <1ms |
15ms, keypad values set, screen updated, timers decremented |
7: update timers |
||
8: update display |
||
1: decode (15 executions) |
1ms |
30ms |
9: poll keypad |
Unknown, <1ms
|
30ms, keypad values set, screen updated, timers decremented |
7: update timers |
||
8: update display |
||
1: decode (15 executions) |
1ms |
40ms |
(Pattern repeats…) |
|
|
The consumer loop runs as fast as possible except when decoding an opcode, which is forced to have an execution time of 1ms. The event structure in the producer loop times out every 16ms in order to enqueue the “9: poll keypad”, “7: update timers”, and “8: update display states”. The implementation is a bit hacky; the “1: decode” state itself enqueues the “1: decode” state, but the overall effect is an approximately 60Hz refresh rate with a 1kHz execution speed, which is generally pleasant to use.
User front panel interaction is captured by the event structure in the producer loop, which will enqueue the appropriate event-handling case and another “1: decode state”. Note that user input via the PC keypad is captured by the “9: poll keypad state” and will only have an effect if the ROM is coded to read keypad input.
Why write a LabVIEW CHIP-8 emulator?
I originally wrote the prototype of this emulator for enjoyment in my spare time, but realised that I could turn it into a useful and fun application that could be used in my LabVIEW teaching. I have purposely left some features unimplemented so that students can gain experience working on an existing code base:
The code is extensively commented and documented throughout. As far as possible I have adhered to the style guidelines summarised in the excellent “Effective LabVIEW Programming” by Thomas J. Bress (NTS Press, 2013), so students can see an example of clean coding and project management style (and critique the parts where I might have cut a corner or two…!)
The CHIP-8 emulator project is included in all LabVIEW classes that I teach as an optional, student-lead exercise that allows students to practice modifying existing code either alone or in a development team and critiquing coding style against a specification.
How do I get started?
Load the project “chip-8_emulator.lvproj”. Under the virtual folder “main”, open “chip-8_emulator.vi” and run it. The splash screen ROM will load and run. You should see the following:
There are five buttons on the front panel:
Front panel button |
Purpose |
Load new ROM |
Launches a dialogue to browse for .ch8 files (CHIP-8 ROMs). Selected files are loaded and run automatically. |
Reset emulator |
Re-initialises the emulator state and runs the ROM currently in memory. Some games do not restart automatically and in those cases you will need to use this button. |
Disable Hwrap |
Disables horizontal wrapping of sprites. Required for some old ROMs. |
Disable Vwrap |
Disables vertical wrapping of sprites. Required for some old ROMs. |
Stop emulator |
Stops the emulator. |
In order to play games, run demos or run applications you will need to obtain .ch8 files from the Internet (see “Where do I get ROMs for this thing?” below).
For ROMs that support user input (mostly games), the PC keypad has been mapped to the CHIP-8 hexadecimal keypad as follows:
PC keypad key |
CHIP-8 key |
NUMPAD_0 |
0x0 |
NUMPAD_1 |
0x1 |
NUMPAD_2 |
0x2 |
NUMPAD_3 |
0x3 |
NUMPAD_4 |
0x4 |
NUMPAD_5 |
0x5 |
NUMPAD_6 |
0x6 |
NUMPAD_7 |
0x7 |
NUMPAD_8 |
0x8 |
NUMPAD_9 |
0x9 |
DIVIDE |
0xA |
MULTIPLY |
0xB |
SUBTRACT |
0xC |
ADD |
0xD |
ENTER (num pad) |
0xE |
DECIMAL |
0xF |
There was no standard specification for the orientation of the CHIP-8 hexadecimal keypad, nor were there guidelines as to which key should do what sort of action. You will need to experiment (or find instructions) in order to work out the controls for any given ROM.
Where do I get ROMs for this thing?
The emulator includes a simple ROM to display a splash screen by default. To avoid potential copyright issues I have not included any third-party CHIP-8 ROM files, but these can be found easily by looking for “CHIP-8 ROMs” with your favourite search engine. CHIP-8 ROMs are binary files which are typically (although arbitrarily) given the “.ch8” extension. Once you have a ROM, start the emulator and press “Load new ROM”. Browse to and select the .ch8 file; the ROM will load and run automatically. Have fun! 🙂
Author Contact Details
Dr Richard Lewis CPhys MInstP FHEA
Director of Postgraduate Studies
School of Physics and Astronomy
Cardiff University
Tel: +44(0)29 2087 5433
Web: http://www.cardiff.ac.uk/people/view/913813-lewis-richard
Email: LewisR54@cardiff.ac.uk
Cardiff University is a registered charity no. 1136855