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}