Stricter instruction restart checks, and some machine config cleanup

This commit is contained in:
Jeff Parsons 2016-03-18 15:46:32 -07:00
commit 6c6decc7ae
29 changed files with 1802 additions and 1215 deletions

View file

@ -265,21 +265,28 @@ Component.subclass(ChipSet);
*
* Unless otherwise noted, all BIOS references refer to the *original* BIOS released with each model.
*/
ChipSet.MODEL_5150 = 5150; // used in reference to the 1st 5150 BIOS, dated Apr 24, 1981
ChipSet.MODEL_5160 = 5160; // used in reference to the 1st 5160 BIOS, dated Nov 8, 1982
ChipSet.MODEL_5170 = 5170; // used in reference to the 1st 5170 BIOS, dated Jan 10, 1984
ChipSet.MODEL_5150 = 5150; // used in reference to the 1st 5150 BIOS, dated Apr 24, 1981
ChipSet.MODEL_5160 = 5160; // used in reference to the 1st 5160 BIOS, dated Nov 8, 1982
ChipSet.MODEL_5170 = 5170; // used in reference to the 1st 5170 BIOS, dated Jan 10, 1984
/*
* The following are fake model numbers, used only to document issues/features in later IBM PC AT BIOS revisions.
*/
ChipSet.MODEL_5170_REV2 = 5170.2; // used in reference to the 2nd 5170 BIOS, dated Jun 10, 1985
ChipSet.MODEL_5170_REV3 = 5170.3; // used in reference to the 3rd 5170 BIOS, dated Nov 15, 1985
ChipSet.MODEL_5170_REV2 = 5170.2; // used in reference to the 2nd 5170 BIOS, dated Jun 10, 1985
ChipSet.MODEL_5170_REV3 = 5170.3; // used in reference to the 3rd 5170 BIOS, dated Nov 15, 1985
/*
* The following are even more fake model numbers, as we begin to depart from the IBM lineage. All that
* really matters at this point is that MODEL_DESKPRO386 > MODEL_5170.
* The following are even more fake model numbers, as we begin to depart more significantly from the IBM lineage.
* All that really matters at this point is that MODEL_COMPAQ_DESKPRO386 > MODEL_5170.
*/
ChipSet.MODEL_DESKPRO386 = 5180;
ChipSet.MODEL_COMPAQ_PORTABLE = 5150.1; // COMPAQ Portable (COMPAQ's first PC)
ChipSet.MODEL_COMPAQ_DESKPRO386 = 5180.1; // COMPAQ DeskPro 386 (COMPAQ's first 80386-based PC)
/*
* More assorted non-IBM models
*/
ChipSet.MODEL_CDP_MPC1600 = 5150.2; // Columbia Data Products MPC 1600
ChipSet.MODEL_ZENITH_Z150 = 5150.3; // Zenith Data Systems Z-150
/*
* Last but not least, a complete list of supported model strings, and corresponding internal model numbers.
@ -288,7 +295,9 @@ ChipSet.MODELS = {
"5150": ChipSet.MODEL_5150,
"5160": ChipSet.MODEL_5160,
"5170": ChipSet.MODEL_5170,
"deskpro386": ChipSet.MODEL_DESKPRO386
"deskpro386": ChipSet.MODEL_COMPAQ_DESKPRO386,
"mpc1600": ChipSet.MODEL_CDP_MPC1600,
"z150": ChipSet.MODEL_ZENITH_Z150
};
ChipSet.CONTROLS = {
@ -573,7 +582,7 @@ ChipSet.PIT0 = {
};
ChipSet.PIT1 = {
PORT: 0x48, // MODEL_DESKPRO386 only
PORT: 0x48, // MODEL_COMPAQ_DESKPRO386 only
TIMER3: 0, // used for fail-safe clock
TIMER4: 1, // N/A
TIMER5: 2 // used for refresher request extend/speed control
@ -1180,8 +1189,8 @@ ChipSet.prototype.reset = function(fHard)
* as TIMER3, TIMER4 and TIMER5; that numbering also matches their indexes in the aTimers array.
*/
this.bPIT1Ctrl = null; // tracks writes to port 0x43
this.bPIT2Ctrl = null; // tracks writes to port 0x4B (MODEL_DESKPRO386 only)
this.aTimers = new Array(this.model == ChipSet.MODEL_DESKPRO386? 6 : 3);
this.bPIT2Ctrl = null; // tracks writes to port 0x4B (MODEL_COMPAQ_DESKPRO386 only)
this.aTimers = new Array(this.model == ChipSet.MODEL_COMPAQ_DESKPRO386? 6 : 3);
for (i = 0; i < this.aTimers.length; i++) {
this.initTimer(i);
}
@ -1226,7 +1235,7 @@ ChipSet.prototype.reset = function(fHard)
this.b8042InPort |= ChipSet.KBC.INPORT.MONO;
}
if (COMPAQ386 && this.model == ChipSet.MODEL_DESKPRO386) {
if (COMPAQ386 && this.model == ChipSet.MODEL_COMPAQ_DESKPRO386) {
this.b8042InPort |= ChipSet.KBC.INPORT.COMPAQ_NO80387 | ChipSet.KBC.INPORT.COMPAQ_NOWEITEK;
}
@ -2819,7 +2828,7 @@ ChipSet.prototype.outDMAPageSpare = function(iSpare, port, bOut, addrFrom)
* TODO: Remove this DEBUG-only DESKPRO386 code once we're done debugging DeskPro 386 ROMs;
* it enables logging of all DeskPro ROM checkpoint I/O to port 0x84.
*/
if (this.messageEnabled(Messages.DMA | Messages.PORT) || DEBUG && this.model == ChipSet.MODEL_DESKPRO386 && port == 0x84) {
if (this.messageEnabled(Messages.DMA | Messages.PORT) || DEBUG && this.model == ChipSet.MODEL_COMPAQ_DESKPRO386 && port == 0x84) {
this.printMessageIO(port, bOut, addrFrom, "DMA.SPARE" + iSpare + ".PAGE", null, true);
}
this.abDMAPageSpare[iSpare] = bOut;

View file

@ -610,7 +610,7 @@ HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
if (this.fATC) {
this.iDriveTable++;
if (this.chipset && this.chipset.model == ChipSet.MODEL_DESKPRO386) this.iDriveTable++;
if (this.chipset && this.chipset.model == ChipSet.MODEL_COMPAQ_DESKPRO386) this.iDriveTable++;
this.iDriveTypeDefault = 2;
bus.addPortInputWidth(HDC.ATC.DATA.PORT, 2);
bus.addPortOutputWidth(HDC.ATC.DATA.PORT, 2);
@ -1556,10 +1556,10 @@ HDC.prototype.inATCByte = function(port, addrFrom)
/*
* Due to the way I'm immediately triggering an interrupt whenever more data is available,
* I must take a "shotgun approach' to regStatus bits in order to make the MODEL_5170_REV1,
* MODEL_5170_REV3, and MODEL_DESKPRO386 all happy.
* MODEL_5170_REV3, and MODEL_COMPAQ_DESKPRO386 all happy.
*
* In general, it's fine for all of STATUS.READY, STATUS.SEEK_OK and STATUS.DATA_REQ to be
* set now; the MODEL_5170_REV3 requires at least the first two, and the MODEL_DESKPRO386
* set now; the MODEL_5170_REV3 requires at least the first two, and the MODEL_COMPAQ_DESKPRO386
* requires the third. Unfortunately, the outlier is the MODEL_5170_REV1, which also needs
* the STATUS.BUSY to be set on the first regStatus read after it finishes reading a sector;
* otherwise, the MODEL_5170_REV1 BIOS will never read any remaining sectors.
@ -1567,10 +1567,10 @@ HDC.prototype.inATCByte = function(port, addrFrom)
* Technically, it doesn't make sense for both BUSY and READY to be set at the same time,
* so we fix that in inATCStatus() by clearing BUSY whenever READY is detected *after* that
* first read. In addition, since this hack is really only needed for the MODEL_5170_REV1,
* we clear BUSY immediately on the MODEL_DESKPRO386 (which makes the Windows 95 protected-mode
* disk driver much happier).
* we clear BUSY immediately on the MODEL_COMPAQ_DESKPRO386 (which makes the Windows 95
* protected-mode disk driver much happier).
*/
if (hdc.chipset && hdc.chipset.model == ChipSet.MODEL_DESKPRO386) hdc.regStatus = 0;
if (hdc.chipset && hdc.chipset.model == ChipSet.MODEL_COMPAQ_DESKPRO386) hdc.regStatus = 0;
hdc.regStatus |= HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK | HDC.ATC.STATUS.DATA_REQ;
} else {
/*

View file

@ -752,25 +752,43 @@ X86.helpFault = function(nFault, nError, nCycles, fHalt)
if (this.nFault < 0) {
/*
* Single-fault (error code is passed through, and the responsible instruction is restartable.
*
* TODO: The following opCS/opLIP/opSS/opLSP checks are primarily required for 80386-based machines
* with paging enabled, because page faults introduce a new set of complex faults that our current
* segment load "probes" are insufficient to catch. So as a stop-gap measure, we rely on these FOUR
* "snapshot" registers to temporarily resolve the general instruction restartability problem.
*
* If you want to closely examine the underlying causes of these more complex faults, set breakpoints
* where indicated below, and examine the stack trace.
*/
if (this.opCS != -1) {
/*
* HACK: We must slam 3 into this.segCS.cpl to ensure that loading the original CS segment doesn't
* fail. For example, if we faulted in the middle of a ring transition that loaded CS with a higher
* privilege (lower CPL) code segment, then our attempt here to reload the lower privilege (higher CPL)
* code segment could be viewed as a privilege violation (which it would be outside this context).
*/
this.segCS.cpl = 3;
this.setCS(this.opCS);
if (this.opCS !== this.segCS.sel) {
/*
* HACK: We slam the RPL into this.segCS.cpl to ensure that loading the original CS segment doesn't
* fail. For example, if we faulted in the middle of a ring transition that loaded CS with a higher
* privilege (lower CPL) code segment, then our attempt here to reload the lower privilege (higher CPL)
* code segment could be viewed as a privilege violation (which it would be outside this context).
*/
this.segCS.cpl = this.opCS & 0x3; // set breakpoint here to inspect complex faults
this.setCS(this.opCS);
}
this.opCS = -1;
}
this.setLIP(this.opLIP);
if (this.opLIP !== this.regLIP) {
this.setLIP(this.opLIP); // set breakpoint here to inspect complex faults
this.assert(this.opLIP === this.regLIP);
}
if (this.opSS != -1) {
this.setSS(this.opSS);
if (this.opSS !== this.segSS.sel) {
this.setSS(this.opSS); // set breakpoint here to inspect complex faults
}
this.opSS = -1;
}
if (this.opLSP !== X86.ADDR_INVALID) {
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
if (this.opLSP !== this.regLSP) { // set breakpoint below to inspect complex faults
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
this.assert(this.opLSP === this.regLSP);
}
this.opLSP = X86.ADDR_INVALID;
}
}