This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Memory Timeline & PC Tracker
Summary
Step 0246 confirmed that the gameyes it is writing to WRAM, but he is doing it descendingly (from `DFFF` downwards) and with value`0x00`(zeros). This is amemory cleanup routine (Zero-Fill)which is normal and correct during initialization.
However, the key piece is still missing:The Chronology. In what order do the operations occur? and who executes them? If the game clears all WRAM to zeros and then looks for `0xFD`... it will never find it. The `0xFD` must be writtenAFTERof the cleaning, or the cleaning should not touch that area.
This Step implements aMemory Timeline & PC Trackerthat combines the tracking of the Program Counter with key writes to memory to reconstruct the entire temporal sequence and determine which instruction (PC) is causing each operation.
Hardware Concept
HeProgram Counter (PC)is a 16-bit register that contains the memory address of the next instruction to execute. Every time the CPU executes an instruction, the PC advances to the next opcode (or jumps to a different address in case of jumps, calls, etc.).
Temporal Tracing of Memory Operations:To understand the sequence of events in a program, it is crucial to know not onlythatoperations occur, but alsowhenoccur andfrom where(what instruction caused them). This allows us to reconstruct the "history" or "timeline" of memory operations.
The Chronology Problem:Step 0246 confirmed that:
- Game writes `0xFD` to HRAM (`FF8D`) - The Scoreboard
- The game clears the WRAM to zeros (`0x00`) from `DFFF` down - The Cleanup
- The game looks for `0xFD` in WRAM and crashes because there are only zeros - The Problem
But you need to know:In what order does this occur?If cleaning occursafterto write the marker, then you are deleting the marker. If bookmark writing occursafterof the cleanup, then the problem is somewhere else (maybe the copy never happens).
PC Trace in Memory Operations:By registering the current PC in each operation memory, we can determine which instruction (and therefore which game routine) is executing each operation. This allows us to correlate operations with the program flow and reconstruct the temporal sequence.
Fountain:Pan Docs - "CPU Registers", "Memory Map"
Implementation
A temporal tracking system is implemented that combines the Program Counter (PC) with key writes in memory. This system records:
- Writes to WRAM: First 200 writes in range `0xC000-0xDFFF` to view the cleaning routine with your corresponding PC.
- Marker Writes (`0xFD`): Any write of the value `0xFD` to any memory address (the "sentinel" that the game looks for).
- Writes to DMA (`0xFF46`): DMA register activity to detect transfers by memory.
Components created/modified
src/core/cpp/MMU.hpp: Added public memberdebug_current_pcto track the current PC CPU.src/core/cpp/MMU.cpp: Initializeddebug_current_pcin the constructor. Replaced the Step 0246 log with the new Timeline Logger that includes the PC in each log.src/core/cpp/CPU.cpp: Added updatemmu_->debug_current_pcin the methodstep()before executing each instruction.
Design decisions
public fielddebug_current_pc:Added as a public member of the MMU class
to allow the CPU to update it easily without the need for additional methods. This field is only
for diagnostic purposes and does not affect normal emulator logic.
Update before fetch:The PC is updated in the MMUbeforeto execute the
instruction (just beforefetch_byte()), so that the registered PC corresponds to the address
of the instruction that isabout to be executed, not the next one.
Unified log format:All logs use the format[TIME] PC:XXXX -> ...to facilitate temporal analysis. The prefix[TIME]allows you to easily filter these logs
other system messages.
200 WRAM Write Limit:Increased the limit from 100 to 200 writes to capture more context of the cleaning routine, but it is still limited to avoid flooding the output.
Affected Files
src/core/cpp/MMU.hpp- Added public memberdebug_current_pc(Step 0247)src/core/cpp/MMU.cpp- Initializeddebug_current_pcin constructor. Replaced logging from Step 0246 with Timeline Logger (Step 0247)src/core/cpp/CPU.cpp- Added PC update in MMU before executing instruction (Step 0247)
Tests and Verification
Verification is performed by running the emulator with output redirection to a file for analysis:
- Recompilation:
.\rebuild_cpp.ps1 - Execution with redirection:
python main.py roms/tetris.gb > timeline.log 2>&1(redirection necessary because the log will be long) - Log analysis:Search lines with
[TIME]to reconstruct the temporal sequence
Modified key code:
// In CPU::step(), before fetch_byte():
mmu_->debug_current_pc = regs_->pc;
// In MMU::write(), logging with PC:
if (addr >= 0xC000 && addr<= 0xDFFF && wram_log_count < 200) {
printf("[TIME] PC:%04X ->Write WRAM [%04X] = %02X\n", debug_current_pc, addr, value);
wram_log_count++;
}
if (value == 0xFD) {
printf("[TIME] PC:%04X -> Write SENTINEL [%04X] = FD\n", debug_current_pc, addr);
}
if (addr == 0xFF46) {
printf("[TIME] PC:%04X -> Write DMA [%04X] = %02X\n", debug_current_pc, addr, value);
}
Compiled C++ module validation:The Timeline Logger is implemented directly in the C++ code of the MMU and CPU, so it requires recompilation to activate. Instrumentation runs in the critical instruction execution loop, but the overhead is minimal (only one assignment and comparisons conditional).
Expected results:
- Scenario A (Cleaning before the marker):They look multiple
[TIME] PC:XXXX -> Write WRAMwith value00, followed by[TIME] PC:YYYY -> Write SENTINELwith valueF.D.. Diagnosis:Cleaning happens earlier, which is fine. The problem is that the marker does not copy to WRAM afterwards. - Scenario B (Marker before cleaning):It looks
[TIME] PC:XXXX -> Write SENTINEL, followed by multiple[TIME] PC:YYYY -> Write WRAMwith value00. Diagnosis:Cleanup is erasing the bookmark after writing it. - Scenario C (Marker is never written):You don't see any
Write SENTINEL. Diagnosis:The game never writes the marker, or the routine that writes it does not run.
Sources consulted
- Bread Docs:CPU Registers and Flags- "Program Counter (PC)" section
- Bread Docs:Memory Map- "Work RAM (WRAM)" section
- Bread Docs:Memory Map- "DMA" section
Educational Integrity
What I Understand Now
- Temporal tracking of operations:To diagnose synchronization problems or sequence in a program, it is crucial to reconstruct the "timeline" or chronology of operations. Recording the PC along with memory operations allows us to determine which instruction (and therefore, which routine) is executing each operation.
- Architectural instrumentation:Add debug fields to data structures main components (such as the MMU) is a common technique to allow related components (such as the CPU) update context information that can then be used for logging or diagnostics.
- Sequence analysis:By analyzing the temporary logs, we can determine if the operations occur in the correct order and if there are operations that are interfering with each other (for example, a cleanup routine that deletes data before another routine uses it).
What remains to be confirmed
- Timeline Logger Results:We need to run the emulator and analyze the logs to determine the actual temporal sequence of operations. Does cleaning occur before or after writing the marker?
- Correlation with game code:Once we know the PC of each operation, We could potentially correlate these PCs with the game code (ROM) to understand what routines are executing these operations.
- Effect on performance:Although the overhead should be minimal, we need verify that the emulator is still running at a reasonable speed with this instrumentation active.
Hypotheses and Assumptions
Main hypothesis:The temporal sequence of memory operations will reveal whether WRAM clearing is occurring before or after writing the `0xFD` flag. If it happens later, then the cleanup is erasing the marker. If it happens before, then the problem is that the marker is never copied to WRAM after writing it to HRAM.
Assumption:We assume that the PC updated before the fetch correctly corresponds to the instruction that is about to be executed. In reality, after the fetch the PC already advances, but as we update before the fetch, the registered PC should correspond to the instruction that is executing memory operation.
Next Steps
- [ ] Run the emulator with output redirection:
python main.py roms/tetris.gb > timeline.log 2>&1 - [ ] Analyze the file
timeline.loglooking for lines with[TIME] - [ ] Reconstruct the temporal sequence: In what order do writes to WRAM, marker, and DMA occur?
- [ ] Identify the PC of each operation to determine what routines these operations are executing
- [ ] Determine if cleanup is deleting the marker or if the marker is never copied to WRAM
- [ ] If necessary, correlate the PCs with the game code (ROM) to understand the program flow