all repos — mgba @ 4fd170ac38a9c312f3dd1d0dd912d2c761f60fdb

mGBA Game Boy Advance Emulator

src/ds/io.c (view raw)

  1/* Copyright (c) 2013-2017 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/audio.h>
 10#include <mgba/internal/ds/ds.h>
 11#include <mgba/internal/ds/gx.h>
 12#include <mgba/internal/ds/ipc.h>
 13#include <mgba/internal/ds/slot1.h>
 14#include <mgba/internal/ds/spi.h>
 15
 16mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O", "ds.io");
 17
 18static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
 19	switch (value >> 6) {
 20	case 0:
 21	default:
 22		break;
 23	case 1:
 24		mLOG(DS_IO, STUB, "Enter GBA mode not supported");
 25		break;
 26	case 2:
 27		ARMHalt(dscore->cpu);
 28		break;
 29	case 3:
 30		mLOG(DS_IO, STUB, "Enter sleep mode not supported");
 31		break;
 32	}
 33}
 34
 35static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) {
 36	mTimingDeschedule(&ds->ds9.timing, &ds->divEvent);
 37	mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68);
 38	return control | 0x8000;
 39}
 40
 41static uint16_t _scheduleSqrt(struct DS* ds, uint16_t control) {
 42	mTimingDeschedule(&ds->ds9.timing, &ds->sqrtEvent);
 43	mTimingSchedule(&ds->ds9.timing, &ds->sqrtEvent, 26);
 44	return control | 0x8000;
 45}
 46
 47static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
 48	switch (address) {
 49	// Video
 50	case DS_REG_DISPSTAT:
 51		DSVideoWriteDISPSTAT(dscore, value);
 52		break;
 53
 54	// DMA Fill
 55	case DS_REG_DMA0FILL_LO:
 56	case DS_REG_DMA0FILL_HI:
 57	case DS_REG_DMA1FILL_LO:
 58	case DS_REG_DMA1FILL_HI:
 59	case DS_REG_DMA2FILL_LO:
 60	case DS_REG_DMA2FILL_HI:
 61	case DS_REG_DMA3FILL_LO:
 62	case DS_REG_DMA3FILL_HI:
 63		break;
 64
 65	// Timers
 66	case DS_REG_TM0CNT_LO:
 67		GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
 68		return 0x20000;
 69	case DS_REG_TM1CNT_LO:
 70		GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
 71		return 0x20000;
 72	case DS_REG_TM2CNT_LO:
 73		GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
 74		return 0x20000;
 75	case DS_REG_TM3CNT_LO:
 76		GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
 77		return 0x20000;
 78
 79	case DS_REG_TM0CNT_HI:
 80		value &= 0x00C7;
 81		DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
 82		break;
 83	case DS_REG_TM1CNT_HI:
 84		value &= 0x00C7;
 85		DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
 86		break;
 87	case DS_REG_TM2CNT_HI:
 88		value &= 0x00C7;
 89		DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
 90		break;
 91	case DS_REG_TM3CNT_HI:
 92		value &= 0x00C7;
 93		DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
 94		break;
 95
 96	// IPC
 97	case DS_REG_IPCSYNC:
 98		value &= 0x6F00;
 99		value |= dscore->memory.io[address >> 1] & 0x000F;
100		DSIPCWriteSYNC(dscore->ipc->cpu, dscore->ipc->memory.io, value);
101		break;
102	case DS_REG_IPCFIFOCNT:
103		value = DSIPCWriteFIFOCNT(dscore, value);
104		break;
105
106	// Cart bus
107	case DS_REG_AUXSPICNT:
108		if (dscore->memory.slot1Access) {
109			value = DSSlot1Configure(dscore->p, value);
110			dscore->ipc->memory.io[address >> 1] = value;
111		} else {
112			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
113			return 0;
114		}
115		break;
116	case DS_REG_AUXSPIDATA:
117		if (dscore->memory.slot1Access) {
118			DSSlot1WriteSPI(dscore, value);
119			dscore->ipc->memory.io[address >> 1] = value;
120		} else {
121			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
122			return 0;
123		}
124		break;
125	case DS_REG_ROMCNT_HI:
126		if (dscore->memory.slot1Access) {
127			DSSlot1ROMCNT cnt = value << 16;
128			cnt |= dscore->memory.io[(address - 2) >> 1];
129			cnt = DSSlot1Control(dscore->p, cnt);
130			value = cnt >> 16;
131			dscore->ipc->memory.io[address >> 1] = value;
132		} else {
133			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
134			return 0;
135		}
136		break;
137	case DS_REG_ROMCNT_LO:
138	case DS_REG_ROMCMD_0:
139	case DS_REG_ROMCMD_2:
140	case DS_REG_ROMCMD_4:
141	case DS_REG_ROMCMD_6:
142		if (dscore->memory.slot1Access) {
143			dscore->ipc->memory.io[address >> 1] = value;
144		} else {
145			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
146			return 0;
147		}
148		break;
149
150	// Interrupts
151	case DS_REG_IME:
152		DSWriteIME(dscore->cpu, dscore->memory.io, value);
153		break;
154	case 0x20A:
155		value = 0;
156		// Some bad interrupt libraries will write to this
157		break;
158	case DS_REG_IF_LO:
159	case DS_REG_IF_HI:
160		value = dscore->memory.io[address >> 1] & ~value;
161		DSGXUpdateGXSTAT(&dscore->p->gx);
162		break;
163	default:
164		return 0;
165	}
166	return value | 0x10000;
167}
168
169uint32_t DSIOWrite32(struct DSCommon* dscore, uint32_t address, uint32_t value) {
170	switch (address) {
171	case DS_REG_DMA0SAD_LO:
172		value = DSDMAWriteSAD(dscore, 0, value);
173		break;
174	case DS_REG_DMA1SAD_LO:
175		value = DSDMAWriteSAD(dscore, 1, value);
176		break;
177	case DS_REG_DMA2SAD_LO:
178		value = DSDMAWriteSAD(dscore, 2, value);
179		break;
180	case DS_REG_DMA3SAD_LO:
181		value = DSDMAWriteSAD(dscore, 3, value);
182		break;
183
184	case DS_REG_DMA0DAD_LO:
185		value = DSDMAWriteDAD(dscore, 0, value);
186		break;
187	case DS_REG_DMA1DAD_LO:
188		value = DSDMAWriteDAD(dscore, 1, value);
189		break;
190	case DS_REG_DMA2DAD_LO:
191		value = DSDMAWriteDAD(dscore, 2, value);
192		break;
193	case DS_REG_DMA3DAD_LO:
194		value = DSDMAWriteDAD(dscore, 3, value);
195		break;
196
197	case DS_REG_IPCFIFOSEND_LO:
198		DSIPCWriteFIFO(dscore, value);
199		break;
200	case DS_REG_IE_LO:
201		DSWriteIE(dscore->cpu, dscore->memory.io, value);
202		break;
203	}
204
205	return value;
206}
207
208static uint16_t DSIOReadExKeyInput(struct DS* ds) {
209	uint16_t input = 0;
210	if (ds->keyCallback) {
211		input = ds->keyCallback->readKeys(ds->keyCallback);
212	} else if (ds->keySource) {
213		input = *ds->keySource;
214	}
215	input = ~(input >> 10) & 0x3;
216	input |= 0x3C;
217	input |= ds->memory.io7[DS7_REG_EXTKEYIN >> 1] & 0xC0;
218	return input;
219}
220
221static uint16_t DSIOReadKeyInput(struct DS* ds) {
222	uint16_t input = 0;
223	if (ds->keyCallback) {
224		input = ds->keyCallback->readKeys(ds->keyCallback);
225	} else if (ds->keySource) {
226		input = *ds->keySource;
227	}
228	// TODO: Put back
229	/*if (!dscore->p->allowOpposingDirections) {
230		unsigned rl = input & 0x030;
231		unsigned ud = input & 0x0C0;
232		input &= 0x30F;
233		if (rl != 0x030) {
234			input |= rl;
235		}
236		if (ud != 0x0C0) {
237			input |= ud;
238		}
239	}*/
240	return ~input & 0x3FF;
241}
242
243static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
244	switch (address) {
245	case DS_REG_TM0CNT_LO:
246		GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, &dscore->memory.io[address >> 1], 0);
247		break;
248	case DS_REG_TM1CNT_LO:
249		GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, &dscore->memory.io[address >> 1], 0);
250		break;
251	case DS_REG_TM2CNT_LO:
252		GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, &dscore->memory.io[address >> 1], 0);
253		break;
254	case DS_REG_TM3CNT_LO:
255		GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, &dscore->memory.io[address >> 1], 0);
256		break;
257	}
258}
259
260void DS7IOInit(struct DS* ds) {
261	memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
262	ds->memory.io7[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
263	ds->memory.io7[DS_REG_POSTFLG >> 1] = 0x0001;
264	ds->memory.io7[DS7_REG_EXTKEYIN >> 1] = 0x007F;
265}
266
267void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
268	switch (address) {
269	case DS_REG_DMA0CNT_HI:
270		DS7DMAWriteCNT(&ds->ds7, 0, (value << 16) | ds->ds7.memory.io[(address - 2) >> 1]);
271		break;
272	case DS_REG_DMA1CNT_HI:
273		DS7DMAWriteCNT(&ds->ds7, 1, (value << 16) | ds->ds7.memory.io[(address - 2) >> 1]);
274		break;
275	case DS_REG_DMA2CNT_HI:
276		DS7DMAWriteCNT(&ds->ds7, 2, (value << 16) | ds->ds7.memory.io[(address - 2) >> 1]);
277		break;
278	case DS_REG_DMA3CNT_HI:
279		DS7DMAWriteCNT(&ds->ds7, 3, (value << 16) | ds->ds7.memory.io[(address - 2) >> 1]);
280		break;
281	case DS7_REG_SPICNT:
282		value &= 0xCF83;
283		value = DSSPIWriteControl(ds, value);
284		break;
285	case DS7_REG_SPIDATA:
286		DSSPIWrite(ds, value);
287		break;
288	case DS7_REG_RTC:
289		value = DSWriteRTC(ds, value);
290		break;
291	case DS7_REG_SOUND0CNT_LO:
292	case DS7_REG_SOUND1CNT_LO:
293	case DS7_REG_SOUND2CNT_LO:
294	case DS7_REG_SOUND3CNT_LO:
295	case DS7_REG_SOUND4CNT_LO:
296	case DS7_REG_SOUND5CNT_LO:
297	case DS7_REG_SOUND6CNT_LO:
298	case DS7_REG_SOUND7CNT_LO:
299	case DS7_REG_SOUND8CNT_LO:
300	case DS7_REG_SOUND9CNT_LO:
301	case DS7_REG_SOUNDACNT_LO:
302	case DS7_REG_SOUNDBCNT_LO:
303	case DS7_REG_SOUNDCCNT_LO:
304	case DS7_REG_SOUNDDCNT_LO:
305	case DS7_REG_SOUNDECNT_LO:
306	case DS7_REG_SOUNDFCNT_LO:
307		value &= 0x837F;
308		DSAudioWriteSOUNDCNT_LO(&ds->audio, (address - DS7_REG_SOUND0CNT_LO) >> 4, value);
309		break;
310	case DS7_REG_SOUND0CNT_HI:
311	case DS7_REG_SOUND1CNT_HI:
312	case DS7_REG_SOUND2CNT_HI:
313	case DS7_REG_SOUND3CNT_HI:
314	case DS7_REG_SOUND4CNT_HI:
315	case DS7_REG_SOUND5CNT_HI:
316	case DS7_REG_SOUND6CNT_HI:
317	case DS7_REG_SOUND7CNT_HI:
318	case DS7_REG_SOUND8CNT_HI:
319	case DS7_REG_SOUND9CNT_HI:
320	case DS7_REG_SOUNDACNT_HI:
321	case DS7_REG_SOUNDBCNT_HI:
322	case DS7_REG_SOUNDCCNT_HI:
323	case DS7_REG_SOUNDDCNT_HI:
324	case DS7_REG_SOUNDECNT_HI:
325	case DS7_REG_SOUNDFCNT_HI:
326		value &= 0xFF7F;
327		DSAudioWriteSOUNDCNT_HI(&ds->audio, (address - DS7_REG_SOUND0CNT_HI) >> 4, value);
328		break;
329	case DS7_REG_SOUND0TMR:
330	case DS7_REG_SOUND1TMR:
331	case DS7_REG_SOUND2TMR:
332	case DS7_REG_SOUND3TMR:
333	case DS7_REG_SOUND4TMR:
334	case DS7_REG_SOUND5TMR:
335	case DS7_REG_SOUND6TMR:
336	case DS7_REG_SOUND7TMR:
337	case DS7_REG_SOUND8TMR:
338	case DS7_REG_SOUND9TMR:
339	case DS7_REG_SOUNDATMR:
340	case DS7_REG_SOUNDBTMR:
341	case DS7_REG_SOUNDCTMR:
342	case DS7_REG_SOUNDDTMR:
343	case DS7_REG_SOUNDETMR:
344	case DS7_REG_SOUNDFTMR:
345		DSAudioWriteSOUNDTMR(&ds->audio, (address - DS7_REG_SOUND0TMR) >> 4, value);
346		break;
347	case DS7_REG_SOUND0PNT:
348	case DS7_REG_SOUND1PNT:
349	case DS7_REG_SOUND2PNT:
350	case DS7_REG_SOUND3PNT:
351	case DS7_REG_SOUND4PNT:
352	case DS7_REG_SOUND5PNT:
353	case DS7_REG_SOUND6PNT:
354	case DS7_REG_SOUND7PNT:
355	case DS7_REG_SOUND8PNT:
356	case DS7_REG_SOUND9PNT:
357	case DS7_REG_SOUNDAPNT:
358	case DS7_REG_SOUNDBPNT:
359	case DS7_REG_SOUNDCPNT:
360	case DS7_REG_SOUNDDPNT:
361	case DS7_REG_SOUNDEPNT:
362	case DS7_REG_SOUNDFPNT:
363		DSAudioWriteSOUNDPNT(&ds->audio, (address - DS7_REG_SOUND0PNT) >> 4, value);
364		break;
365	default:
366		{
367			uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
368			if (v2 & 0x10000) {
369				value = v2;
370				break;
371			} else if (v2 & 0x20000) {
372				return;
373			}
374		}
375		if (address >= DS7_IO_BASE_WIFI && address < DS7_IO_END_WIFI) {
376			DSWifiWriteIO(ds, address & 0x7FFF, value);
377			return;
378		}
379		mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
380		if (address >= DS7_REG_MAX) {
381			mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
382			return;
383		}
384		break;
385	}
386	ds->memory.io7[address >> 1] = value;
387}
388
389void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
390	if (address == DS7_REG_HALTCNT) {
391		_DSHaltCNT(&ds->ds7, value);
392		return;
393	}
394	if (address < DS7_REG_MAX) {
395		uint16_t value16 = value << (8 * (address & 1));
396		value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
397		DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
398	} else {
399		mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
400	}
401}
402
403void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
404	switch (address) {
405	case DS_REG_DMA0SAD_LO:
406	case DS_REG_DMA1SAD_LO:
407	case DS_REG_DMA2SAD_LO:
408	case DS_REG_DMA3SAD_LO:
409	case DS_REG_DMA0DAD_LO:
410	case DS_REG_DMA1DAD_LO:
411	case DS_REG_DMA2DAD_LO:
412	case DS_REG_DMA3DAD_LO:
413	case DS_REG_IPCFIFOSEND_LO:
414	case DS_REG_IE_LO:
415		value = DSIOWrite32(&ds->ds7, address, value);
416		break;
417
418	case DS_REG_DMA0CNT_LO:
419		DS7DMAWriteCNT(&ds->ds7, 0, value);
420		break;
421	case DS_REG_DMA1CNT_LO:
422		DS7DMAWriteCNT(&ds->ds7, 1, value);
423		break;
424	case DS_REG_DMA2CNT_LO:
425		DS7DMAWriteCNT(&ds->ds7, 2, value);
426		break;
427	case DS_REG_DMA3CNT_LO:
428		DS7DMAWriteCNT(&ds->ds7, 3, value);
429		break;
430
431	case DS7_REG_SOUND0SAD_LO:
432	case DS7_REG_SOUND1SAD_LO:
433	case DS7_REG_SOUND2SAD_LO:
434	case DS7_REG_SOUND3SAD_LO:
435	case DS7_REG_SOUND4SAD_LO:
436	case DS7_REG_SOUND5SAD_LO:
437	case DS7_REG_SOUND6SAD_LO:
438	case DS7_REG_SOUND7SAD_LO:
439	case DS7_REG_SOUND8SAD_LO:
440	case DS7_REG_SOUND9SAD_LO:
441	case DS7_REG_SOUNDASAD_LO:
442	case DS7_REG_SOUNDBSAD_LO:
443	case DS7_REG_SOUNDCSAD_LO:
444	case DS7_REG_SOUNDDSAD_LO:
445	case DS7_REG_SOUNDESAD_LO:
446	case DS7_REG_SOUNDFSAD_LO:
447		DSAudioWriteSOUNDSAD(&ds->audio, (address - DS7_REG_SOUND0SAD_LO) >> 4, value);
448		break;
449
450	case DS7_REG_SOUND0LEN_LO:
451	case DS7_REG_SOUND1LEN_LO:
452	case DS7_REG_SOUND2LEN_LO:
453	case DS7_REG_SOUND3LEN_LO:
454	case DS7_REG_SOUND4LEN_LO:
455	case DS7_REG_SOUND5LEN_LO:
456	case DS7_REG_SOUND6LEN_LO:
457	case DS7_REG_SOUND7LEN_LO:
458	case DS7_REG_SOUND8LEN_LO:
459	case DS7_REG_SOUND9LEN_LO:
460	case DS7_REG_SOUNDALEN_LO:
461	case DS7_REG_SOUNDBLEN_LO:
462	case DS7_REG_SOUNDCLEN_LO:
463	case DS7_REG_SOUNDDLEN_LO:
464	case DS7_REG_SOUNDELEN_LO:
465	case DS7_REG_SOUNDFLEN_LO:
466		value &= 0x3FFFFF;
467		DSAudioWriteSOUNDLEN(&ds->audio, (address - DS7_REG_SOUND0LEN_LO) >> 4, value);
468		break;
469
470	default:
471		DS7IOWrite(ds, address, value & 0xFFFF);
472		DS7IOWrite(ds, address | 2, value >> 16);
473		return;
474	}
475	ds->ds7.memory.io[address >> 1] = value;
476	ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
477}
478
479uint16_t DS7IORead(struct DS* ds, uint32_t address) {
480	switch (address) {
481	case DS_REG_TM0CNT_LO:
482	case DS_REG_TM1CNT_LO:
483	case DS_REG_TM2CNT_LO:
484	case DS_REG_TM3CNT_LO:
485		DSIOUpdateTimer(&ds->ds7, address);
486		break;
487	case DS_REG_KEYINPUT:
488		return DSIOReadKeyInput(ds);
489	case DS7_REG_EXTKEYIN:
490		return DSIOReadExKeyInput(ds);
491	case DS_REG_VCOUNT:
492	case DS_REG_DMA0CNT_HI:
493	case DS_REG_DMA1CNT_HI:
494	case DS_REG_DMA2CNT_HI:
495	case DS_REG_DMA3CNT_HI:
496	case DS_REG_DMA0FILL_LO:
497	case DS_REG_DMA0FILL_HI:
498	case DS_REG_DMA1FILL_LO:
499	case DS_REG_DMA1FILL_HI:
500	case DS_REG_DMA2FILL_LO:
501	case DS_REG_DMA2FILL_HI:
502	case DS_REG_DMA3FILL_LO:
503	case DS_REG_DMA3FILL_HI:
504	case DS_REG_TM0CNT_HI:
505	case DS_REG_TM1CNT_HI:
506	case DS_REG_TM2CNT_HI:
507	case DS_REG_TM3CNT_HI:
508	case DS7_REG_RTC:
509	case DS7_REG_SPICNT:
510	case DS7_REG_SPIDATA:
511	case DS_REG_IPCSYNC:
512	case DS_REG_IPCFIFOCNT:
513	case DS_REG_ROMCNT_LO:
514	case DS_REG_ROMCNT_HI:
515	case DS_REG_IME:
516	case 0x20A:
517	case DS_REG_IE_LO:
518	case DS_REG_IE_HI:
519	case DS_REG_IF_LO:
520	case DS_REG_IF_HI:
521	case DS_REG_POSTFLG:
522	case DS7_REG_SOUND0CNT_LO:
523	case DS7_REG_SOUND1CNT_LO:
524	case DS7_REG_SOUND2CNT_LO:
525	case DS7_REG_SOUND3CNT_LO:
526	case DS7_REG_SOUND4CNT_LO:
527	case DS7_REG_SOUND5CNT_LO:
528	case DS7_REG_SOUND6CNT_LO:
529	case DS7_REG_SOUND7CNT_LO:
530	case DS7_REG_SOUND8CNT_LO:
531	case DS7_REG_SOUND9CNT_LO:
532	case DS7_REG_SOUNDACNT_LO:
533	case DS7_REG_SOUNDBCNT_LO:
534	case DS7_REG_SOUNDCCNT_LO:
535	case DS7_REG_SOUNDDCNT_LO:
536	case DS7_REG_SOUNDECNT_LO:
537	case DS7_REG_SOUNDFCNT_LO:
538	case DS7_REG_SOUND0CNT_HI:
539	case DS7_REG_SOUND1CNT_HI:
540	case DS7_REG_SOUND2CNT_HI:
541	case DS7_REG_SOUND3CNT_HI:
542	case DS7_REG_SOUND4CNT_HI:
543	case DS7_REG_SOUND5CNT_HI:
544	case DS7_REG_SOUND6CNT_HI:
545	case DS7_REG_SOUND7CNT_HI:
546	case DS7_REG_SOUND8CNT_HI:
547	case DS7_REG_SOUND9CNT_HI:
548	case DS7_REG_SOUNDACNT_HI:
549	case DS7_REG_SOUNDBCNT_HI:
550	case DS7_REG_SOUNDCCNT_HI:
551	case DS7_REG_SOUNDDCNT_HI:
552	case DS7_REG_SOUNDECNT_HI:
553	case DS7_REG_SOUNDFCNT_HI:
554		// Handled transparently by the registers
555		break;
556	case DS_REG_AUXSPICNT:
557	case DS_REG_AUXSPIDATA:
558		if (ds->ds7.memory.slot1Access) {
559			break;
560		} else {
561			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
562			return 0;
563		}
564	default:
565		if (address >= DS7_IO_BASE_WIFI && address < DS7_IO_END_WIFI) {
566			return DSWifiReadIO(ds, address & 0x7FFF);
567		}
568		mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address);
569	}
570	if (address < DS7_REG_MAX) {
571		return ds->memory.io7[address >> 1];
572	}
573
574	return 0;
575}
576
577uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
578	switch (address) {
579	case DS_REG_IPCFIFORECV_LO:
580		return DSIPCReadFIFO(&ds->ds7);
581	case DS_REG_ROMDATA_0:
582		if (ds->ds7.memory.slot1Access) {
583			return DSSlot1Read(ds);
584		} else {
585			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
586			return 0;
587		}
588	default:
589		return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
590	}
591}
592
593void DS9IOInit(struct DS* ds) {
594	memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
595	ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
596	ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
597	ds->memory.io9[DS9_REG_GXSTAT_HI >> 1] = 0x0600;
598	DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
599	DS9IOWrite(ds, DS9_REG_POWCNT1, 0x0001);
600}
601
602void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
603	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) {
604		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
605	} else if ((address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) || address == DS9_REG_B_MASTER_BRIGHT) {
606		value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
607	} else if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
608		value = DSGXWriteRegister(&ds->gx, address, value);
609	} else {
610		uint16_t oldValue;
611		switch (address) {
612		// DMA
613		case DS_REG_DMA0CNT_HI:
614			DS9DMAWriteCNT(&ds->ds9, 0, (value << 16) | ds->ds9.memory.io[(address - 2) >> 1]);
615			break;
616		case DS_REG_DMA1CNT_HI:
617			DS9DMAWriteCNT(&ds->ds9, 1, (value << 16) | ds->ds9.memory.io[(address - 2) >> 1]);
618			break;
619		case DS_REG_DMA2CNT_HI:
620			DS9DMAWriteCNT(&ds->ds9, 2, (value << 16) | ds->ds9.memory.io[(address - 2) >> 1]);
621			break;
622		case DS_REG_DMA3CNT_HI:
623			DS9DMAWriteCNT(&ds->ds9, 3, (value << 16) | ds->ds9.memory.io[(address - 2) >> 1]);
624			break;
625
626		// Other video
627		case DS9_REG_DISPCAPCNT_LO:
628			value &= 0x1F1F;
629			break;
630		case DS9_REG_DISPCAPCNT_HI:
631			value &= 0xEF3F;
632			break;
633
634		// VRAM control
635		case DS9_REG_VRAMCNT_A:
636		case DS9_REG_VRAMCNT_C:
637		case DS9_REG_VRAMCNT_E:
638			oldValue = ds->memory.io9[address >> 1];
639			value &= 0x9F9F;
640			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
641			DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
642			break;
643		case DS9_REG_VRAMCNT_G:
644			oldValue = ds->memory.io9[address >> 1];
645			value &= 0x039F;
646			DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
647			DSConfigureWRAM(&ds->memory, value >> 8);
648			break;
649		case DS9_REG_VRAMCNT_H:
650			oldValue = ds->memory.io9[address >> 1];
651			value &= 0x9F9F;
652			DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
653			DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
654			break;
655
656		case DS9_REG_EXMEMCNT:
657			value &= 0xE8FF;
658			DSConfigureExternalMemory(ds, value);
659			break;
660
661		// Math
662		case DS9_REG_DIVCNT:
663			value = _scheduleDiv(ds, value);
664			break;
665		case DS9_REG_DIV_NUMER_0:
666		case DS9_REG_DIV_NUMER_1:
667		case DS9_REG_DIV_NUMER_2:
668		case DS9_REG_DIV_NUMER_3:
669		case DS9_REG_DIV_DENOM_0:
670		case DS9_REG_DIV_DENOM_1:
671		case DS9_REG_DIV_DENOM_2:
672		case DS9_REG_DIV_DENOM_3:
673			ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]);
674			break;
675		case DS9_REG_SQRTCNT:
676			value = _scheduleSqrt(ds, value);
677			break;
678		case DS9_REG_SQRT_PARAM_0:
679		case DS9_REG_SQRT_PARAM_1:
680		case DS9_REG_SQRT_PARAM_2:
681		case DS9_REG_SQRT_PARAM_3:
682			ds->memory.io9[DS9_REG_SQRTCNT >> 1] = _scheduleSqrt(ds, ds->memory.io9[DS9_REG_SQRTCNT >> 1]);
683			break;
684
685		// High Video
686		case DS9_REG_POWCNT1:
687			value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
688			break;
689
690		default:
691			{
692				uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
693				if (v2 & 0x10000) {
694					value = v2;
695					break;
696				} else if (v2 & 0x20000) {
697					return;
698				}
699			}
700			mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
701			if (address >= DS7_REG_MAX) {
702				mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
703				return;
704			}
705			break;
706		}
707	}
708	ds->memory.io9[address >> 1] = value;
709}
710
711void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
712	if (address < DS9_REG_MAX) {
713		uint16_t value16 = value << (8 * (address & 1));
714		value16 |= (ds->memory.io9[(address & 0x1FFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
715		DS9IOWrite(ds, address & 0xFFFFFFFE, value16);
716	} else {
717		mLOG(DS, STUB, "Writing to unknown DS9 register: %08X:%02X", address, value);
718	}
719}
720
721void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
722	if ((address >= DS9_REG_RDLINES_COUNT && address <= DS9_REG_VECMTX_RESULT_12) || address == DS9_REG_DISP3DCNT) {
723		value = DSGXWriteRegister32(&ds->gx, address, value);
724	} else {
725		switch (address) {
726		case DS_REG_DMA0SAD_LO:
727		case DS_REG_DMA1SAD_LO:
728		case DS_REG_DMA2SAD_LO:
729		case DS_REG_DMA3SAD_LO:
730		case DS_REG_DMA0DAD_LO:
731		case DS_REG_DMA1DAD_LO:
732		case DS_REG_DMA2DAD_LO:
733		case DS_REG_DMA3DAD_LO:
734		case DS_REG_IPCFIFOSEND_LO:
735		case DS_REG_IE_LO:
736			value = DSIOWrite32(&ds->ds9, address, value);
737			break;
738
739		case DS_REG_DMA0CNT_LO:
740			DS9DMAWriteCNT(&ds->ds9, 0, value);
741			break;
742		case DS_REG_DMA1CNT_LO:
743			DS9DMAWriteCNT(&ds->ds9, 1, value);
744			break;
745		case DS_REG_DMA2CNT_LO:
746			DS9DMAWriteCNT(&ds->ds9, 2, value);
747			break;
748		case DS_REG_DMA3CNT_LO:
749			DS9DMAWriteCNT(&ds->ds9, 3, value);
750			break;
751
752		default:
753			DS9IOWrite(ds, address, value & 0xFFFF);
754			DS9IOWrite(ds, address | 2, value >> 16);
755			return;
756		}
757	}
758	ds->ds9.memory.io[address >> 1] = value;
759	ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
760}
761
762uint16_t DS9IORead(struct DS* ds, uint32_t address) {
763	switch (address) {
764	case DS_REG_TM0CNT_LO:
765	case DS_REG_TM1CNT_LO:
766	case DS_REG_TM2CNT_LO:
767	case DS_REG_TM3CNT_LO:
768		DSIOUpdateTimer(&ds->ds9, address);
769		break;
770	case DS_REG_KEYINPUT:
771		return DSIOReadKeyInput(ds);
772	case DS_REG_VCOUNT:
773	case DS_REG_DMA0CNT_HI:
774	case DS_REG_DMA1CNT_HI:
775	case DS_REG_DMA2CNT_HI:
776	case DS_REG_DMA3CNT_HI:
777	case DS_REG_DMA0FILL_LO:
778	case DS_REG_DMA0FILL_HI:
779	case DS_REG_DMA1FILL_LO:
780	case DS_REG_DMA1FILL_HI:
781	case DS_REG_DMA2FILL_LO:
782	case DS_REG_DMA2FILL_HI:
783	case DS_REG_DMA3FILL_LO:
784	case DS_REG_DMA3FILL_HI:
785	case DS_REG_TM0CNT_HI:
786	case DS_REG_TM1CNT_HI:
787	case DS_REG_TM2CNT_HI:
788	case DS_REG_TM3CNT_HI:
789	case DS_REG_IPCSYNC:
790	case DS_REG_IPCFIFOCNT:
791	case DS_REG_ROMCNT_LO:
792	case DS_REG_ROMCNT_HI:
793	case DS_REG_IME:
794	case 0x20A:
795	case DS_REG_IE_LO:
796	case DS_REG_IE_HI:
797	case DS_REG_IF_LO:
798	case DS_REG_IF_HI:
799	case DS9_REG_DIVCNT:
800	case 0x282:
801	case DS9_REG_DIV_NUMER_0:
802	case DS9_REG_DIV_NUMER_1:
803	case DS9_REG_DIV_NUMER_2:
804	case DS9_REG_DIV_NUMER_3:
805	case DS9_REG_DIV_DENOM_0:
806	case DS9_REG_DIV_DENOM_1:
807	case DS9_REG_DIV_DENOM_2:
808	case DS9_REG_DIV_DENOM_3:
809	case DS9_REG_DIV_RESULT_0:
810	case DS9_REG_DIV_RESULT_1:
811	case DS9_REG_DIV_RESULT_2:
812	case DS9_REG_DIV_RESULT_3:
813	case DS9_REG_DIVREM_RESULT_0:
814	case DS9_REG_DIVREM_RESULT_1:
815	case DS9_REG_DIVREM_RESULT_2:
816	case DS9_REG_DIVREM_RESULT_3:
817	case DS9_REG_SQRTCNT:
818	case DS9_REG_SQRT_PARAM_0:
819	case DS9_REG_SQRT_PARAM_1:
820	case DS9_REG_SQRT_PARAM_2:
821	case DS9_REG_SQRT_PARAM_3:
822	case DS9_REG_SQRT_RESULT_LO:
823	case DS9_REG_SQRT_RESULT_HI:
824	case DS_REG_POSTFLG:
825	case DS9_REG_TOON_TABLE_00:
826	case DS9_REG_TOON_TABLE_01:
827	case DS9_REG_TOON_TABLE_02:
828	case DS9_REG_TOON_TABLE_03:
829	case DS9_REG_TOON_TABLE_04:
830	case DS9_REG_TOON_TABLE_05:
831	case DS9_REG_TOON_TABLE_06:
832	case DS9_REG_TOON_TABLE_07:
833	case DS9_REG_TOON_TABLE_08:
834	case DS9_REG_TOON_TABLE_09:
835	case DS9_REG_TOON_TABLE_0A:
836	case DS9_REG_TOON_TABLE_0B:
837	case DS9_REG_TOON_TABLE_0C:
838	case DS9_REG_TOON_TABLE_0D:
839	case DS9_REG_TOON_TABLE_0E:
840	case DS9_REG_TOON_TABLE_0F:
841	case DS9_REG_TOON_TABLE_10:
842	case DS9_REG_TOON_TABLE_11:
843	case DS9_REG_TOON_TABLE_12:
844	case DS9_REG_TOON_TABLE_13:
845	case DS9_REG_TOON_TABLE_14:
846	case DS9_REG_TOON_TABLE_15:
847	case DS9_REG_TOON_TABLE_16:
848	case DS9_REG_TOON_TABLE_17:
849	case DS9_REG_TOON_TABLE_18:
850	case DS9_REG_TOON_TABLE_19:
851	case DS9_REG_TOON_TABLE_1A:
852	case DS9_REG_TOON_TABLE_1B:
853	case DS9_REG_TOON_TABLE_1C:
854	case DS9_REG_TOON_TABLE_1D:
855	case DS9_REG_TOON_TABLE_1E:
856	case DS9_REG_TOON_TABLE_1F:
857	case DS9_REG_GXSTAT_LO:
858	case DS9_REG_GXSTAT_HI:
859	case DS9_REG_CLIPMTX_RESULT_00:
860	case DS9_REG_CLIPMTX_RESULT_01:
861	case DS9_REG_CLIPMTX_RESULT_02:
862	case DS9_REG_CLIPMTX_RESULT_03:
863	case DS9_REG_CLIPMTX_RESULT_04:
864	case DS9_REG_CLIPMTX_RESULT_05:
865	case DS9_REG_CLIPMTX_RESULT_06:
866	case DS9_REG_CLIPMTX_RESULT_07:
867	case DS9_REG_CLIPMTX_RESULT_08:
868	case DS9_REG_CLIPMTX_RESULT_09:
869	case DS9_REG_CLIPMTX_RESULT_0A:
870	case DS9_REG_CLIPMTX_RESULT_0B:
871	case DS9_REG_CLIPMTX_RESULT_0C:
872	case DS9_REG_CLIPMTX_RESULT_0D:
873	case DS9_REG_CLIPMTX_RESULT_0E:
874	case DS9_REG_CLIPMTX_RESULT_0F:
875	case DS9_REG_CLIPMTX_RESULT_10:
876	case DS9_REG_CLIPMTX_RESULT_11:
877	case DS9_REG_CLIPMTX_RESULT_12:
878	case DS9_REG_CLIPMTX_RESULT_13:
879	case DS9_REG_CLIPMTX_RESULT_14:
880	case DS9_REG_CLIPMTX_RESULT_15:
881	case DS9_REG_CLIPMTX_RESULT_16:
882	case DS9_REG_CLIPMTX_RESULT_17:
883	case DS9_REG_CLIPMTX_RESULT_18:
884	case DS9_REG_CLIPMTX_RESULT_19:
885	case DS9_REG_CLIPMTX_RESULT_1A:
886	case DS9_REG_CLIPMTX_RESULT_1B:
887	case DS9_REG_CLIPMTX_RESULT_1C:
888	case DS9_REG_CLIPMTX_RESULT_1D:
889	case DS9_REG_CLIPMTX_RESULT_1E:
890	case DS9_REG_CLIPMTX_RESULT_1F:
891		// Handled transparently by the registers
892		break;
893	case DS_REG_AUXSPICNT:
894	case DS_REG_AUXSPIDATA:
895		if (ds->ds9.memory.slot1Access) {
896			break;
897		} else {
898			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
899			return 0;
900		}
901	default:
902		mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
903	}
904	if (address < DS9_REG_MAX) {
905		return ds->ds9.memory.io[address >> 1];
906	}
907	return 0;
908}
909
910uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
911	switch (address) {
912	case DS_REG_IPCFIFORECV_LO:
913		return DSIPCReadFIFO(&ds->ds9);
914	case DS_REG_ROMDATA_0:
915		if (ds->ds9.memory.slot1Access) {
916			return DSSlot1Read(ds);
917		} else {
918			mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
919			return 0;
920		}
921	default:
922		return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
923	}
924}