Commit 43f493afb43f041919f46c6fb781baffbf53dbd0

Authored by bellard
1 parent ee22c2f7

more accurate emulation (do not depend on localtime() or gmtime()


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@872 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 102 additions and 58 deletions
hw/mc146818rtc.c
@@ -53,9 +53,8 @@ @@ -53,9 +53,8 @@
53 struct RTCState { 53 struct RTCState {
54 uint8_t cmos_data[128]; 54 uint8_t cmos_data[128];
55 uint8_t cmos_index; 55 uint8_t cmos_index;
56 - int current_time; /* in seconds */ 56 + struct tm current_tm;
57 int irq; 57 int irq;
58 - uint8_t buf_data[10]; /* buffered data */  
59 /* periodic timer */ 58 /* periodic timer */
60 QEMUTimer *periodic_timer; 59 QEMUTimer *periodic_timer;
61 int64_t next_periodic_time; 60 int64_t next_periodic_time;
@@ -66,7 +65,6 @@ struct RTCState { @@ -66,7 +65,6 @@ struct RTCState {
66 }; 65 };
67 66
68 static void rtc_set_time(RTCState *s); 67 static void rtc_set_time(RTCState *s);
69 -static void rtc_set_date_buf(RTCState *s, const struct tm *tm);  
70 static void rtc_copy_date(RTCState *s); 68 static void rtc_copy_date(RTCState *s);
71 69
72 static void rtc_timer_update(RTCState *s, int64_t current_time) 70 static void rtc_timer_update(RTCState *s, int64_t current_time)
@@ -182,27 +180,96 @@ static inline int from_bcd(RTCState *s, int a) @@ -182,27 +180,96 @@ static inline int from_bcd(RTCState *s, int a)
182 180
183 static void rtc_set_time(RTCState *s) 181 static void rtc_set_time(RTCState *s)
184 { 182 {
185 - struct tm tm1, *tm = &tm1; 183 + struct tm *tm = &s->current_tm;
186 184
187 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]); 185 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
188 tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]); 186 tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
189 - tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS]); 187 + tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
  188 + if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
  189 + (s->cmos_data[RTC_HOURS] & 0x80)) {
  190 + tm->tm_hour += 12;
  191 + }
190 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]); 192 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
191 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); 193 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
192 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; 194 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
193 tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100; 195 tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
  196 +}
  197 +
  198 +static void rtc_copy_date(RTCState *s)
  199 +{
  200 + const struct tm *tm = &s->current_tm;
194 201
195 - /* update internal state */  
196 - s->buf_data[RTC_SECONDS] = s->cmos_data[RTC_SECONDS];  
197 - s->buf_data[RTC_MINUTES] = s->cmos_data[RTC_MINUTES];  
198 - s->buf_data[RTC_HOURS] = s->cmos_data[RTC_HOURS];  
199 - s->buf_data[RTC_DAY_OF_WEEK] = s->cmos_data[RTC_DAY_OF_WEEK];  
200 - s->buf_data[RTC_DAY_OF_MONTH] = s->cmos_data[RTC_DAY_OF_MONTH];  
201 - s->buf_data[RTC_MONTH] = s->cmos_data[RTC_MONTH];  
202 - s->buf_data[RTC_YEAR] = s->cmos_data[RTC_YEAR];  
203 - s->current_time = mktime(tm); 202 + s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
  203 + s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
  204 + if (s->cmos_data[RTC_REG_B] & 0x02) {
  205 + /* 24 hour format */
  206 + s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
  207 + } else {
  208 + /* 12 hour format */
  209 + s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
  210 + if (tm->tm_hour >= 12)
  211 + s->cmos_data[RTC_HOURS] |= 0x80;
  212 + }
  213 + s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
  214 + s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
  215 + s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
  216 + s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
  217 +}
  218 +
  219 +/* month is between 0 and 11. */
  220 +static int get_days_in_month(int month, int year)
  221 +{
  222 + static const int days_tab[12] = {
  223 + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  224 + };
  225 + int d;
  226 + if ((unsigned )month >= 12)
  227 + return 31;
  228 + d = days_tab[month];
  229 + if (month == 1) {
  230 + if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
  231 + d++;
  232 + }
  233 + return d;
  234 +}
  235 +
  236 +/* update 'tm' to the next second */
  237 +static void rtc_next_second(struct tm *tm)
  238 +{
  239 + int days_in_month;
  240 +
  241 + tm->tm_sec++;
  242 + if ((unsigned)tm->tm_sec >= 60) {
  243 + tm->tm_sec = 0;
  244 + tm->tm_min++;
  245 + if ((unsigned)tm->tm_min >= 60) {
  246 + tm->tm_min = 0;
  247 + tm->tm_hour++;
  248 + if ((unsigned)tm->tm_hour >= 24) {
  249 + tm->tm_hour = 0;
  250 + /* next day */
  251 + tm->tm_wday++;
  252 + if ((unsigned)tm->tm_wday >= 7)
  253 + tm->tm_wday = 0;
  254 + days_in_month = get_days_in_month(tm->tm_mon,
  255 + tm->tm_year + 1900);
  256 + tm->tm_mday++;
  257 + if (tm->tm_mday < 1) {
  258 + tm->tm_mday = 1;
  259 + } else if (tm->tm_mday > days_in_month) {
  260 + tm->tm_mday = 1;
  261 + tm->tm_mon++;
  262 + if (tm->tm_mon >= 12) {
  263 + tm->tm_mon = 0;
  264 + tm->tm_year++;
  265 + }
  266 + }
  267 + }
  268 + }
  269 + }
204 } 270 }
205 271
  272 +
206 static void rtc_update_second(void *opaque) 273 static void rtc_update_second(void *opaque)
207 { 274 {
208 RTCState *s = opaque; 275 RTCState *s = opaque;
@@ -213,7 +280,7 @@ static void rtc_update_second(void *opaque) @@ -213,7 +280,7 @@ static void rtc_update_second(void *opaque)
213 s->next_second_time += ticks_per_sec; 280 s->next_second_time += ticks_per_sec;
214 qemu_mod_timer(s->second_timer, s->next_second_time); 281 qemu_mod_timer(s->second_timer, s->next_second_time);
215 } else { 282 } else {
216 - s->current_time++; 283 + rtc_next_second(&s->current_tm);
217 284
218 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { 285 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
219 /* update in progress bit */ 286 /* update in progress bit */
@@ -232,10 +299,6 @@ static void rtc_update_second(void *opaque) @@ -232,10 +299,6 @@ static void rtc_update_second(void *opaque)
232 static void rtc_update_second2(void *opaque) 299 static void rtc_update_second2(void *opaque)
233 { 300 {
234 RTCState *s = opaque; 301 RTCState *s = opaque;
235 - time_t ti;  
236 -  
237 - ti = s->current_time;  
238 - rtc_set_date_buf(s, gmtime(&ti));  
239 302
240 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { 303 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
241 rtc_copy_date(s); 304 rtc_copy_date(s);
@@ -244,11 +307,11 @@ static void rtc_update_second2(void *opaque) @@ -244,11 +307,11 @@ static void rtc_update_second2(void *opaque)
244 /* check alarm */ 307 /* check alarm */
245 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { 308 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
246 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 || 309 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
247 - s->cmos_data[RTC_SECONDS_ALARM] == s->buf_data[RTC_SECONDS]) && 310 + s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
248 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 || 311 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
249 - s->cmos_data[RTC_MINUTES_ALARM] == s->buf_data[RTC_MINUTES]) && 312 + s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
250 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 || 313 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
251 - s->cmos_data[RTC_HOURS_ALARM] == s->buf_data[RTC_HOURS])) { 314 + s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
252 315
253 s->cmos_data[RTC_REG_C] |= 0xa0; 316 s->cmos_data[RTC_REG_C] |= 0xa0;
254 pic_set_irq(s->irq, 1); 317 pic_set_irq(s->irq, 1);
@@ -305,36 +368,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) @@ -305,36 +368,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
305 } 368 }
306 } 369 }
307 370
308 -static void rtc_set_date_buf(RTCState *s, const struct tm *tm)  
309 -{  
310 - s->buf_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);  
311 - s->buf_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);  
312 - if (s->cmos_data[RTC_REG_B] & 0x02) {  
313 - /* 24 hour format */  
314 - s->buf_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);  
315 - } else {  
316 - /* 12 hour format */  
317 - s->buf_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);  
318 - if (tm->tm_hour >= 12)  
319 - s->buf_data[RTC_HOURS] |= 0x80;  
320 - }  
321 - s->buf_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);  
322 - s->buf_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);  
323 - s->buf_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);  
324 - s->buf_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);  
325 -}  
326 -  
327 -static void rtc_copy_date(RTCState *s)  
328 -{  
329 - s->cmos_data[RTC_SECONDS] = s->buf_data[RTC_SECONDS];  
330 - s->cmos_data[RTC_MINUTES] = s->buf_data[RTC_MINUTES];  
331 - s->cmos_data[RTC_HOURS] = s->buf_data[RTC_HOURS];  
332 - s->cmos_data[RTC_DAY_OF_WEEK] = s->buf_data[RTC_DAY_OF_WEEK];  
333 - s->cmos_data[RTC_DAY_OF_MONTH] = s->buf_data[RTC_DAY_OF_MONTH];  
334 - s->cmos_data[RTC_MONTH] = s->buf_data[RTC_MONTH];  
335 - s->cmos_data[RTC_YEAR] = s->buf_data[RTC_YEAR];  
336 -}  
337 -  
338 void rtc_set_memory(RTCState *s, int addr, int val) 371 void rtc_set_memory(RTCState *s, int addr, int val)
339 { 372 {
340 if (addr >= 0 && addr <= 127) 373 if (addr >= 0 && addr <= 127)
@@ -343,8 +376,7 @@ void rtc_set_memory(RTCState *s, int addr, int val) @@ -343,8 +376,7 @@ void rtc_set_memory(RTCState *s, int addr, int val)
343 376
344 void rtc_set_date(RTCState *s, const struct tm *tm) 377 void rtc_set_date(RTCState *s, const struct tm *tm)
345 { 378 {
346 - s->current_time = mktime((struct tm *)tm);  
347 - rtc_set_date_buf(s, tm); 379 + s->current_tm = *tm;
348 rtc_copy_date(s); 380 rtc_copy_date(s);
349 } 381 }
350 382
@@ -354,8 +386,14 @@ static void rtc_save(QEMUFile *f, void *opaque) @@ -354,8 +386,14 @@ static void rtc_save(QEMUFile *f, void *opaque)
354 386
355 qemu_put_buffer(f, s->cmos_data, 128); 387 qemu_put_buffer(f, s->cmos_data, 128);
356 qemu_put_8s(f, &s->cmos_index); 388 qemu_put_8s(f, &s->cmos_index);
357 - qemu_put_be32s(f, &s->current_time);  
358 - qemu_put_buffer(f, s->buf_data, 10); 389 +
  390 + qemu_put_be32s(f, &s->current_tm.tm_sec);
  391 + qemu_put_be32s(f, &s->current_tm.tm_min);
  392 + qemu_put_be32s(f, &s->current_tm.tm_hour);
  393 + qemu_put_be32s(f, &s->current_tm.tm_wday);
  394 + qemu_put_be32s(f, &s->current_tm.tm_mday);
  395 + qemu_put_be32s(f, &s->current_tm.tm_mon);
  396 + qemu_put_be32s(f, &s->current_tm.tm_year);
359 397
360 qemu_put_timer(f, s->periodic_timer); 398 qemu_put_timer(f, s->periodic_timer);
361 qemu_put_be64s(f, &s->next_periodic_time); 399 qemu_put_be64s(f, &s->next_periodic_time);
@@ -374,8 +412,14 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id) @@ -374,8 +412,14 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id)
374 412
375 qemu_get_buffer(f, s->cmos_data, 128); 413 qemu_get_buffer(f, s->cmos_data, 128);
376 qemu_get_8s(f, &s->cmos_index); 414 qemu_get_8s(f, &s->cmos_index);
377 - qemu_get_be32s(f, &s->current_time);  
378 - qemu_get_buffer(f, s->buf_data, 10); 415 +
  416 + qemu_get_be32s(f, &s->current_tm.tm_sec);
  417 + qemu_get_be32s(f, &s->current_tm.tm_min);
  418 + qemu_get_be32s(f, &s->current_tm.tm_hour);
  419 + qemu_get_be32s(f, &s->current_tm.tm_wday);
  420 + qemu_get_be32s(f, &s->current_tm.tm_mday);
  421 + qemu_get_be32s(f, &s->current_tm.tm_mon);
  422 + qemu_get_be32s(f, &s->current_tm.tm_year);
379 423
380 qemu_get_timer(f, s->periodic_timer); 424 qemu_get_timer(f, s->periodic_timer);
381 qemu_get_be64s(f, &s->next_periodic_time); 425 qemu_get_be64s(f, &s->next_periodic_time);