OSDN Git Service

[VM][FM77] Add initial support of 2HD (for FM-77).
[csp-qt/common_source_project-fm7.git] / source / src / vm / rp5c01.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2008.04.11-
6
7         [ RP-5C01 / RP-5C15 ]
8 */
9
10 #include "rp5c01.h"
11
12 #define EVENT_1SEC      0
13 #define EVENT_16HZ      1
14 #if defined(Q_OS_WIN)
15 DLL_PREFIX_I struct cur_time_s cur_time;
16 #endif
17
18 void RP5C01::initialize()
19 {
20         DEVICE::initialize();
21         __HAS_RP5C15 = osd->check_feature(_T("HAS_RP5C15"));
22         if(__HAS_RP5C15) set_device_name(_T("RP-5C15 RTC"));
23 //#ifndef HAS_RP5C15
24         if(!__HAS_RP5C15) {
25                 // load ram image
26                 memset(ram, 0, sizeof(ram));
27                 modified = false;
28                 
29                 // FIXME: we need to consider the multiple chips case
30                 FILEIO* fio = new FILEIO();
31                 if(fio->Fopen(create_local_path(_T("RP5C01.BIN")), FILEIO_READ_BINARY)) {
32                         fio->Fread(ram, sizeof(ram), 1);
33                         fio->Fclose();
34                 }
35                 delete fio;
36         }
37 //#endif
38         
39         // initialize rtc
40         memset(regs, 0, sizeof(regs));
41         regs[0x0a] = 1;
42         regs[0x0d] = 8;
43         regs[0x0f] = 0xc;
44         alarm = pulse_1hz = pulse_16hz = false;
45         count_16hz = 0;
46         
47         get_host_time(&cur_time);
48         read_from_cur_time();
49         
50         // register events
51         register_event(this, EVENT_1SEC, 1000000, true, &register_id);
52         register_event(this, EVENT_16HZ, 1000000 / 32, true, NULL);
53 }
54
55 void RP5C01::release()
56 {
57 //#ifndef HAS_RP5C15
58         if(!__HAS_RP5C15) {
59                 // save ram image
60                 if(modified) {
61                         // FIXME: we need to consider the multiple chips case
62                         FILEIO* fio = new FILEIO();
63                         if(fio->Fopen(create_local_path(_T("RP5C01.BIN")), FILEIO_WRITE_BINARY)) {
64                                 fio->Fwrite(ram, sizeof(ram), 1);
65                                 fio->Fclose();
66                         }
67                         delete fio;
68                 }
69         }
70 //#endif
71 }
72
73 void RP5C01::write_io8(uint32_t addr, uint32_t data)
74 {
75         addr &= 0x0f;
76         if(addr <= 0x0c) {
77                 uint8_t nmask;
78                 if(!__HAS_RP5C15) {
79                         nmask = 3;
80                 } else {
81                         nmask = 1;
82                 }
83 //#ifndef HAS_RP5C15
84 //              switch(regs[0x0d] & 3) {
85 //#else
86                 switch(regs[0x0d] & nmask) {
87 //#endif
88                 case 0:
89                         if(time[addr] != data) {
90                                 time[addr] = data;
91                                 write_to_cur_time();
92                         }
93                         return;
94 //#ifndef HAS_RP5C15
95                 case 2:
96                         if(__HAS_RP5C15) break;
97                         if(ram[addr] != data) {
98                                 ram[addr] = data;
99                                 modified = true;
100                         }
101                         return;
102                 case 3:
103                         if(__HAS_RP5C15) break;
104                         if(ram[addr + 13] != data) {
105                                 ram[addr + 13] = data;
106                                 modified = true;
107                         }
108                         return;
109 //#endif
110                 }
111         }
112         
113         uint8_t tmp = regs[addr] ^ data;
114         regs[addr] = data;
115         
116         if(addr == 0x0a) {
117                 if(tmp & 1) {
118                         // am/pm is changed
119                         read_from_cur_time();
120                 }
121         } else if(addr == 0x0f) {
122                 uint8_t nmask;
123                 if(!__HAS_RP5C15) {
124                         nmask = 3;
125                 } else {
126                         nmask = 1;
127                 }
128 //#ifndef HAS_RP5C15
129 //              switch(regs[0x0d] & 3) {
130 //#else
131                 switch(regs[0x0d] & nmask) {
132 //#endif
133                 case 0:
134                         if(data & 3) {
135                                 // timer reset
136                         }
137                         break;
138                 case 1:
139 //#ifndef HAS_RP5C15
140                 case 2:
141                 case 3:
142 //#endif
143                         if(__HAS_RP5C15) break;
144                         if(data & 2) {
145                                 // timer reset
146                         }
147                         if(data & 1) {
148                                 if(alarm) {
149                                         alarm = false;
150                                         update_pulse();
151                                 }
152                         }
153                         break;
154                 }
155         }
156 }
157
158 uint32_t RP5C01::read_io8(uint32_t addr)
159 {
160         addr &= 0x0f;
161         if(addr <= 0x0c) {
162                 uint8_t nmask;
163                 if(!__HAS_RP5C15) {
164                         nmask = 3;
165                 } else {
166                         nmask = 1;
167                 }
168 //#ifndef HAS_RP5C15
169 //              switch(regs[0x0d] & 3) {
170 //#else
171                 switch(regs[0x0d] & nmask) {
172 //#endif
173                 case 0:
174                         return time[addr];
175 //#ifndef HAS_RP5C15
176                 case 2:
177                         if(__HAS_RP5C15) return regs[addr];
178                         return ram[addr];
179                 case 3:
180                         if(__HAS_RP5C15) return regs[addr];
181                         return ram[addr + 13];
182 //#endif
183                 }
184         }
185         if(addr == 0x0b) {
186                 for(int i = 0; i < 3; i++) {
187                         if(LEAP_YEAR(cur_time.year - i)) {
188                                 return i;
189                         }
190                 }
191                 return 3;
192         }
193         return regs[addr];
194 }
195
196 void RP5C01::event_callback(int event_id, int err)
197 {
198         if(event_id == EVENT_1SEC) {
199                 if(cur_time.initialized) {
200                         cur_time.increment();
201                 } else {
202                         get_host_time(&cur_time);       // resync
203                         cur_time.initialized = true;
204                 }
205                 if(regs[0x0d] & 8) {
206                         read_from_cur_time();
207                         if(regs[0x0d] & 4) {
208                                 update_pulse();
209                         }
210                 }
211         } else if(event_id == EVENT_16HZ) {
212                 bool update = false;
213                 // 1Hz
214                 if(++count_16hz == 16) {
215                         pulse_1hz = !pulse_1hz;
216                         if(!(regs[0x0f] & 8)) {
217                                 update = true;
218                         }
219                         count_16hz = 0;
220                 }
221                 // 16Hz
222                 pulse_16hz = !pulse_16hz;
223                 if(!(regs[0x0f] & 4)) {
224                         update = true;
225                 }
226                 if(update) {
227                         update_pulse();
228                 }
229         }
230         
231         // update signal
232 }
233
234 void RP5C01::update_pulse()
235 {
236         bool pulse = false;
237         
238         if(regs[0x0d] & 4) {
239                 pulse |= alarm;
240         }
241         if(!(regs[0x0f] & 8)) {
242                 pulse |= pulse_1hz;
243         }
244         if(!(regs[0x0f] & 4)) {
245                 pulse |= pulse_16hz;
246         }
247         write_signals(&outputs_pulse, pulse ? 0 : 0xffffffff);
248 }
249
250 #define MODE_12H !(regs[0x0a] & 1)
251
252 void RP5C01::read_from_cur_time()
253 {
254         int hour = MODE_12H ? (cur_time.hour % 12) : cur_time.hour;
255         int ampm = (MODE_12H && cur_time.hour >= 12) ? 2 : 0;
256         
257         time[ 0] = TO_BCD_LO(cur_time.second);
258         time[ 1] = TO_BCD_HI(cur_time.second);
259         time[ 2] = TO_BCD_LO(cur_time.minute);
260         time[ 3] = TO_BCD_HI(cur_time.minute);
261         time[ 4] = TO_BCD_LO(hour);
262         time[ 5] = TO_BCD_HI(hour) | ampm;
263         time[ 6] = cur_time.day_of_week;
264         time[ 7] = TO_BCD_LO(cur_time.day);
265         time[ 8] = TO_BCD_HI(cur_time.day);
266         time[ 9] = TO_BCD_LO(cur_time.month);
267         time[10] = TO_BCD_HI(cur_time.month);
268         time[11] = TO_BCD_LO(cur_time.year);
269         time[12] = TO_BCD_HI(cur_time.year);
270         
271         // check alarm
272         static const uint8_t mask[9] = {0, 0, 0x0f, 0x07, 0x0f, 0x03, 0x07, 0x0f, 0x03};
273         bool tmp = true;
274         
275         for(int i = 3; i < 9; i++) {
276                 if((time[i] & mask[i]) != (regs[i] & mask[i])) {
277                         tmp = false;
278                         break;
279                 }
280         }
281         if(tmp) {
282                 alarm = true;
283         }
284 }
285
286 void RP5C01::write_to_cur_time()
287 {
288         cur_time.second = time[0] + (time[1] & 7) * 10;
289         cur_time.minute = time[2] + (time[3] & 7) * 10;
290         if(MODE_12H) {
291                 cur_time.hour = time[4] + (time[5] & 1) * 10 + (time[5] & 2 ? 12 : 0);
292         } else {
293                 cur_time.hour = time[4] + (time[5] & 3) * 10;
294         }
295 //      cur_time.day_of_week = time[6];
296         cur_time.day = time[7] + (time[8] & 3) * 10;
297         cur_time.month = time[9] + (time[10] & 1) * 10;
298         cur_time.year = time[11] + time[12] * 10;
299         cur_time.update_year();
300         cur_time.update_day_of_week();
301         
302         // restart events
303         cancel_event(this, register_id);
304         register_event(this, EVENT_1SEC, 1000000, true, &register_id);
305 }
306
307 #define STATE_VERSION   1
308
309 void RP5C01::save_state(FILEIO* state_fio)
310 {
311         state_fio->FputUint32(STATE_VERSION);
312         state_fio->FputInt32(this_device_id);
313         
314         cur_time.save_state((void *)state_fio);
315         state_fio->FputInt32(register_id);
316         state_fio->Fwrite(regs, sizeof(regs), 1);
317         state_fio->Fwrite(time, sizeof(time), 1);
318 //#ifndef HAS_RP5C15
319         if(!__HAS_RP5C15) {
320                 state_fio->Fwrite(ram, sizeof(ram), 1);
321                 state_fio->FputBool(modified);
322         }
323 //#endif
324         state_fio->FputBool(alarm);
325         state_fio->FputBool(pulse_1hz);
326         state_fio->FputBool(pulse_16hz);
327         state_fio->FputInt32(count_16hz);
328 }
329
330 bool RP5C01::load_state(FILEIO* state_fio)
331 {
332         if(state_fio->FgetUint32() != STATE_VERSION) {
333                 return false;
334         }
335         if(state_fio->FgetInt32() != this_device_id) {
336                 return false;
337         }
338         if(!cur_time.load_state((void *)state_fio)) {
339                 return false;
340         }
341         register_id = state_fio->FgetInt32();
342         state_fio->Fread(regs, sizeof(regs), 1);
343         state_fio->Fread(time, sizeof(time), 1);
344 //#ifndef HAS_RP5C15
345         if(!__HAS_RP5C15) {
346                 state_fio->Fread(ram, sizeof(ram), 1);
347                 modified = state_fio->FgetBool();
348         }
349 //#endif
350         alarm = state_fio->FgetBool();
351         pulse_1hz = state_fio->FgetBool();
352         pulse_16hz = state_fio->FgetBool();
353         count_16hz = state_fio->FgetInt32();
354         return true;
355 }
356