A CHIP-8 emulation environment written in C++ with a raylib backend.
- Cadmium
CHIP-8 is maybe the first breed of what is today called a "fantasy console". The first opcode interpreter was written by Joseph Weisbecker for the "COSMAC" CDP1802 cpu that he developed at RCA and was used in machines like e.g. the COSMAC VIP and the Telmac 1800.
CHIP-8 is today seen as the "Hello world" of emulators, so the first suggestion if someone wants to get into emulator coding is to write a CHIP-8 the get the feeling for it. I already had done some work on emulators and wrote my own implementation of a Commodore 64 emulator in C++, so one could say that I had no reason to actually make a CHIP-8 one, but on a longer weekend off, I stumbled across the topic, I knew the system from my trusty old HP-48GX, during my study times in the 1990s, so I felt it would be fun to try to cobble an implementation together in a few hours. It worked quite well, and as I was using raylib for some tools and took part in a raylib game jam, I saw this as a good fit.
I had so much fun with this project that I added emulation of the original
COSMAC VIP and the DREAM6800 to the project, to allow using those to run
the historical CHIP-8 interpreters from the past and also to allow execution
of hybrid CHIP-8 programs that contain a mix of CHIP-8 and assembly subroutines
using the rarely supported 0nnn
opcode. So this project explores
the corners of historic CHIP-8 as well and sometimes goes a bit beyond it.
Emscripten builds are available here for testing:
- https://games.gulrak.net/cadmium - This is the most current release version
- https://games.gulrak.net/cadmium-wip/?p=xochip - this is the current work in progress
Simply drag rom files (.ch8
, .hc8
, .ch10
, .c8h
, .c8e
, .c8x
, .sc8
, .mc8
, .xo8
) or
Octo sources (.8o
), or Octo cartridges (.gif
), or even COSMAC VIP programs
(.bin
or .ram
) onto the window to load them.
The emulation behavior used in Cadmium is based on opcode information documented in the opcode table, various VIPER magazine issues, the CHIP-8 extensions and compatibility pages and tests on various emulators and the HP-48SX/HP-48GX calculator implementations as well as analyzing the historic interpreters' code and running them on COSMAC VIP and DREAM6800 (but both emulated, as I sadly don't have access to real hardware of those yet).
The emulation is based on different emulation cores and, depending on the core, on a set of "quirks" named options that go along with the core. To lessen the burden to remember the combinations, Cadmium uses "Behavior Presets" to combine a core, some settings and set of quirks to easily switch between CHIP-8 variants. The naming conventions adapted are based on the list put together by Tobias V. Langhoff at https://chip-8.github.io/extensions/ and for the classic COSMAC VIP based variants it is mainly based on the VIPER magazine where those interpreter variants where published.
The Supported presets are:
- CHIP-8
- CHIP-8-STRICT (cycle exact HLE VIP CHIP-8)
- CHIP-8E
- CHIP-8X
- CHIP-10
- CHIP-48
- SUPER-CHIP 1.0
- SUPER-CHIP 1.1
- SUPER-CHIP COMP
- MODERN-SUPER-CHIP
- MegaChip 8 (with Mega8 wrapping/scrolling support if wrapping is enabled)
- XO-CHIP
- VIP-CHIP-8 (CHIP-8 on an emulated COSMAC VIP)
- VIP-CHIP-8 TPD (same, but with 64x64 display)
- VIP-HI-RES-CHIP-8 (same, but with 64x128 display)
- VIP-CHIP-8E (same, with CHIP-8E interpreter)
- VIP-CHIP-8X (same, with CHIP-8X and VP-590/VP-595 add-on boards)
- VIP-CHIP-8X TPD (same hardware as VIP-CHIP-8X but 64x64)
- VIP-HI-RES-CHIP-8X (same hardware as VIP-CHIP-8X but 64x128)
- CHIP-8-DREAM (CHIP-8 on an emulated DREAM6800)
Whith CHIP-8-STRICT
Cadmium might be the first high-level emulator that has
a core that can execute CHIP-8 with the behavior and timing of the initial
VIP CHIP-8 interpreter without actually emulating the actual COSMAC VIP. While
this core, like other HLE emulators, can not execute machine subroutines, it
emulates the exact timing behavior of every instruction and the overall frame
timing to reach cycle exact accuracy compared to the real machine. The main
difference to the VIP-CHIP-8 preset is, that this one needs quite less host
CPU resources, but as said, can't run hybrid programs.
The SUPER-CHIP COMP
or SCHIPC
is a more generic variant that is similar
to Chromatophores SCHPC/GCHPC variants of SCHIP1.1 to allow more modern games
(often developed on Octo) that target SuperCHIP to run without tweeking some
quirks that, while correct for the original SCHIP1.1, are not common in modern
programs.
The MODERN-SUPER-CHIP
is more or less Octo's interpretation of SUSER-CHIP
and what Timendus test suite v4.1 checks for as modern SCHIP.
The VIP-CHIP-8
variant presets activate a core that is emulating a COSMAC
VIP driven by a CDP1802 CPU with 4k RAM, to execute original CHIP-8
interpreter variants to allow more accurate emulation of classic CHIP-8 and even
allow hybrid roms (.hc8
) that contain CDP1802 parts to execute on Cadmium.
The currently available behaviors are classic CHIP-8, CHIP-8 Two Page Display (TPD),
HI-RES-CHIP-8 (four page display, FPD), and CHIP-8X with the VP-590 Color Board
and the VP-595 Simple Sound Board attached, as well as a TDP and a HI-RES
version of 8X using that hardware.
The CHIP-8-DREAM
preset activates a core that is emulating a DREAM6800
driven by an M6800 CPU with 4k RAM, to execute the original CHIP-8 CHIPOS
kernel and run an accurate emulation of DREAM6800 CHIP-8. This core also
allows hybrid roms that use machine code parts.
There now also is the possibility to select a COSMAC VIP without any CHIP-8 interpreter. Cadmium then falls back into a raw 1802 COSMAC VIP emulation that allows to load and run games and programs made for the VIP that don't use CHIP-8.
Cadmium allows for those presets, that emulate actual hardware, to debug seamlessly on the CHIP-8 opcode level or the backend CPU assembly level:
Breakpoints and single stepping works in both views, the selected instruction tab defines the logical entity the debug buttons work on, the register view shows the registers of that entity. Breakpoints of the hidden entity still work and the tab switches to the entity that hit the breakpoint.
Note: Be aware, that in COSMAC VIP 1802 mode, while breakpoints and single stepping works fine, there is no step-over/step-out support as there is no defined way of the 1802 CPU to enter subroutines and return, and no stack in the sense most other CPUs have it. The M6800 debugging of the DREAM6800 side has those features enabled, as have all CHIP-8 modes, even the VIP one.
Emulation uses a number of configurable "quirks" or options, to allow a wide range of roms to work with it. Contrary to some other sources, Cadmium sees the initial original VIP behavior as the reference, so disabling all following options gives a basic VIP CHIP-8:
8xy6
/8xyE
just shift Vx8xy1
/8xy2
/8xy3
don't reset VFFx55
/Fx65
increment I only by xFx55
/Fx65
don't increment IBxnn
/jump0
uses Vx- wrap sprite pixels
Dxyn
doesn't wait for vsync- Lores
Dxy0
draws with 0(classic CHIP-8)/8/16 pixel width Dxyn
uses SCHIP1.1 collision logic- SCHIP lores drawing
- Half-pixel lores scroll
- Mode change clears screen
- 128x64 hires support
- only 128x64 mode
- multicolor support
- Cyclic stack
- Extended display wait
- XO-CHIP sound engine
Function | Windows/Linux | macOS |
---|---|---|
New | Ctrl + O | Cmd + O |
Open File | Ctrl + O | Cmd + O |
Save File | Ctrl + S | Cmd + S |
Key Map | Ctrl + K | Cmd + K |
Quit | Ctrl + Q | Cmd + Q |
Execution | ||
Run/Play | F5 | F5 |
Stop/Pause | Shift + F5 | Shift + F5 |
Step Over | F8 | F8 |
Step Into | F7 | F7 |
Step Out | Shift + F7 | Shift + F7 |
Editor | ||
Find | Ctrl + F | Cmd + F |
Replace | Ctrl + R | Cmd + R |
Copy | Ctrl + C | Cmd + C |
Cut | Ctrl + X | Cmd + X |
Paste | Ctrl + X | Cmd + X |
Cadmium allows to select the CHIP-8 variant on startup via the preset parameter and quirk
options. For that to work you need to give the preset first and then each quirk option
can either implicitly flip the quirk to the negated one of the preset, or you can use
them with an optional true
, on
, yes
for activating the quirk or false
, off
or
no
to disable it. For example cadmium -p chip8 --instant-dxyn
will start with a
classic CHIP-8 that does not wait for vertical blank on sprite draws.
USAGE: bin/cadmium.app/Contents/MacOS/cadmium [options] ...
OPTIONS:
General Options:
--draw-dump
Dump screen after every draw when in trace mode.
--opcode-json
Dump opcode information as JSON to stdout
--random-gen <arg>
Select a predictable random generator used for trace log mode (rand-lgc or counting)
--random-seed <arg>
Select a random seed for use in combination with --random-gen, default: 12345
--screen-dump
When in trace mode, dump the final screen content to the console
--test-suite-menu <arg>
Sets 0x1ff to the given value before starting emulation in trace mode, useful for test suite runs.
--trace-log
If true, enable trace logging into log-view
-b <arg>, --benchmark <arg>
Run given number of cycles as benchmark
-c, --compare
Run and compare with reference engine, trace until diff
-h, --help
Show this help text
-p <arg>, --preset <arg>
Select CHIP-8 preset to use: chip-8, chip-10, chip-48, schip1.0, schip1.1, megachip8, xo-chip of vip-chip-8
-r, --run
if a ROM is given (positional) start it
-s <arg>, --exec-speed <arg>
Set execution speed in instructions per frame (0-500000, 0: unlimited)
-t <arg>, --trace <arg>
Run headless and dump given number of trace lines
Quirks:
--allow-color
If true, support for multi-plane drawing is enabled
--allow-hires
If true, support for hires (128x64) is enabled
--cyclic-stack
If true, stack operations wrap around, overwriting used slots
--dont-reset-vf
If true, Vf will not be reset by 8xy1/8xy2/8xy3
--extended-display-wait
If true, Dxyn might even wait 2 screens depending on size and position
--half-pixel-scroll
If true, use SCHIP1.1 lores half pixel scrolling
--has-16bit-addr
If true, address space is 16bit (64k ram)
--instant-dxyn
If true, Dxyn don't wait for vsync
--jump0-bxnn
If true, use Vx as offset for Bxnn
--just-shift-vx
If true, 8xy6/8xyE will just shift Vx and ignore Vy
--load-store-dont-inc-i
If true, Fx55/Fx65 don't change I
--load-store-inc-i-by-x
If true, Fx55/Fx65 increment I by x
--lores-dxy0-width-16
If true, draw Dxy0 sprites have width 16
--lores-dxy0-width-8
If true, draw Dxy0 sprites have width 8
--mode-change-clear
If true, clear screen on lores/hires changes
--only-hires
If true, emulation has hires mode only
--sc11-collision
If true, use SCHIP1.1 collision logic
--wrap-sprites
If true, Dxyn wrap sprites around border
--xo-chip-sound
If true, use XO-CHIP sound instead of buzzer
...
ROM file or source to load (`.ch8`, `.hc8`, `.ch10`, `.c8h`, `.c8e`, `.c8x`, `.sc8`, `.mc8`, `.xo8`, `.gif`, `.bin`, `.ram`, or `.8o`)
Cadmium uses version numbers to communicate the difference between releases and
work-in-progress builds. Only even patch versions will be used for releases
and odd patch version will only be used for commits while working on the next
version. So a version like v1.0.1
can vary in features and behavior as it is
the changing main
branch head between v1.0.0
and v1.0.2
.
Cadmium is written in C++17 and uses CMake as a build solution. To build it, checkout or download the source.
The Linux build of Cadmium has the same prerequisites that raylib has and as thus the following packages are expected to be installed:
- Ubunbtu:
libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev libwayland-dev libxkbcommon-dev
- Fedora:
alsa-lib-devel mesa-libGL-devel libX11-devel libXrandr-devel libXi-devel libXcursor-devel libXinerama-devel libatomic
- Arch:
alsa-lib mesa libx11 libxrandr libxi libxcursor libxinerama
Additionally, if building for Wayland, one needs wayland-devel libxkbcommon-devel wayland-protocols-devel
and add the -DUSE_WAYLAND=ON
option to the initial CMake call.
For macOS, only the Xcode commandline tools and CMake are needed.
Open a terminal, enter the directory where the code was extracted and run:
cmake -S . -B build
to configure the project, and
cmake --build build
to compile it.
Currently, Cadmium is not being able to compile with MSVC++ so the recommended toolchain is W64devkit. I might try to make it compile on MSVC++, but I am not working on that in the near future. That being said, I would not per-se reject PRs helping with this.
export PATH="$PATH;C:/Program Files/CMake/bin"
cmake -G"Unix Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DGRAPHICS=GRAPHICS_API_OPENGL_21 -S . -B build-w64dev
This has only been tried on macOS yet, you need emsdk and cmake installed, and I expect it to work on Linux the same way.
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=<path-to-emsdk>/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
cmake --build build
- Mastering CHIP-8 - A great document to start the CHIP-8 emulation journey with (not about how emulators work in general)
- CHIP-8 extensions and compatibility - A comprehensive overview of different CHIP-8 variants that was the initial motivator to support many of them, a great overview.
- HP48-Superchip - This repository contains valuable information on the specifics of CHIP-48/Super-Chip that go beyond the specification of Super-Chip.
- Octo - This has to be the first one named, this very user-friendly web based emulation environment sets the standard any CHIP-8 emulator that tries to help in development of CHIP-8 programs needs to be measured by, and I used it to try out a lot of stuff, it's assembly language is the one supported by Cadmium.
- Silicon8 - While experimenting with more than 4 colors in XO-Chip mode this emulator helped to compare the results and test the very advanced and resource hungry roms its author has written.
- raylib - the fun game programming library that inspired me to write this (automatically fetched during configuration)
- raygui - the lightweight immediate mode gui library for raylib (contained in my wrapper rlguipp)
- c-octo assembler - the octo assembler for support of generating binaries from Octo assembler syntax was originally the base of Chiplets assembler, but it has been very heavily reworked and modified, so it is its ancestor but hardly recognizable by now. Still, having this was a huge help and it will allways be named as the reference.
- fmtlib - a modern formatting library in
the style of C++20 std::format (contained in
external/fmt
) - date - A date and time
library based on the C++11/14/17 <chrono> header (contained in
external/date
) - json for modern c++ - A flexible and
nice to use JSON implementation (contained in
external/nlohmann
) - ghc::filesystem - An implementation of std::filesystem for C++11/14/17/20 that also works on MingW variants that don't have a working std::filesystem in C++17 and with better utf8 support (automatically fetched during configuration)
Cadmium started as a "Well I guess I should at least implement CHIP-8 once" project, after I already privately had fun writing emulators But the combined early reactions from the raylib Discord and the Emulation Development Discord gave me so much fun that I started to dig more into the subject. So first of all a big thank you to all those who pushed me with their nice comments and inspirational remarks, I can't name you all, but it really did kick off this project.
I still want to give out some explicit thanks to:
-
Ramon Santamaria (@raysan5) - I stumbled across raylib at the end of 2021 and since 2022 I started using it for some experiments and then took part in the raylib game jam and had so much fun. Without raylib I wouldn't have started this, it was originally a test bed for my wrapper of Ramons raygui but instead became a life of its own.
-
John Earnest (@JohnEarnest) - for his great Octo and for XO-Chip, while I still would have loved for some things to be different in Octo Assembly Syntax, I really appreciate that this new variant got CHIP-8 development to a more modern level, made it more accessible and together with the great web based IDE gave CHIP-8 a new live and the idea of a yearly OctoJam sure helped, pushing it. Thank you for that!
His C-Octo implementation of the assembler also is embedded in Cadmium and drives its assembling capabilities: -
Tim Franssen (@Timendus) - for his CHIP-8 Test Suite helped me a great deal in finding issues and get a better understanding of the quirks that differentiate the CHIP-8 variants, and for his work on a binary CHIP-8 container that could carry emulation parameter allowing easy set-up for roms.
-
@NinjaWeedle - for his great help and countless tests to get some flesh on the very skinny original MegaChip8 specs to allow actually support this exotic CHIP-8 variant and maybe help it to be more accepted. This work resulted in his extended MegaChip8 documentation.
-
@Kouzeru - for pushing me into supporting XO-Audio and the creative discussions on where to go with future chip variants and the cool music he creates on XO-Audio.
-
Joshua Moss (@Bandock) - for motivating me to implement and CDP1802 emulation core and getting "Support a real VIP mode" onto my todo list and for the interesting HyperChip64 ideas.
-
Tobais V. Langhoff (@tobiasvl) - for his excellent page on CHIP-8 extensions and compatibility, and for his DREAM6800 emulator DRÖM that inspired me to look into the DREAM6800.
-
Michael J Bauer (@M-J-Bauer) - for developing the DREAM6800 computer and CHIPOS, to extend the CHIP-8 world to the M6800 CPU, and for making me learn M6800 assembler and giving his okay to use the CHIPOS rom data inside Cadmiums DREAM6800 emulator.
The first time I got into contact with CHIP-8 was with Super-Chip on an HP-48 calculator during my CS studies. The base of that was the CHIP-48 variant, named after the calculator. I think the reliving of CHIP-8 on these calculators was one of the things that helped CHIP-8 survive the time from the seventies until today. Cadmium is an element with the atomic number of 48 in the periodic table of elements, so that made me chose it as the name reference for this project.
Cadmium is about emulating and developing for CHIP-8 and its variants, a platform that has a resolution of traditionally 64x32 pixel, the "hires" mode has 128x64 pixels and even MegaChip8 only 256x192 pixels, so if you hate seeing pixels, this might not be the platform for you. 😉 Jokes aside, I wanted Cadmium to have an original and recognizable look. Many emulators use the absolutely great Dear ImGui for the UI, and the fact that a ready-to-use memory editor exists for it helps a lot, getting something professionally looking done fast. While there is nothing wrong with that, and Dorito another CHIP-8 IDE that I only came to know after I already had editor and assembler working, is a really great example of what can be done that way, I still think there is not much individually recognizable between the look of these emulators. One can argue that standardization is a good thing, and I can see that, I just didn't feel like adding one to the list. So I guess Cadmium is for people that can appreciate this individuality and raygui, the raylib extension that is behind many widgets of Cadmium is the way to get this pixel look.
Cadmium's UI uses a handmade font heavily inspired by the 5x8 pixel
font integrated in the Thomson EF936x
video chip that was used in an extension card of the NDR-Klein-Computer,
a DIY computer that could be build following a german educational tv
program in the eighties. The font is build in an ASCII file in the
tools
folder through a commandline tool and the generated array is
embedded into the src/cadmium.cpp
source file. The chip originally only
has 95 characters and I created the basic set and filled up the latin-1
codepage characters and some additional unicode code points. I think
its historic source and its look add to the unique look and feel of
the Cadmium UI, but I can agree that it is a matter of taste. 😉