This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Post-Clean Execution Flow Investigation
Summary
Implemented five diagnostic monitors to investigate why the game never loads tile data in VRAM after cleaning it. Analysis of Step 0291 confirmed that only cleanup writes (0x00) are detected from PC:0x36E3 and no real data loading. New monitors track execution flow after of the cleanup to identify what code is executed (or should be executed but is not executed) and if there are conditions that prevent the loading of tools.
Hardware Concept
After clearing VRAM, games typically follow a specific execution flow:
- Hardware register configuration: LCDC, BGP, STAT, etc.
- Loading tiles from ROM to VRAM: Copy graphics data (2bpp) to 0x8000-0x97FF
- Tilemap Settings: Writing tile indexes to 0x9800-0x9FFF
- LCD Activation: Enable LCD and start rendering
If step 2 does not occur, the screen will be empty because the tiles referenced by the tilemap will be empty.
Conditions for Loading Tiles
The tiles can be loaded at different times:
- During initialization: Before the first visible frame
- During V-Blank: When VRAM is accessible (between frames)
- During H-Blank: Limited, only in certain PPU modes
Factors that may prevent loading:
- LCDC disabled: LCD OFF may prevent access to VRAM
- Interrupts disabled: IME=0, IE=0 can prevent V-Blank
- Jump conditions not met: Code can jump over the bootstrap
- Wrong ROM bank: The upload code may be in another bank
- Incorrect registration statuses: Flags or registry values may prevent loading
Fountain: Pan Docs - "Video RAM (VRAM)", "LCD Control Register (LCDC)", "Interrupts"
Implementation
Implemented five diagnostic monitors that activate when the cleaning routine is detected (PC:0x36E0-0x36F0) and trace the execution flow after it finishes.
Monitor [PC-TRACE] - Post-Cleanup Execution Trace
Trace the next 500 instructions after the cleanup routine finishes to see what code is executed next. Capture:
- PC, opcode and the next 2 bytes
- Registration status (A, BC, DE, HL, SP)
- Current ROM Bank
- Detection of possible tile loads (LD (HL+), A or LD (HL-), A with HL in VRAM range)
Location: src/core/cpp/CPU.cpp- FunctionCPU::step()
Monitor [REG-TRACE] - Critical Records Trace
Tracks significant changes in registers (AF, BC, DE, HL, SP) and flags after cleanup. Only reports changes greater than 0x100 to avoid saturation. Capture:
- Current PC
- Register values (AF, BC, DE, HL, SP)
- Flag status (Z, N, H, C)
Location: src/core/cpp/CPU.cpp- FunctionCPU::step()
Monitor [JUMP-TRACE] - Jump and Call Trace
Trace jumps, calls, and returns after cleanup to see if the game jumps to code which should load tiles but it doesn't. Detect:
- JP (0xC3, 0xC2, 0xCA, 0xD2, 0xDA) - Absolute jumps
- JR (0x18, 0x20, 0x28, 0x30, 0x38) - Relative jumps
- CALL (0xCD, 0xC4, 0xCC, 0xD4, 0xDC) - Subroutine calls
- RET (0xC9, 0xC0, 0xC8, 0xD0, 0xD8) - Returns
- JP HL (0xE9) - Indirect jump
Location: src/core/cpp/CPU.cpp- FunctionCPU::step()
Monitor [BANK-CHANGE] - Bank ROM Changes
Detects ROM bank changes after cleaning to verify if the code that loads tiles It is in a different bank that is not activated. Only enabled if PC > 0x36F0 (after cleanup).
Location: src/core/cpp/MMU.cpp- FunctionMMU::update_bank_mapping()
Monitor [HARDWARE-STATE] - Critical Hardware Status
Tracks the status of critical hardware registers after cleaning. Sample every 10 instructions approximately and capture:
- LCDC (0xFF40) - LCD Control
- BGP (0xFF47) - Background Palette
- IE (0xFFFF) - Interrupt Enable
- IF (0xFF0F) - Interrupt flags
- IME - Interrupt Master Enable (from CPU)
- LY (0xFF44) - Current scanline
Location: src/core/cpp/CPU.cpp- FunctionCPU::step()
Changes in Architecture
New class members added inCPU.hppto maintain the status of the monitors
(following the pattern of Step 0287 which replaced static variables with class members):
in_post_cleanup_trace_- Flag to activate post-cleaning trackingpost_cleanup_trace_count_- Tracked instruction counterreg_trace_active_- Flag to activate log trackinglast_af_, last_bc_, last_de_, last_hl_- Previous values of recordsreg_trace_count_- Log sample counterjump_trace_active_- Flag to activate jump trackingjump_trace_count_- Tracked jump counterhw_state_trace_active_- Flag to activate hardware trackinghw_state_samples_- Hardware sample counterhw_state_sample_counter_- Internal counter for sampling
Affected Files
src/core/cpp/CPU.hpp- Added class members for Step 0293 monitorssrc/core/cpp/CPU.cpp- Implemented monitors [PC-TRACE], [REG-TRACE], [JUMP-TRACE], [HARDWARE-STATE]src/core/cpp/MMU.cpp- Improved [BANK-CHANGE] monitor to activate after cleaning
Tests and Verification
The implementation was verified by compiling the code and ensuring there were no syntax errors. The monitors are ready to run when the emulator is run with a ROM.
- Compilation: ✅ Code compiled without errors
- Linter: ✅ No linter errors
- Validation: ✅ All monitors implemented correctly
Compile command: python setup.py build_ext --inplace
Result: Compilation successful, Cython module generated successfully.
Note: The monitors will be activated automatically when the emulator detects the cleanup routine (PC:0x36E0-0x36F0) and will trace the execution flow afterwards.
Sources consulted
- Pan Docs: "Video RAM (VRAM)" - VRAM access and timing restrictions
- Pan Docs: "LCD Control Register (LCDC)" - LCD Control and Access Modes
- Pan Docs: "Interrupts" - Interrupt Vectors and Activation Conditions
- Pan Docs: "CPU Instruction Set" - Jump and Call Opcodes
Educational Integrity
What I Understand Now
- Post-initialization execution flow: Games follow a specific pattern after clearing VRAM: configure hardware, load tiles, configure tilemap, activate LCD.
- Loading conditions: Tiles can be loaded during initialization, V-Blank or H-Blank, but there are specific conditions that must be met.
- Diagnostic Monitors: Trace monitors help you understand the execution flow and identify where it deviates from expected behavior.
- Monitor architecture: Using class members instead of static variables allows you to isolate state between tests and avoid global state problems.
What remains to be confirmed
- Actual execution flow: What code is actually executed after cleanup?
- Conditions not met: Are there specific conditions that prevent tiles from loading?
- ROM Banks: The upload code is in another bank that is not activated?
- Hardware status: Do the hardware registers have correct values to allow loading?
Hypotheses and Assumptions
Hypothesis A: The code that loads tiles exists but is not executed due to unmet conditions (timing, interruptions, hardware registers).
Hypothesis B: The code that loads tiles exists but is in a different ROM bank that is not activated.
Hypothesis C: The game waits for a specific hardware state before loading tiles (e.g. a certain number of frames, V-Blanks, etc.).
Hypothesis D: There is a bug in the emulation that prevents the loading code from running (e.g. incorrect jumps, false conditions, etc.).
Next Steps
- Run the emulator: Run the emulator with Pokémon Red and capture the logs of the new monitors.
- Analyze the execution flow: Analyze the [PC-TRACE] logs to see what code is executed after the cleanup.
- Check conditions: Analyze the [REG-TRACE] and [HARDWARE-STATE] logs to verify if there are any unmet conditions.
- Check ROM banks: Analyze [BANK-CHANGE] logs to check for bank changes after cleanup.
- Identify the problem: Determine which of the hypotheses is correct and apply the corresponding corrections.