all repos — mgba @ 0cf9bf75e28e632ca071bb59a15e4f1afa4b4b9c

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