OSDN Git Service

[General][Qt] Merge upstream 2015-03-15.
[csp-qt/common_source_project-fm7.git] / source / src / vm / fmr30 / rtc.cpp
1 /*
2         FUJITSU FMR-30 Emulator 'eFMR-30'
3
4         Author : Takeda.Toshiya
5         Date   : 2008.12.30 -
6
7         [ rtc ]
8 */
9
10 #include "rtc.h"
11 #include "../i8259.h"
12
13 #define EVENT_1HZ       0
14 #define EVENT_32HZ      1
15 #define EVENT_DONE      2
16
17 #define POWON   8
18 #define TCNT    34
19 #define CKHM    35
20 #define CKL     36
21 #define POWOF   36
22 #define POFMI   37
23 #define POFH    38
24 #define POFD    39
25
26 void RTC::initialize()
27 {
28         // load rtc regs image
29         memset(regs, 0, sizeof(regs));
30         regs[POWON] = 0x10;     // cleared
31         
32         FILEIO* fio = new FILEIO();
33         if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_READ_BINARY)) {
34                 fio->Fread(regs + 8, 32, 1);
35                 fio->Fclose();
36         }
37         delete fio;
38         
39         // init registers
40 //      regs[POWON] &= 0x1f;    // local power on
41 //      regs[POWOF] = 0x80;     // program power off
42         regs[POWON] = 0x10;     // cleared
43         regs[POWOF] = 0x20;     // illegal power off
44         regs[TCNT] = 0;
45         update_checksum();
46         
47         rtcmr = rtdsr = 0;
48         
49         // update calendar
50         emu->get_host_time(&cur_time);
51         read_from_cur_time();
52         
53         // register event
54         register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, &register_id);
55         register_event_by_clock(this, EVENT_32HZ, CPU_CLOCKS >> 5, true, NULL);
56 }
57
58 void RTC::release()
59 {
60         // set power off time
61         regs[POFMI] = TO_BCD(cur_time.minute);
62         regs[POFH] = TO_BCD(cur_time.hour);
63         regs[POFD] = TO_BCD(cur_time.day);
64         
65         // save rtc regs image
66         FILEIO* fio = new FILEIO();
67         if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_WRITE_BINARY)) {
68                 fio->Fwrite(regs + 8, 32, 1);
69                 fio->Fclose();
70         }
71         delete fio;
72 }
73
74 void RTC::write_io16(uint32 addr, uint32 data)
75 {
76         switch(addr) {
77         case 0:
78                 rtcmr = data;
79                 break;
80         case 2:
81                 // echo reset
82                 rtdsr &= ~(data & 0xe);
83                 update_intr();
84                 break;
85         case 4:
86                 if(!(rtdsr & 1)) {
87                         rtadr = data;
88                         rtdsr |= 1;
89                         // register event
90                         register_event(this, EVENT_DONE, 100, false, NULL);
91                 }
92                 break;
93         case 6:
94                 rtobr = data;
95                 break;
96         }
97 }
98
99 uint32 RTC::read_io16(uint32 addr)
100 {
101         switch(addr) {
102         case 2:
103                 return rtdsr;
104         case 6:
105                 return rtibr;
106         }
107         return 0xffff;
108 }
109
110 void RTC::event_callback(int event_id, int err)
111 {
112         if(event_id == EVENT_1HZ) {
113                 // update calendar
114                 if(cur_time.initialized) {
115                         cur_time.increment();
116                 } else {
117                         emu->get_host_time(&cur_time);  // resync
118                         cur_time.initialized = true;
119                 }
120                 read_from_cur_time();
121                 
122                 // 1sec interrupt
123                 rtdsr |= 4;
124                 update_intr();
125         } else if(event_id == EVENT_32HZ) {
126                 // update tcnt
127                 regs[TCNT]++;
128         } else if(event_id == EVENT_DONE) {
129                 int ch = (rtadr >> 1) & 0x3f;
130                 if(rtadr & 1) {
131                         // invalid address
132                 } else if(rtadr & 0x80) {
133                         // write
134                         if(ch <= 6) {
135                                 regs[ch] = (uint8)rtobr;
136                                 write_to_cur_time();
137                         } else if(ch == POWON) {
138                                 regs[ch] = (regs[ch] & 0xe0) | (rtobr & 0x1f);
139                                 if((rtobr & 0xe0) == 0xc0) {
140                                         // reipl
141                                         regs[ch] = (regs[ch] & 0x1f) | 0xc0;
142                                         vm->reset();
143                                 } else if((rtobr & 0xe0) == 0xe0) {
144                                         // power off
145                                         emu->power_off();
146                                 }
147                                 update_checksum();
148                         } else if(7 <= ch && ch < 32) {
149                                 regs[ch] = (uint8)rtobr;
150                                 update_checksum();
151                         }
152                 } else {
153                         // read
154                         if(ch < 40) {
155                                 rtibr = regs[ch];
156                         }
157                 }
158                 // update flags
159                 rtdsr &= ~1;
160                 rtdsr |= 2;
161                 update_intr();
162         }
163 }
164
165 void RTC::read_from_cur_time()
166 {
167         regs[0] = TO_BCD(cur_time.second);
168         regs[1] = TO_BCD(cur_time.minute);
169         regs[2] = TO_BCD(cur_time.hour);
170         regs[3] = cur_time.day_of_week;
171         regs[4] = TO_BCD(cur_time.day);
172         regs[5] = TO_BCD(cur_time.month);
173         regs[6] = TO_BCD(cur_time.year);
174 }
175
176 void RTC::write_to_cur_time()
177 {
178         cur_time.second = FROM_BCD(regs[0]);
179         cur_time.minute = FROM_BCD(regs[1]);
180         cur_time.hour = FROM_BCD(regs[2]);
181 //      cur_time.day_of_week = regs[3];
182         cur_time.day = FROM_BCD(regs[4]);
183         cur_time.month = FROM_BCD(regs[5]);
184         cur_time.year = FROM_BCD(regs[6]);
185         cur_time.update_year();
186         cur_time.update_day_of_week();
187         
188         // restart event
189         cancel_event(this, register_id);
190         register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, &register_id);
191 }
192
193 void RTC::update_checksum()
194 {
195         int sum = 0;
196         for(int i = 8; i < 32; i++) {
197                 sum += regs[i] & 0xf;
198                 sum += (regs[i] >> 4) & 0xf;
199         }
200         uint8 ckh = (sum >> 6) & 0xf;
201         uint8 ckm = (sum >> 2) & 0xf;
202         uint8 ckl = (sum >> 0) & 3;
203         
204         regs[CKHM] = ckh | (ckm << 4);
205         regs[CKL] = (regs[CKL] & 0xf0) | ckl | 0xc;
206 }
207
208 void RTC::update_intr()
209 {
210         d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR1, (rtcmr & rtdsr & 0xe) ? 1 : 0, 1);
211 }
212
213 #define STATE_VERSION   1
214
215 void RTC::save_state(FILEIO* state_fio)
216 {
217         state_fio->FputUint32(STATE_VERSION);
218         state_fio->FputInt32(this_device_id);
219         
220         cur_time.save_state((void *)state_fio);
221         state_fio->FputInt32(register_id);
222         state_fio->FputUint16(rtcmr);
223         state_fio->FputUint16(rtdsr);
224         state_fio->FputUint16(rtadr);
225         state_fio->FputUint16(rtobr);
226         state_fio->FputUint16(rtibr);
227         state_fio->Fwrite(regs, sizeof(regs), 1);
228 }
229
230 bool RTC::load_state(FILEIO* state_fio)
231 {
232         if(state_fio->FgetUint32() != STATE_VERSION) {
233                 return false;
234         }
235         if(state_fio->FgetInt32() != this_device_id) {
236                 return false;
237         }
238         if(!cur_time.load_state((void *)state_fio)) {
239                 return false;
240         }
241         register_id = state_fio->FgetInt32();
242         rtcmr = state_fio->FgetUint16();
243         rtdsr = state_fio->FgetUint16();
244         rtadr = state_fio->FgetUint16();
245         rtobr = state_fio->FgetUint16();
246         rtibr = state_fio->FgetUint16();
247         state_fio->Fread(regs, sizeof(regs), 1);
248         return true;
249 }
250