From 1636078b34195b9801748eed68a7641771b7ef96 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 20 Sep 2024 03:12:06 -0700 Subject: [PATCH] GBA I/O: Fix audio register 8-bit write behavior (fixes #3086) --- CHANGES | 1 + src/gba/io.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index f225b8b15..9a1694d69 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Emulation fixes: - GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) + - GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086) - GBA Serialize: Fix some minor save state edge cases - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) diff --git a/src/gba/io.c b/src/gba/io.c index 397eb9253..8853e4d6f 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -326,17 +326,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case GBA_REG_SOUND1CNT_HI: GBAAudioWriteSOUND1CNT_HI(&gba->audio, value); + value &= 0xFFC0; break; case GBA_REG_SOUND1CNT_X: GBAAudioWriteSOUND1CNT_X(&gba->audio, value); - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND2CNT_LO: GBAAudioWriteSOUND2CNT_LO(&gba->audio, value); + value &= 0xFFC0; break; case GBA_REG_SOUND2CNT_HI: GBAAudioWriteSOUND2CNT_HI(&gba->audio, value); - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND3CNT_LO: GBAAudioWriteSOUND3CNT_LO(&gba->audio, value); @@ -344,16 +346,15 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case GBA_REG_SOUND3CNT_HI: GBAAudioWriteSOUND3CNT_HI(&gba->audio, value); - value &= 0xE03F; + value &= 0xE000; break; case GBA_REG_SOUND3CNT_X: GBAAudioWriteSOUND3CNT_X(&gba->audio, value); - // TODO: The low bits need to not be readable, but still 8-bit writable - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND4CNT_LO: GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); - value &= 0xFF3F; + value &= 0xFF00; break; case GBA_REG_SOUND4CNT_HI: GBAAudioWriteSOUND4CNT_HI(&gba->audio, value); @@ -585,9 +586,96 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { if (address > GBA_SIZE_IO) { return; } - uint16_t value16 = value << (8 * (address & 1)); - value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); - GBAIOWrite(gba, address & 0xFFFFFFFE, value16); + uint16_t value16; + + switch (address) { + case GBA_REG_SOUND1CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR11(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0xFF00; + gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value & 0xC0; + break; + case GBA_REG_SOUND1CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR12(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0x00C0; + gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value << 8; + break; + case GBA_REG_SOUND1CNT_X: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR13(&gba->audio.psg, value); + break; + case GBA_REG_SOUND1CNT_X + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR14(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_X)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND2CNT_LO: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR21(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0xFF00; + gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value & 0xC0; + break; + case GBA_REG_SOUND2CNT_LO + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR22(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0x00C0; + gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value << 8; + break; + case GBA_REG_SOUND2CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR23(&gba->audio.psg, value); + break; + case GBA_REG_SOUND2CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR24(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_HI)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND3CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR31(&gba->audio.psg, value); + break; + case GBA_REG_SOUND3CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + gba->audio.psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value); + gba->memory.io[GBA_REG(SOUND3CNT_HI)] = (value & 0xE0) << 8; + break; + case GBA_REG_SOUND3CNT_X: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR33(&gba->audio.psg, value); + break; + case GBA_REG_SOUND3CNT_X + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR34(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND3CNT_X)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND4CNT_LO: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR41(&gba->audio.psg, value); + break; + case GBA_REG_SOUND4CNT_LO + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR42(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_LO)] = value << 8; + break; + case GBA_REG_SOUND4CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR43(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x4000; + gba->memory.io[GBA_REG(SOUND4CNT_HI)] |= value; + break; + case GBA_REG_SOUND4CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR44(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x00FF; + gba->memory.io[GBA_REG(SOUND4CNT_HI)] = (value & 0x40) << 8; + break; + default: + value16 = value << (8 * (address & 1)); + value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); + GBAIOWrite(gba, address & 0xFFFFFFFE, value16); + break; + } } void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {