all repos — mgba @ 97b82ae6cdd2202e41d3c91df07979048d46a79f

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