in cores/gba/src/gba/io.c [344:585]
void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
if (address < REG_SOUND1CNT_LO && (address > REG_VCOUNT || address == REG_DISPCNT)) {
value = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value);
} else {
switch (address) {
// Video
case REG_DISPSTAT:
value &= 0xFFF8;
GBAVideoWriteDISPSTAT(&gba->video, value);
return;
case REG_VCOUNT:
mLOG(GBA_IO, GAME_ERROR, "Write to read-only I/O register: %03X", address);
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;
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_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;
case REG_FIFO_A_LO:
case REG_FIFO_B_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));
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_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_DMA0CNT_LO:
GBADMAWriteCNT_LO(gba, 0, value);
break;
case REG_DMA0CNT_HI:
value = GBADMAWriteCNT_HI(gba, 0, value);
break;
case REG_DMA1CNT_LO:
GBADMAWriteCNT_LO(gba, 1, value);
break;
case REG_DMA1CNT_HI:
value = GBADMAWriteCNT_HI(gba, 1, value);
break;
case REG_DMA2CNT_LO:
GBADMAWriteCNT_LO(gba, 2, value);
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;
// 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_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_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:
GBAWriteIE(gba, value);
break;
case REG_IF:
gba->springIRQ &= ~value;
value = gba->memory.io[REG_IF >> 1] & ~value;
break;
case REG_IME:
GBAWriteIME(gba, value);
break;
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;
}
}
gba->memory.io[address >> 1] = value;
}