Assorted RTC and Debugger improvements

All three types of RTC interrupts (Periodic, Alarm and Update) should be supported now
This commit is contained in:
Jeff Parsons 2014-11-21 18:05:53 -08:00 committed by jeffpar
commit 851b077957
8 changed files with 307 additions and 116 deletions

File diff suppressed because one or more lines are too long

View file

@ -155,3 +155,17 @@
F000:0644 @ SND_DATA ; HANDLE TRANSMISSION OF COMMAND AND DATA BYTES
F000:0680 @ SND_LED ; TURN ON THE MODE INDICATORS
F000:06D1 @ MAKE_LED ; FORM THE DATA BYTE FOR THE MODE INDICATORS
448D +
F000:0000 @ CASSETTE_IO_1 ; BIOS1 (11/15/85)
F000:0065 @ INT15_EVENT_WAIT
F000:00D0 @ INT15_JOY_STICK
F000:016A @ INT15_WAIT
F000:01CA @ INT15_BLOCKMOVE
F000:028A @ SHUT9 ; RETURN FROM SHUTDOWN
F000:03CC @ GATE_A20
F000:03E5 @ EMPTY_8042
F000:03EE @ EXT_MEMORY
F000:03FA @ X_VIRTUAL
4915 +
F000:0000 @ TIME_OF_DAY_1 ; BIOS2 (11/15/85)
F000:0182 @ RTC_INT ; ALARM INTERRUPT (INT 0x70, IRQ 8)

View file

@ -503,13 +503,14 @@ ChipSet.IRQ = {
SLAVE: 0x02,
COM2: 0x03,
COM1: 0x04,
XTC: 0x05, // MODEL_5160 uses this for its HDC; MODEL_5170 designates it for LPT2
XTC: 0x05, // MODEL_5160 uses IRQ 5 for HDC (XTC version)
LPT2: 0x05, // MODEL_5170 uses IRQ 5 for LPT2
FDC: 0x06,
LPT1: 0x07,
RTC: 0x08,
IRQ2: 0x09,
COPROC: 0x0D,
ATC: 0x0E // MODEL_5170 uses this for its HDC
ATC: 0x0E // MODEL_5170 uses IRQ 14 for HDC (ATC version)
};
/*
@ -759,10 +760,10 @@ ChipSet.CMOS = {
RTC_MONTH_DAY: 0x07,
RTC_MONTH: 0x08,
RTC_YEAR: 0x09,
RTC_STATUSA: 0x0A,
RTC_STATUSB: 0x0B,
RTC_STATUSC: 0x0C,
RTC_STATUSD: 0x0D,
STATUSA: 0x0A,
STATUSB: 0x0B,
STATUSC: 0x0C,
STATUSD: 0x0D,
DIAG: 0x0E,
SHUTDOWN: 0x0F,
FDRIVE: 0x10,
@ -785,29 +786,29 @@ ChipSet.CMOS = {
DATA: { // this.abCMOSData
PORT: 0x71
},
STATUSA: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSA]
STATUSA: { // abCMOSData[ChipSet.CMOS.ADDR.STATUSA]
UIP: 0x80, // bit 7: 1 indicates Update-In-Progress, 0 indicates date/time ready to read
DV: 0x70, // bits 6-4 (DV2-DV0) are programmed to 010 to select a 32.768Khz time base
RS: 0x0F // bits 3-0 (RS3-RS0) are programmed to 0110 to select a 976.562us interrupt rate
},
STATUSB: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB]
STATUSB: { // abCMOSData[ChipSet.CMOS.ADDR.STATUSB]
SET: 0x80, // bit 7: 1 to set any/all of the 14 time-bytes
PIE: 0x40, // bit 6: 1 for Periodic Interrupt Enable
AIE: 0x20, // bit 5: 1 for Alarm Interrupt Enable
UIE: 0x10, // bit 4: 1 for Update-Ended Interrupt Enable
UIE: 0x10, // bit 4: 1 for Update Interrupt Enable
SQWE: 0x08, // bit 3: 1 for Square Wave Enabled (as set by the STATUSA rate selection bits)
BINARY: 0x04, // bit 2: 1 for binary Date Mode, 0 for BCD Date Mode
HOUR24: 0x02, // bit 1: 1 for 24-hour mode, 0 for 12-hour mode
DST: 0x01 // bit 0: 1 for Daylight Savings Time enabled
},
STATUSC: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSC] TODO: Does reading this register clear these interrupt conditions? (see F000:01C6 in the MODEL_5170 BIOS)
IRQF: 0x80, // bit 7
STATUSC: { // abCMOSData[ChipSet.CMOS.ADDR.STATUSC]
IRQF: 0x80, // bit 7: 1 indicates one or more of the following bits (PF, AF, UF) are set
PF: 0x40, // bit 6: 1 indicates Periodic Interrupt
AF: 0x20, // bit 5: 1 indicates Alarm Interrupt
UF: 0x10, // bit 4: 1 indicates Update-Ended Interrupt
UF: 0x10, // bit 4: 1 indicates Update Interrupt
RESERVED: 0x0F
},
STATUSD: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSD]
STATUSD: { // abCMOSData[ChipSet.CMOS.ADDR.STATUSD]
VRB: 0x80, // bit 7: 1 indicates Valid RAM Bit (0 implies power was and/or is lost)
RESERVED: 0x7F
},
@ -1012,7 +1013,7 @@ ChipSet.prototype.powerDown = function(fSave)
* reset(fHard)
*
* @this {ChipSet}
* @param {boolean} [fHard] true if a machine reset (not just a soft reset)
* @param {boolean} [fHard] true on the initial reset (not a normal "soft" reset)
*/
ChipSet.prototype.reset = function(fHard)
{
@ -1098,9 +1099,11 @@ ChipSet.prototype.reset = function(fHard)
* and any later ("soft") resets (eg, from powerUp() calls), and make sure the latter preserves
* existing CMOS information.
*/
if (fHard) this.abCMOSData = new Array(ChipSet.CMOS.ADDR.TOTAL);
if (fHard) {
this.abCMOSData = new Array(ChipSet.CMOS.ADDR.TOTAL);
}
this.initRTCDate(this.sRTCDate);
this.initRTCTime(this.sRTCDate);
/*
* initCMOSData() will initialize a variety of "legacy" CMOS bytes, but it will NOT overwrite any memory
@ -1120,7 +1123,7 @@ ChipSet.prototype.reset = function(fHard)
};
/**
* initRTCDate(sDate)
* initRTCTime(sDate)
*
* Initialize the RTC portion of the CMOS registers to match the specified date/time (or if none is specified,
* the current date/time). The date/time should be expressed in the ISO 8601 format; eg: "2011-10-10T14:48:00".
@ -1128,7 +1131,7 @@ ChipSet.prototype.reset = function(fHard)
* NOTE: There are two approaches we could take here: always store the RTC bytes in binary, and convert them
* to/from BCD on-demand (ie, as the simulation reads/writes the CMOS RTC registers); or init/update them in the
* format specified by CMOS_STATUSB.BINARY (1 for binary, 0 for BCD). Both approaches require BCD conversion
* functions, but the former seems more efficient, in part because the periodic calls to updateRTCDate() won't
* functions, but the former seems more efficient, in part because the periodic calls to updateRTCTime() won't
* require any conversions.
*
* We take the same approach with the CMOS_STATUSB.HOUR24 setting: internally, we always operate in 24-hour mode,
@ -1140,7 +1143,7 @@ ChipSet.prototype.reset = function(fHard)
* @this {ChipSet}
* @param {string} [sDate]
*/
ChipSet.prototype.initRTCDate = function(sDate)
ChipSet.prototype.initRTCTime = function(sDate)
{
/*
* NOTE: I've already been burned once by a JavaScript library function that did NOT treat an undefined
@ -1185,12 +1188,14 @@ ChipSet.prototype.initRTCDate = function(sDate)
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_YEAR] = nYear % 100;
var nCentury = (nYear / 100);
this.abCMOSData[ChipSet.CMOS.ADDR.CENTURY_DATE] = (nCentury % 10) | ((nCentury / 10) << 4);
this.nCyclesCMOSLastUpdate = -1;
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSA] = 0x26; // hard-coded default; refer to ChipSet.CMOS.STATUSA.DV and ChipSet.CMOS.STATUSA.RS
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] = ChipSet.CMOS.STATUSB.HOUR24; // default to BCD mode (ChipSet.CMOS.STATUSB.BINARY not set)
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSC] = 0x00;
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSD] = ChipSet.CMOS.STATUSD.VRB;
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSA] = 0x26; // hard-coded default; refer to ChipSet.CMOS.STATUSA.DV and ChipSet.CMOS.STATUSA.RS
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] = ChipSet.CMOS.STATUSB.HOUR24; // default to BCD mode (ChipSet.CMOS.STATUSB.BINARY not set)
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] = 0x00;
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSD] = ChipSet.CMOS.STATUSD.VRB;
this.nRTCCyclesLastUpdate = this.nRTCCyclesNextUpdate = 0;
this.nRTCPeriodsPerSecond = this.nRTCCyclesPerPeriod = null;
};
/**
@ -1201,14 +1206,14 @@ ChipSet.prototype.initRTCDate = function(sDate)
*/
ChipSet.prototype.getRTCByte = function(iRTC)
{
if (DEBUG) this.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.RTC_STATUSD);
if (DEBUG) this.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.STATUSD);
var b = this.abCMOSData[iRTC];
if (iRTC < ChipSet.CMOS.ADDR.RTC_STATUSA) {
if (iRTC < ChipSet.CMOS.ADDR.STATUSA) {
var f12HourValue = false;
if (iRTC == ChipSet.CMOS.ADDR.RTC_HOUR || iRTC == ChipSet.CMOS.ADDR.RTC_HOUR_ALRM) {
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
if (b < 12) {
b = (!b? 12 : b);
} else {
@ -1218,7 +1223,7 @@ ChipSet.prototype.getRTCByte = function(iRTC)
f12HourValue = true;
}
}
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
/*
* We're in BCD mode, so we must convert b from BINARY to BCD. But first:
*
@ -1233,7 +1238,7 @@ ChipSet.prototype.getRTCByte = function(iRTC)
b = (b % 10) | ((b / 10) << 4);
}
} else {
if (iRTC == ChipSet.CMOS.ADDR.RTC_STATUSA) {
if (iRTC == ChipSet.CMOS.ADDR.STATUSA) {
/*
* HACK: Perform a mindless toggling of the "Update-In-Progress" bit, so that it's flipped
* on the next read; this makes the MODEL_5170 BIOS ("POST2_RTCUP") happy.
@ -1253,11 +1258,11 @@ ChipSet.prototype.getRTCByte = function(iRTC)
*/
ChipSet.prototype.setRTCByte = function(iRTC, b)
{
if (DEBUG) this.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.RTC_STATUSD);
if (DEBUG) this.assert(iRTC >= 0 && iRTC <= ChipSet.CMOS.ADDR.STATUSD);
if (iRTC < ChipSet.CMOS.ADDR.RTC_STATUSA) {
if (iRTC < ChipSet.CMOS.ADDR.STATUSA) {
var fBCD = false;
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.BINARY)) {
/*
* We're in BCD mode, so we must convert b from BCD to BINARY (we assume it's valid
* BCD; ie, that both nibbles contain only 0-9, not A-F).
@ -1276,7 +1281,7 @@ ChipSet.prototype.setRTCByte = function(iRTC, b)
b += 0x30;
}
}
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
if (!(this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.HOUR24)) {
if (b <= 12) {
b = (b == 12? 0 : b);
} else {
@ -1290,33 +1295,157 @@ ChipSet.prototype.setRTCByte = function(iRTC, b)
};
/**
* updateRTCDate()
* calcRTCCyclePeriod()
*
* This should be called whenever the timings in STATUSA may have changed.
*
* TODO: 1024 is a hard-coded number of periods per second based on the default interrupt rate of 976.562us
* (ie, 1000000 / 976.562). Calculate the actual number based on the values programmed in the STATUSA register.
*
* @this {ChipSet}
*/
ChipSet.prototype.updateRTCDate = function()
ChipSet.prototype.calcRTCCyclePeriod = function()
{
this.nRTCCyclesLastUpdate = this.cpu.getCycles(this.fScaleTimers);
this.nRTCPeriodsPerSecond = 1024;
this.nRTCCyclesPerPeriod = Math.floor(this.cpu.getCyclesPerSecond() / this.nRTCPeriodsPerSecond);
this.setRTCCycleLimit();
};
/**
* getRTCCycleLimit(nCycles)
*
* This is called by the CPU to determine the maximum number of cycles it can process for the current burst.
*
* @this {ChipSet}
* @param {number} nCycles
* @return {number} maximum number of cycles (<= nCycles)
*/
ChipSet.prototype.getRTCCycleLimit = function(nCycles)
{
if (this.abCMOSData && this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.PIE) {
var nCyclesUpdate = this.nRTCCyclesNextUpdate - this.cpu.getCycles(this.fScaleTimers);
if (nCyclesUpdate > 0) {
if (nCycles > nCyclesUpdate) {
if (DEBUG) this.messageDebugger("getRTCCycleLimit(" + nCycles + "): reduced to " + nCyclesUpdate + " cycles", Debugger.MESSAGE.RTC);
nCycles = nCyclesUpdate;
} else {
if (DEBUG) this.messageDebugger("getRTCCycleLimit(" + nCycles + "): already less than " + nCyclesUpdate + " cycles", Debugger.MESSAGE.RTC);
}
} else {
if (DEBUG) this.messageDebugger("RTC next update has passed by " + nCyclesUpdate + " cycles", Debugger.MESSAGE.RTC);
}
}
return nCycles;
};
/**
* setRTCCycleLimit(nCycles)
*
* This should be called when PIE becomes set in STATUSB (and whenever PF is cleared in STATUSC while PIE is still set).
*
* @this {ChipSet}
* @param {number} [nCycles]
*/
ChipSet.prototype.setRTCCycleLimit = function(nCycles)
{
if (nCycles === undefined) nCycles = this.nRTCCyclesPerPeriod;
this.nRTCCyclesNextUpdate = this.cpu.getCycles(this.fScaleTimers) + nCycles;
if (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.PIE) {
this.cpu.setBurstCycles(nCycles);
}
};
/**
* updateRTCTime()
*
* @this {ChipSet}
*/
ChipSet.prototype.updateRTCTime = function()
{
var nCyclesDelta = 0;
var nCyclesPerSecond = this.cpu.getCyclesPerSecond();
var nCyclesUpdate = this.cpu.getCycles(this.fScaleTimers);
/*
* If nCyclesCMOSLastUpdate hasn't been set yet (ie, if this is our first updateRTCDate() call),
* then do nothing except initialize nCyclesCMOSLastUpdate.
* We must arrange for the very first calcRTCCyclePeriod() call to occur here, on the very first
* updateRTCTime() call, because this is the first point we can be guaranteed that CPU cycle counts
* are initialized (the CPU is the last component to be powered up/restored).
*
* TODO: A side-effect of this is that it undermines the save/restore code's preservation of last
* and next RTC cycle counts, which may change when the next RTC event is delivered.
*/
if (this.nCyclesCMOSLastUpdate >= 0) {
nCyclesDelta = nCyclesUpdate - this.nCyclesCMOSLastUpdate;
if (DEBUG) this.assert(nCyclesDelta >= 0);
var nSecondsDelta = Math.floor(nCyclesDelta / nCyclesPerSecond);
/*
* We trust that updateRTCDate() is being called as part of updateAllTimers(), and is therefore
* being called often enough to ensure that nSecondsDelta will never be greater than one. In fact,
* it would always be LESS than one if it weren't ALSO for the fact that we plow any "unused" cycles
* (nCyclesDelta % nCyclesPerSecond) back into nCyclesCMOSLastUpdate, so that we will eventually
* see a one-second delta.
*/
if (DEBUG) this.assert(nSecondsDelta <= 1);
if (nSecondsDelta) {
if (this.nRTCCyclesPerPeriod == null) this.calcRTCCyclePeriod();
/*
* Step 1: Deal with Periodic Interrupts
*/
if (nCyclesUpdate >= this.nRTCCyclesNextUpdate) {
var bPrev = this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC];
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.PF;
if (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.PIE) {
/*
* When PIE is set, setBurstCycles() should be getting called as needed to ensure
* that updateRTCTime() is called more frequently, so let's assert that we don't have
* an excess of cycles and thus possibly some missed Periodic Interrupts.
*/
if (DEBUG) {
if (nCyclesUpdate - this.nRTCCyclesNextUpdate > this.nRTCCyclesPerPeriod) {
if (bPrev & ChipSet.CMOS.STATUSC.PF) {
this.messageDebugger("RTC interrupt handler failed to clear STATUSC", Debugger.MESSAGE.RTC | Debugger.MESSAGE.WARN);
} else {
this.messageDebugger("CPU took too long trigger new RTC periodic interrupt", Debugger.MESSAGE.RTC | Debugger.MESSAGE.WARN);
}
}
}
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.IRQF;
this.setIRR(ChipSet.IRQ.RTC);
/*
* We could also call setRTCCycleLimit() at this point, but I don't think there's any
* benefit until the interrupt had been acknowledged and STATUSC has been read, thereby
* clearing the way for another Periodic Interrupt; it seems to me that when STATUSC
* is read, that's the more appropriate time to call setRTCCycleLimit().
*/
}
this.nRTCCyclesNextUpdate = nCyclesUpdate + this.nRTCCyclesPerPeriod;
}
/*
* Step 2: Deal with Alarm Interrupts
*/
if (this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] == this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC_ALRM]) {
if (this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN] == this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN_ALRM]) {
if (this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR] == this.abCMOSData[ChipSet.CMOS.ADDR.RTC_HOUR_ALRM]) {
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.AF;
if (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.AIE) {
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.IRQF;
this.setIRR(ChipSet.IRQ.RTC);
}
}
}
}
/*
* Step 3: Update the RTC date/time and deal with Update Interrupts
*/
var nCyclesDelta = nCyclesUpdate - this.nRTCCyclesLastUpdate;
if (DEBUG) this.assert(nCyclesDelta >= 0);
var nSecondsDelta = Math.floor(nCyclesDelta / nCyclesPerSecond);
/*
* We trust that updateRTCTime() is being called as part of updateAllTimers(), and is therefore
* being called often enough to ensure that nSecondsDelta will never be greater than one. In fact,
* it would always be LESS than one if it weren't also for the fact that we plow any "unused" cycles
* (nCyclesDelta % nCyclesPerSecond) back into nRTCCyclesLastUpdate, so that we will eventually
* see a one-second delta.
*/
if (DEBUG) this.assert(nSecondsDelta <= 1);
/*
* Make sure that CMOS.STATUSB.SET isn't set; if it is, then the once-per-second RTC updates must be
* disabled so that software can write new RTC date/time values without interference.
*/
if (nSecondsDelta && !(this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.SET)) {
while (nSecondsDelta--) {
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] >= 60) {
this.abCMOSData[ChipSet.CMOS.ADDR.RTC_SEC] = 0;
if (++this.abCMOSData[ChipSet.CMOS.ADDR.RTC_MIN] >= 60) {
@ -1336,8 +1465,14 @@ ChipSet.prototype.updateRTCDate = function()
}
}
}
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.UF;
if (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.UIE) {
this.abCMOSData[ChipSet.CMOS.ADDR.STATUSC] |= ChipSet.CMOS.STATUSC.IRQF;
this.setIRR(ChipSet.IRQ.RTC);
}
}
this.nCyclesCMOSLastUpdate = nCyclesUpdate - (nCyclesDelta % nCyclesPerSecond);
this.nRTCCyclesLastUpdate = nCyclesUpdate - (nCyclesDelta % nCyclesPerSecond);
};
/**
@ -1501,7 +1636,7 @@ ChipSet.prototype.save = function()
if (this.model >= ChipSet.MODEL_5170) {
state.set(5, [this.b8042Status, this.b8042InBuff, this.b8042CmdData,
this.b8042OutBuff, this.b8042InPort, this.b8042OutPort]);
state.set(6, [this.bMFGData, this.abDMAPageSpare, this.bCMOSAddr, this.abCMOSData, this.nCyclesCMOSLastUpdate]);
state.set(6, [this.bMFGData, this.abDMAPageSpare, this.bCMOSAddr, this.abCMOSData, this.nRTCCyclesLastUpdate, this.nRTCCyclesNextUpdate]);
}
return state.data();
};
@ -1565,16 +1700,17 @@ ChipSet.prototype.restore = function(data)
this.abDMAPageSpare = a[1];
this.bCMOSAddr = a[2];
this.abCMOSData = a[3];
this.nCyclesCMOSLastUpdate = a[4];
this.nRTCCyclesLastUpdate = a[4];
this.nRTCCyclesNextUpdate = a[5];
/*
* TODO: Decide whether restore() should faithfully preserve the RTC date/time that save() saved,
* or always reinitialize the date/time, or give the user (or the machine configuration) the option.
*
* For now, we're always reinitializing the RTC date. Alternatively, we could selectively update
* the CMOS bytes above, instead of overwriting them all, in which case this extra call to initRTCDate()
* the CMOS bytes above, instead of overwriting them all, in which case this extra call to initRTCTime()
* could be avoided.
*/
this.initRTCDate();
this.initRTCTime();
}
return true;
};
@ -2119,7 +2255,7 @@ ChipSet.prototype.dumpCMOS = function()
if (DEBUGGER) {
var sDump = "";
for (var iCMOS = 0; iCMOS < ChipSet.CMOS.ADDR.TOTAL; iCMOS++) {
var b = (iCMOS <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.getRTCByte(iCMOS) : this.abCMOSData[iCMOS]);
var b = (iCMOS <= ChipSet.CMOS.ADDR.STATUSD? this.getRTCByte(iCMOS) : this.abCMOSData[iCMOS]);
if (sDump) sDump += '\n';
sDump += "CMOS[0x" + str.toHexByte(iCMOS) + "]: 0x" + str.toHexByte(b);
}
@ -2660,7 +2796,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
}
}
else {
if (DEBUG) this.messageDebugger("advanceDMA(" + iDMAChannel + ") unsupported xfer mode: " + str.toHexWord(channel.xfer), Debugger.MESSAGE.DMA);
if (DEBUG) this.messageDebugger("advanceDMA(" + iDMAChannel + ") unsupported xfer mode: " + str.toHexWord(channel.xfer), Debugger.MESSAGE.DMA | Debugger.MESSAGE.WARN);
channel.fError = true;
}
}
@ -2855,11 +2991,12 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
pic.bISR &= ~bIREnd;
this.checkIRR(iPIC);
} else {
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", Debugger.MESSAGE.PIC);
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", Debugger.MESSAGE.PIC | Debugger.MESSAGE.WARN);
}
/*
* TODO: Support EOI commands with automatic rotation (eg, ChipSet.PIC_LO.OCW2_EOI_ROT and ChipSet.PIC_LO.OCW2_EOI_ROTSPEC)
*/
if (DEBUG && (bOCW2 & ChipSet.PIC_LO.OCW2_SET_ROTAUTO)) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unsupported OCW2 rotate command: " + str.toHexByte(bOut), Debugger.MESSAGE.PIC | Debugger.MESSAGE.WARN);
}
else if (bOCW2 == ChipSet.PIC_LO.OCW2_SET_PRI) {
/*
@ -2871,7 +3008,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
/*
* TODO: Remaining commands to support: ChipSet.PIC_LO.OCW2_SET_ROTAUTO and ChipSet.PIC_LO.OCW2_CLR_ROTAUTO
*/
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unsupported OCW2 command: " + str.toHexByte(bOut), Debugger.MESSAGE.PIC);
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unsupported OCW2 automatic EOI command: " + str.toHexByte(bOut), Debugger.MESSAGE.PIC | Debugger.MESSAGE.WARN);
}
} else {
/*
@ -2881,7 +3018,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
* that's unfortunate, because I don't support them yet.
*/
if (bOut & (ChipSet.PIC_LO.OCW3_POLL_CMD | ChipSet.PIC_LO.OCW3_SMM_CMD)) {
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unsupported OCW3 command: " + str.toHexByte(bOut), Debugger.MESSAGE.PIC);
if (DEBUG) this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unsupported OCW3 command: " + str.toHexByte(bOut), Debugger.MESSAGE.PIC | Debugger.MESSAGE.WARN);
}
pic.bOCW3 = bOut;
}
@ -3606,7 +3743,7 @@ ChipSet.prototype.updateAllTimers = function(fCycleReset)
for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) {
this.updateTimer(iTimer, fCycleReset);
}
if (this.model >= ChipSet.MODEL_5170) this.updateRTCDate();
if (this.model >= ChipSet.MODEL_5170) this.updateRTCTime();
};
/**
@ -4307,8 +4444,26 @@ ChipSet.prototype.outCMOSAddr = function(port, bOut, addrFrom)
ChipSet.prototype.inCMOSData = function(port, addrFrom)
{
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
var bIn = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.getRTCByte(bAddr) : this.abCMOSData[bAddr]);
var bIn = (bAddr <= ChipSet.CMOS.ADDR.STATUSD? this.getRTCByte(bAddr) : this.abCMOSData[bAddr]);
this.messagePort(port, null, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", Debugger.MESSAGE.CMOS, bIn);
if (addrFrom != null) {
if (bAddr == ChipSet.CMOS.ADDR.STATUSC) {
/*
* When software reads the STATUSC port, all interrupt bits (PF, AF, and UF) are automatically
* cleared, which in turn clears the IRQF bit, which in turn clears the IRQ.
*/
this.abCMOSData[bAddr] &= ChipSet.CMOS.STATUSC.RESERVED;
if (bIn & ChipSet.CMOS.STATUSC.IRQF) this.clearIRR(ChipSet.IRQ.RTC);
/*
* If we just cleared PF, and PIE is still set, then we need to make sure the next Periodic Interrupt
* occurs in a timely manner, too.
*/
if ((bIn & ChipSet.CMOS.STATUSC.PF) && (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.PIE)) {
if (DEBUG) this.messageDebugger("RTC periodic interrupt cleared", Debugger.MESSAGE.RTC);
this.setRTCCycleLimit();
}
}
}
return bIn;
};
@ -4324,7 +4479,16 @@ ChipSet.prototype.outCMOSData = function(port, bOut, addrFrom)
{
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
this.messagePort(port, bOut, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", Debugger.MESSAGE.CMOS);
this.abCMOSData[bAddr] = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.setRTCByte(bAddr, bOut) : bOut);
var bDelta = bOut ^ this.abCMOSData[bAddr];
this.abCMOSData[bAddr] = (bAddr <= ChipSet.CMOS.ADDR.STATUSD? this.setRTCByte(bAddr, bOut) : bOut);
if (bAddr == ChipSet.CMOS.ADDR.STATUSB && (bDelta & ChipSet.CMOS.STATUSB.PIE)) {
if (bOut & ChipSet.CMOS.STATUSB.PIE) {
if (DEBUG) this.messageDebugger("RTC periodic interrupts enabled", Debugger.MESSAGE.RTC);
this.setRTCCycleLimit();
} else {
if (DEBUG) this.messageDebugger("RTC periodic interrupts disabled", Debugger.MESSAGE.RTC);
}
}
};
/**
@ -4394,9 +4558,12 @@ ChipSet.prototype.outNMI = function(port, bOut, addrFrom)
ChipSet.prototype.intBIOSRTC = function(addr)
{
if (DEBUGGER) {
var AH = this.cpu.regAX >> 8;
if (this.dbg && this.dbg.messageEnabled(Debugger.MESSAGE.RTC)) {
this.dbg.messageInt(Debugger.INT.RTC, addr);
if (this.dbg && this.dbg.messageEnabled(Debugger.MESSAGE.RTC) && this.dbg.messageInt(Debugger.INT.RTC, addr)) {
/*
* By computing AH now, we get the incoming AH value; if we computed it below, along with
* the rest of the register values, we'd get the outgoing AH value, which is not what we want.
*/
var AH = this.cpu.regAX >> 8;
this.cpu.addIntReturn(addr, function(chipset, nCycles) {
return function onBIOSRTCReturn(nLevel) {
nCycles = chipset.cpu.getCycles() - nCycles;

View file

@ -98,6 +98,7 @@ function CPU(parmsCPU, nCyclesDefault)
this.aFlags.fPowered = false;
this.aFlags.fRunning = false;
this.aFlags.fStarting = false;
this.aFlags.fAutoStart = parmsCPU['autoStart'];
/*
@ -955,7 +956,7 @@ CPU.prototype.calcRemainingTime = function()
this.aCounts.nCyclesRecalc += this.aCounts.nCyclesThisRun;
if (DEBUG && this.dbg && this.dbg.messageEnabled(Debugger.MESSAGE.LOG) && msRemainsThisRun) {
this.dbg.message("at " + this.aCounts.msEndThisRun + "ms, calcRemainingTime returned " + msRemainsThisRun + "ms to sleep");
this.dbg.message("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.aCounts.msEndThisRun + "ms");
}
this.aCounts.msEndThisRun += msRemainsThisRun;
@ -998,6 +999,7 @@ CPU.prototype.runCPU = function(fOnClick)
* nCyclesPerBurst = nCyclesTimer0;
* }
*/
if (this.chipset) nCyclesPerBurst = this.chipset.getRTCCycleLimit(nCyclesPerBurst);
/*
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
@ -1065,6 +1067,7 @@ CPU.prototype.startCPU = function(fSetFocus)
this.setSpeed();
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.aFlags.fRunning = true;
this.aFlags.fStarting = true;
if (this.chipset) this.chipset.setSpeaker();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";

View file

@ -81,7 +81,7 @@ function Debugger(parmsDbg)
this.cInstructions = -1;
/*
* The default numder of hex characters in a physical address; updated by initBus().
* Default number of hex chars in a physical address (ie, for real-mode); updated by initBus().
*/
this.cchAddr = 5;
@ -214,9 +214,11 @@ Debugger.MESSAGE = {
STATE: 0x00800000,
MOUSE: 0x01000000,
COMPUTER: 0x02000000,
LOG: 0x04000000,
DOS: 0x08000000,
HALT: 0x80000000
DOS: 0x04000000,
OTHER: 0x08000000,
LOG: 0x10000000,
WARN: 0x20000000,
HALT: 0x40000000
};
if (DEBUGGER) {
@ -531,8 +533,10 @@ if (DEBUGGER) {
"state": Debugger.MESSAGE.STATE,
"mouse": Debugger.MESSAGE.MOUSE,
"computer": Debugger.MESSAGE.COMPUTER,
"log": Debugger.MESSAGE.LOG,
"dos": Debugger.MESSAGE.DOS,
"other": Debugger.MESSAGE.OTHER,
"log": Debugger.MESSAGE.LOG,
"warn": Debugger.MESSAGE.WARN,
/*
* Now we turn to message actions rather than message types; for example, setting "halt"
* on or off doesn't enable "halt" messages, but rather halts the CPU on any message above.
@ -1265,6 +1269,9 @@ if (DEBUGGER) {
var a = dbg.parseCommand(sInput, true);
for (var s in a) dbg.doCommand(a[s]);
}
else if (event.keyCode == Keyboard.KEYCODE.ESC) {
control.value = sInput = "";
}
else {
if (event.keyCode == Keyboard.KEYCODE.UP) {
if (dbg.iPrevCmd < dbg.aPrevCmds.length - 1) {
@ -1297,7 +1304,8 @@ if (DEBUGGER) {
function onClickDebugEnter(fRepeat) {
if (dbg.controlDebug) {
var sInput = dbg.controlDebug.value;
var a = dbg.parseCommand(sInput, true, true);
dbg.controlDebug.value = "";
var a = dbg.parseCommand(sInput, true);
for (var s in a) dbg.doCommand(a[s]);
return true;
}
@ -1537,7 +1545,7 @@ if (DEBUGGER) {
Debugger.prototype.initMessages = function(sEnable)
{
this.afnDumpers = [];
this.bitsMessageEnabled = 0;
this.bitsMessageEnabled = Debugger.MESSAGE.WARN;
this.sMessagePrev = null;
var aEnable = this.parseCommand(sEnable);
if (aEnable.length) {
@ -1575,13 +1583,16 @@ if (DEBUGGER) {
* NOTE: If the caller specifies multiple MESSAGE category flags, then ALL the corresponding flags
* in the Debugger's bitsMessageEnabled variable must be enabled as well, else the result will be false.
*
* One wrinkle is MESSAGE.WARN: if that category is enabled, then ANY value with that bit will return true.
*
* @this {Debugger}
* @param {number} bitsMessage is one or more Debugger MESSAGE_* category flag(s)
* @return {boolean} true if message category is enabled, false if not
*/
Debugger.prototype.messageEnabled = function(bitsMessage)
{
return ((this.bitsMessageEnabled & bitsMessage) === bitsMessage);
var bitsEnabled = this.bitsMessageEnabled & bitsMessage;
return (bitsEnabled === bitsMessage || !!(bitsEnabled & Debugger.MESSAGE.WARN));
};
/**
@ -1620,8 +1631,8 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {number} nInt
* @param {number} addr
* @return {boolean} true if message generated, false if not
* @param {number} addr (EIP after the "INT n" instruction has been fetched but not dispatched)
* @return {boolean} true if message generated (which in turn triggers addIntReturn() inside checkIntNotify()), false if not
*/
Debugger.prototype.messageInt = function(nInt, addr)
{
@ -1642,6 +1653,12 @@ if (DEBUGGER) {
this.updateRegValues();
sFunc = " " + str.replaceArray(this.aRegValues, sFunc);
}
/*
* For purposes of display only, rewind addr to the address of the responsible "INT n" instruction; we
* know it's the two-byte "INT n" instruction because that's the only opcode handler that calls checkIntNotify()
* at the moment. If that changes, then this will have to change as well.
*/
addr -= 2;
this.message("INT 0x" + str.toHexByte(nInt) + ": AH=" + str.toHexByte(AH) + " at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel) + sFunc);
}
return fMessage;
@ -3977,8 +3994,8 @@ if (DEBUGGER) {
if (aAddr[0] == null && aAddr[2] == null) return;
var addr = this.getAddr(aAddr);
this.println(sSymbol + ": " + this.hexAddr(aAddr) + " (%" + str.toHex(addr, this.cchAddr) + ")");
sSymbol = sSymbol? (sSymbol + ": ") : "";
this.println(sSymbol + this.hexAddr(aAddr) + " (%" + str.toHex(addr, this.cchAddr) + ")");
var aSymbol = this.findSymbolAtAddr(aAddr, true);
if (aSymbol.length) {
@ -4121,7 +4138,7 @@ if (DEBUGGER) {
if (sCategory !== undefined) {
var bitsMessage = 0;
if (sCategory == "all") {
bitsMessage = 0xffffffff;
bitsMessage = 0xffffffff & ~Debugger.MESSAGE.HALT;
sCategory = null;
} else if (sCategory == "on") {
fCriteria = true;
@ -4674,15 +4691,14 @@ if (DEBUGGER) {
};
/**
* parseCommand(sCmd, fSave, fRepeat)
* parseCommand(sCmd, fSave)
*
* @this {Debugger}
* @param {string|undefined} sCmd
* @param {boolean} [fSave] is true to save the command, false if not
* @param {boolean} [fRepeat] is true if the command may be repeated (and therefore iPrevCmd should not be decremented)
* @return {Array.<string>}
*/
Debugger.prototype.parseCommand = function(sCmd, fSave, fRepeat)
Debugger.prototype.parseCommand = function(sCmd, fSave)
{
if (fSave) {
if (!sCmd) {
@ -4692,7 +4708,7 @@ if (DEBUGGER) {
this.aPrevCmds.splice(0, 0, sCmd);
this.iPrevCmd = 0;
}
if (!fRepeat) this.iPrevCmd--;
this.iPrevCmd--;
}
}
var a = (sCmd? sCmd.split(sCmd.indexOf('|') >= 0? '|' : ';') : ['']);
@ -4743,24 +4759,18 @@ if (DEBUGGER) {
* For all other commands, if they lack a space between the command and argument portions,
* insert a space before the first non-alpha character, so that split() will have the desired effect.
*/
/*
* These commands work great, except that they won't compile, and in fact, I don't WANT them in the
* compiled version, but putting them inside (!COMPILED) doesn't help, so I must disable them for now.
*
if (!COMPILED) {
if (sCmd == "debug") {
DEBUG = true;
window.DEBUG = true;
this.println("DEBUG checks on");
return true;
}
else if (sCmd == "nodebug") {
DEBUG = false;
window.DEBUG = false;
this.println("DEBUG checks off");
return true;
}
}
*/
var ch, ch0, i;
switch (sCmd) {

View file

@ -1420,7 +1420,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
/*
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
*/
if (DEBUG) this.assert(!!(hdc.regStatus & HDC.ATC.STATUS.BUSY));
if (DEBUG) hdc.assert(!!(hdc.regStatus & HDC.ATC.STATUS.BUSY));
} else {
/*
* TODO: It would be nice to be a bit more specific about the error (if any) that just occurred.

View file

@ -2423,21 +2423,20 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
this.aFlags.fComplete = true;
/*
* fDebugCheck is true if we need to "check" every instruction with the Debugger. The Debugger will
* call cpu.stepCPU(n) with n == 0 if it's executing only ONE instruction (ie, the user just clicked the
* "Step" button, or they've issued a "t" or "t1" command). Otherwise, it will call with n == 1
* (ie, the user is holding the "Step" button, or they've issued a "t#" command where # > 1).
*
* In the first case, we want to ignore (ie, "step over") any breakpoints; otherwise, the Debugger has
* no easy way of moving past a breakpoint (other than clearing it, of course). In the second case,
* we want to honor any breakpoints, which in turn will set fComplete to false and signal the Debugger
* to stop.
*
* Note that as a practical matter, both 0 and 1 are otherwise treated the same when it comes to the
* minimum number of cycles to process: one and only one instruction will execute, since every (valid)
* instruction consumes at least 1 cycle.
* fDebugCheck is true if we need to "check" every instruction with the Debugger.
*/
var fDebugCheck = this.aFlags.fDebugCheck = (DEBUGGER && nMinCycles && this.dbg && this.dbg.checksEnabled());
var fDebugCheck = this.aFlags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
/*
* fDebugSkip is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
* to checkInstruction() that it can skip breakpoint checks, and that will be true ONLY when fStarting is
* true OR nMinCycles is zero (the latter means the Debugger is single-stepping).
*
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have officially
* "started" now.
*/
var fDebugSkip = this.aFlags.fStarting || !nMinCycles;
this.aFlags.fStarting = false;
/*
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
@ -2519,9 +2518,12 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
}
}
if (DEBUGGER && fDebugCheck && this.dbg.checkInstruction(this.regEIP)) {
this.stopCPU();
break;
if (DEBUGGER && fDebugCheck) {
if (this.dbg.checkInstruction(this.regEIP, fDebugSkip)) {
this.stopCPU();
break;
}
fDebugSkip = false;
}
this.opFlags = 0;

View file

@ -728,11 +728,6 @@ Component.prototype = {
} catch(e) {}
return;
}
/*
* If there's no Debugger, or the current component didn't bother saving a reference to the Debugger
* (eg, in its initBus() handler), then this component-level assert() the same as the class-level assert(),
* except for the (slightly) more detailed log() message.
*/
this.log(s);
throw new Error(s);
}