1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cmos.cc,v 1.64 2008/02/15 22:05:41 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2002 MandrakeSoft S.A.
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.
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.
44 #define REG_SEC_ALARM 0x01
46 #define REG_MIN_ALARM 0x03
48 #define REG_HOUR_ALARM 0x05
49 #define REG_WEEK_DAY 0x06
50 #define REG_MONTH_DAY 0x07
51 #define REG_MONTH 0x08
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 */
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
)
91 return ((value
>> 4) * 10) + (value
& 0x0f);
94 Bit8u
bin_to_bcd(Bit8u value
, bx_bool is_binary
)
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)
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)
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
));
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 $"));
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
) {
173 BX_INFO(("Using utc time for initial clock"));
175 BX_CMOS_THIS s
.timeval
= time(NULL
);
179 struct tm
*utc_holder
= gmtime(&BX_CMOS_THIS s
.timeval
);
180 utc_holder
->tm_isdst
= -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???
187 BX_CMOS_THIS s
.timeval
= timelocal(utc_holder
);
188 #endif //BX_HAVE_MKTIME
189 #endif //BX_HAVE_GMTIME
192 BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)"));
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()) {
202 struct stat stat_buf
;
204 fd
= open(SIM
->get_param_string(BXPN_CMOSIMAGE_PATH
)->getptr(), O_RDONLY
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
);
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."));
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()) {
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;
244 BX_CMOS_THIS s
.rtc_mode_12hour
= 0;
245 BX_CMOS_THIS s
.rtc_mode_binary
= 0;
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
));
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:
266 // CRB: bits 4,5,6 forced to 0
267 // CRC: bits 4,5,6,7 forced to 0
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
,
276 // handle periodic interrupt rate select
277 BX_CMOS_THIS
CRA_change();
280 void bx_cmos_c::save_image(void)
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
291 ret
= ::write(fd
, (bx_ptr_t
) BX_CMOS_THIS s
.reg
, 128);
293 BX_PANIC(("CMOS: error writing cmos file."));
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
++) {
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)
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
331 // values 0001b and 0010b are the same as 1000b and 1001b
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);
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
)
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
)
365 BX_INFO(("CMOS read of CMOS register 0x%02x",
366 (unsigned) BX_CMOS_THIS s
.cmos_mem_address
));
370 // this register is write-only on most machines
371 BX_DEBUG(("read of index port 0x70. returning 0xff"));
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);
383 BX_PANIC(("unsupported cmos read, address=0x%04x!", (unsigned) address
));
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
)
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
)
402 #endif // !BX_USE_CMOS_SMF
404 BX_DEBUG(("CMOS write to address: 0x%04x = 0x%02x", address
, value
));
408 BX_CMOS_THIS s
.cmos_mem_address
= value
& 0x7F;
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
]));
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;
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
453 // 110 Divider Chain RESET
454 // 111 Divider Chain RESET
455 // bit 3..0: Periodic Interrupt Rate Select
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();
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
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
500 // bit 5: Alarm Interrupt Enable
501 // 1 = enable generation of alarm interrupt
503 // bit 6: Periodic Interrupt Enable
504 // 1 = enable generation of periodic interrupt
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
512 BX_ERROR(("write status reg B, daylight savings unsupported"));
514 value
&= 0xf7; // bit3 always 0
515 // Note: setting bit 7 clears bit 4
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);
526 if ((prev_CRB
& 0x04) != (value
& 0x04)) {
527 BX_CMOS_THIS s
.rtc_mode_binary
= ((value
& 0x04) != 0);
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
);
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
) {
547 BX_CMOS_THIS s
.timeval_change
= 0;
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
));
557 case REG_DIAGNOSTIC_STATUS
:
558 BX_DEBUG(("write register 0x0e: 0x%02x", value
));
559 BX_CMOS_THIS s
.reg
[REG_DIAGNOSTIC_STATUS
] = value
;
562 case REG_SHUTDOWN_STATUS
:
564 case 0x00: /* proceed with normal POST (soft reset) */
565 BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST"));
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"));
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"));
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"));
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."));
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."));
588 BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !"));
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)."));
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)."));
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."));
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"));
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"));
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"));
616 BX_ERROR(("unsupported shutdown status: 0x%02x!", value
));
618 BX_CMOS_THIS s
.reg
[REG_SHUTDOWN_STATUS
] = value
;
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
;
630 void bx_cmos_c::checksum_cmos(void)
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)
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)
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()
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
])
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
])
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
])
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
;
731 time_calendar
= localtime(& BX_CMOS_THIS s
.timeval
);
734 BX_CMOS_THIS s
.reg
[REG_SEC
] = bin_to_bcd(time_calendar
->tm_sec
,
735 BX_CMOS_THIS s
.rtc_mode_binary
);
738 BX_CMOS_THIS s
.reg
[REG_MIN
] = bin_to_bcd(time_calendar
->tm_min
,
739 BX_CMOS_THIS s
.rtc_mode_binary
);
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
;
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
);
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
);
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
);
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
;
791 time_calendar
.tm_sec
= bcd_to_bin(BX_CMOS_THIS s
.reg
[REG_SEC
],
792 BX_CMOS_THIS s
.rtc_mode_binary
);
795 time_calendar
.tm_min
= bcd_to_bin(BX_CMOS_THIS s
.reg
[REG_MIN
],
796 BX_CMOS_THIS s
.rtc_mode_binary
);
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)) {
805 } else if ((val_bin
== 12) & (pm_flag
== 0)) {
808 time_calendar
.tm_hour
= val_bin
;
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
);
819 time_calendar
.tm_mon
= bcd_to_bin(BX_CMOS_THIS s
.reg
[REG_MONTH
],
820 BX_CMOS_THIS s
.rtc_mode_binary
) - 1;
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
);