A CHIP-8 emulator.
Not portable. Works on Linux and WSL (as long as you have a display server proxy).
- SDL2: graphic display & keyboard input
- portaudio: sound generation
These are the important ones. Run with --help
for a complete list.
- -a, --audio-dev: audio device id that's used by portuadio as output device. If you don't specify this, you'll get a list of output devices. Look for
pulseaudio
orpipewire
. - -c, --cpu-freq: CPU frequency in Hz. Pick something between 200-500 for a realistic experience.
- -i, --ref-int: refresh the screen every N instructions. Note that frequent refreshes (e.g.:
-i 1
) is likely to cause a segfault in libSDL2. Aim for ~30fps. - -l, --lazy-render: refesh the screen after every screen updating instruction. Makes the previous option redundant. Whether using this is worth it or not depends on the ROM you execute.
- -n, --new-shift: use the newer (Super CHIP-8) implementation of the shift operations. Required for ROMs such as space invaders.
Here are some examples of how you should run various ROMs:
$ ./bin/mvemu.chip8 -n -a 13 -i 10 -s 10 -c 300 ./roms/games/invaders.ch8
$ ./bin/mvemu.chip8 -a 13 -l -s 20 -c 500 ./roms/demos/ibm.ch8
CHIP-8
|
Host keyboard
|
- src/cli_args.c: definition of CLI arguments and parser. Based on
argp
. - src/display.c: sprite drawing and screen refresh. Updates are rendered to a 32x64 texture. On screen refresh, the texture is copied to the backbuffer and scaled automatically during this process.
- src/main.c: emulator entry point. Not much to look at here.
- src/sound.c: a sin-based audio signal generator and all the necessary setup code.
- src/system.c: handles instruction decoding and interpretation. All timers are based on POSIX differential timers. If the frequency is too high (i.e.: single clock cycle time slice is too short), a whole cycle is abandoned and a warning is displayed. Detection of such cases is done by comparing the decoder function's RBP to a reference value. This works only because a POSIX timer's callback is executed in the same thread but with a separate stack (allocated once, during the timer creation). This behaviour may vary across implementations of POSIX timers, so I can't guarantee that the emulator will work.
- include/util.h: just some macros that I like using for logging. Also, some other handy definitions.