all repos — mgba @ c14da05d8dca225010677643c32fea5c0ac8517a

mGBA Game Boy Advance Emulator

src/gba/hardware.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 "hardware.h"
  7
  8#include "gba/io.h"
  9#include "gba/serialize.h"
 10#include "util/hash.h"
 11
 12#ifdef PSP2
 13#include <psp2/rtc.h>
 14#endif
 15
 16const int GBA_LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 };
 17
 18static void _readPins(struct GBACartridgeHardware* hw);
 19static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);
 20
 21static void _rtcReadPins(struct GBACartridgeHardware* hw);
 22static unsigned _rtcOutput(struct GBACartridgeHardware* hw);
 23static void _rtcProcessByte(struct GBACartridgeHardware* hw);
 24static void _rtcUpdateClock(struct GBACartridgeHardware* hw);
 25static unsigned _rtcBCD(unsigned value);
 26
 27static time_t _rtcGenericCallback(struct GBARTCSource* source);
 28
 29static void _gyroReadPins(struct GBACartridgeHardware* hw);
 30
 31static void _rumbleReadPins(struct GBACartridgeHardware* hw);
 32
 33static void _lightReadPins(struct GBACartridgeHardware* hw);
 34
 35static uint16_t _gbpRead(struct GBAKeyCallback*);
 36static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
 37static int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles);
 38
 39static const int RTC_BYTES[8] = {
 40	0, // Force reset
 41	0, // Empty
 42	7, // Date/Time
 43	0, // Force IRQ
 44	1, // Control register
 45	0, // Empty
 46	3, // Time
 47	0 // Empty
 48};
 49
 50void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
 51	hw->gpioBase = base;
 52	GBAHardwareClear(hw);
 53
 54	hw->gbpCallback.d.readKeys = _gbpRead;
 55	hw->gbpCallback.p = hw;
 56	hw->gbpDriver.d.init = 0;
 57	hw->gbpDriver.d.deinit = 0;
 58	hw->gbpDriver.d.load = 0;
 59	hw->gbpDriver.d.unload = 0;
 60	hw->gbpDriver.d.writeRegister = _gbpSioWriteRegister;
 61	hw->gbpDriver.d.processEvents = _gbpSioProcessEvents;
 62	hw->gbpDriver.p = hw;
 63}
 64
 65void GBAHardwareClear(struct GBACartridgeHardware* hw) {
 66	hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION);
 67	hw->direction = GPIO_WRITE_ONLY;
 68	hw->pinState = 0;
 69	hw->direction = 0;
 70
 71	if (hw->p->sio.drivers.normal == &hw->gbpDriver.d) {
 72		GBASIOSetDriver(&hw->p->sio, 0, SIO_NORMAL_32);
 73	}
 74}
 75
 76void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
 77	switch (address) {
 78	case GPIO_REG_DATA:
 79		hw->pinState &= ~hw->direction;
 80		hw->pinState |= value;
 81		_readPins(hw);
 82		break;
 83	case GPIO_REG_DIRECTION:
 84		hw->direction = value;
 85		break;
 86	case GPIO_REG_CONTROL:
 87		hw->readWrite = value;
 88		break;
 89	default:
 90		GBALog(hw->p, GBA_LOG_WARN, "Invalid GPIO address");
 91	}
 92	if (hw->readWrite) {
 93		uint16_t old = hw->gpioBase[0];
 94		old &= ~hw->direction;
 95		hw->gpioBase[0] = old | hw->pinState;
 96	} else {
 97		hw->gpioBase[0] = 0;
 98	}
 99}
100
101void GBAHardwareInitRTC(struct GBACartridgeHardware* hw) {
102	hw->devices |= HW_RTC;
103	hw->rtc.bytesRemaining = 0;
104
105	hw->rtc.transferStep = 0;
106
107	hw->rtc.bitsRead = 0;
108	hw->rtc.bits = 0;
109	hw->rtc.commandActive = 0;
110	hw->rtc.command = 0;
111	hw->rtc.control = 0x40;
112	memset(hw->rtc.time, 0, sizeof(hw->rtc.time));
113}
114
115void _readPins(struct GBACartridgeHardware* hw) {
116	if (hw->devices & HW_RTC) {
117		_rtcReadPins(hw);
118	}
119
120	if (hw->devices & HW_GYRO) {
121		_gyroReadPins(hw);
122	}
123
124	if (hw->devices & HW_RUMBLE) {
125		_rumbleReadPins(hw);
126	}
127
128	if (hw->devices & HW_LIGHT_SENSOR) {
129		_lightReadPins(hw);
130	}
131}
132
133void _outputPins(struct GBACartridgeHardware* hw, unsigned pins) {
134	if (hw->readWrite) {
135		uint16_t old = hw->gpioBase[0];
136		old &= hw->direction;
137		hw->pinState = old | (pins & ~hw->direction & 0xF);
138		hw->gpioBase[0] = hw->pinState;
139	}
140}
141
142// == RTC
143
144void _rtcReadPins(struct GBACartridgeHardware* hw) {
145	// Transfer sequence:
146	// P: 0 | 1 |  2 | 3
147	// == Initiate
148	// > HI | - | LO | -
149	// > HI | - | HI | -
150	// == Transfer bit (x8)
151	// > LO | x | HI | -
152	// > HI | - | HI | -
153	// < ?? | x | ?? | -
154	// == Terminate
155	// >  - | - | LO | -
156	switch (hw->rtc.transferStep) {
157	case 0:
158		if ((hw->pinState & 5) == 1) {
159			hw->rtc.transferStep = 1;
160		}
161		break;
162	case 1:
163		if ((hw->pinState & 5) == 5) {
164			hw->rtc.transferStep = 2;
165		}
166		break;
167	case 2:
168		if (!(hw->pinState & 1)) {
169			hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);
170			hw->rtc.bits |= ((hw->pinState & 2) >> 1) << hw->rtc.bitsRead;
171		} else {
172			if (hw->pinState & 4) {
173				// GPIO direction should always != reading
174				if (hw->direction & 2) {
175					if (RTCCommandDataIsReading(hw->rtc.command)) {
176						GBALog(hw->p, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
177					}
178					++hw->rtc.bitsRead;
179					if (hw->rtc.bitsRead == 8) {
180						_rtcProcessByte(hw);
181					}
182				} else {
183					_outputPins(hw, 5 | (_rtcOutput(hw) << 1));
184					++hw->rtc.bitsRead;
185					if (hw->rtc.bitsRead == 8) {
186						--hw->rtc.bytesRemaining;
187						if (hw->rtc.bytesRemaining <= 0) {
188							hw->rtc.commandActive = 0;
189							hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
190						}
191						hw->rtc.bitsRead = 0;
192					}
193				}
194			} else {
195				hw->rtc.bitsRead = 0;
196				hw->rtc.bytesRemaining = 0;
197				hw->rtc.commandActive = 0;
198				hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
199				hw->rtc.transferStep = 0;
200			}
201		}
202		break;
203	}
204}
205
206void _rtcProcessByte(struct GBACartridgeHardware* hw) {
207	--hw->rtc.bytesRemaining;
208	if (!hw->rtc.commandActive) {
209		RTCCommandData command;
210		command = hw->rtc.bits;
211		if (RTCCommandDataGetMagic(command) == 0x06) {
212			hw->rtc.command = command;
213
214			hw->rtc.bytesRemaining = RTC_BYTES[RTCCommandDataGetCommand(command)];
215			hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
216			switch (RTCCommandDataGetCommand(command)) {
217			case RTC_RESET:
218				hw->rtc.control = 0;
219				break;
220			case RTC_DATETIME:
221			case RTC_TIME:
222				_rtcUpdateClock(hw);
223				break;
224			case RTC_FORCE_IRQ:
225			case RTC_CONTROL:
226				break;
227			}
228		} else {
229			GBALog(hw->p, GBA_LOG_WARN, "Invalid RTC command byte: %02X", hw->rtc.bits);
230		}
231	} else {
232		switch (RTCCommandDataGetCommand(hw->rtc.command)) {
233		case RTC_CONTROL:
234			hw->rtc.control = hw->rtc.bits;
235			break;
236		case RTC_FORCE_IRQ:
237			GBALog(hw->p, GBA_LOG_STUB, "Unimplemented RTC command %u", RTCCommandDataGetCommand(hw->rtc.command));
238			break;
239		case RTC_RESET:
240		case RTC_DATETIME:
241		case RTC_TIME:
242			break;
243		}
244	}
245
246	hw->rtc.bits = 0;
247	hw->rtc.bitsRead = 0;
248	if (!hw->rtc.bytesRemaining) {
249		hw->rtc.commandActive = 0;
250		hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
251	}
252}
253
254unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
255	uint8_t outputByte = 0;
256	switch (RTCCommandDataGetCommand(hw->rtc.command)) {
257	case RTC_CONTROL:
258		outputByte = hw->rtc.control;
259		break;
260	case RTC_DATETIME:
261	case RTC_TIME:
262		outputByte = hw->rtc.time[7 - hw->rtc.bytesRemaining];
263		break;
264	case RTC_FORCE_IRQ:
265	case RTC_RESET:
266		break;
267	}
268	unsigned output = (outputByte >> hw->rtc.bitsRead) & 1;
269	return output;
270}
271
272void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
273	time_t t;
274	struct GBARTCSource* rtc = hw->p->rtcSource;
275	if (rtc) {
276		if (rtc->sample) {
277			rtc->sample(rtc);
278		}
279		t = rtc->unixTime(rtc);
280	} else {
281		t = time(0);
282	}
283	struct tm date;
284#ifdef _WIN32
285	localtime_s(&date, &t);
286#elif defined(PSP2)
287	SceRtcTime sceRtc;
288	sceRtcSetTime_t(&sceRtc, t);
289	date.tm_year = sceRtc.year;
290	date.tm_mon = sceRtc.month;
291	date.tm_mday = sceRtc.day;
292	date.tm_hour = sceRtc.hour;
293	date.tm_min = sceRtc.minutes;
294	date.tm_sec = sceRtc.seconds;
295	date.tm_wday = sceRtcGetDayOfWeek(sceRtc.year, sceRtc.month, sceRtc.day);
296#else
297	localtime_r(&t, &date);
298#endif
299	hw->rtc.time[0] = _rtcBCD(date.tm_year - 100);
300	hw->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
301	hw->rtc.time[2] = _rtcBCD(date.tm_mday);
302	hw->rtc.time[3] = _rtcBCD(date.tm_wday);
303	if (RTCControlIsHour24(hw->rtc.control)) {
304		hw->rtc.time[4] = _rtcBCD(date.tm_hour);
305	} else {
306		hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
307	}
308	hw->rtc.time[5] = _rtcBCD(date.tm_min);
309	hw->rtc.time[6] = _rtcBCD(date.tm_sec);
310}
311
312unsigned _rtcBCD(unsigned value) {
313	int counter = value % 10;
314	value /= 10;
315	counter += (value % 10) << 4;
316	return counter;
317}
318
319time_t _rtcGenericCallback(struct GBARTCSource* source) {
320	struct GBARTCGenericSource* rtc = (struct GBARTCGenericSource*) source;
321	switch (rtc->override) {
322	case RTC_NO_OVERRIDE:
323	default:
324		return time(0);
325	case RTC_FIXED:
326		return rtc->value;
327	case RTC_FAKE_EPOCH:
328		return rtc->value + rtc->p->video.frameCounter * (int64_t) VIDEO_TOTAL_LENGTH / GBA_ARM7TDMI_FREQUENCY;
329	}
330}
331
332void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba) {
333	rtc->p = gba;
334	rtc->override = RTC_NO_OVERRIDE;
335	rtc->value = 0;
336	rtc->d.sample = 0;
337	rtc->d.unixTime = _rtcGenericCallback;
338}
339
340// == Gyro
341
342void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) {
343	hw->devices |= HW_GYRO;
344	hw->gyroSample = 0;
345	hw->gyroEdge = 0;
346}
347
348void _gyroReadPins(struct GBACartridgeHardware* hw) {
349	struct GBARotationSource* gyro = hw->p->rotationSource;
350	if (!gyro || !gyro->readGyroZ) {
351		return;
352	}
353
354	if (hw->pinState & 1) {
355		if (gyro->sample) {
356			gyro->sample(gyro);
357		}
358		int32_t sample = gyro->readGyroZ(gyro);
359
360		// Normalize to ~12 bits, focused on 0x6C0
361		hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
362	}
363
364	if (hw->gyroEdge && !(hw->pinState & 2)) {
365		// Write bit on falling edge
366		unsigned bit = hw->gyroSample >> 15;
367		hw->gyroSample <<= 1;
368		_outputPins(hw, bit << 2);
369	}
370
371	hw->gyroEdge = !!(hw->pinState & 2);
372}
373
374// == Rumble
375
376void GBAHardwareInitRumble(struct GBACartridgeHardware* hw) {
377	hw->devices |= HW_RUMBLE;
378}
379
380void _rumbleReadPins(struct GBACartridgeHardware* hw) {
381	struct GBARumble* rumble = hw->p->rumble;
382	if (!rumble) {
383		return;
384	}
385
386	rumble->setRumble(rumble, !!(hw->pinState & 8));
387}
388
389// == Light sensor
390
391void GBAHardwareInitLight(struct GBACartridgeHardware* hw) {
392	hw->devices |= HW_LIGHT_SENSOR;
393	hw->lightCounter = 0;
394	hw->lightEdge = false;
395	hw->lightSample = 0xFF;
396}
397
398void _lightReadPins(struct GBACartridgeHardware* hw) {
399	if (hw->pinState & 4) {
400		// Boktai chip select
401		return;
402	}
403	if (hw->pinState & 2) {
404		struct GBALuminanceSource* lux = hw->p->luminanceSource;
405		GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Got reset");
406		hw->lightCounter = 0;
407		if (lux) {
408			lux->sample(lux);
409			hw->lightSample = lux->readLuminance(lux);
410		} else {
411			hw->lightSample = 0xFF;
412		}
413	}
414	if ((hw->pinState & 1) && hw->lightEdge) {
415		++hw->lightCounter;
416	}
417	hw->lightEdge = !(hw->pinState & 1);
418
419	bool sendBit = hw->lightCounter >= hw->lightSample;
420	_outputPins(hw, sendBit << 3);
421	GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Output %u with pins %u", hw->lightCounter, hw->pinState);
422}
423
424// == Tilt
425
426void GBAHardwareInitTilt(struct GBACartridgeHardware* hw) {
427	hw->devices |= HW_TILT;
428	hw->tiltX = 0xFFF;
429	hw->tiltY = 0xFFF;
430	hw->tiltState = 0;
431}
432
433void GBAHardwareTiltWrite(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) {
434	switch (address) {
435	case 0x8000:
436		if (value == 0x55) {
437			hw->tiltState = 1;
438		} else {
439			GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
440		}
441		break;
442	case 0x8100:
443		if (value == 0xAA && hw->tiltState == 1) {
444			hw->tiltState = 0;
445			struct GBARotationSource* rotationSource = hw->p->rotationSource;
446			if (!rotationSource || !rotationSource->readTiltX || !rotationSource->readTiltY) {
447				return;
448			}
449			if (rotationSource->sample) {
450				rotationSource->sample(rotationSource);
451			}
452			int32_t x = rotationSource->readTiltX(rotationSource);
453			int32_t y = rotationSource->readTiltY(rotationSource);
454			// Normalize to ~12 bits, focused on 0x3A0
455			hw->tiltX = (x >> 21) + 0x3A0; // Crop off an extra bit so that we can't go negative
456			hw->tiltY = (y >> 21) + 0x3A0;
457		} else {
458			GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
459		}
460		break;
461	default:
462		GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor write to %04x: %02x", address, value);
463		break;
464	}
465}
466
467uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
468	switch (address) {
469	case 0x8200:
470		return hw->tiltX & 0xFF;
471	case 0x8300:
472		return ((hw->tiltX >> 8) & 0xF) | 0x80;
473	case 0x8400:
474		return hw->tiltY & 0xFF;
475	case 0x8500:
476		return (hw->tiltY >> 8) & 0xF;
477	default:
478		GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor read from %04x", address);
479		break;
480	}
481	return 0xFF;
482}
483
484// == Game Boy Player
485
486static const uint16_t _logoPalette[] = {
487	0xFFDF, 0x640C, 0xE40C, 0xE42D, 0x644E, 0xE44E, 0xE46E, 0x68AF,
488	0xE8B0, 0x68D0, 0x68F0, 0x6911, 0xE911, 0x6D32, 0xED32, 0xED73,
489	0x6D93, 0xED94, 0x6DB4, 0xF1D5, 0x71F5, 0xF1F6, 0x7216, 0x7257,
490	0xF657, 0x7678, 0xF678, 0xF699, 0xF6B9, 0x76D9, 0xF6DA, 0x7B1B,
491	0xFB1B, 0xFB3C, 0x7B5C, 0x7B7D, 0xFF7D, 0x7F9D, 0x7FBE, 0x7FFF,
492	0x642D, 0x648E, 0xE88F, 0xE8F1, 0x6D52, 0x6D73, 0xF1B4, 0xF216,
493	0x7237, 0x7698, 0x7AFA, 0xFAFA, 0xFB5C, 0xFFBE, 0x7FDE, 0xFFFF,
494	0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
495};
496
497static const uint32_t _logoHash = 0xEEDA6963;
498
499static const uint32_t _gbpTxData[] = {
500	0x0000494E, 0x0000494E,
501	0xB6B1494E, 0xB6B1544E,
502	0xABB1544E, 0xABB14E45,
503	0xB1BA4E45, 0xB1BA4F44,
504	0xB0BB4F44, 0xB0BB8002,
505	0x10000010, 0x20000013,
506	0x30000003, 0x30000003,
507	0x30000003, 0x30000003,
508	0x30000003, 0x00000000,
509};
510
511static const uint32_t _gbpRxData[] = {
512	0x00000000, 0x494EB6B1,
513	0x494EB6B1, 0x544EB6B1,
514	0x544EABB1, 0x4E45ABB1,
515	0x4E45B1BA, 0x4F44B1BA,
516	0x4F44B0BB, 0x8000B0BB,
517	0x10000010, 0x20000013,
518	0x40000004, 0x40000004,
519	0x40000004, 0x40000004,
520	0x40000004, 0x40000004
521};
522
523bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) {
524	if (memcmp(video->palette, _logoPalette, sizeof(_logoPalette)) != 0) {
525		return false;
526	}
527	uint32_t hash = hash32(&video->renderer->vram[0x4000], 0x4000, 0);
528	return hash == _logoHash;
529}
530
531void GBAHardwarePlayerUpdate(struct GBA* gba) {
532	if (gba->memory.hw.devices & HW_GB_PLAYER) {
533		if (GBAHardwarePlayerCheckScreen(&gba->video)) {
534			++gba->memory.hw.gbpInputsPosted;
535			gba->memory.hw.gbpInputsPosted %= 3;
536			gba->keyCallback = &gba->memory.hw.gbpCallback.d;
537		} else {
538			// TODO: Save and restore
539			gba->keyCallback = 0;
540		}
541		gba->memory.hw.gbpTxPosition = 0;
542		return;
543	}
544	if (gba->keyCallback || gba->sio.drivers.normal) {
545		return;
546	}
547	if (GBAHardwarePlayerCheckScreen(&gba->video)) {
548		gba->memory.hw.devices |= HW_GB_PLAYER;
549		gba->memory.hw.gbpInputsPosted = 0;
550		gba->memory.hw.gbpNextEvent = INT_MAX;
551		gba->keyCallback = &gba->memory.hw.gbpCallback.d;
552		GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32);
553	}
554}
555
556uint16_t _gbpRead(struct GBAKeyCallback* callback) {
557	struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback;
558	if (gbpCallback->p->gbpInputsPosted == 2) {
559		return 0x30F;
560	}
561	return 0x3FF;
562}
563
564uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
565	struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver;
566	if (address == REG_SIOCNT) {
567		if (value & 0x0080) {
568			if (gbp->p->gbpTxPosition <= 16 && gbp->p->gbpTxPosition > 0) {
569				uint32_t rx = gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] << 16);
570				uint32_t expected = _gbpRxData[gbp->p->gbpTxPosition];
571				// TODO: Check expected
572				uint32_t mask = 0;
573				if (gbp->p->gbpTxPosition == 15) {
574					mask = 0x22;
575					if (gbp->p->p->rumble) {
576						gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == mask);
577					}
578				}
579			}
580			gbp->p->gbpNextEvent = 2048;
581		}
582		value &= 0x78FB;
583	}
584	return value;
585}
586
587int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles) {
588	struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver;
589	gbp->p->gbpNextEvent -= cycles;
590	if (gbp->p->gbpNextEvent <= 0) {
591		uint32_t tx = 0;
592		if (gbp->p->gbpTxPosition <= 16) {
593			tx = _gbpTxData[gbp->p->gbpTxPosition];
594			++gbp->p->gbpTxPosition;
595		}
596		gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx;
597		gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
598		if (gbp->d.p->normalControl.irq) {
599			GBARaiseIRQ(gbp->p->p, IRQ_SIO);
600		}
601		gbp->d.p->normalControl.start = 0;
602		gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt;
603		gbp->p->gbpNextEvent = INT_MAX;
604	}
605	return gbp->p->gbpNextEvent;
606}
607
608// == Serialization
609
610void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
611	state->hw.readWrite = hw->readWrite;
612	state->hw.pinState = hw->pinState;
613	state->hw.pinDirection = hw->direction;
614	state->hw.devices = hw->devices;
615	state->hw.rtc = hw->rtc;
616	state->hw.gyroSample = hw->gyroSample;
617	state->hw.gyroEdge = hw->gyroEdge;
618	state->hw.tiltSampleX = hw->tiltX;
619	state->hw.tiltSampleY = hw->tiltY;
620	state->hw.tiltState = hw->tiltState;
621	state->hw.lightCounter = hw->lightCounter;
622	state->hw.lightSample = hw->lightSample;
623	state->hw.lightEdge = hw->lightEdge;
624	state->hw.gbpInputsPosted = hw->gbpInputsPosted;
625	state->hw.gbpTxPosition = hw->gbpTxPosition;
626	state->hw.gbpNextEvent = hw->gbpNextEvent;
627}
628
629void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) {
630	hw->readWrite = state->hw.readWrite;
631	hw->pinState = state->hw.pinState;
632	hw->direction = state->hw.pinDirection;
633	hw->devices = state->hw.devices;
634	hw->rtc = state->hw.rtc;
635	hw->gyroSample = state->hw.gyroSample;
636	hw->gyroEdge = state->hw.gyroEdge;
637	hw->tiltX = state->hw.tiltSampleX;
638	hw->tiltY = state->hw.tiltSampleY;
639	hw->tiltState = state->hw.tiltState;
640	hw->lightCounter = state->hw.lightCounter;
641	hw->lightSample = state->hw.lightSample;
642	hw->lightEdge = state->hw.lightEdge;
643	hw->gbpInputsPosted = state->hw.gbpInputsPosted;
644	hw->gbpTxPosition = state->hw.gbpTxPosition;
645	hw->gbpNextEvent = state->hw.gbpNextEvent;
646	if (hw->devices & HW_GB_PLAYER) {
647		GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32);
648	}
649}