Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: ci

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Test (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Format check
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
run: zig fmt --check .
- name: Unit and integration tests
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
run: zig build test -Dsdl2=off --summary all
- name: Regenerate bundled ROMs
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
run: zig build fixtures -Dsdl2=off
- name: Committed fixtures match regeneration
shell: bash
if: ${{ matrix.os == 'ubuntu-latest' }}
run: git diff --exit-code -- fixtures
- name: Demo ROM reports PASS
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
run: zig build run-demo -Dsdl2=off
- name: Build headless tools
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
run: zig build -Dsdl2=off

windowed:
name: Windowed frontend (ubuntu)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install SDL2
run: sudo apt-get update && sudo apt-get install -y libsdl2-dev
- name: Build windowed frontend
run: zig build --summary all
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ zig-out/
*.exe
.DS_Store
*.gb
!fixtures/roms/*.gb
*.sav
*.swp
138 changes: 94 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,139 @@
# Dot Matrix Deck

Dot Matrix Deck is a Game Boy emulator workbench written in Zig. It models the
SM83 CPU, the memory bus, timers, serial output, and the pixel pipeline. A
headless runner makes emulator behavior easy to inspect in scripts and CI.

## Current status

This project is under active development. The headless core is the most useful
entry point today. The SDL2 windowed frontend is available when SDL2 is
installed, but it is still being built out.
Dot Matrix Deck is a Game Boy emulator written in Zig.
It emulates the SM83 CPU, the memory bus, the timers, and the pixel pipeline.
The SDL2 frontend shows the screen in a window with a DMG-style shell.
A headless runner verifies the core with test ROMs.

## Features

- SM83 CPU and instruction execution
- Memory bus with timer, serial, and pixel-processing components
- Headless ROM runner with cycle limits and serial verdicts
- Optional instruction trace output
- Small assembler tool for test ROM sources
- Deterministic unit tests for the emulator core and assembler
- Full SM83 instruction set with the CB prefix group.
- Timer, joypad, and pixel-processing devices.
- SDL2 windowed frontend with a dot-matrix presentation.
- Headless ROM runner with cycle caps and serial verdicts.
- Small SM83 assembler for test ROM sources.
- Bundled demo ROM that draws a title and prints PASS.
- Deterministic unit tests for every core module.

## Requirements

- Zig 0.16 or later
- SDL2 for the optional windowed frontend
- Zig 0.16 or later.
- SDL2 for the windowed frontend.

The headless build does not require SDL2. On Windows, the build searches common
MSYS2 and vcpkg prefixes. You can also set `SDL2_DIR` or pass an SDL2 prefix.
The headless tools do not need SDL2.
On Windows, the build searches MSYS2 and vcpkg prefixes.
Set `SDL2_DIR` to choose a prefix.

## Build

Build the headless runner and assembler:
Build the headless tools:

```text
zig build -Dsdl2=off
```

Build with the windowed frontend when SDL2 is available:
Build the windowed frontend:

```text
zig build
```

## Run a ROM
## Test

Run a ROM without a window:
Run all tests:

```text
zig build run-headless -- path/to/rom.gb
zig build test -Dsdl2=off
```

Useful options are `--max-cycles N`, `--trace`, and `--expect pass|fail|any`.
The runner prints serial output and returns a status from the detected verdict.
Regenerate the bundled ROMs:

Run a ROM in the SDL2 frontend:
```text
zig build fixtures -Dsdl2=off
```

## Run

Run the bundled demo headlessly:

```text
zig build run -- path/to/rom.gb
zig build run-demo -Dsdl2=off
```

## Test
Run the demo in a window:

Run the core and assembler tests:
```text
zig build run -- fixtures/roms/demo.gb
```

Run any ROM headlessly:

```text
zig build test -Dsdl2=off
zig build run-headless -- path/to/rom.gb
```

The repository also contains build steps for generated fixtures and the Blargg
CPU instruction suite. Add the required fixture files before using those steps.
Useful options are `--max-cycles N`, `--trace`, and `--expect pass|fail|any`.

## Sample output

This is the output of `zig build run-demo -Dsdl2=off`:

## Project layout
```text
Serial output:
DOT MATRIX DECK
PASS
Verdict: pass
```

- `src/cpu.zig` - SM83 CPU implementation
- `src/emulator.zig` - emulator composition and core tests
- `src/bus.zig` - memory and device routing
- `src/headless.zig` - command-line ROM runner
- `src/main.zig` - SDL2 frontend
- `tools/gbasm.zig` - small assembler for fixture ROMs
## Window controls

| Key | Action |
| --- | --- |
| Arrow keys | D-pad |
| Z | A button |
| X | B button |
| Enter | Start |
| Shift | Select |
| P | Pause |
| R | Reset |
| F | Fast forward |
| ESC | Quit |

## Architecture

`src/emulator.zig` drives the run loop.
`src/bus.zig` routes reads and writes to the devices.
`src/cpu.zig` executes the SM83 instruction set.
`src/joypad.zig`, `src/timer.zig`, and `src/ppu.zig` model the hardware.
`src/serial.zig` decodes pass and fail verdicts.
`src/disasm.zig` formats instructions for the trace mode.
`src/frontend.zig` and `src/main.zig` form the SDL2 frontend.
`tools/gbasm.zig` assembles SM83 source into ROM images.

The frontend and the headless runner share the same core.
The core does not depend on SDL2.
All tests run without SDL2.

## Limitations

Hardware coverage is incomplete. Timing accuracy, cartridge support, audio,
and frontend features will improve as the project grows.
Hardware coverage is incomplete.
Timing accuracy is not cycle-perfect.
Cartridge support covers ROM-only and MBC1 images.
Audio is not implemented.
The frontend does not handle save files yet.

## Test status

The suite runs 68 checks.
It covers the CPU, timer, PPU, joypad, serial verdicts, and the assembler.
A round-trip test rebuilds the demo ROM and compares it byte for byte.
Continuous integration runs the suite on Ubuntu and Windows.

## Roadmap

See [ROADMAP.md](ROADMAP.md) for what is done and what comes next.

## License

No license file is published yet. Treat this repository as an experimental
project until a license is added.
No license is published yet.
Treat this project as experimental.
35 changes: 35 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Roadmap

This document tracks what the project delivers and what remains.
It is the plan for the next releases.

## Done

- SM83 CPU with the full 256-opcode set and the CB prefix group.
- Memory bus with WRAM, HRAM, and hardware I/O registers.
- Timer with DIV, TIMA, TMA, and TAC.
- PPU with background maps, tile data, and sprite rendering.
- Joypad device with column selection and interrupts.
- Cartridge support for ROM-only and MBC1 images.
- Headless ROM runner with cycle caps, traces, and verdicts.
- SDL2 windowed frontend with a DMG-style shell.
- SM83 assembler with labels, data directives, and expressions.
- Bundled demo ROM that draws a dot-matrix title and prints PASS.
- Deterministic unit tests for every core module.
- Continuous integration on Ubuntu and Windows.

## Next

- Cycle-accurate timing for the timer and the PPU STAT modes.
- MBC2, MBC3, and MBC5 cartridge support.
- Serial link emulation between two emulator instances.
- Audio processing unit (APU).
- Save game files and battery-backed RAM.
- Configurable key bindings for the windowed frontend.
- A pause and reset debugger overlay.

## Known limits

- The core starts at the post-boot state. Boot ROM emulation is out of scope.
- The PPU has no window line counter edge cases yet.
- The SDL2 frontend ships without audio.
Loading