in cores/gba/src/gba/io.c [703:916]
uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
if (!GBAIOIsReadConstant(address)) {
// Most IO reads need to disable idle removal
gba->haltPending = false;
}
switch (address) {
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
case REG_TM0CNT_LO:
GBATimerUpdateRegister(gba, 0, 4);
break;
case REG_TM1CNT_LO:
GBATimerUpdateRegister(gba, 1, 4);
break;
case REG_TM2CNT_LO:
GBATimerUpdateRegister(gba, 2, 4);
break;
case REG_TM3CNT_LO:
GBATimerUpdateRegister(gba, 3, 4);
break;
case REG_KEYINPUT:
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
return 0x3FF ^ gba->rr->queryInput(gba->rr);
} else {
uint16_t input = 0x3FF;
if (gba->keyCallback) {
input = gba->keyCallback->readKeys(gba->keyCallback);
if (gba->keySource) {
*gba->keySource = input;
}
} else if (gba->keySource) {
input = *gba->keySource;
}
if (!gba->allowOpposingDirections) {
unsigned rl = input & 0x030;
unsigned ud = input & 0x0C0;
input &= 0x30F;
if (rl != 0x030) {
input |= rl;
}
if (ud != 0x0C0) {
input |= ud;
}
}
if (gba->rr && gba->rr->isRecording(gba->rr)) {
gba->rr->logInput(gba->rr, input);
}
return 0x3FF ^ input;
}
case REG_SIOCNT:
return gba->sio.siocnt;
case REG_RCNT:
return gba->sio.rcnt;
case REG_BG0HOFS:
case REG_BG0VOFS:
case REG_BG1HOFS:
case REG_BG1VOFS:
case REG_BG2HOFS:
case REG_BG2VOFS:
case REG_BG3HOFS:
case REG_BG3VOFS:
case REG_BG2PA:
case REG_BG2PB:
case REG_BG2PC:
case REG_BG2PD:
case REG_BG2X_LO:
case REG_BG2X_HI:
case REG_BG2Y_LO:
case REG_BG2Y_HI:
case REG_BG3PA:
case REG_BG3PB:
case REG_BG3PC:
case REG_BG3PD:
case REG_BG3X_LO:
case REG_BG3X_HI:
case REG_BG3Y_LO:
case REG_BG3Y_HI:
case REG_WIN0H:
case REG_WIN1H:
case REG_WIN0V:
case REG_WIN1V:
case REG_MOSAIC:
case REG_BLDY:
case REG_FIFO_A_LO:
case REG_FIFO_A_HI:
case REG_FIFO_B_LO:
case REG_FIFO_B_HI:
case REG_DMA0SAD_LO:
case REG_DMA0SAD_HI:
case REG_DMA0DAD_LO:
case REG_DMA0DAD_HI:
case REG_DMA1SAD_LO:
case REG_DMA1SAD_HI:
case REG_DMA1DAD_LO:
case REG_DMA1DAD_HI:
case REG_DMA2SAD_LO:
case REG_DMA2SAD_HI:
case REG_DMA2DAD_LO:
case REG_DMA2DAD_HI:
case REG_DMA3SAD_LO:
case REG_DMA3SAD_HI:
case REG_DMA3DAD_LO:
case REG_DMA3DAD_HI:
// Write-only register
mLOG(GBA_IO, GAME_ERROR, "Read from write-only I/O register: %03X", address);
return GBALoadBad(gba->cpu);
case REG_DMA0CNT_LO:
case REG_DMA1CNT_LO:
case REG_DMA2CNT_LO:
case REG_DMA3CNT_LO:
// Write-only register
mLOG(GBA_IO, GAME_ERROR, "Read from write-only I/O register: %03X", address);
return 0;
case REG_JOY_RECV_LO:
case REG_JOY_RECV_HI:
gba->memory.io[REG_JOYSTAT >> 1] &= ~JOYSTAT_RECV_BIT;
break;
case REG_SOUNDBIAS:
case REG_POSTFLG:
mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address);
break;
case REG_SOUND1CNT_LO:
case REG_SOUND1CNT_HI:
case REG_SOUND1CNT_X:
case REG_SOUND2CNT_LO:
case REG_SOUND2CNT_HI:
case REG_SOUND3CNT_LO:
case REG_SOUND3CNT_HI:
case REG_SOUND3CNT_X:
case REG_SOUND4CNT_LO:
case REG_SOUND4CNT_HI:
case REG_SOUNDCNT_LO:
case REG_SOUNDCNT_HI:
if (!GBAudioEnableIsEnable(gba->memory.io[REG_SOUNDCNT_X >> 1])) {
// TODO: Is writing allowed when the circuit is disabled?
return 0;
}
// Fall through
case REG_DISPCNT:
case REG_DISPSTAT:
case REG_VCOUNT:
case REG_BG0CNT:
case REG_BG1CNT:
case REG_BG2CNT:
case REG_BG3CNT:
case REG_WININ:
case REG_WINOUT:
case REG_BLDCNT:
case REG_BLDALPHA:
case REG_SOUNDCNT_X:
case REG_WAVE_RAM0_LO:
case REG_WAVE_RAM0_HI:
case REG_WAVE_RAM1_LO:
case REG_WAVE_RAM1_HI:
case REG_WAVE_RAM2_LO:
case REG_WAVE_RAM2_HI:
case REG_WAVE_RAM3_LO:
case REG_WAVE_RAM3_HI:
case REG_DMA0CNT_HI:
case REG_DMA1CNT_HI:
case REG_DMA2CNT_HI:
case REG_DMA3CNT_HI:
case REG_TM0CNT_HI:
case REG_TM1CNT_HI:
case REG_TM2CNT_HI:
case REG_TM3CNT_HI:
case REG_KEYCNT:
case REG_SIOMULTI0:
case REG_SIOMULTI1:
case REG_SIOMULTI2:
case REG_SIOMULTI3:
case REG_SIOMLT_SEND:
case REG_JOYCNT:
case REG_JOY_TRANS_LO:
case REG_JOY_TRANS_HI:
case REG_JOYSTAT:
case REG_IE:
case REG_IF:
case REG_WAITCNT:
case REG_IME:
// Handled transparently by registers
break;
case REG_MAX:
// Some bad interrupt libraries will read from this
case 0x066:
case 0x06E:
case 0x076:
case 0x07A:
case 0x07E:
case 0x086:
case 0x08A:
case 0x136:
case 0x142:
case 0x15A:
case 0x206:
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
return 0;
case REG_DEBUG_ENABLE:
if (gba->debug) {
return 0x1DEA;
}
// Fall through
default:
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
return GBALoadBad(gba->cpu);
}
return gba->memory.io[address >> 1];
}