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).
This commit is contained in:
Antonio Niño Díaz 2021-03-21 18:31:06 +00:00 committed by Vicki Pfau
parent c0cfa602af
commit aec8ef45ef
1 changed files with 233 additions and 227 deletions

View File

@ -342,248 +342,254 @@ void GBAIOInit(struct GBA* gba) {
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);
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_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;
}
// 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;
}
// 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;
}
gba->memory.io[address >> 1] = value;
}