uint16_t GBAIORead()

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];
}