all repos — mgba @ 2ab7289a059ab3055f00f0677f7c20bfed7591d5

mGBA Game Boy Advance Emulator

src/ds/io.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/ds/io.h>
  7
  8#include <mgba/core/interface.h>
  9#include <mgba/internal/ds/ds.h>
 10#include <mgba/internal/ds/gx.h>
 11#include <mgba/internal/ds/ipc.h>
 12#include <mgba/internal/ds/slot1.h>
 13#include <mgba/internal/ds/spi.h>
 14
 15mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
 16
 17static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
 18	switch (value >> 6) {
 19	case 0:
 20	default:
 21		break;
 22	case 1:
 23		mLOG(DS_IO, STUB, "Enter GBA mode not supported");
 24		break;
 25	case 2:
 26		ARMHalt(dscore->cpu);
 27		break;
 28	case 3:
 29		mLOG(DS_IO, STUB, "Enter sleep mode not supported");
 30		break;
 31	}
 32}
 33
 34static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) {
 35	mTimingDeschedule(&ds->ds9.timing, &ds->divEvent);
 36	mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68);
 37	return control | 0x8000;
 38}
 39
 40static uint16_t _scheduleSqrt(struct DS* ds, uint16_t control) {
 41	mTimingDeschedule(&ds->ds9.timing, &ds->sqrtEvent);
 42	mTimingSchedule(&ds->ds9.timing, &ds->sqrtEvent, 26);
 43	return control | 0x8000;
 44}
 45
 46static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
 47	switch (address) {
 48	// Video
 49	case DS_REG_DISPSTAT:
 50		DSVideoWriteDISPSTAT(dscore, value);
 51		break;
 52
 53	// DMA Fill
 54	case DS_REG_DMA0FILL_LO:
 55	case DS_REG_DMA0FILL_HI:
 56	case DS_REG_DMA1FILL_LO:
 57	case DS_REG_DMA1FILL_HI:
 58	case DS_REG_DMA2FILL_LO:
 59	case DS_REG_DMA2FILL_HI:
 60	case DS_REG_DMA3FILL_LO:
 61	case DS_REG_DMA3FILL_HI:
 62		break;
 63
 64	// Timers
 65	case DS_REG_TM0CNT_LO:
 66		GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
 67		return 0x20000;
 68	case DS_REG_TM1CNT_LO:
 69		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 70		return 0x20000;
 71	case DS_REG_TM2CNT_LO:
 72		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 73		return 0x20000;
 74	case DS_REG_TM3CNT_LO:
 75		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 76		return 0x20000;
 77
 78	case DS_REG_TM0CNT_HI:
 79		value &= 0x00C7;
 80		DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
 81		break;
 82	case DS_REG_TM1CNT_HI:
 83		value &= 0x00C7;
 84		DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
 85		break;
 86	case DS_REG_TM2CNT_HI:
 87		value &= 0x00C7;
 88		DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
 89		break;
 90	case DS_REG_TM3CNT_HI:
 91		value &= 0x00C7;
 92		DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
 93		break;
 94
 95	// IPC
 96	case DS_REG_IPCSYNC:
 97		value &= 0x6F00;
 98		value |= dscore->memory.io[address >> 1] & 0x000F;
 99		DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
100		break;
101	case DS_REG_IPCFIFOCNT:
102		value = DSIPCWriteFIFOCNT(dscore, value);
103		break;
104
105	// Cart bus
106	case DS_REG_AUXSPICNT:
107		if (dscore->memory.slot1Access) {
108			value = DSSlot1Configure(dscore->p, value);
109			dscore->ipc->memory.io[address >> 1] = value;
110		} else {
111			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
112			return 0;
113		}
114		break;
115	case DS_REG_AUXSPIDATA:
116		if (dscore->memory.slot1Access) {
117			DSSlot1WriteSPI(dscore, value);
118			dscore->ipc->memory.io[address >> 1] = value;
119		} else {
120			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
121			return 0;
122		}
123		break;
124	case DS_REG_ROMCNT_HI:
125		if (dscore->memory.slot1Access) {
126			DSSlot1ROMCNT cnt = value << 16;
127			cnt |= dscore->memory.io[(address - 2) >> 1];
128			cnt = DSSlot1Control(dscore->p, cnt);
129			value = cnt >> 16;
130			dscore->ipc->memory.io[address >> 1] = value;
131		} else {
132			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
133			return 0;
134		}
135		break;
136	case DS_REG_ROMCNT_LO:
137	case DS_REG_ROMCMD_0:
138	case DS_REG_ROMCMD_2:
139	case DS_REG_ROMCMD_4:
140	case DS_REG_ROMCMD_6:
141		if (dscore->memory.slot1Access) {
142			dscore->ipc->memory.io[address >> 1] = value;
143		} else {
144			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
145			return 0;
146		}
147		break;
148
149	// Interrupts
150	case DS_REG_IME:
151		DSWriteIME(dscore->cpu, dscore->memory.io, value);
152		break;
153	case 0x20A:
154		value = 0;
155		// Some bad interrupt libraries will write to this
156		break;
157	case DS_REG_IF_LO:
158	case DS_REG_IF_HI:
159		value = dscore->memory.io[address >> 1] & ~value;
160		DSGXUpdateGXSTAT(&dscore->p->gx);
161		break;
162	default:
163		return 0;
164	}
165	return value | 0x10000;
166}
167
168uint32_t DSIOWrite32(struct DSCommon* dscore, uint32_t address, uint32_t value) {
169	switch (address) {
170	case DS_REG_DMA0SAD_LO:
171		value = DSDMAWriteSAD(dscore, 0, value);
172		break;
173	case DS_REG_DMA1SAD_LO:
174		value = DSDMAWriteSAD(dscore, 1, value);
175		break;
176	case DS_REG_DMA2SAD_LO:
177		value = DSDMAWriteSAD(dscore, 2, value);
178		break;
179	case DS_REG_DMA3SAD_LO:
180		value = DSDMAWriteSAD(dscore, 3, value);
181		break;
182
183	case DS_REG_DMA0DAD_LO:
184		value = DSDMAWriteDAD(dscore, 0, value);
185		break;
186	case DS_REG_DMA1DAD_LO:
187		value = DSDMAWriteDAD(dscore, 1, value);
188		break;
189	case DS_REG_DMA2DAD_LO:
190		value = DSDMAWriteDAD(dscore, 2, value);
191		break;
192	case DS_REG_DMA3DAD_LO:
193		value = DSDMAWriteDAD(dscore, 3, value);
194		break;
195
196	case DS_REG_IPCFIFOSEND_LO:
197		DSIPCWriteFIFO(dscore, value);
198		break;
199	case DS_REG_IE_LO:
200		DSWriteIE(dscore->cpu, dscore->memory.io, value);
201		break;
202	}
203
204	return value;
205}
206
207static uint16_t DSIOReadExKeyInput(struct DS* ds) {
208	uint16_t input = 0;
209	if (ds->keyCallback) {
210		input = ds->keyCallback->readKeys(ds->keyCallback);
211	} else if (ds->keySource) {
212		input = *ds->keySource;
213	}
214	input = ~(input >> 10) & 0x3;
215	input |= 0x3C;
216	input |= ds->memory.io7[DS7_REG_EXTKEYIN >> 1] & 0xC0;
217	return input;
218}
219
220static uint16_t DSIOReadKeyInput(struct DS* ds) {
221	uint16_t input = 0;
222	if (ds->keyCallback) {
223		input = ds->keyCallback->readKeys(ds->keyCallback);
224	} else if (ds->keySource) {
225		input = *ds->keySource;
226	}
227	// TODO: Put back
228	/*if (!dscore->p->allowOpposingDirections) {
229		unsigned rl = input & 0x030;
230		unsigned ud = input & 0x0C0;
231		input &= 0x30F;
232		if (rl != 0x030) {
233			input |= rl;
234		}
235		if (ud != 0x0C0) {
236			input |= ud;
237		}
238	}*/
239	return ~input & 0x3FF;
240}
241
242static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
243	switch (address) {
244	case DS_REG_TM0CNT_LO:
245		GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
246		break;
247	case DS_REG_TM1CNT_LO:
248		GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
249		break;
250	case DS_REG_TM2CNT_LO:
251		GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
252		break;
253	case DS_REG_TM3CNT_LO:
254		GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
255		break;
256	}
257}
258
259void DS7IOInit(struct DS* ds) {
260	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
261	ds->memory.io7[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
262	ds->memory.io7[DS_REG_POSTFLG >> 1] = 0x0001;
263	ds->memory.io7[DS7_REG_EXTKEYIN >> 1] = 0x007F;
264}
265
266void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
267	switch (address) {
268	case DS7_REG_SPICNT:
269		value &= 0xCF83;
270		value = DSSPIWriteControl(ds, value);
271		break;
272	case DS7_REG_SPIDATA:
273		DSSPIWrite(ds, value);
274		break;
275	default:
276		{
277			uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
278			if (v2 & 0x10000) {
279				value = v2;
280				break;
281			} else if (v2 & 0x20000) {
282				return;
283			}
284		}
285		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
286		if (address >= DS7_REG_MAX) {
287			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
288			return;
289		}
290		break;
291	}
292	ds->memory.io7[address >> 1] = value;
293}
294
295void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
296	if (address == DS7_REG_HALTCNT) {
297		_DSHaltCNT(&ds->ds7, value);
298		return;
299	}
300	if (address < DS7_REG_MAX) {
301		uint16_t value16 = value << (8 * (address & 1));
302		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
303		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
304	} else {
305		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
306	}
307}
308
309void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
310	switch (address) {
311	case DS_REG_DMA0SAD_LO:
312	case DS_REG_DMA1SAD_LO:
313	case DS_REG_DMA2SAD_LO:
314	case DS_REG_DMA3SAD_LO:
315	case DS_REG_DMA0DAD_LO:
316	case DS_REG_DMA1DAD_LO:
317	case DS_REG_DMA2DAD_LO:
318	case DS_REG_DMA3DAD_LO:
319	case DS_REG_IPCFIFOSEND_LO:
320	case DS_REG_IE_LO:
321		value = DSIOWrite32(&ds->ds7, address, value);
322		break;
323
324	case DS_REG_DMA0CNT_LO:
325		DS7DMAWriteCNT(&ds->ds7, 0, value);
326		break;
327	case DS_REG_DMA1CNT_LO:
328		DS7DMAWriteCNT(&ds->ds7, 1, value);
329		break;
330	case DS_REG_DMA2CNT_LO:
331		DS7DMAWriteCNT(&ds->ds7, 2, value);
332		break;
333	case DS_REG_DMA3CNT_LO:
334		DS7DMAWriteCNT(&ds->ds7, 3, value);
335		break;
336	default:
337		DS7IOWrite(ds, address, value & 0xFFFF);
338		DS7IOWrite(ds, address | 2, value >> 16);
339		return;
340	}
341	ds->ds7.memory.io[address >> 1] = value;
342	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
343}
344
345uint16_t DS7IORead(struct DS* ds, uint32_t address) {
346	switch (address) {
347	case DS_REG_TM0CNT_LO:
348	case DS_REG_TM1CNT_LO:
349	case DS_REG_TM2CNT_LO:
350	case DS_REG_TM3CNT_LO:
351		DSIOUpdateTimer(&ds->ds7, address);
352		break;
353	case DS_REG_KEYINPUT:
354		return DSIOReadKeyInput(ds);
355	case DS7_REG_EXTKEYIN:
356		return DSIOReadExKeyInput(ds);
357	case DS_REG_VCOUNT:
358	case DS_REG_DMA0FILL_LO:
359	case DS_REG_DMA0FILL_HI:
360	case DS_REG_DMA1FILL_LO:
361	case DS_REG_DMA1FILL_HI:
362	case DS_REG_DMA2FILL_LO:
363	case DS_REG_DMA2FILL_HI:
364	case DS_REG_DMA3FILL_LO:
365	case DS_REG_DMA3FILL_HI:
366	case DS_REG_TM0CNT_HI:
367	case DS_REG_TM1CNT_HI:
368	case DS_REG_TM2CNT_HI:
369	case DS_REG_TM3CNT_HI:
370	case DS7_REG_SPICNT:
371	case DS7_REG_SPIDATA:
372	case DS_REG_IPCSYNC:
373	case DS_REG_IPCFIFOCNT:
374	case DS_REG_ROMCNT_LO:
375	case DS_REG_ROMCNT_HI:
376	case DS_REG_IME:
377	case 0x20A:
378	case DS_REG_IE_LO:
379	case DS_REG_IE_HI:
380	case DS_REG_IF_LO:
381	case DS_REG_IF_HI:
382	case DS_REG_POSTFLG:
383		// Handled transparently by the registers
384		break;
385	case DS_REG_AUXSPICNT:
386	case DS_REG_AUXSPIDATA:
387		if (ds->ds7.memory.slot1Access) {
388			break;
389		} else {
390			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
391			return 0;
392		}
393	default:
394		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
395	}
396	if (address < DS7_REG_MAX) {
397		return ds->memory.io7[address >> 1];
398	}
399	return 0;
400}
401
402uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
403	switch (address) {
404	case DS_REG_IPCFIFORECV_LO:
405		return DSIPCReadFIFO(&ds->ds7);
406	case DS_REG_ROMDATA_0:
407		if (ds->ds7.memory.slot1Access) {
408			return DSSlot1Read(ds);
409		} else {
410			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
411			return 0;
412		}
413	default:
414		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
415	}
416}
417
418void DS9IOInit(struct DS* ds) {
419	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
420	ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
421	ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
422	ds->memory.io9[DS9_REG_GXSTAT_HI >> 1] = 0x0600;
423	DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
424}
425
426void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
427	if ((address <= DS9_REG_A_BLDY && address > DS_REG_VCOUNT) || address == DS9_REG_A_DISPCNT_LO || address == DS9_REG_A_DISPCNT_HI || address == DS9_REG_A_MASTER_BRIGHT) {
428		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
429	} else if ((address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) || address == DS9_REG_B_MASTER_BRIGHT) {
430		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
431	} else if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
432		value = DSGXWriteRegister(&ds->gx, address, value);
433	} else {
434		uint16_t oldValue;
435		switch (address) {
436		// VRAM control
437		case DS9_REG_VRAMCNT_A:
438		case DS9_REG_VRAMCNT_C:
439		case DS9_REG_VRAMCNT_E:
440			oldValue = ds->memory.io9[address >> 1];
441			value &= 0x9F9F;
442			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
443			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
444			break;
445		case DS9_REG_VRAMCNT_G:
446			oldValue = ds->memory.io9[address >> 1];
447			value &= 0x039F;
448			DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
449			DSConfigureWRAM(&ds->memory, value >> 8);
450			break;
451		case DS9_REG_VRAMCNT_H:
452			oldValue = ds->memory.io9[address >> 1];
453			value &= 0x9F9F;
454			DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
455			DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
456			break;
457
458		case DS9_REG_EXMEMCNT:
459			value &= 0xE8FF;
460			DSConfigureExternalMemory(ds, value);
461			break;
462
463		// Math
464		case DS9_REG_DIVCNT:
465			value = _scheduleDiv(ds, value);
466			break;
467		case DS9_REG_DIV_NUMER_0:
468		case DS9_REG_DIV_NUMER_1:
469		case DS9_REG_DIV_NUMER_2:
470		case DS9_REG_DIV_NUMER_3:
471		case DS9_REG_DIV_DENOM_0:
472		case DS9_REG_DIV_DENOM_1:
473		case DS9_REG_DIV_DENOM_2:
474		case DS9_REG_DIV_DENOM_3:
475			ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
476			break;
477		case DS9_REG_SQRTCNT:
478			value = _scheduleSqrt(ds, value);
479			break;
480		case DS9_REG_SQRT_PARAM_0:
481		case DS9_REG_SQRT_PARAM_1:
482		case DS9_REG_SQRT_PARAM_2:
483		case DS9_REG_SQRT_PARAM_3:
484			ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
485			break;
486
487		// High Video
488		case DS9_REG_POWCNT1:
489			value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
490			break;
491
492		default:
493			{
494				uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
495				if (v2 & 0x10000) {
496					value = v2;
497					break;
498				} else if (v2 & 0x20000) {
499					return;
500				}
501			}
502			mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
503			if (address >= DS7_REG_MAX) {
504				mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
505				return;
506			}
507			break;
508		}
509	}
510	ds->memory.io9[address >> 1] = value;
511}
512
513void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
514	if (address < DS9_REG_MAX) {
515		uint16_t value16 = value << (8 * (address & 1));
516		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
517		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
518	} else {
519		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
520	}
521}
522
523void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
524	if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
525		value = DSGXWriteRegister32(&ds->gx, address, value);
526	} else {
527		switch (address) {
528		case DS_REG_DMA0SAD_LO:
529		case DS_REG_DMA1SAD_LO:
530		case DS_REG_DMA2SAD_LO:
531		case DS_REG_DMA3SAD_LO:
532		case DS_REG_DMA0DAD_LO:
533		case DS_REG_DMA1DAD_LO:
534		case DS_REG_DMA2DAD_LO:
535		case DS_REG_DMA3DAD_LO:
536		case DS_REG_IPCFIFOSEND_LO:
537		case DS_REG_IE_LO:
538			value = DSIOWrite32(&ds->ds9, address, value);
539			break;
540
541		case DS_REG_DMA0CNT_LO:
542			DS9DMAWriteCNT(&ds->ds9, 0, value);
543			break;
544		case DS_REG_DMA1CNT_LO:
545			DS9DMAWriteCNT(&ds->ds9, 1, value);
546			break;
547		case DS_REG_DMA2CNT_LO:
548			DS9DMAWriteCNT(&ds->ds9, 2, value);
549			break;
550		case DS_REG_DMA3CNT_LO:
551			DS9DMAWriteCNT(&ds->ds9, 3, value);
552			break;
553
554		default:
555			DS9IOWrite(ds, address, value & 0xFFFF);
556			DS9IOWrite(ds, address | 2, value >> 16);
557			return;
558		}
559	}
560	ds->ds9.memory.io[address >> 1] = value;
561	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
562}
563
564uint16_t DS9IORead(struct DS* ds, uint32_t address) {
565	switch (address) {
566	case DS_REG_TM0CNT_LO:
567	case DS_REG_TM1CNT_LO:
568	case DS_REG_TM2CNT_LO:
569	case DS_REG_TM3CNT_LO:
570		DSIOUpdateTimer(&ds->ds9, address);
571		break;
572	case DS_REG_KEYINPUT:
573		return DSIOReadKeyInput(ds);
574	case DS_REG_VCOUNT:
575	case DS_REG_DMA0FILL_LO:
576	case DS_REG_DMA0FILL_HI:
577	case DS_REG_DMA1FILL_LO:
578	case DS_REG_DMA1FILL_HI:
579	case DS_REG_DMA2FILL_LO:
580	case DS_REG_DMA2FILL_HI:
581	case DS_REG_DMA3FILL_LO:
582	case DS_REG_DMA3FILL_HI:
583	case DS_REG_TM0CNT_HI:
584	case DS_REG_TM1CNT_HI:
585	case DS_REG_TM2CNT_HI:
586	case DS_REG_TM3CNT_HI:
587	case DS_REG_IPCSYNC:
588	case DS_REG_IPCFIFOCNT:
589	case DS_REG_ROMCNT_LO:
590	case DS_REG_ROMCNT_HI:
591	case DS_REG_IME:
592	case 0x20A:
593	case DS_REG_IE_LO:
594	case DS_REG_IE_HI:
595	case DS_REG_IF_LO:
596	case DS_REG_IF_HI:
597	case DS9_REG_DIVCNT:
598	case DS9_REG_DIV_NUMER_0:
599	case DS9_REG_DIV_NUMER_1:
600	case DS9_REG_DIV_NUMER_2:
601	case DS9_REG_DIV_NUMER_3:
602	case DS9_REG_DIV_DENOM_0:
603	case DS9_REG_DIV_DENOM_1:
604	case DS9_REG_DIV_DENOM_2:
605	case DS9_REG_DIV_DENOM_3:
606	case DS9_REG_DIV_RESULT_0:
607	case DS9_REG_DIV_RESULT_1:
608	case DS9_REG_DIV_RESULT_2:
609	case DS9_REG_DIV_RESULT_3:
610	case DS9_REG_DIVREM_RESULT_0:
611	case DS9_REG_DIVREM_RESULT_1:
612	case DS9_REG_DIVREM_RESULT_2:
613	case DS9_REG_DIVREM_RESULT_3:
614	case DS9_REG_SQRTCNT:
615	case DS9_REG_SQRT_PARAM_0:
616	case DS9_REG_SQRT_PARAM_1:
617	case DS9_REG_SQRT_PARAM_2:
618	case DS9_REG_SQRT_PARAM_3:
619	case DS9_REG_SQRT_RESULT_LO:
620	case DS9_REG_SQRT_RESULT_HI:
621	case DS_REG_POSTFLG:
622	case DS9_REG_GXSTAT_LO:
623	case DS9_REG_GXSTAT_HI:
624		// Handled transparently by the registers
625		break;
626	case DS_REG_AUXSPICNT:
627	case DS_REG_AUXSPIDATA:
628		if (ds->ds9.memory.slot1Access) {
629			break;
630		} else {
631			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
632			return 0;
633		}
634	default:
635		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
636	}
637	if (address < DS9_REG_MAX) {
638		return ds->ds9.memory.io[address >> 1];
639	}
640	return 0;
641}
642
643uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
644	switch (address) {
645	case DS_REG_IPCFIFORECV_LO:
646		return DSIPCReadFIFO(&ds->ds9);
647	case DS_REG_ROMDATA_0:
648		if (ds->ds9.memory.slot1Access) {
649			return DSSlot1Read(ds);
650		} else {
651			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
652			return 0;
653		}
654	default:
655		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
656	}
657}