This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Step 0250: The Prequel (Expanded ROM Dump)
Summary
Step 0249 revealed that the infinite loop in0x2B20look for value0xFDin the memory pointed out byH.L.. Since our memory is empty (everything0x00), the loop never ends. This Step expands the ROM dump to the previous range (0x2AE0 - 0x2B20) to find how to initializeH.L.before entering the loop. The analysis reveals thatH.L.is initialized from a table of pointers in0x2BAC, and that the final value depends on data read from RAM that is not initialized correctly.
Hardware Concept
Pointer Tables in ROM:Game Boy games frequently store tables of pointers in ROM that point to data in RAM. These tables allow code to dynamically access different regions of memory based on an index. The typical format is little-endian: the low byte goes first, followed by the high byte.
Memory Indirection:Code can use multiple levels of indirection: first read a pointer from ROM, then use that pointer to read data from RAM, and finally use that data as another address or value. If any of these levels are not initialized correctly, the program may crash or enter an infinite loop.
Memory Initialization:The Game Boy does not automatically initialize the RAM upon power up. Games must explicitly initialize the memory regions they are going to use. If a game assumes that certain data is already in memory (for example, copied by DMA or by an initialization routine), but that data is never copied, the program may crash.
Implementation
Modified the dump script to analyze the range before the infinite loop and created a stream analysis script to understand how it is initializedH.L..
Components created/modified
tools/dump_rom_zone.py: Modified to dump range0x2AE0-0x2B20.tools/analyze_code_flow.py: New script that disassembles and analyzes the code flow between0x2B05and0x2B20.
Code Flow Analysis
The code between0x2B05and0x2B20performs the following operations:
- 0x2B05:
LD HL, 0x2BAC- Initializes HL by pointing to a table of pointers in ROM. - 0x2B08:
RLCA- Rotate record A (probably an index). - 0x2B09-0x2B0A:
LD E,A/LD D,0x00- Converts A to a 16-bit offset. - 0x2B0C:
ADD HL,DE- Calculates the address of the entry in the table:HL=0x2BAC+A. - 0x2B0D-0x2B0F: Read a pointer from
[HL]and stores it inOF. - 0x2B10-0x2B14: Read data from
[OF]and uses them to configureH.L.. - 0x2B16-0x2B1B: Write data to hardware registers (
0xFF90,0xFF91). - 0x2B1D-0x2B1F: Read another pointer from
[HL]and stores it inOF. - 0x2B20:
INC HL- HERE THE LOOP BEGINS!
Key Finding
The dump of0x2BACreveals an address table:
2BAC: 68 2C 6C 2C 70 2C 74 2C 78 2C 7C 2C 80 2C 84 2C
2BBC: 88 2C 8C 2C 90 2C 94 2C 98 2C 9C 2C A0 2C A4 2C
2BCC: A8 2C AC 2C
These are little-endian pointers that point to addresses in the range0x2C68 - 0x2CAC. The code uses the value ofTOas an index to select one of these pointers, then read data from the pointed address, and finally use that data to setH.L..
The Problem:If the data at the address pointed to by the table is not initialized (it is0x00), H.L.will be configured incorrectly, and the loop in0x2B20will search0xFDat an incorrect address or in empty memory.
Affected Files
tools/dump_rom_zone.py- Modified: Changed default range to0x2AE0-0x2B20.tools/analyze_code_flow.py- New: Code flow analysis script with detailed disassembly.
Tests and Verification
Executed the dump script and analysis script to verify the code flow:
$ python tools/dump_rom_zone.py
📦 ROM Dump: tetris.gb
📍 Zone: 0x2ae0 - 0x2b20 (64 bytes)
...
✅ Dump completed: 64 bytes
$python tools/analyze_code_flow.py
🔍 CODE FLOW ANALYSIS (0x2B05 - 0x2B20)
...
📊 FLOW SUMMARY:
1. 2B05: LD HL, 0x2BAC → HL points to pointer table in ROM
2. 2B08: RLCA → Rota A (index?)
3. 2B09: LD E,A / 2B0A: LD D,0x00 → DE = A (as offset)
4. 2B0C: ADD HL,DE → HL = 0x2BAC + A (points to table entry)
5. 2B0D-2B0F: Read pointer from [HL] → DE = [HL] (memory address)
6. 2B10-2B14: Read data from [DE] → HL = [DE] (new address)
7. 2B16-2B1B: Write data to hardware registers (0xFF90, 0xFF91)
8. 2B1D-2B1F: Read ANOTHER pointer from [HL] → DE = [HL+1]
9. 2B20: INC HL → HERE THE LOOP BEGINS!
Validation:The analysis confirms thatH.L.is initialized from a table of pointers and that the final value depends on data read from RAM that is not initialized.
Sources consulted
- Bread Docs:CPU Instruction Set- Opcode reference LR35902
- GBEDG:Game Boy Opcodes Reference- Complete opcode table
Note: Code flow analysis is based on the LR35902 instruction set specification.
Educational Integrity
What I Understand Now
- HL initialization:The record
H.L.is initialized from a table of pointers in ROM using an index computed from the registerTO. - Multiple Indirection:The code uses multiple levels of indirection: ROM → RAM → RAM, which means that if any level is not initialized, the program fails.
- Initialization Dependency:The game assumes that certain data is already in RAM before executing this code, but that data is never copied because DMA or the initialization routines do not work correctly.
What remains to be confirmed
- A value:What value does the registry have?
TOwhen it runsRLCAin0x2B08? This determines which table entry is selected. - Expected Data:What specific data does the game expect to find in the direction pointed to by the table? Is it configuration data, sprites, or something else?
- Initialization Routine:What routine should copy this data to RAM? Is it DMA, a V-Blank interrupt, or a game initialization routine?
Hypotheses and Assumptions
Main Hypothesis:The game waits for an initialization routine (probably executed during boot or in a V-Blank interrupt) to copy data from ROM to RAM before executing the code in0x2B05. Since this routine never runs or fails, the data is not in RAM,H.L.is configured incorrectly, and the loop in0x2B20never finds the terminator0xFD.
Next Steps
- [ ] Check the value of
TOwhen it runsRLCAin0x2B08(record tracking). - [ ] Dump the memory region pointed to by the table (for example,
0x2C68) to see what data the game expects. - [ ] Search the ROM for initialization routines that copy data to RAM (looking for patterns such as
LD HL, ...followed byLD (HL+),AeitherLDI). - [ ] Check if the game expects DMA to copy this data (check for writes to
0xFF46before0x2B05). - [ ] Implement record tracking to see the exact value of
H.L.when it enters the loop in0x2B20.