355 lines
11 KiB
Markdown
355 lines
11 KiB
Markdown
# Emulator Regressions - November 2025
|
|
|
|
## Status: UNRESOLVED
|
|
|
|
Two regressions have been identified in the SNES emulator that affect:
|
|
1. Input handling (A button not working on file naming screen)
|
|
2. PPU rendering (title screen BG layer not showing)
|
|
|
|
**Note**: Keybindings system is currently being modified by another agent. Changes may interact.
|
|
|
|
---
|
|
|
|
## Issue 1: Input Button Mapping Bug
|
|
|
|
### Symptoms
|
|
- A button does not work on the ALTTP file naming screen
|
|
- D-pad works correctly
|
|
- A button works on title screen (different code path?)
|
|
|
|
### Root Cause Analysis
|
|
|
|
**Bug Location**: `src/app/emu/snes.cc:763`
|
|
|
|
```cpp
|
|
void Snes::SetButtonState(int player, int button, bool pressed) {
|
|
// BUG: This logic is inverted!
|
|
Input* input = (player == 1) ? &input1 : &input2;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
When calling `SetButtonState(0, button, true)` (player 0 = player 1 in SNES terms), it incorrectly selects `input2` instead of `input1`.
|
|
|
|
**Introduced in**: Commit `9ffb7803f5` (Oct 11, 2025)
|
|
- "refactor(emulator): enhance input handling and audio resampling features"
|
|
|
|
### Attempted Fix
|
|
|
|
In this session, we updated the button constants in `save_state_manager.h` from bitmasks to bit indices:
|
|
```cpp
|
|
// Before (incorrect for SetButtonState API):
|
|
constexpr uint16_t kA = 0x0080; // Bitmask
|
|
|
|
// After (correct bit index):
|
|
constexpr int kA = 8; // Bit index
|
|
```
|
|
|
|
However, this fix alone doesn't resolve the issue because `SetButtonState` itself has the player mapping inverted.
|
|
|
|
### Proposed Fix
|
|
|
|
Change line 763 in `snes.cc`:
|
|
```cpp
|
|
// Current (wrong):
|
|
Input* input = (player == 1) ? &input1 : &input2;
|
|
|
|
// Should be:
|
|
Input* input = (player == 0) ? &input1 : &input2;
|
|
```
|
|
|
|
Or alternatively, to match common conventions (player 1 = first player):
|
|
```cpp
|
|
Input* input = (player <= 1) ? &input1 : &input2;
|
|
```
|
|
|
|
### Additional Notes
|
|
|
|
The title screen may work because it uses a different input reading path or auto-joypad read timing that happens to work despite the bug.
|
|
|
|
---
|
|
|
|
## Issue 2: PPU Title Screen BG Layer Not Rendering
|
|
|
|
### Symptoms
|
|
- Title screen background layer(s) not showing
|
|
- Timing unclear - may have been introduced in recent commits
|
|
|
|
### Potential Root Cause
|
|
|
|
**Commit**: `e37497e9ef` (Nov 23, 2025)
|
|
- "feat(emu): add PPU JIT catch-up for mid-scanline raster effects"
|
|
|
|
This commit refactored PPU rendering from a simple `RunLine()` call to a progressive JIT system:
|
|
|
|
```cpp
|
|
// Old approach:
|
|
ppu_.RunLine(line); // Render entire line at once
|
|
|
|
// New approach:
|
|
ppu_.StartLine(line); // Setup for line
|
|
ppu_.CatchUp(512); // Render first half
|
|
ppu_.CatchUp(1104); // Render second half
|
|
```
|
|
|
|
### Key Changes to Investigate
|
|
|
|
1. **StartLine() timing**: Now called at H=0 instead of H=512
|
|
- `StartLine()` does sprite evaluation and mode 7 setup
|
|
- May need to be called earlier or with different conditions
|
|
|
|
2. **CatchUp() vs RunLine()**: The new progressive rendering may have edge cases
|
|
- `CatchUp(512)` renders pixels 0-127
|
|
- `CatchUp(1104)` should render pixels 128-255
|
|
- But 1104/4 = 276, so it tries to render up to 256 (clamped)
|
|
|
|
3. **WriteBBus PPU catch-up**: Added mid-scanline PPU register write handling
|
|
- May interfere with normal rendering sequence
|
|
|
|
### Files Changed in PPU Refactor
|
|
|
|
- `src/app/emu/video/ppu.cc`: Added `StartLine()`, `CatchUp()`, `last_rendered_x_`
|
|
- `src/app/emu/video/ppu.h`: Added new method declarations
|
|
- `src/app/emu/snes.cc`: Changed `RunLine()` calls to `StartLine()`/`CatchUp()`
|
|
|
|
### Key Timing Difference
|
|
|
|
**Before PPU JIT (commit e37497e9ef~1)**:
|
|
```cpp
|
|
case 512: {
|
|
if (!in_vblank_ && memory_.v_pos() > 0)
|
|
ppu_.RunLine(memory_.v_pos()); // Everything at H=512
|
|
}
|
|
```
|
|
|
|
**After PPU JIT**:
|
|
```cpp
|
|
case 16: {
|
|
ppu_.StartLine(memory_.v_pos()); // Sprite eval at H=16
|
|
}
|
|
case 512: {
|
|
ppu_.CatchUp(512); // Pixels 0-127 at H=512
|
|
}
|
|
case 1104: {
|
|
ppu_.CatchUp(1104); // Pixels 128-255 at H=1104
|
|
}
|
|
```
|
|
|
|
The sprite evaluation (`EvaluateSprites`) now happens at H=16 instead of H=512. This timing change could affect games that modify OAM or PPU registers via HDMA between H=16 and H=512.
|
|
|
|
### Quick Test: Revert to Old PPU Timing
|
|
|
|
To test if the PPU JIT is causing the issue, temporarily revert to `RunLine()`:
|
|
|
|
In `src/app/emu/snes.cc`, change the case 16 and 512 blocks:
|
|
|
|
```cpp
|
|
case 16: {
|
|
next_horiz_event = 512;
|
|
if (memory_.v_pos() == 0)
|
|
memory_.init_hdma_request();
|
|
// Remove StartLine call
|
|
} break;
|
|
case 512: {
|
|
next_horiz_event = 1104;
|
|
if (!in_vblank_ && memory_.v_pos() > 0)
|
|
ppu_.RunLine(memory_.v_pos()); // Back to old method
|
|
} break;
|
|
case 1104: {
|
|
// Remove CatchUp call
|
|
if (!in_vblank_)
|
|
memory_.run_hdma_request();
|
|
// ... rest unchanged
|
|
```
|
|
|
|
### Debugging Steps
|
|
|
|
1. Add logging to PPU to verify:
|
|
- Is `StartLine()` being called for each visible scanline?
|
|
- Is `CatchUp()` rendering all 256 pixels?
|
|
- Are any BG enable flags being cleared unexpectedly?
|
|
|
|
2. Test reverting PPU changes:
|
|
```bash
|
|
git checkout e37497e9ef~1 -- src/app/emu/video/ppu.cc src/app/emu/video/ppu.h src/app/emu/snes.cc
|
|
```
|
|
|
|
3. Compare title screen behavior before and after commit `e37497e9ef`
|
|
|
|
---
|
|
|
|
## Git History Reference
|
|
|
|
### Key Commits (Chronological)
|
|
|
|
| Date | Commit | Description |
|
|
|------|--------|-------------|
|
|
| Oct 11, 2025 | `9ffb7803f5` | Input handling refactor - introduced player mapping bug |
|
|
| Nov 23, 2025 | `e37497e9ef` | PPU JIT catch-up - potential BG rendering regression |
|
|
| Nov 25, 2025 | `9d788fe6b0` | Lazy SNES init - may affect startup timing |
|
|
| Nov 26, 2025 | (this session) | SaveStateManager button constant fix |
|
|
|
|
### Commands to Investigate
|
|
|
|
```bash
|
|
# View input handling changes
|
|
git show 9ffb7803f5 -- src/app/emu/snes.cc
|
|
|
|
# View PPU changes
|
|
git show e37497e9ef -- src/app/emu/video/ppu.cc src/app/emu/snes.cc
|
|
|
|
# Diff current vs before PPU JIT
|
|
git diff e37497e9ef~1..HEAD -- src/app/emu/video/ppu.cc
|
|
|
|
# Test with old PPU code
|
|
git stash
|
|
git checkout e37497e9ef~1 -- src/app/emu/video/ppu.cc src/app/emu/video/ppu.h
|
|
cmake --build build --target yaze
|
|
# Test emulator, then restore:
|
|
git checkout HEAD -- src/app/emu/video/ppu.cc src/app/emu/video/ppu.h
|
|
git stash pop
|
|
```
|
|
|
|
---
|
|
|
|
## Attempted Fixes (Did Not Resolve)
|
|
|
|
### Session 2025-11-26
|
|
|
|
1. **Button constants fix** (`save_state_manager.h`)
|
|
- Changed from bitmasks to bit indices
|
|
- Status: Applied, did not fix input issue
|
|
|
|
2. **SetButtonState player mapping** (`snes.cc:763`)
|
|
- Changed `player == 1` to `player <= 1`
|
|
- Status: Applied, did not fix input issue
|
|
|
|
3. **PPU JIT revert** (`snes.cc`)
|
|
- Reverted StartLine/CatchUp back to RunLine
|
|
- Status: Applied, did not fix BG layer issue
|
|
|
|
## Investigation Session 2025-11-26 (New Findings)
|
|
|
|
### Input Bug Analysis
|
|
|
|
**SetButtonState is now correct** (`snes.cc:750`):
|
|
```cpp
|
|
Input* input = (player <= 1) ? &input1 : &input2;
|
|
```
|
|
|
|
**Debug logging already exists** in HandleInput():
|
|
- Logs when A button is active in `current_state_`
|
|
- Logs `port_auto_read_[0]` value after auto-joypad read
|
|
|
|
**CRITICAL SUSPECT: ImGui WantTextInput blocking**
|
|
|
|
In `src/app/emu/input/sdl3_input_backend.cc:67-73`:
|
|
```cpp
|
|
if (io.WantTextInput) {
|
|
static int text_input_log_count = 0;
|
|
if (text_input_log_count++ < 5) {
|
|
LOG_DEBUG("InputBackend", "Blocking game input - WantTextInput=true");
|
|
}
|
|
return ControllerState{}; // <-- ALL input blocked!
|
|
}
|
|
```
|
|
|
|
If ANY ImGui text input widget is active, ALL game input is blocked. This could explain:
|
|
- Why D-pad works but A doesn't → unlikely, would block both
|
|
- Why title screen works but naming screen doesn't → possible if yaze UI has text field active
|
|
|
|
**Diagnostic**: Check if "Blocking game input - WantTextInput=true" appears in logs when on naming screen.
|
|
|
|
### PPU Bug Analysis
|
|
|
|
**CRITICAL FINDING: "Revert" was incomplete**
|
|
|
|
Current `snes.cc:214` calls `ppu_.RunLine()`:
|
|
```cpp
|
|
case 512: {
|
|
next_horiz_event = 1104;
|
|
if (!in_vblank_ && memory_.v_pos() > 0)
|
|
ppu_.RunLine(memory_.v_pos()); // Looks like old code
|
|
}
|
|
```
|
|
|
|
BUT `RunLine()` in `ppu.cc:174-178` now calls the JIT mechanism:
|
|
```cpp
|
|
void Ppu::RunLine(int line) {
|
|
// Legacy wrapper - renders the whole line at once
|
|
StartLine(line); // <-- Uses new JIT setup
|
|
CatchUp(2000); // <-- Uses new JIT rendering
|
|
}
|
|
```
|
|
|
|
**Original `RunLine()` was a direct loop** (before e37497e9ef):
|
|
```cpp
|
|
void Ppu::RunLine(int line) {
|
|
obj_pixel_buffer_.fill(0);
|
|
if (!forced_blank_) EvaluateSprites(line - 1);
|
|
if (mode == 7) CalculateMode7Starts(line);
|
|
for (int x = 0; x < 256; x++) {
|
|
HandlePixel(x, line); // Direct loop, no JIT state
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key Difference**:
|
|
- Old: Uses `line` parameter directly in `HandlePixel(x, line)`
|
|
- New: Uses member variable `current_scanline_` set by `StartLine()`
|
|
|
|
**Potential Bug**: If `current_scanline_` or `last_rendered_x_` have stale/incorrect values, rendering breaks.
|
|
|
|
**TRUE REVERT Required**: To test if JIT is the cause, must restore the original `ppu.cc` implementation, not just the `snes.cc` call sites.
|
|
|
|
---
|
|
|
|
## Investigation Session 2025-11-27 (snes-emulator-expert)
|
|
|
|
### PPU State Check (current dirty tree)
|
|
- `ppu.cc` has already been changed back to the legacy full-line renderer inside `RunLine()` (StartLine/CatchUp still exist but are unused). The earlier suspicion that the wrapper itself was blanking the BG no longer applies.
|
|
- `snes.cc` only calls `RunLine()` once per scanline at H=512; there are no remaining PPU catch-up hooks in `WriteBBus`, so the JIT path is effectively dead code right now.
|
|
|
|
### Runtime Observation (yaze_emu_trace.log)
|
|
- Headless run shows the CPU stuck in the SPC handshake loop at `$00:88B6` (`CMP.w APUIO0` / `BNE .wait_for_zero`), with NMIs never enabled in the first 120 frames.
|
|
- If the SPC handshake never completes, the game never uploads title-screen VRAM/CGRAM or enables 212C/212D, so the blank BG may be a fallout of stalled boot rather than a renderer defect.
|
|
|
|
### Next Steps (PPU-focused)
|
|
- First, confirm the SPC handshake completes (APUIO0 transitions off zero) so the game can reach module `0x01`; otherwise any PPU checks are moot.
|
|
- After the handshake, instrument `RunLine` (e.g., when `line==100`) to log `forced_blank_`, `mode`, and `layer_[i].mainScreenEnabled` to ensure BGs are actually enabled on the title frame.
|
|
- If layers are enabled but BG still missing, capture VRAM around the title tilemap upload to ensure DMA is populating the expected addresses.
|
|
|
|
---
|
|
|
|
## Updated Next Steps
|
|
|
|
### Priority 1: Input Bug
|
|
- [ ] Check logs for "Blocking game input - WantTextInput=true" message
|
|
- [ ] Verify if any ImGui InputText widget is active during emulation
|
|
- [ ] Test with `WantTextInput` check temporarily removed
|
|
- [ ] Trace: SDL key state → Poll() → SetButtonState() → HandleInput()
|
|
|
|
### Priority 2: PPU Bug
|
|
- [ ] **TRUE revert test**: Restore original `ppu.cc` from `e37497e9ef~1`
|
|
```bash
|
|
git show e37497e9ef~1:src/app/emu/video/ppu.cc > /tmp/old_ppu.cc
|
|
# Compare and apply the old RunLine() implementation
|
|
```
|
|
- [ ] Add logging to verify `current_scanline_` and `last_rendered_x_` values
|
|
- [ ] Check layer enable flags (`layer_[i].mainScreenEnabled`) during title screen
|
|
- [ ] Verify VRAM contains tile data
|
|
|
|
### Priority 3: General
|
|
- [ ] **Git bisect** to find exact commit where emulator last worked
|
|
- [ ] Coordinate with keybindings agent work
|
|
|
|
## Potentially Relevant Commits
|
|
|
|
| Commit | Date | Description |
|
|
|--------|------|-------------|
|
|
| `0579fc2c65` | Earlier | Implement input management system with SDL2 |
|
|
| `9ffb7803f5` | Oct 11 | Enhance input handling (introduced SetButtonState) |
|
|
| `2f0006ac0b` | Later | SDL compatibility layer |
|
|
| `a5dc884612` | Later | SDL3 backend infrastructure |
|
|
| `e37497e9ef` | Nov 23 | PPU JIT catch-up (reverted) |
|