all repos — mgba @ c5212970935a527355a36a84e1577409e66bf421

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		if (address >= DS7_IO_BASE_WIFI && address < DS7_IO_END_WIFI) {
286			DSWifiWriteIO(ds, address & 0x7FFF, value);
287			return;
288		}
289		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
290		if (address >= DS7_REG_MAX) {
291			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
292			return;
293		}
294		break;
295	}
296	ds->memory.io7[address >> 1] = value;
297}
298
299void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
300	if (address == DS7_REG_HALTCNT) {
301		_DSHaltCNT(&ds->ds7, value);
302		return;
303	}
304	if (address < DS7_REG_MAX) {
305		uint16_t value16 = value << (8 * (address & 1));
306		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
307		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
308	} else {
309		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
310	}
311}
312
313void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
314	switch (address) {
315	case DS_REG_DMA0SAD_LO:
316	case DS_REG_DMA1SAD_LO:
317	case DS_REG_DMA2SAD_LO:
318	case DS_REG_DMA3SAD_LO:
319	case DS_REG_DMA0DAD_LO:
320	case DS_REG_DMA1DAD_LO:
321	case DS_REG_DMA2DAD_LO:
322	case DS_REG_DMA3DAD_LO:
323	case DS_REG_IPCFIFOSEND_LO:
324	case DS_REG_IE_LO:
325		value = DSIOWrite32(&ds->ds7, address, value);
326		break;
327
328	case DS_REG_DMA0CNT_LO:
329		DS7DMAWriteCNT(&ds->ds7, 0, value);
330		break;
331	case DS_REG_DMA1CNT_LO:
332		DS7DMAWriteCNT(&ds->ds7, 1, value);
333		break;
334	case DS_REG_DMA2CNT_LO:
335		DS7DMAWriteCNT(&ds->ds7, 2, value);
336		break;
337	case DS_REG_DMA3CNT_LO:
338		DS7DMAWriteCNT(&ds->ds7, 3, value);
339		break;
340	default:
341		DS7IOWrite(ds, address, value & 0xFFFF);
342		DS7IOWrite(ds, address | 2, value >> 16);
343		return;
344	}
345	ds->ds7.memory.io[address >> 1] = value;
346	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
347}
348
349uint16_t DS7IORead(struct DS* ds, uint32_t address) {
350	switch (address) {
351	case DS_REG_TM0CNT_LO:
352	case DS_REG_TM1CNT_LO:
353	case DS_REG_TM2CNT_LO:
354	case DS_REG_TM3CNT_LO:
355		DSIOUpdateTimer(&ds->ds7, address);
356		break;
357	case DS_REG_KEYINPUT:
358		return DSIOReadKeyInput(ds);
359	case DS7_REG_EXTKEYIN:
360		return DSIOReadExKeyInput(ds);
361	case DS_REG_VCOUNT:
362	case DS_REG_DMA0FILL_LO:
363	case DS_REG_DMA0FILL_HI:
364	case DS_REG_DMA1FILL_LO:
365	case DS_REG_DMA1FILL_HI:
366	case DS_REG_DMA2FILL_LO:
367	case DS_REG_DMA2FILL_HI:
368	case DS_REG_DMA3FILL_LO:
369	case DS_REG_DMA3FILL_HI:
370	case DS_REG_TM0CNT_HI:
371	case DS_REG_TM1CNT_HI:
372	case DS_REG_TM2CNT_HI:
373	case DS_REG_TM3CNT_HI:
374	case DS7_REG_SPICNT:
375	case DS7_REG_SPIDATA:
376	case DS_REG_IPCSYNC:
377	case DS_REG_IPCFIFOCNT:
378	case DS_REG_ROMCNT_LO:
379	case DS_REG_ROMCNT_HI:
380	case DS_REG_IME:
381	case 0x20A:
382	case DS_REG_IE_LO:
383	case DS_REG_IE_HI:
384	case DS_REG_IF_LO:
385	case DS_REG_IF_HI:
386	case DS_REG_POSTFLG:
387		// Handled transparently by the registers
388		break;
389	case DS_REG_AUXSPICNT:
390	case DS_REG_AUXSPIDATA:
391		if (ds->ds7.memory.slot1Access) {
392			break;
393		} else {
394			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
395			return 0;
396		}
397	default:
398		if (address >= DS7_IO_BASE_WIFI && address < DS7_IO_END_WIFI) {
399			return DSWifiReadIO(ds, address & 0x7FFF);
400		}
401		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
402	}
403	if (address < DS7_REG_MAX) {
404		return ds->memory.io7[address >> 1];
405	}
406
407	return 0;
408}
409
410uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
411	switch (address) {
412	case DS_REG_IPCFIFORECV_LO:
413		return DSIPCReadFIFO(&ds->ds7);
414	case DS_REG_ROMDATA_0:
415		if (ds->ds7.memory.slot1Access) {
416			return DSSlot1Read(ds);
417		} else {
418			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
419			return 0;
420		}
421	default:
422		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
423	}
424}
425
426void DS9IOInit(struct DS* ds) {
427	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
428	ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
429	ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
430	ds->memory.io9[DS9_REG_GXSTAT_HI >> 1] = 0x0600;
431	DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
432}
433
434void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
435	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) {
436		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
437	} else if ((address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) || address == DS9_REG_B_MASTER_BRIGHT) {
438		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
439	} else if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
440		value = DSGXWriteRegister(&ds->gx, address, value);
441	} else {
442		uint16_t oldValue;
443		switch (address) {
444		// Other video
445		case DS9_REG_DISPCAPCNT_LO:
446			value &= 0x1F1F;
447			break;
448		case DS9_REG_DISPCAPCNT_HI:
449			value &= 0xEF3F;
450			break;
451
452		// VRAM control
453		case DS9_REG_VRAMCNT_A:
454		case DS9_REG_VRAMCNT_C:
455		case DS9_REG_VRAMCNT_E:
456			oldValue = ds->memory.io9[address >> 1];
457			value &= 0x9F9F;
458			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
459			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
460			break;
461		case DS9_REG_VRAMCNT_G:
462			oldValue = ds->memory.io9[address >> 1];
463			value &= 0x039F;
464			DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
465			DSConfigureWRAM(&ds->memory, value >> 8);
466			break;
467		case DS9_REG_VRAMCNT_H:
468			oldValue = ds->memory.io9[address >> 1];
469			value &= 0x9F9F;
470			DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
471			DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
472			break;
473
474		case DS9_REG_EXMEMCNT:
475			value &= 0xE8FF;
476			DSConfigureExternalMemory(ds, value);
477			break;
478
479		// Math
480		case DS9_REG_DIVCNT:
481			value = _scheduleDiv(ds, value);
482			break;
483		case DS9_REG_DIV_NUMER_0:
484		case DS9_REG_DIV_NUMER_1:
485		case DS9_REG_DIV_NUMER_2:
486		case DS9_REG_DIV_NUMER_3:
487		case DS9_REG_DIV_DENOM_0:
488		case DS9_REG_DIV_DENOM_1:
489		case DS9_REG_DIV_DENOM_2:
490		case DS9_REG_DIV_DENOM_3:
491			ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
492			break;
493		case DS9_REG_SQRTCNT:
494			value = _scheduleSqrt(ds, value);
495			break;
496		case DS9_REG_SQRT_PARAM_0:
497		case DS9_REG_SQRT_PARAM_1:
498		case DS9_REG_SQRT_PARAM_2:
499		case DS9_REG_SQRT_PARAM_3:
500			ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
501			break;
502
503		// High Video
504		case DS9_REG_POWCNT1:
505			value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
506			break;
507
508		default:
509			{
510				uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
511				if (v2 & 0x10000) {
512					value = v2;
513					break;
514				} else if (v2 & 0x20000) {
515					return;
516				}
517			}
518			mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
519			if (address >= DS7_REG_MAX) {
520				mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
521				return;
522			}
523			break;
524		}
525	}
526	ds->memory.io9[address >> 1] = value;
527}
528
529void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
530	if (address < DS9_REG_MAX) {
531		uint16_t value16 = value << (8 * (address & 1));
532		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
533		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
534	} else {
535		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
536	}
537}
538
539void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
540	if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
541		value = DSGXWriteRegister32(&ds->gx, address, value);
542	} else {
543		switch (address) {
544		case DS_REG_DMA0SAD_LO:
545		case DS_REG_DMA1SAD_LO:
546		case DS_REG_DMA2SAD_LO:
547		case DS_REG_DMA3SAD_LO:
548		case DS_REG_DMA0DAD_LO:
549		case DS_REG_DMA1DAD_LO:
550		case DS_REG_DMA2DAD_LO:
551		case DS_REG_DMA3DAD_LO:
552		case DS_REG_IPCFIFOSEND_LO:
553		case DS_REG_IE_LO:
554			value = DSIOWrite32(&ds->ds9, address, value);
555			break;
556
557		case DS_REG_DMA0CNT_LO:
558			DS9DMAWriteCNT(&ds->ds9, 0, value);
559			break;
560		case DS_REG_DMA1CNT_LO:
561			DS9DMAWriteCNT(&ds->ds9, 1, value);
562			break;
563		case DS_REG_DMA2CNT_LO:
564			DS9DMAWriteCNT(&ds->ds9, 2, value);
565			break;
566		case DS_REG_DMA3CNT_LO:
567			DS9DMAWriteCNT(&ds->ds9, 3, value);
568			break;
569
570		default:
571			DS9IOWrite(ds, address, value & 0xFFFF);
572			DS9IOWrite(ds, address | 2, value >> 16);
573			return;
574		}
575	}
576	ds->ds9.memory.io[address >> 1] = value;
577	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
578}
579
580uint16_t DS9IORead(struct DS* ds, uint32_t address) {
581	switch (address) {
582	case DS_REG_TM0CNT_LO:
583	case DS_REG_TM1CNT_LO:
584	case DS_REG_TM2CNT_LO:
585	case DS_REG_TM3CNT_LO:
586		DSIOUpdateTimer(&ds->ds9, address);
587		break;
588	case DS_REG_KEYINPUT:
589		return DSIOReadKeyInput(ds);
590	case DS_REG_VCOUNT:
591	case DS_REG_DMA0FILL_LO:
592	case DS_REG_DMA0FILL_HI:
593	case DS_REG_DMA1FILL_LO:
594	case DS_REG_DMA1FILL_HI:
595	case DS_REG_DMA2FILL_LO:
596	case DS_REG_DMA2FILL_HI:
597	case DS_REG_DMA3FILL_LO:
598	case DS_REG_DMA3FILL_HI:
599	case DS_REG_TM0CNT_HI:
600	case DS_REG_TM1CNT_HI:
601	case DS_REG_TM2CNT_HI:
602	case DS_REG_TM3CNT_HI:
603	case DS_REG_IPCSYNC:
604	case DS_REG_IPCFIFOCNT:
605	case DS_REG_ROMCNT_LO:
606	case DS_REG_ROMCNT_HI:
607	case DS_REG_IME:
608	case 0x20A:
609	case DS_REG_IE_LO:
610	case DS_REG_IE_HI:
611	case DS_REG_IF_LO:
612	case DS_REG_IF_HI:
613	case DS9_REG_DIVCNT:
614	case DS9_REG_DIV_NUMER_0:
615	case DS9_REG_DIV_NUMER_1:
616	case DS9_REG_DIV_NUMER_2:
617	case DS9_REG_DIV_NUMER_3:
618	case DS9_REG_DIV_DENOM_0:
619	case DS9_REG_DIV_DENOM_1:
620	case DS9_REG_DIV_DENOM_2:
621	case DS9_REG_DIV_DENOM_3:
622	case DS9_REG_DIV_RESULT_0:
623	case DS9_REG_DIV_RESULT_1:
624	case DS9_REG_DIV_RESULT_2:
625	case DS9_REG_DIV_RESULT_3:
626	case DS9_REG_DIVREM_RESULT_0:
627	case DS9_REG_DIVREM_RESULT_1:
628	case DS9_REG_DIVREM_RESULT_2:
629	case DS9_REG_DIVREM_RESULT_3:
630	case DS9_REG_SQRTCNT:
631	case DS9_REG_SQRT_PARAM_0:
632	case DS9_REG_SQRT_PARAM_1:
633	case DS9_REG_SQRT_PARAM_2:
634	case DS9_REG_SQRT_PARAM_3:
635	case DS9_REG_SQRT_RESULT_LO:
636	case DS9_REG_SQRT_RESULT_HI:
637	case DS_REG_POSTFLG:
638	case DS9_REG_GXSTAT_LO:
639	case DS9_REG_GXSTAT_HI:
640	case DS9_REG_CLIPMTX_RESULT_00:
641	case DS9_REG_CLIPMTX_RESULT_01:
642	case DS9_REG_CLIPMTX_RESULT_02:
643	case DS9_REG_CLIPMTX_RESULT_03:
644	case DS9_REG_CLIPMTX_RESULT_04:
645	case DS9_REG_CLIPMTX_RESULT_05:
646	case DS9_REG_CLIPMTX_RESULT_06:
647	case DS9_REG_CLIPMTX_RESULT_07:
648	case DS9_REG_CLIPMTX_RESULT_08:
649	case DS9_REG_CLIPMTX_RESULT_09:
650	case DS9_REG_CLIPMTX_RESULT_0A:
651	case DS9_REG_CLIPMTX_RESULT_0B:
652	case DS9_REG_CLIPMTX_RESULT_0C:
653	case DS9_REG_CLIPMTX_RESULT_0D:
654	case DS9_REG_CLIPMTX_RESULT_0E:
655	case DS9_REG_CLIPMTX_RESULT_0F:
656	case DS9_REG_CLIPMTX_RESULT_10:
657	case DS9_REG_CLIPMTX_RESULT_11:
658	case DS9_REG_CLIPMTX_RESULT_12:
659	case DS9_REG_CLIPMTX_RESULT_13:
660	case DS9_REG_CLIPMTX_RESULT_14:
661	case DS9_REG_CLIPMTX_RESULT_15:
662	case DS9_REG_CLIPMTX_RESULT_16:
663	case DS9_REG_CLIPMTX_RESULT_17:
664	case DS9_REG_CLIPMTX_RESULT_18:
665	case DS9_REG_CLIPMTX_RESULT_19:
666	case DS9_REG_CLIPMTX_RESULT_1A:
667	case DS9_REG_CLIPMTX_RESULT_1B:
668	case DS9_REG_CLIPMTX_RESULT_1C:
669	case DS9_REG_CLIPMTX_RESULT_1D:
670	case DS9_REG_CLIPMTX_RESULT_1E:
671	case DS9_REG_CLIPMTX_RESULT_1F:
672		// Handled transparently by the registers
673		break;
674	case DS_REG_AUXSPICNT:
675	case DS_REG_AUXSPIDATA:
676		if (ds->ds9.memory.slot1Access) {
677			break;
678		} else {
679			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
680			return 0;
681		}
682	default:
683		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
684	}
685	if (address < DS9_REG_MAX) {
686		return ds->ds9.memory.io[address >> 1];
687	}
688	return 0;
689}
690
691uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
692	switch (address) {
693	case DS_REG_IPCFIFORECV_LO:
694		return DSIPCReadFIFO(&ds->ds9);
695	case DS_REG_ROMDATA_0:
696		if (ds->ds9.memory.slot1Access) {
697			return DSSlot1Read(ds);
698		} else {
699			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
700			return 0;
701		}
702	default:
703		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
704	}
705}