diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 7f46fe07a..be2965783 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -85,7 +85,7 @@ function Debugger(parmsDbg) this.cInstructions = -1; /* - * Default number of hex chars in a register and a physical address (ie, for real-mode); + * Default number of hex chars in a register and a linear address (ie, for real-mode); * updated by initBus(). */ this.cchReg = 4; @@ -99,8 +99,8 @@ function Debugger(parmsDbg) * update aAddrNextData and aAddrNextCode, respectively, when they're done. * * The format of all aAddr variables is [off, seg, addr], where seg:off is the segmented - * address and addr is the corresponding physical address (if known). For certain segmented - * addresses (eg, breakpoint addresses), we pre-compute the physical address and save that + * address and addr is the corresponding linear address (if known). For certain segmented + * addresses (eg, breakpoint addresses), we pre-compute the linear address and save that * in aAddr[2], so that the breakpoint will still operate as intended even if the mode changes * later (eg, from real-mode to protected-mode). * @@ -2576,14 +2576,14 @@ if (DEBUGGER) { * @param {Array} aAddr * @param {boolean} [fWrite] * @param {number} [cb] is number of bytes to check (1, 2 or 4); default is 1 - * @return {number} is the corresponding physical address, or X86.ADDR_INVALID + * @return {number} is the corresponding linear address, or X86.ADDR_INVALID */ Debugger.prototype.getAddr = function(aAddr, fWrite, cb) { /* - * Some addresses (eg, breakpoint addresses) save their original physical address in aAddr[2], + * Some addresses (eg, breakpoint addresses) save their original linear address in aAddr[2], * so we want to use that if it's there, but otherwise, aAddr is assumed to be a virtual address - * ([off, seg]) whose physical address must be calculated based on current machine state + * ([off, seg]) whose linear address must be calculated based on current machine state * (mode, active descriptor tables, etc). */ var addr = aAddr[2]; @@ -2763,8 +2763,8 @@ if (DEBUGGER) { } if (fTemp) { /* - * Force temporary breakpoints to be interpreted as physical breakpoints - * (hence the assertion that there IS a physical address stored in aAddr); + * Force temporary breakpoints to be interpreted as linear breakpoints + * (hence the assertion that there IS a linear address stored in aAddr); * this allows us to step over calls or interrupts that change the processor mode */ aAddr[0] = -1; @@ -2814,7 +2814,7 @@ if (DEBUGGER) { /** * listBreakpoints(aBreak) * - * TODO: We may need to start listing the physical addresses of breakpoints, because + * TODO: We may need to start listing the linear addresses of breakpoints, because * segmented address can be ambiguous. * * @this {Debugger} @@ -2938,14 +2938,14 @@ if (DEBUGGER) { var aAddrBreak = aBreak[i]; /* - * We need to zap the physical address field of the breakpoint address before - * calling getAddr(), to force it to recalculate the physical address every time, - * unless this is a breakpoint on a physical address (as indicated by a -1 offset). + * We need to zap the linear address field of the breakpoint address before + * calling getAddr(), to force it to recalculate the linear address every time, + * unless this is a breakpoint on a linear address (as indicated by a -1 offset). */ if (aAddrBreak[0] != -1) aAddrBreak[2] = null; /* - * We used to calculate the physical address of the breakpoint at the time the + * We used to calculate the linear address of the breakpoint at the time the * breakpoint was added, so that a breakpoint set in one mode (eg, in real-mode) * would still work as intended if the mode changed later (eg, to protected-mode). * @@ -3519,11 +3519,11 @@ if (DEBUGGER) { * parseAddr(sAddr, type) * * As discussed above, the format of aAddr variables is [off, seg, addr]; they represent a segmented - * address (seg:off) when seg is defined or a physical address (addr) when seg is undefined (or null). + * address (seg:off) when seg is defined or a linear address (addr) when seg is undefined (or null). * - * To create a segmented address, specify two values separated by ":"; for a physical address, use + * To create a segmented address, specify two values separated by ":"; for a linear address, use * a "%" prefix. We check for ":" after "%", so if for some strange reason you specify both, the - * address will be treated as segmented, not physical. + * address will be treated as segmented, not linear. * * The "%" syntax is similar to that used by the Windows 80386 kernel debugger (wdeb386) for linear * addresses. If/when we add support for processors with page tables, we will likely adopt the same @@ -3938,10 +3938,10 @@ if (DEBUGGER) { * * As the "help" output below indicates, the following breakpoint commands are supported: * - * bp [a] set exec breakpoint on physical addr [a] - * br [a] set read breakpoint on physical addr [a] - * bw [a] set write breakpoint on physical addr [a] - * bc [a] clear breakpoint on physical addr [a] (use "*" for all breakpoints) + * bp [a] set exec breakpoint on linear addr [a] + * br [a] set read breakpoint on linear addr [a] + * bw [a] set write breakpoint on linear addr [a] + * bc [a] clear breakpoint on linear addr [a] (use "*" for all breakpoints) * bl list breakpoints * * to which we have recently added the following I/O breakpoint commands: diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index f6b227c2f..fe2fda5b2 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -1581,17 +1581,17 @@ X86CPU.prototype.setProtMode = function(fProt) if (fProt === undefined) { fProt = !!(this.regCR0 & X86.CR0.MSW.PE); } - if (!fProt != !(this.regCR0 & X86.CR0.MSW.PE)) { + if (!fProt != !(this.regCR0 & X86.CR0.MSW.PE) && this.messageEnabled()) { this.printMessage("CPU switching to " + (fProt? "protected" : "real") + "-mode", this.bitsMessage, true); } this.aOpGrp6 = (fProt? X86.aOpGrp6Prot : X86.aOpGrp6Real); - this.segCS.updateMode(fProt); - this.segDS.updateMode(fProt); - this.segSS.updateMode(fProt); - this.segES.updateMode(fProt); + this.segCS.updateMode(); + this.segDS.updateMode(); + this.segSS.updateMode(); + this.segES.updateMode(); if (I386 && this.model >= X86.MODEL_80386) { - this.segFS.updateMode(fProt); - this.segGS.updateMode(fProt); + this.segFS.updateMode(); + this.segGS.updateMode(); this.resetSizes(); } }; diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 74f2eaf8e..e22cc77c4 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -54,12 +54,14 @@ function X86Seg(cpu, id, sName, fProt) this.id = id; this.sName = sName || ""; this.sel = 0; - this.limit = 0xffff; // in protected-mode, this is descriptor word 0x0 (word 0x6 on the 80386 supplements limit bits 16-19) - this.base = 0; // in protected-mode, this is descriptor word 0x2 (word 0x6 on the 80386 supplements base bits 24-31) - this.acc = 0; // in protected-mode, this is descriptor word 0x4, masked with 0xff00 (bits 0-7 supplement base bits 16-23) - this.ext = 0; // in protected-mode, this is descriptor word 0x6 (used only on the 80386) + this.limit = 0xffff; // in protected-mode, this is descriptor word 0x0 + this.base = 0; // in protected-mode, this is descriptor word 0x2 + this.acc = this.type = 0; // in protected-mode, this is descriptor word 0x4, masked with 0xff00 (bits 0-7 supplement base bits 16-23) + this.ext = 0; // in protected-mode, this is descriptor word 0x6 (80386 only; supplements limit bits 16-19 and base bits 24-31) this.cpl = this.dpl = 0; this.addrDesc = X86.ADDR_INVALID; + this.dataSize = this.addrSize = 2; + this.dataMask = this.addrMask = 0xffff; /* * The following properties are used for CODE segments only (ie, segCS); if the process of loading * CS also requires a stack switch, then fStackSwitch will be set to true; additionally, if the stack @@ -84,13 +86,7 @@ function X86Seg(cpu, id, sName, fProt) this.awParms = (this.id == X86Seg.ID.CODE? new Array(32) : []); this.fCall = null; this.fStackSwitch = false; - /* - * The following properties are used for STACK segments only (ie, segSS); we want to make it easier - * for setSS() to set stack lower and upper limits, which requires knowing whether or not the segment is - * marked as EXPDOWN. - */ - this.fExpDown = false; - this.updateMode(fProt); + this.updateMode(true, fProt); } X86Seg.ID = { @@ -117,8 +113,17 @@ X86Seg.ID = { X86Seg.prototype.loadReal = function loadReal(sel, fSuppress) { this.sel = sel & 0xffff; - this.dataSize = this.addrSize = 2; - this.dataMask = this.addrMask = 0xffff; + /* + * Apparently, loading a new value into a segment register in real-mode alters NEITHER the limit NOR these + * other attributes, so I've since moved these defaults into the constructor. If you run any code that switches + * to protected-mode, loads a 32-bit code segment, and then switches back to real-mode, it is THAT code's + * responsibility to load a 16-bit segment into CS before returning to real-mode; otherwise, you're probably toast. + * + * this.dataSize = this.addrSize = 2; + * this.dataMask = this.addrMask = 0xffff; + * + * Only the selector and base portions of a segment register can be changed in real-mode. + */ return this.base = this.sel << 4; }; @@ -556,7 +561,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel) this.type = (acc & X86.DESC.ACC.TYPE.MASK); this.ext = 0; this.addrDesc = addrDesc; - this.updateMode(); + this.updateMode(true); this.messageSeg(sel, base, limit, acc); @@ -798,7 +803,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) this.type = type; this.ext = ext; this.addrDesc = addrDesc; - this.updateMode(); + this.updateMode(true); break; } if (!fSuppress) this.messageSeg(sel, base, limit, acc, ext); @@ -867,20 +872,26 @@ X86Seg.prototype.restore = function(a) }; /** - * updateMode(fProt) + * updateMode(fLoad, fProt) * * Ensures that the segment register's access (ie, load and check methods) matches the specified (or current) * operating mode (real or protected). * * @this {X86Seg} + * @param {boolean} [fLoad] true if the segment was just (re)loaded, false if not * @param {boolean} [fProt] true for protected-mode access, false for real-mode access, undefined for current mode * @return {boolean} */ -X86Seg.prototype.updateMode = function(fProt) +X86Seg.prototype.updateMode = function(fLoad, fProt) { if (fProt === undefined) { fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE); } + /* + * The following properties are used for STACK segments only (ie, segSS); we want to make it easier + * for setSS() to set stack lower and upper limits, which requires knowing whether or not the segment is + * marked as EXPDOWN. + */ this.fExpDown = false; if (fProt) { /* @@ -919,12 +930,21 @@ X86Seg.prototype.updateMode = function(fProt) } } this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; - if (this.cpu.model < X86.MODEL_80386 || !(this.ext & X86.DESC.EXT.BIG)) { - this.addrSize = 2; - this.addrMask = 0xffff; - } else { - this.addrSize = 4; - this.addrMask = (0xffffffff|0); + if (fLoad) { + /* + * Any update to the OPERAND and ADDRESS sizes should happen only on segment loads, not simply when + * we're updating the segment register as part of a mode change. Note that there is no counterpart to + * this for real-mode, because real-mode segment loads never change these attributes. + */ + if (this.cpu.model < X86.MODEL_80386 || !(this.ext & X86.DESC.EXT.BIG)) { + this.dataSize = 2; + this.dataMask = 0xffff; + } else { + this.dataSize = 4; + this.dataMask = (0xffffffff|0); + } + this.addrSize = this.dataSize; + this.addrMask = this.dataMask; } } else { this.load = this.loadReal; @@ -932,25 +952,25 @@ X86Seg.prototype.updateMode = function(fProt) this.checkRead = this.checkReadReal; this.checkWrite = this.checkWriteReal; /* - * Like the base, we don't want to mess with the limit, so that features like "Unreal" mode - * (or "Big Real" mode as it's called in the HIMEM source code) will work, at least until the - * next explicit segment load. + * We don't want to mess with the limit, so that features like "Unreal" mode (or "Big Real" mode + * as it's called in the HIMEM source code) will work, at least until the next explicit segment load. * * See http://www.os2museum.com/wp/himem-sys-unreal-mode-and-loadall/ for more details. * + * Ditto for other attributes such as acc, type, ext, and the OPERAND and ADDRESS sizes, which are derived + * from the ext property. Even an explicit segment load in real-mode does not alter these properties. + * * this.limit = 0xffff; + * this.acc = this.type = this.ext = 0; + * this.dataSize = this.addrSize = 2; + * this.dataMask = this.addrMask = 0xffff; * * TODO: The checkReadReal() and checkWriteReal() functions need to generate GP faults for offsets * beyond the current real-mode limit. */ - this.acc = this.ext = 0; - this.cpl = this.dpl = 0; this.addrDesc = X86.ADDR_INVALID; - this.addrSize = 2; - this.addrMask = 0xffff; + this.cpl = this.dpl = 0; } - this.dataSize = this.addrSize; - this.dataMask = this.addrMask; return fProt; };