src/gba/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 "gba.h"
7
8#include "gba-hardware.h"
9#include "gba-serialize.h"
10
11#include <time.h>
12
13static void _readPins(struct GBACartridgeHardware* hw);
14static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);
15
16static void _rtcReadPins(struct GBACartridgeHardware* hw);
17static unsigned _rtcOutput(struct GBACartridgeHardware* hw);
18static void _rtcProcessByte(struct GBACartridgeHardware* hw);
19static void _rtcUpdateClock(struct GBACartridgeHardware* hw);
20static unsigned _rtcBCD(unsigned value);
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.packed = 0;
86 hw->rtc.control.packed = 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->p0) {
144 hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);
145 hw->rtc.bits |= hw->p1 << hw->rtc.bitsRead;
146 } else {
147 if (hw->p2) {
148 // GPIO direction should always != reading
149 if (hw->dir1) {
150 if (hw->rtc.command.reading) {
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.reading = 0;
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.reading = 0;
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 union RTCCommandData command;
185 command.packed = hw->rtc.bits;
186 if (command.magic == 0x06) {
187 hw->rtc.command = command;
188
189 hw->rtc.bytesRemaining = RTC_BYTES[hw->rtc.command.command];
190 hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
191 switch (command.command) {
192 case RTC_RESET:
193 hw->rtc.control.packed = 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 (hw->rtc.command.command) {
208 case RTC_CONTROL:
209 hw->rtc.control.packed = hw->rtc.bits;
210 break;
211 case RTC_FORCE_IRQ:
212 GBALog(hw->p, GBA_LOG_STUB, "Unimplemented RTC command %u", hw->rtc.command.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.reading = 0;
226 }
227}
228
229unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
230 uint8_t outputByte = 0;
231 switch (hw->rtc.command.command) {
232 case RTC_CONTROL:
233 outputByte = hw->rtc.control.packed;
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 rtc->sample(rtc);
252 t = rtc->unixTime(rtc);
253 } else {
254 t = time(0);
255 }
256 struct tm date;
257#ifdef _WIN32
258 date = *localtime(&t);
259#else
260 localtime_r(&t, &date);
261#endif
262 hw->rtc.time[0] = _rtcBCD(date.tm_year - 100);
263 hw->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
264 hw->rtc.time[2] = _rtcBCD(date.tm_mday);
265 hw->rtc.time[3] = _rtcBCD(date.tm_wday);
266 if (hw->rtc.control.hour24) {
267 hw->rtc.time[4] = _rtcBCD(date.tm_hour);
268 } else {
269 hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
270 }
271 hw->rtc.time[5] = _rtcBCD(date.tm_min);
272 hw->rtc.time[6] = _rtcBCD(date.tm_sec);
273}
274
275unsigned _rtcBCD(unsigned value) {
276 int counter = value % 10;
277 value /= 10;
278 counter += (value % 10) << 4;
279 return counter;
280}
281
282// == Gyro
283
284void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) {
285 hw->devices |= HW_GYRO;
286 hw->gyroSample = 0;
287 hw->gyroEdge = 0;
288}
289
290void _gyroReadPins(struct GBACartridgeHardware* hw) {
291 struct GBARotationSource* gyro = hw->p->rotationSource;
292 if (!gyro) {
293 return;
294 }
295
296 if (hw->p0) {
297 if (gyro->sample) {
298 gyro->sample(gyro);
299 }
300 int32_t sample = gyro->readGyroZ(gyro);
301
302 // Normalize to ~12 bits, focused on 0x6C0
303 hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
304 }
305
306 if (hw->gyroEdge && !hw->p1) {
307 // Write bit on falling edge
308 unsigned bit = hw->gyroSample >> 15;
309 hw->gyroSample <<= 1;
310 _outputPins(hw, bit << 2);
311 }
312
313 hw->gyroEdge = hw->p1;
314}
315
316// == Rumble
317
318void GBAHardwareInitRumble(struct GBACartridgeHardware* hw) {
319 hw->devices |= HW_RUMBLE;
320}
321
322void _rumbleReadPins(struct GBACartridgeHardware* hw) {
323 struct GBARumble* rumble = hw->p->rumble;
324 if (!rumble) {
325 return;
326 }
327
328 rumble->setRumble(rumble, hw->p3);
329}
330
331// == Light sensor
332
333void GBAHardwareInitLight(struct GBACartridgeHardware* hw) {
334 hw->devices |= HW_LIGHT_SENSOR;
335 hw->lightCounter = 0;
336 hw->lightEdge = false;
337 hw->lightSample = 0xFF;
338}
339
340void _lightReadPins(struct GBACartridgeHardware* hw) {
341 if (hw->p2) {
342 // Boktai chip select
343 return;
344 }
345 if (hw->p1) {
346 struct GBALuminanceSource* lux = hw->p->luminanceSource;
347 GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Got reset");
348 hw->lightCounter = 0;
349 if (lux) {
350 lux->sample(lux);
351 hw->lightSample = lux->readLuminance(lux);
352 } else {
353 hw->lightSample = 0xFF;
354 }
355 }
356 if (hw->p0 && hw->lightEdge) {
357 ++hw->lightCounter;
358 }
359 hw->lightEdge = !hw->p0;
360
361 bool sendBit = hw->lightCounter >= hw->lightSample;
362 _outputPins(hw, sendBit << 3);
363 GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Output %u with pins %u", hw->lightCounter, hw->pinState);
364}
365
366// == Tilt
367
368void GBAHardwareInitTilt(struct GBACartridgeHardware* hw) {
369 hw->devices |= HW_TILT;
370 hw->tiltX = 0xFFF;
371 hw->tiltY = 0xFFF;
372 hw->tiltState = 0;
373}
374
375void GBAHardwareTiltWrite(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) {
376 switch (address) {
377 case 0x8000:
378 if (value == 0x55) {
379 hw->tiltState = 1;
380 } else {
381 GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
382 }
383 break;
384 case 0x8100:
385 if (value == 0xAA && hw->tiltState == 1) {
386 hw->tiltState = 0;
387 struct GBARotationSource* rotationSource = hw->p->rotationSource;
388 if (!rotationSource || !rotationSource->readTiltX || !rotationSource->readTiltY) {
389 return;
390 }
391 if (rotationSource->sample) {
392 rotationSource->sample(rotationSource);
393 }
394 int32_t x = rotationSource->readTiltX(rotationSource);
395 int32_t y = rotationSource->readTiltY(rotationSource);
396 // Normalize to ~12 bits, focused on 0x3A0
397 hw->tiltX = (x >> 21) + 0x3A0; // Crop off an extra bit so that we can't go negative
398 hw->tiltY = (y >> 21) + 0x3A0;
399 } else {
400 GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
401 }
402 break;
403 default:
404 GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor write to %04x: %02x", address, value);
405 break;
406 }
407}
408
409uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
410 switch (address) {
411 case 0x8200:
412 return hw->tiltX & 0xFF;
413 case 0x8300:
414 return ((hw->tiltX >> 8) & 0xF) | 0x80;
415 case 0x8400:
416 return hw->tiltY & 0xFF;
417 case 0x8500:
418 return (hw->tiltY >> 8) & 0xF;
419 default:
420 GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor read from %04x", address);
421 break;
422 }
423 return 0xFF;
424}
425
426// == Serialization
427
428void GBAHardwareSerialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
429 state->hw.readWrite = hw->readWrite;
430 state->hw.pinState = hw->pinState;
431 state->hw.pinDirection = hw->direction;
432 state->hw.devices = hw->devices;
433 state->hw.rtc = hw->rtc;
434 state->hw.gyroSample = hw->gyroSample;
435 state->hw.gyroEdge = hw->gyroEdge;
436 state->hw.tiltSampleX = hw->tiltX;
437 state->hw.tiltSampleY = hw->tiltY;
438 state->hw.tiltState = hw->tiltState;
439 state->hw.lightCounter = hw->lightCounter;
440 state->hw.lightSample = hw->lightSample;
441 state->hw.lightEdge = hw->lightEdge;
442}
443
444void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
445 hw->readWrite = state->hw.readWrite;
446 hw->pinState = state->hw.pinState;
447 hw->direction = state->hw.pinDirection;
448 // TODO: Deterministic RTC
449 hw->rtc = state->hw.rtc;
450 hw->gyroSample = state->hw.gyroSample;
451 hw->gyroEdge = state->hw.gyroEdge;
452 hw->tiltX = state->hw.tiltSampleX;
453 hw->tiltY = state->hw.tiltSampleY;
454 hw->tiltState = state->hw.tiltState;
455 hw->lightCounter = state->hw.lightCounter;
456 hw->lightSample = state->hw.lightSample;
457 hw->lightEdge = state->hw.lightEdge;
458}