Browse Source

Initial commit: Game Boy Hello World with GBDK-2020, PyBoy test, and AGENTS.md

master
Matteo Benedetto 3 days ago
commit
08218f3cda
  1. 125
      AGENTS.md
  2. 10
      Makefile
  3. 63
      README.md
  4. BIN
      hello.gb
  5. 9
      main.c

125
AGENTS.md

@ -0,0 +1,125 @@
# AGENTS.md — gameboy-hello
Knowledge base for this Game Boy Hello World project and related tools/workflow.
## Project overview
- **Repository**: `ssh://git@git.enne2.net:222/enne2/gameboy-hello.git`
- **Local path**: `/home/enne2/Development/gameboy-hello`
- **Goal**: Minimal Game Boy "Hello World" using GBDK-2020 (C) and headless verification with PyBoy.
- **Artifacts**:
- `main.c` — C source (`<gb/gb.h>`, `<stdio.h>`, `printf("Hello Game Boy!")`).
- `Makefile` — builds `hello.gb` with `/home/enne2/.local/gbdk/bin/lcc`.
- `hello.gb` — generated 32 KB Game Boy ROM.
- `README.md` — user-facing build/run instructions.
- This `AGENTS.md` — captured knowledge for coding agents.
## Game Boy development stack
### Toolchain: GBDK-2020
- **Location**: `/home/enne2/.local/gbdk` (downloaded from GitHub release, no system install needed).
- **Compiler frontend**: `/home/enne2/.local/gbdk/bin/lcc`
- **Typical flags used**:
- `-Wl-yt1` — MBC type 1 (ROM only).
- `-Wl-ya4` — 4 RAM banks.
- **Build command**:
```bash
cd /home/enne2/Development/gameboy-hello
make
```
- **Important**: GBDK C is compiled to SM83/Z80-like assembly by SDCC. It is higher-level than RGBDS assembly but slower and with larger ROM footprint. Critical sections can still be written in assembly if needed.
### Hello World source pattern
```c
#include <gb/gb.h>
#include <stdio.h>
void main(void) {
printf("Hello Game Boy!");
while (1) {
wait_vbl_done();
}
}
```
- `wait_vbl_done()` synchronizes with the vertical blank interval; always call inside the main loop.
- `printf` renders to the Game Boy's text console/tilemap area.
### Emulation / verification
- **PyBoy** installed via `pip install --user pyboy`.
- **Headless screenshot test** (useful for CI/agent verification):
```python
from pyboy import PyBoy
pyboy = PyBoy('hello.gb', window='null')
for _ in range(60 * 5):
pyboy.tick()
pyboy.screen.image.save('/tmp/hello_gb.png')
pyboy.stop()
```
- **SDL2 window test**:
```python
from pyboy import PyBoy
pyboy = PyBoy('hello.gb', window='SDL2')
for _ in range(60 * 30):
pyboy.tick()
pyboy.stop()
```
- ROM validation: check Nintendo logo at offset `0x104` (48 bytes) and header checksum at `0x14D`.
## Game Boy hardware limits (relevant for future expansion)
- **CPU**: Sharp SM83 @ ~4.19 MHz (Z80-like).
- **VRAM**: 8 KB; only writable during VBlank/HBlank.
- **OAM sprites**: max 40 total, max 10 per scanline.
- **Tilemap**: 20×18 visible or 32×32 total.
- **Audio**: 4 channels (2 square, 1 wave, 1 noise).
- **Cartridge MBC**: this project uses MBC1 with `-Wl-yt1 -Wl-ya4`.
## Reverse-engineering Tetris (original Game Boy)
Learned from `kaspermeerts/tetris` disassembly and other sources:
- **Piece representation**: each piece is a 4-block sprite rendered into OAM; rotation is selected by the lowest 2 bits of a state byte (4 pre-rotated sprites per piece type).
- **Rotation logic**: `RotateAndShiftPiece` increments/decrements orientation, renders into OAM buffer, calls `DetectCollision`, and **cancels the rotation on collision** (no SRS-style wall kicks).
- **Collision detection**: done on the rendered OAM sprites by looking up background tiles; a non-space tile means collision.
- **Randomizer (`PickRandomPiece`)**: uses `rDIV` hardware divider as entropy and rerolls up to 3 times if the new piece matches the previous one (history of 1). It is NOT a modern 7-bag randomizer.
- **DAS (Delayed Auto Shift)**: 23 frames before auto-repeat, then 9 frames per repeat.
- **Scoring**: BCD arithmetic, level-dependent multipliers.
- **Bugs/trivia**:
- Top two rows can never be cleared (only 16 of 18 rows are checked).
- When clearing multiple lines, the top row is duplicated below — likely an oversight.
- Demo recording code exists but is unused.
## pi-hitl-programming extension knowledge
- **Goal**: Human-in-the-Loop gate for `write`/`edit` on programming/config files.
- **Source**: `/home/enne2/Development/pi-hitl/src/index.ts`
- **Behavior**:
- Intercepts `write`/`edit` for file extensions like `.py`, `.js`, `.ts`, `.c`, `.cpp`, `.go`, `.rs`, `.sh`, `.html`, `.css`, `.json`, `.yaml`, `.toml`, `.md`, etc.
- Generates a short LLM explanation of the proposed code.
- Shows a scrollable panel with explanation + proposed code.
- Lets user choose: Accept / Modify / Discuss / Reject.
- Can be toggled with `F10` or `/hitl on|off`.
- **Lessons learned**:
- Long explanations cause UI flashing and scrolling issues; keep explanation prompt to 5–6 lines.
- Use fixed-height scrollable panel for code preview; avoid full-screen expansion.
- Avoid `PageUp`/`PageDown` bindings that may crash; use `Ctrl+U`/`Ctrl+D` instead.
- Pass the full source code (up to 60k chars) to the LLM for explanation, not only the TUI preview.
## General workflow rules
- When writing or modifying files, always use the `write` or `edit` tool; never use shell redirections to bypass HITL review.
- For C/C++ code, `<` in content may be truncated by certain providers; use placeholder replacement if needed.
- Prefer `python3 -m py_compile` for quick syntax checks on Python scripts.
- Use `tmux` for interactive TUI tests when the agent cannot open a real terminal.
## Follow-up ideas
- Expand this project into a real Game Boy Tetris faithful to the original:
- Use sprite OAM for pieces (4 blocks per piece).
- Implement the original orientation-based rotation and collision cancel logic.
- Replicate the original `rDIV`-based randomizer with 1-history reroll.
- Add DAS, BCD scoring, and proper line-clear behavior.

10
Makefile

@ -0,0 +1,10 @@
CC = /home/enne2/.local/gbdk/bin/lcc
CFLAGS = -Wl-yt1 -Wl-ya4
hello.gb: main.c
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f *.gb *.ihx *.cdb *.adb *.noi *.map *.lst *.sym *.rel
.PHONY: clean

63
README.md

@ -0,0 +1,63 @@
# Game Boy Hello World
Questa sottocartella contiene un minimo esempio di "Hello World" per Game Boy originale, compilato con **GBDK-2020** in C.
## Struttura
- `main.c` — sorgente C.
- `Makefile` — regola per compilare con `lcc` (GBDK-2020).
- `hello.gb` — ROM Game Boy risultante (32 KB).
## Requisiti
GBDK-2020 e installato in `/home/enne2/.local/gbdk`. Se vuoi spostarlo altrove, aggiorna il `CC` nel `Makefile`.
## Build
```bash
cd gameboy-hello
make
```
## Test con emulatore
### PyBoy (senza finestra, screenshot)
```bash
python3 - <<'PY'
from pyboy import PyBoy
rom = 'hello.gb'
pyboy = PyBoy(rom, window='null')
for _ in range(60*5):
pyboy.tick()
pyboy.screen.image.save('/tmp/hello_gb.png')
pyboy.stop()
print('Screenshot salvato in /tmp/hello_gb.png')
PY
```
### PyBoy (con finestra SDL2)
```bash
python3 - <<'PY'
from pyboy import PyBoy
pyboy = PyBoy('hello.gb', window='SDL2')
for _ in range(60*30):
pyboy.tick()
pyboy.stop()
PY
```
## Note
- La ROM usa il logo Nintendo corretto e il checksum di header e valido.
- Il testo viene visualizzato con `printf` sulla console testuale del Game Boy.
- Il loop principale chiama `wait_vbl_done()` per sincronizzarsi con il VBlank.
## Prossimi passi
Da qui si puo partire per un vero gioco Game Boy (ad esempio Tetris), aggiungendo:
- gestione di input tramite `joypad()`;
- sprite OAM per i pezzi;
- tilemap per il campo di gioco;
- musica/effetti sonori con i 4 canali audio del Game Boy.

BIN
hello.gb

Binary file not shown.

9
main.c

@ -0,0 +1,9 @@
#include <gb/gb.h>
#include <stdio.h>
void main(void) {
printf("Hello Game Boy!");
while (1) {
wait_vbl_done();
}
}
Loading…
Cancel
Save