PeakRacing/chip8

chip8

★ 0Forks 0CGitHub ↗Compare

README

CHIP-8 Emulator

A portable CHIP-8 interpreter/emulator written in pure C11, inspired by the architecture and coding style of PeakRacing/nes.

C11 Build Graphics License

Features

  • 35 CHIP-8 opcodes — full original instruction set (no SUPER CHIP-8 extensions)
  • Three-layer architecture — portable core (inc/ + src/), example port layer (port/), and SDL2 implementation (sdl/)
  • Platform abstraction — standard C memory, file I/O, and rendering via user-overridable weak symbols
  • SDL2 frontend — hardware-accelerated rendering, keyboard input, and square-wave audio
  • Battery included — SDL2 fetched automatically by xmake, no manual dependency installation
  • Configurable — display scale, CPU frequency, color depth, and logging level at compile time

Quick Start

Prerequisites

  • xmake (install via winget install xmake on Windows, or your platform's package manager)
  • SDL2 is auto-fetched by xmake — nothing to install manually

Build & Run

cd sdl
xmake                      # fetch SDL2, compile, link
xmake run chip8 <romfile>  # load and run a CHIP-8 ROM

Debug Build

cd sdl
xmake f -m debug
xmake
xmake run chip8 <romfile>

Architecture

chip8/
├── inc/                    # Portable public headers (no platform deps)
│   ├── chip8.h             # Core struct chip8_t + lifecycle API
│   ├── chip8_cpu.h         # CPU state (16 registers, stack, timers)
│   ├── chip8_display.h     # 64×32 monochrome framebuffer
│   ├── chip8_keypad.h      # 16-key bitfield
│   ├── chip8_rom.h         # ROM loader
│   ├── chip8_log.h         # Tiered logging macros
│   └── chip8_default.h     # Platform abstraction + compile-time config
├── src/                    # Portable core (no platform deps)
│   ├── chip8.c             # Init, deinit, main emulation loop
│   ├── chip8_cpu.c         # 35-opcode fetch-decode-execute
│   ├── chip8_rom.c         # Raw binary ROM loader
│   └── chip8_default.c     # Default weak-function stubs
├── port/                   # Example port (user-replaceable)
│   ├── chip8_conf.h        # Configurable macros
│   └── chip8_port.c        # Platform function stubs
└── sdl/                    # SDL2 platform implementation
    ├── xmake.lua            # xmake build config
    ├── main.c               # Entry point: init → load → run → free
    └── port/
        ├── chip8_conf.h     # SDL-specific config (FS enabled)
        └── chip8_port.c     # SDL window, renderer, input, audio, frame timing

Layer Isolation

  1. inc/ + src/ — never include platform headers (no <SDL.h>, <windows.h>)
  2. port/ — example port. Functions declared with CHIP8_WEAK so users can override them
  3. sdl/port/ — SDL-specific adapter. Overrides port/ stubs with real SDL2 implementations
  4. src/*.c — only include "chip8.h" (which aggregates all public headers)

Key Mapping

Physical keyboard 1 2 3 4 Q W E R A S D F Z X C V maps to the CHIP-8 hex keypad:

CHIP-8 Keyboard CHIP-8 Keyboard
1 1 9 D
2 2 A Z
3 3 0 X
C 4 B C
4 Q F V
5 W 7 A
6 E 8 S
D R E F
CHIP-8 Keypad Layout:        Physical 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 │
└───┴───┴───┴───┘            └───┴───┴───┴───┘

Technical Specifications

Memory Map

Address Range Usage
0x000 – 0x04F Font set (16 characters × 5 bytes = 80 bytes)
0x050 – 0x1FF Reserved (original COSMAC VIP interpreter area)
0x200 – 0xFFF Program ROM + working RAM

CPU State

  • 16 × 8-bit general-purpose registers V0–VF (VF doubles as carry/borrow flag)
  • 16-bit index register I
  • 16-bit program counter PC
  • 8-bit stack pointer SP (16-level call stack)
  • 8-bit delay timer (decremented at 60 Hz)
  • 8-bit sound timer (beeps while > 0, decremented at 60 Hz)

Instruction Set (35 opcodes)

Category Opcodes Description
Display 00E0, DXYN Clear screen, draw sprite
Flow 1NNN, 2NNN, 00EE, BNNN Jump, call, return
Conditional 3XNN, 4XNN, 5XY0, 9XY0 Skip if equal / not equal
Constants 6XNN, 7XNN Load, add
Bitwise 8XY1, 8XY2, 8XY3, 8XY6, 8XYE OR, AND, XOR, SHR, SHL
Arithmetic 8XY4, 8XY5, 8XY7 Add / subtract with carry/borrow
Memory ANNN, FX1E, FX29, FX33, FX55, FX65 I-register, BCD, block load/store
Timer FX07, FX15, FX18 Delay / sound timer access
Keypad EX9E, EXA1, FX0A Key pressed / not pressed / wait
Random CXNN Random AND mask

Configuration

Edit sdl/port/chip8_conf.h before building:

Macro Default Description
CHIP8_DISPLAY_SCALE 16 Scale factor (64×32 → 1024×512)
CHIP8_CPU_FREQ 500 Instructions per second
CHIP8_COLOR_DEPTH 32 Pixel format (32 = ARGB8888, 16 = RGB565)
CHIP8_LOG_LEVEL INFO Log verbosity (NONE / ERROR / WARN / INFO / DEBUG)
CHIP8_USE_FS 1 Enable file system for ROM loading

Porting to New Platforms

  1. Copy port/chip8_conf.h and port/chip8_port.c as starting templates
  2. Provide platform-specific implementations for the function stubs declared in chip8_default.h:
    • chip8_malloc, chip8_free, chip8_memset, chip8_memcpy, chip8_memcmp — memory operations
    • chip8_initex, chip8_deinitex — platform init/teardown
    • chip8_draw — render framebuffer to screen
    • chip8_frame — per-frame hook (input polling, audio, frame pacing)
    • (Optional) chip8_fopen, chip8_fread, chip8_fwrite, chip8_fseek, chip8_fclose — file I/O
  3. Build with your platform's toolchain, linking against inc/ headers and src/*.c source files

Project Structure

Directory Purpose Platform-Dependent
inc/ Public headers No
src/ Core emulator No
port/ Example port (stubs) Configurable
sdl/ SDL2 frontend + build Yes

License

Apache License 2.0 © PeakRacing

References

Contributors

PeakRacing

Issues