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