Real-World Applications

cancel
Showing results for 
Search instead for 
Did you mean: 

CHIP-8 Virtual Machine in LabVIEW: A Template for Development Best Practice, Code Modification, and Style Review

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:

 

Tetris by Fran Dachille (CHIP-8): a surprisingly complete game of Tetris that plays very well.  Due to the primitive sprite handling and XOR-based collision detection, you can see a single pixel being scanned across the blocks whenever a tetronimo is placed.Tetris by Fran Dachille (CHIP-8): a surprisingly complete game of Tetris that plays very well. Due to the primitive sprite handling and XOR-based collision detection, you can see a single pixel being scanned across the blocks whenever a tetronimo is placed.

Blinky by Hans Christien Egeberg (CHIP-8): an homage to the classic Pac-Man in 64x32.  The power pills are missing and there are only two ghosts, but the game is quite playable.Blinky by Hans Christien Egeberg (CHIP-8): an homage to the classic Pac-Man in 64x32. The power pills are missing and there are only two ghosts, but the game is quite playable.Joust by Erin Catto (S-CHIP8): a faithful clone of Joust with two AI opponents, multiple levels, a lives counter and collision physics.  Defeated enemies turn into eggs that must be collected quickly otherwise they return to battle atop their faithful ostrich, lance in hand!  Joust is weird…Joust by Erin Catto (S-CHIP8): a faithful clone of Joust with two AI opponents, multiple levels, a lives counter and collision physics. Defeated enemies turn into eggs that must be collected quickly otherwise they return to battle atop their faithful ostrich, lance in hand! Joust is weird…SuperTrip8 by Revival Studios (S-CHIP8): an impressive demo for the S-CHIP8 with fancy screen transitions, pseudo-3D effects and sprite trail animations.SuperTrip8 by Revival Studios (S-CHIP8): an impressive demo for the S-CHIP8 with fancy screen transitions, pseudo-3D effects and sprite trail animations.

 

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:

  • CHIP-8: 64x32 resolution, CHIP-8 instruction set;
  • HiRes CHIP-8: 64x64 resolution, CHIP-8 instruction set;
  • S-CHIP8 (experimental): 128x64 resolution, S-CHIP8 instruction set (superset of CHIP-8).

 

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:

  • Some ROMs (typically older CHIP-8 ROMS such as Blitz) will require horizontal wrap (Hwrap) and/or vertical wrap (Vwrap) to be disabled.  By default, Hwrap and Vwrap are enabled, which means that running code written for implementations without wrap will result in parts of sprites appearing at the opposite side of the screen, usually with a game-breaking result;
  • There was no standardised execution (opcode decoding) speed in the original specification, so the 1kHz execution frequency used here is an arbitrary value.  This generally gives a good, playable result with most games, but some older ROMs might run too quickly.

 

Known bugs

 

  • The sound and delay timers are decremented at the timeout rate of the event structure in the producer loop.  By default this is 60Hz, as per the CHIP-8 specification.  Note that the timers should be decremented at 60Hz regardless of the timeout rate;
  • The HiRes CHIP-8 graphics mode has a 1:1 aspect ratio, but is displayed in the same 2:1 aspect ratio as for the CHIP-8 and S-CHIP8 modes;
  • S-CHIP support is experimental.  While most ROMS will run without issue, some edge cases involving sprite collision detection result in VF being incorrectly set or reset.  For example, this manifests itself in Ant where the player sprite cannot be moved to the right along a surface unless the player jumps.

 

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:

 

LabVIEW CHIP-8 emulator block diagram, showing the producer-consumer (events) state machine pattern.  Error handling is implemented throughout; the VI will shut down gracefully and report any errors.LabVIEW CHIP-8 emulator block diagram, showing the producer-consumer (events) state machine pattern. Error handling is implemented throughout; the VI will shut down gracefully and report any errors.

 

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:

 

  • While the sound counter and all sound-related operations work as expected on the CHIP-8 internal state, there is no sound output currently implemented;
  • The PC keypad to CHIP-8 hex keypad mapping is currently fixed, but there is an empty state to allow remapping functionality to be added;
  • The front panel theme is currently fixed, but there is an empty state to allow changes in the future.

 

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:

 

LabVIEW CHIP-8 emulator splash screen.  The splash screen is presented in S-CHIP8 mode (128x64).LabVIEW CHIP-8 emulator splash screen. The splash screen is presented in S-CHIP8 mode (128x64).

 

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

Contributors