Learn Game Boy DMG Assembly and ROM Hacking (Build and Modify Games on the Original Game Boy)
Goal: After completing this guide, you will understand the original Game Boy (DMG-01) as a complete, deterministic machine and be able to build real, playable games in pure assembly. You will know how the SM83 CPU, PPU, APU, memory map, and cartridge hardware interact on each frame, and you will be able to reason about timing, interrupts, and data movement with confidence. You will also learn a careful, legal ROM hacking workflow: how to analyze a ROM you own, identify data structures, alter graphics/text/behavior safely, and ship patches that never distribute copyrighted data. Your outcome is not just a ROM file, but the ability to explain every byte that makes the DMG render a frame.
Introduction
Game Boy DMG assembly is bare-metal programming for the original 1989 handheld: a 4.194304 MHz SM83 CPU driving a tile-based PPU, a four-channel APU, and a tightly constrained 64 KB address space. There is no OS, no drivers, and no safety net; the only way to make pixels move is to write bytes to specific memory-mapped registers at the correct time. In this guide you will build 20 progressive projects that start with a bootable ROM and culminate in complete DMG games and carefully crafted ROM modifications.
Scope (strict):
- Included: DMG-01 hardware only, SM83 assembly, RGBDS toolchain, DMG memory map, DMG PPU/APU timing, DMG cartridge header and MBCs used by DMG games, and ROM hacking with owned ROMs.
- Excluded: Game Boy Color (CGB) features, Super Game Boy (SGB) features, Game Boy Advance (GBA), and C-based frameworks like GBDK.
Big picture diagram (end-to-end flow):
Assembly Source Assembler/Linker Cartridge ROM DMG Hardware
+-----------------+ +------------------+ +----------------+ +-----------------------+
| .asm + assets | -> | rgbasm/rgblink | --> | .gb ROM | -> | CPU -> PPU/APU -> LCD |
+-----------------+ | rgbfix header | +----------------+ +-----------------------+
| |
v v
map/symbols Emulator debugger
What you will build:
- A pure-assembly toolchain and ROM template that boots on DMG.
- Core subsystems: input, graphics (tiles/sprites), timing, audio, saves.
- Two complete games (assembly-only) and a content pipeline.
- A ROM hacking workflow for graphics, text, and behavior edits with patches.
How to Use This Guide
- Read the Theory Primer first. It is a mini-book and provides the mental models you will need to survive real DMG constraints.
- Start with Project 1 and proceed in order. Each project depends on the previous.
- Use the Definition of Done checklist to verify each build.
- Do the Thinking Exercise before coding; it trains your mental model.
- Use the Hints in Layers only when stuck.
- Keep a notebook: every time you read or write a register, write why.
Prerequisites & Background Knowledge
1) Essential Prerequisites (Must Have)
- Comfortable with basic programming (variables, loops, control flow).
- Familiar with hex/binary and bitwise operations.
- Basic command line usage (running tools, reading build output).
2) Helpful But Not Required
- Understanding of CPU concepts (registers, stack, interrupts).
- Basic digital logic or architecture exposure.
- Any prior assembly experience (x86, ARM, etc.).
3) Self-Assessment Questions
- Can you explain what a stack is and why a CPU needs it?
- Can you read a hex value like
0xFF40and understand it is an address? - Do you know what an interrupt is in general terms?
- Can you trace a loop that updates memory?
4) Development Environment Setup
- RGBDS (assembler/linker/patcher):
rgbasm,rgblink,rgbfix,rgbgfx- https://rgbds.gbdev.io/ - Emulator with debugger: SameBoy (https://sameboy.github.io/) or BGB (https://bgb.bircd.org/) for single-step, VRAM/OAM view, and breakpoints.
- Graphics tools: GBTD (https://www.devrs.com/gb/hmgd/gbtd.html) / GBMB (https://www.devrs.com/gb/hmgd/gbmb.html) or any 2bpp-capable editor;
rgbgfxfor asset conversion. - Optional disassembler:
rgbdsrgbdisor a ROM explorer tool for hacking tasks.
5) Time Investment
- Projects 1-5: 1-3 days each
- Projects 6-12: 3-7 days each
- Projects 13-20: 1-2 weeks each (hacking and full games)
6) Important Reality Check
DMG programming is unforgiving. A single wrong write to LCD registers at the wrong time can freeze the screen. Your reward is deep mastery of hardware and true control over a classic platform.
Big Picture / Mental Model
The DMG Frame
+--------------------------------------------+
| CPU executes instructions (4.19 MHz) |
| | |
| | writes memory-mapped registers |
| v |
| PPU runs scanline state machine (mode 0-3) |
| | |
| | reads VRAM/OAM for tiles/sprites |
| v |
| LCD shows 160x144 pixels @ ~59.7 Hz |
| |
| APU generates audio via 4 channels |
+--------------------------------------------+
CPU <-> Memory Map <-> Cartridge (ROM/RAM + MBC)
Theory Primer (Mini-Book)
Chapter 1: DMG Hardware Model and Memory Map
Fundamentals
The DMG is a memory-mapped system: every hardware feature is controlled by reading or writing a specific address. The CPU sees a flat 64 KB address space, but the meaning of an address depends on its range. For example, VRAM lives at 0x8000-0x9FFF, OAM at 0xFE00-0xFE9F, and hardware registers at 0xFF00-0xFF7F. Understanding these ranges is non-negotiable because the DMG has no abstraction layer. When you write bytes to the LCD control register at 0xFF40, you turn the display on or off. When you write to 0xFF46, you trigger OAM DMA. The DMG boot ROM initializes some registers, shows the logo, and then hands control to the cartridge at 0x0100. If you do not build a correct ROM header, your program will never run. Every project in this guide depends on knowing which memory region you are touching and what side effects it triggers.
Deep Dive into the concept
The DMG memory map is the machine’s API, and it is intentionally simple but full of pitfalls. The CPU addresses 64 KB, but several regions are special. The first 32 KB (0x0000-0x7FFF) typically holds the cartridge ROM, split into a fixed bank (ROM0) and a switchable bank (ROMX) when an MBC is present. This means that even though the CPU only sees 32 KB at a time, the cartridge can expose far more by switching banks through MBC registers. VRAM (0x8000-0x9FFF) is the only place the PPU reads tile and tilemap data, but it cannot be written freely while the LCD is actively drawing; you must respect LCD modes or use VBlank/HBlank windows. External cartridge RAM (0xA000-0xBFFF) may or may not exist and often requires enabling via MBC. Internal work RAM (0xC000-0xDFFF) is your general-purpose RAM; its echo (0xE000-0xFDFF) exists for compatibility but is not recommended. OAM (0xFE00-0xFE9F) holds sprite attributes and has strict size limits (40 sprites, 4 bytes each). The unusable range (0xFEA0-0xFEFF) should never be accessed. High RAM (0xFF80-0xFFFE) is a tiny but fast stack-friendly area. The interrupt enable register at 0xFFFF is separate and must be explicitly set.
The practical consequence is that you must always know the memory region before performing a write. A beginner mistake is to write tile data to 0x9000 while the LCD is on and wonder why the sprite corrupts. Another is to place data in ROM0 beyond 0x3FFF without banking, which silently truncates or overlaps. When ROM hacking, understanding the header and memory map lets you locate the entry point (0x0100), the title region, the cartridge type, and the global checksums. It also helps you interpret address references inside the game: a pointer to 0x9800 is almost certainly a background tilemap, while a pointer to 0xFE00 is sprite data. The DMG memory map is also the bridge between assembly programming and ROM hacking: hacking is just disciplined memory editing with an understanding of what each address means.
Two additional subtleties matter in practice. First, the DMG power-up state is not fully deterministic across hardware revisions. Some registers have defined reset values, but others can vary. If you assume a register defaults to a specific value and skip initialization, your game may run in an emulator and fail on real hardware. A robust DMG program explicitly initializes the hardware registers it relies on and disables the LCD before large VRAM transfers. Second, you must treat the echo RAM area as undefined. It mirrors WRAM but can introduce confusing bugs when code accidentally reads from or writes to it. When you see unpredictable behavior, the first diagnostic step is to confirm you are using the canonical ranges.
From a ROM hacking perspective, address context guides interpretation. If you see a table of bytes referenced by code that also writes to 0xFF47 or 0xFF40, the table is likely palette or LCD control values. If you see code that writes to 0x2000, it is changing banks; any pointers that follow likely refer to data in the newly selected bank. These correlations allow you to infer structure even without symbols. The memory map is not just a list of addresses; it is the key to deciphering intent.
How this fits on projects
- Projects 1-3 teach the ROM header, entry point, and safe writes to LCD/VRAM.
- Projects 4-7 require OAM, VRAM, and tilemap control.
- Projects 16-20 rely on identifying memory regions in existing ROMs.
Definitions & key terms
- Memory map: The mapping from address ranges to hardware components.
- ROM0/ROMX: Fixed and switchable ROM banks.
- VRAM: Video RAM holding tiles and maps.
- OAM: Object Attribute Memory for sprites.
- HRAM: High RAM (
0xFF80-0xFFFE). - MMIO: Memory-mapped I/O registers.
Mental model diagram
Address Space (DMG)
0xFFFF +---------------+ IE (interrupt enable)
0xFF80 | HRAM |
0xFF00 | I/O Regs | LCDC, STAT, Joypad, etc.
0xFEA0 | Unusable |
0xFE00 | OAM | 40 sprites
0xE000 | Echo RAM | (avoid)
0xC000 | WRAM |
0xA000 | Cart RAM | optional
0x8000 | VRAM | tiles + maps
0x4000 | ROMX (banked) |
0x0000 | ROM0 (fixed) |
+---------------+
How it works (step-by-step)
- CPU fetches instructions from ROM0/ROMX.
- Code reads/writes MMIO addresses to control hardware.
- PPU reads VRAM/OAM and renders scanlines.
- Interrupts fire (VBlank, STAT, Timer, etc.) and change flow.
- Cartridge MBC switches banks to access more ROM/RAM.
Minimal concrete example
; Turn LCD on by writing to LCDC (0xFF40)
ld a, $91
ld [$FF40], a
Common misconceptions
- “VRAM is just RAM; I can write anytime.” (You cannot during Mode 3.)
- “All ROM addresses are fixed.” (ROMX is banked and changes.)
- “Echo RAM is safe.” (It is unreliable and not recommended.)
Check-your-understanding questions
- What is the difference between ROM0 and ROMX?
- Why must VRAM writes be synchronized?
- What is stored in OAM?
Check-your-understanding answers
- ROM0 is fixed bank 0; ROMX is a switchable bank controlled by the MBC.
- The PPU reads VRAM during rendering, so writes during Mode 3 corrupt data.
- Sprite attributes: Y, X, tile index, and flags for 40 sprites.
Real-world applications
- Creating a tile streaming engine for scrolling levels.
- Detecting sprite overflow and flicker behavior.
- ROM hacking: locating data by address range and interpreting it.
Where you’ll apply it Projects 1-7, 12-13, 16-20.
References
- Pan Docs: Memory Map - https://gbdev.io/pandocs/Memory_Map.html
- Game Boy: Complete Technical Reference (gekkio) - https://gbdev.io/gbctr/
- The Game Boy Programming Manual (Nintendo) - https://archive.org/details/GameBoyProgManVer1.1
Key insight If you can predict what a write to any address does, you can control the DMG.
Summary The DMG is a memory map. Every byte has a meaning. Your job is to respect the ranges, understand the side effects, and write at the right time.
Homework/Exercises
- Draw the memory map from memory with address ranges.
- Mark which regions are safe to write during VBlank.
- Identify three registers you will write in every game.
Solutions
- Compare with the diagram above.
- VRAM, OAM, and some I/O are safest in VBlank.
- LCDC (0xFF40), BGP (0xFF47), JOYP (0xFF00).
Chapter 2: SM83 CPU and Assembly Fundamentals
Fundamentals The Game Boy CPU is a Sharp SM83 (often called LR35902), which is similar to a Z80 but with important differences. It has 8-bit registers (A, F, B, C, D, E, H, L), 16-bit register pairs (AF, BC, DE, HL), and two special 16-bit registers: SP and PC. Instructions operate on registers or memory addressed by HL, BC, DE, or an immediate value. The F register holds flags (Z, N, H, C) that determine branching and arithmetic behavior. Because the DMG has no OS, your assembly controls the boot sequence, stack, and interrupt vector table directly. Every project in this guide assumes you can read and write assembly and reason about flags and cycles.
Deep Dive into the concept The SM83 is a hybrid between the Intel 8080 and Z80. It removes some Z80 features (like IX/IY registers) and adds some conveniences. Understanding the instruction set is critical because it determines your data movement patterns and performance. The CPU has a simple pipeline: fetch, decode, execute. On DMG, instruction timing is deterministic and measured in machine cycles, which directly relate to PPU timing. Some instructions are multi-cycle, and the difference between 4 and 20 cycles matters when you are racing the VBlank window. The flags are especially tricky: Z is set when result is zero, N indicates subtraction, H (half-carry) is used for BCD corrections, and C indicates carry or borrow. The DAA instruction is notoriously subtle, and ROM hacks that patch arithmetic often break because flags are mishandled.
In assembly-only development, you must also manage the stack manually. The stack grows downward from the address in SP. PUSH and POP affect SP and write to WRAM/HRAM. If you forget to initialize SP early, your first PUSH may overwrite critical data or ROM data that you assumed was safe. You must define interrupt vectors at 0x0040-0x0060, and when an interrupt fires, the CPU pushes PC to stack and jumps to the ISR. If IME is disabled or the interrupt flag is not set, the ISR never executes. A game that relies on VBlank interrupts will hang if IME is not enabled or if the ISR never clears the interrupt flag.
The SM83 lacks some Z80 instructions like IN/OUT; instead, it uses memory-mapped I/O. That means reading from $FF00 for joypad or $FF44 for LCD Y coordinate is just a load instruction. This unifies the programming model but also makes it easy to mistakenly treat hardware registers as normal memory. In practice, you should separate your code into high-level subsystems (input, graphics, audio) even in assembly, and use macros or include files for register constants. RGBDS provides .equ and .const directives that help you maintain clarity.
Instruction encoding and length matter for ROM hacking. When you patch a ROM, you must preserve instruction length or adjust all branch targets. Many hacking workflows use “free space” (unused bytes) and long jumps to new code, then jump back to the original location. This requires an understanding of relative jumps (jr) vs absolute jumps (jp) and the limitations of each (8-bit offset vs 16-bit absolute). If you patch a jr to a far address, the ROM will crash. For DMG, you must also think about banked code: a jp to an address in ROMX only makes sense if the correct bank is currently mapped.
The instruction set also includes a CB-prefixed table that expands bit operations (BIT, SET, RES, shifts, rotates). These are common in real games for flag manipulation and tile/index calculations. The difference between a rotate through carry and a logical shift matters when you are packing multiple flags into a byte. On DMG, instruction timing is deterministic but not uniform; the same instruction can take different cycles depending on whether a branch is taken. This matters when you write code inside a VBlank window or an ISR. A practical habit is to treat the instruction timing table as a budgeting tool: for critical sections, sum cycles and compare to the time you have. This is how you avoid frame drops and glitchy updates.
How this fits on projects
- Projects 1-5 teach the instruction set through direct hardware control.
- Projects 8-10 use timers and interrupts to schedule work.
- Projects 16-20 require safe patching and control flow reasoning.
Definitions & key terms
- SM83/LR35902: The DMG CPU.
- Flags (Z, N, H, C): Status bits set by arithmetic/logic ops.
- IME: Interrupt Master Enable.
- ISR: Interrupt Service Routine.
Mental model diagram
Fetch -> Decode -> Execute -> Update flags -> Next PC
^ |
| v
+------------ Interrupt? --------
How it works (step-by-step)
- CPU reads opcode at PC.
- Decodes instruction and operands.
- Executes and updates flags.
- Updates PC to next instruction.
- If interrupt enabled and pending, pushes PC and jumps to vector.
Minimal concrete example
; Add B to A and branch if zero
add a, b
jr z, .is_zero
Common misconceptions
- “Flags update only on arithmetic.” (Many ops update flags.)
- “JR can jump anywhere.” (It has an 8-bit signed range.)
- “IME on is enough.” (You must set IE and clear IF bits.)
Check-your-understanding questions
- Why is DAA hard to implement correctly?
- What is the difference between
jrandjp? - Why must SP be initialized before interrupts?
Check-your-understanding answers
- It depends on prior operation and flag state; BCD correction is conditional.
jris relative 8-bit;jpis absolute 16-bit.- ISR pushes PC to stack; uninitialized SP corrupts memory.
Real-world applications
- Optimizing loops to fit inside VBlank.
- Patching ROM code while preserving instruction length.
Where you’ll apply it Projects 1-20 (all).
References
- Pan Docs: CPU Instruction Set - https://gbdev.io/pandocs/CPU_Instruction_Set.html
- Game Boy: Complete Technical Reference (gekkio) - https://gbdev.io/gbctr/
- RGBDS documentation - https://rgbds.gbdev.io/docs/
Key insight Assembly on DMG is not just syntax; it is timing, memory, and correctness.
Summary The SM83 is simple but strict. Mastering registers, flags, and instruction timing is the foundation for everything else.
Homework/Exercises
- Write a short loop that copies 16 bytes from HL to DE.
- Explain why
jrcan fail in ROM hacks with long jumps. - Write a function prologue/epilogue using PUSH/POP.
Solutions
- Use
ld a,[hl+]andld [de],awith a counter. - The offset is limited to -128..+127 bytes.
push af…pop af, update SP accordingly.
Chapter 3: ROM Layout, Header, and Banking (MBC)
Fundamentals
Every DMG program is a cartridge ROM with a strict header format at fixed addresses. The header includes the entry point, Nintendo logo data, title, cartridge type (MBC), ROM/RAM size, and checksums. When you build a ROM with RGBDS, rgbfix fills the header and checksums, and incorrect header data will prevent boot. Bank controllers (MBC1, MBC3, etc.) allow the CPU to access more than 32 KB of ROM and optional external RAM. For assembly-only development, you must design your code and data to fit in ROM0 and ROMX and manage bank switching explicitly. For ROM hacking, you must understand how a game uses its banks to avoid patching the wrong bank.
Deep Dive into the concept
The DMG boot ROM verifies the cartridge logo and header before transferring control to the game at 0x0100. The header structure (roughly 0x0100-0x014F) defines not just metadata but hardware behavior. The cartridge type field determines whether an MBC is present and what features it supports (battery-backed RAM, RTC, etc.). For example, a game using MBC1 can have multiple ROM banks and optional RAM banks. This makes banking a core programming concept: code running at 0x4000-0x7FFF changes depending on the selected bank. If you jump into ROMX without selecting the correct bank, you will execute garbage. In assembly projects, you will often keep the main loop and interrupt handlers in ROM0 and switch banks only for data-heavy routines (level data, graphics, scripts). In ROM hacking, you must discover which banks contain the data you want to modify and ensure your patch does not exceed the bank size or collide with other content.
RGBDS uses sections (SECTION) to place code/data into specific banks. SECTION "Code", ROM0[$0150] is common for your entry point, while SECTION "Banked", ROMX[$4000], BANK[1] places code in bank 1. The linker map file is your truth: it tells you where each symbol ended up. When hacking, you rarely have a linker map, so you recreate one by disassembling and mapping patterns (like jump tables or pointer tables). Bank switching itself is done by writing to MBC registers in the 0x0000-0x7FFF region (which is counterintuitive because it is ROM space). For MBC1, writes to 0x2000-0x3FFF select lower ROM bank bits. If you accidentally bank-switch in an ISR or forget to restore the previous bank, you can crash the game or corrupt data reads. This is why many DMG engines store the current bank in RAM and wrap bank changes in macros that save/restore state.
ROM hacking also intersects with checksums. When you modify bytes in a ROM, the header checksum and global checksum may become invalid. Some emulators ignore this, but real hardware may not. Tools like rgbfix can recalculate checksums for your custom ROMs, and patch formats (IPS/BPS) often ignore checksums, leaving it to the player to verify. If you distribute patches, you should document the expected base ROM hash so the patch applies correctly.
Banking has additional traps. MBC1, for instance, cannot select bank 0 in ROMX; writing 0 is treated as bank 1. Many engines rely on that quirk, so a naive bank switch routine can accidentally mirror data. MBC3 adds RAM banking and an RTC, which introduces new registers and access rules. Even if you do not use RTC, you must be aware that some ROMs interleave RAM and RTC registers in the same address range, so your writes might affect time registers instead of RAM. From a software design perspective, it is useful to wrap bank switching and RAM enable/disable into tiny functions or macros so you can audit every bank change. This is critical in ROM hacks where you may inject code into a game that already has its own bank management; you must either reuse its bank selection conventions or you will clash with its expectations.
How this fits on projects
- Projects 1-3 teach the header and
rgbfixusage. - Projects 12-13 implement MBC banking and save RAM.
- Projects 16-20 rely on bank-aware ROM hacking.
Definitions & key terms
- ROM header: Metadata and checksums at
0x0100-0x014F. - MBC: Memory Bank Controller.
- ROM0/ROMX: Fixed vs banked ROM regions.
- Checksum: Header/global validation values.
Mental model diagram
ROM Addressing
ROM0 (fixed) 0x0000-0x3FFF
ROMX (bank) 0x4000-0x7FFF
[Bank 1] [Bank 2] [Bank 3] ...
^ write MBC register to select
How it works (step-by-step)
- Boot ROM checks header/logo.
- Jumps to
0x0100entry point. - Game code runs in ROM0.
- Game selects banks for ROMX as needed.
- Data/code is fetched from active bank.
Minimal concrete example
; MBC1: select ROM bank 2
ld a, $02
ld [$2000], a
Common misconceptions
- “Bank switching changes ROM0.” (It only changes ROMX.)
- “Checksums are optional.” (Hardware expects correct header checksum.)
Check-your-understanding questions
- Why must ISR code live in ROM0?
- What does the MBC register write actually do?
- Why can a ROM hack crash only in some areas?
Check-your-understanding answers
- Interrupt vectors are fixed in ROM0; banked code may not be mapped.
- It selects which bank is visible at
0x4000-0x7FFF. - You may have patched a bank that is not currently mapped.
Real-world applications
- Building a multi-level game with banked level data.
- Patching text stored in a specific bank.
Where you’ll apply it Projects 1-3, 12-13, 16-20.
References
- Pan Docs: Cartridge Header - https://gbdev.io/pandocs/The_Cartridge_Header.html
- Pan Docs: MBCs - https://gbdev.io/pandocs/MBCs.html
- RGBDS documentation (
rgbfix,rgblink) - https://rgbds.gbdev.io/docs/
Key insight Banking is how the DMG gets big games into a small address space.
Summary The header and banking rules are strict. Respect them or nothing runs.
Homework/Exercises
- Identify the header fields you would change to mark a ROM as MBC1.
- Explain why bank 0 is special.
- Sketch a banked layout for a 128 KB ROM.
Solutions
- Cartridge type, ROM size, RAM size, and checksums.
- Bank 0 is fixed and always mapped to
0x0000-0x3FFF. - Banks 0-7, ROM0 fixed, ROMX banked.
Chapter 4: RGBDS Toolchain and Build Pipeline
Fundamentals
RGBDS is the standard assembler/linker suite for Game Boy development. rgbasm assembles .asm files into object files, rgblink links them into a ROM, and rgbfix writes header metadata and checksums. For graphics, rgbgfx converts images to the 2bpp tile format. A clean build pipeline means you can repeat builds, generate a map file, and debug with symbols. For DMG assembly, RGBDS is the difference between guessing and knowing where your code lives. You should think of the toolchain as part of the system: if you cannot reproduce a build or map a label to an address, you cannot debug timing issues or patch safely.
Deep Dive into the concept
RGBDS is not just a set of tools; it is the workflow for deterministic assembly builds. rgbasm reads your source and produces an object file that contains code, data, and relocation information. This lets you use labels without hand-calculating addresses. rgblink resolves those labels and places sections according to your SECTION directives. The linker can output a map file that lists each symbol and its address, which is essential when debugging or when building ROM hacks that need precise address knowledge. rgbfix writes the Nintendo logo data, header fields (title, cartridge type, ROM/RAM size), and recalculates checksums. If you skip rgbfix or set the wrong ROM size, your ROM may boot in an emulator but fail on hardware.
RGBDS also provides macros and constants to improve maintainability. A DMG project should define all I/O registers and bit flags in a shared include file (e.g., hardware.inc). This reduces mistakes and clarifies intent: ldh [rLCDC], a is far more readable than ldh [$FF40], a. The ldh instruction is optimized for high RAM and I/O space and should be used for I/O registers in $FF00-$FF7F. RGBDS supports SECTION attributes to control bank placement, alignment, and origin. This is essential when you want to put interrupt vectors at exact addresses or reserve data blocks for DMA routines.
For ROM hacking, RGBDS offers two critical advantages: (1) you can rebuild and insert patches into a new ROM using precise addresses, and (2) you can assemble “overlay” code to be inserted into free space, then redirect control flow from the original code. A typical workflow is: locate free space, assemble new code into that space using SECTION "Free", ROMX[$7A00], BANK[3], then patch the original routine to jp into the new code and back. This requires exact control over addresses, which RGBDS provides. It also requires discipline: you must track which banks are used and ensure that your patch does not overlap existing data.
A production-grade build also benefits from repeatability. Use a simple build script or Makefile so every build produces the same ROM, map, and symbol files. This makes debugging reproducible and makes it easier to bisect regressions. RGBDS supports symbol output formats that many emulators can load, enabling source-level breakpoints. Also note that alignment matters: if you place data in ROMX without alignment, you may split tables across boundaries or misalign data expected by DMA routines. If you are building a data-driven engine, define fixed section layouts early so assets and tables stay in predictable locations. When hacking, you can also use RGBDS to reassemble disassembled code with labels, then re-link it to match original addresses. This is a powerful workflow for major hacks because it allows you to keep the codebase maintainable while still producing a ROM compatible with the original game. As your project grows, a deterministic build becomes a debugging instrument: you can reproduce a bug, compare map files, and isolate what changed. Keep build artifacts under version control for traceability.
How this fits on projects
- Projects 1-5 build your baseline ROM pipeline.
- Projects 12-13 rely on linker-controlled bank layout.
- Projects 16-20 use RGBDS for patching and rebuilding ROMs.
Definitions & key terms
- Object file: Assembled code with relocations.
- Map file: Symbol/address listing from the linker.
- Section: A named chunk of code/data with placement rules.
Mental model diagram
.asm --> rgbasm --> .o --> rgblink --> .gb --> rgbfix --> bootable ROM
| |
v v
map file emulator/debugger
How it works (step-by-step)
- Write assembly and asset includes.
- Assemble into object files.
- Link into a ROM with sections.
- Fix header and checksums.
- Run in emulator with symbols.
Minimal concrete example
; Build commands
; rgbasm -o build/main.o src/main.asm
; rgblink -o build/game.gb -m build/game.map build/main.o
; rgbfix -v -p 0 build/game.gb
Common misconceptions
- “I can skip the map file.” (You need it for debugging and hacking.)
- “Linker will place code automatically.” (Bad placement can break vectors.)
Check-your-understanding questions
- What does
rgbfixchange in a ROM? - Why is a map file useful in a debugger?
- How does a
SECTIONcontrol code placement?
Check-your-understanding answers
- Header metadata and checksums.
- It maps addresses to labels so you can set breakpoints by name.
- It sets origin/bank and alignment for code/data.
Real-world applications
- Deterministic ROM builds for homebrew releases.
- Patching ROMs in free space with precise addresses.
Where you’ll apply it Projects 1-3, 12-13, 16-20.
References
- RGBDS documentation - https://rgbds.gbdev.io/docs/
- gbdev.io development hub - https://gbdev.io/
Key insight RGBDS makes assembly predictable, which is essential for real DMG work.
Summary The toolchain is part of the system. Learn it early or debug forever.
Homework/Exercises
- Create a minimal
SECTIONlayout for ROM0 and ROMX. - Generate a map file and find the address of a label.
Solutions
- Use ROM0 at
$0100, ROMX at$4000in bank 1. rgblink -m build/game.mapand search the label in the map.
Chapter 5: PPU Graphics Pipeline (Tiles, Maps, Sprites)
Fundamentals
The DMG PPU renders a 160x144 screen using 8x8 tiles stored in VRAM. Backgrounds are 32x32 tile maps (256x256 pixels) that the PPU scrolls with SCX and SCY. The window layer is an optional overlay, and sprites (OBJs) are stored in OAM. The PPU operates in modes (OAM scan, pixel transfer, HBlank, VBlank). Because the PPU reads VRAM and OAM while drawing, the CPU must update those regions only during safe windows (VBlank or certain HBlank timing). Every visual in your game is a combination of tile data, map data, sprite attributes, and palette registers. If you can trace how a pixel reaches the LCD, you can debug almost any graphics bug.
Deep Dive into the concept The DMG display is deceptively simple. Each frame is drawn as 154 scanlines: 144 visible lines and 10 VBlank lines. On each visible line, the PPU cycles through a state machine: Mode 2 (OAM scan), Mode 3 (pixel transfer), Mode 0 (HBlank). During Mode 3, VRAM access is restricted; during Mode 2, OAM access is restricted. This means your code must schedule VRAM/OAM updates carefully or use DMA. The DMG stores tiles in 2bpp format: each tile row is two bytes representing low/high bits for eight pixels. Tile maps are just bytes that index tiles. Because of this, animation is often done by swapping tile indices rather than moving pixel data.
Sprites are limited: only 40 total, and only 10 per scanline. Sprite attributes include X/Y position (with a hardware offset), tile index, and flags (palette, flip, priority). When you exceed the per-scanline limit, the DMG drops sprites based on OAM order, which leads to flicker. Understanding this is essential for game design: if you keep enemies in higher OAM indices, they will vanish first. The window layer is special: it ignores SCX/SCY and has its own WX/WY position. This is useful for HUDs, but the window triggers late in the scanline and can cause timing artifacts if not configured correctly.
From a data perspective, you must manage two kinds of VRAM data: tile patterns (usually at 0x8000) and tile maps (at 0x9800 or 0x9C00). When hacking an existing ROM, you can locate maps by scanning for sequences of tile indices, and you can locate tiles by searching for 2bpp patterns. If you alter tiles, you must ensure that the tile indices in the map still point to the correct patterns. When building your own game, you should build a tooling pipeline that exports tiles and maps with a predictable index order.
The LCD control register (LCDC) governs everything: enabling the LCD, selecting the tile data area, enabling sprites, choosing sprite size (8x8 or 8x16), enabling the window, and selecting map bases. The STAT register provides status flags and can generate interrupts for mode transitions and line comparisons (LYC). A mature DMG game uses STAT interrupts for mid-frame effects, such as a fixed HUD (window) or split scrolling. This is more advanced but achievable with a strict understanding of scanline timing.
Another nuance is tile addressing mode. The DMG has two tile data regions, and one uses signed tile indices (0x8800 region) while the other uses unsigned indices (0x8000 region). If you choose the signed mode, tile index 0 refers to 0x9000, and negative indices point backward. Many beginners see scrambled graphics because they load tiles into one region but configure the other in LCDC. In ROM hacking, this distinction is crucial: if a game uses signed indices, you cannot just insert tiles at 0x8000 and expect them to appear. You must identify which mode is active and match your asset placement to that mode. Finally, remember that palette values are applied to tile pixel values (0-3), not to tiles themselves. So changing the palette can dramatically change the look of the entire background without touching tile data.
How this fits on projects
- Projects 3-7 implement sprites, tilemaps, and scrolling.
- Projects 14-15 build full games using the PPU pipeline.
- Projects 17-18 modify graphics and maps in existing ROMs.
Definitions & key terms
- Tile: 8x8 pixel block stored in 2bpp format.
- Tile map: 32x32 grid of tile indices.
- OAM: Sprite attribute table.
- LCDC/STAT: LCD control/status registers.
Mental model diagram
PPU pipeline per scanline
[Mode 2: OAM] -> [Mode 3: Pixel Transfer] -> [Mode 0: HBlank]
After 144 lines: [Mode 1: VBlank]
How it works (step-by-step)
- PPU scans OAM for sprites on the line.
- PPU fetches tiles and map data from VRAM.
- Pixels are pushed to LCD.
- HBlank occurs; CPU can update VRAM/OAM.
- After line 144, VBlank occurs and CPU updates safely.
Minimal concrete example
; Set background palette to DMG default
ld a, %11100100
ld [$FF47], a
Common misconceptions
- “Sprites are just tiles.” (They have separate attributes and limits.)
- “Window is a second background.” (It is its own layer with separate rules.)
Check-your-understanding questions
- Why is writing to VRAM during Mode 3 unsafe?
- What happens when more than 10 sprites are on a scanline?
- What does LCDC bit 7 do?
Check-your-understanding answers
- PPU is actively reading VRAM; writes cause corruption.
- Extra sprites are dropped based on OAM order.
- It enables or disables the LCD display.
Real-world applications
- Split-screen effects using STAT interrupts.
- Sprite flicker management by OAM ordering.
Where you’ll apply it Projects 3-7, 14-15, 17-18.
References
- Pan Docs: Rendering - https://gbdev.io/pandocs/Rendering.html
- Pan Docs: LCDC - https://gbdev.io/pandocs/LCDC.html
- Pan Docs: STAT - https://gbdev.io/pandocs/STAT.html
- Game Boy Programming Manual (Nintendo) - https://archive.org/details/GameBoyProgManVer1.1
- Game Boy: Complete Technical Reference (gekkio) - https://gbdev.io/gbctr/
Key insight The PPU is a strict, timed machine; graphics are about timing, not pixels.
Summary You do not draw pixels on DMG; you schedule tile data for a rasterizer.
Homework/Exercises
- Decode a single 8x8 tile from 2bpp bytes.
- Draw the order of PPU modes for one frame.
- Identify which LCDC bits affect sprite size.
Solutions
- Combine low/high bitplanes per pixel.
- Mode 2->3->0 x 144 lines, then Mode 1 for 10 lines.
- LCDC bit 2 (sprite size).
Chapter 6: Timing, Interrupts, and the Game Loop
Fundamentals
On DMG, timing is everything. The CPU, PPU, and APU run in lockstep, and the system generates interrupts for VBlank, LCD status, Timer, Serial, and Joypad. A game loop that ignores timing will flicker or crash. You must understand the IE and IF registers, the IME flag, and how to write reliable interrupt service routines (ISRs). The VBlank interrupt is the safest time to update VRAM and OAM, while Timer interrupts can drive fixed-step game logic. Even simple input handling depends on timing because the joypad state can change during a frame. Your loop is also your scheduler; it decides when hardware is touched.
Deep Dive into the concept DMG timing is deterministic and based on CPU cycles. The system divides time into frames; each frame contains 154 scanlines, each scanline is 456 cycles. VBlank occurs after line 143, giving you a safe window to update graphics. The VBlank interrupt is triggered by the PPU when entering Mode 1. The LCD STAT interrupt can be configured to fire on mode transitions or line compare (LYC). This is the mechanism for split-screen effects and precise mid-frame changes.
Timer interrupts are driven by the internal divider (DIV) and timer registers (TIMA/TMA/TAC). The timer can be configured to tick at different frequencies derived from the CPU clock. This is perfect for fixed-step game logic: you can run physics at a constant rate independent of rendering. A classic pattern is to increment a counter in the timer ISR and run logic when it reaches a threshold. The Serial and Joypad interrupts are less common but still useful for synchronous input or link cable features.
Interrupts require careful setup: set the interrupt enable register (IE), clear any pending interrupt flags (IF), install ISRs at vectors, and enable IME (with ei). In an ISR, you must save registers that you modify and restore them before returning with reti. Failure to preserve registers causes hard-to-debug corruption. Timing mistakes are subtle: if your VBlank ISR is too long, you will overlap into drawing time and corrupt VRAM. If your timer ISR is too frequent, it can starve the main loop. In ROM hacking, ISR timing is even riskier because you may be inserting code into a game with strict timing assumptions.
A stable game loop on DMG typically includes: (1) input read, (2) game logic update, (3) wait for VBlank, (4) render updates. This can be done by polling or by using the VBlank ISR to signal the main loop. Polling is easier but wastes cycles; ISR-driven loops are more responsive. Understanding timing also helps you design DMA updates: you can start OAM DMA at the top of VBlank and know exactly how long it will take.
There are also hardware quirks that affect timing, such as the DMG HALT bug. On certain revisions, executing HALT with interrupts disabled can cause the CPU to skip the next instruction fetch. Most homebrew ignores this, but ROM hacks that insert HALT or modify interrupt flow can trigger subtle bugs. The safe pattern is to enable interrupts before using HALT or to avoid HALT entirely in timing-critical code. Another subtlety: IME is not immediate after ei; it is enabled after the next instruction. This matters when you expect an interrupt to fire immediately. If you need precise control, you must insert a no-op or structure your code so the first interrupt arrives after the instruction following ei. These details are not academic; they are the difference between a stable loop and a sporadic crash. When you respect timing, your game becomes predictable and easier to debug. Predictability is what makes long projects possible.
How this fits on projects
- Projects 2-5 introduce VBlank-safe updates.
- Projects 7-10 use STAT and Timer interrupts.
- Projects 14-15 require stable loops for full games.
Definitions & key terms
- VBlank: The vertical blanking period after line 143.
- ISR: Interrupt Service Routine.
- IME: Interrupt Master Enable.
- TIMA/TMA/TAC: Timer registers.
Mental model diagram
Frame loop
[Run logic] -> [Wait VBlank] -> [Update VRAM/OAM] -> repeat
^ |
| v
Timer ISR VBlank ISR
How it works (step-by-step)
- Enable interrupts (IE, IF cleared,
ei). - CPU runs main loop.
- VBlank fires; ISR sets a flag.
- Main loop sees flag and updates VRAM/OAM.
- Timer ISR updates fixed-step counters.
Minimal concrete example
; Enable VBlank interrupt
ld a, %00000001
ld [$FFFF], a
; Enable IME
ei
Common misconceptions
- “Interrupts are optional.” (They are required for safe timing.)
- “ISRs can be long.” (They must be short and predictable.)
Check-your-understanding questions
- Why do you need to clear IF before enabling interrupts?
- What happens if a VBlank ISR runs too long?
- How does TIMA overflow generate an interrupt?
Check-your-understanding answers
- Old pending flags would trigger immediately and unexpectedly.
- It can overlap PPU drawing and corrupt VRAM.
- On overflow, TIMA reloads TMA and sets the timer interrupt flag.
Real-world applications
- Stable frame pacing for action games.
- Mid-frame effects like status bars.
Where you’ll apply it Projects 2-10, 14-15.
References
- Pan Docs: Interrupts - https://gbdev.io/pandocs/Interrupts.html
- Pan Docs: Timer - https://gbdev.io/pandocs/Timer_and_Divider_Registers.html
- Game Boy Programming Manual (Nintendo) - https://archive.org/details/GameBoyProgManVer1.1
Key insight On DMG, time is a resource you must budget.
Summary Interrupts and timing are the glue between CPU and PPU. Master them to build stable games.
Homework/Exercises
- Compute the cycles per frame given 456 cycles per line.
- Design a timer-driven fixed-step loop at 60 Hz.
Solutions
- 456 * 154 = 70224 cycles per frame.
- Configure timer to tick at 4096 Hz and count 68 ticks per step.
Chapter 7: Data Movement, DMA, and Safe VRAM/OAM Updates
Fundamentals
The DMG has strict rules for moving data to VRAM and OAM. You can copy bytes manually, but large sprite updates should use DMA by writing to 0xFF46, which copies 160 bytes from a source page to OAM. DMA blocks CPU access for a short period, so you must schedule it carefully. Safe data movement patterns are essential for stable rendering and for ROM hacks that modify sprites or tilemaps without glitches. Treat VRAM and OAM as shared resources with the PPU, and budget your writes like you would budget CPU time. A single unsafe write can create a frame of corrupted graphics.
Deep Dive into the concept
DMA is the DMG’s fast path for sprite updates. When you write a source page (upper 8 bits of the source address) to 0xFF46, the hardware copies 160 bytes from source << 8 to OAM. During this transfer, CPU access to most memory is blocked. This means you should place your OAM buffer in WRAM (e.g., 0xC000) and update it during the main loop, then trigger DMA during VBlank. The canonical pattern is: maintain an OAM shadow buffer in WRAM, update it freely, then trigger DMA at VBlank. This yields glitch-free sprites and removes per-sprite OAM writes.
VRAM updates are more complex. The PPU locks VRAM during Mode 3, so you must schedule updates during VBlank or HBlank. Many games use a “VRAM update list”: the main loop builds a list of tile/map updates, and the VBlank ISR applies a limited number each frame. This ensures that updates fit within the VBlank window. For DMG, VBlank is only about 1.1 ms; that is enough for a small number of tile updates but not large transfers. For large transfers (new level data), you may turn off the LCD temporarily, load data, then re-enable the LCD. This creates a blank frame but allows full VRAM access. This technique is common in early boot or transitions.
In ROM hacking, DMA knowledge helps you interpret sprite glitches. If you patch a game and see sprites disappear, you may have broken the OAM buffer layout or altered DMA timing. If you add sprites or change their order without adjusting the DMA source, you may exceed 40 sprites or corrupt the buffer. Similarly, tile editing can break if you change tile indices without updating maps. DMA and data movement are not just performance concerns; they define correctness. You should design data formats that are DMA-friendly: contiguous sprite entries, aligned tile data, and tables that can be copied in fixed-size blocks.
The DMG also uses the LDH instruction for high memory access, which is faster and smaller than full 16-bit addressing. In tight loops, prefer ldh for I/O and HRAM. When copying data, consider unrolling or using ldi instructions to copy sequential bytes efficiently. But avoid over-optimization early; correctness and timing windows are more important.
A useful mental model is that VRAM bandwidth is the scarce resource. You get a small chunk of time per frame where writes are safe, and that time is smaller than your instincts suggest. If you need to update many tiles, you either spread the updates across multiple frames or turn off the LCD for a brief load. The DMG does not have the CGB HDMA feature, so you cannot rely on automatic HBlank DMA. You must schedule updates manually. In practice, many engines reserve a small “VRAM budget” per frame (for example 32-64 bytes) and refuse to exceed it. This kind of budgeting is what makes DMG engines stable and prevents the intermittent corruption bugs that haunt beginners. This is intentional.
How this fits on projects
- Projects 4-6 use DMA and VRAM-safe updates.
- Projects 8-10 use update lists to fit into VBlank.
- Projects 14-15 rely on fast sprite updates.
Definitions & key terms
- DMA: Direct Memory Access for OAM.
- OAM shadow: WRAM buffer for sprite data.
- VRAM update list: A queued list of tile/map writes.
Mental model diagram
WRAM OAM Buffer --> [DMA] --> OAM --> Sprites on screen
^
|
Main loop updates
How it works (step-by-step)
- Build OAM data in WRAM.
- Wait for VBlank.
- Write source page to
0xFF46. - DMA copies 160 bytes to OAM.
- PPU uses new sprite data next frame.
Minimal concrete example
; Trigger OAM DMA from 0xC000
ld a, $C0
ld [$FF46], a
Common misconceptions
- “DMA is optional.” (Manual OAM writes are slower and riskier.)
- “I can update VRAM any time if fast.” (Mode 3 still blocks.)
Check-your-understanding questions
- What does the value written to
0xFF46represent? - Why keep a shadow OAM buffer?
- When is it safe to write VRAM?
Check-your-understanding answers
- The high byte of the source address.
- To update sprite data without touching OAM directly.
- VBlank (and sometimes HBlank).
Real-world applications
- Smooth sprite animation without flicker.
- Transition screens with LCD off and bulk VRAM copy.
Where you’ll apply it Projects 4-6, 8-10, 14-15.
References
- Pan Docs: OAM DMA - https://gbdev.io/pandocs/OAM_DMA_Transfer.html
- Pan Docs: VRAM access rules - https://gbdev.io/pandocs/Rendering.html
- Game Boy Programming Manual (Nintendo) - https://archive.org/details/GameBoyProgManVer1.1
Key insight DMA is not just speed; it is the safe path for sprite correctness.
Summary Data movement is scheduled, not random. Use DMA and update lists for stability.
Homework/Exercises
- Design a 40-sprite OAM buffer layout.
- Compute how many bytes you can copy during one VBlank.
Solutions
- 40 entries x 4 bytes = 160 bytes contiguous in WRAM.
- Roughly a few hundred bytes; use a small update list per frame.
Chapter 8: Audio (APU) Fundamentals
Fundamentals The DMG APU has four channels: two square waves, one wave channel, and one noise channel. Each channel is controlled by memory-mapped registers for frequency, envelope, length, and duty cycle. Audio programming is about writing the right values at the right time. A simple sound effect can be produced by enabling a channel, setting its envelope, and triggering it. Because audio is fully deterministic, you can build tiny data tables that describe sounds and then play them with a short routine, which is ideal for assembly-only games. Small, repeatable sound routines also make debugging much easier. Even a simple beep teaches you register sequencing.
Deep Dive into the concept The notes you hear from DMG games are not recorded audio; they are synthesized in real time. Channels 1 and 2 generate square waves with selectable duty cycles. Channel 1 has a frequency sweep unit, which can produce characteristic “laser” sounds. Channel 3 is a programmable 4-bit wave channel: you load a 32-sample waveform into wave RAM and play it back at varying frequencies. Channel 4 generates noise using an LFSR (linear-feedback shift register), ideal for percussion and explosions. The mixer and volume controls determine how channels are combined and routed to left/right outputs.
Audio timing is critical. The APU frame sequencer updates envelopes, length counters, and sweeps at fixed intervals. If you update registers in the wrong order, you may get silent channels or clicks. For example, you must enable the APU (NR52) before using any channel, and you should write frequency registers in the correct sequence. In many engines, sound effects are scheduled via a sound queue that updates registers during VBlank or a timer interrupt. This avoids conflicting writes from multiple game systems.
In ROM hacking, audio is often data-driven. Games store note tables, instrument definitions, and song patterns in ROM. Hacking audio requires identifying these tables and understanding the game’s engine. This is an advanced skill but achievable: search for repetitive patterns, look for commands that match known music engine formats, or use a debugger to watch writes to sound registers and backtrack to their data sources.
There are important register details that make or break sound. The frequency registers (for channels 1-3) are split across low and high bytes, and the high byte also contains the trigger bit. If you write only the low byte, you will not retrigger the sound. If you write the high byte too early, you might reset the channel before updating frequency. The wave channel requires you to enable it, load wave RAM, and then trigger playback. Some games disable the wave channel before writing to wave RAM, then re-enable it to avoid glitches. Mixer control is split across NR50 (master volume) and NR51 (routing to left/right). If you forget to set NR51, you may hear nothing even though the channel is active. These details are why “no sound” bugs are so common.
The APU also includes length counters and envelopes that update on a fixed frame sequencer. If you set a very short length, your sound may cut off immediately. If you use an envelope with a negative sweep and a low starting volume, the sound might decay to silence before you can hear it. In practice, create a small set of test sounds and verify them in isolation before integrating them into a game. For ROM hacking, you often need to recognize the music engine’s command stream. A common pattern is a sequence of note/duration bytes and instrument references; you can detect these by watching which ROM addresses are read before writes to APU registers. This skill scales to full music engines.
How this fits on projects
- Projects 9-10 implement sound effects and simple music.
- Projects 14-15 use audio in full games.
- Projects 19-20 may modify sound behavior in ROM hacks.
Definitions & key terms
- APU: Audio Processing Unit.
- Envelope: Automatic volume decay/increase.
- Wave RAM: 32-sample waveform storage for channel 3.
Mental model diagram
Square 1 Square 2 Wave Noise
| | | |
+----------+---------+-------+--> Mixer --> Output
How it works (step-by-step)
- Enable APU (NR52).
- Configure channel registers (NR1x/NR2x/NR3x/NR4x).
- Trigger channel by writing to the high register.
- Audio plays and envelope updates.
Minimal concrete example
; Enable sound
ld a, $80
ld [$FF26], a
Common misconceptions
- “Audio is too complex for DMG.” (It is register-driven and manageable.)
- “Wave channel is optional.” (It gives unique timbres not possible with square waves.)
Check-your-understanding questions
- Which channel provides noise?
- What does the envelope control?
- Why must NR52 be enabled first?
Check-your-understanding answers
- Channel 4.
- Automatic volume change over time.
- It powers the APU; other registers are ignored otherwise.
Real-world applications
- Procedural sound effects.
- Simple music trackers for DMG.
Where you’ll apply it Projects 9-10, 14-15.
References
- Pan Docs: Audio - https://gbdev.io/pandocs/Audio.html
- Game Boy Programming Manual (Nintendo) - https://archive.org/details/GameBoyProgManVer1.1
Key insight DMG audio is a small synth; learn the registers and you can compose sound.
Summary The APU is deterministic and register-driven. Treat it like a tiny instrument.
Homework/Exercises
- Define a simple 4-step envelope and predict volume changes.
- Sketch a 32-sample waveform for the wave channel.
Solutions
- Start volume 8, decrease every step, reach 0 after 8 steps.
- Use a simple sine-like or saw shape in 4-bit values.
Chapter 9: Asset and Data Pipeline (2bpp, Maps, Compression)
Fundamentals All DMG graphics are 2 bits per pixel (2bpp). That means four colors, and tile data is packed into two bitplanes per row. Tile maps are arrays of tile indices. If you want to create or modify graphics, you need a deterministic pipeline from source art to 2bpp tiles, then to a tile map. For ROM hacking, you must extract and reinsert tiles without changing their alignment or size. Treat assets as data structures, not images; a small change in a tile index can change a whole level layout. A clean pipeline prevents accidental map breakage. Your pipeline is also your documentation.
Deep Dive into the concept
The tile format defines how art becomes bytes. Each 8x8 tile is 16 bytes: two bytes per row. The first byte of a row stores the low bits of the eight pixels; the second byte stores the high bits. This encoding is simple but non-intuitive, which is why tools like rgbgfx or tile editors are essential. A typical pipeline is: draw art in a limited palette, export as PNG, convert to 2bpp using rgbgfx, and then include the resulting binary in your ROM with INCBIN. Tile maps can be built using map editors (like GBMB) or generated procedurally. The key is consistent tile ordering: if the tile order changes, the map indices become wrong.
Compression is common in commercial DMG games to save ROM space. Many games use simple RLE (run-length encoding) or custom LZ-like formats. If you are building your own game, you may choose to avoid compression at first and only introduce it when you run out of ROM. If you are hacking a commercial ROM, you must understand whether tiles or maps are compressed before you can edit them. The process is: locate a compressed block, determine its format by pattern analysis or debugger tracing, write a decompressor (or reuse existing tools), then recompress your modified data to fit the same size or expand into free space with new pointers.
In assembly-only games, data tables are powerful. You can store animation frames as tile index lists or pointer tables. This lets you animate without rewriting tiles every frame. For example, a player sprite might have 4 frames, each a set of tile indices; your animation system just updates OAM tile numbers. This is more efficient than rewriting tile graphics. For ROM hacking, understanding these tables is essential: a change in one table entry can re-skin a sprite or swap frames.
Pipeline discipline is what keeps large projects sane. You should treat tilesets and maps as versioned assets, not ad-hoc binary blobs. Keep a source image, a conversion step, and a deterministic output so you can regenerate assets when needed. When you change a tileset, re-run conversion and confirm that tile indices remain consistent; otherwise, every map that references those indices will break. In ROM hacks, the problem is magnified because you cannot easily rebuild everything: you must preserve existing indices or update every map that references them. Many commercial games also compress map data to save ROM space, so changing a single tile index might require a complete recompress. The practical approach is to first learn how the game stores its data (raw or compressed), then decide whether to work within those constraints or relocate the data and patch pointers. This is why the asset pipeline chapter matters: it teaches you to think in terms of data contracts, not just pixels. The same discipline applies to text tables and pointer lists, which are just another form of data pipeline. It is worth documenting every conversion step. Consistency beats speed in asset work.
How this fits on projects
- Projects 3-7 require tile and map pipelines.
- Projects 11-12 rely on data tables and animation.
- Projects 17-18 perform graphics and text edits in ROMs.
Definitions & key terms
- 2bpp: Two bits per pixel tile format.
- Tile map: Grid of tile indices.
- INCBIN: RGBDS directive to include binary data.
Mental model diagram
PNG art -> rgbgfx -> tiles.bin -> INCBIN -> VRAM tiles -> tile map -> screen
How it works (step-by-step)
- Create pixel art with 4-color palette.
- Convert to 2bpp tile data.
- Build tile map with tile indices.
- Load tiles into VRAM and map into BG.
Minimal concrete example
SECTION "Tiles", ROM0
MyTiles:
INCBIN "assets/player.2bpp"
Common misconceptions
- “Tiles are stored as pixels.” (They are stored as bitplanes.)
- “Tile maps store pixels.” (They store indices only.)
Check-your-understanding questions
- How many bytes are in one 8x8 tile?
- Why does tile order matter?
- What does
INCBINdo?
Check-your-understanding answers
- 16 bytes.
- Maps reference tile indices; order changes meaning.
- It includes raw binary data at assembly time.
Real-world applications
- Building a reusable sprite library.
- Replacing graphics in a ROM hack.
Where you’ll apply it Projects 3-7, 11-12, 17-18.
References
- Pan Docs: Tile data format - https://gbdev.io/pandocs/Tile_Data.html
- RGBDS
rgbgfxdocumentation - https://rgbds.gbdev.io/docs/ - 2bpp graphics guide - https://www.huderlem.com/demos/gameboy2bpp.html
Key insight Graphics are data tables. Once you control the tables, you control the art.
Summary A disciplined asset pipeline prevents broken maps and corrupted sprites.
Homework/Exercises
- Manually encode one 8x8 tile into 2bpp bytes.
- Create a 2-tile map and render it.
Solutions
- Use two bitplanes and compute each byte by pixel bits.
- Place two indices in a map and load them into BG map.
Chapter 10: Debugging, Testing, and ROM Hacking Workflow
Fundamentals Debugging on DMG is about visibility. You need an emulator with a good debugger, symbol support, and the ability to inspect VRAM/OAM. You also need deterministic builds and a workflow for ROM hacking: identify a target ROM, verify legality clue (you own it), back it up, analyze its data and code, make minimal changes, test in emulator, and package a patch (IPS/BPS) instead of distributing the ROM itself. This chapter turns hacking into a disciplined engineering task, not guesswork. A careful workflow prevents you from corrupting ROMs and saves you hours when you need to retrace a change.
Deep Dive into the concept The most powerful tool in DMG development is not the assembler; it is the debugger. Emulators like BGB and SameBoy allow breakpoints, memory watches, register views, and tilemap inspectors. With symbols from an RGBDS map file, you can break on label names, step through code, and inspect state at the exact cycle where a bug occurs. This is essential because many DMG bugs are timing-related: a sprite flickers only when OAM updates overlap a scanline, or a tile corrupts because VRAM is written during Mode 3. A debugger lets you see the LCDC/STAT state and confirm that your updates happen in VBlank.
Testing also means using known-good ROMs. For emulation or ROM hacking, you can use established test ROM suites (like Blargg’s tests) to verify CPU and hardware behavior. Even if you are not writing an emulator, test ROMs help you validate that your assumptions about timing and registers are correct. In your own game, you should create debug modes that render internal values (player coordinates, bank numbers, frame counters) on the screen or output to a serial log. This accelerates iteration.
ROM hacking is a structured process. Step 1: create a clean dump of a ROM you legally own and compute its hash (CRC/MD5). Step 2: inspect the header to identify cartridge type and ROM size. Step 3: locate assets using known patterns (tile graphics, text tables, or level data) by searching for repeating bytes or by tracing code in a debugger. Step 4: extract the asset, modify it with a controlled toolchain, and reinsert it at the same size or in free space. Step 5: patch the ROM and update checksums if necessary. Step 6: test thoroughly in an emulator and optionally on real hardware. Step 7: distribute only the patch (IPS/BPS/xdelta) and document the expected base ROM hash. This is both ethical and legally safer because you are not redistributing copyrighted ROMs.
You must also respect DMG-specific behaviors when hacking. Many ROMs rely on hardware quirks such as the HALT bug or undefined register power-on values. A patch that changes timing or assumes a clean state may fail on real hardware even if it works in an emulator. This is why you must test on multiple emulators and, when possible, real DMG hardware. It is also why you should prefer minimal patches: change as little as possible, keep instruction lengths intact, and avoid altering interrupt timing unless you understand the consequences.
Finally, treat debugging as a first-class feature. Add a debug build mode to your own games that prints internal counters or bank numbers to the screen. For ROM hacks, build a notebook of memory addresses and observed behaviors as you trace code. These notes become your informal “symbols” and save hours later. If you use a disassembler, label routines and data blocks as you identify them; this gradually converts an opaque ROM into an understandable codebase. That process is slow, but it is how real ROM hacks become stable and maintainable.
How this fits on projects
- Projects 1-15 use debugger-driven development.
- Projects 16-20 formalize a ROM hacking pipeline.
Definitions & key terms
- Breakpoint: Pauses execution at a target address.
- Watchpoint: Stops when a memory address changes.
- Patch: A diff file that modifies a ROM without distributing it.
Mental model diagram
ROM (owned) -> Analyze -> Modify -> Test -> Create Patch -> Share Patch
How it works (step-by-step)
- Build ROM with symbols and map file.
- Debug with breakpoints and memory watches.
- Identify data/code sections.
- Patch with minimal changes.
- Validate with emulator/hardware tests.
Minimal concrete example
; Example of a patch-friendly jump
jp NewRoutine
Common misconceptions
- “If it works in one emulator, it’s done.” (Test in multiple.)
- “Patches can ignore checksums.” (Some hardware checks headers.)
Check-your-understanding questions
- Why use a patch instead of sharing a ROM?
- What is a watchpoint used for?
- Why should patches be minimal?
Check-your-understanding answers
- Legal and ethical reasons; patches contain no copyrighted data.
- To stop when a memory address changes.
- To avoid unintended timing or size changes.
Real-world applications
- Translation patches and fan mods.
- Bug fixes and quality-of-life improvements.
Where you’ll apply it Projects 16-20.
References
- BGB debugger - https://bgb.bircd.org/
- SameBoy debugger - https://sameboy.github.io/
- Pan Docs - https://gbdev.io/pandocs/
- BPS/IPS patching overview - https://www.marcrobledo.com/RomPatcher.js/
- xdelta project - https://github.com/jmacd/xdelta-gpl
Key insight ROM hacking is disciplined reverse engineering, not random byte edits.
Summary Debugging and hacking are about visibility and restraint. Use tools, respect constraints, and share patches responsibly.
Homework/Exercises
- Set a breakpoint on a known address and step through a frame.
- Create a tiny patch that changes a single byte and verify it.
Solutions
- Use your emulator’s breakpoint list and step to observe registers.
- Use an IPS/BPS tool and confirm the ROM hash changes.
Glossary (High-Signal)
- DMG: The original Game Boy hardware (DMG-01).
- SM83: The DMG CPU (LR35902).
- PPU: Picture Processing Unit (graphics).
- APU: Audio Processing Unit.
- LCDC/STAT: LCD control/status registers.
- VBlank/HBlank: Safe display periods for updates.
- OAM: Sprite attribute memory.
- VRAM: Video RAM (tiles/maps).
- MBC: Memory Bank Controller.
- ROMX: Switchable ROM bank.
- ISR: Interrupt Service Routine.
- 2bpp: Two bits per pixel graphics format.
- IPS/BPS: Patch formats for ROM differences.
Why Game Boy DMG Assembly Matters
Modern motivation and real-world use cases
- The Game Boy family sold 118.69 million units worldwide (Nintendo consolidated sales reported as of 2024), making it one of the most successful hardware platforms ever. This scale means your DMG knowledge maps to a huge library and a vibrant homebrew community.
- DMG development is a masterclass in performance and memory discipline: 4 MHz CPU, tiny RAM, strict timing. These constraints build skills that transfer to embedded systems, optimization work, and low-level debugging.
- ROM hacking remains one of the most direct ways to learn reverse engineering: you can observe how real commercial games are structured and learn from their optimizations.
Context & Evolution (optional history) The DMG released in 1989 with a simple monochrome display but robust battery life. Its success created a massive ecosystem of games and hardware variants. While later models (CGB, GBA) added features, the DMG remains the purest form of the platform: a fixed, well-documented, timing-sensitive system that rewards deep understanding.
Comparison diagram
High-level Engine DMG Assembly
+----------------------+ +----------------------+
| Engine handles input | | You read JOYP reg |
| Engine draws sprites | | You write OAM/VRAM |
| Engine mixes audio | | You program APU regs |
+----------------------+ +----------------------+
Result: fast iteration Result: deep mastery
Sources for stats: Nintendo consolidated sales figures (as cited in public Nintendo IR summaries and Wikipedia’s Game Boy sales table).
Concept Summary Table
| Concept | Why It Matters | Where It Shows Up |
|---|---|---|
| DMG Memory Map | The hardware is the API | Projects 1-7, 12-13, 16-20 |
| SM83 CPU & Flags | Correct logic and patch safety | Projects 1-20 |
| ROM Header & Banking | Booting and large games | Projects 1-3, 12-13, 16-20 |
| RGBDS Toolchain | Deterministic builds, symbols | Projects 1-3, 16-20 |
| PPU Graphics Pipeline | Every visual on screen | Projects 3-7, 14-15, 17-18 |
| Timing & Interrupts | Stable frames and input | Projects 2-10, 14-15 |
| DMA & Data Movement | Safe sprite updates | Projects 4-6, 8-10 |
| APU Audio | Sound effects and music | Projects 9-10, 14-15 |
| Asset/Data Pipeline | Reliable art + maps | Projects 3-7, 11-12, 17-18 |
| Debugging & ROM Hacking | Safe reverse engineering | Projects 16-20 |
Project-to-Concept Map
| Project | Key Concepts |
|---|---|
| 1. Toolchain + Bootable ROM | Toolchain, Header, Memory Map |
| 2. LCD Init + Background Text | Memory Map, PPU, Timing |
| 3. Joypad + Sprite Mover | CPU, PPU, Timing |
| 4. OAM DMA Sprite System | DMA, OAM, Timing |
| 5. Tilemap Engine | PPU, Asset Pipeline |
| 6. Scrolling + Collision | PPU, Timing, Data Tables |
| 7. Window HUD via STAT | PPU, Interrupts |
| 8. Timer-Driven Loop | Timing, CPU |
| 9. Sound Effects | APU |
| 10. Music Playback | APU, Data Tables |
| 11. Animation System | Asset Pipeline, DMA |
| 12. Save/Load with RAM | MBC, Memory Map |
| 13. Banked Code/Data | MBC, Toolchain |
| 14. Full Game: Breakout | All core systems |
| 15. Full Game: Adventure | All core systems |
| 16. ROM Analysis + Header | Header, Debugging |
| 17. Graphics ROM Hack | Asset Pipeline, PPU |
| 18. Text/Script Hack | Data Tables, ROM Layout |
| 19. Behavior Patch | CPU, Debugging, Banking |
| 20. Patch Packaging | ROM Hacking Workflow |
Deep Dive Reading by Concept
| Concept | Primary References | Why This Matters |
|---|---|---|
| DMG Memory Map | Pan Docs (Memory Map) | Official ranges and hardware behavior |
| SM83 CPU | Pan Docs CPU Instruction Set; “Low-Level Programming” (Zhirkov) Ch. 1-3 | Flags, opcodes, instruction timing |
| ROM Header & Banking | Pan Docs Cartridge Header + MBCs | Correct booting and bank switching |
| RGBDS Toolchain | RGBDS documentation | Deterministic builds and symbols |
| PPU Pipeline | Pan Docs Rendering + LCDC/STAT | Tile-based rendering details |
| Timing & Interrupts | Pan Docs Interrupts + Timer | VBlank and timer correctness |
| DMA & Data Movement | Pan Docs OAM DMA | Safe sprite updates |
| APU Audio | Pan Docs Audio | Register-level audio control |
| Asset Pipeline | 2bpp guide (gbdev) | Correct tile encoding |
| ROM Hacking Workflow | Data Crystal “Introduction to ROM Hacking” | Structured reverse engineering |
Quick Start (First 48 Hours)
Day 1
- Install RGBDS and a debugger-capable emulator.
- Build and run Project 1 (bootable ROM).
- Read Chapter 1 (memory map) and Chapter 4 (toolchain).
Day 2
- Complete Project 2 (LCD init + background text).
- Step through code in the debugger and inspect VRAM.
- Read Chapter 5 (PPU) and Chapter 6 (timing/interrupts).
Recommended Learning Paths
- Pure Assembly Builder: Projects 1-15 in order.
- ROM Hacker Focus: Projects 1-4, then 16-20, then return to 5-15.
- Audio Specialist: Projects 1-8, then 9-10, then 14.
- Graphics Specialist: Projects 1-7, 11, 14, 17-18.
Success Metrics
- You can explain the DMG memory map without notes.
- You can build a ROM that boots on real DMG hardware.
- You can update sprites without flicker using DMA.
- You can identify and modify graphics or text in an owned ROM.
- You can ship a patch (IPS/BPS/xdelta) with correct documentation.
Optional Appendices
Appendix A: Key DMG Registers (Quick Reference)
0xFF40LCDC (LCD control)0xFF41STAT (LCD status)0xFF42SCY (scroll Y)0xFF43SCX (scroll X)0xFF44LY (current scanline)0xFF45LYC (line compare)0xFF47BGP (background palette)0xFF46DMA (OAM DMA)0xFF00JOYP (joypad)0xFF04-0xFF07DIV/TIMA/TMA/TAC (timer)0xFFFFIE (interrupt enable)0xFF0FIF (interrupt flags)
Appendix B: RGBDS Command Cheatsheet
rgbasm -o file.o file.asmrgblink -o game.gb -m game.map file.orgbfix -v -p 0 game.gbrgbgfx -o tiles.2bpp tiles.png
Appendix C: ROM Hacking Safety Checklist
- Work from legally owned ROMs and keep originals unchanged.
- Record ROM hash before patching.
- Make minimal edits; avoid expanding data unless you control pointers.
- Distribute patches only, never ROMs.
Projects (20 Total)
Project 1: Toolchain + Bootable ROM (DMG Only)
- Main Programming Language: Game Boy Assembly (RGBDS)
- Difficulty: Beginner
- Time estimate: 1-2 days
- Core topics: ROM header, entry point, toolchain
What you’ll build: A minimal .gb ROM that boots and loops forever on DMG, with a valid header and checksum.
Why it teaches DMG development: Without a correct header and entry point, nothing else matters. This project forces you to understand the ROM format and toolchain.
Core challenges you’ll face:
- Installing RGBDS and building a pipeline.
- Writing the correct header and entry point.
- Verifying boot in a debugger.
Real World Outcome
You will run:
$ rgbasm -o build/main.o src/main.asm
$ rgblink -o build/boot.gb -m build/boot.map build/main.o
$ rgbfix -v -p 0 build/boot.gb
Then in your emulator you see the Nintendo logo scroll and a blank screen that stays stable (no crash, no garbage). The debugger shows PC looping in your main loop at 0x0150.
The Core Question You’re Answering
How do I create a DMG ROM that boots reliably on real hardware?
Concepts You Must Understand First
- ROM header layout (Pan Docs)
- RGBDS build pipeline
- Entry point and reset vector behavior
Questions to Guide Your Design
- Where is the entry point after the boot ROM finishes?
- What minimum header fields must be correct?
- How do you verify checksums after editing bytes?
Thinking Exercise
Draw the header region from 0x0100 to 0x014F and mark where each field lives.
The Interview Questions They’ll Ask
- What is the purpose of the Game Boy header?
- How does
rgbfixaffect a ROM? - Why does an invalid logo prevent boot?
- What is the difference between ROM0 and ROMX?
Hints in Layers
- Hint 1: Start with a single
jpto your main loop at0x0150. - Hint 2: Use a
SECTIONto place code at0x0100. - Hint 3: Always run
rgbfixafter linking. - Hint 4: Use the emulator’s ROM info panel to confirm header fields.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Low-level execution | Low-Level Programming (Zhirkov) | Ch. 1-2 | | Assembly fundamentals | The Art of Assembly Language | Ch. 1-3 |
Common Pitfalls & Debugging
Problem: ROM boots to white screen and resets
- Why: Header checksum or logo is invalid.
- Fix: Run
rgbfixwith-vand ensure header bytes are correct. - Quick test: Emulator ROM info shows valid checksums.
Definition of Done
- ROM boots in at least two emulators.
- Header checksum is valid.
- PC loops at a known label.
Project 2: LCD Init + Background Text
- Main Programming Language: Game Boy Assembly (RGBDS)
- Difficulty: Beginner
- Time estimate: 2-3 days
- Core topics: LCDC, VRAM, tile maps
What you’ll build: A ROM that initializes the LCD, loads a tile set into VRAM, and displays a simple text message on the background.
Why it teaches: This is your first real interaction with the PPU and VRAM rules.
Real World Outcome
You will see a clean “HELLO DMG” message centered on the screen. The background remains stable, no flicker. In the debugger, VRAM shows your tile data at 0x8000 and the BG map at 0x9800.
The Core Question You’re Answering
How do I safely load tiles and maps into VRAM and display them?
Concepts You Must Understand First
- VRAM address ranges
- LCDC enable/disable
- Tile map basics
Questions to Guide Your Design
- When is it safe to write to VRAM?
- Which tile map base is active?
- How will you generate your tile data?
Thinking Exercise
Sketch the 32x32 tile map and mark where your text will appear.
The Interview Questions They’ll Ask
- Why can you not write VRAM during Mode 3?
- What does LCDC bit 4 control?
- How do tile maps reference tile data?
- How can you update text without redrawing tiles?
Hints in Layers
- Hint 1: Disable LCD before loading tiles for simplicity.
- Hint 2: Use
INCBINto load a prebuilt 2bpp font. - Hint 3: Write tile indices into the BG map.
- Hint 4: Re-enable LCD and wait for VBlank.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Computer architecture | Code (Petzold) | Ch. 12-13 | | Assembly fundamentals | Learn to Program with Assembly | Ch. 2-4 |
Common Pitfalls & Debugging
Problem: Screen stays blank
- Why: LCDC not enabled or tile map base incorrect.
- Fix: Set LCDC bit 7 and choose correct map.
- Quick test: Check
0xFF40in debugger.
Definition of Done
- LCDC enabled and stable.
- Tiles loaded into VRAM.
- Message appears without flicker.
Project 3: Joypad Input + Sprite Mover
- Difficulty: Beginner-Intermediate
- Time estimate: 3-4 days
What you’ll build: A movable sprite controlled by the D-pad, updated during VBlank.
Why it teaches: You will read the joypad register, manipulate OAM, and integrate input into the main loop.
Real World Outcome
You see a single sprite on screen that moves smoothly with the D-pad. The sprite never tears, and its position matches your input. The debugger shows joypad values changing at 0xFF00.
The Core Question You’re Answering
How do I read input and update sprite position without corrupting OAM?
Concepts You Must Understand First
- Joypad register usage
- OAM layout
- VBlank timing
Questions to Guide Your Design
- How will you debounce or handle held input?
- How do you translate input into coordinates?
- Will you write OAM directly or use a buffer?
Thinking Exercise
Draw the 4-byte OAM entry for your sprite and label each byte.
The Interview Questions They’ll Ask
- How does the joypad register work?
- Why is OAM DMA recommended?
- What is the OAM coordinate offset?
- How do you prevent flicker?
Hints in Layers
- Hint 1: Select D-pad bits by writing to JOYP.
- Hint 2: Use a WRAM variable for X and Y.
- Hint 3: Update OAM during VBlank.
- Hint 4: Use a shadow OAM buffer.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 4 | | Assembly fundamentals | The Art of Assembly Language | Ch. 4 |
Common Pitfalls & Debugging
Problem: Input seems inverted
- Why: JOYP bits are active low.
- Fix: Invert or mask correctly.
- Quick test: Print JOYP values in debugger.
Definition of Done
- Sprite moves in all four directions.
- No flicker or corrupted tiles.
- Input reads correctly each frame.
Project 4: OAM DMA Sprite System
- Difficulty: Intermediate
- Time estimate: 4-6 days
What you’ll build: A sprite system using a shadow OAM buffer and DMA transfer each frame.
Why it teaches: This is the canonical DMG sprite pipeline used in real games.
Real World Outcome
You can move multiple sprites smoothly. In the debugger, you see your WRAM OAM buffer updated, then a DMA transfer during VBlank. The screen shows no flicker even with 8-10 sprites.
The Core Question You’re Answering
How do I update all sprites safely and efficiently every frame?
Concepts You Must Understand First
- DMA behavior and timing
- OAM structure
- VBlank scheduling
Questions to Guide Your Design
- Where will your OAM shadow live in RAM?
- How will you update sprite data each frame?
- When will you trigger DMA?
Thinking Exercise
Calculate how many cycles DMA takes and how much VBlank time remains.
The Interview Questions They’ll Ask
- Why is DMA preferred over manual OAM writes?
- What happens to the CPU during DMA?
- How do you avoid sprite dropouts?
Hints in Layers
- Hint 1: Keep shadow OAM at
0xC000. - Hint 2: Update it in the main loop.
- Hint 3: Write
$C0to0xFF46during VBlank. - Hint 4: Ensure LCD is on when DMA runs.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Systems understanding | Code (Petzold) | Ch. 14 | | Assembly | Learn to Program with Assembly | Ch. 5 |
Common Pitfalls & Debugging
Problem: Sprites vanish randomly
- Why: DMA source points to wrong page.
- Fix: Ensure source address is page-aligned.
- Quick test: Inspect DMA source in debugger.
Definition of Done
- Shadow OAM buffer in WRAM.
- DMA triggered each frame.
- Multiple sprites update smoothly.
Project 5: Tilemap Engine
- Difficulty: Intermediate
- Time estimate: 5-7 days
What you’ll build: A background tilemap system that can draw a small level with walls and floors.
Why it teaches: You will build the core of any DMG level renderer and manage tile indices.
Real World Outcome
A static map appears, with clear walls and floors. Changing the map array changes the level layout. You can verify tile indices in VRAM.
The Core Question You’re Answering
How do I load and render a tile-based level using the DMG background system?
Concepts You Must Understand First
- Tile data and maps
- VRAM write timing
- Tile index mapping
Questions to Guide Your Design
- How will you store the map in ROM?
- Will you load all tiles at once or stream?
- How will you map tile IDs to tile graphics?
Thinking Exercise
Design a 10x10 room using tile indices and translate it to byte values.
The Interview Questions They’ll Ask
- What is a tile map?
- How do you scroll a tile map?
- How does tile data differ from tile map data?
Hints in Layers
- Hint 1: Use
INCBINfor the map data. - Hint 2: Copy map data into
0x9800. - Hint 3: Load tiles into
0x8000. - Hint 4: Ensure LCDC selects the correct map base.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Data representation | Code (Petzold) | Ch. 16 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 3-4 |
Common Pitfalls & Debugging
Problem: Tiles appear scrambled
- Why: Tile indices do not match tile order.
- Fix: Verify tile order in the tileset.
- Quick test: Render a map of sequential tile indices.
Definition of Done
- Map data renders correctly.
- Tile indices map to intended graphics.
- No flicker or corruption.
Project 6: Scrolling + Collision Map
- Difficulty: Intermediate-Advanced
- Time estimate: 1-2 weeks
What you’ll build: A scrolling map with a simple collision system that stops the player at walls.
Why it teaches: Scrolling, camera control, and collision are core to real games.
Real World Outcome
The background scrolls as the player moves. When the player hits a wall tile, movement stops. The camera stays centered and the map wraps or clamps correctly.
The Core Question You’re Answering
How do I scroll a tilemap and detect collisions using tile data?
Concepts You Must Understand First
- SCX/SCY registers
- Tilemap indexing
- Collision maps
Questions to Guide Your Design
- Will you store collision data separate from graphics?
- How will you convert player position to tile coordinates?
- How will you handle map boundaries?
Thinking Exercise
Given a player X/Y, compute the tile coordinates under the feet.
The Interview Questions They’ll Ask
- How does tile-based collision work?
- What is the cost of per-frame collision checks?
- How do you keep scroll and player position in sync?
Hints in Layers
- Hint 1: Use SCX/SCY for camera.
- Hint 2: Store collision flags in a parallel array.
- Hint 3: Use bit masks for tile types.
- Hint 4: Clamp scroll at map edges.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Algorithms | Algorithms in C (Sedgewick) | Ch. 1 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 5 |
Common Pitfalls & Debugging
Problem: Collision is off by one tile
- Why: Tile coordinate conversion is incorrect.
- Fix: Use integer division by 8 and adjust for sprite offsets.
- Quick test: Print tile coords on screen.
Definition of Done
- Smooth scrolling without tearing.
- Collision stops player correctly.
- Camera stays within bounds.
Project 7: Window HUD via STAT Interrupt
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A fixed HUD at the top using the window layer triggered by a STAT interrupt.
Why it teaches: This introduces mid-frame timing and interrupt-driven rendering tricks.
Real World Outcome
You see a fixed status bar at the top while the background scrolls beneath. The HUD never moves. In the debugger, STAT interrupts fire at a chosen scanline.
The Core Question You’re Answering
How do I use STAT interrupts to change PPU behavior mid-frame?
Concepts You Must Understand First
- STAT register and LYC
- Scanline timing
- Window layer behavior
Questions to Guide Your Design
- Which scanline should trigger the HUD?
- How will you switch scroll values mid-frame?
- How will you keep ISR short?
Thinking Exercise
Calculate the scanline number where you want the HUD to begin.
The Interview Questions They’ll Ask
- What is LYC compare?
- Why are STAT interrupts useful?
- What happens if ISR is too long?
Hints in Layers
- Hint 1: Enable STAT interrupt on LYC.
- Hint 2: Set LYC to the line where HUD starts.
- Hint 3: In ISR, adjust SCY/SCX or enable window.
- Hint 4: Re-arm the interrupt for the next frame.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 7 | | Systems thinking | Code (Petzold) | Ch. 17 |
Common Pitfalls & Debugging
Problem: HUD flickers or shifts
- Why: ISR timing is too late or too long.
- Fix: Keep ISR minimal and tune LYC.
- Quick test: Watch STAT mode changes in debugger.
Definition of Done
- HUD remains fixed.
- Background scrolls independently.
- STAT ISR is stable and short.
Project 8: Timer-Driven Fixed-Step Loop
- Difficulty: Advanced
- Time estimate: 1 week
What you’ll build: A fixed-step update loop driven by TIMA interrupts, decoupling logic from rendering.
Why it teaches: Real games need stable physics and predictable timing.
Real World Outcome
Game logic updates at a fixed rate (e.g., 60 Hz) even if rendering is slower. You can toggle a debug counter to see the timer ticks.
The Core Question You’re Answering
How do I use the DMG timer to drive deterministic game logic?
Concepts You Must Understand First
- Timer registers (DIV/TIMA/TMA/TAC)
- ISR design
- Fixed-step game loops
Questions to Guide Your Design
- What timer frequency is appropriate?
- How will you count ticks into frames?
- How will you avoid missing ticks?
Thinking Exercise
Design a timer setup that produces exactly 60 logic updates per second.
The Interview Questions They’ll Ask
- Why use fixed-step logic?
- How do you prevent ISR jitter?
- What happens if the timer ISR runs too long?
Hints in Layers
- Hint 1: Use TAC to select 4096 Hz.
- Hint 2: Count ticks and run logic every N ticks.
- Hint 3: Keep ISR short, set a flag.
- Hint 4: Use VBlank only for rendering.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Systems programming | Computer Systems: A Programmer’s Perspective | Ch. 2 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 6 |
Common Pitfalls & Debugging
Problem: Logic speed varies
- Why: Timer frequency misconfigured.
- Fix: Verify TAC bits and count intervals.
- Quick test: Toggle a pixel every tick and measure.
Definition of Done
- Logic updates at stable rate.
- Rendering tied to VBlank.
- No missed or double ticks.
Project 9: Sound Effects Engine
- Difficulty: Advanced
- Time estimate: 1 week
What you’ll build: A basic sound effect engine using channels 1-4.
Why it teaches: You learn to control the APU directly and manage envelopes.
Real World Outcome
Pressing buttons triggers different sound effects (beep, noise burst, sweep). The sound is consistent and does not cut off other channels unexpectedly.
The Core Question You’re Answering
How do I program the APU to play reliable sound effects?
Concepts You Must Understand First
- APU channel registers
- Envelope and length
- Mixer and volume
Questions to Guide Your Design
- Which channel is best for each effect?
- How will you avoid channel conflicts?
- When will you trigger the sound?
Thinking Exercise
Design a short “jump” sound using channel 1 sweep.
The Interview Questions They’ll Ask
- What does NR52 do?
- How does the envelope affect volume?
- Why is wave RAM special?
Hints in Layers
- Hint 1: Enable APU before any channel writes.
- Hint 2: Use channel 4 for noise effects.
- Hint 3: Trigger by writing to the high register.
- Hint 4: Store SFX presets in tables.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Digital audio | Computer Systems: A Programmer’s Perspective | Ch. 3 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 8 |
Common Pitfalls & Debugging
Problem: No sound output
- Why: NR52 not enabled or volume zero.
- Fix: Set NR52 and NR50/NR51.
- Quick test: Trigger a square wave and check registers.
Definition of Done
- At least 3 distinct sound effects.
- No channel conflict.
- Audio stable across frames.
Project 10: Simple Music Playback
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A minimal music player that plays a short looped melody.
Why it teaches: You will manage timed audio updates and data tables.
Real World Outcome
A short melody plays in the background while you move a sprite. The music loops seamlessly without stuttering.
The Core Question You’re Answering
How can I schedule note data and play it through the DMG APU?
Concepts You Must Understand First
- Timer-driven updates
- Note tables and frequency values
- APU register sequencing
Questions to Guide Your Design
- Will you store note data in ROM or RAM?
- How will you time note changes?
- How do you handle rests?
Thinking Exercise
Create a 4-note table with frequencies and durations.
The Interview Questions They’ll Ask
- How do you convert musical notes to frequency values?
- Why is a fixed timer useful for music playback?
- What happens if you update APU registers too often?
Hints in Layers
- Hint 1: Use timer ISR to tick a music counter.
- Hint 2: Store note data as (freq, duration).
- Hint 3: Update channel registers only when the counter expires.
- Hint 4: Test with a single channel before multi-channel.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Systems timing | Computer Systems: A Programmer’s Perspective | Ch. 5 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 8 |
Common Pitfalls & Debugging
Problem: Music glitches
- Why: Timer and music tick are misaligned.
- Fix: Update notes only on fixed ticks.
- Quick test: Log note index changes per second.
Definition of Done
- Melody plays and loops correctly.
- No audible clicks between notes.
- Works while the game loop runs.
Project 11: Animation System (Tiles and Sprites)
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A sprite animation system using frame tables and OAM updates.
Why it teaches: Data-driven animation is the key to scalable DMG games.
Real World Outcome
A player sprite animates between walk frames. You can control animation speed and direction with tables. The animation is stable and does not require reloading tile data every frame.
The Core Question You’re Answering
How do I animate sprites efficiently using data tables?
Concepts You Must Understand First
- OAM tile indices
- Data tables in ROM
- Timing counters
Questions to Guide Your Design
- Will you animate by swapping tile indices or tile data?
- How will you store frame sequences?
- How will you handle direction changes?
Thinking Exercise
Design a table that maps direction + frame to tile indices.
The Interview Questions They’ll Ask
- Why are data tables better than hardcoded animation?
- How do you reduce VRAM updates?
- What is the cost of tile swapping?
Hints in Layers
- Hint 1: Store frames as lists of tile indices.
- Hint 2: Use a frame counter updated each tick.
- Hint 3: Write tile indices into the OAM buffer.
- Hint 4: Only update when the frame changes.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Data structures | Algorithms in C (Sedgewick) | Ch. 3 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 9 |
Common Pitfalls & Debugging
Problem: Animation flickers
- Why: Tiles are reloaded too often or OAM updates overlap.
- Fix: Update only on frame changes and use DMA.
- Quick test: Log frame index and verify it changes at expected rate.
Definition of Done
- Smooth animation with correct timing.
- Directional frames work.
- No VRAM corruption.
Project 12: Save/Load with Cartridge RAM
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A save system that stores player state in external RAM (MBC-backed).
Why it teaches: Persistence is a classic DMG challenge and teaches MBC control.
Real World Outcome
You can save the player’s position and score. After restarting the emulator, the values persist. The save feature works with the correct MBC type.
The Core Question You’re Answering
How do I safely write and read external cartridge RAM?
Concepts You Must Understand First
- MBC enable registers
- External RAM address range
- Data layout in RAM
Questions to Guide Your Design
- How will you enable and disable RAM access?
- What data structure will you store?
- How will you validate save data on load?
Thinking Exercise
Design a 16-byte save block layout and checksum.
The Interview Questions They’ll Ask
- Why do MBCs require RAM enable?
- How do you avoid corrupting saves?
- What is a simple checksum strategy?
Hints in Layers
- Hint 1: Enable RAM by writing the correct value to the MBC register.
- Hint 2: Write your save block to
0xA000. - Hint 3: Add a simple checksum byte.
- Hint 4: Disable RAM when done.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Data integrity | Effective C | Ch. 8 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 10 |
Common Pitfalls & Debugging
Problem: Save data lost after reset
- Why: RAM not enabled or wrong MBC type.
- Fix: Verify cartridge type and enable RAM before writes.
- Quick test: Read back the data immediately after writing.
Definition of Done
- Save data persists across resets.
- Save block validated with checksum.
- Works on emulators that support RAM.
Project 13: Banked Code/Data Architecture
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A banked code/data system where large assets live in ROMX and are loaded on demand.
Why it teaches: Real DMG games require banking to fit content.
Real World Outcome
You can switch between two level banks and load different graphics/maps without crashing. The bank number is tracked and restored correctly.
The Core Question You’re Answering
How do I safely switch ROM banks and access data on demand?
Concepts You Must Understand First
- ROMX banking
- MBC register writes
- Section placement in RGBDS
Questions to Guide Your Design
- Where will your bank switch code live?
- How will you avoid switching banks inside an ISR?
- How will you restore the previous bank?
Thinking Exercise
Design a macro that saves the current bank, switches, then restores.
The Interview Questions They’ll Ask
- Why is bank 0 special?
- What happens if you forget to restore a bank?
- How do you keep ISR code safe with banking?
Hints in Layers
- Hint 1: Keep interrupt vectors in ROM0.
- Hint 2: Store current bank in WRAM.
- Hint 3: Use macros for bank switching.
- Hint 4: Use map file to verify placement.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Systems programming | Computer Systems: A Programmer’s Perspective | Ch. 6 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 11 |
Common Pitfalls & Debugging
Problem: Random crashes after bank switch
- Why: Code jumped into an unmapped bank.
- Fix: Ensure correct bank is selected before calling.
- Quick test: Log bank value before/after calls.
Definition of Done
- Code and data split into banks.
- Bank switches safe and reversible.
- No crashes when switching levels.
Project 14: Full Game 1 - Breakout Clone
- Difficulty: Advanced
- Time estimate: 2-3 weeks
What you’ll build: A complete Breakout-style game with paddle, ball, bricks, scoring, and sound.
Why it teaches: It integrates all core systems: input, graphics, timing, and audio.
Real World Outcome
A playable Breakout game appears: paddle moves smoothly, ball bounces with proper collision, bricks disappear, and sound effects play on impact. Score updates on a HUD.
The Core Question You’re Answering
Can I combine all DMG subsystems into a real, playable game?
Concepts You Must Understand First
- PPU tile and sprite usage
- Collision detection
- Timer-driven updates
- Basic sound effects
Questions to Guide Your Design
- Will you use sprites or background for bricks?
- How will you update the score HUD?
- How will you handle ball speed and direction?
Thinking Exercise
Sketch the game loop states: serve, play, life lost, game over.
The Interview Questions They’ll Ask
- How do you manage collision in a tile-based game?
- How do you avoid sprite flicker with many bricks?
- How do you update UI without corrupting background?
Hints in Layers
- Hint 1: Use a background tilemap for bricks.
- Hint 2: Use sprites for ball and paddle.
- Hint 3: Store brick states in a RAM array.
- Hint 4: Update HUD in VBlank using window layer.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Game logic | Algorithms in C (Sedgewick) | Ch. 1 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 12 |
Common Pitfalls & Debugging
Problem: Ball passes through bricks
- Why: Collision check is too coarse.
- Fix: Check both axes and adjust after collision.
- Quick test: Slow ball speed and log collision checks.
Definition of Done
- Game is fully playable.
- Score and lives update correctly.
- Audio and graphics are stable.
Project 15: Full Game 2 - Top-Down Adventure
- Difficulty: Advanced
- Time estimate: 3-4 weeks
What you’ll build: A small top-down adventure with scrolling, collisions, and multiple screens.
Why it teaches: This is a full-scale DMG game architecture with level data and state management.
Real World Outcome
You can move through a scrolling world, collide with walls, and transition between map screens. The HUD shows health or items. The game feels like a real DMG title.
The Core Question You’re Answering
How do I build a multi-screen game architecture on DMG?
Concepts You Must Understand First
- Scrolling and map streaming
- Banked data (multiple maps)
- Sprite animation
Questions to Guide Your Design
- How will you stream or load maps?
- Will you store maps in banks?
- How will you handle map transitions?
Thinking Exercise
Design a 2x2 world grid and map out transitions.
The Interview Questions They’ll Ask
- How do you handle large maps with limited VRAM?
- How do you manage game state across screens?
- How do you keep sprite animation in sync?
Hints in Layers
- Hint 1: Use banked ROM for map data.
- Hint 2: Use a small window for HUD.
- Hint 3: Stream tiles into VRAM during transitions.
- Hint 4: Keep player state in WRAM.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Systems design | Code Complete | Ch. 3 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 13 |
Common Pitfalls & Debugging
Problem: Map transitions glitch
- Why: VRAM updates happen while LCD is on.
- Fix: Turn off LCD during transitions or use VBlank updates.
- Quick test: Add a transition delay and check for artifacts.
Definition of Done
- Multiple maps with transitions.
- Stable scrolling and collision.
- HUD and animations work.
Project 16: ROM Analysis + Header Verification
- Difficulty: Intermediate
- Time estimate: 3-5 days
What you’ll build: A ROM analysis report for a game you own: header fields, bank count, checksum validity, and basic memory map notes.
Why it teaches: It formalizes the first step of ROM hacking and teaches discipline.
Real World Outcome
You generate a short report listing the ROM’s title, cartridge type, ROM size, and checksum status. You can load the ROM in an emulator and verify these values.
The Core Question You’re Answering
How do I safely analyze a ROM before making changes?
Concepts You Must Understand First
- ROM header layout
- MBC types
- Checksums
Questions to Guide Your Design
- How will you extract header bytes?
- How will you verify checksums?
- How will you document the ROM hash?
Thinking Exercise
List the header fields you will extract and what they mean.
The Interview Questions They’ll Ask
- What is in the ROM header?
- Why does the Nintendo logo matter?
- How can you tell which MBC is used?
Hints in Layers
- Hint 1: Use a hex viewer or small script to read
0x0100-0x014F. - Hint 2: Compare fields to Pan Docs definitions.
- Hint 3: Compute a CRC/MD5 hash.
- Hint 4: Use emulator ROM info panel for cross-check.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Binary analysis | Practical Binary Analysis | Ch. 1-2 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 14 |
Common Pitfalls & Debugging
Problem: Header values don’t match expected sizes
- Why: Some games use unusual size codes.
- Fix: Use Pan Docs size tables.
- Quick test: Compare with emulator header output.
Definition of Done
- Header fields extracted and documented.
- ROM hash recorded.
- MBC type identified.
Project 17: Graphics ROM Hack (Tiles and Sprites)
- Difficulty: Advanced
- Time estimate: 1-2 weeks
What you’ll build: A graphics-only ROM hack that replaces a sprite or tileset with your own art.
Why it teaches: You learn to identify and edit 2bpp graphics in a real game.
Real World Outcome
A character or background graphic is visibly replaced in-game. The ROM passes header checks and runs correctly in multiple emulators.
The Core Question You’re Answering
How do I safely replace graphics in a DMG ROM?
Concepts You Must Understand First
- 2bpp tile format
- Tilemap indexing
- ROM free space and size limits
Questions to Guide Your Design
- How will you locate the graphics data?
- Can you replace in-place or need free space?
- How will you preserve tile order?
Thinking Exercise
Extract a 16-tile block and convert it to an image; then reinsert it unchanged.
The Interview Questions They’ll Ask
- Why is 2bpp format tricky?
- What happens if tile order changes?
- How do you verify a graphics edit is correct?
Hints in Layers
- Hint 1: Use a 2bpp tile viewer to locate graphics.
- Hint 2: Export tiles, edit with 4-color palette.
- Hint 3: Reinsert with identical size.
- Hint 4: Test in emulator and verify checksums.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Binary data formats | Practical Binary Analysis | Ch. 4 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 15 |
Common Pitfalls & Debugging
Problem: Graphics appear garbled
- Why: Wrong tile order or palette.
- Fix: Preserve order and use correct 4-color palette.
- Quick test: Compare original and modified tile indices.
Definition of Done
- Graphics replaced without corruption.
- ROM boots and runs in emulator.
- Patch distributed as IPS/BPS.
Project 18: Text/Script Hack (Tables and Pointers)
- Difficulty: Advanced
- Time estimate: 2-3 weeks
What you’ll build: A text modification patch that changes in-game dialogue or menu strings.
Why it teaches: Text hacking forces you to understand character encoding and pointer tables.
Real World Outcome
Modified text appears in-game, with correct line breaks and no crashes. The patch includes documentation of the base ROM hash.
The Core Question You’re Answering
How do I locate and modify text safely in a DMG ROM?
Concepts You Must Understand First
- Custom encodings
- Pointer tables
- ROM banking
Questions to Guide Your Design
- What encoding does the game use?
- Where are pointers stored?
- How will you expand text without breaking pointers?
Thinking Exercise
Create a mapping table from glyphs to byte values for the game.
The Interview Questions They’ll Ask
- Why do many games use custom text encodings?
- What is a pointer table?
- How do you handle text expansion?
Hints in Layers
- Hint 1: Search ROM for repeating string patterns.
- Hint 2: Build a glyph table by inspecting tiles.
- Hint 3: Use pointers to locate text blocks.
- Hint 4: If expanding text, move data to free space and update pointers.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Reverse engineering | Practical Binary Analysis | Ch. 5-6 | | Low-level programming | Low-Level Programming (Zhirkov) | Ch. 16 |
Common Pitfalls & Debugging
Problem: Game crashes when text displays
- Why: Pointer table not updated correctly.
- Fix: Verify all pointers and bank offsets.
- Quick test: Check pointer values in debugger.
Definition of Done
- Text changes appear in-game.
- No crashes or glitches.
- Patch documented with ROM hash.
Project 19: Behavior Patch (Logic Change)
- Difficulty: Advanced
- Time estimate: 2-3 weeks
What you’ll build: A small logic change (e.g., modify player speed or enemy behavior) by patching assembly in a ROM.
Why it teaches: Behavior hacking is the deepest form of ROM modification and tests your CPU knowledge.
Real World Outcome
A visible behavior change occurs (e.g., faster movement, altered enemy AI). The patch is stable in multiple emulators.
The Core Question You’re Answering
How do I safely modify game logic without breaking timing or bank constraints?
Concepts You Must Understand First
- SM83 instruction encoding
- Control flow and branches
- Banked code
Questions to Guide Your Design
- How will you locate the behavior code?
- Can you patch in-place or need a trampoline?
- How will you test timing impact?
Thinking Exercise
Find a simple loop and predict how changing one instruction affects behavior.
The Interview Questions They’ll Ask
- What risks come with patching control flow?
- How do you deal with instruction length differences?
- Why is bank context important?
Hints in Layers
- Hint 1: Use a debugger to find writes to a behavior variable.
- Hint 2: Trace back to the routine that updates it.
- Hint 3: Patch with same-length instructions when possible.
- Hint 4: If longer, create a jump to free space and back.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Reverse engineering | Practical Binary Analysis | Ch. 7 | | Assembly | The Art of Assembly Language | Ch. 6 |
Common Pitfalls & Debugging
Problem: Game freezes after patch
- Why: Jump target invalid or bank not mapped.
- Fix: Verify bank and address before jump.
- Quick test: Set breakpoint at patch location.
Definition of Done
- Behavior changes as intended.
- No crashes or timing artifacts.
- Patch works in multiple emulators.
Project 20: Patch Packaging and Validation
- Difficulty: Intermediate
- Time estimate: 3-5 days
What you’ll build: A clean patch release with documentation: base ROM hash, patch file, and verification steps.
Why it teaches: Responsible ROM hacking requires reproducible patches and clear documentation.
Real World Outcome
You produce a .bps or .ips patch file, a README with the base ROM hash, and validation instructions. Applying the patch produces the expected changes.
The Core Question You’re Answering
How do I distribute a ROM modification ethically and reproducibly?
Concepts You Must Understand First
- Patch formats (IPS/BPS/xdelta)
- ROM hashing
- Documentation standards
Questions to Guide Your Design
- Which patch format is appropriate?
- How will you document the base ROM?
- How will you verify patch application?
Thinking Exercise
Write a short README that explains how to apply your patch.
The Interview Questions They’ll Ask
- Why distribute patches instead of ROMs?
- What is the difference between IPS and BPS?
- How do you ensure reproducible hacks?
Hints in Layers
- Hint 1: Compute a hash of the base ROM and include it.
- Hint 2: Use a patch tool that supports BPS.
- Hint 3: Test applying the patch to a clean ROM.
- Hint 4: Verify changes in emulator and document.
Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Security mindset | Foundations of Information Security | Ch. 2 | | Professional practice | The Pragmatic Programmer | Ch. 8 |
Common Pitfalls & Debugging
Problem: Patch fails to apply
- Why: Base ROM does not match expected hash.
- Fix: Verify the ROM hash and use the correct base.
- Quick test: Apply patch to verified clean ROM.
Definition of Done
- Patch file created and tested.
- README includes base ROM hash and steps.
- Patch produces expected changes.
Project Comparison Table
| # | Project | Difficulty | Time | Focus |
|---|---|---|---|---|
| 1 | Toolchain + Bootable ROM | Beginner | 1-2 days | Header/Toolchain |
| 2 | LCD Init + Background | Beginner | 2-3 days | PPU/VRAM |
| 3 | Joypad + Sprite | Beginner-Int | 3-4 days | Input/OAM |
| 4 | OAM DMA System | Intermediate | 4-6 days | DMA |
| 5 | Tilemap Engine | Intermediate | 5-7 days | Maps |
| 6 | Scrolling + Collision | Int-Adv | 1-2 weeks | Camera/Collision |
| 7 | Window HUD | Advanced | 1-2 weeks | STAT interrupts |
| 8 | Timer Loop | Advanced | 1 week | Timing |
| 9 | Sound Effects | Advanced | 1 week | APU |
| 10 | Music Playback | Advanced | 1-2 weeks | Audio data |
| 11 | Animation System | Advanced | 1-2 weeks | Data tables |
| 12 | Save/Load | Advanced | 1-2 weeks | MBC RAM |
| 13 | Banked Architecture | Advanced | 1-2 weeks | Banking |
| 14 | Breakout Game | Advanced | 2-3 weeks | Full game |
| 15 | Adventure Game | Advanced | 3-4 weeks | Full game |
| 16 | ROM Analysis | Intermediate | 3-5 days | Header/Reverse |
| 17 | Graphics Hack | Advanced | 1-2 weeks | 2bpp |
| 18 | Text Hack | Advanced | 2-3 weeks | Pointers |
| 19 | Behavior Patch | Advanced | 2-3 weeks | ASM patch |
| 20 | Patch Packaging | Intermediate | 3-5 days | Distribution |
Summary
By following these 20 projects, you will progress from a bootable DMG ROM to fully playable assembly games, then into disciplined ROM hacking. You will understand the DMG memory map, SM83 instruction set, PPU/PPU timing, DMA, APU audio, and cartridge banking deeply enough to both build new content and safely modify existing games you own. Your final outcomes are a portfolio of working DMG ROMs, a structured hacking toolkit, and the confidence to reason about every byte on the original Game Boy.