2015-02-25 18:23:10 +01:00
|
|
|
"use strict";
|
|
|
|
|
2015-03-09 01:20:56 +01:00
|
|
|
/** @const */ var CMOS_RTC_SECONDS = 0x00;
|
|
|
|
/** @const */ var CMOS_RTC_SECONDS_ALARM = 0x01;
|
|
|
|
/** @const */ var CMOS_RTC_MINUTES = 0x02;
|
|
|
|
/** @const */ var CMOS_RTC_MINUTES_ALARM = 0x03;
|
|
|
|
/** @const */ var CMOS_RTC_HOURS = 0x04;
|
|
|
|
/** @const */ var CMOS_RTC_HOURS_ALARM = 0x05;
|
|
|
|
/** @const */ var CMOS_RTC_DAY_WEEK = 0x06;
|
|
|
|
/** @const */ var CMOS_RTC_DAY_MONTH = 0x07;
|
|
|
|
/** @const */ var CMOS_RTC_MONTH = 0x08;
|
|
|
|
/** @const */ var CMOS_RTC_YEAR = 0x09;
|
|
|
|
/** @const */ var CMOS_STATUS_A = 0x0a;
|
|
|
|
/** @const */ var CMOS_STATUS_B = 0x0b;
|
|
|
|
/** @const */ var CMOS_STATUS_C = 0x0c;
|
|
|
|
/** @const */ var CMOS_STATUS_D = 0x0d;
|
|
|
|
/** @const */ var CMOS_RESET_CODE = 0x0f;
|
|
|
|
|
|
|
|
/** @const */ var CMOS_FLOPPY_DRIVE_TYPE = 0x10;
|
|
|
|
/** @const */ var CMOS_DISK_DATA = 0x12;
|
|
|
|
/** @const */ var CMOS_EQUIPMENT_INFO = 0x14;
|
2016-01-03 19:20:45 +01:00
|
|
|
/** @const */ var CMOS_MEM_BASE_LOW = 0x15;
|
|
|
|
/** @const */ var CMOS_MEM_BASE_HIGH = 0x16;
|
|
|
|
/** @const */ var CMOS_MEM_OLD_EXT_LOW = 0x17;
|
|
|
|
/** @const */ var CMOS_MEM_OLD_EXT_HIGH = 0x18;
|
2015-03-09 01:20:56 +01:00
|
|
|
/** @const */ var CMOS_DISK_DRIVE1_TYPE = 0x19;
|
|
|
|
/** @const */ var CMOS_DISK_DRIVE2_TYPE = 0x1a;
|
|
|
|
/** @const */ var CMOS_DISK_DRIVE1_CYL = 0x1b;
|
|
|
|
/** @const */ var CMOS_DISK_DRIVE2_CYL = 0x24;
|
|
|
|
/** @const */ var CMOS_MEM_EXTMEM_LOW = 0x30;
|
|
|
|
/** @const */ var CMOS_MEM_EXTMEM_HIGH = 0x31;
|
|
|
|
/** @const */ var CMOS_CENTURY = 0x32;
|
|
|
|
/** @const */ var CMOS_MEM_EXTMEM2_LOW = 0x34;
|
|
|
|
/** @const */ var CMOS_MEM_EXTMEM2_HIGH = 0x35;
|
|
|
|
/** @const */ var CMOS_BIOS_BOOTFLAG1 = 0x38;
|
|
|
|
/** @const */ var CMOS_BIOS_DISKTRANSFLAG = 0x39;
|
|
|
|
/** @const */ var CMOS_BIOS_BOOTFLAG2 = 0x3d;
|
|
|
|
/** @const */ var CMOS_MEM_HIGHMEM_LOW = 0x5b;
|
|
|
|
/** @const */ var CMOS_MEM_HIGHMEM_MID = 0x5c;
|
|
|
|
/** @const */ var CMOS_MEM_HIGHMEM_HIGH = 0x5d;
|
|
|
|
/** @const */ var CMOS_BIOS_SMP_COUNT = 0x5f;
|
2015-02-25 18:23:10 +01:00
|
|
|
|
|
|
|
|
2013-11-06 01:12:55 +01:00
|
|
|
/**
|
|
|
|
* RTC (real time clock) and CMOS
|
|
|
|
* @constructor
|
2015-02-25 18:23:10 +01:00
|
|
|
* @param {CPU} cpu
|
2013-11-06 01:12:55 +01:00
|
|
|
*/
|
2015-02-25 18:23:10 +01:00
|
|
|
function RTC(cpu)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-09-15 21:58:40 +02:00
|
|
|
/** @const @type {CPU} */
|
2014-09-25 06:13:54 +02:00
|
|
|
this.cpu = cpu;
|
2015-01-12 18:05:10 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
this.cmos_index = 0;
|
2016-01-22 23:06:05 +01:00
|
|
|
this.cmos_data = new Uint8Array(128);
|
2015-02-25 18:23:10 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
// used for cmos entries
|
|
|
|
this.rtc_time = Date.now();
|
|
|
|
this.last_update = this.rtc_time;
|
2014-02-23 23:32:41 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
// used for periodic interrupt
|
|
|
|
this.next_interrupt = 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
this.periodic_interrupt = false;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
// corresponds to default value for cmos_a
|
|
|
|
this.periodic_interrupt_time = 1000 / 1024;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
this.cmos_a = 0x26;
|
|
|
|
this.cmos_b = 2;
|
|
|
|
this.cmos_c = 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
|
|
|
this.nmi_disabled = 0;
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
cpu.io.register_write(0x70, this, function(out_byte)
|
2014-09-25 06:13:54 +02:00
|
|
|
{
|
|
|
|
this.cmos_index = out_byte & 0x7F;
|
|
|
|
this.nmi_disabled = out_byte >> 7;
|
2014-10-21 21:51:42 +02:00
|
|
|
});
|
2014-09-25 06:13:54 +02:00
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
cpu.io.register_write(0x71, this, this.cmos_port_write);
|
|
|
|
cpu.io.register_read(0x71, this, this.cmos_port_read);
|
2014-09-25 06:13:54 +02:00
|
|
|
}
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
RTC.prototype.get_state = function()
|
|
|
|
{
|
|
|
|
var state = [];
|
|
|
|
|
|
|
|
state[0] = this.cmos_index;
|
|
|
|
state[1] = this.cmos_data;
|
|
|
|
state[2] = this.rtc_time;
|
|
|
|
state[3] = this.last_update;
|
|
|
|
state[4] = this.next_interrupt;
|
2016-11-09 13:42:53 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
state[6] = this.periodic_interrupt;
|
|
|
|
state[7] = this.periodic_interrupt_time;
|
|
|
|
state[8] = this.cmos_a;
|
|
|
|
state[9] = this.cmos_b;
|
|
|
|
state[10] = this.cmos_c;
|
|
|
|
state[11] = this.nmi_disabled;
|
|
|
|
|
|
|
|
return state;
|
|
|
|
};
|
|
|
|
|
|
|
|
RTC.prototype.set_state = function(state)
|
|
|
|
{
|
|
|
|
this.cmos_index = state[0];
|
|
|
|
this.cmos_data = state[1];
|
|
|
|
this.rtc_time = state[2];
|
|
|
|
this.last_update = state[3];
|
|
|
|
this.next_interrupt = state[4];
|
2016-11-09 13:42:53 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.periodic_interrupt = state[6];
|
|
|
|
this.periodic_interrupt_time = state[7];
|
|
|
|
this.cmos_a = state[8];
|
|
|
|
this.cmos_b = state[9];
|
|
|
|
this.cmos_c = state[10];
|
|
|
|
this.nmi_disabled = state[11];
|
|
|
|
};
|
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
RTC.prototype.timer = function(time, legacy_mode)
|
|
|
|
{
|
2016-02-14 13:15:09 +01:00
|
|
|
time = Date.now(); // XXX
|
2015-01-14 02:43:09 +01:00
|
|
|
this.rtc_time += time - this.last_update;
|
|
|
|
this.last_update = time;
|
|
|
|
|
2016-11-09 13:42:53 +01:00
|
|
|
if(this.periodic_interrupt && this.next_interrupt < time)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-05-17 01:56:50 +02:00
|
|
|
this.cpu.device_raise_irq(8);
|
|
|
|
this.cmos_c |= 1 << 6 | 1 << 7;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
this.next_interrupt += this.periodic_interrupt_time *
|
2014-09-25 06:13:54 +02:00
|
|
|
Math.ceil((time - this.next_interrupt) / this.periodic_interrupt_time);
|
2015-01-14 02:43:09 +01:00
|
|
|
|
|
|
|
return Math.max(0, time - this.next_interrupt);
|
2014-09-25 06:13:54 +02:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2015-01-14 02:43:09 +01:00
|
|
|
return 100;
|
2014-09-25 06:13:54 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
RTC.prototype.bcd_pack = function(n)
|
2015-09-12 01:10:38 +02:00
|
|
|
{
|
|
|
|
var i = 0,
|
2014-09-25 06:13:54 +02:00
|
|
|
result = 0,
|
|
|
|
digit;
|
2015-09-12 01:10:38 +02:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
while(n)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-09-12 01:10:38 +02:00
|
|
|
digit = n % 10;
|
|
|
|
|
|
|
|
result |= digit << (4 * i);
|
|
|
|
i++;
|
2014-09-25 06:13:54 +02:00
|
|
|
n = (n - digit) / 10;
|
2015-09-12 01:10:38 +02:00
|
|
|
}
|
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
return result;
|
|
|
|
};
|
2014-03-11 16:58:35 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
RTC.prototype.encode_time = function(t)
|
|
|
|
{
|
|
|
|
if(this.cmos_b & 4)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-25 06:13:54 +02:00
|
|
|
// binary mode
|
|
|
|
return t;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-25 06:13:54 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.bcd_pack(t);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
// - interrupt on update
|
|
|
|
// - countdown
|
|
|
|
// - letting bios/os set values
|
2015-09-12 01:10:38 +02:00
|
|
|
// (none of these are used by seabios or the OSes we're
|
2014-09-25 06:13:54 +02:00
|
|
|
// currently testing)
|
2015-02-25 18:23:10 +01:00
|
|
|
RTC.prototype.cmos_port_read = function()
|
2014-09-25 06:13:54 +02:00
|
|
|
{
|
|
|
|
var index = this.cmos_index;
|
|
|
|
|
|
|
|
//this.cmos_index = 0xD;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-25 06:13:54 +02:00
|
|
|
switch(index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_SECONDS:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCSeconds());
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_MINUTES:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCMinutes());
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_HOURS:
|
2014-09-25 06:13:54 +02:00
|
|
|
// TODO: 12 hour mode
|
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCHours());
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_DAY_MONTH:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCDate());
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_MONTH:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCMonth() + 1);
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_RTC_YEAR:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCFullYear() % 100);
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_STATUS_A:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.cmos_a;
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_STATUS_B:
|
2014-09-25 06:13:54 +02:00
|
|
|
//dbg_log("cmos read from index " + h(index));
|
|
|
|
return this.cmos_b;
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_STATUS_C:
|
2014-09-25 06:13:54 +02:00
|
|
|
// It is important to know that upon a IRQ 8, Status Register C
|
|
|
|
// will contain a bitmask telling which interrupt happened.
|
|
|
|
// What is important is that if register C is not read after an
|
2015-09-12 01:10:38 +02:00
|
|
|
// IRQ 8, then the interrupt will not happen again.
|
2016-11-09 13:42:53 +01:00
|
|
|
this.cpu.device_lower_irq(8);
|
2014-09-25 06:13:54 +02:00
|
|
|
|
|
|
|
dbg_log("cmos reg C read", LOG_RTC);
|
|
|
|
// Missing IRQF flag
|
|
|
|
//return cmos_b & 0x70;
|
2015-05-17 01:57:52 +02:00
|
|
|
var c = this.cmos_c;
|
2014-09-25 06:13:54 +02:00
|
|
|
|
2015-05-17 01:57:52 +02:00
|
|
|
this.cmos_c &= ~0xF0;
|
|
|
|
|
|
|
|
return c;
|
2014-09-25 06:13:54 +02:00
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_STATUS_D:
|
|
|
|
return 0xFF;
|
2014-09-25 06:13:54 +02:00
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
case CMOS_CENTURY:
|
2014-09-25 06:13:54 +02:00
|
|
|
return this.encode_time(new Date(this.rtc_time).getUTCFullYear() / 100 | 0);
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
default:
|
|
|
|
dbg_log("cmos read from index " + h(index), LOG_RTC);
|
|
|
|
return this.cmos_data[this.cmos_index];
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-25 06:13:54 +02:00
|
|
|
};
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
RTC.prototype.cmos_port_write = function(data_byte)
|
2014-09-25 06:13:54 +02:00
|
|
|
{
|
|
|
|
switch(this.cmos_index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-25 06:13:54 +02:00
|
|
|
case 0xA:
|
|
|
|
this.cmos_a = data_byte & 0x7F;
|
|
|
|
this.periodic_interrupt_time = 1000 / (32768 >> (this.cmos_a & 0xF) - 1);
|
|
|
|
|
|
|
|
dbg_log("Periodic interrupt, a=" + h(this.cmos_a, 2) + " t=" + this.periodic_interrupt_time , LOG_RTC);
|
|
|
|
break;
|
|
|
|
case 0xB:
|
|
|
|
this.cmos_b = data_byte;
|
|
|
|
if(this.cmos_b & 0x40)
|
|
|
|
{
|
|
|
|
this.next_interrupt = Date.now();
|
|
|
|
}
|
|
|
|
|
2016-03-26 15:21:16 +01:00
|
|
|
if(this.cmos_b & 0x20) dbg_log("Unimplemented: alarm interrupt", LOG_RTC);
|
|
|
|
if(this.cmos_b & 0x10) dbg_log("Unimplemented: updated interrupt", LOG_RTC);
|
2014-09-25 06:13:54 +02:00
|
|
|
|
|
|
|
dbg_log("cmos b=" + h(this.cmos_b, 2), LOG_RTC);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dbg_log("cmos write index " + h(this.cmos_index) + ": " + h(data_byte), LOG_RTC);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-25 06:13:54 +02:00
|
|
|
|
|
|
|
this.periodic_interrupt = (this.cmos_b & 0x40) === 0x40 && (this.cmos_a & 0xF) > 0;
|
|
|
|
};
|
2015-02-25 18:23:10 +01:00
|
|
|
|
2016-01-03 19:20:45 +01:00
|
|
|
/**
|
|
|
|
* @param {number} index
|
|
|
|
*/
|
|
|
|
RTC.prototype.cmos_read = function(index)
|
|
|
|
{
|
2016-01-22 23:06:05 +01:00
|
|
|
dbg_assert(index < 128);
|
2016-01-03 19:20:45 +01:00
|
|
|
return this.cmos_data[index];
|
|
|
|
};
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
/**
|
|
|
|
* @param {number} index
|
|
|
|
* @param {number} value
|
|
|
|
*/
|
|
|
|
RTC.prototype.cmos_write = function(index, value)
|
|
|
|
{
|
|
|
|
dbg_log("cmos " + h(index) + " <- " + h(value), LOG_RTC);
|
2016-01-22 23:06:05 +01:00
|
|
|
dbg_assert(index < 128);
|
2015-02-25 18:23:10 +01:00
|
|
|
this.cmos_data[index] = value;
|
|
|
|
};
|