rlm@1
|
1 #include <cstring>
|
rlm@1
|
2
|
rlm@1
|
3 #include "../Port.h"
|
rlm@1
|
4 #include "../NLS.h"
|
rlm@1
|
5 #include "../common/System.h" // systemMessage
|
rlm@1
|
6 #include "../common/Util.h"
|
rlm@1
|
7 #include "../common/movie.h"
|
rlm@1
|
8 #include "GBAGlobals.h"
|
rlm@1
|
9
|
rlm@1
|
10 enum RTCSTATE { IDLE, COMMAND, DATA, READDATA };
|
rlm@1
|
11
|
rlm@1
|
12 typedef struct
|
rlm@1
|
13 {
|
rlm@1
|
14 u8 byte0;
|
rlm@1
|
15 u8 byte1;
|
rlm@1
|
16 u8 byte2;
|
rlm@1
|
17 u8 command;
|
rlm@1
|
18 int dataLen;
|
rlm@1
|
19 int bits;
|
rlm@1
|
20 RTCSTATE state;
|
rlm@1
|
21 u8 data[12];
|
rlm@1
|
22 // reserved variables for future
|
rlm@1
|
23 u8 reserved[12];
|
rlm@1
|
24 bool reserved2;
|
rlm@1
|
25 u32 reserved3;
|
rlm@1
|
26 } RTCCLOCKDATA;
|
rlm@1
|
27
|
rlm@1
|
28 static RTCCLOCKDATA rtcClockData;
|
rlm@1
|
29 static bool rtcEnabled = false;
|
rlm@1
|
30
|
rlm@1
|
31 void rtcEnable(bool enable)
|
rlm@1
|
32 {
|
rlm@1
|
33 rtcEnabled = enable;
|
rlm@1
|
34 }
|
rlm@1
|
35
|
rlm@1
|
36 bool rtcIsEnabled()
|
rlm@1
|
37 {
|
rlm@1
|
38 return rtcEnabled;
|
rlm@1
|
39 }
|
rlm@1
|
40
|
rlm@1
|
41 u16 rtcRead(u32 address)
|
rlm@1
|
42 {
|
rlm@1
|
43 if (rtcEnabled)
|
rlm@1
|
44 {
|
rlm@1
|
45 if (address == 0x80000c8)
|
rlm@1
|
46 return rtcClockData.byte2;
|
rlm@1
|
47 else if (address == 0x80000c6)
|
rlm@1
|
48 return rtcClockData.byte1;
|
rlm@1
|
49 else if (address == 0x80000c4)
|
rlm@1
|
50 {
|
rlm@1
|
51 return rtcClockData.byte0;
|
rlm@1
|
52 }
|
rlm@1
|
53 }
|
rlm@1
|
54
|
rlm@1
|
55 return READ16LE((&rom[address & 0x1FFFFFE]));
|
rlm@1
|
56 }
|
rlm@1
|
57
|
rlm@1
|
58 static u8 toBCD(u8 value)
|
rlm@1
|
59 {
|
rlm@1
|
60 value = value % 100;
|
rlm@1
|
61 int l = value % 10;
|
rlm@1
|
62 int h = value / 10;
|
rlm@1
|
63 return h * 16 + l;
|
rlm@1
|
64 }
|
rlm@1
|
65
|
rlm@1
|
66 bool rtcWrite(u32 address, u16 value)
|
rlm@1
|
67 {
|
rlm@1
|
68 if (!rtcEnabled)
|
rlm@1
|
69 return false;
|
rlm@1
|
70
|
rlm@1
|
71 if (address == 0x80000c8)
|
rlm@1
|
72 {
|
rlm@1
|
73 rtcClockData.byte2 = (u8)value; // enable ?
|
rlm@1
|
74 }
|
rlm@1
|
75 else if (address == 0x80000c6)
|
rlm@1
|
76 {
|
rlm@1
|
77 rtcClockData.byte1 = (u8)value; // read/write
|
rlm@1
|
78 }
|
rlm@1
|
79 else if (address == 0x80000c4)
|
rlm@1
|
80 {
|
rlm@1
|
81 if (rtcClockData.byte2 & 1)
|
rlm@1
|
82 {
|
rlm@1
|
83 if (rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5)
|
rlm@1
|
84 {
|
rlm@1
|
85 rtcClockData.state = COMMAND;
|
rlm@1
|
86 rtcClockData.bits = 0;
|
rlm@1
|
87 rtcClockData.command = 0;
|
rlm@1
|
88 }
|
rlm@1
|
89 else if (!(rtcClockData.byte0 & 1) && (value & 1)) // bit transfer
|
rlm@1
|
90 {
|
rlm@1
|
91 rtcClockData.byte0 = (u8)value;
|
rlm@1
|
92 switch (rtcClockData.state)
|
rlm@1
|
93 {
|
rlm@1
|
94 case COMMAND:
|
rlm@1
|
95 rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits);
|
rlm@1
|
96 rtcClockData.bits++;
|
rlm@1
|
97 if (rtcClockData.bits == 8)
|
rlm@1
|
98 {
|
rlm@1
|
99 rtcClockData.bits = 0;
|
rlm@1
|
100 switch (rtcClockData.command)
|
rlm@1
|
101 {
|
rlm@1
|
102 case 0x60:
|
rlm@1
|
103 // not sure what this command does but it doesn't take parameters
|
rlm@1
|
104 // maybe it is a reset or stop
|
rlm@1
|
105 rtcClockData.state = IDLE;
|
rlm@1
|
106 rtcClockData.bits = 0;
|
rlm@1
|
107 break;
|
rlm@1
|
108 case 0x62:
|
rlm@1
|
109 // this sets the control state but not sure what those values are
|
rlm@1
|
110 rtcClockData.state = READDATA;
|
rlm@1
|
111 rtcClockData.dataLen = 1;
|
rlm@1
|
112 break;
|
rlm@1
|
113 case 0x63:
|
rlm@1
|
114 rtcClockData.dataLen = 1;
|
rlm@1
|
115 rtcClockData.data[0] = 0x40;
|
rlm@1
|
116 rtcClockData.state = DATA;
|
rlm@1
|
117 break;
|
rlm@1
|
118 case 0x65:
|
rlm@1
|
119 {
|
rlm@1
|
120 struct tm *newtime;
|
rlm@1
|
121 time_t long_time;
|
rlm@1
|
122
|
rlm@1
|
123 if (VBAMovieActive() || VBAMovieLoading())
|
rlm@1
|
124 {
|
rlm@1
|
125 long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60;
|
rlm@1
|
126 newtime = gmtime(&long_time);
|
rlm@1
|
127 }
|
rlm@1
|
128 else
|
rlm@1
|
129 {
|
rlm@1
|
130 time(&long_time); /* Get time as long integer. */
|
rlm@1
|
131 newtime = localtime(&long_time); /* Convert to local time. */
|
rlm@1
|
132 }
|
rlm@1
|
133
|
rlm@1
|
134 rtcClockData.dataLen = 7;
|
rlm@1
|
135 rtcClockData.data[0] = toBCD(newtime->tm_year);
|
rlm@1
|
136 rtcClockData.data[1] = toBCD(newtime->tm_mon+1);
|
rlm@1
|
137 rtcClockData.data[2] = toBCD(newtime->tm_mday);
|
rlm@1
|
138 rtcClockData.data[3] = 0;
|
rlm@1
|
139 rtcClockData.data[4] = toBCD(newtime->tm_hour);
|
rlm@1
|
140 rtcClockData.data[5] = toBCD(newtime->tm_min);
|
rlm@1
|
141 rtcClockData.data[6] = toBCD(newtime->tm_sec);
|
rlm@1
|
142 rtcClockData.state = DATA;
|
rlm@1
|
143 break;
|
rlm@1
|
144 }
|
rlm@1
|
145 case 0x67:
|
rlm@1
|
146 {
|
rlm@1
|
147 struct tm *newtime;
|
rlm@1
|
148 time_t long_time;
|
rlm@1
|
149
|
rlm@1
|
150 if (VBAMovieActive() || VBAMovieLoading())
|
rlm@1
|
151 {
|
rlm@1
|
152 long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60;
|
rlm@1
|
153 newtime = gmtime(&long_time);
|
rlm@1
|
154 }
|
rlm@1
|
155 else
|
rlm@1
|
156 {
|
rlm@1
|
157 time(&long_time); /* Get time as long integer. */
|
rlm@1
|
158 newtime = localtime(&long_time); /* Convert to local time. */
|
rlm@1
|
159 }
|
rlm@1
|
160
|
rlm@1
|
161 rtcClockData.dataLen = 3;
|
rlm@1
|
162 rtcClockData.data[0] = toBCD(newtime->tm_hour);
|
rlm@1
|
163 rtcClockData.data[1] = toBCD(newtime->tm_min);
|
rlm@1
|
164 rtcClockData.data[2] = toBCD(newtime->tm_sec);
|
rlm@1
|
165 rtcClockData.state = DATA;
|
rlm@1
|
166 break;
|
rlm@1
|
167 }
|
rlm@1
|
168 default:
|
rlm@1
|
169 systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command);
|
rlm@1
|
170 rtcClockData.state = IDLE;
|
rlm@1
|
171 break;
|
rlm@1
|
172 }
|
rlm@1
|
173 }
|
rlm@1
|
174 break;
|
rlm@1
|
175 case DATA:
|
rlm@1
|
176 if (rtcClockData.byte1 & 2)
|
rlm@1
|
177 {}
|
rlm@1
|
178 else
|
rlm@1
|
179 {
|
rlm@1
|
180 rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
|
rlm@1
|
181 ((rtcClockData.data[rtcClockData.bits >> 3] >>
|
rlm@1
|
182 (rtcClockData.bits & 7)) & 1)*2;
|
rlm@1
|
183 rtcClockData.bits++;
|
rlm@1
|
184 if (rtcClockData.bits == 8*rtcClockData.dataLen)
|
rlm@1
|
185 {
|
rlm@1
|
186 rtcClockData.bits = 0;
|
rlm@1
|
187 rtcClockData.state = IDLE;
|
rlm@1
|
188 }
|
rlm@1
|
189 }
|
rlm@1
|
190 break;
|
rlm@1
|
191 case READDATA:
|
rlm@1
|
192 if (!(rtcClockData.byte1 & 2))
|
rlm@1
|
193 {}
|
rlm@1
|
194 else
|
rlm@1
|
195 {
|
rlm@1
|
196 rtcClockData.data[rtcClockData.bits >> 3] =
|
rlm@1
|
197 (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
|
rlm@1
|
198 ((value << 6) & 128);
|
rlm@1
|
199 rtcClockData.bits++;
|
rlm@1
|
200 if (rtcClockData.bits == 8*rtcClockData.dataLen)
|
rlm@1
|
201 {
|
rlm@1
|
202 rtcClockData.bits = 0;
|
rlm@1
|
203 rtcClockData.state = IDLE;
|
rlm@1
|
204 }
|
rlm@1
|
205 }
|
rlm@1
|
206 break;
|
rlm@1
|
207 default:
|
rlm@1
|
208 break;
|
rlm@1
|
209 }
|
rlm@1
|
210 }
|
rlm@1
|
211 else
|
rlm@1
|
212 rtcClockData.byte0 = (u8)value;
|
rlm@1
|
213 }
|
rlm@1
|
214 }
|
rlm@1
|
215 return true;
|
rlm@1
|
216 }
|
rlm@1
|
217
|
rlm@1
|
218 void rtcReset()
|
rlm@1
|
219 {
|
rlm@1
|
220 memset(&rtcClockData, 0, sizeof(rtcClockData));
|
rlm@1
|
221
|
rlm@1
|
222 rtcClockData.byte0 = 0;
|
rlm@1
|
223 rtcClockData.byte1 = 0;
|
rlm@1
|
224 rtcClockData.byte2 = 0;
|
rlm@1
|
225 rtcClockData.command = 0;
|
rlm@1
|
226 rtcClockData.dataLen = 0;
|
rlm@1
|
227 rtcClockData.bits = 0;
|
rlm@1
|
228 rtcClockData.state = IDLE;
|
rlm@1
|
229 }
|
rlm@1
|
230
|
rlm@1
|
231 void rtcSaveGame(gzFile gzFile)
|
rlm@1
|
232 {
|
rlm@1
|
233 utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData));
|
rlm@1
|
234 }
|
rlm@1
|
235
|
rlm@1
|
236 void rtcReadGame(gzFile gzFile)
|
rlm@1
|
237 {
|
rlm@1
|
238 utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData));
|
rlm@1
|
239 }
|
rlm@1
|
240
|