all repos — mgba @ 60fb32b71f7716a1be7ac88f58db82bb06f1c927

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", "ds.io");
 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		// Other video
437		case DS9_REG_DISPCAPCNT_LO:
438			value &= 0x1F1F;
439			break;
440		case DS9_REG_DISPCAPCNT_HI:
441			value &= 0xEF3F;
442			break;
443
444		// VRAM control
445		case DS9_REG_VRAMCNT_A:
446		case DS9_REG_VRAMCNT_C:
447		case DS9_REG_VRAMCNT_E:
448			oldValue = ds->memory.io9[address >> 1];
449			value &= 0x9F9F;
450			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
451			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
452			break;
453		case DS9_REG_VRAMCNT_G:
454			oldValue = ds->memory.io9[address >> 1];
455			value &= 0x039F;
456			DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
457			DSConfigureWRAM(&ds->memory, value >> 8);
458			break;
459		case DS9_REG_VRAMCNT_H:
460			oldValue = ds->memory.io9[address >> 1];
461			value &= 0x9F9F;
462			DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
463			DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
464			break;
465
466		case DS9_REG_EXMEMCNT:
467			value &= 0xE8FF;
468			DSConfigureExternalMemory(ds, value);
469			break;
470
471		// Math
472		case DS9_REG_DIVCNT:
473			value = _scheduleDiv(ds, value);
474			break;
475		case DS9_REG_DIV_NUMER_0:
476		case DS9_REG_DIV_NUMER_1:
477		case DS9_REG_DIV_NUMER_2:
478		case DS9_REG_DIV_NUMER_3:
479		case DS9_REG_DIV_DENOM_0:
480		case DS9_REG_DIV_DENOM_1:
481		case DS9_REG_DIV_DENOM_2:
482		case DS9_REG_DIV_DENOM_3:
483			ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
484			break;
485		case DS9_REG_SQRTCNT:
486			value = _scheduleSqrt(ds, value);
487			break;
488		case DS9_REG_SQRT_PARAM_0:
489		case DS9_REG_SQRT_PARAM_1:
490		case DS9_REG_SQRT_PARAM_2:
491		case DS9_REG_SQRT_PARAM_3:
492			ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
493			break;
494
495		// High Video
496		case DS9_REG_POWCNT1:
497			value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
498			break;
499
500		default:
501			{
502				uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
503				if (v2 & 0x10000) {
504					value = v2;
505					break;
506				} else if (v2 & 0x20000) {
507					return;
508				}
509			}
510			mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
511			if (address >= DS7_REG_MAX) {
512				mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
513				return;
514			}
515			break;
516		}
517	}
518	ds->memory.io9[address >> 1] = value;
519}
520
521void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
522	if (address < DS9_REG_MAX) {
523		uint16_t value16 = value << (8 * (address & 1));
524		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
525		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
526	} else {
527		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
528	}
529}
530
531void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
532	if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
533		value = DSGXWriteRegister32(&ds->gx, address, value);
534	} else {
535		switch (address) {
536		case DS_REG_DMA0SAD_LO:
537		case DS_REG_DMA1SAD_LO:
538		case DS_REG_DMA2SAD_LO:
539		case DS_REG_DMA3SAD_LO:
540		case DS_REG_DMA0DAD_LO:
541		case DS_REG_DMA1DAD_LO:
542		case DS_REG_DMA2DAD_LO:
543		case DS_REG_DMA3DAD_LO:
544		case DS_REG_IPCFIFOSEND_LO:
545		case DS_REG_IE_LO:
546			value = DSIOWrite32(&ds->ds9, address, value);
547			break;
548
549		case DS_REG_DMA0CNT_LO:
550			DS9DMAWriteCNT(&ds->ds9, 0, value);
551			break;
552		case DS_REG_DMA1CNT_LO:
553			DS9DMAWriteCNT(&ds->ds9, 1, value);
554			break;
555		case DS_REG_DMA2CNT_LO:
556			DS9DMAWriteCNT(&ds->ds9, 2, value);
557			break;
558		case DS_REG_DMA3CNT_LO:
559			DS9DMAWriteCNT(&ds->ds9, 3, value);
560			break;
561
562		default:
563			DS9IOWrite(ds, address, value & 0xFFFF);
564			DS9IOWrite(ds, address | 2, value >> 16);
565			return;
566		}
567	}
568	ds->ds9.memory.io[address >> 1] = value;
569	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
570}
571
572uint16_t DS9IORead(struct DS* ds, uint32_t address) {
573	switch (address) {
574	case DS_REG_TM0CNT_LO:
575	case DS_REG_TM1CNT_LO:
576	case DS_REG_TM2CNT_LO:
577	case DS_REG_TM3CNT_LO:
578		DSIOUpdateTimer(&ds->ds9, address);
579		break;
580	case DS_REG_KEYINPUT:
581		return DSIOReadKeyInput(ds);
582	case DS_REG_VCOUNT:
583	case DS_REG_DMA0FILL_LO:
584	case DS_REG_DMA0FILL_HI:
585	case DS_REG_DMA1FILL_LO:
586	case DS_REG_DMA1FILL_HI:
587	case DS_REG_DMA2FILL_LO:
588	case DS_REG_DMA2FILL_HI:
589	case DS_REG_DMA3FILL_LO:
590	case DS_REG_DMA3FILL_HI:
591	case DS_REG_TM0CNT_HI:
592	case DS_REG_TM1CNT_HI:
593	case DS_REG_TM2CNT_HI:
594	case DS_REG_TM3CNT_HI:
595	case DS_REG_IPCSYNC:
596	case DS_REG_IPCFIFOCNT:
597	case DS_REG_ROMCNT_LO:
598	case DS_REG_ROMCNT_HI:
599	case DS_REG_IME:
600	case 0x20A:
601	case DS_REG_IE_LO:
602	case DS_REG_IE_HI:
603	case DS_REG_IF_LO:
604	case DS_REG_IF_HI:
605	case DS9_REG_DIVCNT:
606	case DS9_REG_DIV_NUMER_0:
607	case DS9_REG_DIV_NUMER_1:
608	case DS9_REG_DIV_NUMER_2:
609	case DS9_REG_DIV_NUMER_3:
610	case DS9_REG_DIV_DENOM_0:
611	case DS9_REG_DIV_DENOM_1:
612	case DS9_REG_DIV_DENOM_2:
613	case DS9_REG_DIV_DENOM_3:
614	case DS9_REG_DIV_RESULT_0:
615	case DS9_REG_DIV_RESULT_1:
616	case DS9_REG_DIV_RESULT_2:
617	case DS9_REG_DIV_RESULT_3:
618	case DS9_REG_DIVREM_RESULT_0:
619	case DS9_REG_DIVREM_RESULT_1:
620	case DS9_REG_DIVREM_RESULT_2:
621	case DS9_REG_DIVREM_RESULT_3:
622	case DS9_REG_SQRTCNT:
623	case DS9_REG_SQRT_PARAM_0:
624	case DS9_REG_SQRT_PARAM_1:
625	case DS9_REG_SQRT_PARAM_2:
626	case DS9_REG_SQRT_PARAM_3:
627	case DS9_REG_SQRT_RESULT_LO:
628	case DS9_REG_SQRT_RESULT_HI:
629	case DS_REG_POSTFLG:
630	case DS9_REG_GXSTAT_LO:
631	case DS9_REG_GXSTAT_HI:
632	case DS9_REG_CLIPMTX_RESULT_00:
633	case DS9_REG_CLIPMTX_RESULT_01:
634	case DS9_REG_CLIPMTX_RESULT_02:
635	case DS9_REG_CLIPMTX_RESULT_03:
636	case DS9_REG_CLIPMTX_RESULT_04:
637	case DS9_REG_CLIPMTX_RESULT_05:
638	case DS9_REG_CLIPMTX_RESULT_06:
639	case DS9_REG_CLIPMTX_RESULT_07:
640	case DS9_REG_CLIPMTX_RESULT_08:
641	case DS9_REG_CLIPMTX_RESULT_09:
642	case DS9_REG_CLIPMTX_RESULT_0A:
643	case DS9_REG_CLIPMTX_RESULT_0B:
644	case DS9_REG_CLIPMTX_RESULT_0C:
645	case DS9_REG_CLIPMTX_RESULT_0D:
646	case DS9_REG_CLIPMTX_RESULT_0E:
647	case DS9_REG_CLIPMTX_RESULT_0F:
648	case DS9_REG_CLIPMTX_RESULT_10:
649	case DS9_REG_CLIPMTX_RESULT_11:
650	case DS9_REG_CLIPMTX_RESULT_12:
651	case DS9_REG_CLIPMTX_RESULT_13:
652	case DS9_REG_CLIPMTX_RESULT_14:
653	case DS9_REG_CLIPMTX_RESULT_15:
654	case DS9_REG_CLIPMTX_RESULT_16:
655	case DS9_REG_CLIPMTX_RESULT_17:
656	case DS9_REG_CLIPMTX_RESULT_18:
657	case DS9_REG_CLIPMTX_RESULT_19:
658	case DS9_REG_CLIPMTX_RESULT_1A:
659	case DS9_REG_CLIPMTX_RESULT_1B:
660	case DS9_REG_CLIPMTX_RESULT_1C:
661	case DS9_REG_CLIPMTX_RESULT_1D:
662	case DS9_REG_CLIPMTX_RESULT_1E:
663	case DS9_REG_CLIPMTX_RESULT_1F:
664		// Handled transparently by the registers
665		break;
666	case DS_REG_AUXSPICNT:
667	case DS_REG_AUXSPIDATA:
668		if (ds->ds9.memory.slot1Access) {
669			break;
670		} else {
671			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
672			return 0;
673		}
674	default:
675		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
676	}
677	if (address < DS9_REG_MAX) {
678		return ds->ds9.memory.io[address >> 1];
679	}
680	return 0;
681}
682
683uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
684	switch (address) {
685	case DS_REG_IPCFIFORECV_LO:
686		return DSIPCReadFIFO(&ds->ds9);
687	case DS_REG_ROMDATA_0:
688		if (ds->ds9.memory.slot1Access) {
689			return DSSlot1Read(ds);
690		} else {
691			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
692			return 0;
693		}
694	default:
695		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
696	}
697}