all repos — mgba @ 74ac89a584b068b9d9c02e5117eddbe6da22d9c5

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