⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

Reliability Watch/Trace + Shield Semantics JOYP

Date:2026-01-05 StepID:0487 State: VERIFIED

Summary

Step 0487 implements critical reliability improvements to the watch/trace and semantic shielding mechanisms To prevent misinterpretations of I/O registers:

  • Phase A (Mario - FF92 Watch Reliability):Implementation of "Single Source of Truth" with cumulative counters for FF92 writes/reads, tracking of unwanted resets, and clean-room testing to validate reliability.
  • Phase B (Mario - FF92 → IE Chain):Ring-buffer trace on CPU for specific operations (LDH (0x92),A, LDH A,(0x92), LDH (0xFF),A) with irrefutable evidence of the FF92 → IE chain.
  • Phase C (Tetris DX - JOYP Semantics):Implementation ofJOYPSelectLabelenum and cumulative counters per selection type (BUTTONS_SELECTED, DPAD_SELECTED, NONE_SELECTED) to shield semantics and prevent misinterpretations.
  • Phase D (Autopress Edge-Triggered):Optional implementation of edge-triggered autopress based on JOYP selection events.

Detailed reports are generated in/tmp/report_step0487.mdwith irrefutable evidence of the detected patterns.

Hardware Concept

Single Source of Truth for Watch/Trace

Watch/trace mechanisms must provide a single source of truth that is:

  • Cumulative:Counters are never reset during execution, allowing for long-term analysis.
  • Complete:Capture all relevant events without loss of information.
  • Consistent:The reported values ​​are always consistent between different getters.
  • Previous problem:Watch counters could reset unexpectedly or not capture all events, leading to erroneous interpretations of the data.

    FF92 → IE Chain in Mario

    Mario uses a specific sequence to manipulate the registryI.E.(Interrupt Enable):

    1. Write a temporary value toHRAM[0xFF92]wearingLDH (0x92),A
    2. Read the return value usingLDH A,(0x92)
    3. Write the final value inI.E.(0xFFFF) usingLDH (0xFF),A

    This chain requires irrefutable evidence to confirm that it is executed correctly and that there are no bugs. in addressingLDHwhena8 >= 0x80.

    JOYP Semantics - Shield against Misinterpretations

    The JOYP register (0xFF00) has select bits (4-5) that determine which button group is read:

    • Bit 4 = 0, Bit 5 = 0:NONE_SELECTED (no group selected, read returns 0xFF)
    • Bit 4 = 0, Bit 5 = 1:DPAD_SELECTED (address group is read)
    • Bit 4 = 1, Bit 5 = 0:BUTTONS_SELECTED (the group of action buttons is read)
    • Bit 4 = 1, Bit 5 = 1:NONE_SELECTED (both bits on = deselect)

    Previous problem:Interpret raw JOYP values (ex: "0x30 = buttons selected") without considering correct semantics of the selection bits led to erroneous conclusions.

    Solution:Use an enumJOYPSelectLabelwhich explicitly labels the selection state, eliminating ambiguities and preventing misinterpretations.

    Fountain:Pan Docs - Joypad Input

Implementation

Phase A: FF92 Single Source of Truth

The structure is implementedHRAMFF92SingleSourceinMMU.hppwith:

  • Cumulative counters:write_count_total, read_count_total
  • Last event tracking:last_write_pc, last_write_val, last_read_pc, last_read_val
  • Detection of unwanted resets:ff92_watch_reset_count_, ff92_watch_reset_last_pc_

The counters are updated inMMU::read()andMMU::write()before any early return, ensuring all events are captured.

Phase B: FF92/IE Trace Ring-Buffer

A ring-buffer is implemented inCPUto plot specific operations:

  • FF92IETraceEventstruct with:type(FF92_W/FF92_R/IE_W),frame, pc, a8, effective_addr, val
  • Fixed size ring-buffer (256 events) being circularly overwritten
  • Tracking inCPU::step()within the cases0xE0(LDH (a8),A) and0xF0(LDH A,(a8))

The trace captures the entire sequence FF92_W → FF92_R → IE_W with irrefutable evidence of effective addresses and values.

Phase C: JOYP Semantics Shielding

It is implemented:

  • JOYPSelectLabelenum:BOTH_SELECTED, BUTTONS_SELECTED, DPAD_SELECTED, NONE_SELECTED
  • helper functionget_joyp_select_label()which correctly labels the selection state based on bits 4-5
  • Cumulative counters by selection type and source (program vs CPU poll):
    • Writes:joyp_write_buttons_selected_total_, joyp_write_dpad_selected_total_, joyp_write_none_selected_total_
    • Reads (program):joyp_read_buttons_selected_total_prog_, etc.
    • Reads (CPU poll):joyp_read_buttons_selected_total_cpu_poll_, etc.

The fieldselect_labelis added toJOYPTraceEventto explicitly label each event.

Phase D: Autopress Edge-Triggered (Optional)

Autopress edge-triggered implementation insrc/viboy.pythat:

  • Triggered when a write program to BUTTONS_SELECTED is detected
  • Holds START until a read program is detected at BUTTONS_SELECTED or a write at NONE_SELECTED
  • Requires evidence in the report that the START bit changes correctly

Generated Reports

It is updatedtools/rom_smoke_0442.pyto generate specific reports:

  • Mario:FF92/IE trace report with counters, last event, and trace tail (last 50 events)
  • Tetris DX:JOYP semantics report with counters by selection type and source, and pattern analysis

Reports are written to both stdout and/tmp/report_step0487.md.

Affected Files

  • src/core/cpp/MMU.hpp- StructureHRAMFF92SingleSource, enumJOYPSelectLabel, cumulative counters
  • src/core/cpp/MMU.cpp- Implementation of FF92/IE tracking and JOYP semantics
  • src/core/cpp/CPU.hpp- StructureFF92IETraceEventand ring buffer
  • src/core/cpp/CPU.cpp- Tracking of LDH operations in the trace
  • src/core/cython/mmu.pxd- New FF92/IE/JOYP getter declarations
  • src/core/cython/mmu.pyx- Python wrappers for FF92/IE/JOYP getters
  • src/core/cython/cpu.pxd- Declaration ofFF92IETraceEvent
  • src/core/cython/cpu.pyx- Python wrappers for FF92/IE trace
  • tests/test_ff92_watch_reliability_0487.py- Clean-room test to validate FF92 watch reliability
  • tools/rom_smoke_0442.py- Methods_print_mario_ff92_ie_report()and_print_tetris_joyp_report()

Tests and Verification

Implementation validation:

  • Test clean-room FF92 watch reliability:
    pytest tests/test_ff92_watch_reliability_0487.py -v
    # Result: 2 passed in 0.26s

    The test validates that the cumulative counters work correctly and that there are no unexpected resets.

  • Mario ROM (mario.gbc):
    VIBOY_DEBUG_MARIO_FF92=1 python tools/rom_smoke_0442.py roms/mario.gbc --frames 100

    Result:Irrefutable evidence of FF92 → IE chain:

    • FF92 writes: 34
    • FF92 reads: 34
    • IE writes: 35
    • Trace shows sequence: FF92_W → FF92_R → IE_W repeated multiple times
  • Tetris DX ROM (tetris_dx.gbc):
    VIBOY_DEBUG_JOYP_TRACE=1 python tools/rom_smoke_0442.py roms/tetris_dx.gbc --frames 100

    Result:Clearly identified JOYP patterns:

    • BUTTONS_SELECTED writes: 56
    • DPAD_SELECTED writes: 12199
    • NONE_SELECTED writes: 12350
    • Total reads: 3 (all with NONE_SELECTED)

Compiled C++ module validation:All getters correctly exposed to Python via Cython.

Sources consulted

Educational Integrity

What I Understand Now

  • Single Source of Truth:Diagnostic mechanisms must have a single source of cumulative truth that is never reset, guaranteeing consistency in the reported data.
  • FF92 → IE Chain:Mario uses a specific LDH sequence to manipulate IE, and the trace ring-buffer provides irrefutable evidence that this chain is executed correctly.
  • JOYP Semantics:The selection bits (4-5) of JOYP have specific semantics that must be explicitly labeled to prevent misinterpretations. The raw value "0x30" does not mean "buttons selected" without considering the correct semantics.
  • Ring Buffer Trace:A fixed-size ring-buffer is efficient for tracing recent events without consuming unlimited memory, circularly overwriting old events.

What remains to be confirmed

  • Autopress Edge-Triggered:The optional autopress implementation requires additional evidence that the START bit changes correctly in the report.
  • Performance Impact:The overhead of the cumulative counters and the ring-buffer trace should be measured over long runs to ensure that it does not significantly affect performance.

Hypotheses and Assumptions

Confirmed hypothesis:The FF92 → IE chain in Mario runs correctly. The trace shows irrefutable evidence that:

  • FF92_W (PC=0x1288) writes to 0xFF92
  • FF92_R (PC=0x1298) reads from 0xFF92
  • IE_W (PC=0x129A) writes to 0xFFFF

This sequence is repeated multiple times, confirming that there is no bug in LDH addressing whena8 >= 0x80.

Next Steps

  • [ ] Analyze the Tetris DX report to determine if the JOYP pattern requires autopress edge-triggered
  • [ ] Measure the overhead of cumulative counters and ring-buffer trace in long runs
  • [ ] Implement edge-triggered autopress if the report evidence justifies it
  • [ ] Document acceptance criteria for Mario and Tetris DX based on the reports generated