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}