From ee2f5ff761cfeb5dd4384a3d73b2751feaf3daf8 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Thu, 3 Sep 2015 09:07:40 -0700 Subject: [PATCH] Revamped dump extensions to accept argument arrays, added probeDesc() for Debugger-safe segment descriptor loading --- modules/pcjs/lib/chipset.js | 19 +-- modules/pcjs/lib/debugger.js | 214 +++++++++++++++++++-------------- modules/pcjs/lib/hdc.js | 43 +------ modules/pcjs/lib/interrupts.js | 31 ++++- modules/pcjs/lib/video.js | 94 ++++++++------- modules/pcjs/lib/x86cpu.js | 58 +++++---- modules/pcjs/lib/x86func.js | 5 + modules/pcjs/lib/x86ops.js | 19 ++- modules/pcjs/lib/x86seg.js | 76 ++++++++++-- 9 files changed, 340 insertions(+), 219 deletions(-) diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js index 1d8176777..3a54af4b5 100644 --- a/modules/pcjs/lib/chipset.js +++ b/modules/pcjs/lib/chipset.js @@ -1060,8 +1060,8 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg) dbg.messageDump(Messages.PIC, function onDumpPIC() { chipset.dumpPIC(); }); - dbg.messageDump(Messages.TIMER, function onDumpTimer(sParm) { - chipset.dumpTimer(sParm); + dbg.messageDump(Messages.TIMER, function onDumpTimer(asArgs) { + chipset.dumpTimer(asArgs); }); dbg.messageDump(Messages.CMOS, function onDumpCMOS() { chipset.dumpCMOS(); @@ -2344,16 +2344,17 @@ ChipSet.prototype.dumpPIC = function() }; /** - * dumpTimer(sParm) + * dumpTimer(asArgs) * * Use "d timer" to dump all timers, or "d timer n" to dump only timer n. * * @this {ChipSet} - * @param {string} [sParm] + * @param {Array.} asArgs */ -ChipSet.prototype.dumpTimer = function(sParm) +ChipSet.prototype.dumpTimer = function(asArgs) { if (DEBUGGER) { + var sParm = asArgs[0]; var nTimer = (sParm? +sParm : null); for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) { if (nTimer != null && iTimer != nTimer) continue; @@ -2909,7 +2910,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit) channel.sAddrDebug = str.toHex(addr >> 4, 4) + ":" + str.toHex(addr & 0xf, 4); 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); + this.dbg.doDump(["db", channel.sAddrDebug, 'l', channel.cbDebug]); } } if (channel.type == ChipSet.DMA_MODE.TYPE_WRITE) { @@ -3024,7 +3025,7 @@ ChipSet.prototype.updateDMA = function(channel) 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); + this.dbg.doDump(["db", channel.sAddrDebug, 'l', channel.cbDebug]); } if (channel.done) { @@ -3172,7 +3173,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom) this.checkIRR(); } else { if (DEBUG && this.messageEnabled(Messages.PIC | Messages.WARN)) { - this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", true); + this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", true, true); if (!SAMPLER && MAXDEBUG) this.dbg.stopCPU(); } } @@ -4966,7 +4967,7 @@ ChipSet.prototype.outCoprocReset = function(port, bOut, addrFrom) ChipSet.prototype.intBIOSRTC = function(addr) { if (DEBUGGER) { - if (this.messageEnabled(Messages.RTC) && this.dbg.messageInt(Interrupts.RTC, addr)) { + if (this.messageEnabled(Messages.INT) && this.dbg.messageInt(Interrupts.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. diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 96e67ff06..6b1254804 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -259,12 +259,19 @@ if (DEBUGGER) { 0x13: Messages.FDC, 0x15: Messages.CHIPSET, 0x16: Messages.KEYBOARD, - // 0x1a: Messages.RTC, // ChipSet contains its own custom messageInt() handler for the RTC - 0x1c: Messages.TIMER, + // 0x1A: Messages.RTC, // ChipSet contains its own custom messageInt() handler for the RTC + 0x1C: Messages.TIMER, 0x21: Messages.DOS, 0x33: Messages.MOUSE }; + /* + * Information regarding "annoying" interrupts (which aren't annoying so much as too frequent); + * note that some of these can still be enabled if you really want them (eg, RTC can be turned on + * with RTC messages, ALT_TIMER with TIMER messages, etc). + */ + Debugger.INT_ANNOYING = [Interrupts.RTC, Interrupts.ALT_TIMER, Interrupts.DOS_IDLE, Interrupts.DOS_NETBIOS, Interrupts.ALT_VIDEO]; + Debugger.COMMANDS = { '?': "help/print", 'a [#]': "assemble", @@ -1258,11 +1265,11 @@ 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); }); + this.messageDump(Messages.BUS, function onDumpBus(asArgs) { dbg.dumpBus(asArgs); }); + this.messageDump(Messages.MEM, function onDumpMem(asArgs) { dbg.dumpMem(asArgs); }); + this.messageDump(Messages.DESC, function onDumpDesc(asArgs) { dbg.dumpDesc(asArgs); }); + this.messageDump(Messages.TSS, function onDumpTSS(asArgs) { dbg.dumpTSS(asArgs); }); + this.messageDump(Messages.DOS, function onDumpDOS(asArgs) { dbg.dumpDOS(asArgs); }); if (Interrupts.WINDBG.ENABLED) { this.fWinDbg = null; @@ -1633,7 +1640,7 @@ if (DEBUGGER) { seg.limit = 0xffff; // although an ACTUAL real-mode segment load would not modify the limit, seg.offMax = 0x10000; // proper segDebugger operation requires that we update the limit ourselves } else { - seg.loadProt(sel); + seg.probeDesc(sel); } return seg; }; @@ -1659,6 +1666,9 @@ if (DEBUGGER) { if (addr == null) { addr = X86.ADDR_INVALID; if (dbgAddr) { + /* + * TODO: We should try to cache the seg inside dbgAddr, to avoid unnecessary calls to getSegment(). + */ var seg = this.getSegment(dbgAddr.sel, dbgAddr.type); if (seg) { if (!fWrite) { @@ -1987,18 +1997,19 @@ if (DEBUGGER) { }; /** - * dumpDOS(sMCB) + * dumpDOS(asArgs) * * Dumps DOS MCBs (Memory Control Blocks). * * TODO: Add some code to detect the current version of DOS (if any) and locate the first MCB automatically. * * @this {Debugger} - * @param {string} [sMCB] + * @param {Array.} asArgs */ - Debugger.prototype.dumpDOS = function(sMCB) + Debugger.prototype.dumpDOS = function(asArgs) { var mcb; + var sMCB = asArgs[0]; if (sMCB) { mcb = this.parseValue(sMCB); } @@ -2044,35 +2055,37 @@ if (DEBUGGER) { 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]); + if (block.type !== Memory.TYPE.NONE) { + 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) + * dumpBus(asArgs) * * Dumps Bus allocations. * * @this {Debugger} - * @param {string} [sAddr] (optional block address) + * @param {Array.} asArgs (asArgs[0] is an optional block address) */ - Debugger.prototype.dumpBus = function(sAddr) + Debugger.prototype.dumpBus = function(asArgs) { - this.dumpBlocks(this.cpu.aBusBlocks, sAddr); + this.dumpBlocks(this.cpu.aBusBlocks, asArgs[0]); }; /** - * dumpInfo(sAddr) + * dumpInfo(asArgs) * * @this {Debugger} - * @param {string|undefined} sAddr + * @param {Array.} asArgs */ - Debugger.prototype.dumpInfo = function(sAddr) + Debugger.prototype.dumpInfo = function(asArgs) { var sInfo = "no information"; if (BACKTRACK) { + var sAddr = asArgs[0]; var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true, true); if (dbgAddr) { var addr = this.getAddr(dbgAddr); @@ -2097,21 +2110,21 @@ if (DEBUGGER) { }; /** - * dumpMem(sAddr) + * dumpMem(asArgs) * * Dumps page allocations. * * @this {Debugger} - * @param {string} [sAddr] (optional block address) + * @param {Array.} asArgs (asArgs[0] is an optional block address) */ - Debugger.prototype.dumpMem = function(sAddr) + Debugger.prototype.dumpMem = function(asArgs) { var aBlocks = this.cpu.aMemBlocks; if (aBlocks === this.cpu.aBusBlocks) { this.println("paging not enabled"); return; } - this.dumpBlocks(aBlocks, sAddr); + this.dumpBlocks(aBlocks, asArgs[0]); }; Debugger.SYSDESCS = { @@ -2130,23 +2143,25 @@ if (DEBUGGER) { }; /** - * dumpDesc(s) + * dumpDesc(asArgs) * * Dumps a descriptor for the given selector. * * @this {Debugger} - * @param {string} [s] + * @param {Array.} asArgs */ - Debugger.prototype.dumpDesc = function(s) + Debugger.prototype.dumpDesc = function(asArgs) { - if (!s) { + var sSel = asArgs[0]; + + if (!sSel) { this.println("no selector"); return; } - var sel = this.parseValue(s); + var sel = this.parseValue(sSel); if (sel === undefined) { - this.println("invalid selector: " + s); + this.println("invalid selector: " + sSel); return; } @@ -2360,22 +2375,24 @@ if (DEBUGGER) { }; /** - * dumpTSS(s) + * dumpTSS(asArgs) * * This dumps a TSS using the given selector. If none is specified, the current TR is used. * * @this {Debugger} - * @param {string} [s] + * @param {Array.} asArgs */ - Debugger.prototype.dumpTSS = function(s) + Debugger.prototype.dumpTSS = function(asArgs) { var seg; - if (!s) { + var sSel = asArgs[0]; + + if (!sSel) { seg = this.cpu.segTSS; } else { - var sel = this.parseValue(s); + var sel = this.parseValue(sSel); if (sel === undefined) { - this.println("invalid task selector: " + s); + this.println("invalid task selector: " + sSel); return; } seg = this.getSegment(sel, Debugger.ADDR.PROT); @@ -2449,7 +2466,7 @@ if (DEBUGGER) { * * @this {Debugger} * @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 + * @param {function(Array.)} fnDumper is a function the Debugger can use to dump data for that category * @return {boolean} true if successfully registered, false if not */ Debugger.prototype.messageDump = function(bitMessage, fnDumper) @@ -2765,7 +2782,7 @@ if (DEBUGGER) { Debugger.prototype.message = function(sMessage, fAddress) { if (fAddress) { - sMessage += " @" + this.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " (%" + str.toHex(this.cpu.regLIP, 6) + ")"; + sMessage += " @" + this.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " (%" + str.toHex(this.cpu.regLIP) + ")"; } if (this.sMessagePrev && sMessage == this.sMessagePrev) return; @@ -2801,17 +2818,39 @@ if (DEBUGGER) { */ Debugger.prototype.messageInt = function(nInt, addr, fForce) { - var fMessage, AH, DL; - if (fForce) { - fMessage = true; - } else { - fMessage = this.messageEnabled(Messages.CPU) && nInt != Interrupts.DOS_IDLE /* 0x28 */ && nInt != Interrupts.DOS_NETBIOS /* 0x2A */; - var nCategory = Debugger.INT_MESSAGES[nInt]; - if (nCategory) { - if (this.messageEnabled(nCategory)) { - fMessage = true; - } else { - fMessage = (nCategory == Messages.FDC && this.messageEnabled(nCategory = Messages.HDC)); + var AH, DL; + var fMessage = fForce; + + /* + * We currently arrive here only because the CPU has already determined that INT messages are enabled, + * or because the ChipSet's RTC interrupt handler has already determined that INT messages are enabled. + * + * But software interrupts are very common, so we generally require additional categories to be enabled; + * unless the caller has set fForce, we check those additional categories now. + */ + if (!fMessage) { + /* + * Display all software interrupts if CPU messages are enabled (and it's not an "annoying" interrupt); + * note that in some cases, even "annoying" interrupts can be turned with an extra message category. + */ + fMessage = this.messageEnabled(Messages.CPU) && Debugger.INT_ANNOYING.indexOf(nInt) < 0; + if (!fMessage) { + /* + * Alternatively, display this software interrupt if its corresponding message category is enabled. + */ + var nCategory = Debugger.INT_MESSAGES[nInt]; + if (nCategory) { + if (this.messageEnabled(nCategory)) { + fMessage = true; + } else { + /* + * Alternatively, display this FDC interrupt if HDC messages are enabled (since they share + * a common software interrupt). Normally, an HDC BIOS will copy the original DISK (0x13) + * vector to the ALT_DISK (0x40) vector, but it's a nuisance having to check different + * interrupts in different configurations for the same frickin' functionality, so we don't. + */ + fMessage = (nCategory == Messages.FDC && this.messageEnabled(nCategory = Messages.HDC)); + } } } } @@ -3890,7 +3929,7 @@ if (DEBUGGER) { } var typeCPU = null; - var fNonPrefix = true; + var fComplete = true; for (var iOperand = 1; iOperand <= cOperands; iOperand++) { @@ -3906,7 +3945,7 @@ if (DEBUGGER) { continue; } if (typeSize == Debugger.TYPE_PREFIX) { - fNonPrefix = false; + fComplete = false; continue; } var typeMode = type & Debugger.TYPE_MODE; @@ -4004,7 +4043,7 @@ if (DEBUGGER) { sComment = Debugger.CPUS[typeCPU] + " CPU only"; } - if (sComment && fNonPrefix) { + if (sComment && fComplete) { sLine = str.pad(sLine, dbgAddrIns.fAddr32? 74 : 56) + ';' + sComment; if (!this.cpu.aFlags.fChecksum) { sLine += (nSequence != null? '=' + nSequence.toString() : ""); @@ -4014,7 +4053,7 @@ if (DEBUGGER) { } } - this.initAddrSize(dbgAddr, fNonPrefix, cOverrides); + this.initAddrSize(dbgAddr, fComplete, cOverrides); return sLine; }; @@ -5340,21 +5379,22 @@ if (DEBUGGER) { }; /** - * doDump(sCmd, sAddr, sLen, sBytes) + * doDump(asArgs) * - * sLen is interpreted as a number of bytes, in hex, which we convert to the appropriate number of lines, - * because we always display whole lines. If sLen is omitted/undefined, sLen defaults to 0x80 (128.) bytes, - * which normally translates to 8 lines. + * The length parameter is interpreted as a number of bytes, in hex, which we convert to the appropriate number + * of lines, because we always display whole lines. If the length is omitted/undefined, it defaults to 0x80 (128.) + * bytes, which normally translates to 8 lines. * * @this {Debugger} - * @param {string} sCmd - * @param {string|undefined} sAddr - * @param {string|undefined} [sLen] (# of bytes to dump, in hex; default is 0x80) - * @param {string|undefined} [sBytes] (this is checked only if sLen was an 'l', in honor of old DEBUG.COM syntax) + * @param {Array.} asArgs (formerly sCmd, [sAddr], [sLen] and [sBytes]) */ - Debugger.prototype.doDump = function(sCmd, sAddr, sLen, sBytes) + Debugger.prototype.doDump = function(asArgs) { var m; + var sCmd = asArgs[0]; + var sAddr = asArgs[1]; + var sLen = asArgs[2]; + var sBytes = asArgs[3]; if (sAddr == '?') { var sDumpers = ""; @@ -5420,9 +5460,8 @@ if (DEBUGGER) { } if (sCmd == "ds") { // transform a "ds" command into a "d desc" command - sCmd = "d"; - sLen = sAddr; - sAddr = "desc"; + sCmd = 'd'; + asArgs = [sCmd, "desc", sAddr]; } if (sCmd == 'd') { @@ -5430,7 +5469,9 @@ if (DEBUGGER) { if (sAddr == m) { var fnDumper = this.afnDumpers[m]; if (fnDumper) { - fnDumper(sLen); + asArgs.shift(); + asArgs.shift(); + fnDumper(asArgs); } else { this.println("no dump registered for " + sAddr); } @@ -5448,7 +5489,8 @@ if (DEBUGGER) { } if (sCmd == "di") { - var sInfo = this.dumpInfo(sAddr); + asArgs.shift(); + var sInfo = this.dumpInfo(asArgs); this.println(sInfo); return; } @@ -6403,11 +6445,6 @@ if (DEBUGGER) { /** * doRun(sAddr, sOptions, fQuiet) * - * NOTE: We assume that whenever we're being called with fQuiet set to true, that we're being - * called in the context of checkBreakpoint(), and therefore the CPU is already running, and - * therefore we can skip the runCPU() call -- because if we don't, then breakpoint commands that - * include the "g" command will produce unwanted "noise". - * * @this {Debugger} * @param {string} sAddr * @param {string} [sOptions] @@ -6668,36 +6705,38 @@ if (DEBUGGER) { }; /** - * initAddrSize(dbgAddr, fNonPrefix, cOverrides) + * initAddrSize(dbgAddr, fComplete, cOverrides) * * @this {Debugger} * @param {DbgAddr} dbgAddr - * @param {boolean} fNonPrefix + * @param {boolean} fComplete * @param {number} [cOverrides] */ - Debugger.prototype.initAddrSize = function(dbgAddr, fNonPrefix, cOverrides) + Debugger.prototype.initAddrSize = function(dbgAddr, fComplete, cOverrides) { /* - * Use cOverrides to record whether we previously processed any OPERAND or ADDRESS overrides. + * We use dbgAddr.fComplete to record whether or not the caller (ie, getInstruction()) + * processed a complete instruction. */ - dbgAddr.cOverrides = cOverrides || 0; + dbgAddr.fComplete = fComplete; /* * For proper disassembly of instructions preceded by an OPERAND (0x66) size prefix, we set * dbgAddr.fData32 to true whenever the operand size is 32-bit; similarly, for an ADDRESS (0x67) - * size prefix, we set dbgAddr.fAddr32 to true whenever the address size is 32-bit. Initially, - * both fields must be set to match the size of the current code segment. + * size prefix, we set dbgAddr.fAddr32 to true whenever the address size is 32-bit. + * + * Initially (and every time we've processed a complete instruction), both fields must be + * set to their original value. */ - if (fNonPrefix) { + if (fComplete) { if (dbgAddr.fData32Orig != null) dbgAddr.fData32 = dbgAddr.fData32Orig; if (dbgAddr.fAddr32Orig != null) dbgAddr.fAddr32 = dbgAddr.fAddr32Orig; dbgAddr.fData32Orig = dbgAddr.fData32; dbgAddr.fAddr32Orig = dbgAddr.fAddr32; } /* - * We also use dbgAddr.fComplete to record whether the caller (ie, getInstruction()) is reporting that - * it processed a complete instruction (ie, a non-prefix) or not. + * Use cOverrides to record whether we previously processed any OPERAND or ADDRESS overrides. */ - dbgAddr.fComplete = fNonPrefix; + dbgAddr.cOverrides = cOverrides || 0; }; /** @@ -6727,10 +6766,10 @@ if (DEBUGGER) { if (n === undefined) n = 1; - var dbgAddrEnd, cb = 0x100; + var cb = 0x100; if (sAddrEnd !== undefined) { - dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR.CODE); + var dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR.CODE); if (!dbgAddrEnd || dbgAddrEnd.off < dbgAddr.off) return; cb = dbgAddrEnd.off - dbgAddr.off; @@ -6745,9 +6784,6 @@ if (DEBUGGER) { } n = -1; } - else { - dbgAddrEnd = this.newAddr(this.maskReg, dbgAddr.sel, this.bus.nBusLimit, dbgAddr.type, dbgAddr.fData32, dbgAddr.fAddr32); - } var cLines = 0; this.initAddrSize(dbgAddr, true); @@ -6925,7 +6961,7 @@ if (DEBUGGER) { break; } this.shiftArgs(asArgs); - this.doDump(asArgs[0], asArgs[1], asArgs[2], asArgs[3]); + this.doDump(asArgs); break; case 'e': if (asArgs[0] == "else") break; diff --git a/modules/pcjs/lib/hdc.js b/modules/pcjs/lib/hdc.js index c4f1380ba..a046dc14d 100644 --- a/modules/pcjs/lib/hdc.js +++ b/modules/pcjs/lib/hdc.js @@ -37,6 +37,7 @@ if (typeof module !== 'undefined') { var web = require("../../shared/lib/weblib"); var DiskAPI = require("../../shared/lib/diskapi"); var Component = require("../../shared/lib/component"); + var Interrupts = require("./interrupts"); var Messages = require("./messages"); var ChipSet = require("./chipset"); var Disk = require("./disk"); @@ -470,44 +471,6 @@ if (DEBUG) { }; } -/* - * HDC BIOS interrupts, functions, and other parameters - * - * When the HDC BIOS overwrites the ROM BIOS INT 0x13 address, it saves the original INT 0x13 address - * in the INT 0x40 vector. - */ -HDC.BIOS = { - INT_DISK: 0x13, - INT_DISKETTE: 0x40 -}; - -/* - * NOTE: These are useful values for reference, but they're not actually used for anything at the moment. - */ -HDC.BIOS.DISK_CMD = { - RESET: 0x00, - GET_STATUS: 0x01, - READ_SECTORS: 0x02, - WRITE_SECTORS: 0x03, - VERIFY_SECTORS: 0x04, - FORMAT_TRK: 0x05, - FORMAT_BAD: 0x06, - FORMAT_DRIVE: 0x07, - GET_DRIVEPARMS: 0x08, - SET_DRIVEPARMS: 0x09, - READ_LONG: 0x0A, - WRITE_LONG: 0x0B, - SEEK: 0x0C, - ALT_RESET: 0x0D, - READ_BUFFER: 0x0E, - WRITE_BUFFER: 0x0F, - TEST_READY: 0x10, - RECALIBRATE: 0x11, - RAM_DIAGNOSTIC: 0x12, - DRV_DIAGNOSTIC: 0x13, - CTL_DIAGNOSTIC: 0x14 -}; - /** * setBinding(sHTMLType, sBinding, control) * @@ -550,8 +513,8 @@ HDC.prototype.initBus = function(cmp, bus, cpu, dbg) bus.addPortInputTable(this, this.fATC? HDC.aATCPortInput : HDC.aXTCPortInput); bus.addPortOutputTable(this, this.fATC? HDC.aATCPortOutput : HDC.aXTCPortOutput); - cpu.addIntNotify(HDC.BIOS.INT_DISK, this.intBIOSDisk.bind(this)); - cpu.addIntNotify(HDC.BIOS.INT_DISKETTE, this.intBIOSDiskette.bind(this)); + cpu.addIntNotify(Interrupts.DISK, this.intBIOSDisk.bind(this)); + cpu.addIntNotify(Interrupts.ALT_DISK, this.intBIOSDiskette.bind(this)); /* * The following code used to be performed in the HDC constructor, but now we need to wait for information diff --git a/modules/pcjs/lib/interrupts.js b/modules/pcjs/lib/interrupts.js index ede292e38..db31bd41b 100644 --- a/modules/pcjs/lib/interrupts.js +++ b/modules/pcjs/lib/interrupts.js @@ -54,11 +54,13 @@ var Interrupts = { CASSETTE: 0x15, KBD: 0x16, RTC: 0x1A, - TIMER_TICK: 0x1C, + ALT_TIMER: 0x1C, // invoked by the BIOS timer interrupt handler (vector 0x08) DOS: 0x21, DOS_IDLE: 0x28, DOS_NETBIOS:0x2A, MOUSE: 0x33, + ALT_DISK: 0x40, // HDC BIOS saves original FDC BIOS vector here + ALT_VIDEO: 0x6D, // IBM VGA BIOS saves original video BIOS vector here WINDBG: { // Windows Debugger protected-mode interface VECTOR: 0x41, IS_LOADED: 0x004F, // AX command @@ -104,6 +106,33 @@ if (DEBUGGER) { 0x16: "get drive %DL change line status", 0x17: "set drive %DL DASD type", 0x18: "set drive %DL media type" + /* + * Here's an additional function reference, previously in the HDC component, but moved here + * because our components are hardware emulations, not BIOS emulations, so this information is + * really only of interest to the Debugger (or the casual observer). + * + * RESET: 0x00, + * GET_STATUS: 0x01, + * READ_SECTORS: 0x02, + * WRITE_SECTORS: 0x03, + * VERIFY_SECTORS: 0x04, + * FORMAT_TRK: 0x05, + * FORMAT_BAD: 0x06, + * FORMAT_DRIVE: 0x07, + * GET_DRIVEPARMS: 0x08, + * SET_DRIVEPARMS: 0x09, + * READ_LONG: 0x0A, + * WRITE_LONG: 0x0B, + * SEEK: 0x0C, + * ALT_RESET: 0x0D, + * READ_BUFFER: 0x0E, + * WRITE_BUFFER: 0x0F, + * TEST_READY: 0x10, + * RECALIBRATE: 0x11, + * RAM_DIAGNOSTIC: 0x12, + * DRV_DIAGNOSTIC: 0x13, + * CTL_DIAGNOSTIC: 0x14 + */ }; Interrupts.FUNCS[Interrupts.CASSETTE] = { 0x80: "open device", diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index 595071a43..947c94b3d 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -2496,38 +2496,38 @@ Card.prototype.dumpVideoCard = function() }; /** - * dumpVideoBuffer(sParm) + * dumpVideoBuffer(asArgs) * - * Rather than requiring sParm to ALWAYS be a frame buffer address OR a frame buffer offset, - * we'll just make a guess as to what the user intended and support BOTH; basically if the value - * is less than the frame buffer address, we'll assume it's an offset. + * Rather than requiring the first parameter to ALWAYS be a frame buffer address OR a frame buffer + * offset, we'll just make a guess as to what the user intended and support BOTH; basically, if the + * value is less than the frame buffer address, we'll assume it's an offset. * - * Also, we allow some special options to be encoded in sParm: 'l' followed by a number means + * Also, we allow some special options to be encoded in asArgs: 'l' followed by a number means * print that many rows of data. 'n' followed by a number (1-8) means print only that number of * memory locations per row, and then adjust the starting address of the next row by the number * of bytes per row (or whatever is specified by the 'w' option) so that the dump reflects a - * rectangular chunk of video data. Finally, if sParm contains 'p' followed by a number (0-3), + * rectangular chunk of video data. Finally, if asArgs contains 'p' followed by a number (0-3), * we display only the bits from that plane for each memory location, in binary instead of hex. * * For example, assuming a standard VGA frame buffer with 640x480 pixels across 38400 (0x9600) memory * locations, the following command will dump a vertical swath of bits from plane 0 that is 32 (0x20) * rows tall and 8 columns wide, from roughly the center of the screen (0x4B00 + 0x28 - 2 = 0x4B26). * - * d video 4b26l20n8p0 + * d video 4b26 l20 n8 p0 * * Subsequent commands that omit a starting address or offset will continue where the last dump * left off; eg: * - * d video n8p0 + * d video n8 p0 * * To dump a chunk of off-screen memory starting at 0x9600, where the Windows VGA driver typically * stores a copy of the video memory containing the current mouse pointer: * - * d video 9600l20n5w5p0 + * d video 9600 l20 n5 w5 p0 * * Alternatively, you could use decimal values: * - * d video 9600l32.n5.w5.p0. + * d video 9600 l32. n5. w5. p0. * * NOTE: If these commands look suspiciously like weird Hayes modem command strings, trust me, * that is ENTIRELY coincidental (but mildly amusing). @@ -2535,49 +2535,58 @@ Card.prototype.dumpVideoCard = function() * TODO: Make these options more general-purpose (it currently assumes a conventional VGA planar layout). * * @this {Card} - * @param {string} sParm + * @param {Array.} asArgs */ -Card.prototype.dumpVideoBuffer = function(sParm) +Card.prototype.dumpVideoBuffer = function(asArgs) { if (DEBUGGER) { if (!this.adwMemory) { this.dbg.println("no buffer"); return; } - var i, idw, fColAdjust = false; + + var i, j, idw, fColAdjust = false; var l = 8, n = 8, p = -1, w = this.video.nCols >> 3; - var a = sParm.split(/([lnpw])/); - for (i = 0; i < a.length; i++) { + for (i = 0; i < asArgs.length; i++) { + + var s = asArgs[i]; if (!i) { - if (a[0].length) { - idw = str.parseInt(a[0]); + idw = str.parseInt(s); + continue; + } + + var ch = s.charAt(0); + j = str.parseInt(s.substr(1)); + + switch(ch) { + case 'l': + l = j; + break; + case 'n': + if (j >= 1 && j <= 8) { + n = j; + fColAdjust = true; } - } else { - var j = str.parseInt(a[i+1]); - if (a[i] == 'l') { - l = j; - } - else if (a[i] == 'n') { - if (j >= 1 && j <= 8) { - n = j; - fColAdjust = true; - } - } - else if (a[i] == 'p') { - if (j >= 0 && j <= 3) p = j; - } - else if (a[i] == 'w') { - if (j < w) w = j; - } - i++; + break; + case 'p': + if (j >= 0 && j <= 3) p = j; + break; + case 'w': + if (j < w) w = j; + break; + default: + this.dbg.println("unrecognized argument: " + s); + break; } } + if (idw === undefined) { idw = this.prevDump || 0; } else if (idw >= this.addrBuffer) { idw -= this.addrBuffer; } + var sDump = ""; for (i = 0; i < l; i++) { var sData = str.toHex(this.addrBuffer + idw) + ":"; @@ -2589,6 +2598,7 @@ Card.prototype.dumpVideoBuffer = function(sParm) if (sDump) sDump += "\n"; sDump += sData; } + if (sDump) this.dbg.println(sDump); this.prevDump = idw; } @@ -2793,8 +2803,8 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg) if (DEBUGGER && dbg) { var video = this; - dbg.messageDump(Messages.VIDEO, function onDumpVideo(sParm) { - video.dumpVideo(sParm); + dbg.messageDump(Messages.VIDEO, function onDumpVideo(asArgs) { + video.dumpVideo(asArgs); }); } @@ -6804,20 +6814,20 @@ Video.prototype.inCardStatus = function(card, addrFrom) }; /** - * dumpVideo(sParm) + * dumpVideo(asArgs) * * @this {Video} - * @param {string} [sParm] + * @param {Array.} asArgs */ -Video.prototype.dumpVideo = function(sParm) +Video.prototype.dumpVideo = function(asArgs) { if (DEBUGGER) { if (!this.cardActive) { this.dbg.println("no active video card"); return; } - if (sParm) { - this.cardActive.dumpVideoBuffer(sParm); + if (asArgs[0]) { + this.cardActive.dumpVideoBuffer(asArgs); return; } this.dbg.println("BIOSMODE: " + str.toHexByte(this.nMode)); diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 76fa573e6..908bd00a2 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -132,10 +132,9 @@ function X86CPU(parmsCPU) /* * List of software interrupt notification functions: aIntNotify is an array, indexed by - * interrupt number, of 2-element sub-arrays that, in turn, contain: + * interrupt number, where each element contains: * - * [0]: registered component - * [1]: registered function to call for every software interrupt + * registered function to call for every software interrupt * * The registered function is called with the linear address (LIP) following the software interrupt; * if any function returns false, the software interrupt will be skipped (presumed to be emulated), @@ -1068,7 +1067,7 @@ X86CPU.prototype.initProcessor = function() this.PS_SET = X86.PS.BIT1; // on the 80286, only BIT1 of Processor Status (flags) is always set this.PS_DIRECT |= X86.PS.IOPL.MASK | X86.PS.NT; - this.OPFLAG_NOINTR_8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads) + this.OPFLAG_NOINTR_8086 = 0; // for instructions that do *not* set NOINTR on an 80286 (eg, non-SS segment loads) this.aOps[0x0F] = X86.op0F; this.aOps0F = X86.aOps0F.slice(); @@ -1080,13 +1079,7 @@ X86CPU.prototype.initProcessor = function() if (I386 && this.model >= X86.MODEL_80386) { var bOpcode; - /* - * TODO: Determine if the Nested Task (PS.NT) flag should really be cleared in real-mode on an 80386 - * (we already know based on the OS/2 CPU test discussed in setPS() that it can't be set in real-mode - * on an 80286); for now, we assume that it should remain clear on all CPUs, to avoid any unexpected - * nested-task weirdness in real-mode. - */ - this.PS_CLEAR_RM = X86.PS.NT; + this.PS_CLEAR_RM = 0; // NOTE: This allows the 80386 to modify X86.PS.NT in real-mode (which is presumably OK) this.PS_DIRECT |= X86.PS.RF | X86.PS.VM; this.aOps[X86.OPCODE.FS] = X86.opFS; // 0x64 this.aOps[X86.OPCODE.GS] = X86.opGS; // 0x65 @@ -1635,7 +1628,7 @@ X86CPU.prototype.checkIntNotify = function(nInt) var aNotify = this.aIntNotify[nInt]; if (aNotify !== undefined) { for (var i = 0; i < aNotify.length; i++) { - if (!aNotify[i](aNotify[i][0], this.regLIP)) { + if (!aNotify[i](this.regLIP)) { return false; } } @@ -1882,7 +1875,7 @@ X86CPU.prototype.setProtMode = function(fProt, fV86) if (fV86 === undefined) { fV86 = !!(this.regPS & X86.PS.VM); } - if (!fProt != !(this.regCR0 & X86.CR0.MSW.PE) && this.messageEnabled()) { + if (DEBUG && (!fProt != !(this.regCR0 & X86.CR0.MSW.PE) || fV86 != !!(this.regPS & X86.PS.VM)) && this.messageEnabled()) { this.printMessage("CPU switching to " + (fProt? (fV86? "v86" : "protected") : "real") + "-mode", this.bitsMessage, true); } this.aOpGrp6 = (fProt && !fV86? X86.aOpGrp6Prot : X86.aOpGrp6Real); @@ -2994,13 +2987,10 @@ X86CPU.prototype.setPS = function(regPS, cpl) * pops 0xF000 into the flags is able to set *any* of flag bits 12-15: if it can, then OS/2 declares * the CPU an 80386. * - * So, if the CPU is an 80286, we zero incoming bits 12-14 in real-mode (bit 15 is never allowed to - * be modified, so there's no need to mask it). And if the CPU is an 80386, we zero only bit 14 (PS.NT), - * allowing the IOPL bits to change; however, that should not affect any real-mode operations, since - * CPL will always be zero, making IOPL irrelevant. - * - * It's still an open question whether an 80386 should also clear the Nested Task (PS.NT) flag in - * real-mode; if not, then initProcessor() should set PS_CLEAR_RM to zero. + * So, if the CPU is an 80286, we clear incoming bits 12-14 in real-mode (bit 15 is never allowed to + * be modified, so there's no need to mask it). And if the CPU is an 80386, no bits are automatically + * cleared in real-mode (PS_CLEAR_RM is zero); although that allows the IOPL bits to change, it doesn't + * affect real-mode operation, since CPL is always zero, making IOPL irrelevant. */ if (!(this.regCR0 & X86.CR0.MSW.PE)) regPS &= ~this.PS_CLEAR_RM; @@ -3147,24 +3137,40 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control) }; /** - * probeAddr(addr) + * probeAddr(addr, size) * * Used by the Debugger to probe addresses without risk of triggering a page fault, and by internal * functions, like fnFaultMessage(), that also need to avoid triggering faults, since they're not part * of standard CPU operation. * + * NOTE: If the size parameter is used, then the caller is required to provide a valid size (1, 2 or 4) + * and ensure that the data is contained entirely with the requested block. + * * @this {X86CPU} * @param {number} addr is a linear address + * @param {number} [size] is a length (default is 1) * @return {number|null} byte (8-bit) value at that address, or null if invalid */ -X86CPU.prototype.probeAddr = function(addr) +X86CPU.prototype.probeAddr = function(addr, size) { var block = this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift]; - if (block.type == Memory.TYPE.UNPAGED) { - block = this.mapPageBlock(addr, false, true); - if (!block) return null; + if (block) { + if (block.type == Memory.TYPE.UNPAGED) { + block = this.mapPageBlock(addr, false, true); + } } - return block.readByteDirect(addr & this.nBlockLimit, addr); + if (block) { + var off = addr & this.nBlockLimit; + switch(size) { + default: + return block.readByteDirect(off, addr); + case 2: + return block.readShortDirect(off, addr); + case 4: + return block.readLongDirect(off, addr); + } + } + return null; }; /** diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 34218ecc2..1a851bb27 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -3225,6 +3225,11 @@ X86.fnSLDT = function SLDT(dst, src) /** * fnSMSW(dst, src) * + * TODO: I've seen a claim that SMSW can be used with an operand size override to obtain the entire CR0. + * I don't dispute that, and since I don't mask the return value, that should be possible here; however, it + * should still be confirmed on real hardware at some point. Note that this differs from LMSW, which is + * REQUIRED to mask the source operand. + * * op=0x0F,0x01,reg=0x4 (GRP7:SMSW) * * @this {X86CPU} diff --git a/modules/pcjs/lib/x86ops.js b/modules/pcjs/lib/x86ops.js index 1a412c04a..01ab130fc 100644 --- a/modules/pcjs/lib/x86ops.js +++ b/modules/pcjs/lib/x86ops.js @@ -2475,17 +2475,23 @@ X86.opPUSHF = function PUSHF() var regPS = this.getPS(); if (I386) { if ((regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG) this.printMessage("PUSHF in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } /* * It doesn't matter whether this is PUSHF or PUSHFD: the VM and RF flags are never pushed, so - * we can always clear them. + * we should always clear them. NOTE: This contradicts what the "INTEL 80386 PROGRAMMER'S REFERENCE + * MANUAL 1986" says on page 81 (which we assume is wrong): + * + * SYSTEMS FLAGS (INCLUDING THE IOPL FIELD, AND THE VM, RF, AND IF FLAGS) ARE PUSHED AND ARE + * VISIBLE TO APPLICATIONS PROGRAMS. HOWEVER, WHEN AN APPLICATIONS PROGRAM POPS THE FLAGS, + * THESE ITEMS ARE NOT CHANGED, REGARDLESS OF THE VALUES POPPED INTO THEM. * * This does, however, beg the question: how does code running in V86-mode detect that's in V86-mode * and not real-mode? By using the SMSW instruction and checking the PE (protected-mode enabled) bit. * The SMSW instruction returns a subset of the CR0 bits, and unlike the MOV reg,CR0 instruction, is - * allowed in V86-mode. + * allowed in V86-mode. See fnSMSW() for more information. */ regPS &= ~(X86.PS.VM | X86.PS.RF); } @@ -2504,6 +2510,7 @@ X86.opPOPF = function POPF() * TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode. */ if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG) this.printMessage("POPF in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } @@ -3439,6 +3446,7 @@ X86.opINT3 = function INT3() * TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode. */ if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG) this.printMessage("INT 0x03 in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } @@ -3478,14 +3486,15 @@ X86.opINT3 = function INT3() */ X86.opINTn = function INTn() { + var nInt = this.getIPByte(); /* * TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode. */ if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG && this.messageEnabled()) this.printMessage("INT " + str.toHexByte(nInt) + " in v86-mode (IOPL < 3)", true, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } - var nInt = this.getIPByte(); /* * checkIntNotify() checks for any notification handlers registered via addIntNotify(), calls them, * and returns false ONLY if a notification handler returned false (ie, requesting the interrupt be skipped). @@ -3509,6 +3518,7 @@ X86.opINTO = function INTO() * TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode. */ if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG) this.printMessage("INTO in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } @@ -3529,6 +3539,7 @@ X86.opIRET = function IRET() * TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode. */ if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) { + if (DEBUG) this.printMessage("IRET in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } @@ -4131,6 +4142,7 @@ X86.opCLI = function CLI() * and in V86-mode, CPL is always 3. */ if (this.nCPL > this.nIOPL) { + if (DEBUG && (this.regPS & X86.PS.VM)) this.printMessage("CLI in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } @@ -4150,6 +4162,7 @@ X86.opSTI = function STI() * and in V86-mode, CPL is always 3. */ if (this.nCPL > this.nIOPL) { + if (DEBUG && (this.regPS & X86.PS.VM)) this.printMessage("STI in v86-mode (IOPL < 3)", this.bitsMessage, true); X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0); return; } diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 6905708fc..934c3cfd0 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -225,11 +225,11 @@ X86Seg.prototype.loadProt = function loadProt(sel, fProbe) /* * The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT, * which has no bearing on the ROM's own code, because it never loads any LDT selectors, but if at the same - * time our Debugger attempts to validate a selector in one of its breakpoints, that could cause some - * grief here. We avoid that grief by skipping segment lookup if the descriptor table being referenced is zero - * AND the Debugger's ID.DBG segment register is being used. + * time our Debugger attempts to validate a selector in one of its breakpoints, that could cause some grief. + * + * Fortunately, the Debugger now has its own interface, probeDesc(), so that should no longer be a concern. */ - if (addrDT || this.id != X86Seg.ID.DBG) { + if (addrDT) { var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0; if ((addrDTLimit - addrDesc)|0 >= 7) { /* @@ -238,7 +238,7 @@ X86Seg.prototype.loadProt = function loadProt(sel, fProbe) * starting with a 15-cycle difference. Obviously the difference will vary with the instruction, * and will be much greater whenever the load fails. */ - if (this.id != X86Seg.ID.DBG) cpu.nStepCycles -= 15; + cpu.nStepCycles -= 15; return this.loadDesc8(addrDesc, sel, fProbe); } if (this.id < X86Seg.ID.VER) { @@ -740,7 +740,6 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe) if (!I386 || !(type & X86.DESC.ACC.NONSEG_386)) { offSP = (cplNew << 2) + X86.TSS286.CPL0_SP; lenSP = 2; - cpu.assert(!(regPS & X86.PS.VM)); } else { offSP = (cplNew << 2) + X86.TSS386.CPL0_ESP; lenSP = 4; @@ -923,8 +922,9 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe) default: /* - * The only other case should be X86Seg.ID.DBG, for which we do nothing. + * The only other case used to be X86Seg.ID.DBG, but the Debugger uses probeDesc() now, so we should never get here. */ + cpu.assert(false); break; } @@ -946,7 +946,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe) this.ext = ext; this.addrDesc = addrDesc; /* - * A quick recap of what updateMode(fLoad=true, fProt=true, fV86=false) actually updates next: + * A quick recap of what updateMode(fLoad=true, fProt=true, fV86=false) actually updates: * * cpl * dpl @@ -968,6 +968,64 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe) return base; }; +/** + * probeDesc(sel) + * + * This is a neutered version of loadProt() designed for the Debugger. + * + * @this {X86Seg} + * @param {number} sel + * @return {number} base address of selected segment, or X86.ADDR_INVALID if error + */ +X86Seg.prototype.probeDesc = function(sel) +{ + var addrDT; + var addrDTLimit; + var cpu = this.cpu; + + sel &= 0xffff; + + if (!(sel & X86.SEL.LDT)) { + addrDT = cpu.addrGDT; + addrDTLimit = cpu.addrGDTLimit; + } else { + addrDT = cpu.segLDT.base; + addrDTLimit = (addrDT + cpu.segLDT.limit)|0; + } + + var addrDesc = (addrDT + (sel & X86.SEL.MASK))|0; + + if ((addrDTLimit - addrDesc)|0 >= 7) { + + /* + * Load the descriptor from memory using probeAddr(). + */ + var limit = cpu.probeAddr(addrDesc + X86.DESC.LIMIT.OFFSET, 2); + var acc = cpu.probeAddr(addrDesc + X86.DESC.ACC.OFFSET, 2); + var type = (acc & X86.DESC.ACC.TYPE.MASK); + var base = cpu.probeAddr(addrDesc + X86.DESC.BASE.OFFSET, 2) | ((acc & X86.DESC.ACC.BASE1623) << 16); + var ext = cpu.probeAddr(addrDesc + X86.DESC.EXT.OFFSET, 2); + + if (I386 && cpu.model >= X86.MODEL_80386) { + base |= (ext & X86.DESC.EXT.BASE2431) << 16; + limit |= (ext & X86.DESC.EXT.LIMIT1619) << 16; + if (ext & X86.DESC.EXT.LIMITPAGES) limit = (limit << 12) | 0xfff; + } + + this.sel = sel; + this.base = base; + this.limit = limit; + this.offMax = (limit >>> 0) + 1; + this.acc = acc; + this.type = type; + this.ext = ext; + this.addrDesc = addrDesc; + this.updateMode(true, true, false); + return base; + } + return X86.ADDR_INVALID; +}; + /** * switchTSS(selNew, fNest) * @@ -1408,7 +1466,7 @@ X86Seg.prototype.updateMode = function(fLoad, fProt, fV86) X86Seg.prototype.messageSeg = function(sel, base, limit, type, ext) { if (DEBUG) { - if (DEBUGGER && this.id != X86Seg.ID.DBG && this.dbg && this.dbg.messageEnabled(Messages.SEG)) { + if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.SEG)) { var ch = (this.sName.length < 3? " " : ""); var sDPL = " dpl=" + this.dpl; if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl;