Compaq DeskPro 386 ROM BIOS boots UNMODIFIED now
This commit is contained in:
parent
b9252390bf
commit
1508cd1bf3
12 changed files with 298 additions and 207 deletions
|
|
@ -476,13 +476,6 @@ Bus.prototype.getA20 = function()
|
|||
*/
|
||||
Bus.prototype.setA20 = function(fEnable)
|
||||
{
|
||||
/*
|
||||
* TODO: Until I determine why the Compaq DeskPro 386 ROM is switching to protected-mode
|
||||
* with the A20 line disabled, resulting in an almost immediate crash, I'm ignoring its requests
|
||||
* to turn A20 off.
|
||||
*/
|
||||
if (I386 && COMPAQ386) fEnable = true;
|
||||
|
||||
if (this.nBusWidth > 20) {
|
||||
var addrMask = (this.busMask & ~0x100000) | (fEnable? 0x100000 : 0);
|
||||
if (addrMask != this.busMask) {
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ ChipSet.MODEL_5160 = 5160; // used in reference to the 1st 5160 BIO
|
|||
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 of note in later IBM PC AT BIOS revisions.
|
||||
* 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
|
||||
|
|
@ -408,14 +408,14 @@ ChipSet.DMA_MASK = {
|
|||
};
|
||||
|
||||
ChipSet.DMA_MODE = {
|
||||
CHANNEL: 0x03,
|
||||
XFER: 0x0C,
|
||||
XFER_VERIFY: 0x00,
|
||||
XFER_WRITE: 0x04,
|
||||
XFER_READ: 0x08,
|
||||
CHANNEL: 0x03, // bits 0-1 select 1 of 4 possible channels
|
||||
TYPE: 0x0C, // bits 2-3 select 1 of 3 valid (4 possible) transfer types
|
||||
TYPE_VERIFY: 0x00, // pseudo transfer (generates addresses, responds to EOP, but nothing is moved)
|
||||
TYPE_WRITE: 0x04, // write to memory (move data FROM an I/O device; eg, reading a sector from a disk)
|
||||
TYPE_READ: 0x08, // read from memory (move data TO an I/O device; eg, writing a sector to a disk)
|
||||
AUTOINIT: 0x10,
|
||||
DECREMENT: 0x20, // clear for INCREMENT
|
||||
MODE: 0xC0,
|
||||
MODE: 0xC0, // bits 6-7 select 1 of 4 possible transfer modes
|
||||
MODE_DEMAND: 0x00,
|
||||
MODE_SINGLE: 0x40,
|
||||
MODE_BLOCK: 0x80,
|
||||
|
|
@ -1174,8 +1174,18 @@ ChipSet.prototype.reset = function(fHard)
|
|||
* TODO: Provide more control over these 8042 "Input Port" bits (eg, the keyboard lock)
|
||||
*/
|
||||
this.b8042InPort = ChipSet.KBC.INPORT.MFG_OFF | ChipSet.KBC.INPORT.KBD_UNLOCKED;
|
||||
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBC.INPORT.ENABLE_256KB;
|
||||
if (this.getSWVideoMonitor() == ChipSet.MONITOR.MONO) this.b8042InPort |= ChipSet.KBC.INPORT.MONO;
|
||||
|
||||
if (this.getSWMemorySize() >= 512) {
|
||||
this.b8042InPort |= ChipSet.KBC.INPORT.ENABLE_256KB;
|
||||
}
|
||||
|
||||
if (this.getSWVideoMonitor() == ChipSet.MONITOR.MONO) {
|
||||
this.b8042InPort |= ChipSet.KBC.INPORT.MONO;
|
||||
}
|
||||
|
||||
if (COMPAQ386 && this.model == ChipSet.MODEL_DESKPRO386) {
|
||||
this.b8042InPort |= ChipSet.KBC.INPORT.COMPAQ_NO80387 | ChipSet.KBC.INPORT.COMPAQ_NOWEITEK;
|
||||
}
|
||||
|
||||
this.b8042OutPort = ChipSet.KBC.OUTPORT.NO_RESET | ChipSet.KBC.OUTPORT.A20_ON;
|
||||
|
||||
|
|
@ -2381,11 +2391,11 @@ ChipSet.prototype.inDMAChannelAddr = function(iDMAC, iChannel, port, addrFrom)
|
|||
controller.bIndex ^= 0x1;
|
||||
/*
|
||||
* Technically, aTimers[1].fOut is what drives DMA requests for DMA channel 0 (ChipSet.DMA_REFRESH),
|
||||
* every 15us, once the BIOS has initialized the channel's "mode" with MODE_SINGLE, INCREMENT, AUTOINIT, and XFER_READ (0x58)
|
||||
* and initialized TIMER1 appropriately.
|
||||
* every 15us, once the BIOS has initialized the channel's "mode" with MODE_SINGLE, INCREMENT, AUTOINIT,
|
||||
* and TYPE_READ (0x58) and initialized TIMER1 appropriately.
|
||||
*
|
||||
* However, we don't need to be that particular. Simply simulate an ever-increasing address after every read of the full
|
||||
* DMA channel 0 address.
|
||||
* However, we don't need to be that particular. Simply simulate an ever-increasing address after every
|
||||
* read of the full DMA channel 0 address.
|
||||
*/
|
||||
if (!iDMAC && iChannel == ChipSet.DMA_REFRESH && !controller.bIndex) {
|
||||
channel.addrCurrent[0]++;
|
||||
|
|
@ -2443,9 +2453,10 @@ ChipSet.prototype.inDMAChannelCount = function(iDMAC, iChannel, port, addrFrom)
|
|||
/*
|
||||
* Technically, aTimers[1].fOut is what drives DMA requests for DMA channel 0 (ChipSet.DMA_REFRESH),
|
||||
* every 15us, once the BIOS has initialized the channel's "mode" with MODE_SINGLE, INCREMENT, AUTOINIT,
|
||||
* and XFER_READ (0x58) and initialized TIMER1 appropriately.
|
||||
* and TYPE_READ (0x58) and initialized TIMER1 appropriately.
|
||||
*
|
||||
* However, we don't need to be that particular. Simply simulate an ever-decreasing count after every read of the full DMA channel 0 count.
|
||||
* However, we don't need to be that particular. Simply simulate an ever-decreasing count after every
|
||||
* read of the full DMA channel 0 count.
|
||||
*/
|
||||
if (!iDMAC && iChannel == ChipSet.DMA_REFRESH && !controller.bIndex) {
|
||||
channel.countCurrent[0]--;
|
||||
|
|
@ -2731,7 +2742,7 @@ ChipSet.prototype.inDMAPageSpare = function(iSpare, port, addrFrom)
|
|||
ChipSet.prototype.outDMAPageSpare = function(iSpare, port, bOut, addrFrom)
|
||||
{
|
||||
/*
|
||||
* TODO: Remove the DEBUG-only DESKPRO386 code once we're done debugging DeskPro 386 ROMs;
|
||||
* 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) {
|
||||
|
|
@ -2789,10 +2800,10 @@ ChipSet.prototype.connectDMA = function(iDMAChannel, component, sFunction, obj)
|
|||
* @param {number} iDMAChannel
|
||||
* @param {function(boolean)} [done]
|
||||
*
|
||||
* For DMA_MODE_XFER_WRITE transfers, fnTransfer(-1) must return bytes as long as we request them (although it may
|
||||
* For DMA_MODE.TYPE_WRITE transfers, fnTransfer(-1) must return bytes as long as we request them (although it may
|
||||
* return -1 if it runs out of bytes prematurely).
|
||||
*
|
||||
* Similarly, for DMA_MODE_XFER_READ transfers, fnTransfer(b) must accept bytes as long as we deliver them (although
|
||||
* Similarly, for DMA_MODE.TYPE_READ transfers, fnTransfer(b) must accept bytes as long as we deliver them (although
|
||||
* it is certainly free to ignore bytes it no longer wants).
|
||||
*/
|
||||
ChipSet.prototype.requestDMA = function(iDMAChannel, done)
|
||||
|
|
@ -2846,7 +2857,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
{
|
||||
if (fInit) {
|
||||
channel.count = (channel.countCurrent[1] << 8) | channel.countCurrent[0];
|
||||
channel.xfer = (channel.mode & ChipSet.DMA_MODE.XFER);
|
||||
channel.type = (channel.mode & ChipSet.DMA_MODE.TYPE);
|
||||
channel.fWarning = channel.fError = false;
|
||||
if (DEBUG && DEBUGGER) {
|
||||
channel.cbDebug = channel.count + 1;
|
||||
|
|
@ -2876,12 +2887,12 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
var addr = (channel.bPage << 16) | (channel.addrCurrent[1] << 8) | channel.addrCurrent[0];
|
||||
if (DEBUG && DEBUGGER && channel.sAddrDebug === null) {
|
||||
channel.sAddrDebug = str.toHex(addr >> 4, 4) + ":" + str.toHex(addr & 0xf, 4);
|
||||
if (this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.xfer != ChipSet.DMA_MODE.XFER_WRITE) {
|
||||
if (this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.type != ChipSet.DMA_MODE.TYPE_WRITE) {
|
||||
this.printMessage("advanceDMA(" + iDMAChannel + ") transferring " + channel.cbDebug + " bytes from " + channel.sAddrDebug, true);
|
||||
this.dbg.doDump("db", channel.sAddrDebug, "l" + channel.cbDebug);
|
||||
}
|
||||
}
|
||||
if (channel.xfer == ChipSet.DMA_MODE.XFER_WRITE) {
|
||||
if (channel.type == ChipSet.DMA_MODE.TYPE_WRITE) {
|
||||
fAsyncRequest = true;
|
||||
(function advanceDMAWrite(addrCur) {
|
||||
channel.fnTransfer.call(channel.component, channel.obj, -1, function onTransferDMA(b, fAsync, obj, off) {
|
||||
|
|
@ -2893,7 +2904,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
channel.fWarning = true;
|
||||
}
|
||||
/*
|
||||
* TODO: Determine whether to abort, as we do for DMA_MODE_XFER_READ.
|
||||
* TODO: Determine whether to abort, as we do for DMA_MODE.TYPE_READ.
|
||||
*/
|
||||
b = 0xff;
|
||||
}
|
||||
|
|
@ -2916,7 +2927,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
});
|
||||
}(addr));
|
||||
}
|
||||
else if (channel.xfer == ChipSet.DMA_MODE.XFER_READ) {
|
||||
else if (channel.type == ChipSet.DMA_MODE.TYPE_READ) {
|
||||
/*
|
||||
* TODO: Determine whether we should support async dmaWrite() functions (currently not required)
|
||||
*/
|
||||
|
|
@ -2924,15 +2935,20 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
if (channel.fnTransfer.call(channel.component, channel.obj, b) < 0) {
|
||||
/*
|
||||
* In this case, I think I have no choice but to terminate the DMA operation in response to a failure,
|
||||
* because the ROM BIOS FDC.REG_DATA.CMD.FORMAT_TRACK command specifies a count that is MUCH too large (a side-effect
|
||||
* of the ROM BIOS using the same "DMA_SETUP" code for reads, writes AND formats).
|
||||
* because the ROM BIOS FDC.REG_DATA.CMD.FORMAT_TRACK command specifies a count that is MUCH too large
|
||||
* (a side-effect of the ROM BIOS using the same "DMA_SETUP" code for reads, writes AND formats).
|
||||
*/
|
||||
channel.fError = true;
|
||||
}
|
||||
}
|
||||
else if (channel.type == ChipSet.DMA_MODE.TYPE_VERIFY) {
|
||||
/*
|
||||
* Nothing to read or write; just call updateDMA()
|
||||
*/
|
||||
}
|
||||
else {
|
||||
if (DEBUG && this.messageEnabled(Messages.DMA | Messages.WARN)) {
|
||||
this.printMessage("advanceDMA(" + iDMAChannel + ") unsupported xfer mode: " + str.toHexWord(channel.xfer), true);
|
||||
this.printMessage("advanceDMA(" + iDMAChannel + ") unsupported transfer type: " + str.toHexWord(channel.type), true);
|
||||
}
|
||||
channel.fError = true;
|
||||
}
|
||||
|
|
@ -2986,7 +3002,7 @@ ChipSet.prototype.updateDMA = function(channel)
|
|||
channel.component = channel.obj = null;
|
||||
}
|
||||
|
||||
if (DEBUG && this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.xfer == ChipSet.DMA_MODE.XFER_WRITE && channel.sAddrDebug) {
|
||||
if (DEBUG && this.messageEnabled(this.messageBitsDMA(iDMAChannel)) && channel.type == ChipSet.DMA_MODE.TYPE_WRITE && channel.sAddrDebug) {
|
||||
this.printMessage("updateDMA(" + iDMAChannel + ") transferred " + channel.cbDebug + " bytes to " + channel.sAddrDebug, true);
|
||||
this.dbg.doDump("db", channel.sAddrDebug, "l" + channel.cbDebug);
|
||||
}
|
||||
|
|
@ -4078,7 +4094,7 @@ ChipSet.prototype.inPPIC = function(port, addrFrom)
|
|||
|
||||
/*
|
||||
* The ROM BIOS polls this port incessantly during its memory tests, checking for memory parity errors
|
||||
* (which of course we never report), so we further restrict these port messages to MESSAGE_MEM.
|
||||
* (which of course we never report), so we further restrict these port messages to Messages.MEM.
|
||||
*/
|
||||
this.printMessageIO(port, null, addrFrom, "PPI_C", b, Messages.CHIPSET | Messages.MEM);
|
||||
return b;
|
||||
|
|
@ -4301,8 +4317,8 @@ ChipSet.prototype.in8042RWReg = function(port, addrFrom)
|
|||
*/
|
||||
var b = this.bPPIB & ~(ChipSet.KBC.RWREG.NMI_ERROR | ChipSet.KBC.RWREG.REFRESH_BIT) | ((this.cpu.getCycles() & 0x40)? ChipSet.KBC.RWREG.REFRESH_BIT : 0);
|
||||
/*
|
||||
* Thanks to the WAITF function, this has become a very "busy" port, so let's not generate messages
|
||||
* unless both MESSAGE_8042 *and* MESSAGE_LOG are set.
|
||||
* Thanks to the WAITF function, this has become a very "busy" port, so if this generates too
|
||||
* many messages, try adding Messages.LOG to the criteria.
|
||||
*/
|
||||
this.printMessageIO(port, null, addrFrom, "8042_RWREG", b, Messages.C8042);
|
||||
return b;
|
||||
|
|
@ -4347,7 +4363,7 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
|
|||
*
|
||||
* This provides a single poll delay, so that the aforementioned "flush" won't toss our response.
|
||||
* If longer delays are needed down the road, we may need to set a delay count in the upper (hidden)
|
||||
* bits of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
|
||||
* bits of b8042Status, instead of using a single delay bit.
|
||||
*/
|
||||
if (this.b8042Status & ChipSet.KBC.STATUS.OUTBUFF_DELAY) {
|
||||
this.b8042Status |= ChipSet.KBC.STATUS.OUTBUFF_FULL;
|
||||
|
|
@ -4391,16 +4407,11 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.WRITE_CMD: // 0x60
|
||||
case ChipSet.KBC.CMD.WRITE_OUTPORT: // 0xD1
|
||||
/*
|
||||
* No further action required for this first group of commands; more data is expected via out8042InBuffData()
|
||||
* No further action required for this command; more data is expected via out8042InBuffData()
|
||||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.READ_INPORT: // 0xC0
|
||||
this.set8042OutBuff(this.b8042InPort);
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.DISABLE_KBD: // 0xAD
|
||||
this.set8042CmdData(this.b8042CmdData | ChipSet.KBC.DATA.CMD.NO_CLOCK);
|
||||
if (DEBUG) this.printMessage("keyboard disabled", Messages.KEYBOARD | Messages.PORT);
|
||||
|
|
@ -4432,6 +4443,20 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
this.set8042OutBuff(ChipSet.KBC.DATA.INTF_TEST.OK);
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.READ_INPORT: // 0xC0
|
||||
this.set8042OutBuff(this.b8042InPort);
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.READ_OUTPORT: // 0xD0
|
||||
this.set8042OutBuff(this.b8042OutPort);
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.WRITE_OUTPORT: // 0xD1
|
||||
/*
|
||||
* No further action required for this command; more data is expected via out8042InBuffData()
|
||||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.READ_TEST: // 0xE0
|
||||
this.set8042OutBuff((this.b8042CmdData & ChipSet.KBC.DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBC.TESTPORT.KBD_CLOCK);
|
||||
break;
|
||||
|
|
@ -4516,7 +4541,9 @@ ChipSet.prototype.set8042OutBuff = function(b)
|
|||
ChipSet.prototype.set8042OutPort = function(b)
|
||||
{
|
||||
this.b8042OutPort = b;
|
||||
this.bus.setA20(!!(b & ChipSet.KBC.OUTPORT.A20_ON));
|
||||
|
||||
this.cpu.setA20(!!(b & ChipSet.KBC.OUTPORT.A20_ON));
|
||||
|
||||
if (!(b & ChipSet.KBC.OUTPORT.NO_RESET)) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ if (DEBUGGER) {
|
|||
0x33: Messages.MOUSE
|
||||
};
|
||||
|
||||
Debugger.aCommands = {
|
||||
Debugger.COMMANDS = {
|
||||
'?': "help",
|
||||
'a [#]': "assemble",
|
||||
'b [#]': "breakpoint",
|
||||
|
|
@ -1660,7 +1660,7 @@ if (DEBUGGER) {
|
|||
* messageDump(bitMessage, fnDumper)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} bitMessage is one Debugger MESSAGE_* category flag
|
||||
* @param {number} bitMessage is one Messages category flag
|
||||
* @param {function(string)} fnDumper is a function the Debugger can use to dump data for that category
|
||||
* @return {boolean} true if successfully registered, false if not
|
||||
*/
|
||||
|
|
@ -1936,7 +1936,7 @@ if (DEBUGGER) {
|
|||
* @param {number|null} [addrFrom]
|
||||
* @param {string|null} [name] of the port, if any
|
||||
* @param {number|null} [bIn] is the input value, if known, on an input operation
|
||||
* @param {number} [bitsMessage] is one or more Debugger MESSAGE_* category flag(s)
|
||||
* @param {number} [bitsMessage] is one or more Messages category flag(s)
|
||||
*/
|
||||
Debugger.prototype.messageIO = function(component, port, bOut, addrFrom, name, bIn, bitsMessage)
|
||||
{
|
||||
|
|
@ -2414,7 +2414,7 @@ if (DEBUGGER) {
|
|||
* The rest of the instruction tracking logic can only be performed if historyInit() has allocated
|
||||
* the necessary data structures; note that there is no explicit UI for enabling/disabling history,
|
||||
* other than adding/removing breakpoints, simply because it's breakpoints that trigger the call to
|
||||
* checkInstruction() -- well, OK, and a few other things now, like enabling MESSAGE_INT messages.
|
||||
* checkInstruction() -- well, OK, and a few other things now, like enabling Messages.INT messages.
|
||||
*/
|
||||
if (nState >= 0 && this.aaOpcodeCounts.length) {
|
||||
this.cInstructions++;
|
||||
|
|
@ -2734,7 +2734,17 @@ if (DEBUGGER) {
|
|||
if (aBreak != this.aBreakExec) {
|
||||
this.bus.addMemBreak(this.getAddr(aAddr), aBreak == this.aBreakWrite);
|
||||
}
|
||||
if (!fTemp) this.println("breakpoint enabled: " + this.hexAddr(aAddr) + " (" + aBreak[0] + ")");
|
||||
if (fTemp) {
|
||||
/*
|
||||
* Force temporary breakpoints to be interpreted as physical breakpoints
|
||||
* (hence the assertion that there IS a physical address stored in aAddr);
|
||||
* this allows us to step over calls or interrupts that change the processor mode
|
||||
*/
|
||||
aAddr[0] = -1;
|
||||
this.assert(aAddr[2]);
|
||||
} else {
|
||||
this.println("breakpoint enabled: " + this.hexAddr(aAddr) + " (" + aBreak[0] + ")");
|
||||
}
|
||||
this.historyInit();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3826,8 +3836,8 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doHelp = function()
|
||||
{
|
||||
var s = "commands:";
|
||||
for (var sCommand in Debugger.aCommands) {
|
||||
s += '\n' + sCommand + " ".substr(0, 7-sCommand.length) + Debugger.aCommands[sCommand];
|
||||
for (var sCommand in Debugger.COMMANDS) {
|
||||
s += '\n' + sCommand + " ".substr(0, 7-sCommand.length) + Debugger.COMMANDS[sCommand];
|
||||
}
|
||||
if (!this.checksEnabled()) s += "\nnote: frequency/history disabled if no exec breakpoints";
|
||||
this.println(s);
|
||||
|
|
@ -4969,6 +4979,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
}
|
||||
} while (fPrefix);
|
||||
|
||||
if (this.fProcStep) {
|
||||
this.setTempBreakpoint(aAddr);
|
||||
if (!this.runCPU()) {
|
||||
|
|
|
|||
|
|
@ -616,7 +616,7 @@ Mouse.prototype.notifyMCR = function(bMCR)
|
|||
* driver shuts down and Windows exits, since it DID turn RTS on), that doesn't appear to actually happen.
|
||||
* At the very least, Windows will have (re)masked the serial port's IRQ, so what does it matter? Not much,
|
||||
* I just would have preferred that fActive properly reflect whether we should continue dispatching mouse
|
||||
* events, displaying MESSAGE_MOUSE messages, etc.
|
||||
* events, displaying MOUSE messages, etc.
|
||||
*
|
||||
* We could ask the ChipSet component to notify the SerialPort component whenever its IRQ is masked/unmasked,
|
||||
* and then have the SerialPort pass that notification on to us, but I'm assuming that in the real world,
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ State.prototype = {
|
|||
* messageEnabled(bitsMessage)
|
||||
*
|
||||
* @this {State}
|
||||
* @param {number} [bitsMessage] is one or more Debugger MESSAGE_* category flag(s)
|
||||
* @param {number} [bitsMessage] is one or more Messages category flag(s)
|
||||
* @return {boolean}
|
||||
*/
|
||||
messageEnabled: function(bitsMessage) {
|
||||
|
|
|
|||
|
|
@ -186,6 +186,11 @@ function X86CPU(parmsCPU)
|
|||
* so that if/when we call restore(), it will have something to fill in.
|
||||
*/
|
||||
this.resetRegs();
|
||||
|
||||
/*
|
||||
* Initially, the logical A20 state should be true, but from this point on, it's up to the machine to decide.
|
||||
*/
|
||||
this.fA20 = true;
|
||||
}
|
||||
|
||||
Component.subclass(X86CPU, CPU);
|
||||
|
|
@ -637,10 +642,42 @@ X86CPU.prototype.initMemory = function(aMemBlocks, blockShift, blockLimit, block
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setA20(fEnable)
|
||||
*
|
||||
* setA20() used to ONLY be a Bus function, but we now route all setA20() calls through the CPU,
|
||||
* so that the CPU can maintain a logical A20 state (fA20), separate from the physical A20 state.
|
||||
*
|
||||
* In real-mode, all cpu.setA20() calls go straight to bus.setA20(), and we update the logical
|
||||
* A20 state (fA20); in protected-mode, we only update the logical A20 state (fA20).
|
||||
*
|
||||
* In addition, when transitioning from real-mode to protected-mode, we call bus.setA20(true), and
|
||||
* when transitioning back to real-mode, we call bus.setA20(fA20). See setProtMode() for details.
|
||||
*
|
||||
* This gives the CPU an unusual amount of control over the A20 line, but it protects us from "bad"
|
||||
* protected-mode code that fails to ensure A20 is enabled; I've run into code in the Compaq DeskPro
|
||||
* 386 ROM BIOS that fails without this work-around. This seems like a fairly safe hack, because
|
||||
* it's hard to imagine any real-world protected-mode code relying on A20 being off. However, that
|
||||
* doesn't change the fact that this hack should NOT be necessary.
|
||||
*
|
||||
* TODO: Figure out why the DeskPro 386 ROM BIOS misbehaves under emulation, necessitating this hack.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {boolean} fEnable is true to enable A20, false to disable
|
||||
*/
|
||||
X86CPU.prototype.setA20 = function(fEnable)
|
||||
{
|
||||
this.fA20 = fEnable;
|
||||
if (!(this.regCR0 & X86.CR0.MSW.PE)) {
|
||||
this.bus.setA20(fEnable);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setAddressMask(busMask)
|
||||
*
|
||||
* Notification from Bus.setA20(), called whenever the A20 line changes.
|
||||
* Notification from Bus.setA20(), called whenever the physical A20 line changes; this is
|
||||
* independent of the CPU's own logical A20 state (fA20).
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} busMask
|
||||
|
|
@ -1173,6 +1210,7 @@ X86CPU.prototype.setAddrSize = function()
|
|||
this.aOpModGrpWord = X86ModW.aOpModGrp;
|
||||
} else {
|
||||
if (this.addrSize == 2) {
|
||||
this.getAddr = this.getShort;
|
||||
this.aOpModRegByte = X86ModB16.aOpModReg;
|
||||
this.aOpModMemByte = X86ModB16.aOpModMem;
|
||||
this.aOpModGrpByte = X86ModB16.aOpModGrp;
|
||||
|
|
@ -1180,6 +1218,7 @@ X86CPU.prototype.setAddrSize = function()
|
|||
this.aOpModMemWord = X86ModW16.aOpModMem;
|
||||
this.aOpModGrpWord = X86ModW16.aOpModGrp;
|
||||
} else {
|
||||
this.getAddr = this.getLong;
|
||||
this.aOpModRegByte = X86ModB32.aOpModReg;
|
||||
this.aOpModMemByte = X86ModB32.aOpModMem;
|
||||
this.aOpModGrpByte = X86ModB32.aOpModGrp;
|
||||
|
|
@ -1304,7 +1343,7 @@ X86CPU.prototype.checkIntNotify = function(nInt)
|
|||
}
|
||||
}
|
||||
/*
|
||||
* The enabling of MESSAGE_INT messages is one of the criteria that's also included in the Debugger's
|
||||
* The enabling of INT messages is one of the criteria that's also included in the Debugger's
|
||||
* checksEnabled() function, and therefore in fDebugCheck, so for maximum speed, we check fDebugCheck first.
|
||||
*/
|
||||
if (DEBUGGER && this.aFlags.fDebugCheck) {
|
||||
|
|
@ -1378,6 +1417,9 @@ X86CPU.prototype.checkIntReturn = function(addr)
|
|||
* (ie, LLDT, LTR, SLDT, STR are invalid instructions in real-mode, and are among the opcode handlers that we
|
||||
* update here).
|
||||
*
|
||||
* NOTE: Ideally, this function would do its work ONLY on mode *transitions*, but we assume calls to setProtMode()
|
||||
* are sufficiently infrequent that it doesn't really matter.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {boolean} [fProt] (use the current MSW PE bit if not specified)
|
||||
*/
|
||||
|
|
@ -1398,6 +1440,16 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
this.segFS.updateMode(fProt);
|
||||
this.segGS.updateMode(fProt);
|
||||
}
|
||||
/*
|
||||
* Work-around to update the A20 line whenever transitioning modes; see cpu.setA20() for details.
|
||||
*
|
||||
* Unfortunately, we can't immediately update the physical A20 line on return to real-mode, because
|
||||
* segment registers are likely still loaded with base addresses above 1Mb, so we leave the physical
|
||||
* A20 line enabled for now.
|
||||
*
|
||||
* if (this.bus) this.bus.setA20(fProt? true : this.fA20);
|
||||
*/
|
||||
if (this.bus && fProt) this.bus.setA20(true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1411,7 +1463,7 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
X86CPU.prototype.saveProtMode = function()
|
||||
{
|
||||
if (this.addrGDT != null) {
|
||||
return [this.regCR0, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL];
|
||||
return [this.regCR0, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL, this.fA20];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
@ -1435,6 +1487,7 @@ X86CPU.prototype.restoreProtMode = function(a)
|
|||
this.segLDT.restore(a[5]);
|
||||
this.segTSS.restore(a[6]);
|
||||
this.nIOPL = a[7];
|
||||
this.fA20 = (a[8] !== undefined? a[8] : this.bus.getA20());
|
||||
this.setProtMode();
|
||||
}
|
||||
};
|
||||
|
|
@ -1495,6 +1548,7 @@ X86CPU.prototype.restore = function(data)
|
|||
* properly AND to ensure the CPU's default ADDRESS and OPERAND sizes are set properly.
|
||||
*/
|
||||
this.setCSIP(a[0], this.segCS.sel);
|
||||
|
||||
/*
|
||||
* It's also important to call setSP(), so that the linear SP register (regLSP) will be updated properly;
|
||||
* we also need to call setSS(), to ensure that the lower and upper stack limits are properly initialized.
|
||||
|
|
@ -2370,8 +2424,8 @@ X86CPU.prototype.setMSW = function(w)
|
|||
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
|
||||
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
|
||||
/*
|
||||
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
|
||||
* must worry about is to protected-mode. And don't worry, there's no harm calling setProtMode()
|
||||
* Since the 80286 cannot return to real-mode via this instruction, the only transition
|
||||
* we must worry about is to protected-mode. And there's no harm calling setProtMode()
|
||||
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
|
||||
* case, but this instruction isn't used frequently enough to warrant it).
|
||||
*/
|
||||
|
|
@ -3127,11 +3181,34 @@ X86CPU.prototype.getIPLong = function()
|
|||
return l;
|
||||
};
|
||||
|
||||
/**
|
||||
* getIPAddr()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number} word at the current IP; IP advanced by 2 or 4, depending on address size
|
||||
*/
|
||||
X86CPU.prototype.getIPAddr = function()
|
||||
{
|
||||
/*
|
||||
* TODO: Add PREFETCH support to this function
|
||||
*/
|
||||
var w = this.getAddr(this.regLIP);
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
|
||||
}
|
||||
this.regLIP += this.addrSize;
|
||||
if (this.regLIP > this.regLIPLimit) {
|
||||
this.setIP(this.regLIP - this.segCS.base);
|
||||
}
|
||||
return w;
|
||||
};
|
||||
|
||||
/**
|
||||
* getIPWord()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {number} word at the current IP; IP advanced by 2 or 4
|
||||
* @return {number} word at the current IP; IP advanced by 2 or 4, depending on operand size
|
||||
*/
|
||||
X86CPU.prototype.getIPWord = function()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1042,7 +1042,7 @@ X86.fnLAR = function LAR(dst, src)
|
|||
X86.fnLCR0 = function LCR0(l)
|
||||
{
|
||||
this.regCR0 = l;
|
||||
this.setProtMode(!!(this.regCR0 & X86.CR0.MSW.PE));
|
||||
this.setProtMode();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ X86.opADDmb = function ADDmb()
|
|||
* point you can also choose to halt if Messages.HALT is enabled).
|
||||
*/
|
||||
if (DEBUG && !b) {
|
||||
this.printMessage("suspicious opcode: 0x00 0x00");
|
||||
this.printMessage("suspicious opcode: 0x00 0x00", DEBUGGER || this.bitsMessage);
|
||||
if (DEBUGGER) this.stopCPU();
|
||||
}
|
||||
this.aOpModMemByte[b].call(this, X86.fnADDb);
|
||||
};
|
||||
|
|
@ -2493,7 +2494,7 @@ X86.opLAHF = function LAHF()
|
|||
*/
|
||||
X86.opMOVALm = function MOVALm()
|
||||
{
|
||||
this.regEAX = (this.regEAX & ~0xff) | this.getSOByte(this.segData, this.getIPWord());
|
||||
this.regEAX = (this.regEAX & ~0xff) | this.getSOByte(this.segData, this.getIPAddr());
|
||||
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo;
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesMovAM;
|
||||
};
|
||||
|
|
@ -2505,7 +2506,7 @@ X86.opMOVALm = function MOVALm()
|
|||
*/
|
||||
X86.opMOVAXm = function MOVAXm()
|
||||
{
|
||||
this.regEAX = (this.regEAX & ~this.dataMask) | this.getSOWord(this.segData, this.getIPWord());
|
||||
this.regEAX = (this.regEAX & ~this.dataMask) | this.getSOWord(this.segData, this.getIPAddr());
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiAL = this.backTrack.btiMemLo; this.backTrack.btiAH = this.backTrack.btiMemHi;
|
||||
}
|
||||
|
|
@ -2523,7 +2524,7 @@ X86.opMOVmAL = function MOVmAL()
|
|||
/*
|
||||
* setSOByte() truncates the value as appropriate
|
||||
*/
|
||||
this.setSOByte(this.segData, this.getIPWord(), this.regEAX);
|
||||
this.setSOByte(this.segData, this.getIPAddr(), this.regEAX);
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesMovMA;
|
||||
};
|
||||
|
||||
|
|
@ -2540,7 +2541,7 @@ X86.opMOVmAX = function MOVmAX()
|
|||
/*
|
||||
* setSOWord() truncates the value as appropriate
|
||||
*/
|
||||
this.setSOWord(this.segData, this.getIPWord(), this.regEAX);
|
||||
this.setSOWord(this.segData, this.getIPAddr(), this.regEAX);
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesMovMA;
|
||||
};
|
||||
|
||||
|
|
@ -3188,7 +3189,7 @@ X86.opGRP2wn = function GRP2wn()
|
|||
*/
|
||||
X86.opRETn = function RETn()
|
||||
{
|
||||
var n = this.getIPWord() << (this.dataSize >> 2);
|
||||
var n = this.getIPShort() << (this.dataSize >> 2);
|
||||
var newIP = this.popWord();
|
||||
if (DEBUG) this.printMessage(" returning to " + str.toHex(this.segCS.sel, 4) + ':' + str.toHex(newIP, this.dataSize << 1), this.bitsMessage, true);
|
||||
this.setIP(newIP);
|
||||
|
|
@ -3285,7 +3286,7 @@ X86.opMOVw = function MOVw()
|
|||
*/
|
||||
X86.opENTER = function ENTER()
|
||||
{
|
||||
var wLocal = this.getIPWord();
|
||||
var wLocal = this.getIPShort();
|
||||
var bLevel = this.getIPByte() & 0x1f;
|
||||
/*
|
||||
* NOTE: 11 is the minimum cycle time for the 80286; the 80186/80188 has different cycle times: 15, 25 and
|
||||
|
|
@ -3330,7 +3331,7 @@ X86.opLEAVE = function LEAVE()
|
|||
*/
|
||||
X86.opRETFn = function RETFn()
|
||||
{
|
||||
X86.fnRETF.call(this, this.getIPWord());
|
||||
X86.fnRETF.call(this, this.getIPShort());
|
||||
this.nStepCycles -= this.cycleCounts.nOpCyclesRetFn;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue