diff --git a/src/gba/io.c b/src/gba/io.c index b70411094..ea2e1dc8d 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -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; }