Program, build, debug, and port Sega Master System (and Game Gear / SG-1000) homebrew games in C with devkitSMS (SMSlib + PSGlib) and the SDCC Z80 compiler. Use whenever the user writes, compiles, fixes, optimizes, or ports an SMS/Master System or Game Gear game or demo: the SDCC + devkitSMS toolchain; producing a `.sms`/`.gg` ROM; VDP tiles, the tilemap, hardware sprites, palettes, PSG music/SFX, joypad input, VBlank/line interrupts, ROM bank switching, SRAM saves; converting assets (`.psgco...
Scanned 9/5/2026
Install to Claude Code
npx -y skills add haroldo-ok/sega-master-system-skill-for-claude --skill sega-master-system-devkitsms --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sega Master System Devkitsms?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/haroldo-ok-sega-master-system-devkitsms)More formats (shields.io, HTML) on the badges page.
---
name: sega-master-system-devkitsms
description: >-
Program, build, debug, and port Sega Master System (and Game Gear / SG-1000)
homebrew games in C with devkitSMS (SMSlib + PSGlib) and the SDCC Z80
compiler. Use whenever the user writes, compiles, fixes, optimizes, or ports
an SMS/Master System or Game Gear game or demo: the SDCC + devkitSMS
toolchain; producing a `.sms`/`.gg` ROM; VDP tiles, the tilemap, hardware
sprites, palettes, PSG music/SFX, joypad input, VBlank/line interrupts, ROM
bank switching, SRAM saves; converting assets (`.psgcompr` tiles, `vgm2psg`
music, `folder2c`/`assets2banks`); or debugging the classic "compiles but
boots to a black screen" failure. Trigger on mentions of devkitSMS, SMSlib,
PSGlib, SDCC `-mz80`, ihx2sms, crt0_sms, `SMS_addSprite`/`SMS_waitForVBlank`,
a `.sms` ROM, Master System / Game Gear homebrew, or Haroldo-OK's SMS games
(Jill of the Jungle, Dangerous Dave, Food Fighter, Sub Rescue, Commander
Keen). Prefer it over general knowledge for the SMS toolchain, VDP limits, or
ROM format — easily gotten wrong from memory.
---
# Sega Master System game development & porting (devkitSMS)
This skill turns an idea — or an existing game to port — into a **playable,
verified `.sms` ROM**, built with **SDCC** (Z80 C compiler) and **devkitSMS**
(sverx's SMSlib + PSGlib). It bundles a consistent toolchain snapshot, project
templates, a headless test emulator, and references distilled from Haroldo-OK's
open-source SMS games.
The SMS is a Z80 with a tile/sprite VDP (256×192, two 16-colour palettes,
≤64 sprites / 8-per-line), a 4-channel PSG, and **8 KiB of RAM**. There is no
framebuffer and no float unit. Success means respecting those limits, not
fighting them.
Do **not** rely on memory for SMSlib signatures, VDP addresses, build flags, or
the ROM header — they are precise and version-sensitive. Read the references and
**verify by building and by running the test harness.**
## Definition of done (do not stop at "it compiled")
A ROM that links tells you almost nothing; the default failure is a clean build
that **boots to a black screen**. A task is done only when all hold — report each:
1. **Compiles and links** — no SDCC errors, no `?ASlink` undefined-symbol warnings.
2. **Becomes a ROM** — `ihx2sms` reports `SEGA header found, checksum updated`.
3. **Not a black screen** — `smstest` boots the real ROM; a screenshot frame has
the display on and several distinct colours.
4. **Reaches its states** — scripted input drives title → play (→ game over) and
named game variables take expected values.
5. **Reachable by the user** — the `.sms` is copied to a known output path and
presented, not left in a scratch dir.
Details and the harness DSL: `references/testing.md`.
## Step 0 — set up the toolchain (once)
```sh
bash scripts/setup_toolchain.sh # installs SDCC, builds tools + harness
export PATH="$HOME/.devkitsms/bin:$PATH" # ihx2sms, folder2c, smstest, assets2banks
```
This installs SDCC (≥ 4.2, required), builds `ihx2sms`/`folder2c` from the
bundled public-domain sources, installs a **mutually-consistent** SMSlib/PSGlib/
crt0/header set into `~/.devkitsms`, builds the `smstest` emulator, and runs a
smoke build. Full details + flag meanings: `references/toolchain-and-build.md`.
## Pick the workflow
- **New game from scratch** → copy `assets/template/` (Makefile + `src/main.c`
skeleton), then read `references/game-structure.md` for the frame loop, actor
pattern, fixed-point, and collision model. For a game like an existing one,
clone that reference repo instead (`references/examples.md`). For a
**tile-grid / maze** game (Pac-Man, Sokoban, Bomberman) also read
`references/grid-and-maze-games.md`, and for an **RPG / adventure / roguelike**
(scrolling world, menus, dialogue, turns, battles, saves) read
`references/rpg-adventure-roguelike.md` — lane movement, turn-queuing, tunnel wrap,
target-cell chaser AI, procedural art, and maze validation.
- **Porting an existing game** (DOS/SDL/Allegro/C, another console) →
`references/porting.md` (and, if a view is too expensive to compute live but is a
pure function of a little state, `references/prerendered-video.md`). Core idea: keep the platform-clean game logic, rewrite
the platform layer against SMSlib, down-res to 256×192, turn runtime bitmaps
into compiled-in tiles, replace framebuffer collision with a RAM tile-grid, and
kill floating point.
- **Building/fixing an existing devkitSMS project** → use the project's **own**
vendored SMSlib (header+lib together — see the version rule below). Regenerate
data with `folder2c`, then build.
- **Debugging** → jump to "Black-screen triage" below and `references/testing.md`.
- **Optimizing** → fixed-point everywhere, precomputed LUTs, avoid `*`/`/`/`%` by
non-powers-of-two, `--max-allocs-per-node 100000`, keep hot data in RAM.
Whatever the path, wire up a `smstest` playtest early — it is how you *know* you
are done instead of hoping.
## The build pipeline (four steps)
```sh
folder2c data data # assets/ -> data.c + data.h
sdcc -c -mz80 -I$DK/include \ # each .c -> .rel
--peep-file $DK/include/peep-rules.txt file.c -o file.rel
sdcc -o game.ihx -mz80 --no-std-crt0 --data-loc 0xC000 \ # link (crt0 FIRST,
$DK/lib/crt0_sms.rel *.rel $DK/lib/SMSlib.lib $DK/lib/PSGlib.lib # libs LAST)
ihx2sms game.ihx game.sms # .ihx -> padded ROM + checksum
```
`$DK` = `~/.devkitsms`. Prefer `assets/template/Makefile` (single ROM) or
`assets/template/Makefile.banked` (>48 KiB, bank-switched); both add a `playtest`
target. Linking also emits `game.noi` (symbols the test harness reads) — keep it.
## The canonical frame loop
```c
for (;;) {
keys = SMS_getKeysStatus(); // input
update_world(); // RAM-only: physics, AI, collision (fixed-point)
SMS_initSprites(); // build sprite list in RAM
draw_actors(); // SMS_addSprite / SMS_addSpriteClipping
SMS_finalizeSprites();
SMS_waitForVBlank(); // frame clock (60/50 Hz), exactly one per loop
SMS_copySpritestoSAT(); // push sprites — FIRST thing after VBlank
update_hud(); // any other VRAM writes go here too
}
```
**The one hard rule: touch VRAM / CRAM / the sprite SAT only during VBlank.** All
game logic is RAM work before the wait; every VRAM write happens after it. Music
ticks from the line interrupt (`PSGFrame()` in the handler) so it survives slow
frames. See `assets/template/src/main.c` and `references/game-structure.md`.
## Hard constraints cheat-sheet
- **RAM is 8 KiB** at `0xC000` (`--data-loc 0xC000`). No real heap — use static
fixed-size pools, keep large/immutable data in ROM.
- **VRAM is 16 KiB**: 448 tile slots + tilemap + SAT. Plan the tile budget up
front. Reach VRAM only via SMSlib VDP calls, only in VBlank.
- **Colour is 2 bits/channel** (`RGB(r,g,b)`, 0–3), two 16-entry palettes (BG +
sprite); sprite entry 0 is transparent.
- **Sprites: ≤64 total, 8 per scanline.** Wide rows flicker — stagger or use
8×16 tall mode.
- **No float, slow divide.** Use 8.8 fixed-point and precomputed LUTs.
- **Code lives in the fixed first 32 KiB**; page data banks into `0x8000–0xBFFF`
with `SMS_mapROMBank(n)`.
- **Embed the ROM header once** at file scope: `SMS_EMBED_SEGA_ROM_HEADER(9999,0)`
(+ optional `SMS_EMBED_SDSC_HEADER_AUTO_DATE(...)`), else `ihx2sms` can't fix a
checksum and real hardware/emulators may reject it.
## Version consistency (the #1 build breaker)
`SMSlib.h` and `SMSlib.lib` **must** come from the same devkitSMS version. Across
releases, `SMS_addSprite` changed from a function to a macro over
`SMS_addSprite_f` and the calling convention moved to `__sdcccall(1)` (needs SDCC
≥ 4.2). Symptoms and fixes:
- `?ASlink-Warning-Undefined Global '_SMS_addSprite'` → mismatched header/lib.
Use one consistent set (the bundle, or the project's own vendored pair).
- `error 98: conflict with previous declaration of 'putchar'` → old code vs SDCC
4.2 `<stdio.h>`; drop the local prototype, use SMSlib's text renderer.
More in `references/toolchain-and-build.md`.
## Black-screen triage
When a ROM compiles but shows black, check in this order (see
`references/testing.md`, `references/game-structure.md`):
1. **Display left off** — no `SMS_displayOn()` after setup.
2. **Palette never loaded** — every pixel maps to a black entry. Load BG/sprite
palettes before the first frame.
3. **Tiles/tilemap never uploaded** — nothing to show. Load with display off.
4. **VRAM written outside VBlank** — setup raced the beam; do bulk loads with the
display off, per-frame writes after `SMS_waitForVBlank()`.
5. **Crash in setup** — a bad pointer/among the `UNSAFE_*` calls wedged `main`
before the loop. Add a `smstest` `expectvdp display 1` right after setup.
6. **Missing/!bad ROM header** — checksum not fixed; re-check the embed macro.
Run `smstest game.sms tests/playtest.txt` (start from the smoke test in
`references/testing.md`) to see exactly which frame goes wrong and to grab `.ppm`
screenshots.
## Bundled resources
- `scripts/setup_toolchain.sh` — one-shot toolchain install + harness build + smoke test.
- `assets/devkitSMS/` — pinned, consistent SMSlib/PSGlib/crt0/headers + tool sources (`VERSION.txt` explains the version rule).
- `assets/template/` — `Makefile`, `Makefile.banked`, and a commented `src/main.c` game-loop skeleton.
- `assets/smstest/` — the headless SMS test emulator (`smstest.c` + superzazu's MIT Z80 core) and its build; `ATTRIBUTION.md` credits sources.
- `assets/smsvideo/` — reusable pre-rendered-video toolkit: `smsvideo.py` (delta/snapshot tile-stream encoder + verifier), `smsvideo.{c,h}` (runtime player), `mkrom.py` (bank splicer), banked `Makefile`, `example.c`. See `references/prerendered-video.md`.
- `assets/playtest.example.txt` — a real, worked playtest script.
- `references/toolchain-and-build.md` — install, pipeline, flags, memory model, version gotchas, GG/SG notes.
- `references/smslib-api.md` — task-oriented SMSlib + PSGlib API map.
- `references/game-structure.md` — frame loop, fixed-point, actor pattern, collision, HUD, state machine.
- `references/rpg-adventure-roguelike.md` — scrolling OR flip-screen tilemap worlds, menus & dialogue boxes, turn-based/roguelike loops (procgen, FOV) AND real-time action combat (i-frames, knockback), party HUD/minimap, SRAM battery save (+ how to test it). For RPGs/adventures/roguelikes.
- `references/grid-and-maze-games.md` — tile-grid/maze games: lane movement, turn-queuing, tunnel wrap, ghost-style target-cell AI, per-actor speed, procedural sprite/tile generation, maze flood-fill validation, clearing the nametable. Also **metatiles** (16×16 blocks) for large maps.
- `references/prerendered-video.md` — effects the Z80 can't compute live (pseudo-3D road, rotations): pre-render offline and stream from ROM as simulation-indexed tile deltas + snapshots, inside the VBlank budget. Keeps it a game (the sim seeks the video), not a movie.
- `references/assets.md` — images→tiles, music→PSG, maps, compression, banking.
- `references/testing.md` — the harness, the playtest DSL, making variables visible, definition of done.
- `references/porting.md` — the port method and the original→SMS systems mapping.
- `references/examples.md` — catalog of Haroldo-OK's games and what to copy from each.
- `references/community-examples.md` — field-tested techniques from shipped devkitSMS games: Game Gear target, built-in text renderer, hardware sprite-collision (and its Power Base Converter caveat), SRAM saves, PRNG-from-register-R, bank-switching data, input edge patterns.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!