A modern, minimal C17 Chip-8 emulator with a clean separation between a pure core library and an SDL2 platform. Built with CMake, includes unit test scaffolding (Unity), runs on Windows and Linux, and ships with CI.
- Core design:
chip8_coreis platform-agnostic and deterministic (RNG injected), exposing a compact API. - SDL2 platform:
chip8executable provides rendering, audio, input, and timing (700 Hz CPU, 60 Hz timers). - Tooling: C17, strict warnings, sanitizers in Debug, clang-format, Unity tests, and GitHub Actions CI.
- Simple UX: CLI flags for scale, speed, vsync, logging, and quirks; clear key mappings and controls.
chip8(executable): SDL-based emulator front-end.chip8_core(static library): pure CHIP-8 core (no SDL, deterministic, testable).chip8_tests(executable): Unity-based unit tests (sample included).
Tooling:
- C17
- Warnings:
-Wall -Wextra -Werror -pedantic(or/W4 /WXon MSVC) - Optimization:
-O2 - Address/UB sanitizers in Debug on GCC/Clang
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
ctest --test-dir build --output-on-failurecmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug -j
ctest --test-dir build --output-on-failure --build-config Debug./build/chip8 ./assets/your.rom --scale 10 --hz 700 --vsyncUnity is fetched automatically via CMake into third_party/.
--scale N(default 10): integer upscale factor (64×32 → N×)--hz N(default 700): CPU cycles per second--vsync: enable vsync on the renderer--log: reserved for extra logging (minimal now)--delay-quirk on|off: accepted but currently not used by the core--mem-quirk on|off: accepted; core defaults to original increment-I semantics
1 2 3 4 → 1 2 3 C
Q W E R → 4 5 6 D
A S D F → 7 8 9 E
Z X C V → A 0 B F
- Esc: Quit
- P: Pause
- N: Single-step one instruction (when paused)
- F1 / F5: Reset core and reload the ROM
- F12: Dump snapshot (PC, I, DT, ST, SP, stack top, hash, V registers) to stdout
CMakeLists.txt– root build and global tooling flagscmake/– CMake helpers (Unity fetch)core/– CHIP-8 core (chip8.c/.h,opcodes.c/.h,chip8_state.h)src/– SDL platform (platform_sdl.c/.h) andmain.ctests/– Unity test runner and samplesthird_party/– fetched dependenciesassets/– ROMs (empty placeholder)
The core is a single-cycle fetch-decode-execute engine with a small, test-friendly API and deterministic RNG injection. Timers are externally ticked at 60 Hz.
Key entry points:
chip8_create(chip8_rand_func rng, void* user)/chip8_destroychip8_reset,chip8_load_rom(data, size)(loads at 0x200)chip8_step()– one CPU cycle; no timer decrement insidechip8_tick_60hz()– decrements delay/sound timers if > 0chip8_key_down/up(hexKey)– keypad 0x0–0xFchip8_framebuffer()– 64×32 1bpp buffer (0/1 per pixel)chip8_get_snapshot(Chip8Snapshot*)– compact state for tests
Implemented opcodes include the standard CHIP-8 set (CLS, RET, JP, CALL, SE/SNE, LD/ADD, ALU 8xy*, SNE 9xy0, LD I, JP V0, RND, DRW with wrapping and collision in VF, SKP/SKNP, timers and memory ops Fx1E/Fx29/Fx33/Fx55/Fx65). SCHIP quirks are off by default; internal flags exist for future tuning.
- Rendering: 64×32 monochrome framebuffer uploaded as grayscale texture and scaled by
--scale(default 10 → 640×320). - Audio: simple square-wave beep while
sound_timer > 0. - Timing: ~
--hzCPU pacing via accumulator; 60 Hz timers viaSDL_AddTimerposting a user event.
- Language: C17
- Warnings/optimization:
-Wall -Wextra -Werror -pedantic,/W4 /WXon MSVC,-O2 - Debug sanitizers (GCC/Clang): Address + Undefined Behavior
- Formatting:
.clang-format(Google-ish) - Tests: Unity fetched by CMake;
ctestintegration - CI: GitHub Actions workflow builds and runs tests on Windows and Linux (Debug/Release)
- Milestone 1 — CMake scaffolding
- Root project with strict flags, sanitizers (Debug), and
chip8_core/chip8/chip8_teststargets. - Unity fetched via CMake; sample test integrated with CTest.
- Root project with strict flags, sanitizers (Debug), and
- Milestone 2 — CHIP-8 core
- Public API (
chip8.h,chip8_state.h) with deterministic RNG and snapshot support. - Opcode implementation and fast decode path; fontset installed at 0x50.
- Public API (
- Milestone 3 — SDL platform & app
- Window/renderer/texture (64×32 → scaled), audio beep, input mapping, and timing.
- CLI flags for scale, speed, vsync, and quirks placeholders; snapshot dumping.
- Milestone 4 — CI & tooling polish
- GitHub Actions CI (Windows/Linux), clang-format, and improved README.
- Expose quirk toggles via public API (e.g., shift source, I increment semantics).
- Add comprehensive unit tests and ROM-based behavior checks.
- Optional: add ROM selector UI, on-screen HUD, or debugger (disassembly/step/inspect).
Built it using C