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