all repos — mgba @ 8891abaca842dab75858c52a9c13feb20c0bfc51

mGBA Game Boy Advance Emulator

src/gba/io.c (view raw)

  1/* Copyright (c) 2013-2015 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 "io.h"
  7
  8#include "gba/supervisor/rr.h"
  9#include "gba/serialize.h"
 10#include "gba/sio.h"
 11#include "gba/video.h"
 12
 13const char* GBAIORegisterNames[] = {
 14	// Video
 15	"DISPCNT",
 16	0,
 17	"DISPSTAT",
 18	"VCOUNT",
 19	"BG0CNT",
 20	"BG1CNT",
 21	"BG2CNT",
 22	"BG3CNT",
 23	"BG0HOFS",
 24	"BG0VOFS",
 25	"BG1HOFS",
 26	"BG1VOFS",
 27	"BG2HOFS",
 28	"BG2VOFS",
 29	"BG3HOFS",
 30	"BG3VOFS",
 31	"BG2PA",
 32	"BG2PB",
 33	"BG2PC",
 34	"BG2PD",
 35	"BG2X_LO",
 36	"BG2X_HI",
 37	"BG2Y_LO",
 38	"BG2Y_HI",
 39	"BG3PA",
 40	"BG3PB",
 41	"BG3PC",
 42	"BG3PD",
 43	"BG3X_LO",
 44	"BG3X_HI",
 45	"BG3Y_LO",
 46	"BG3Y_HI",
 47	"WIN0H",
 48	"WIN1H",
 49	"WIN0V",
 50	"WIN1V",
 51	"WININ",
 52	"WINOUT",
 53	"MOSAIC",
 54	0,
 55	"BLDCNT",
 56	"BLDALPHA",
 57	"BLDY",
 58	0,
 59	0,
 60	0,
 61	0,
 62	0,
 63
 64	// Sound
 65	"SOUND1CNT_LO",
 66	"SOUND1CNT_HI",
 67	"SOUND1CNT_X",
 68	0,
 69	"SOUND2CNT_LO",
 70	0,
 71	"SOUND2CNT_HI",
 72	0,
 73	"SOUND3CNT_LO",
 74	"SOUND3CNT_HI",
 75	"SOUND3CNT_X",
 76	0,
 77	"SOUND4CNT_LO",
 78	0,
 79	"SOUND4CNT_HI",
 80	0,
 81	"SOUNDCNT_LO",
 82	"SOUNDCNT_HI",
 83	"SOUNDCNT_X",
 84	0,
 85	"SOUNDBIAS",
 86	0,
 87	0,
 88	0,
 89	"WAVE_RAM0_LO",
 90	"WAVE_RAM0_HI",
 91	"WAVE_RAM1_LO",
 92	"WAVE_RAM1_HI",
 93	"WAVE_RAM2_LO",
 94	"WAVE_RAM2_HI",
 95	"WAVE_RAM3_LO",
 96	"WAVE_RAM3_HI",
 97	"FIFO_A_LO",
 98	"FIFO_A_HI",
 99	"FIFO_B_LO",
100	"FIFO_B_HI",
101	0,
102	0,
103	0,
104	0,
105
106	// DMA
107	"DMA0SAD_LO",
108	"DMA0SAD_HI",
109	"DMA0DAD_LO",
110	"DMA0DAD_HI",
111	"DMA0CNT_LO",
112	"DMA0CNT_HI",
113	"DMA1SAD_LO",
114	"DMA1SAD_HI",
115	"DMA1DAD_LO",
116	"DMA1DAD_HI",
117	"DMA1CNT_LO",
118	"DMA1CNT_HI",
119	"DMA2SAD_LO",
120	"DMA2SAD_HI",
121	"DMA2DAD_LO",
122	"DMA2DAD_HI",
123	"DMA2CNT_LO",
124	"DMA2CNT_HI",
125	"DMA3SAD_LO",
126	"DMA3SAD_HI",
127	"DMA3DAD_LO",
128	"DMA3DAD_HI",
129	"DMA3CNT_LO",
130	"DMA3CNT_HI",
131
132	0, 0, 0, 0, 0, 0, 0, 0,
133	0, 0, 0, 0, 0, 0, 0, 0,
134
135	// Timers
136	"TM0CNT_LO",
137	"TM0CNT_HI",
138	"TM1CNT_LO",
139	"TM1CNT_HI",
140	"TM2CNT_LO",
141	"TM2CNT_HI",
142	"TM3CNT_LO",
143	"TM3CNT_HI",
144
145	0, 0, 0, 0, 0, 0, 0, 0,
146
147	// SIO
148	"SIOMULTI0",
149	"SIOMULTI1",
150	"SIOMULTI2",
151	"SIOMULTI3",
152	"SIOCNT",
153	"SIOMLT_SEND",
154	0,
155	0,
156	"KEYINPUT",
157	"KEYCNT",
158	"RCNT",
159	0,
160	0,
161	0,
162	0,
163	0,
164	"JOYCNT",
165	0,
166	0,
167	0,
168	0,
169	0,
170	0,
171	0,
172	"JOY_RECV_LO",
173	"JOY_RECV_HI",
174	"JOY_TRANS_LO",
175	"JOY_TRANS_HI",
176	"JOYSTAT",
177	0,
178	0,
179	0,
180
181	0, 0, 0, 0, 0, 0, 0, 0,
182	0, 0, 0, 0, 0, 0, 0, 0,
183	0, 0, 0, 0, 0, 0, 0, 0,
184	0, 0, 0, 0, 0, 0, 0, 0,
185	0, 0, 0, 0, 0, 0, 0, 0,
186	0, 0, 0, 0, 0, 0, 0, 0,
187	0, 0, 0, 0, 0, 0, 0, 0,
188	0, 0, 0, 0, 0, 0, 0, 0,
189	0, 0, 0, 0, 0, 0, 0, 0,
190	0, 0, 0, 0, 0, 0, 0, 0,
191
192	// Interrupts, etc
193	"IE",
194	"IF",
195	"WAITCNT",
196	0,
197	"IME"
198};
199
200static const int _isValidRegister[REG_MAX >> 1] = {
201	// Video
202	1, 0, 1, 1, 1, 1, 1, 1,
203	1, 1, 1, 1, 1, 1, 1, 1,
204	1, 1, 1, 1, 1, 1, 1, 1,
205	1, 1, 1, 1, 1, 1, 1, 1,
206	1, 1, 1, 1, 1, 1, 1, 0,
207	1, 1, 1, 0, 0, 0, 0, 0,
208	// Audio
209	1, 1, 1, 0, 1, 0, 1, 0,
210	1, 1, 1, 0, 1, 0, 1, 0,
211	1, 1, 1, 0, 1, 0, 0, 0,
212	1, 1, 1, 1, 1, 1, 1, 1,
213	1, 1, 1, 1, 1, 0, 0, 0,
214	// DMA
215	1, 1, 1, 1, 1, 1, 1, 1,
216	1, 1, 1, 1, 1, 1, 1, 1,
217	1, 1, 1, 1, 1, 1, 1, 1,
218	0, 0, 0, 0, 0, 0, 0, 0,
219	0, 0, 0, 0, 0, 0, 0, 0,
220	// Timers
221	1, 1, 1, 1, 1, 1, 1, 1,
222	0, 0, 0, 0, 0, 0, 0, 0,
223	// SIO
224	1, 1, 1, 1, 1, 0, 0, 0,
225	1, 1, 1, 0, 0, 0, 0, 0,
226	1, 0, 0, 0, 0, 0, 0, 0,
227	1, 0, 1, 0, 1, 0, 0, 0,
228	0, 0, 0, 0, 0, 0, 0, 0,
229	0, 0, 0, 0, 0, 0, 0, 0,
230	0, 0, 0, 0, 0, 0, 0, 0,
231	0, 0, 0, 0, 0, 0, 0, 0,
232	0, 0, 0, 0, 0, 0, 0, 0,
233	0, 0, 0, 0, 0, 0, 0, 0,
234	0, 0, 0, 0, 0, 0, 0, 0,
235	0, 0, 0, 0, 0, 0, 0, 0,
236	0, 0, 0, 0, 0, 0, 0, 0,
237	0, 0, 0, 0, 0, 0, 0, 0,
238	// Interrupts
239	1, 1, 1, 0, 1
240};
241
242static const int _isSpecialRegister[REG_MAX >> 1] = {
243	// Video
244	0, 0, 1, 1, 0, 0, 0, 0,
245	0, 0, 0, 0, 0, 0, 0, 0,
246	0, 0, 0, 0, 0, 0, 0, 0,
247	0, 0, 0, 0, 0, 0, 0, 0,
248	0, 0, 0, 0, 0, 0, 0, 0,
249	0, 0, 0, 0, 0, 0, 0, 0,
250	// Audio
251	0, 0, 0, 0, 0, 0, 0, 0,
252	0, 0, 0, 0, 0, 0, 0, 0,
253	0, 0, 0, 0, 0, 0, 0, 0,
254	1, 1, 1, 1, 1, 1, 1, 1,
255	1, 1, 1, 1, 0, 0, 0, 0,
256	// DMA
257	0, 0, 0, 0, 0, 1, 0, 0,
258	0, 0, 0, 1, 0, 0, 0, 0,
259	0, 1, 0, 0, 0, 0, 0, 1,
260	0, 0, 0, 0, 0, 0, 0, 0,
261	0, 0, 0, 0, 0, 0, 0, 0,
262	// Timers
263	1, 1, 1, 1, 1, 1, 1, 1,
264	0, 0, 0, 0, 0, 0, 0, 0,
265	// SIO
266	1, 1, 1, 1, 1, 0, 0, 0,
267	1, 1, 1, 0, 0, 0, 0, 0,
268	1, 0, 0, 0, 0, 0, 0, 0,
269	1, 0, 1, 0, 1, 0, 0, 0,
270	0, 0, 0, 0, 0, 0, 0, 0,
271	0, 0, 0, 0, 0, 0, 0, 0,
272	0, 0, 0, 0, 0, 0, 0, 0,
273	0, 0, 0, 0, 0, 0, 0, 0,
274	0, 0, 0, 0, 0, 0, 0, 0,
275	0, 0, 0, 0, 0, 0, 0, 0,
276	0, 0, 0, 0, 0, 0, 0, 0,
277	0, 0, 0, 0, 0, 0, 0, 0,
278	0, 0, 0, 0, 0, 0, 0, 0,
279	0, 0, 0, 0, 0, 0, 0, 0,
280	// Interrupts
281	1, 1, 0, 0, 1
282};
283
284void GBAIOInit(struct GBA* gba) {
285	gba->memory.io[REG_DISPCNT >> 1] = 0x0080;
286	gba->memory.io[REG_RCNT >> 1] = RCNT_INITIAL;
287	gba->memory.io[REG_KEYINPUT >> 1] = 0x3FF;
288	gba->memory.io[REG_SOUNDBIAS >> 1] = 0x200;
289	gba->memory.io[REG_BG2PA >> 1] = 0x100;
290	gba->memory.io[REG_BG2PD >> 1] = 0x100;
291	gba->memory.io[REG_BG3PA >> 1] = 0x100;
292	gba->memory.io[REG_BG3PD >> 1] = 0x100;
293}
294
295void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
296	if (address < REG_SOUND1CNT_LO && address != REG_DISPSTAT) {
297		value = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value);
298	} else {
299		switch (address) {
300		// Video
301		case REG_DISPSTAT:
302			value &= 0xFFF8;
303			GBAVideoWriteDISPSTAT(&gba->video, value);
304			return;
305
306		// Audio
307		case REG_SOUND1CNT_LO:
308			GBAAudioWriteSOUND1CNT_LO(&gba->audio, value);
309			value &= 0x00FF;
310			break;
311		case REG_SOUND1CNT_HI:
312			GBAAudioWriteSOUND1CNT_HI(&gba->audio, value);
313			break;
314		case REG_SOUND1CNT_X:
315			GBAAudioWriteSOUND1CNT_X(&gba->audio, value);
316			value &= 0x47FF;
317			break;
318		case REG_SOUND2CNT_LO:
319			GBAAudioWriteSOUND2CNT_LO(&gba->audio, value);
320			break;
321		case REG_SOUND2CNT_HI:
322			GBAAudioWriteSOUND2CNT_HI(&gba->audio, value);
323			value &= 0x47FF;
324			break;
325		case REG_SOUND3CNT_LO:
326			GBAAudioWriteSOUND3CNT_LO(&gba->audio, value);
327			value &= 0x00E0;
328			break;
329		case REG_SOUND3CNT_HI:
330			GBAAudioWriteSOUND3CNT_HI(&gba->audio, value);
331			value &= 0xE000;
332			break;
333		case REG_SOUND3CNT_X:
334			GBAAudioWriteSOUND3CNT_X(&gba->audio, value);
335			// TODO: The low bits need to not be readable, but still 8-bit writable
336			value &= 0x43FF;
337			break;
338		case REG_SOUND4CNT_LO:
339			GBAAudioWriteSOUND4CNT_LO(&gba->audio, value);
340			value &= 0xFF00;
341			break;
342		case REG_SOUND4CNT_HI:
343			GBAAudioWriteSOUND4CNT_HI(&gba->audio, value);
344			value &= 0x40FF;
345			break;
346		case REG_SOUNDCNT_LO:
347			GBAAudioWriteSOUNDCNT_LO(&gba->audio, value);
348			break;
349		case REG_SOUNDCNT_HI:
350			GBAAudioWriteSOUNDCNT_HI(&gba->audio, value);
351			break;
352		case REG_SOUNDCNT_X:
353			GBAAudioWriteSOUNDCNT_X(&gba->audio, value);
354			break;
355		case REG_SOUNDBIAS:
356			GBAAudioWriteSOUNDBIAS(&gba->audio, value);
357			break;
358
359		case REG_WAVE_RAM0_LO:
360		case REG_WAVE_RAM1_LO:
361		case REG_WAVE_RAM2_LO:
362		case REG_WAVE_RAM3_LO:
363			GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value);
364			break;
365
366		case REG_WAVE_RAM0_HI:
367		case REG_WAVE_RAM1_HI:
368		case REG_WAVE_RAM2_HI:
369		case REG_WAVE_RAM3_HI:
370			GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));
371			break;
372
373		// TODO: Confirm this behavior on real hardware
374		case REG_FIFO_A_LO:
375		case REG_FIFO_B_LO:
376			if (gba->performingDMA) {
377				GBAAudioWriteFIFO16(&gba->audio, address, value);
378			} else {
379				GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value);
380			}
381			break;
382
383		case REG_FIFO_A_HI:
384		case REG_FIFO_B_HI:
385			if (gba->performingDMA) {
386				GBAAudioWriteFIFO16(&gba->audio, address, value);
387			} else {
388				GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));
389			}
390			break;
391
392		// DMA
393		case REG_DMA0SAD_LO:
394		case REG_DMA0DAD_LO:
395		case REG_DMA1SAD_LO:
396		case REG_DMA1DAD_LO:
397		case REG_DMA2SAD_LO:
398		case REG_DMA2DAD_LO:
399		case REG_DMA3SAD_LO:
400		case REG_DMA3DAD_LO:
401			GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value);
402			break;
403
404		case REG_DMA0SAD_HI:
405		case REG_DMA0DAD_HI:
406		case REG_DMA1SAD_HI:
407		case REG_DMA1DAD_HI:
408		case REG_DMA2SAD_HI:
409		case REG_DMA2DAD_HI:
410		case REG_DMA3SAD_HI:
411		case REG_DMA3DAD_HI:
412			GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));
413			break;
414
415		case REG_DMA0CNT_LO:
416			GBAMemoryWriteDMACNT_LO(gba, 0, value);
417			break;
418		case REG_DMA0CNT_HI:
419			value = GBAMemoryWriteDMACNT_HI(gba, 0, value);
420			break;
421		case REG_DMA1CNT_LO:
422			GBAMemoryWriteDMACNT_LO(gba, 1, value);
423			break;
424		case REG_DMA1CNT_HI:
425			value = GBAMemoryWriteDMACNT_HI(gba, 1, value);
426			break;
427		case REG_DMA2CNT_LO:
428			GBAMemoryWriteDMACNT_LO(gba, 2, value);
429			break;
430		case REG_DMA2CNT_HI:
431			value = GBAMemoryWriteDMACNT_HI(gba, 2, value);
432			break;
433		case REG_DMA3CNT_LO:
434			GBAMemoryWriteDMACNT_LO(gba, 3, value);
435			break;
436		case REG_DMA3CNT_HI:
437			value = GBAMemoryWriteDMACNT_HI(gba, 3, value);
438			break;
439
440		// Timers
441		case REG_TM0CNT_LO:
442			GBATimerWriteTMCNT_LO(gba, 0, value);
443			return;
444		case REG_TM1CNT_LO:
445			GBATimerWriteTMCNT_LO(gba, 1, value);
446			return;
447		case REG_TM2CNT_LO:
448			GBATimerWriteTMCNT_LO(gba, 2, value);
449			return;
450		case REG_TM3CNT_LO:
451			GBATimerWriteTMCNT_LO(gba, 3, value);
452			return;
453
454		case REG_TM0CNT_HI:
455			value &= 0x00C7;
456			GBATimerWriteTMCNT_HI(gba, 0, value);
457			break;
458		case REG_TM1CNT_HI:
459			value &= 0x00C7;
460			GBATimerWriteTMCNT_HI(gba, 1, value);
461			break;
462		case REG_TM2CNT_HI:
463			value &= 0x00C7;
464			GBATimerWriteTMCNT_HI(gba, 2, value);
465			break;
466		case REG_TM3CNT_HI:
467			value &= 0x00C7;
468			GBATimerWriteTMCNT_HI(gba, 3, value);
469			break;
470
471		// SIO
472		case REG_SIOCNT:
473			GBASIOWriteSIOCNT(&gba->sio, value);
474			break;
475		case REG_RCNT:
476			value &= 0xC1FF;
477			GBASIOWriteRCNT(&gba->sio, value);
478			break;
479		case REG_SIOMLT_SEND:
480			GBASIOWriteSIOMLT_SEND(&gba->sio, value);
481			break;
482
483		// Interrupts and misc
484		case REG_WAITCNT:
485			GBAAdjustWaitstates(gba, value);
486			break;
487		case REG_IE:
488			GBAWriteIE(gba, value);
489			break;
490		case REG_IF:
491			value = gba->memory.io[REG_IF >> 1] & ~value;
492			break;
493		case REG_IME:
494			GBAWriteIME(gba, value);
495			break;
496		case REG_MAX:
497			// Some bad interrupt libraries will write to this
498			break;
499		default:
500			GBALog(gba, GBA_LOG_STUB, "Stub I/O register write: %03x", address);
501			break;
502		}
503	}
504	gba->memory.io[address >> 1] = value;
505}
506
507void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
508	if (address == REG_HALTCNT) {
509		value &= 0x80;
510		if (!value) {
511			GBAHalt(gba);
512		} else {
513			GBALog(gba, GBA_LOG_STUB, "Stop unimplemented");
514		}
515		return;
516	}
517	uint16_t value16 = value << (8 * (address & 1));
518	value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
519	GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
520}
521
522void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
523	switch (address) {
524	case REG_WAVE_RAM0_LO:
525		GBAAudioWriteWaveRAM(&gba->audio, 0, value);
526		break;
527	case REG_WAVE_RAM1_LO:
528		GBAAudioWriteWaveRAM(&gba->audio, 1, value);
529		break;
530	case REG_WAVE_RAM2_LO:
531		GBAAudioWriteWaveRAM(&gba->audio, 2, value);
532		break;
533	case REG_WAVE_RAM3_LO:
534		GBAAudioWriteWaveRAM(&gba->audio, 3, value);
535		break;
536	case REG_FIFO_A_LO:
537	case REG_FIFO_B_LO:
538		GBAAudioWriteFIFO(&gba->audio, address, value);
539		break;
540	case REG_DMA0SAD_LO:
541		GBAMemoryWriteDMASAD(gba, 0, value);
542		break;
543	case REG_DMA0DAD_LO:
544		GBAMemoryWriteDMADAD(gba, 0, value);
545		break;
546	case REG_DMA1SAD_LO:
547		GBAMemoryWriteDMASAD(gba, 1, value);
548		break;
549	case REG_DMA1DAD_LO:
550		GBAMemoryWriteDMADAD(gba, 1, value);
551		break;
552	case REG_DMA2SAD_LO:
553		GBAMemoryWriteDMASAD(gba, 2, value);
554		break;
555	case REG_DMA2DAD_LO:
556		GBAMemoryWriteDMADAD(gba, 2, value);
557		break;
558	case REG_DMA3SAD_LO:
559		GBAMemoryWriteDMASAD(gba, 3, value);
560		break;
561	case REG_DMA3DAD_LO:
562		GBAMemoryWriteDMADAD(gba, 3, value);
563		break;
564	default:
565		GBAIOWrite(gba, address, value & 0xFFFF);
566		GBAIOWrite(gba, address | 2, value >> 16);
567		return;
568	}
569	gba->memory.io[address >> 1] = value;
570	gba->memory.io[(address >> 1) + 1] = value >> 16;
571}
572
573uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
574	gba->lastJump = -1; // IO reads need to invalidate detected idle loops
575	switch (address) {
576	case REG_TM0CNT_LO:
577		GBATimerUpdateRegister(gba, 0);
578		break;
579	case REG_TM1CNT_LO:
580		GBATimerUpdateRegister(gba, 1);
581		break;
582	case REG_TM2CNT_LO:
583		GBATimerUpdateRegister(gba, 2);
584		break;
585	case REG_TM3CNT_LO:
586		GBATimerUpdateRegister(gba, 3);
587		break;
588
589	case REG_KEYINPUT:
590		if (gba->rr && gba->rr->isPlaying(gba->rr)) {
591			return 0x3FF ^ gba->rr->queryInput(gba->rr);
592		} else if (gba->keySource) {
593			uint16_t input = *gba->keySource;
594			if (gba->rr && gba->rr->isRecording(gba->rr)) {
595				gba->rr->logInput(gba->rr, input);
596			}
597			return 0x3FF ^ input;
598		}
599		break;
600
601	case REG_SIOCNT:
602		return gba->sio.siocnt;
603	case REG_RCNT:
604		return gba->sio.rcnt;
605
606	case REG_DMA0CNT_LO:
607	case REG_DMA1CNT_LO:
608	case REG_DMA2CNT_LO:
609	case REG_DMA3CNT_LO:
610		// Write-only register
611		return 0;
612	case REG_DISPCNT:
613	case REG_DISPSTAT:
614	case REG_VCOUNT:
615	case REG_BG0CNT:
616	case REG_BG1CNT:
617	case REG_BG2CNT:
618	case REG_BG3CNT:
619	case REG_WININ:
620	case REG_WINOUT:
621	case REG_BLDCNT:
622	case REG_BLDALPHA:
623	case REG_SOUND1CNT_LO:
624	case REG_SOUND1CNT_HI:
625	case REG_SOUND1CNT_X:
626	case REG_SOUND2CNT_LO:
627	case REG_SOUND2CNT_HI:
628	case REG_SOUND3CNT_LO:
629	case REG_SOUND3CNT_HI:
630	case REG_SOUND3CNT_X:
631	case REG_SOUND4CNT_LO:
632	case REG_SOUND4CNT_HI:
633	case REG_SOUNDCNT_LO:
634	case REG_SOUNDCNT_HI:
635	case REG_DMA0CNT_HI:
636	case REG_DMA1CNT_HI:
637	case REG_DMA2CNT_HI:
638	case REG_DMA3CNT_HI:
639	case REG_SIOMULTI0:
640	case REG_SIOMULTI1:
641	case REG_SIOMULTI2:
642	case REG_SIOMULTI3:
643	case REG_SIOMLT_SEND:
644	case REG_IE:
645	case REG_IF:
646	case REG_WAITCNT:
647	case REG_IME:
648		// Handled transparently by registers
649		break;
650	case REG_MAX:
651		// Some bad interrupt libraries will read from this
652		break;
653	default:
654		GBALog(gba, GBA_LOG_STUB, "Stub I/O register read: %03x", address);
655		break;
656	}
657	return gba->memory.io[address >> 1];
658}
659
660void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
661	int i;
662	for (i = 0; i < REG_MAX; i += 2) {
663		if (_isSpecialRegister[i >> 1]) {
664			state->io[i >> 1] = gba->memory.io[i >> 1];
665		} else if (_isValidRegister[i >> 1]) {
666			state->io[i >> 1] = GBAIORead(gba, i);
667		}
668	}
669
670	for (i = 0; i < 4; ++i) {
671		state->io[(REG_DMA0CNT_LO + i * 12) >> 1] = gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1];
672		state->dma[i].nextSource = gba->memory.dma[i].nextSource;
673		state->dma[i].nextDest = gba->memory.dma[i].nextDest;
674		state->dma[i].nextCount = gba->memory.dma[i].nextCount;
675		state->dma[i].nextEvent = gba->memory.dma[i].nextEvent;
676	}
677
678	memcpy(state->timers, gba->timers, sizeof(state->timers));
679	GBAHardwareSerialize(&gba->memory.hw, state);
680}
681
682void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
683	int i;
684	for (i = 0; i < REG_MAX; i += 2) {
685		if (_isSpecialRegister[i >> 1]) {
686			gba->memory.io[i >> 1] = state->io[i >> 1];
687		} else if (_isValidRegister[i >> 1]) {
688			GBAIOWrite(gba, i, state->io[i >> 1]);
689		}
690	}
691
692	gba->timersEnabled = 0;
693	memcpy(gba->timers, state->timers, sizeof(gba->timers));
694	for (i = 0; i < 4; ++i) {
695		gba->memory.dma[i].reg = state->io[(REG_DMA0CNT_HI + i * 12) >> 1];
696		gba->memory.dma[i].nextSource = state->dma[i].nextSource;
697		gba->memory.dma[i].nextDest = state->dma[i].nextDest;
698		gba->memory.dma[i].nextCount = state->dma[i].nextCount;
699		gba->memory.dma[i].nextEvent = state->dma[i].nextEvent;
700		if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
701			GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]);
702		}
703
704		if (gba->timers[i].enable) {
705			gba->timersEnabled |= 1 << i;
706		}
707	}
708	GBAHardwareDeserialize(&gba->memory.hw, state);
709}