A dual-platform CHIP-8 emulator written in C, supporting both modern desktop PCs and the M5Stack Cardputer (ESP32). This project provides a clean, well-structured implementation of the CHIP-8 virtual machine, allowing classic games and programs to run on different hardware with minimal platform-specific code.
- Dual-Platform Support:
- PC/Desktop: Utilizes the raylib library for simple, cross-platform graphics and input handling.
- M5Stack Cardputer: A fully portable version for the ESP32-based handheld, complete with SD card support, an interactive ROM selection menu, and hardware-specific drivers for the display, keyboard, and speaker.
- Accurate Emulation: Implements the full CHIP-8 instruction set, timers, and display logic.
- Timing-Accurate CPU Clock: The desktop version uses a delta time accumulator to run the CPU at a configurable clock speed (defaulting to the original 540 Hz), independent of the host's frame rate.
- Clean Codebase: A strong separation between the core emulator logic and platform-specific code makes it easy to understand and maintain.
The emulator's design emphasizes modularity. The core chip8.c file contains the heart of the virtual machine—the fetch-decode-execute cycle—and has no dependencies on any specific graphics or input library.
A key feature is the timing-accurate game loop in the PC version, which decouples the emulation speed from the host's frame rate. This is achieved using a time accumulator, ensuring that the CHIP-8 CPU runs at a consistent 540Hz, as it was designed.
// src/pc/main.c - Timing loop
current_time = GetTime();
delta_time = current_time - last_time;
last_time = current_time;
accumulator += delta_time;
while(accumulator >= time_per_instruction) {
Chip8Cycle(chip8);
accumulator -= time_per_instruction;
}This ensures a consistent experience regardless of the machine it runs on.
.
├── CMakeLists.txt # Build script for the PC version
├── include/ # Header files for core and PC platform
│ ├── chip8.h # Core CHIP-8 VM state and function prototypes
│ ├── platform.h # PC (raylib) platform-specific function prototypes
│ ├── stack.h # Stack data structure
│ └── utils.h # Utility function prototypes
└── src/
├── core/ # Core, platform-independent emulator logic
│ ├── chip8.c # CHIP-8 CPU implementation (opcodes, timers)
│ └── stack.c # Stack implementation
├── pc/ # PC (Desktop) version
│ ├── main.c # Main entry point and game loop
│ └── platform.c # raylib rendering and keyboard input
└── cardputer/ # M5Stack Cardputer version (ESP32)
├── cardputer.ino # Main entry point (setup() and loop())
├── platform.ino # Display rendering, ROM selection menu, input
├── utils.ino # Serial logging helpers
├── chip8.c # Duplicated core CPU file for Arduino IDE
└── stack.c # Duplicated stack file for Arduino IDE
Platform-specific logic is handled in separate directories:
src/pc/contains the code for the desktop version, which interfaces withraylib.src/cardputer/contains the Arduino sketches (.inofiles) for the M5Stack Cardputer.
The core emulator files (chip8.c, stack.c) are intentionally duplicated inside the src/cardputer directory. While this introduces redundancy, it was a pragmatic decision to drastically simplify the build process for the Arduino IDE, which can be brittle when handling relative include paths outside the main sketch directory. This allows the Cardputer version to be compiled directly from the IDE without complex build configuration.
The desktop version is built with CMake and requires raylib.
Dependencies:
- A C compiler (e.g., GCC, Clang)
cmakemakerayliblibrary. On Debian/Ubuntu, install withsudo apt install libraylib-dev.pkg-config(usually installed with build tools)
Build and Run:
- Clone the repository.
- Create a build directory:
mkdir build && cd build
- Run CMake and make:
cmake .. make
- Execute the emulator with a ROM file:
./chip8-8-emu path/to/your/rom.ch8
PC Keyboard Mapping:
| CHIP-8 Key | Keyboard | CHIP-8 Key | Keyboard | |
|---|---|---|---|---|
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 |
This version runs on the M5Stack Cardputer and loads ROMs from an SD card.

Hardware/Software Requirements:
- M5Stack Cardputer
- A microSD card
- Arduino IDE or PlatformIO
Libraries:
- Install the
M5Cardputerlibrary from the Arduino Library Manager.
Setup:
- Format your microSD card (FAT32 is recommended).
- Copy your CHIP-8 ROM files (with a
.ch8extension) onto the SD card. The emulator will automatically search for ROMs in the following locations:- The root directory (
/) - A
/romsdirectory - A
/chip8directory
- The root directory (
- Insert the SD card into the Cardputer.
Build and Flash:
- Open the main sketch file
src/cardputer/cardputer.inoin the Arduino IDE. - The IDE should automatically open all other
.inoand.cfiles in that directory. - Select "M5Stack-Cardputer" as your board.
- Compile and upload the sketch.
Usage:
- On startup, the Cardputer will scan the SD card and display a menu of available ROMs.
- Use the
;(up) and.(down) keys to navigate the list. - Press
Enterto load and run the selected ROM. - To return to the ROM selection menu, press the
`(backtick) orBackspacekey.
Contributions are welcome! Feel free to open an issue to report bugs or suggest features. If you'd like to contribute code, please open a pull request.