GBA Audio: Prevent write to audio registers if audio is OFF On hardware, if audio is OFF it isn't possible to write to registers at addresses 0x4000060h to 0x4000081 (inclusive).
Antonio Niño Díaz antonio_nd@outlook.com
Sun, 21 Mar 2021 18:31:06 +0000
1 files changed,
231 insertions(+),
225 deletions(-)
jump to
M
src/gba/io.c
→
src/gba/io.c
@@ -342,248 +342,254 @@ }
void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { if (address < REG_SOUND1CNT_LO && (address > REG_VCOUNT || address < REG_DISPSTAT)) { - value = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value); - } else { - switch (address) { - // Video - case REG_DISPSTAT: - value &= 0xFFF8; - GBAVideoWriteDISPSTAT(&gba->video, value); - return; + gba->memory.io[address >> 1] = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value); + return; + } - case REG_VCOUNT: - mLOG(GBA_IO, GAME_ERROR, "Write to read-only I/O register: %03X", address); - return; + if (address >= REG_SOUND1CNT_LO && address <= REG_SOUNDCNT_LO && !gba->audio.enable) { + // Ignore writes to most audio registers if the hardware is off. + return; + } - // Audio - case REG_SOUND1CNT_LO: - GBAAudioWriteSOUND1CNT_LO(&gba->audio, value); - value &= 0x007F; - break; - case REG_SOUND1CNT_HI: - GBAAudioWriteSOUND1CNT_HI(&gba->audio, value); - break; - case REG_SOUND1CNT_X: - GBAAudioWriteSOUND1CNT_X(&gba->audio, value); - value &= 0x47FF; - break; - case REG_SOUND2CNT_LO: - GBAAudioWriteSOUND2CNT_LO(&gba->audio, value); - break; - case REG_SOUND2CNT_HI: - GBAAudioWriteSOUND2CNT_HI(&gba->audio, value); - value &= 0x47FF; - break; - case REG_SOUND3CNT_LO: - GBAAudioWriteSOUND3CNT_LO(&gba->audio, value); - value &= 0x00E0; - break; - case REG_SOUND3CNT_HI: - GBAAudioWriteSOUND3CNT_HI(&gba->audio, value); - value &= 0xE03F; - break; - case REG_SOUND3CNT_X: - GBAAudioWriteSOUND3CNT_X(&gba->audio, value); - // TODO: The low bits need to not be readable, but still 8-bit writable - value &= 0x47FF; - break; - case REG_SOUND4CNT_LO: - GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); - value &= 0xFF3F; - break; - case REG_SOUND4CNT_HI: - GBAAudioWriteSOUND4CNT_HI(&gba->audio, value); - value &= 0x40FF; - break; - case REG_SOUNDCNT_LO: - GBAAudioWriteSOUNDCNT_LO(&gba->audio, value); - value &= 0xFF77; - break; - case REG_SOUNDCNT_HI: - GBAAudioWriteSOUNDCNT_HI(&gba->audio, value); - value &= 0x770F; - break; - case REG_SOUNDCNT_X: - GBAAudioWriteSOUNDCNT_X(&gba->audio, value); - value &= 0x0080; - value |= gba->memory.io[REG_SOUNDCNT_X >> 1] & 0xF; - break; - case REG_SOUNDBIAS: - GBAAudioWriteSOUNDBIAS(&gba->audio, value); - break; + switch (address) { + // Video + case REG_DISPSTAT: + value &= 0xFFF8; + GBAVideoWriteDISPSTAT(&gba->video, value); + return; - case REG_WAVE_RAM0_LO: - case REG_WAVE_RAM1_LO: - case REG_WAVE_RAM2_LO: - case REG_WAVE_RAM3_LO: - GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); - break; + case REG_VCOUNT: + mLOG(GBA_IO, GAME_ERROR, "Write to read-only I/O register: %03X", address); + return; - case REG_WAVE_RAM0_HI: - case REG_WAVE_RAM1_HI: - case REG_WAVE_RAM2_HI: - case REG_WAVE_RAM3_HI: - GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); - break; + // Audio + case REG_SOUND1CNT_LO: + GBAAudioWriteSOUND1CNT_LO(&gba->audio, value); + value &= 0x007F; + break; + case REG_SOUND1CNT_HI: + GBAAudioWriteSOUND1CNT_HI(&gba->audio, value); + break; + case REG_SOUND1CNT_X: + GBAAudioWriteSOUND1CNT_X(&gba->audio, value); + value &= 0x47FF; + break; + case REG_SOUND2CNT_LO: + GBAAudioWriteSOUND2CNT_LO(&gba->audio, value); + break; + case REG_SOUND2CNT_HI: + GBAAudioWriteSOUND2CNT_HI(&gba->audio, value); + value &= 0x47FF; + break; + case REG_SOUND3CNT_LO: + GBAAudioWriteSOUND3CNT_LO(&gba->audio, value); + value &= 0x00E0; + break; + case REG_SOUND3CNT_HI: + GBAAudioWriteSOUND3CNT_HI(&gba->audio, value); + value &= 0xE03F; + break; + case REG_SOUND3CNT_X: + GBAAudioWriteSOUND3CNT_X(&gba->audio, value); + // TODO: The low bits need to not be readable, but still 8-bit writable + value &= 0x47FF; + break; + case REG_SOUND4CNT_LO: + GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); + value &= 0xFF3F; + break; + case REG_SOUND4CNT_HI: + GBAAudioWriteSOUND4CNT_HI(&gba->audio, value); + value &= 0x40FF; + break; + case REG_SOUNDCNT_LO: + GBAAudioWriteSOUNDCNT_LO(&gba->audio, value); + value &= 0xFF77; + break; + case REG_SOUNDCNT_HI: + GBAAudioWriteSOUNDCNT_HI(&gba->audio, value); + value &= 0x770F; + break; + case REG_SOUNDCNT_X: + GBAAudioWriteSOUNDCNT_X(&gba->audio, value); + value &= 0x0080; + value |= gba->memory.io[REG_SOUNDCNT_X >> 1] & 0xF; + break; + case REG_SOUNDBIAS: + GBAAudioWriteSOUNDBIAS(&gba->audio, value); + break; - case REG_FIFO_A_LO: - case REG_FIFO_B_LO: - GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); - return; + case REG_WAVE_RAM0_LO: + case REG_WAVE_RAM1_LO: + case REG_WAVE_RAM2_LO: + case REG_WAVE_RAM3_LO: + GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); + break; - case REG_FIFO_A_HI: - case REG_FIFO_B_HI: - GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); - return; + case REG_WAVE_RAM0_HI: + case REG_WAVE_RAM1_HI: + case REG_WAVE_RAM2_HI: + case REG_WAVE_RAM3_HI: + GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); + break; - // DMA - case REG_DMA0SAD_LO: - case REG_DMA0DAD_LO: - case REG_DMA1SAD_LO: - case REG_DMA1DAD_LO: - case REG_DMA2SAD_LO: - case REG_DMA2DAD_LO: - case REG_DMA3SAD_LO: - case REG_DMA3DAD_LO: - GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); - break; + case REG_FIFO_A_LO: + case REG_FIFO_B_LO: + GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); + return; - case REG_DMA0SAD_HI: - case REG_DMA0DAD_HI: - case REG_DMA1SAD_HI: - case REG_DMA1DAD_HI: - case REG_DMA2SAD_HI: - case REG_DMA2DAD_HI: - case REG_DMA3SAD_HI: - case REG_DMA3DAD_HI: - GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); - break; + case REG_FIFO_A_HI: + case REG_FIFO_B_HI: + GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); + return; - case REG_DMA0CNT_LO: - GBADMAWriteCNT_LO(gba, 0, value & 0x3FFF); - break; - case REG_DMA0CNT_HI: - value = GBADMAWriteCNT_HI(gba, 0, value); - break; - case REG_DMA1CNT_LO: - GBADMAWriteCNT_LO(gba, 1, value & 0x3FFF); - break; - case REG_DMA1CNT_HI: - value = GBADMAWriteCNT_HI(gba, 1, value); - break; - case REG_DMA2CNT_LO: - GBADMAWriteCNT_LO(gba, 2, value & 0x3FFF); - break; - case REG_DMA2CNT_HI: - value = GBADMAWriteCNT_HI(gba, 2, value); - break; - case REG_DMA3CNT_LO: - GBADMAWriteCNT_LO(gba, 3, value); - break; - case REG_DMA3CNT_HI: - value = GBADMAWriteCNT_HI(gba, 3, value); - break; + // DMA + case REG_DMA0SAD_LO: + case REG_DMA0DAD_LO: + case REG_DMA1SAD_LO: + case REG_DMA1DAD_LO: + case REG_DMA2SAD_LO: + case REG_DMA2DAD_LO: + case REG_DMA3SAD_LO: + case REG_DMA3DAD_LO: + GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value); + break; - // Timers - case REG_TM0CNT_LO: - GBATimerWriteTMCNT_LO(gba, 0, value); - return; - case REG_TM1CNT_LO: - GBATimerWriteTMCNT_LO(gba, 1, value); - return; - case REG_TM2CNT_LO: - GBATimerWriteTMCNT_LO(gba, 2, value); - return; - case REG_TM3CNT_LO: - GBATimerWriteTMCNT_LO(gba, 3, value); - return; + case REG_DMA0SAD_HI: + case REG_DMA0DAD_HI: + case REG_DMA1SAD_HI: + case REG_DMA1DAD_HI: + case REG_DMA2SAD_HI: + case REG_DMA2DAD_HI: + case REG_DMA3SAD_HI: + case REG_DMA3DAD_HI: + GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16)); + break; - case REG_TM0CNT_HI: - value &= 0x00C7; - GBATimerWriteTMCNT_HI(gba, 0, value); - break; - case REG_TM1CNT_HI: - value &= 0x00C7; - GBATimerWriteTMCNT_HI(gba, 1, value); - break; - case REG_TM2CNT_HI: - value &= 0x00C7; - GBATimerWriteTMCNT_HI(gba, 2, value); - break; - case REG_TM3CNT_HI: - value &= 0x00C7; - GBATimerWriteTMCNT_HI(gba, 3, value); - break; + case REG_DMA0CNT_LO: + GBADMAWriteCNT_LO(gba, 0, value & 0x3FFF); + break; + case REG_DMA0CNT_HI: + value = GBADMAWriteCNT_HI(gba, 0, value); + break; + case REG_DMA1CNT_LO: + GBADMAWriteCNT_LO(gba, 1, value & 0x3FFF); + break; + case REG_DMA1CNT_HI: + value = GBADMAWriteCNT_HI(gba, 1, value); + break; + case REG_DMA2CNT_LO: + GBADMAWriteCNT_LO(gba, 2, value & 0x3FFF); + break; + case REG_DMA2CNT_HI: + value = GBADMAWriteCNT_HI(gba, 2, value); + break; + case REG_DMA3CNT_LO: + GBADMAWriteCNT_LO(gba, 3, value); + break; + case REG_DMA3CNT_HI: + value = GBADMAWriteCNT_HI(gba, 3, value); + break; - // SIO - case REG_SIOCNT: - GBASIOWriteSIOCNT(&gba->sio, value); - break; - case REG_RCNT: - value &= 0xC1FF; - GBASIOWriteRCNT(&gba->sio, value); - break; - case REG_JOY_TRANS_LO: - case REG_JOY_TRANS_HI: - gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT; - // Fall through - case REG_SIODATA32_LO: - case REG_SIODATA32_HI: - case REG_SIOMLT_SEND: - case REG_JOYCNT: - case REG_JOYSTAT: - case REG_JOY_RECV_LO: - case REG_JOY_RECV_HI: - value = GBASIOWriteRegister(&gba->sio, address, value); - break; + // Timers + case REG_TM0CNT_LO: + GBATimerWriteTMCNT_LO(gba, 0, value); + return; + case REG_TM1CNT_LO: + GBATimerWriteTMCNT_LO(gba, 1, value); + return; + case REG_TM2CNT_LO: + GBATimerWriteTMCNT_LO(gba, 2, value); + return; + case REG_TM3CNT_LO: + GBATimerWriteTMCNT_LO(gba, 3, value); + return; - // Interrupts and misc - case REG_KEYCNT: - value &= 0xC3FF; - gba->memory.io[address >> 1] = value; - GBATestKeypadIRQ(gba); - return; - case REG_WAITCNT: - value &= 0x5FFF; - GBAAdjustWaitstates(gba, value); - break; - case REG_IE: - gba->memory.io[REG_IE >> 1] = value; - GBATestIRQ(gba, 1); - return; - case REG_IF: - value = gba->memory.io[REG_IF >> 1] & ~value; - gba->memory.io[REG_IF >> 1] = value; - GBATestIRQ(gba, 1); + case REG_TM0CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 0, value); + break; + case REG_TM1CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 1, value); + break; + case REG_TM2CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 2, value); + break; + case REG_TM3CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 3, value); + break; + + // SIO + case REG_SIOCNT: + GBASIOWriteSIOCNT(&gba->sio, value); + break; + case REG_RCNT: + value &= 0xC1FF; + GBASIOWriteRCNT(&gba->sio, value); + break; + case REG_JOY_TRANS_LO: + case REG_JOY_TRANS_HI: + gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT; + // Fall through + case REG_SIODATA32_LO: + case REG_SIODATA32_HI: + case REG_SIOMLT_SEND: + case REG_JOYCNT: + case REG_JOYSTAT: + case REG_JOY_RECV_LO: + case REG_JOY_RECV_HI: + value = GBASIOWriteRegister(&gba->sio, address, value); + break; + + // Interrupts and misc + case REG_KEYCNT: + value &= 0xC3FF; + gba->memory.io[address >> 1] = value; + GBATestKeypadIRQ(gba); + return; + case REG_WAITCNT: + value &= 0x5FFF; + GBAAdjustWaitstates(gba, value); + break; + case REG_IE: + gba->memory.io[REG_IE >> 1] = value; + GBATestIRQ(gba, 1); + return; + case REG_IF: + value = gba->memory.io[REG_IF >> 1] & ~value; + gba->memory.io[REG_IF >> 1] = value; + GBATestIRQ(gba, 1); + return; + case REG_IME: + gba->memory.io[REG_IME >> 1] = value & 1; + GBATestIRQ(gba, 1); + return; + case REG_MAX: + // Some bad interrupt libraries will write to this + break; + case REG_DEBUG_ENABLE: + gba->debug = value == 0xC0DE; + return; + case REG_DEBUG_FLAGS: + if (gba->debug) { + GBADebug(gba, value); return; - case REG_IME: - gba->memory.io[REG_IME >> 1] = value & 1; - GBATestIRQ(gba, 1); + } + // Fall through + default: + if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { + STORE_16LE(value, address - REG_DEBUG_STRING, gba->debugString); return; - case REG_MAX: - // Some bad interrupt libraries will write to this - break; - case REG_DEBUG_ENABLE: - gba->debug = value == 0xC0DE; + } + mLOG(GBA_IO, STUB, "Stub I/O register write: %03X", address); + if (address >= REG_MAX) { + mLOG(GBA_IO, GAME_ERROR, "Write to unused I/O register: %03X", address); return; - case REG_DEBUG_FLAGS: - if (gba->debug) { - GBADebug(gba, value); - return; - } - // Fall through - default: - if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { - STORE_16LE(value, address - REG_DEBUG_STRING, gba->debugString); - return; - } - mLOG(GBA_IO, STUB, "Stub I/O register write: %03X", address); - if (address >= REG_MAX) { - mLOG(GBA_IO, GAME_ERROR, "Write to unused I/O register: %03X", address); - return; - } - break; } + break; } gba->memory.io[address >> 1] = value; }