diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gb.c | 16 | ||||
| -rw-r--r-- | src/gb.h | 5 |
2 files changed, 19 insertions, 2 deletions
@@ -64,7 +64,19 @@ u8 gb_read(GB *gb, u16 addr) { if (addr == 0xFF02) return gb->sc | 0x7E; if (addr >= 0xFF04 && addr <= 0xFF07) return timer_read(gb, addr); if (addr == 0xFF0F) return gb->iff | 0xE0; - if (addr >= 0xFF10 && addr <= 0xFF3F) return 0xFF; // APU (stub) + if (addr >= 0xFF10 && addr <= 0xFF3F) { + // Register-level APU: return stored value OR'd with the bits that read + // back as 1 on real hardware (write-only / unused bits). NR50 (0xFF24) + // has mask 0x00, so it reads back exactly what was written, which is + // what the game's volume fade-out relies on. + static const u8 apu_or[0x30] = { + 0x80,0x3F,0x00,0xFF,0xBF, 0xFF, 0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, 0xFF, 0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* FF30-FF3F wave RAM: exact */ + }; + return gb->apu[addr - 0xFF10] | apu_or[addr - 0xFF10]; + } if (addr >= 0xFF40 && addr <= 0xFF4B) { if (addr == 0xFF46) return gb->dma; return ppu_read_reg(gb, addr); @@ -114,7 +126,7 @@ void gb_write(GB *gb, u16 addr, u8 val) { } if (addr >= 0xFF04 && addr <= 0xFF07) { timer_write(gb, addr, val); return; } if (addr == 0xFF0F) { gb->iff = val & 0x1F; return; } - if (addr >= 0xFF10 && addr <= 0xFF3F) return; // APU stub + if (addr >= 0xFF10 && addr <= 0xFF3F) { gb->apu[addr - 0xFF10] = val; return; } if (addr >= 0xFF40 && addr <= 0xFF4B) { if (addr == 0xFF46) { oam_dma(gb, val); return; } ppu_write_reg(gb, addr, val); @@ -94,6 +94,11 @@ struct GB { // serial u8 sb, sc; + // APU registers 0xFF10-0xFF3F. We don't synthesize sound, but games read + // these back (e.g. the music fade-out decrements NR50/rAUDVOL until it + // reads 0), so they must behave as real storage, not a 0xFF black hole. + u8 apu[0x30]; + // DMA u8 dma; |
