Added separate dumpers for physical and linear address space, added more information to the TSS dump, fixed breakpoint updates when the address(es) are not yet valid, fixed recursion error when an UNPAGED block is accessed, made string instructions restartable after a fault, and enabled port-trapping in V86-mode.

This commit is contained in:
Jeff Parsons 2015-07-25 16:42:39 -07:00
commit fc9a2a903f
7 changed files with 191 additions and 146 deletions

View file

@ -1316,6 +1316,7 @@ if (DEBUGGER) {
}
this.messageDump(Messages.BUS, function onDumpBus(s) { dbg.dumpBus(s); });
this.messageDump(Messages.MEM, function onDumpMem(s) { dbg.dumpMem(s); });
this.messageDump(Messages.DESC, function onDumpDesc(s) { dbg.dumpDesc(s); });
this.messageDump(Messages.TSS, function onDumpTSS(s) { dbg.dumpTSS(s); });
this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); });
@ -1695,8 +1696,8 @@ if (DEBUGGER) {
* hexOffset(off, sel, fAddr32)
*
* @this {Debugger}
* @param {number|null} [off]
* @param {number|null} [sel]
* @param {number|null|undefined} [off]
* @param {number|null|undefined} [sel]
* @param {boolean} [fAddr32] is true for 32-bit ADDRESS size
* @return {string} the hex representation of off (or sel:off)
*/
@ -1723,7 +1724,7 @@ if (DEBUGGER) {
/**
* getSZ(dbgAddr, cchMax)
*
* Get zero-terminated (aka "ASCIIZ") string from dbgAddr. It also stops at the first '$', in case this is
* Gets zero-terminated (aka "ASCIIZ") string from dbgAddr. It also stops at the first '$', in case this is
* a '$'-terminated string -- mainly because I'm lazy and didn't feel like writing a separate get() function.
* Yes, a zero-terminated string containing a '$' will be prematurely terminated, and no, I don't care.
*
@ -1745,35 +1746,34 @@ if (DEBUGGER) {
};
/**
* dumpDOS(s)
* dumpDOS(sMCB)
*
* This dumps DOS MCBs (Memory Control Blocks).
* Dumps DOS MCBs (Memory Control Blocks).
*
* TODO: Add some code to detect the running version of DOS (if any) and locate the first MCB automatically.
*
* @this {Debugger}
* @param {string} [s]
* @param {string} [sMCB]
*/
Debugger.prototype.dumpDOS = function(s)
Debugger.prototype.dumpDOS = function(sMCB)
{
if (!s) {
this.println("no MCB");
var mcb;
if (sMCB) {
mcb = this.parseValue(sMCB);
}
if (mcb === undefined) {
this.println("invalid MCB");
return;
}
this.println("dumpDOS(" + s + ")");
/*
* If s is provided and str.parseInt(s) succeeds, then we assume it represents a starting
* MCB (Memory Control Block) segment, and we dump the corresponding blocks.
*/
var sel = this.parseValue(s);
while (sel) {
var dbgAddr = this.newAddr(0, sel);
this.println("dumpMCB(" + str.toHexWord(mcb) + ")");
while (mcb) {
var dbgAddr = this.newAddr(0, mcb);
var bSig = this.getByte(dbgAddr, 1);
var wPID = this.getShort(dbgAddr, 2);
var wParas = this.getShort(dbgAddr, 5);
if (bSig != 0x4D && bSig != 0x5A) break;
this.println(this.hexOffset(0, sel) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.getSZ(dbgAddr, 8) + '"');
sel += 1 + wParas;
this.println(this.hexOffset(0, mcb) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.getSZ(dbgAddr, 8) + '"');
mcb += 1 + wParas;
}
};
@ -1832,24 +1832,65 @@ if (DEBUGGER) {
};
/**
* dumpBus(s)
*
* This dumps Bus allocations.
* dumpBlocks(aBlocks, sAddr)
*
* @this {Debugger}
* @param {string} [s]
* @param {Array} aBlocks
* @param {string} [sAddr] (optional block address)
*/
Debugger.prototype.dumpBus = function(s)
Debugger.prototype.dumpBlocks = function(aBlocks, sAddr)
{
var i = 0, n = aBlocks.length;
this.println("id physaddr blkaddr used size type");
this.println("-------- --------- -------- ------ ------ ----");
for (var i = 0; i < this.cpu.aMemBlocks.length; i++) {
var block = this.cpu.aBusBlocks[i];
if (sAddr) {
var addr = this.parseValue(sAddr);
if (addr !== undefined) {
i = addr >>> this.cpu.nBlockShift;
n = 1;
}
}
while (n--) {
var block = aBlocks[i];
if (block.type === Memory.TYPE.NONE) continue;
this.println(str.toHex(block.id) + " %" + str.toHex(i << this.cpu.nBlockShift) + ": " + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + Memory.TYPE.NAMES[block.type]);
i++;
}
};
/**
* dumpBus(sAddr)
*
* Dumps Bus allocations.
*
* @this {Debugger}
* @param {string} [sAddr] (optional block address)
*/
Debugger.prototype.dumpBus = function(sAddr)
{
this.dumpBlocks(this.cpu.aBusBlocks, sAddr);
};
/**
* dumpMem(sAddr)
*
* Dumps page allocations.
*
* @this {Debugger}
* @param {string} [sAddr] (optional block address)
*/
Debugger.prototype.dumpMem = function(sAddr)
{
var aBlocks = this.cpu.aMemBlocks;
if (aBlocks === this.cpu.aBusBlocks) {
this.println("paging not enabled");
return;
}
this.dumpBlocks(aBlocks, sAddr);
};
Debugger.SYSDESCS = {
0x0100: ["tss286", false],
0x0200: ["ldt", false],
@ -1868,7 +1909,7 @@ if (DEBUGGER) {
/**
* dumpDesc(s)
*
* This dumps a descriptor for the given selector.
* Dumps a descriptor for the given selector.
*
* @this {Debugger}
* @param {string} [s]
@ -2058,12 +2099,12 @@ if (DEBUGGER) {
var iPort = 0;
off = (v >>> 16);
/*
* We arbitrarily cut the IOPM dump off at port 0x3FF, because we're not interested in anything above that.
* We arbitrarily cut the IOPM dump off at port 0x3FF; we're not currently interested in anything above that.
*/
while (off < seg.offMax && iPort < 0x3ff) {
addr = seg.base + off;
v = this.cpu.probeAddr(addr) | (this.cpu.probeAddr(addr + 1) << 8);
sDump += "\nports " + str.toHexWord(iPort) + '-' + str.toHexWord(iPort+15) + ": " + str.toBinBytes(v, 2);
sDump += "\n" + str.toHexWord(off) + " ports " + str.toHexWord(iPort) + '-' + str.toHexWord(iPort+15) + ": " + str.toBinBytes(v, 2);
iPort += 16;
off += 2;
}
@ -2615,7 +2656,7 @@ if (DEBUGGER) {
*/
if (fUpdateCPU !== false) this.cpu.updateCPU();
this.updateStatus(fRegs || false, false);
this.updateStatus(fRegs || false);
return (this.nCycles > 0);
};
@ -2631,16 +2672,14 @@ if (DEBUGGER) {
};
/**
* updateStatus(fRegs, fCompact)
* updateStatus(fRegs)
*
* @this {Debugger}
* @param {boolean} [fRegs] (default is true)
* @param {boolean} [fCompact] (default is true)
*/
Debugger.prototype.updateStatus = function(fRegs, fCompact)
Debugger.prototype.updateStatus = function(fRegs)
{
if (fRegs === undefined) fRegs = true;
if (fCompact === undefined) fCompact = true;
this.dbgAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS());
/*
@ -2651,7 +2690,7 @@ if (DEBUGGER) {
if (!fRegs || this.fProcStep == 1)
this.doUnassemble();
else {
this.doRegisters(null, fCompact);
this.doRegisters(null);
}
};
@ -2862,7 +2901,7 @@ if (DEBUGGER) {
}
this.println(sStopped);
}
this.updateStatus(true, this.fProcStep != 2);
this.updateStatus(true);
this.setFocus();
this.clearTempBreakpoint(this.cpu.regLIP);
}
@ -3053,6 +3092,8 @@ if (DEBUGGER) {
*/
Debugger.prototype.addBreakpoint = function(aBreak, dbgAddr, fTemp)
{
var fSuccess = false;
this.nSuppressBreaks++;
if (!this.findBreakpoint(aBreak, dbgAddr)) {
dbgAddr.fTempBreak = fTemp;
aBreak.push(dbgAddr);
@ -3071,9 +3112,10 @@ if (DEBUGGER) {
this.println("breakpoint enabled: " + this.hexAddr(dbgAddr) + " (" + aBreak[0] + ")");
}
if (!fTemp) this.historyInit();
return true;
fSuccess = true;
}
return false;
this.nSuppressBreaks--;
return fSuccess;
};
/**
@ -3091,7 +3133,8 @@ if (DEBUGGER) {
var addr = this.mapBreakpoint(this.getAddr(dbgAddr));
for (var i = 1; i < aBreak.length; i++) {
var dbgAddrBreak = aBreak[i];
if (addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak))) {
if (addr != X86.ADDR_INVALID && addr == this.mapBreakpoint(this.getAddr(dbgAddrBreak)) ||
addr == X86.ADDR_INVALID && dbgAddr.sel == dbgAddrBreak.sel && dbgAddr.off == dbgAddrBreak.off) {
fFound = true;
if (fRemove) {
aBreak.splice(i, 1);
@ -3206,8 +3249,10 @@ if (DEBUGGER) {
* because any CS-based breakpoint you set immediately after a CPU reset will have a physical address
* in the top 16Mb, yet after the first inter-segment JMP, you will be running in the first 1Mb.
*/
var mask = (this.maskAddr & ~0xffff);
if ((addr & mask) == mask) addr &= 0x000fffff;
if (addr != X86.ADDR_INVALID) {
var mask = (this.maskAddr & ~0xffff);
if ((addr & mask) == mask) addr &= 0x000fffff;
}
return addr;
};
@ -5148,13 +5193,12 @@ if (DEBUGGER) {
};
/**
* doRegisters(asArgs, fCompact)
* doRegisters(asArgs)
*
* @this {Debugger}
* @param {Array.<string>} [asArgs]
* @param {boolean} [fCompact]
*/
Debugger.prototype.doRegisters = function(asArgs, fCompact)
Debugger.prototype.doRegisters = function(asArgs)
{
if (asArgs && asArgs[1] == "?") {
this.println("register commands:");
@ -5378,7 +5422,6 @@ if (DEBUGGER) {
}
this.cpu.updateCPU();
this.println("updated registers:");
fCompact = true;
}
}
@ -5448,7 +5491,7 @@ if (DEBUGGER) {
this.fProcStep = fProcStep;
this.incAddr(dbgAddr, 1);
break;
case X86.OPCODE.INTn:
case X86.OPCODE.INTN:
case X86.OPCODE.LOOPNZ:
case X86.OPCODE.LOOPZ:
case X86.OPCODE.LOOP:

View file

@ -491,13 +491,11 @@ Memory.prototype = {
* @return {Memory}
*/
getPageBlock: function(addr, fWrite) {
var block = this.cpu.mapPageBlock(addr, fWrite);
/*
* If mapPageBlock() fails -- which can easily happen if the page is not present or has insufficient
* privileges -- then a fault will be triggered and block will be null. We still have to return a block,
* but it will be our old "unpaged" self.
* Even when mapPageBlock() fails (ie, when the page is not present or has insufficient privileges), it
* will trigger a fault (since we don't set fSuppress), but it will still return a block (ie, an empty block).
*/
return block || this;
return this.cpu.mapPageBlock(addr, fWrite);
},
/**
* setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE)

View file

@ -382,6 +382,7 @@ var X86 = {
NOREAD: 0x0001,
NOWRITE: 0x0002,
NOINTR: 0x0004, // indicates a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
FAULT: 0x0008, // indicates a fault occurred during the current instruction
SEG: 0x0010, // segment override
LOCK: 0x0020, // lock prefix
REPZ: 0x0040, // repeat while Z (NOTE: this value MUST match PS.ZF; see opCMPSb/opCMPSw/opSCASb/opSCASw)
@ -440,7 +441,7 @@ var X86 = {
SCASB: 0xAE, // opSCASb()
SCASW: 0xAF, // opSCASw()
INT3: 0xCC, // opINT3()
INTn: 0xCD, // opINTn()
INTN: 0xCD, // opINTn()
INTO: 0xCE, // opINTO()
LOOPNZ: 0xE0, // opLOOPNZ()
LOOPZ: 0xE1, // opLOOPZ()

View file

@ -677,7 +677,7 @@ X86CPU.prototype.setAddressMask = function(nBusMask)
* of the Bus block array, to simulate paging. Whenever the CPU turns paging off, disablePageBlocks()
* must be called to restore our copy of the Bus block array to its original (physical) mapping.
*
* This also requires PAGEBLOCKS be enabled, ensuring that the Bus is configured with a 4Kb block size.
* This also requires PAGEBLOCKS be enabled, to ensure the Bus is configured with a 4Kb block size.
*
* The first time this function is called, aMemBlocks and aBusBlocks are identical, so aMemBlocks is
* reinitialized with special UNPAGED Memory blocks that know how to perform page directory/page table
@ -690,7 +690,7 @@ X86CPU.prototype.setAddressMask = function(nBusMask)
X86CPU.prototype.enablePageBlocks = function()
{
if (!PAGEBLOCKS) {
this.setError("PAGEBLOCK support required");
this.setError("PAGEBLOCKS support required");
return;
}
if (this.aMemBlocks === this.aBusBlocks) {
@ -699,6 +699,11 @@ X86CPU.prototype.enablePageBlocks = function()
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
this.aMemBlocks[iBlock] = this.blockUnpaged;
}
/*
* We also need a special "empty" Memory block that mapPageBlock() can pass back to callers
* whenever a valid block cannot be found for an UNPAGED block.
*/
this.memEmpty = new Memory();
} else {
for (var i = 0; i < this.aBlocksPaged.length; i++) {
this.aMemBlocks[this.aBlocksPaged[i]] = this.blockUnpaged;
@ -708,7 +713,7 @@ X86CPU.prototype.enablePageBlocks = function()
};
/**
* mapPageBlock(addr, fWrite)
* mapPageBlock(addr, fWrite, fSuppress)
*
* Locate the corresponding physical PDE, PTE and memory blocks for the given linear address, and then
* upgrade the block from an UNPAGED Memory block to a new PAGED Memory block; all future accesses to
@ -737,7 +742,7 @@ X86CPU.prototype.enablePageBlocks = function()
* @param {number} addr is a linear address
* @param {boolean} fWrite (true if called for a write, false if for a read)
* @param {boolean} [fSuppress] (true if any faults, remapping, etc should be suppressed)
* @return {Memory|null}
* @return {Memory}
*/
X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
{
@ -753,12 +758,12 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
if (!(pde & X86.PTE.PRESENT)) {
if (!fSuppress) X86.fnPageFault.call(this, addr, false, fWrite);
return null;
return this.memEmpty;
}
if (!(pde & X86.PTE.USER) && this.segCS.cpl == 3) {
if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite);
return null;
return this.memEmpty;
}
var offPTE = (addr & X86.LADDR.PTE.MASK) >>> X86.LADDR.PTE.SHIFT;
@ -773,12 +778,12 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
if (!(pte & X86.PTE.PRESENT) && !fSuppress) {
if (!fSuppress) X86.fnPageFault.call(this, addr, false, fWrite);
return null;
return this.memEmpty;
}
if (!(pte & X86.PTE.USER) && this.segCS.cpl == 3) {
if (!fSuppress) X86.fnPageFault.call(this, addr, true, fWrite);
return null;
return this.memEmpty;
}
var addrPhys = (pte & X86.PTE.FRAME) + (addr & X86.LADDR.OFFSET);
@ -816,6 +821,7 @@ X86CPU.prototype.disablePageBlocks = function()
this.aMemBlocks = this.aBusBlocks;
this.blockUnpaged = null;
this.aBlocksPaged = null;
this.memEmpty = null;
}
};
@ -2756,7 +2762,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
X86CPU.prototype.checkIOPM = function(port, nPorts)
{
var bitsPorts = 0;
if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && this.segCS.cpl > this.nIOPL && this.segTSS.addrIOPM) {
if (I386 && (this.regCR0 & X86.CR0.MSW.PE) && (this.segCS.cpl > this.nIOPL || (this.regPS & X86.PS.VM)) && this.segTSS.addrIOPM) {
var offIOPM = port >>> 3;
var addrIOPM = this.segTSS.addrIOPM + offIOPM;
bitsPorts = ((1 << nPorts) - 1) << (port & 0x7);
@ -2767,7 +2773,12 @@ X86CPU.prototype.checkIOPM = function(port, nPorts)
addrIOPM++;
}
}
return !bitsPorts;
if (bitsPorts) {
if (this.messageEnabled(Messages.PORT)) this.printMessage("checkIOPM(" + str.toHexWord(port) + "," + nPorts + "): trapped", true, true);
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, false);
return false;
}
return true;
};
/**

View file

@ -3679,11 +3679,13 @@ X86.fnSrcNone = function SrcNone()
* @this {X86CPU}
* @param {number} nFault
* @param {number} [nError] (if omitted, no error code will be pushed)
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
* @param {boolean} [fHalt] true to halt the CPU (if the Debugger is loaded), false to not, undefined if "it depends"
* @param {number} [nCycles] cycle count to pass through to fnINT(), if any
*/
X86.fnFault = function(nFault, nError, fHalt, nCycles)
{
this.opFlags |= X86.OPFLAG.FAULT;
if (!this.aFlags.fComplete) {
this.printMessage("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Messages.WARN);
this.setIP(this.opLIP - this.segCS.base);
@ -3774,7 +3776,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
* @this {X86CPU}
* @param {number} nFault
* @param {number} [nError] (if omitted, no error code will be reported)
* @param {boolean} [fHalt] true if the CPU should always be halted, false if "it depends"
* @param {boolean} [fHalt] true to halt the CPU (if the Debugger is loaded), false to not, undefined if "it depends"
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
*/
X86.fnFaultMessage = function(nFault, nError, fHalt)
@ -3798,15 +3800,31 @@ X86.fnFaultMessage = function(nFault, nError, fHalt)
*/
if (bOpcode == X86.OPCODE.INT3 && !this.addrIDTLimit) {
fHalt = false;
bitsMessage |= Messages.CPU;
}
/*
* Windows 3.00 (and other versions of enhanced-mode Windows) use an ARPL in V86-mode to switch out
* of V86-mode; we don't need to report the UD_FAULT by default.
* There are a number of V86-mode exceptions we don't need to know about. For starters, Windows 3.00
* (and other versions of enhanced-mode Windows) use an ARPL in V86-mode to switch out of V86-mode, so
* we can ignore those UD_FAULTs.
*
* Ditto for software interrupts, which will generate a GP_FAULT when the interrupt number (eg, 0x6D)
* exceeds the protected-mode IDT's limit (eg, a limit of 0x2FF yields a maximum interrupt number of 0x5F).
* Windows doesn't really care if its IDT is too small, because no matter what, it has to simulate all
* software interrupts in V86-mode (they will also generate a GP_FAULT if IOPL < 3, and even if IOPL == 3,
* only the protected-mode IDT handler gets to run).
*/
if (bOpcode == X86.OPCODE.ARPL && (this.regPS & X86.PS.VM)) {
fHalt = false;
if ((this.regPS & X86.PS.VM)) {
if (nFault == X86.EXCEPTION.UD_FAULT && bOpcode == X86.OPCODE.ARPL ||
nFault == X86.EXCEPTION.GP_FAULT && bOpcode == X86.OPCODE.INTN) {
fHalt = false;
}
}
/*
* If fHalt has been explicitly set to false, we also take that as a cue to disable fault messages
* (which you can override by turning on CPU messages).
*/
if (fHalt === false) {
bitsMessage |= Messages.CPU;
}
@ -3822,16 +3840,13 @@ X86.fnFaultMessage = function(nFault, nError, fHalt)
/*
* However, the foregoing notwithstanding, if MESSAGE.HALT is enabled along with all the other required
* MESSAGE bits, then we want to halt regardless.
*
* TODO: Eventually remove the code below that halts on all MODEL_80386 GP_FAULTs and PG_FAULTs; this is
* just to make it easier to catch bad faults on DeskPro 386 configurations.
*/
if (DEBUGGER && this.model == X86.MODEL_80386 && (nFault == X86.EXCEPTION.GP_FAULT || nFault == X86.EXCEPTION.PG_FAULT) || this.messageEnabled(bitsMessage | Messages.HALT)) {
if (this.messageEnabled(bitsMessage | Messages.HALT)) {
fHalt = true;
}
if (this.messageEnabled(bitsMessage) || fHalt) {
var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")";
var sMessage = "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")";
var fRunning = this.aFlags.fRunning;
if (this.printMessage(sMessage, bitsMessage)) {
if (fHalt) {

View file

@ -1503,13 +1503,11 @@ X86.opINSb = function INSb()
if (nReps--) {
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
var b = this.bus.checkPortInputNotify(port, this.regLIP - nDelta - 1);
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO;
this.setSOByte(this.segES, this.regEDI & this.addrMask, b);
if (this.opFlags & X86.OPFLAG.FAULT) return;
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiIO;
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
@ -1556,10 +1554,7 @@ X86.opINSw = function INSw()
var addrFrom = this.regLIP - nDelta - 1;
var w = 0, shift = 0;
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
for (var n = 0; n < this.dataSize; n++) {
w |= this.bus.checkPortInputNotify(port, addrFrom) << shift;
shift += 8;
@ -1572,6 +1567,7 @@ X86.opINSw = function INSw()
}
}
this.setSOWord(this.segES, this.regEDI & this.addrMask, w);
if (this.opFlags & X86.OPFLAG.FAULT) return;
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
@ -1615,11 +1611,9 @@ X86.opOUTSb = function OUTSb()
}
if (nReps--) {
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
var b = this.getSOByte(this.segDS, this.regESI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiMemLo;
this.bus.checkPortOutputNotify(port, b, this.regLIP - nDelta - 1);
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
@ -1665,12 +1659,10 @@ X86.opOUTSw = function OUTSw()
}
if (nReps--) {
var w = this.getSOWord(this.segDS, this.regESI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
var addrFrom = this.regLIP - nDelta - 1, shift = 0;
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
for (var n = 0; n < this.dataSize; n++) {
if (BACKTRACK) {
if (!n) {
@ -2609,8 +2601,9 @@ X86.opMOVSb = function MOVSb()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesMovSr0;
}
if (nReps--) {
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
this.setSOByte(this.segES, this.regEDI & this.addrMask, this.getSOByte(this.segData, this.regESI & this.addrMask));
if (this.opFlags & X86.OPFLAG.FAULT) return;
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask);
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask);
this.nStepCycles -= nCycles;
@ -2644,8 +2637,9 @@ X86.opMOVSw = function MOVSw()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesMovSr0;
}
if (nReps--) {
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
this.setSOWord(this.segES, this.regEDI & this.addrMask, this.getSOWord(this.segData, this.regESI & this.addrMask));
if (this.opFlags & X86.OPFLAG.FAULT) return;
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask);
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask);
this.nStepCycles -= nCycles;
@ -2679,17 +2673,18 @@ X86.opCMPSb = function CMPSb()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesCmpSr0;
}
if (nReps--) {
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
var bDst = this.getEAByte(this.segData, this.regESI & this.addrMask);
var bSrc = this.modEAByte(this.segES, this.regEDI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
X86.fnCMPb.call(this, bDst, bSrc);
var nInc = ((this.regPS & X86.PS.DF)? -1 : 1);
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask);
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* NOTE: As long as we're calling fnCMPb(), all our cycle times must be reduced by nOpCyclesArithRM
*/
this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is
* set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those
@ -2724,17 +2719,18 @@ X86.opCMPSw = function CMPSw()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesCmpSr0;
}
if (nReps--) {
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
var wDst = this.getEAWord(this.segData, this.regESI & this.addrMask);
var wSrc = this.modEAWord(this.segES, this.regEDI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
X86.fnCMPw.call(this, wDst, wSrc);
var nInc = ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize);
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + nInc) & this.addrMask);
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + nInc) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* NOTE: As long as we're calling fnCMPw(), all our cycle times must be reduced by nOpCyclesArithRM
*/
this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is
* set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those
@ -2793,11 +2789,12 @@ X86.opSTOSb = function STOSb()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesStoSr0;
}
if (nReps--) {
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiAL;
this.setSOByte(this.segES, this.regEDI & this.addrMask, this.regEAX);
if (this.opFlags & X86.OPFLAG.FAULT) return;
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiAL;
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
@ -2829,17 +2826,14 @@ X86.opSTOSw = function STOSw()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesStoSr0;
}
if (nReps--) {
/*
* NOTE: Storing a word imposes another 4-cycle penalty on the 8088, so consider that
* if you think the cycle times here are too high.
*/
this.setSOWord(this.segES, this.regEDI & this.addrMask, this.regEAX);
if (this.opFlags & X86.OPFLAG.FAULT) return;
if (BACKTRACK) {
this.backTrack.btiMemLo = this.backTrack.btiAL; this.backTrack.btiMemHi = this.backTrack.btiAH;
}
this.setSOWord(this.segES, this.regEDI & this.addrMask, this.regEAX);
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(-2); // this instruction does not support multiple overrides
@ -2869,11 +2863,13 @@ X86.opLODSb = function LODSb()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesLodSr0;
}
if (nReps--) {
this.regEAX = (this.regEAX & ~0xff) | this.getSOByte(this.segData, this.regESI & this.addrMask);
var b = this.getSOByte(this.segData, this.regESI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
this.regEAX = (this.regEAX & ~0xff) | b;
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo;
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
@ -2903,13 +2899,15 @@ X86.opLODSw = function LODSw()
if (!(this.opPrefixes & X86.OPFLAG.REPEAT)) this.nStepCycles -= this.cycleCounts.nOpCyclesLodSr0;
}
if (nReps--) {
this.regEAX = (this.regEAX & ~this.dataMask) | this.getSOWord(this.segData, this.regESI & this.addrMask);
var w = this.getSOWord(this.segData, this.regESI & this.addrMask);
if (this.opFlags & X86.OPFLAG.FAULT) return;
this.regEAX = (this.regEAX & ~this.dataMask) | w;
if (BACKTRACK) {
this.backTrack.btiAL = this.backTrack.btiMemLo; this.backTrack.btiAH = this.backTrack.btiMemHi;
}
this.regESI = (this.regESI & ~this.addrMask) | ((this.regESI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
this.nStepCycles -= nCycles;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
this.nStepCycles -= nCycles;
if (nReps) {
if (BUGS_8086) {
this.rewindIP(((this.opPrefixes & X86.OPFLAG.SEG)? -3 : -2));
@ -2940,12 +2938,13 @@ X86.opSCASb = function SCASb()
}
if (nReps--) {
X86.fnCMPb.call(this, this.regEAX & 0xff, this.modEAByte(this.segES, this.regEDI & this.addrMask));
if (this.opFlags & X86.OPFLAG.FAULT) return;
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -1 : 1)) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* NOTE: As long as we're calling fnCMPb(), all our cycle times must be reduced by nOpCyclesArithRM
*/
this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is
* set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those
@ -2981,12 +2980,13 @@ X86.opSCASw = function SCASw()
}
if (nReps--) {
X86.fnCMPw.call(this, this.regEAX & this.dataMask, this.modEAWord(this.segES, this.regEDI & this.addrMask));
if (this.opFlags & X86.OPFLAG.FAULT) return;
this.regEDI = (this.regEDI & ~this.addrMask) | ((this.regEDI + ((this.regPS & X86.PS.DF)? -this.dataSize : this.dataSize)) & this.addrMask);
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* NOTE: As long as we're calling fnCMPw(), all our cycle times must be reduced by nOpCyclesArithRM
*/
this.nStepCycles -= nCycles - this.cycleCounts.nOpCyclesArithRM;
this.regECX = (this.regECX & ~this.addrMask) | ((this.regECX - nDelta) & this.addrMask);
/*
* Repetition continues while ZF matches bit 0 of the REP prefix. getZF() returns 0x40 if ZF is
* set, and OP_REPZ (which represents the REP prefix whose bit 0 is set) is 0x40 as well, so when those
@ -3692,10 +3692,7 @@ X86.opJCXZ = function JCXZ()
X86.opINb = function INb()
{
var port = this.getIPByte();
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
this.regEAX = (this.regEAX & ~0xff) | this.bus.checkPortInputNotify(port, this.regLIP - 2);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.nStepCycles -= this.cycleCounts.nOpCyclesInP;
@ -3709,10 +3706,7 @@ X86.opINb = function INb()
X86.opINw = function INw()
{
var port = this.getIPByte();
if (!this.checkIOPM(port, 2)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 2)) return;
this.regEAX = this.bus.checkPortInputNotify(port, this.regLIP - 2);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
/*
@ -3733,10 +3727,7 @@ X86.opINw = function INw()
X86.opOUTb = function OUTb()
{
var port = this.getIPByte();
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 2);
this.nStepCycles -= this.cycleCounts.nOpCyclesOutP;
};
@ -3749,10 +3740,7 @@ X86.opOUTb = function OUTb()
X86.opOUTw = function OUTw()
{
var port = this.getIPByte();
if (!this.checkIOPM(port, 2)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 2)) return;
this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 2);
/*
* TODO: Specs are clear that bits 8-15 of the port address for the FIRST byte of I/O will be zero, but
@ -3824,10 +3812,7 @@ X86.opJMPs = function JMPs()
X86.opINDXb = function INDXb()
{
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
this.regEAX = (this.regEAX & ~0xff) | this.bus.checkPortInputNotify(port, this.regLIP - 1);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.nStepCycles -= this.cycleCounts.nOpCyclesInDX;
@ -3841,10 +3826,7 @@ X86.opINDXb = function INDXb()
X86.opINDXw = function INDXw()
{
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 2)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 2)) return;
this.regEAX = this.bus.checkPortInputNotify(port, this.regLIP - 1);
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiIO;
this.regEAX |= (this.bus.checkPortInputNotify((port + 1) & 0xffff, this.regLIP - 1) << 8);
@ -3860,10 +3842,7 @@ X86.opINDXw = function INDXw()
X86.opOUTDXb = function OUTDXb()
{
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 1)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 1)) return;
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAL;
this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 1);
this.nStepCycles -= this.cycleCounts.nOpCyclesOutDX;
@ -3877,10 +3856,7 @@ X86.opOUTDXb = function OUTDXb()
X86.opOUTDXw = function OUTDXw()
{
var port = this.regEDX & 0xffff;
if (!this.checkIOPM(port, 2)) {
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
if (!this.checkIOPM(port, 2)) return;
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAL;
this.bus.checkPortOutputNotify(port, this.regEAX & 0xff, this.regLIP - 1);
if (BACKTRACK) this.backTrack.btiIO = this.backTrack.btiAH;

View file

@ -60,6 +60,7 @@ if (typeof module !== 'undefined') {
* @property {number} base (in protected-mode, this comes from descriptor word 0x2)
* @property {number} acc (in protected-mode, this comes from descriptor word 0x4; bits 0-7 supplement base bits 16-23)
* @property {number} ext (in protected-mode, this is descriptor word 0x6, 80386 only; supplements limit bits 16-19 and base bits 24-31)
* @property {number} type (this is a subset of acc, using X86.DESC.ACC.TYPE.MASK)
*
* TODO: Determine what good, if any, these class annotations are for either an IDE like WebStorm or a tool like
* the Closure Compiler. More importantly, what good do they do at runtime? Is it better to simply ensure that all
@ -763,7 +764,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
* last valid address in segTSS.addrIOPMLimit.
*/
if (typeTSS == X86.DESC.ACC.TYPE.TSS386) {
this.addrIOPM = (base + cpu.getShort(base + X86.TSS386.TASK_IOPM))|0;
this.addrIOPM = (base + cpu.getShort(base + X86.TSS386.TASK_IOPM + 2))|0;
this.addrIOPMLimit = (base + this.limit)|0;
}
}