1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cmos.cc 14175 2021-03-07 16:01:39Z vruppert $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002-2021 The Bochs Project
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 /////////////////////////////////////////////////////////////////////////
21
22 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
23 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
24 // is used to know when we are exporting symbols and when we are importing.
25 #define BX_PLUGGABLE
26
27 #include "iodev.h"
28 #include "cmos.h"
29 #include "virt_timer.h"
30
31 #define LOG_THIS theCmosDevice->
32
33 bx_cmos_c *theCmosDevice = NULL;
34
35 // CMOS register definitions from Ralf Brown's interrupt list v6.1, in a file
36 // called cmos.lst. In cases where there are multiple uses for a given
37 // register in the interrupt list, I only listed the purpose that Bochs
38 // actually uses it for, but I wrote "alternatives" next to it.
39 #define REG_SEC 0x00
40 #define REG_SEC_ALARM 0x01
41 #define REG_MIN 0x02
42 #define REG_MIN_ALARM 0x03
43 #define REG_HOUR 0x04
44 #define REG_HOUR_ALARM 0x05
45 #define REG_WEEK_DAY 0x06
46 #define REG_MONTH_DAY 0x07
47 #define REG_MONTH 0x08
48 #define REG_YEAR 0x09
49 #define REG_STAT_A 0x0a
50 #define REG_STAT_B 0x0b
51 #define REG_STAT_C 0x0c
52 #define REG_STAT_D 0x0d
53 #define REG_DIAGNOSTIC_STATUS 0x0e /* alternatives */
54 #define REG_SHUTDOWN_STATUS 0x0f
55 #define REG_EQUIPMENT_BYTE 0x14
56 #define REG_CSUM_HIGH 0x2e
57 #define REG_CSUM_LOW 0x2f
58 #define REG_IBM_CENTURY_BYTE 0x32 /* alternatives */
59 #define REG_IBM_PS2_CENTURY_BYTE 0x37 /* alternatives */
60
61 // Bochs CMOS map
62 //
63 // Idx Len Description
64 // 0x10 1 floppy drive types
65 // 0x11 1 configuration bits
66 // 0x12 1 harddisk types
67 // 0x13 1 advanced configuration bits
68 // 0x15 2 base memory in 1k
69 // 0x17 2 memory size above 1M in 1k
70 // 0x19 2 extended harddisk types
71 // 0x1b 9 harddisk configuration (hd0)
72 // 0x24 9 harddisk configuration (hd1)
73 // 0x2d 1 boot sequence (fd/hd)
74 // 0x30 2 memory size above 1M in 1k
75 // 0x34 2 memory size above 16M in 64k
76 // 0x38 1 eltorito boot sequence (#3) + bootsig check
77 // 0x39 2 ata translation policy (ata0...ata3)
78 // 0x3d 1 eltorito boot sequence (#1 + #2)
79 //
80 // Qemu CMOS map
81 //
82 // Idx Len Description
83 // 0x5b 3 extra memory above 4GB
84 // 0x5f 1 number of processors
85
86
bcd_to_bin(Bit8u value,bool is_binary)87 Bit8u bcd_to_bin(Bit8u value, bool is_binary)
88 {
89 if (is_binary)
90 return value;
91 else
92 return ((value >> 4) * 10) + (value & 0x0f);
93 }
94
bin_to_bcd(Bit8u value,bool is_binary)95 Bit8u bin_to_bcd(Bit8u value, bool is_binary)
96 {
97 if (is_binary)
98 return value;
99 else
100 return ((value / 10) << 4) | (value % 10);
101 }
102
PLUGIN_ENTRY_FOR_MODULE(cmos)103 PLUGIN_ENTRY_FOR_MODULE(cmos)
104 {
105 if (mode == PLUGIN_INIT) {
106 theCmosDevice = new bx_cmos_c();
107 bx_devices.pluginCmosDevice = theCmosDevice;
108 BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theCmosDevice, BX_PLUGIN_CMOS);
109 } else if (mode == PLUGIN_FINI) {
110 delete theCmosDevice;
111 } else if (mode == PLUGIN_PROBE) {
112 return (int)PLUGTYPE_CORE;
113 }
114 return 0; // Success
115 }
116
bx_cmos_c(void)117 bx_cmos_c::bx_cmos_c(void)
118 {
119 put("CMOS");
120 memset(&s, 0, sizeof(s));
121 s.periodic_timer_index = BX_NULL_TIMER_HANDLE;
122 s.one_second_timer_index = BX_NULL_TIMER_HANDLE;
123 s.uip_timer_index = BX_NULL_TIMER_HANDLE;
124 }
125
~bx_cmos_c(void)126 bx_cmos_c::~bx_cmos_c(void)
127 {
128 save_image();
129 char *tmptime;
130 if ((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) != NULL) {
131 tmptime[strlen(tmptime)-1]='\0';
132 BX_INFO(("Last time is %u (%s)", (unsigned) get_timeval(), tmptime));
133 free(tmptime);
134 }
135 SIM->get_bochs_root()->remove("cmos");
136 BX_DEBUG(("Exit"));
137 }
138
init(void)139 void bx_cmos_c::init(void)
140 {
141 BX_DEBUG(("Init $Id: cmos.cc 14175 2021-03-07 16:01:39Z vruppert $"));
142 // CMOS RAM & RTC
143
144 DEV_register_ioread_handler(this, read_handler, 0x0070, "CMOS RAM", 1);
145 DEV_register_ioread_handler(this, read_handler, 0x0071, "CMOS RAM", 1);
146 DEV_register_iowrite_handler(this, write_handler, 0x0070, "CMOS RAM", 1);
147 DEV_register_iowrite_handler(this, write_handler, 0x0071, "CMOS RAM", 1);
148 DEV_register_irq(8, "CMOS RTC");
149
150 int clock_sync = SIM->get_param_enum(BXPN_CLOCK_SYNC)->get();
151 BX_CMOS_THIS s.rtc_sync = ((clock_sync == BX_CLOCK_SYNC_REALTIME) ||
152 (clock_sync == BX_CLOCK_SYNC_BOTH)) &&
153 SIM->get_param_bool(BXPN_CLOCK_RTC_SYNC)->get();
154
155 if (BX_CMOS_THIS s.periodic_timer_index == BX_NULL_TIMER_HANDLE) {
156 BX_CMOS_THIS s.periodic_timer_index =
157 DEV_register_timer(this, periodic_timer_handler,
158 1000000, 1,0, "cmos"); // continuous, not-active
159 }
160 if (BX_CMOS_THIS s.one_second_timer_index == BX_NULL_TIMER_HANDLE) {
161 BX_CMOS_THIS s.one_second_timer_index =
162 bx_virt_timer.register_timer(this, one_second_timer_handler,
163 1000000, 1, 0, BX_CMOS_THIS s.rtc_sync, "cmos"); // continuous, not-active
164 if (BX_CMOS_THIS s.rtc_sync) {
165 BX_INFO(("CMOS RTC using realtime synchronisation method"));
166 }
167 }
168 if (BX_CMOS_THIS s.uip_timer_index == BX_NULL_TIMER_HANDLE) {
169 BX_CMOS_THIS s.uip_timer_index =
170 DEV_register_timer(this, uip_timer_handler,
171 244, 0, 0, "cmos"); // one-shot, not-active
172 }
173
174 if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_LOCAL) {
175 BX_INFO(("Using local time for initial clock"));
176 BX_CMOS_THIS s.timeval = time(NULL);
177 } else if (SIM->get_param_num(BXPN_CLOCK_TIME0)->get() == BX_CLOCK_TIME0_UTC) {
178 bool utc_ok = 0;
179
180 BX_INFO(("Using utc time for initial clock"));
181
182 BX_CMOS_THIS s.timeval = time(NULL);
183
184 #if BX_HAVE_GMTIME
185 #if BX_HAVE_MKTIME
186 struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
187 utc_holder->tm_isdst = -1;
188 utc_ok = 1;
189 BX_CMOS_THIS s.timeval = mktime(utc_holder);
190 #elif BX_HAVE_TIMELOCAL
191 struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
192 utc_holder->tm_isdst = 0; // XXX Is this correct???
193 utc_ok = 1;
194 BX_CMOS_THIS s.timeval = timelocal(utc_holder);
195 #endif //BX_HAVE_MKTIME
196 #endif //BX_HAVE_GMTIME
197
198 if (!utc_ok) {
199 BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)"));
200 }
201 } else {
202 BX_INFO(("Using specified time for initial clock"));
203 BX_CMOS_THIS s.timeval = SIM->get_param_num(BXPN_CLOCK_TIME0)->get();
204 }
205
206 // load CMOS from image file if requested.
207 BX_CMOS_THIS s.use_image = SIM->get_param_bool(BXPN_CMOSIMAGE_ENABLED)->get();
208 if (BX_CMOS_THIS s.use_image) {
209 int fd, ret;
210 struct stat stat_buf;
211
212 fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_RDONLY
213 #ifdef O_BINARY
214 | O_BINARY
215 #endif
216 );
217 if (fd < 0) {
218 BX_PANIC(("trying to open cmos image file '%s'",
219 SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
220 }
221 ret = fstat(fd, &stat_buf);
222 if (ret) {
223 BX_PANIC(("CMOS: could not fstat() image file."));
224 }
225 if ((stat_buf.st_size != 64) && (stat_buf.st_size != 128) &&
226 (stat_buf.st_size != 256)) {
227 BX_PANIC(("CMOS: image file size must be 64, 128 or 256"));
228 } else {
229 BX_CMOS_THIS s.max_reg = (Bit8u)(stat_buf.st_size - 1);
230 if (BX_CMOS_THIS s.max_reg == 255) {
231 DEV_register_ioread_handler(this, read_handler, 0x0072, "Ext CMOS RAM", 1);
232 DEV_register_ioread_handler(this, read_handler, 0x0073, "Ext CMOS RAM", 1);
233 DEV_register_iowrite_handler(this, write_handler, 0x0072, "Ext CMOS RAM", 1);
234 DEV_register_iowrite_handler(this, write_handler, 0x0073, "Ext CMOS RAM", 1);
235 }
236 }
237
238 ret = ::read(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, (unsigned)stat_buf.st_size);
239 if (ret != stat_buf.st_size) {
240 BX_PANIC(("CMOS: error reading cmos file."));
241 }
242 close(fd);
243 BX_INFO(("successfully read from image file '%s'.",
244 SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr()));
245 BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
246 BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
247 if (SIM->get_param_bool(BXPN_CMOSIMAGE_RTC_INIT)->get()) {
248 update_timeval();
249 } else {
250 update_clock();
251 }
252 } else {
253 BX_CMOS_THIS s.max_reg = 128;
254 // CMOS values generated
255 BX_CMOS_THIS s.reg[REG_STAT_A] = 0x26;
256 BX_CMOS_THIS s.reg[REG_STAT_B] = 0x02;
257 BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
258 BX_CMOS_THIS s.reg[REG_STAT_D] = 0x80;
259 #if BX_SUPPORT_FPU == 1
260 BX_CMOS_THIS s.reg[REG_EQUIPMENT_BYTE] |= 0x02;
261 #endif
262 BX_CMOS_THIS s.rtc_mode_12hour = 0;
263 BX_CMOS_THIS s.rtc_mode_binary = 0;
264 update_clock();
265 }
266
267 char *tmptime;
268 while((tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) == NULL) {
269 BX_PANIC(("Out of memory."));
270 }
271 tmptime[strlen(tmptime)-1]='\0';
272 BX_INFO(("Setting initial clock to: %s (time0=%u)", tmptime, (Bit32u)BX_CMOS_THIS s.timeval));
273 free(tmptime);
274
275 BX_CMOS_THIS s.timeval_change = 0;
276
277 #if BX_DEBUGGER
278 // register device for the 'info device' command (calls debug_dump())
279 bx_dbg_register_debug_info("cmos", this);
280 #endif
281 }
282
reset(unsigned type)283 void bx_cmos_c::reset(unsigned type)
284 {
285 BX_CMOS_THIS s.cmos_mem_address = 0;
286 BX_CMOS_THIS s.irq_enabled = 1;
287
288 // RESET affects the following registers:
289 // CRA: no effects
290 // CRB: bits 4,5,6 forced to 0
291 // CRC: bits 4,5,6,7 forced to 0
292 // CRD: no effects
293 BX_CMOS_THIS s.reg[REG_STAT_B] &= 0x8f;
294 BX_CMOS_THIS s.reg[REG_STAT_C] = 0;
295
296 // One second timer for updating clock & alarm functions
297 bx_virt_timer.activate_timer(BX_CMOS_THIS s.one_second_timer_index,
298 1000000, 1);
299
300 // handle periodic interrupt rate select
301 BX_CMOS_THIS CRA_change();
302 }
303
save_image(void)304 void bx_cmos_c::save_image(void)
305 {
306 int fd, ret;
307
308 // save CMOS to image file if requested.
309 if (BX_CMOS_THIS s.use_image) {
310 fd = open(SIM->get_param_string(BXPN_CMOSIMAGE_PATH)->getptr(), O_WRONLY
311 #ifdef O_BINARY
312 | O_BINARY
313 #endif
314 );
315 ret = ::write(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, BX_CMOS_THIS s.max_reg + 1);
316 if (ret != (BX_CMOS_THIS s.max_reg + 1)) {
317 BX_PANIC(("CMOS: error writing cmos file."));
318 }
319 close(fd);
320 }
321 }
322
register_state(void)323 void bx_cmos_c::register_state(void)
324 {
325 bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "cmos", "CMOS State");
326 BXRS_HEX_PARAM_FIELD(list, mem_address, BX_CMOS_THIS s.cmos_mem_address);
327 BXRS_PARAM_BOOL(list, irq_enabled, BX_CMOS_THIS s.irq_enabled);
328 new bx_shadow_data_c(list, "ram", BX_CMOS_THIS s.reg, 128, 1);
329 }
330
after_restore_state(void)331 void bx_cmos_c::after_restore_state(void)
332 {
333 BX_CMOS_THIS s.rtc_mode_12hour = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x02) == 0);
334 BX_CMOS_THIS s.rtc_mode_binary = ((BX_CMOS_THIS s.reg[REG_STAT_B] & 0x04) != 0);
335 BX_CMOS_THIS update_timeval();
336 BX_CMOS_THIS CRA_change();
337 }
338
CRA_change(void)339 void bx_cmos_c::CRA_change(void)
340 {
341 Bit8u nibble, dcc;
342
343 // Periodic Interrupt timer
344 nibble = BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f;
345 dcc = (BX_CMOS_THIS s.reg[REG_STAT_A] >> 4) & 0x07;
346 if ((nibble == 0) || ((dcc & 0x06) == 0)) {
347 // No Periodic Interrupt Rate when 0, deactivate timer
348 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
349 BX_CMOS_THIS s.periodic_interval_usec = (Bit32u) -1; // max value
350 } else {
351 // values 0001b and 0010b are the same as 1000b and 1001b
352 if (nibble <= 2)
353 nibble += 7;
354 BX_CMOS_THIS s.periodic_interval_usec = (unsigned) (1000000.0L /
355 (32768.0L / (1 << (nibble - 1))));
356
357 // if Periodic Interrupt Enable bit set, activate timer
358 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40)
359 bx_pc_system.activate_timer(BX_CMOS_THIS s.periodic_timer_index,
360 BX_CMOS_THIS s.periodic_interval_usec, 1);
361 else
362 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
363 }
364 }
365
366
367 // static IO port read callback handler
368 // redirects to non-static class handler to avoid virtual functions
369
read_handler(void * this_ptr,Bit32u address,unsigned io_len)370 Bit32u bx_cmos_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
371 {
372 #if !BX_USE_CMOS_SMF
373 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
374 return class_ptr->read(address, io_len);
375 }
376
read(Bit32u address,unsigned io_len)377 Bit32u bx_cmos_c::read(Bit32u address, unsigned io_len)
378 {
379 #else
380 UNUSED(this_ptr);
381 #endif
382 Bit8u ret8;
383
384 BX_DEBUG(("CMOS read of CMOS register 0x%02x", (unsigned) BX_CMOS_THIS s.cmos_mem_address));
385
386 switch (address) {
387 case 0x0070:
388 case 0x0072:
389 // this register is write-only on most machines
390 BX_DEBUG(("read of index port 0x%02x returning 0xff", address));
391 return 0xff;
392
393 case 0x0071:
394 ret8 = BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address];
395 // all bits of Register C are cleared after a read occurs.
396 if (BX_CMOS_THIS s.cmos_mem_address == REG_STAT_C) {
397 BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
398 if (BX_CMOS_THIS s.irq_enabled) {
399 DEV_pic_lower_irq(8);
400 }
401 }
402 return ret8;
403
404 case 0x0073:
405 return BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_ext_mem_addr];
406
407 default:
408 BX_PANIC(("unsupported cmos read, address=0x%04x!", (unsigned) address));
409 return 0;
410 }
411 }
412
413
414 // static IO port write callback handler
415 // redirects to non-static class handler to avoid virtual functions
write_handler(void * this_ptr,Bit32u address,Bit32u value,unsigned io_len)416 void bx_cmos_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
417 {
418 #if !BX_USE_CMOS_SMF
419 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
420 class_ptr->write(address, value, io_len);
421 }
422
write(Bit32u address,Bit32u value,unsigned io_len)423 void bx_cmos_c::write(Bit32u address, Bit32u value, unsigned io_len)
424 {
425 #else
426 UNUSED(this_ptr);
427 #endif // !BX_USE_CMOS_SMF
428
429 BX_DEBUG(("CMOS write to address: 0x%04x = 0x%02x", address, value));
430
431 switch (address) {
432 case 0x0070:
433 BX_CMOS_THIS s.cmos_mem_address = value & 0x7F;
434 break;
435
436 case 0x0072:
437 BX_CMOS_THIS s.cmos_ext_mem_addr = value | 0x80;
438 break;
439
440 case 0x0071:
441 switch (BX_CMOS_THIS s.cmos_mem_address) {
442 case REG_SEC_ALARM: // seconds alarm
443 case REG_MIN_ALARM: // minutes alarm
444 case REG_HOUR_ALARM: // hours alarm
445 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
446 BX_DEBUG(("alarm time changed to %02x:%02x:%02x", BX_CMOS_THIS s.reg[REG_HOUR_ALARM],
447 BX_CMOS_THIS s.reg[REG_MIN_ALARM], BX_CMOS_THIS s.reg[REG_SEC_ALARM]));
448 break;
449
450 case REG_SEC: // seconds
451 case REG_MIN: // minutes
452 case REG_HOUR: // hours
453 case REG_WEEK_DAY: // day of the week
454 case REG_MONTH_DAY: // day of the month
455 case REG_MONTH: // month
456 case REG_YEAR: // year
457 case REG_IBM_CENTURY_BYTE: // century
458 case REG_IBM_PS2_CENTURY_BYTE: // century (PS/2)
459 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
460 if (BX_CMOS_THIS s.cmos_mem_address == REG_IBM_PS2_CENTURY_BYTE) {
461 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = value;
462 }
463 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) {
464 BX_CMOS_THIS s.timeval_change = 1;
465 } else {
466 update_timeval();
467 }
468 break;
469
470 case REG_STAT_A: // Control Register A
471 // bit 7: Update in Progress (read-only)
472 // 1 = signifies time registers will be updated within 244us
473 // 0 = time registers will not occur before 244us
474 // note: this bit reads 0 when CRB bit 7 is 1
475 // bit 6..4: Divider Chain Control
476 // 000 oscillator disabled
477 // 001 oscillator disabled
478 // 010 Normal operation
479 // 011 TEST
480 // 100 TEST
481 // 101 TEST
482 // 110 Divider Chain RESET
483 // 111 Divider Chain RESET
484 // bit 3..0: Periodic Interrupt Rate Select
485 // 0000 None
486 // 0001 3.90625 ms
487 // 0010 7.8125 ms
488 // 0011 122.070 us
489 // 0100 244.141 us
490 // 0101 488.281 us
491 // 0110 976.562 us
492 // 0111 1.953125 ms
493 // 1000 3.90625 ms
494 // 1001 7.8125 ms
495 // 1010 15.625 ms
496 // 1011 31.25 ms
497 // 1100 62.5 ms
498 // 1101 125 ms
499 // 1110 250 ms
500 // 1111 500 ms
501
502 unsigned dcc;
503 dcc = (value >> 4) & 0x07;
504 if ((dcc & 0x06) == 0x06) {
505 BX_INFO(("CRA: divider chain RESET"));
506 } else if (dcc > 0x02) {
507 BX_PANIC(("CRA: divider chain control 0x%02x", dcc));
508 }
509 BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x80;
510 BX_CMOS_THIS s.reg[REG_STAT_A] |= (value & 0x7f);
511 BX_CMOS_THIS CRA_change();
512 break;
513
514 case REG_STAT_B: // Control Register B
515 // bit 0: Daylight Savings Enable
516 // 1 = enable daylight savings
517 // 0 = disable daylight savings
518 // bit 1: 24/12 hour mode
519 // 1 = 24 hour format
520 // 0 = 12 hour format
521 // bit 2: Data Mode
522 // 1 = binary format
523 // 0 = BCD format
524 // bit 3: "square wave enable"
525 // Not supported and always read as 0
526 // bit 4: Update Ended Interrupt Enable
527 // 1 = enable generation of update ended interrupt
528 // 0 = disable
529 // bit 5: Alarm Interrupt Enable
530 // 1 = enable generation of alarm interrupt
531 // 0 = disable
532 // bit 6: Periodic Interrupt Enable
533 // 1 = enable generation of periodic interrupt
534 // 0 = disable
535 // bit 7: Set mode
536 // 1 = user copy of time is "frozen" allowing time registers
537 // to be accessed without regard for an occurance of an update
538 // 0 = time updates occur normally
539
540 if (value & 0x01)
541 BX_ERROR(("write status reg B, daylight savings unsupported"));
542
543 value &= 0xf7; // bit3 always 0
544 // Note: setting bit 7 clears bit 4
545 if (value & 0x80)
546 value &= 0xef;
547
548 unsigned prev_CRB;
549 prev_CRB = BX_CMOS_THIS s.reg[REG_STAT_B];
550 BX_CMOS_THIS s.reg[REG_STAT_B] = value;
551 if ((prev_CRB & 0x02) != (value & 0x02)) {
552 BX_CMOS_THIS s.rtc_mode_12hour = ((value & 0x02) == 0);
553 update_clock();
554 }
555 if ((prev_CRB & 0x04) != (value & 0x04)) {
556 BX_CMOS_THIS s.rtc_mode_binary = ((value & 0x04) != 0);
557 update_clock();
558 }
559 if ((prev_CRB & 0x40) != (value & 0x40)) {
560 // Periodic Interrupt Enabled changed
561 if (prev_CRB & 0x40) {
562 // transition from 1 to 0, deactivate timer
563 bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
564 } else {
565 // transition from 0 to 1
566 // if rate select is not 0, activate timer
567 if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f) != 0) {
568 bx_pc_system.activate_timer(
569 BX_CMOS_THIS s.periodic_timer_index,
570 BX_CMOS_THIS s.periodic_interval_usec, 1);
571 }
572 }
573 }
574 if ((prev_CRB >= 0x80) && (value < 0x80) && BX_CMOS_THIS s.timeval_change) {
575 update_timeval();
576 BX_CMOS_THIS s.timeval_change = 0;
577 }
578 break;
579
580 case REG_STAT_C: // Control Register C
581 case REG_STAT_D: // Control Register D
582 BX_ERROR(("write to control register 0x%02x ignored (read-only)",
583 BX_CMOS_THIS s.cmos_mem_address));
584 break;
585
586 case REG_DIAGNOSTIC_STATUS:
587 BX_DEBUG(("write register 0x0e: 0x%02x", value));
588 BX_CMOS_THIS s.reg[REG_DIAGNOSTIC_STATUS] = value;
589 break;
590
591 case REG_SHUTDOWN_STATUS:
592 switch (value) {
593 case 0x00: /* proceed with normal POST (soft reset) */
594 BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST"));
595 break;
596 case 0x01: /* shutdown after memory size check */
597 BX_DEBUG(("Reg 0Fh(01): request to change shutdown action"
598 " to shutdown after memory size check"));
599 break;
600 case 0x02: /* shutdown after successful memory test */
601 BX_DEBUG(("Reg 0Fh(02): request to change shutdown action"
602 " to shutdown after successful memory test"));
603 break;
604 case 0x03: /* shutdown after failed memory test */
605 BX_DEBUG(("Reg 0Fh(03): request to change shutdown action"
606 " to shutdown after successful memory test"));
607 break;
608 case 0x04: /* jump to disk bootstrap routine */
609 BX_DEBUG(("Reg 0Fh(04): request to change shutdown action "
610 "to jump to disk bootstrap routine."));
611 break;
612 case 0x05: /* flush keyboard (issue EOI) and jump via 40h:0067h */
613 BX_DEBUG(("Reg 0Fh(05): request to change shutdown action "
614 "to flush keyboard (issue EOI) and jump via 40h:0067h."));
615 break;
616 case 0x06:
617 BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !"));
618 break;
619 case 0x07: /* reset (after failed test in virtual mode) */
620 BX_DEBUG(("Reg 0Fh(07): request to change shutdown action "
621 "to reset (after failed test in virtual mode)."));
622 break;
623 case 0x08: /* used by POST during protected-mode RAM test (return to POST) */
624 BX_DEBUG(("Reg 0Fh(08): request to change shutdown action "
625 "to return to POST (used by POST during protected-mode RAM test)."));
626 break;
627 case 0x09: /* return to BIOS extended memory block move
628 (interrupt 15h, func 87h was in progress) */
629 BX_DEBUG(("Reg 0Fh(09): request to change shutdown action "
630 "to return to BIOS extended memory block move."));
631 break;
632 case 0x0a: /* jump to DWORD pointer at 40:67 */
633 BX_DEBUG(("Reg 0Fh(0a): request to change shutdown action"
634 " to jump to DWORD at 40:67"));
635 break;
636 case 0x0b: /* iret to DWORD pointer at 40:67 */
637 BX_DEBUG(("Reg 0Fh(0b): request to change shutdown action"
638 " to iret to DWORD at 40:67"));
639 break;
640 case 0x0c: /* retf to DWORD pointer at 40:67 */
641 BX_DEBUG(("Reg 0Fh(0c): request to change shutdown action"
642 " to retf to DWORD at 40:67"));
643 break;
644 default:
645 if (!BX_CMOS_THIS s.use_image) {
646 BX_ERROR(("unsupported shutdown status: 0x%02x!", value));
647 } else {
648 BX_DEBUG(("shutdown status register set to 0x%02x", value));
649 }
650 }
651 BX_CMOS_THIS s.reg[REG_SHUTDOWN_STATUS] = value;
652 break;
653
654 default:
655 BX_DEBUG(("write reg 0x%02x: value = 0x%02x",
656 BX_CMOS_THIS s.cmos_mem_address, value));
657 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
658 }
659 break;
660
661 case 0x0073:
662 BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_ext_mem_addr] = value;
663 break;
664 }
665 }
666
checksum_cmos(void)667 void bx_cmos_c::checksum_cmos(void)
668 {
669 Bit16u sum = 0;
670 for (unsigned i=0x10; i<=0x2d; i++)
671 sum += BX_CMOS_THIS s.reg[i];
672 BX_CMOS_THIS s.reg[REG_CSUM_HIGH] = (sum >> 8) & 0xff; /* checksum high */
673 BX_CMOS_THIS s.reg[REG_CSUM_LOW] = (sum & 0xff); /* checksum low */
674 }
675
periodic_timer_handler(void * this_ptr)676 void bx_cmos_c::periodic_timer_handler(void *this_ptr)
677 {
678 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
679 class_ptr->periodic_timer();
680 }
681
periodic_timer()682 void bx_cmos_c::periodic_timer()
683 {
684 // if periodic interrupts are enabled, trip IRQ 8, and
685 // update status register C
686 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40) {
687 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xc0; // Interrupt Request, Periodic Int
688 if (BX_CMOS_THIS s.irq_enabled) {
689 DEV_pic_raise_irq(8);
690 }
691 }
692 }
693
one_second_timer_handler(void * this_ptr)694 void bx_cmos_c::one_second_timer_handler(void *this_ptr)
695 {
696 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
697 class_ptr->one_second_timer();
698 }
699
one_second_timer()700 void bx_cmos_c::one_second_timer()
701 {
702 // divider chain reset - RTC stopped
703 if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x60) == 0x60)
704 return;
705
706 // update internal time/date buffer
707 BX_CMOS_THIS s.timeval++;
708
709 // Dont update CMOS user copy of time/date if CRB bit7 is 1
710 // Nothing else do to
711 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80)
712 return;
713
714 BX_CMOS_THIS s.reg[REG_STAT_A] |= 0x80; // set UIP bit
715
716 // UIP timer for updating clock & alarm functions
717 bx_pc_system.activate_timer(BX_CMOS_THIS s.uip_timer_index, 244, 0);
718 }
719
uip_timer_handler(void * this_ptr)720 void bx_cmos_c::uip_timer_handler(void *this_ptr)
721 {
722 bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
723 class_ptr->uip_timer();
724 }
725
uip_timer()726 void bx_cmos_c::uip_timer()
727 {
728 update_clock();
729
730 // if update interrupts are enabled, trip IRQ 8, and
731 // update status register C
732 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x10) {
733 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0x90; // Interrupt Request, Update Ended
734 if (BX_CMOS_THIS s.irq_enabled) {
735 DEV_pic_raise_irq(8);
736 }
737 }
738
739 // compare CMOS user copy of time/date to alarm time/date here
740 if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x20) {
741 // Alarm interrupts enabled
742 bool alarm_match = 1;
743 if ((BX_CMOS_THIS s.reg[REG_SEC_ALARM] & 0xc0) != 0xc0) {
744 // seconds alarm not in dont care mode
745 if (BX_CMOS_THIS s.reg[REG_SEC] != BX_CMOS_THIS s.reg[REG_SEC_ALARM])
746 alarm_match = 0;
747 }
748 if ((BX_CMOS_THIS s.reg[REG_MIN_ALARM] & 0xc0) != 0xc0) {
749 // minutes alarm not in dont care mode
750 if (BX_CMOS_THIS s.reg[REG_MIN] != BX_CMOS_THIS s.reg[REG_MIN_ALARM])
751 alarm_match = 0;
752 }
753 if ((BX_CMOS_THIS s.reg[REG_HOUR_ALARM] & 0xc0) != 0xc0) {
754 // hours alarm not in dont care mode
755 if (BX_CMOS_THIS s.reg[REG_HOUR] != BX_CMOS_THIS s.reg[REG_HOUR_ALARM])
756 alarm_match = 0;
757 }
758 if (alarm_match) {
759 BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xa0; // Interrupt Request, Alarm Int
760 if (BX_CMOS_THIS s.irq_enabled) {
761 DEV_pic_raise_irq(8);
762 }
763 }
764 }
765 BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x7f; // clear UIP bit
766 }
767
update_clock()768 void bx_cmos_c::update_clock()
769 {
770 struct tm *time_calendar;
771 unsigned year, month, day, century;
772 Bit8u val_bcd, hour;
773
774 time_calendar = localtime(& BX_CMOS_THIS s.timeval);
775
776 // update seconds
777 BX_CMOS_THIS s.reg[REG_SEC] = bin_to_bcd(time_calendar->tm_sec,
778 BX_CMOS_THIS s.rtc_mode_binary);
779
780 // update minutes
781 BX_CMOS_THIS s.reg[REG_MIN] = bin_to_bcd(time_calendar->tm_min,
782 BX_CMOS_THIS s.rtc_mode_binary);
783
784 // update hours
785 if (BX_CMOS_THIS s.rtc_mode_12hour) {
786 hour = time_calendar->tm_hour;
787 val_bcd = (hour > 11) ? 0x80 : 0x00;
788 if (hour > 11) hour -= 12;
789 if (hour == 0) hour = 12;
790 val_bcd |= bin_to_bcd(hour, BX_CMOS_THIS s.rtc_mode_binary);
791 BX_CMOS_THIS s.reg[REG_HOUR] = val_bcd;
792 } else {
793 BX_CMOS_THIS s.reg[REG_HOUR] = bin_to_bcd(time_calendar->tm_hour,
794 BX_CMOS_THIS s.rtc_mode_binary);
795 }
796
797 // update day of the week
798 day = time_calendar->tm_wday + 1; // 0..6 to 1..7
799 BX_CMOS_THIS s.reg[REG_WEEK_DAY] = bin_to_bcd(day,
800 BX_CMOS_THIS s.rtc_mode_binary);
801
802 // update day of the month
803 day = time_calendar->tm_mday;
804 BX_CMOS_THIS s.reg[REG_MONTH_DAY] = bin_to_bcd(day,
805 BX_CMOS_THIS s.rtc_mode_binary);
806
807 // update month
808 month = time_calendar->tm_mon + 1;
809 BX_CMOS_THIS s.reg[REG_MONTH] = bin_to_bcd(month,
810 BX_CMOS_THIS s.rtc_mode_binary);
811
812 // update year
813 year = time_calendar->tm_year % 100;
814 BX_CMOS_THIS s.reg[REG_YEAR] = bin_to_bcd(year,
815 BX_CMOS_THIS s.rtc_mode_binary);
816
817 // update century
818 century = (time_calendar->tm_year / 100) + 19;
819 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = bin_to_bcd(century,
820 BX_CMOS_THIS s.rtc_mode_binary);
821
822 // Raul Hudea pointed out that some bioses also use reg 0x37 for the
823 // century byte. Tony Heller says this is critical in getting WinXP to run.
824 BX_CMOS_THIS s.reg[REG_IBM_PS2_CENTURY_BYTE] =
825 BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE];
826 }
827
update_timeval()828 void bx_cmos_c::update_timeval()
829 {
830 struct tm time_calendar;
831 Bit8u val_bin, pm_flag;
832
833 // update seconds
834 time_calendar.tm_sec = bcd_to_bin(BX_CMOS_THIS s.reg[REG_SEC],
835 BX_CMOS_THIS s.rtc_mode_binary);
836
837 // update minutes
838 time_calendar.tm_min = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MIN],
839 BX_CMOS_THIS s.rtc_mode_binary);
840
841 // update hours
842 if (BX_CMOS_THIS s.rtc_mode_12hour) {
843 pm_flag = BX_CMOS_THIS s.reg[REG_HOUR] & 0x80;
844 val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR] & 0x70,
845 BX_CMOS_THIS s.rtc_mode_binary);
846 if ((val_bin < 12) & (pm_flag > 0)) {
847 val_bin += 12;
848 } else if ((val_bin == 12) & (pm_flag == 0)) {
849 val_bin = 0;
850 }
851 time_calendar.tm_hour = val_bin;
852 } else {
853 time_calendar.tm_hour = bcd_to_bin(BX_CMOS_THIS s.reg[REG_HOUR],
854 BX_CMOS_THIS s.rtc_mode_binary);
855 }
856
857 // update day of the month
858 time_calendar.tm_mday = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH_DAY],
859 BX_CMOS_THIS s.rtc_mode_binary);
860
861 // update month
862 time_calendar.tm_mon = bcd_to_bin(BX_CMOS_THIS s.reg[REG_MONTH],
863 BX_CMOS_THIS s.rtc_mode_binary) - 1;
864
865 // update year
866 val_bin = bcd_to_bin(BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE],
867 BX_CMOS_THIS s.rtc_mode_binary);
868 val_bin = (val_bin - 19) * 100;
869 val_bin += bcd_to_bin(BX_CMOS_THIS s.reg[REG_YEAR],
870 BX_CMOS_THIS s.rtc_mode_binary);
871 time_calendar.tm_year = val_bin;
872
873 BX_CMOS_THIS s.timeval = mktime(& time_calendar);
874 }
875
876 #if BX_DEBUGGER
debug_dump(int argc,char ** argv)877 void bx_cmos_c::debug_dump(int argc, char **argv)
878 {
879 int i, j, r;
880
881 dbg_printf("CMOS RTC\n\n");
882 dbg_printf("Index register: 0x%02x\n\n", BX_CMOS_THIS s.cmos_mem_address);
883 r = 0;
884 for (i=0; i<8; i++) {
885 dbg_printf("%04x ", r);
886 for (j=0; j<16; j++) {
887 dbg_printf(" %02x", BX_CMOS_THIS s.reg[r++]);
888 }
889 dbg_printf("\n");
890 }
891 if (argc > 0) {
892 dbg_printf("\nAdditional options not supported\n");
893 }
894 }
895 #endif
896