1 #include "../System.h"
2 #include "GBA.h"
3 #include "Globals.h"
4 #include "../common/Port.h"
5 #include "../Util.h"
6 #include "../NLS.h"
7
8 #include <time.h>
9 #include <memory.h>
10 #include <string.h>
11
12 enum RTCSTATE
13 {
14 IDLE = 0,
15 COMMAND,
16 DATA,
17 READDATA
18 };
19
20 typedef struct
21 {
22 u8 byte0;
23 u8 select;
24 u8 enable;
25 u8 command;
26 int dataLen;
27 int bits;
28 RTCSTATE state;
29 u8 data[12];
30 // reserved variables for future
31 u8 reserved[12];
32 bool reserved2;
33 u32 reserved3;
34 } RTCCLOCKDATA;
35
36 struct tm gba_time;
37 static RTCCLOCKDATA rtcClockData;
38 static bool rtcClockEnabled = true;
39 static bool rtcRumbleEnabled = false;
40
41 u32 countTicks = 0;
42
rtcEnable(bool e)43 void rtcEnable(bool e)
44 {
45 rtcClockEnabled = e;
46 }
47
rtcIsEnabled()48 bool rtcIsEnabled()
49 {
50 return rtcClockEnabled;
51 }
52
rtcEnableRumble(bool e)53 void rtcEnableRumble(bool e)
54 {
55 rtcRumbleEnabled = e;
56 }
57
rtcRead(u32 address)58 u16 rtcRead(u32 address)
59 {
60 int res = 0;
61
62 switch (address)
63 {
64 case 0x80000c8:
65 return rtcClockData.enable;
66 break;
67
68 case 0x80000c6:
69 return rtcClockData.select;
70 break;
71
72 case 0x80000c4:
73 if (!(rtcClockData.enable & 1))
74 {
75 return 0;
76 }
77
78 // Boktai Solar Sensor
79 if (rtcClockData.select == 0x07)
80 {
81 if (rtcClockData.reserved[11] >= systemGetSensorDarkness())
82 {
83 res |= 8;
84 }
85 }
86
87 // WarioWare Twisted Tilt Sensor
88 if (rtcClockData.select == 0x0b)
89 {
90 u16 v = systemGetSensorZ();
91 v = 0x6C0 + v;
92 res |= ((v >> rtcClockData.reserved[11]) & 1) << 2;
93 }
94
95 // Real Time Clock
96 if (rtcClockEnabled && (rtcClockData.select & 0x04))
97 {
98 res |= rtcClockData.byte0;
99 }
100
101 return res;
102 break;
103 }
104
105 return READ16LE((&rom[address & 0x1FFFFFE]));
106 }
107
toBCD(u8 value)108 static u8 toBCD(u8 value)
109 {
110 value = value % 100;
111 int l = value % 10;
112 int h = value / 10;
113 return h * 16 + l;
114 }
115
SetGBATime()116 void SetGBATime()
117 {
118 time_t long_time;
119 time(&long_time); /* Get time as long integer. */
120 gba_time = *localtime(&long_time); /* Convert to local time. */
121 }
122
rtcUpdateTime(int ticks)123 void rtcUpdateTime(int ticks)
124 {
125 countTicks += ticks;
126
127 if (countTicks > TICKS_PER_SECOND)
128 {
129 countTicks -= TICKS_PER_SECOND;
130 gba_time.tm_sec++;
131 mktime(&gba_time);
132 }
133 }
134
rtcWrite(u32 address,u16 value)135 bool rtcWrite(u32 address, u16 value)
136 {
137 if (address == 0x80000c8)
138 {
139 rtcClockData.enable = (u8)value; // bit 0 = enable reading from 0x80000c4 c6 and c8
140 }
141 else if (address == 0x80000c6)
142 {
143 rtcClockData.select = (u8)value; // 0=read/1=write (for each of 4 low bits)
144
145 // rumble is off when not writing to that pin
146 if (rtcRumbleEnabled && !(value & 8)) systemCartridgeRumble(false);
147 }
148 else if (address == 0x80000c4) // 4 bits of I/O Port Data (upper bits not used)
149 {
150 // WarioWare Twisted rumble
151 if (rtcRumbleEnabled && (rtcClockData.select & 0x08))
152 {
153 systemCartridgeRumble(value & 8);
154 }
155
156 // Boktai solar sensor
157 if (rtcClockData.select == 0x07)
158 {
159 if (value & 2)
160 {
161 // reset counter to 0
162 rtcClockData.reserved[11] = 0;
163 }
164
165 if ((value & 1) && !(rtcClockData.reserved[10] & 1))
166 {
167 // increase counter, ready to do another read
168 if (rtcClockData.reserved[11] < 255)
169 {
170 rtcClockData.reserved[11]++;
171 }
172 else
173 {
174 rtcClockData.reserved[11] = 0;
175 }
176 }
177
178 rtcClockData.reserved[10] = value & rtcClockData.select;
179 }
180
181 // WarioWare Twisted rotation sensor
182 if (rtcClockData.select == 0x0b)
183 {
184 if (value & 2)
185 {
186 // clock goes high in preperation for reading a bit
187 rtcClockData.reserved[11]--;
188 }
189
190 if (value & 1)
191 {
192 // start ADC conversion
193 rtcClockData.reserved[11] = 15;
194 }
195
196 rtcClockData.byte0 = value & rtcClockData.select;
197 }
198
199 // Real Time Clock
200 if (rtcClockData.select & 4)
201 {
202 if (rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5)
203 {
204 rtcClockData.state = COMMAND;
205 rtcClockData.bits = 0;
206 rtcClockData.command = 0;
207 }
208 else if (!(rtcClockData.byte0 & 1) && (value & 1)) // bit transfer
209 {
210 rtcClockData.byte0 = (u8)value;
211
212 switch (rtcClockData.state)
213 {
214 case COMMAND:
215 rtcClockData.command |= ((value & 2) >> 1) << (7 - rtcClockData.bits);
216 rtcClockData.bits++;
217
218 if (rtcClockData.bits == 8)
219 {
220 rtcClockData.bits = 0;
221
222 switch (rtcClockData.command)
223 {
224 case 0x60:
225 // not sure what this command does but it doesn't take parameters
226 // maybe it is a reset or stop
227 rtcClockData.state = IDLE;
228 rtcClockData.bits = 0;
229 break;
230
231 case 0x62:
232 // this sets the control state but not sure what those values are
233 rtcClockData.state = READDATA;
234 rtcClockData.dataLen = 1;
235 break;
236
237 case 0x63:
238 rtcClockData.dataLen = 1;
239 rtcClockData.data[0] = 0x40;
240 rtcClockData.state = DATA;
241 break;
242
243 case 0x64:
244 break;
245
246 case 0x65:
247 {
248 if (rtcEnabled)
249 SetGBATime();
250
251 rtcClockData.dataLen = 7;
252 rtcClockData.data[0] = toBCD(gba_time.tm_year);
253 rtcClockData.data[1] = toBCD(gba_time.tm_mon + 1);
254 rtcClockData.data[2] = toBCD(gba_time.tm_mday);
255 rtcClockData.data[3] = toBCD(gba_time.tm_wday);
256 rtcClockData.data[4] = toBCD(gba_time.tm_hour);
257 rtcClockData.data[5] = toBCD(gba_time.tm_min);
258 rtcClockData.data[6] = toBCD(gba_time.tm_sec);
259 rtcClockData.state = DATA;
260 }
261 break;
262
263 case 0x67:
264 {
265 if (rtcEnabled)
266 SetGBATime();
267
268 rtcClockData.dataLen = 3;
269 rtcClockData.data[0] = toBCD(gba_time.tm_hour);
270 rtcClockData.data[1] = toBCD(gba_time.tm_min);
271 rtcClockData.data[2] = toBCD(gba_time.tm_sec);
272 rtcClockData.state = DATA;
273 }
274 break;
275
276 default:
277 log(N_("Unknown RTC command %02x"), rtcClockData.command);
278 rtcClockData.state = IDLE;
279 break;
280 }
281 }
282
283 break;
284
285 case DATA:
286 if (rtcClockData.select & 2)
287 {
288 }
289 else if (rtcClockData.select & 4)
290 {
291 rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
292 ((rtcClockData.data[rtcClockData.bits >> 3] >>
293 (rtcClockData.bits & 7)) & 1) * 2;
294 rtcClockData.bits++;
295
296 if (rtcClockData.bits == 8 * rtcClockData.dataLen)
297 {
298 rtcClockData.bits = 0;
299 rtcClockData.state = IDLE;
300 }
301 }
302
303 break;
304
305 case READDATA:
306 if (!(rtcClockData.select & 2))
307 {
308 }
309 else
310 {
311 rtcClockData.data[rtcClockData.bits >> 3] =
312 (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
313 ((value << 6) & 128);
314 rtcClockData.bits++;
315
316 if (rtcClockData.bits == 8 * rtcClockData.dataLen)
317 {
318 rtcClockData.bits = 0;
319 rtcClockData.state = IDLE;
320 }
321 }
322
323 break;
324
325 default:
326 break;
327 }
328 }
329 else
330 rtcClockData.byte0 = (u8)value;
331 }
332 }
333
334 return true;
335 }
336
rtcReset()337 void rtcReset()
338 {
339 memset(&rtcClockData, 0, sizeof(rtcClockData));
340 rtcClockData.byte0 = 0;
341 rtcClockData.select = 0;
342 rtcClockData.enable = 0;
343 rtcClockData.command = 0;
344 rtcClockData.dataLen = 0;
345 rtcClockData.bits = 0;
346 rtcClockData.state = IDLE;
347 rtcClockData.reserved[11] = 0;
348 SetGBATime();
349 }
350
351 #ifdef __LIBRETRO__
rtcSaveGame(u8 * & data)352 void rtcSaveGame(u8* &data)
353 {
354 utilWriteMem(data, &rtcClockData, sizeof(rtcClockData));
355 }
356
rtcReadGame(const u8 * & data)357 void rtcReadGame(const u8* &data)
358 {
359 utilReadMem(&rtcClockData, data, sizeof(rtcClockData));
360 }
361 #else
rtcSaveGame(gzFile gzFile)362 void rtcSaveGame(gzFile gzFile)
363 {
364 utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData));
365 }
366
rtcReadGame(gzFile gzFile)367 void rtcReadGame(gzFile gzFile)
368 {
369 utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData));
370 }
371 #endif
372