- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / cmos.cc
blobefaea4c24524ecdb53b59b2e7d6d36733870f2e6
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cmos.cc,v 1.64 2008/02/15 22:05:41 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
29 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
30 // is used to know when we are exporting symbols and when we are importing.
31 #define BX_PLUGGABLE
33 #include "iodev.h"
35 #define LOG_THIS theCmosDevice->
37 bx_cmos_c *theCmosDevice = NULL;
39 // CMOS register definitions from Ralf Brown's interrupt list v6.1, in a file
40 // called cmos.lst. In cases where there are multiple uses for a given
41 // register in the interrupt list, I only listed the purpose that Bochs
42 // actually uses it for, but I wrote "alternatives" next to it.
43 #define REG_SEC 0x00
44 #define REG_SEC_ALARM 0x01
45 #define REG_MIN 0x02
46 #define REG_MIN_ALARM 0x03
47 #define REG_HOUR 0x04
48 #define REG_HOUR_ALARM 0x05
49 #define REG_WEEK_DAY 0x06
50 #define REG_MONTH_DAY 0x07
51 #define REG_MONTH 0x08
52 #define REG_YEAR 0x09
53 #define REG_STAT_A 0x0a
54 #define REG_STAT_B 0x0b
55 #define REG_STAT_C 0x0c
56 #define REG_STAT_D 0x0d
57 #define REG_DIAGNOSTIC_STATUS 0x0e /* alternatives */
58 #define REG_SHUTDOWN_STATUS 0x0f
59 #define REG_EQUIPMENT_BYTE 0x14
60 #define REG_CSUM_HIGH 0x2e
61 #define REG_CSUM_LOW 0x2f
62 #define REG_IBM_CENTURY_BYTE 0x32 /* alternatives */
63 #define REG_IBM_PS2_CENTURY_BYTE 0x37 /* alternatives */
65 // Bochs CMOS map
67 // Idx Len Description
68 // 0x10 1 floppy drive types
69 // 0x11 1 configuration bits
70 // 0x12 1 harddisk types
71 // 0x13 1 advanced configuration bits
72 // 0x15 2 base memory in 1k
73 // 0x17 2 memory size above 1M in 1k
74 // 0x19 2 extended harddisk types
75 // 0x1b 9 harddisk configuration (hd0)
76 // 0x24 9 harddisk configuration (hd1)
77 // 0x2d 1 boot sequence (fd/hd)
78 // 0x30 2 memory size above 1M in 1k
79 // 0x34 2 memory size above 16M in 64k
80 // 0x38 1 eltorito boot sequence (#3) + bootsig check
81 // 0x39 2 ata translation policy (ata0...ata3)
82 // 0x3d 1 eltorito boot sequence (#1 + #2)
86 Bit8u bcd_to_bin(Bit8u value, bx_bool is_binary)
88 if (is_binary)
89 return value;
90 else
91 return ((value >> 4) * 10) + (value & 0x0f);
94 Bit8u bin_to_bcd(Bit8u value, bx_bool is_binary)
96 if (is_binary)
97 return value;
98 else
99 return ((value / 10) << 4) | (value % 10);
102 int libcmos_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
104 theCmosDevice = new bx_cmos_c();
105 bx_devices.pluginCmosDevice = theCmosDevice;
106 BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theCmosDevice, BX_PLUGIN_CMOS);
107 return(0); // Success
110 void libcmos_LTX_plugin_fini(void)
111 { if (theCmosDevice != NULL)
112 { delete theCmosDevice;
113 theCmosDevice = NULL;
117 bx_cmos_c::bx_cmos_c(void)
119 put("CMOS");
120 settype(CMOSLOG);
122 for (unsigned i=0; i<128; i++) s.reg[i] = 0;
124 s.periodic_timer_index = BX_NULL_TIMER_HANDLE;
125 s.one_second_timer_index = BX_NULL_TIMER_HANDLE;
126 s.uip_timer_index = BX_NULL_TIMER_HANDLE;
129 bx_cmos_c::~bx_cmos_c(void)
131 save_image();
132 char *tmptime;
133 if ((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) != NULL) {
134 tmptime[strlen(tmptime)-1]='\0';
135 BX_INFO(("Last time is %u (%s)", (unsigned) get_timeval(), tmptime));
136 free(tmptime);
138 BX_DEBUG(("Exit"));
141 void bx_cmos_c::init(void)
143 BX_DEBUG(("Init $Id: cmos.cc,v 1.64 2008/02/15 22:05:41 sshwarts Exp $"));
144 // CMOS RAM & RTC
146 DEV_register_ioread_handler(this, read_handler, 0x0070, "CMOS RAM", 1);
147 DEV_register_ioread_handler(this, read_handler, 0x0071, "CMOS RAM", 1);
148 DEV_register_iowrite_handler(this, write_handler, 0x0070, "CMOS RAM", 1);
149 DEV_register_iowrite_handler(this, write_handler, 0x0071, "CMOS RAM", 1);
150 DEV_register_irq(8, "CMOS RTC");
151 if (BX_CMOS_THIS s.periodic_timer_index == BX_NULL_TIMER_HANDLE) {
152 BX_CMOS_THIS s.periodic_timer_index =
153 DEV_register_timer(this, periodic_timer_handler,
154 1000000, 1,0, "cmos"); // continuous, not-active
156 if (BX_CMOS_THIS s.one_second_timer_index == BX_NULL_TIMER_HANDLE) {
157 BX_CMOS_THIS s.one_second_timer_index =
158 DEV_register_timer(this, one_second_timer_handler,
159 1000000, 1,0, "cmos"); // continuous, not-active
161 if (BX_CMOS_THIS s.uip_timer_index == BX_NULL_TIMER_HANDLE) {
162 BX_CMOS_THIS s.uip_timer_index =
163 DEV_register_timer(this, uip_timer_handler,
164 244, 0, 0, "cmos"); // one-shot, not-active
167 if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_LOCAL) {
168 BX_INFO(("Using local time for initial clock"));
169 BX_CMOS_THIS s.timeval = time(NULL);
170 } else if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_UTC) {
171 bx_bool utc_ok = 0;
173 BX_INFO(("Using utc time for initial clock"));
175 BX_CMOS_THIS s.timeval = time(NULL);
177 #if BX_HAVE_GMTIME
178 #if BX_HAVE_MKTIME
179 struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
180 utc_holder->tm_isdst = -1;
181 utc_ok = 1;
182 BX_CMOS_THIS s.timeval = mktime(utc_holder);
183 #elif BX_HAVE_TIMELOCAL
184 struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
185 utc_holder->tm_isdst = 0; // XXX Is this correct???
186 utc_ok = 1;
187 BX_CMOS_THIS s.timeval = timelocal(utc_holder);
188 #endif //BX_HAVE_MKTIME
189 #endif //BX_HAVE_GMTIME
191 if (!utc_ok) {
192 BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)"));
194 } else {
195 BX_INFO(("Using specified time for initial clock"));
196 BX_CMOS_THIS s.timeval = SIM->get_param_num(BXPN_CLOCK_TIME0)->get();
199 // load CMOS from image file if requested.
200 if (SIM->get_param_bool(BXPN_CMOSIMAGE_ENABLED)->get()) {
201 int fd, ret;
202 struct stat stat_buf;
204 fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_RDONLY
205 #ifdef O_BINARY
206 | O_BINARY
207 #endif
209 if (fd < 0) {
210 BX_PANIC(("trying to open cmos image file '%s'",
211 SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
213 ret = fstat(fd, &stat_buf);
214 if (ret) {
215 BX_PANIC(("CMOS: could not fstat() image file."));
217 if ((stat_buf.st_size != 64) && (stat_buf.st_size != 128)) {
218 BX_PANIC(("CMOS: image file size must be 64 or 128"));
221 ret = ::read(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, (unsigned)stat_buf.st_size);
222 if (ret != stat_buf.st_size) {
223 BX_PANIC(("CMOS: error reading cmos file."));
225 close(fd);
226 BX_INFO(("successfuly read from image file '%s'.",
227 SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
228 BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
229 BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
230 if (SIM->get_param_bool(BXPN_CMOSIMAGE_RTC_INIT)->get()) {
231 update_timeval();
232 } else {
233 update_clock();
235 } else {
236 // CMOS values generated
237 BX_CMOS_THIS s.reg[REG_STAT_A] = 0x26;
238 BX_CMOS_THIS s.reg[REG_STAT_B] = 0x02;
239 BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
240 BX_CMOS_THIS s.reg[REG_STAT_D] = 0x80;
241 #if BX_SUPPORT_FPU == 1
242 BX_CMOS_THIS s.reg[REG_EQUIPMENT_BYTE] |= 0x02;
243 #endif
244 BX_CMOS_THIS s.rtc_mode_12hour = 0;
245 BX_CMOS_THIS s.rtc_mode_binary = 0;
246 update_clock();
249 char *tmptime;
250 while((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) == NULL) {
251 BX_PANIC(("Out of memory."));
253 tmptime[strlen(tmptime)-1]='\0';
254 BX_INFO(("Setting initial clock to: %s (time0=%u)", tmptime, (Bit32u)BX_CMOS_THIS s.timeval));
255 free(tmptime);
257 BX_CMOS_THIS s.timeval_change = 0;
260 void bx_cmos_c::reset(unsigned type)
262 BX_CMOS_THIS s.cmos_mem_address = 0;
264 // RESET affects the following registers:
265 // CRA: no effects
266 // CRB: bits 4,5,6 forced to 0
267 // CRC: bits 4,5,6,7 forced to 0
268 // CRD: no effects
269 BX_CMOS_THIS s.reg[REG_STAT_B] &= 0x8f;
270 BX_CMOS_THIS s.reg[REG_STAT_C] = 0;
272 // One second timer for updating clock & alarm functions
273 bx_pc_system.activate_timer(BX_CMOS_THIS s.one_second_timer_index,
274 1000000, 1);
276 // handle periodic interrupt rate select
277 BX_CMOS_THIS CRA_change();
280 void bx_cmos_c::save_image(void)
282 int fd, ret;
284 // save CMOS to image file if requested.
285 if (SIM->get_param_bool(BXPN_CMOSIMAGE_ENABLED)->get()) {
286 fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_WRONLY
287 #ifdef O_BINARY
288 | O_BINARY
289 #endif
291 ret = ::write(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, 128);
292 if (ret != 128) {
293 BX_PANIC(("CMOS: error writing cmos file."));
295 close(fd);
299 void bx_cmos_c::register_state(void)
301 bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "cmos", "CMOS State", 2);
302 BXRS_HEX_PARAM_FIELD(list, mem_address, BX_CMOS_THIS s.cmos_mem_address);
303 bx_list_c *ram = new bx_list_c(list, "ram", 128);
304 for (unsigned i=0; i<128; i++) {
305 char name[6];
306 sprintf(name, "0x%02x", i);
307 new bx_shadow_num_c(ram, name, &BX_CMOS_THIS s.reg[i], BASE_HEX);
311 void bx_cmos_c::after_restore_state(void)
313 BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
314 BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
315 BX_CMOS_THIS update_timeval();
316 BX_CMOS_THIS CRA_change();
319 void bx_cmos_c::CRA_change(void)
321 Bit8u nibble, dcc;
323 // Periodic Interrupt timer
324 nibble = BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f;
325 dcc = (BX_CMOS_THIS s.reg[REG_STAT_A] >> 4) & 0x07;
326 if ((nibble == 0) || ((dcc & 0x06) == 0)) {
327 // No Periodic Interrupt Rate when 0, deactivate timer
328 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
329 BX_CMOS_THIS s.periodic_interval_usec = (Bit32u) -1; // max value
330 } else {
331 // values 0001b and 0010b are the same as 1000b and 1001b
332 if (nibble <= 2)
333 nibble += 7;
334 BX_CMOS_THIS s.periodic_interval_usec = (unsigned) (1000000.0L /
335 (32768.0L / (1 << (nibble - 1))));
337 // if Periodic Interrupt Enable bit set, activate timer
338 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40)
339 bx_pc_system.activate_timer(BX_CMOS_THIS s.periodic_timer_index,
340 BX_CMOS_THIS s.periodic_interval_usec, 1);
341 else
342 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
347 // static IO port read callback handler
348 // redirects to non-static class handler to avoid virtual functions
350 Bit32u bx_cmos_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
352 #if !BX_USE_CMOS_SMF
353 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
354 return class_ptr->read(address, io_len);
357 Bit32u bx_cmos_c::read(Bit32u address, unsigned io_len)
359 #else
360 UNUSED(this_ptr);
361 #endif
362 Bit8u ret8;
364 if (bx_dbg.cmos)
365 BX_INFO(("CMOS read of CMOS register 0x%02x",
366 (unsigned) BX_CMOS_THIS s.cmos_mem_address));
368 switch (address) {
369 case 0x0070:
370 // this register is write-only on most machines
371 BX_DEBUG(("read of index port 0x70. returning 0xff"));
372 return(0xff);
373 case 0x0071:
374 ret8 = BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address];
375 // all bits of Register C are cleared after a read occurs.
376 if (BX_CMOS_THIS s.cmos_mem_address == REG_STAT_C) {
377 BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
378 DEV_pic_lower_irq(8);
380 return(ret8);
382 default:
383 BX_PANIC(("unsupported cmos read, address=0x%04x!", (unsigned) address));
384 return(0);
389 // static IO port write callback handler
390 // redirects to non-static class handler to avoid virtual functions
391 void bx_cmos_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
393 #if !BX_USE_CMOS_SMF
394 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
395 class_ptr->write(address, value, io_len);
398 void bx_cmos_c::write(Bit32u address, Bit32u value, unsigned io_len)
400 #else
401 UNUSED(this_ptr);
402 #endif // !BX_USE_CMOS_SMF
404 BX_DEBUG(("CMOS write to address: 0x%04x = 0x%02x", address, value));
406 switch (address) {
407 case 0x0070:
408 BX_CMOS_THIS s.cmos_mem_address = value & 0x7F;
409 break;
411 case 0x0071:
412 switch (BX_CMOS_THIS s.cmos_mem_address) {
413 case REG_SEC_ALARM: // seconds alarm
414 case REG_MIN_ALARM: // minutes alarm
415 case REG_HOUR_ALARM: // hours alarm
416 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
417 BX_DEBUG(("alarm time changed to %02x:%02x:%02x", BX_CMOS_THIS s.reg[REG_HOUR_ALARM],
418 BX_CMOS_THIS s.reg[REG_MIN_ALARM], BX_CMOS_THIS s.reg[REG_SEC_ALARM]));
419 break;
421 case REG_SEC: // seconds
422 case REG_MIN: // minutes
423 case REG_HOUR: // hours
424 case REG_WEEK_DAY: // day of the week
425 case REG_MONTH_DAY: // day of the month
426 case REG_MONTH: // month
427 case REG_YEAR: // year
428 case REG_IBM_CENTURY_BYTE: // century
429 case REG_IBM_PS2_CENTURY_BYTE: // century (PS/2)
430 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
431 if (BX_CMOS_THIS s.cmos_mem_address == REG_IBM_PS2_CENTURY_BYTE) {
432 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = value;
434 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) {
435 BX_CMOS_THIS s.timeval_change = 1;
436 } else {
437 update_timeval();
439 break;
441 case REG_STAT_A: // Control Register A
442 // bit 7: Update in Progress (read-only)
443 // 1 = signifies time registers will be updated within 244us
444 // 0 = time registers will not occur before 244us
445 // note: this bit reads 0 when CRB bit 7 is 1
446 // bit 6..4: Divider Chain Control
447 // 000 oscillator disabled
448 // 001 oscillator disabled
449 // 010 Normal operation
450 // 011 TEST
451 // 100 TEST
452 // 101 TEST
453 // 110 Divider Chain RESET
454 // 111 Divider Chain RESET
455 // bit 3..0: Periodic Interrupt Rate Select
456 // 0000 None
457 // 0001 3.90625 ms
458 // 0010 7.8125 ms
459 // 0011 122.070 us
460 // 0100 244.141 us
461 // 0101 488.281 us
462 // 0110 976.562 us
463 // 0111 1.953125 ms
464 // 1000 3.90625 ms
465 // 1001 7.8125 ms
466 // 1010 15.625 ms
467 // 1011 31.25 ms
468 // 1100 62.5 ms
469 // 1101 125 ms
470 // 1110 250 ms
471 // 1111 500 ms
473 unsigned dcc;
474 dcc = (value >> 4) & 0x07;
475 if ((dcc & 0x06) == 0x06) {
476 BX_INFO(("CRA: divider chain RESET"));
477 } else if (dcc > 0x02) {
478 BX_PANIC(("CRA: divider chain control 0x%02x", dcc));
480 BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x80;
481 BX_CMOS_THIS s.reg[REG_STAT_A] |= (value & 0x7f);
482 BX_CMOS_THIS CRA_change();
483 break;
485 case REG_STAT_B: // Control Register B
486 // bit 0: Daylight Savings Enable
487 // 1 = enable daylight savings
488 // 0 = disable daylight savings
489 // bit 1: 24/12 hour mode
490 // 1 = 24 hour format
491 // 0 = 12 hour format
492 // bit 2: Data Mode
493 // 1 = binary format
494 // 0 = BCD format
495 // bit 3: "square wave enable"
496 // Not supported and always read as 0
497 // bit 4: Update Ended Interrupt Enable
498 // 1 = enable generation of update ended interrupt
499 // 0 = disable
500 // bit 5: Alarm Interrupt Enable
501 // 1 = enable generation of alarm interrupt
502 // 0 = disable
503 // bit 6: Periodic Interrupt Enable
504 // 1 = enable generation of periodic interrupt
505 // 0 = disable
506 // bit 7: Set mode
507 // 1 = user copy of time is "frozen" allowing time registers
508 // to be accessed without regard for an occurance of an update
509 // 0 = time updates occur normally
511 if (value & 0x01)
512 BX_ERROR(("write status reg B, daylight savings unsupported"));
514 value &= 0xf7; // bit3 always 0
515 // Note: setting bit 7 clears bit 4
516 if (value & 0x80)
517 value &= 0xef;
519 unsigned prev_CRB;
520 prev_CRB = BX_CMOS_THIS s.reg[REG_STAT_B];
521 BX_CMOS_THIS s.reg[REG_STAT_B] = value;
522 if ((prev_CRB & 0x02) != (value & 0x02)) {
523 BX_CMOS_THIS s.rtc_mode_12hour = ((value & 0x02) == 0);
524 update_clock();
526 if ((prev_CRB & 0x04) != (value & 0x04)) {
527 BX_CMOS_THIS s.rtc_mode_binary = ((value & 0x04) != 0);
528 update_clock();
530 if ((prev_CRB & 0x40) != (value & 0x40)) {
531 // Periodic Interrupt Enabled changed
532 if (prev_CRB & 0x40) {
533 // transition from 1 to 0, deactivate timer
534 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
535 } else {
536 // transition from 0 to 1
537 // if rate select is not 0, activate timer
538 if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f) != 0) {
539 bx_pc_system.activate_timer(
540 BX_CMOS_THIS s.periodic_timer_index,
541 BX_CMOS_THIS s.periodic_interval_usec, 1);
545 if ((prev_CRB >= 0x80) && (value < 0x80) && BX_CMOS_THIS s.timeval_change) {
546 update_timeval();
547 BX_CMOS_THIS s.timeval_change = 0;
549 break;
551 case REG_STAT_C: // Control Register C
552 case REG_STAT_D: // Control Register D
553 BX_ERROR(("write to control register 0x%02x ignored (read-only)",
554 BX_CMOS_THIS s.cmos_mem_address));
555 break;
557 case REG_DIAGNOSTIC_STATUS:
558 BX_DEBUG(("write register 0x0e: 0x%02x", value));
559 BX_CMOS_THIS s.reg[REG_DIAGNOSTIC_STATUS] = value;
560 break;
562 case REG_SHUTDOWN_STATUS:
563 switch (value) {
564 case 0x00: /* proceed with normal POST (soft reset) */
565 BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST"));
566 break;
567 case 0x01: /* shutdown after memory size check */
568 BX_DEBUG(("Reg 0Fh(01): request to change shutdown action"
569 " to shutdown after memory size check"));
570 break;
571 case 0x02: /* shutdown after successful memory test */
572 BX_DEBUG(("Reg 0Fh(02): request to change shutdown action"
573 " to shutdown after successful memory test"));
574 break;
575 case 0x03: /* shutdown after failed memory test */
576 BX_DEBUG(("Reg 0Fh(03): request to change shutdown action"
577 " to shutdown after successful memory test"));
578 break;
579 case 0x04: /* jump to disk bootstrap routine */
580 BX_DEBUG(("Reg 0Fh(04): request to change shutdown action "
581 "to jump to disk bootstrap routine."));
582 break;
583 case 0x05: /* flush keyboard (issue EOI) and jump via 40h:0067h */
584 BX_DEBUG(("Reg 0Fh(05): request to change shutdown action "
585 "to flush keyboard (issue EOI) and jump via 40h:0067h."));
586 break;
587 case 0x06:
588 BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !"));
589 break;
590 case 0x07: /* reset (after failed test in virtual mode) */
591 BX_DEBUG(("Reg 0Fh(07): request to change shutdown action "
592 "to reset (after failed test in virtual mode)."));
593 break;
594 case 0x08: /* used by POST during protected-mode RAM test (return to POST) */
595 BX_DEBUG(("Reg 0Fh(08): request to change shutdown action "
596 "to return to POST (used by POST during protected-mode RAM test)."));
597 break;
598 case 0x09: /* return to BIOS extended memory block move
599 (interrupt 15h, func 87h was in progress) */
600 BX_DEBUG(("Reg 0Fh(09): request to change shutdown action "
601 "to return to BIOS extended memory block move."));
602 break;
603 case 0x0a: /* jump to DWORD pointer at 40:67 */
604 BX_DEBUG(("Reg 0Fh(0a): request to change shutdown action"
605 " to jump to DWORD at 40:67"));
606 break;
607 case 0x0b: /* iret to DWORD pointer at 40:67 */
608 BX_DEBUG(("Reg 0Fh(0b): request to change shutdown action"
609 " to iret to DWORD at 40:67"));
610 break;
611 case 0x0c: /* retf to DWORD pointer at 40:67 */
612 BX_DEBUG(("Reg 0Fh(0c): request to change shutdown action"
613 " to retf to DWORD at 40:67"));
614 break;
615 default:
616 BX_ERROR(("unsupported shutdown status: 0x%02x!", value));
618 BX_CMOS_THIS s.reg[REG_SHUTDOWN_STATUS] = value;
619 break;
621 default:
622 BX_DEBUG(("write reg 0x%02x: value = 0x%02x",
623 BX_CMOS_THIS s.cmos_mem_address, value));
624 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
626 break;
630 void bx_cmos_c::checksum_cmos(void)
632 Bit16u sum = 0;
633 for (unsigned i=0x10; i<=0x2d; i++)
634 sum += BX_CMOS_THIS s.reg[i];
635 BX_CMOS_THIS s.reg[REG_CSUM_HIGH] = (sum >> 8) & 0xff; /* checksum high */
636 BX_CMOS_THIS s.reg[REG_CSUM_LOW] = (sum & 0xff); /* checksum low */
639 void bx_cmos_c::periodic_timer_handler(void *this_ptr)
641 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
642 class_ptr->periodic_timer();
645 void bx_cmos_c::periodic_timer()
647 // if periodic interrupts are enabled, trip IRQ 8, and
648 // update status register C
649 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40) {
650 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xc0; // Interrupt Request, Periodic Int
651 DEV_pic_raise_irq(8);
655 void bx_cmos_c::one_second_timer_handler(void *this_ptr)
657 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
658 class_ptr->one_second_timer();
661 void bx_cmos_c::one_second_timer()
663 // divider chain reset - RTC stopped
664 if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x60) == 0x60)
665 return;
667 // update internal time/date buffer
668 BX_CMOS_THIS s.timeval++;
670 // Dont update CMOS user copy of time/date if CRB bit7 is 1
671 // Nothing else do to
672 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80)
673 return;
675 BX_CMOS_THIS s.reg[REG_STAT_A] |= 0x80; // set UIP bit
677 // UIP timer for updating clock & alarm functions
678 bx_pc_system.activate_timer(BX_CMOS_THIS s.uip_timer_index, 244, 0);
681 void bx_cmos_c::uip_timer_handler(void *this_ptr)
683 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
684 class_ptr->uip_timer();
687 void bx_cmos_c::uip_timer()
689 update_clock();
691 // if update interrupts are enabled, trip IRQ 8, and
692 // update status register C
693 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x10) {
694 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0x90; // Interrupt Request, Update Ended
695 DEV_pic_raise_irq(8);
698 // compare CMOS user copy of time/date to alarm time/date here
699 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x20) {
700 // Alarm interrupts enabled
701 bx_bool alarm_match = 1;
702 if ((BX_CMOS_THIS s.reg[REG_SEC_ALARM] & 0xc0) != 0xc0) {
703 // seconds alarm not in dont care mode
704 if (BX_CMOS_THIS s.reg[REG_SEC] != BX_CMOS_THIS s.reg[REG_SEC_ALARM])
705 alarm_match = 0;
707 if ((BX_CMOS_THIS s.reg[REG_MIN_ALARM] & 0xc0) != 0xc0) {
708 // minutes alarm not in dont care mode
709 if (BX_CMOS_THIS s.reg[REG_MIN] != BX_CMOS_THIS s.reg[REG_MIN_ALARM])
710 alarm_match = 0;
712 if ((BX_CMOS_THIS s.reg[REG_HOUR_ALARM] & 0xc0) != 0xc0) {
713 // hours alarm not in dont care mode
714 if (BX_CMOS_THIS s.reg[REG_HOUR] != BX_CMOS_THIS s.reg[REG_HOUR_ALARM])
715 alarm_match = 0;
717 if (alarm_match) {
718 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xa0; // Interrupt Request, Alarm Int
719 DEV_pic_raise_irq(8);
722 BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x7f; // clear UIP bit
725 void bx_cmos_c::update_clock()
727 struct tm *time_calendar;
728 unsigned year, month, day, century;
729 Bit8u val_bcd, hour;
731 time_calendar = localtime(& BX_CMOS_THIS s.timeval);
733 // update seconds
734 BX_CMOS_THIS s.reg[REG_SEC] = bin_to_bcd(time_calendar->tm_sec,
735 BX_CMOS_THIS s.rtc_mode_binary);
737 // update minutes
738 BX_CMOS_THIS s.reg[REG_MIN] = bin_to_bcd(time_calendar->tm_min,
739 BX_CMOS_THIS s.rtc_mode_binary);
741 // update hours
742 if (BX_CMOS_THIS s.rtc_mode_12hour) {
743 hour = time_calendar->tm_hour;
744 val_bcd = (hour > 11) ? 0x80 : 0x00;
745 if (hour > 11) hour -= 12;
746 if (hour == 0) hour = 12;
747 val_bcd |= bin_to_bcd(hour, BX_CMOS_THIS s.rtc_mode_binary);
748 BX_CMOS_THIS s.reg[REG_HOUR] = val_bcd;
749 } else {
750 BX_CMOS_THIS s.reg[REG_HOUR] = bin_to_bcd(time_calendar->tm_hour,
751 BX_CMOS_THIS s.rtc_mode_binary);
754 // update day of the week
755 day = time_calendar->tm_wday + 1; // 0..6 to 1..7
756 BX_CMOS_THIS s.reg[REG_WEEK_DAY] = bin_to_bcd(day,
757 BX_CMOS_THIS s.rtc_mode_binary);
759 // update day of the month
760 day = time_calendar->tm_mday;
761 BX_CMOS_THIS s.reg[REG_MONTH_DAY] = bin_to_bcd(day,
762 BX_CMOS_THIS s.rtc_mode_binary);
764 // update month
765 month = time_calendar->tm_mon + 1;
766 BX_CMOS_THIS s.reg[REG_MONTH] = bin_to_bcd(month,
767 BX_CMOS_THIS s.rtc_mode_binary);
769 // update year
770 year = time_calendar->tm_year % 100;
771 BX_CMOS_THIS s.reg[REG_YEAR] = bin_to_bcd(year,
772 BX_CMOS_THIS s.rtc_mode_binary);
774 // update century
775 century = (time_calendar->tm_year / 100) + 19;
776 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = bin_to_bcd(century,
777 BX_CMOS_THIS s.rtc_mode_binary);
779 // Raul Hudea pointed out that some bioses also use reg 0x37 for the
780 // century byte. Tony Heller says this is critical in getting WinXP to run.
781 BX_CMOS_THIS s.reg[REG_IBM_PS2_CENTURY_BYTE] =
782 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE];
785 void bx_cmos_c::update_timeval()
787 struct tm time_calendar;
788 Bit8u val_bin, pm_flag;
790 // update seconds
791 time_calendar.tm_sec = bcd_to_bin(BX_CMOS_THIS s.reg[REG_SEC],
792 BX_CMOS_THIS s.rtc_mode_binary);
794 // update minutes
795 time_calendar.tm_min = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MIN],
796 BX_CMOS_THIS s.rtc_mode_binary);
798 // update hours
799 if (BX_CMOS_THIS s.rtc_mode_12hour) {
800 pm_flag = BX_CMOS_THIS s.reg[REG_HOUR] & 0x80;
801 val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR] & 0x70,
802 BX_CMOS_THIS s.rtc_mode_binary);
803 if ((val_bin < 12) & (pm_flag > 0)) {
804 val_bin += 12;
805 } else if ((val_bin == 12) & (pm_flag == 0)) {
806 val_bin = 0;
808 time_calendar.tm_hour = val_bin;
809 } else {
810 time_calendar.tm_hour = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR],
811 BX_CMOS_THIS s.rtc_mode_binary);
814 // update day of the month
815 time_calendar.tm_mday = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH_DAY],
816 BX_CMOS_THIS s.rtc_mode_binary);
818 // update month
819 time_calendar.tm_mon = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH],
820 BX_CMOS_THIS s.rtc_mode_binary) - 1;
822 // update year
823 val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE],
824 BX_CMOS_THIS s.rtc_mode_binary);
825 val_bin = (val_bin - 19) * 100;
826 val_bin += bcd_to_bin(BX_CMOS_THIS s.reg[REG_YEAR],
827 BX_CMOS_THIS s.rtc_mode_binary);
828 time_calendar.tm_year = val_bin;
830 BX_CMOS_THIS s.timeval = mktime(& time_calendar);