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