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 53 struct RTCState {
54 54 uint8_t cmos_data[128];
55 55 uint8_t cmos_index;
56   - int current_time; /* in seconds */
  56 + struct tm current_tm;
57 57 int irq;
58   - uint8_t buf_data[10]; /* buffered data */
59 58 /* periodic timer */
60 59 QEMUTimer *periodic_timer;
61 60 int64_t next_periodic_time;
... ... @@ -66,7 +65,6 @@ struct RTCState {
66 65 };
67 66  
68 67 static void rtc_set_time(RTCState *s);
69   -static void rtc_set_date_buf(RTCState *s, const struct tm *tm);
70 68 static void rtc_copy_date(RTCState *s);
71 69  
72 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 180  
183 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 185 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
188 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 192 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
191 193 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
192 194 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
193 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 273 static void rtc_update_second(void *opaque)
207 274 {
208 275 RTCState *s = opaque;
... ... @@ -213,7 +280,7 @@ static void rtc_update_second(void *opaque)
213 280 s->next_second_time += ticks_per_sec;
214 281 qemu_mod_timer(s->second_timer, s->next_second_time);
215 282 } else {
216   - s->current_time++;
  283 + rtc_next_second(&s->current_tm);
217 284  
218 285 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
219 286 /* update in progress bit */
... ... @@ -232,10 +299,6 @@ static void rtc_update_second(void *opaque)
232 299 static void rtc_update_second2(void *opaque)
233 300 {
234 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 303 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
241 304 rtc_copy_date(s);
... ... @@ -244,11 +307,11 @@ static void rtc_update_second2(void *opaque)
244 307 /* check alarm */
245 308 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
246 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 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 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 316 s->cmos_data[RTC_REG_C] |= 0xa0;
254 317 pic_set_irq(s->irq, 1);
... ... @@ -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 371 void rtc_set_memory(RTCState *s, int addr, int val)
339 372 {
340 373 if (addr >= 0 && addr <= 127)
... ... @@ -343,8 +376,7 @@ void rtc_set_memory(RTCState *s, int addr, int val)
343 376  
344 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 380 rtc_copy_date(s);
349 381 }
350 382  
... ... @@ -354,8 +386,14 @@ static void rtc_save(QEMUFile *f, void *opaque)
354 386  
355 387 qemu_put_buffer(f, s->cmos_data, 128);
356 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 398 qemu_put_timer(f, s->periodic_timer);
361 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 412  
375 413 qemu_get_buffer(f, s->cmos_data, 128);
376 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 424 qemu_get_timer(f, s->periodic_timer);
381 425 qemu_get_be64s(f, &s->next_periodic_time);
... ...