OSDN Git Service

3a0d9391618477ee9efec82ae3ca8bdf3b30eccf
[csp-qt/common_source_project-fm7.git] / source / src / vm / hd146818p.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.10.11 -
6
7         [ HD146818P ]
8 */
9
10 #include "hd146818p.h"
11
12 #define EVENT_1SEC      0
13 #define EVENT_SQW       1
14
15 // [DV2-DV0][RS3-RS0]
16 static const int periodic_intr_rate[3][16] = {
17         {0,   1,   2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384},  // 4.194304 MHz
18         {0,   1,   2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384},  // 1.048576 MHz
19         {0, 128, 256, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}   // 32.768kHz
20 };
21 #if defined(Q_OS_WIN)
22 DLL_PREFIX_I struct cur_time_s cur_time;
23 #endif
24
25 void HD146818P::initialize()
26 {
27         DEVICE::initialize();
28         // load ram image
29         memset(regs, 0, sizeof(regs));
30         modified = false;
31         
32         // FIXME: we need to consider the multiple chips case
33         FILEIO* fio = new FILEIO();
34         if(fio->Fopen(create_local_path(_T("HD146818P.BIN")), FILEIO_READ_BINARY)) {
35                 fio->Fread(regs + 14, 50, 1);
36                 fio->Fclose();
37         }
38         delete fio;
39         
40         // initialize
41         ch = period = 0;
42         intr = sqw = false;
43         register_id_sqw = -1;
44         
45         get_host_time(&cur_time);
46         read_from_cur_time();
47         
48         // register event
49         register_event(this, EVENT_1SEC, 1000000, true, &register_id_1sec);
50 }
51
52 void HD146818P::release()
53 {
54         if(modified) {
55                 // FIXME: we need to consider the multiple chips case
56                 FILEIO* fio = new FILEIO();
57                 if(fio->Fopen(create_local_path(_T("HD146818P.BIN")), FILEIO_WRITE_BINARY)) {
58                         fio->Fwrite(regs + 14, 50, 1);
59                         fio->Fclose();
60                 }
61                 delete fio;
62         }
63 }
64
65 void HD146818P::reset()
66 {
67         regs[0x0b] &= ~0x78;
68         regs[0x0c] = 0;
69 }
70
71 void HD146818P::write_io8(uint32_t addr, uint32_t data)
72 {
73         if(addr & 1) {
74                 ch = data & 0x3f;
75         } else {
76                 if(ch <= 9) {
77                         regs[ch] = data;
78                         if(!(ch == 1 || ch == 3 || ch == 5)) {
79                                 write_to_cur_time();
80                         }
81                         if(ch <= 5) {
82                                 check_alarm();
83                                 update_intr();
84                         }
85                 } else if(ch == 0x0a) {
86                         // periodic interrupt
87                         int dv = (data >> 4) & 7, next = 0;
88                         if(dv < 3) {
89                                 next = periodic_intr_rate[dv][data & 0x0f];
90                         }
91                         if(next != period) {
92                                 if(register_id_sqw != -1) {
93                                         cancel_event(this, register_id_sqw);
94                                         register_id_sqw = -1;
95                                 }
96                                 if(next) {
97                                         // raise event twice per one period
98                                         register_event(this, EVENT_SQW, 1000000.0 / 65536.0 * next, true, &register_id_sqw);
99                                 }
100                                 period = next;
101                         }
102                         regs[ch] = data & 0x7f; // always UIP=0
103                 } else if(ch == 0x0b) {
104                         if((regs[0x0b] & 8) && !(data & 8)) {
105                                 // keep sqw = L when sqwe = 0
106                                 write_signals(&outputs_sqw, 0);
107                         }
108                         bool tmp = (((regs[ch] ^ data) & 4) != 0);
109                         regs[ch] = data;
110                         if(tmp) {
111                                 read_from_cur_time();
112                                 check_alarm();
113                         }
114                         update_intr();
115                 } else if(ch > 0x0d) {
116                         // internal ram
117                         if(regs[ch] != data) {
118                                 regs[ch] = data;
119                                 modified = true;
120                         }
121                 }
122         }
123 }
124
125 uint32_t HD146818P::read_io8(uint32_t addr)
126 {
127         if(addr & 1) {
128                 return 0xff;
129         } else {
130                 uint8_t val = regs[ch];
131                 if(ch == 0x0c) {
132                         regs[0x0c] = 0;
133                         update_intr();
134                 }
135                 return val;
136         }
137 }
138
139 #define TO_BCD_BIN(v)   ((regs[0x0b] & 4) ? (v) : TO_BCD(v))
140 #define FROM_BCD_BIN(v) ((regs[0x0b] & 4) ? (v) : FROM_BCD(v))
141
142 void HD146818P::event_callback(int event_id, int err)
143 {
144         if(event_id == EVENT_1SEC) {
145                 if(cur_time.initialized) {
146                         cur_time.increment();
147                 } else {
148                         get_host_time(&cur_time);       // resync
149                         cur_time.initialized = true;
150                 }
151                 read_from_cur_time();
152                 regs[0x0c] |= 0x10; // updated
153                 check_alarm();
154                 update_intr();
155         } else if(event_id == EVENT_SQW) {
156                 // periodic interrupt
157                 sqw = !sqw;
158                 if(sqw) {  // OK? 20180516 K.Ohta
159                         regs[0x0c] |= 0x40;
160                         update_intr();
161                 }
162                 // square wave
163                 if(regs[0x0b] & 8) {
164                         // output sqw when sqwe = 1
165                         write_signals(&outputs_sqw, sqw ? 0xffffffff : 0);
166                 }
167         }
168 }
169
170 void HD146818P::read_from_cur_time()
171 {
172         int hour = (regs[0x0b] & 2) ? cur_time.hour : (cur_time.hour % 12);
173         int ampm = (regs[0x0b] & 2) ? 0 : (cur_time.hour > 11) ? 0x80 : 0;
174         
175         regs[0] = TO_BCD_BIN(cur_time.second);
176         regs[2] = TO_BCD_BIN(cur_time.minute);
177         regs[4] = TO_BCD_BIN(hour) | ampm;
178         regs[6] = cur_time.day_of_week + 1;
179         regs[7] = TO_BCD_BIN(cur_time.day);
180         regs[8] = TO_BCD_BIN(cur_time.month);
181         regs[9] = TO_BCD_BIN(cur_time.year);
182 }
183
184 void HD146818P::write_to_cur_time()
185 {
186         cur_time.second = FROM_BCD_BIN(regs[0] & 0x7f);
187         cur_time.minute = FROM_BCD_BIN(regs[2] & 0x7f);
188         if(regs[0x0b] & 2) {
189                 cur_time.hour = FROM_BCD_BIN(regs[4] & 0x3f);
190         } else {
191                 cur_time.hour = FROM_BCD_BIN(regs[4] & 0x1f);
192                 if(regs[4] & 0x80) {
193                         cur_time.hour += 12;
194                 }
195         }
196 //      cur_time.day_of_week = regs[6] - 1;
197         cur_time.day = FROM_BCD_BIN(regs[7]);
198         cur_time.month = FROM_BCD_BIN(regs[8]);
199         cur_time.year = FROM_BCD_BIN(regs[9]);
200         cur_time.update_year();
201         cur_time.update_day_of_week();
202         
203         // restart event
204         cancel_event(this, register_id_1sec);
205         register_event(this, EVENT_1SEC, 1000000, true, &register_id_1sec);
206 }
207
208 void HD146818P::check_alarm()
209 {
210         if(regs[0] == regs[1] && regs[2] == regs[3] && regs[4] == regs[5]) {
211                 regs[0x0c] |= 0x20;
212         }
213 }
214
215 void HD146818P::update_intr()
216 {
217         bool next = ((regs[0x0b] & regs[0x0c] & 0x70) != 0);
218         if(intr != next) {
219                 write_signals(&outputs_intr, next ? 0xffffffff : 0);
220                 intr = next;
221         }
222 }
223
224 #define STATE_VERSION   1
225
226 #include "../statesub.h"
227
228 void HD146818P::decl_state()
229 {
230         enter_decl_state(STATE_VERSION);
231         
232         DECL_STATE_ENTRY_INT32(register_id_1sec);
233         DECL_STATE_ENTRY_1D_ARRAY(regs, sizeof(regs));
234         DECL_STATE_ENTRY_INT32(ch);
235         DECL_STATE_ENTRY_INT32(period);
236         DECL_STATE_ENTRY_INT32(register_id_sqw);
237         DECL_STATE_ENTRY_BOOL(intr);
238         DECL_STATE_ENTRY_BOOL(sqw);
239         DECL_STATE_ENTRY_BOOL(modified);
240
241         leave_decl_state();
242 }
243
244 void HD146818P::save_state(FILEIO* state_fio)
245 {
246         //state_fio->FputUint32(STATE_VERSION);
247         //state_fio->FputInt32(this_device_id);
248         
249         if(state_entry != NULL) {
250                 state_entry->save_state(state_fio);
251         }
252         cur_time.save_state((void *)state_fio);
253         //state_fio->FputInt32(register_id_1sec);
254         //state_fio->Fwrite(regs, sizeof(regs), 1);
255         //state_fio->FputInt32(ch);
256         //state_fio->FputInt32(period);
257         //state_fio->FputInt32(register_id_sqw);
258         //state_fio->FputBool(intr);
259         //state_fio->FputBool(sqw);
260         //state_fio->FputBool(modified);
261 }
262
263 bool HD146818P::load_state(FILEIO* state_fio)
264 {
265         bool mb = false;
266         if(state_entry != NULL) {
267                 mb = state_entry->load_state(state_fio);
268         }
269         if(!mb) return false;
270         //if(state_fio->FgetUint32() != STATE_VERSION) {
271         //      return false;
272         //}
273         //if(state_fio->FgetInt32() != this_device_id) {
274         //      return false;
275         //}
276         if(!cur_time.load_state((void *)state_fio)) {
277                 return false;
278         }
279         //register_id_1sec = state_fio->FgetInt32();
280         //state_fio->Fread(regs, sizeof(regs), 1);
281         //ch = state_fio->FgetInt32();
282         //period = state_fio->FgetInt32();
283         //register_id_sqw = state_fio->FgetInt32();
284         //intr = state_fio->FgetBool();
285         //sqw = state_fio->FgetBool();
286         //modified = state_fio->FgetBool();
287         return true;
288 }
289