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