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