Improved segment compatibility across mode changes
This commit is contained in:
parent
45f2d6e2d1
commit
df838b8f86
3 changed files with 79 additions and 59 deletions
|
|
@ -85,7 +85,7 @@ function Debugger(parmsDbg)
|
||||||
this.cInstructions = -1;
|
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().
|
* updated by initBus().
|
||||||
*/
|
*/
|
||||||
this.cchReg = 4;
|
this.cchReg = 4;
|
||||||
|
|
@ -99,8 +99,8 @@ function Debugger(parmsDbg)
|
||||||
* update aAddrNextData and aAddrNextCode, respectively, when they're done.
|
* 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
|
* 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
|
* address and addr is the corresponding linear address (if known). For certain segmented
|
||||||
* addresses (eg, breakpoint addresses), we pre-compute the physical address and save that
|
* 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
|
* 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).
|
* later (eg, from real-mode to protected-mode).
|
||||||
*
|
*
|
||||||
|
|
@ -2576,14 +2576,14 @@ if (DEBUGGER) {
|
||||||
* @param {Array} aAddr
|
* @param {Array} aAddr
|
||||||
* @param {boolean} [fWrite]
|
* @param {boolean} [fWrite]
|
||||||
* @param {number} [cb] is number of bytes to check (1, 2 or 4); default is 1
|
* @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)
|
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
|
* 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).
|
* (mode, active descriptor tables, etc).
|
||||||
*/
|
*/
|
||||||
var addr = aAddr[2];
|
var addr = aAddr[2];
|
||||||
|
|
@ -2763,8 +2763,8 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
if (fTemp) {
|
if (fTemp) {
|
||||||
/*
|
/*
|
||||||
* Force temporary breakpoints to be interpreted as physical breakpoints
|
* Force temporary breakpoints to be interpreted as linear breakpoints
|
||||||
* (hence the assertion that there IS a physical address stored in aAddr);
|
* (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
|
* this allows us to step over calls or interrupts that change the processor mode
|
||||||
*/
|
*/
|
||||||
aAddr[0] = -1;
|
aAddr[0] = -1;
|
||||||
|
|
@ -2814,7 +2814,7 @@ if (DEBUGGER) {
|
||||||
/**
|
/**
|
||||||
* listBreakpoints(aBreak)
|
* 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.
|
* segmented address can be ambiguous.
|
||||||
*
|
*
|
||||||
* @this {Debugger}
|
* @this {Debugger}
|
||||||
|
|
@ -2938,14 +2938,14 @@ if (DEBUGGER) {
|
||||||
var aAddrBreak = aBreak[i];
|
var aAddrBreak = aBreak[i];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to zap the physical address field of the breakpoint address before
|
* We need to zap the linear address field of the breakpoint address before
|
||||||
* calling getAddr(), to force it to recalculate the physical address every time,
|
* calling getAddr(), to force it to recalculate the linear address every time,
|
||||||
* unless this is a breakpoint on a physical address (as indicated by a -1 offset).
|
* unless this is a breakpoint on a linear address (as indicated by a -1 offset).
|
||||||
*/
|
*/
|
||||||
if (aAddrBreak[0] != -1) aAddrBreak[2] = null;
|
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)
|
* 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).
|
* would still work as intended if the mode changed later (eg, to protected-mode).
|
||||||
*
|
*
|
||||||
|
|
@ -3519,11 +3519,11 @@ if (DEBUGGER) {
|
||||||
* parseAddr(sAddr, type)
|
* parseAddr(sAddr, type)
|
||||||
*
|
*
|
||||||
* As discussed above, the format of aAddr variables is [off, seg, addr]; they represent a segmented
|
* 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
|
* 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
|
* 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
|
* 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:
|
* As the "help" output below indicates, the following breakpoint commands are supported:
|
||||||
*
|
*
|
||||||
* bp [a] set exec breakpoint on physical addr [a]
|
* bp [a] set exec breakpoint on linear addr [a]
|
||||||
* br [a] set read breakpoint on physical addr [a]
|
* br [a] set read breakpoint on linear addr [a]
|
||||||
* bw [a] set write breakpoint on physical addr [a]
|
* bw [a] set write breakpoint on linear addr [a]
|
||||||
* bc [a] clear breakpoint on physical addr [a] (use "*" for all breakpoints)
|
* bc [a] clear breakpoint on linear addr [a] (use "*" for all breakpoints)
|
||||||
* bl list breakpoints
|
* bl list breakpoints
|
||||||
*
|
*
|
||||||
* to which we have recently added the following I/O breakpoint commands:
|
* to which we have recently added the following I/O breakpoint commands:
|
||||||
|
|
|
||||||
|
|
@ -1581,17 +1581,17 @@ X86CPU.prototype.setProtMode = function(fProt)
|
||||||
if (fProt === undefined) {
|
if (fProt === undefined) {
|
||||||
fProt = !!(this.regCR0 & X86.CR0.MSW.PE);
|
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.printMessage("CPU switching to " + (fProt? "protected" : "real") + "-mode", this.bitsMessage, true);
|
||||||
}
|
}
|
||||||
this.aOpGrp6 = (fProt? X86.aOpGrp6Prot : X86.aOpGrp6Real);
|
this.aOpGrp6 = (fProt? X86.aOpGrp6Prot : X86.aOpGrp6Real);
|
||||||
this.segCS.updateMode(fProt);
|
this.segCS.updateMode();
|
||||||
this.segDS.updateMode(fProt);
|
this.segDS.updateMode();
|
||||||
this.segSS.updateMode(fProt);
|
this.segSS.updateMode();
|
||||||
this.segES.updateMode(fProt);
|
this.segES.updateMode();
|
||||||
if (I386 && this.model >= X86.MODEL_80386) {
|
if (I386 && this.model >= X86.MODEL_80386) {
|
||||||
this.segFS.updateMode(fProt);
|
this.segFS.updateMode();
|
||||||
this.segGS.updateMode(fProt);
|
this.segGS.updateMode();
|
||||||
this.resetSizes();
|
this.resetSizes();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,14 @@ function X86Seg(cpu, id, sName, fProt)
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.sName = sName || "";
|
this.sName = sName || "";
|
||||||
this.sel = 0;
|
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.limit = 0xffff; // in protected-mode, this is descriptor word 0x0
|
||||||
this.base = 0; // in protected-mode, this is descriptor word 0x2 (word 0x6 on the 80386 supplements base bits 24-31)
|
this.base = 0; // in protected-mode, this is descriptor word 0x2
|
||||||
this.acc = 0; // in protected-mode, this is descriptor word 0x4, masked with 0xff00 (bits 0-7 supplement base bits 16-23)
|
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 (used only on the 80386)
|
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.cpl = this.dpl = 0;
|
||||||
this.addrDesc = X86.ADDR_INVALID;
|
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
|
* 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
|
* 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.awParms = (this.id == X86Seg.ID.CODE? new Array(32) : []);
|
||||||
this.fCall = null;
|
this.fCall = null;
|
||||||
this.fStackSwitch = false;
|
this.fStackSwitch = false;
|
||||||
/*
|
this.updateMode(true, fProt);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X86Seg.ID = {
|
X86Seg.ID = {
|
||||||
|
|
@ -117,8 +113,17 @@ X86Seg.ID = {
|
||||||
X86Seg.prototype.loadReal = function loadReal(sel, fSuppress)
|
X86Seg.prototype.loadReal = function loadReal(sel, fSuppress)
|
||||||
{
|
{
|
||||||
this.sel = sel & 0xffff;
|
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;
|
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.type = (acc & X86.DESC.ACC.TYPE.MASK);
|
||||||
this.ext = 0;
|
this.ext = 0;
|
||||||
this.addrDesc = addrDesc;
|
this.addrDesc = addrDesc;
|
||||||
this.updateMode();
|
this.updateMode(true);
|
||||||
|
|
||||||
this.messageSeg(sel, base, limit, acc);
|
this.messageSeg(sel, base, limit, acc);
|
||||||
|
|
||||||
|
|
@ -798,7 +803,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.ext = ext;
|
this.ext = ext;
|
||||||
this.addrDesc = addrDesc;
|
this.addrDesc = addrDesc;
|
||||||
this.updateMode();
|
this.updateMode(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!fSuppress) this.messageSeg(sel, base, limit, acc, ext);
|
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)
|
* Ensures that the segment register's access (ie, load and check methods) matches the specified (or current)
|
||||||
* operating mode (real or protected).
|
* operating mode (real or protected).
|
||||||
*
|
*
|
||||||
* @this {X86Seg}
|
* @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
|
* @param {boolean} [fProt] true for protected-mode access, false for real-mode access, undefined for current mode
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
X86Seg.prototype.updateMode = function(fProt)
|
X86Seg.prototype.updateMode = function(fLoad, fProt)
|
||||||
{
|
{
|
||||||
if (fProt === undefined) {
|
if (fProt === undefined) {
|
||||||
fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE);
|
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;
|
this.fExpDown = false;
|
||||||
if (fProt) {
|
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;
|
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)) {
|
if (fLoad) {
|
||||||
this.addrSize = 2;
|
/*
|
||||||
this.addrMask = 0xffff;
|
* Any update to the OPERAND and ADDRESS sizes should happen only on segment loads, not simply when
|
||||||
} else {
|
* we're updating the segment register as part of a mode change. Note that there is no counterpart to
|
||||||
this.addrSize = 4;
|
* this for real-mode, because real-mode segment loads never change these attributes.
|
||||||
this.addrMask = (0xffffffff|0);
|
*/
|
||||||
|
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 {
|
} else {
|
||||||
this.load = this.loadReal;
|
this.load = this.loadReal;
|
||||||
|
|
@ -932,25 +952,25 @@ X86Seg.prototype.updateMode = function(fProt)
|
||||||
this.checkRead = this.checkReadReal;
|
this.checkRead = this.checkReadReal;
|
||||||
this.checkWrite = this.checkWriteReal;
|
this.checkWrite = this.checkWriteReal;
|
||||||
/*
|
/*
|
||||||
* Like the base, we don't want to mess with the limit, so that features like "Unreal" mode
|
* We don't want to mess with the limit, so that features like "Unreal" mode (or "Big Real" mode
|
||||||
* (or "Big Real" mode as it's called in the HIMEM source code) will work, at least until the
|
* as it's called in the HIMEM source code) will work, at least until the next explicit segment load.
|
||||||
* next explicit segment load.
|
|
||||||
*
|
*
|
||||||
* See http://www.os2museum.com/wp/himem-sys-unreal-mode-and-loadall/ for more details.
|
* 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.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
|
* TODO: The checkReadReal() and checkWriteReal() functions need to generate GP faults for offsets
|
||||||
* beyond the current real-mode limit.
|
* beyond the current real-mode limit.
|
||||||
*/
|
*/
|
||||||
this.acc = this.ext = 0;
|
|
||||||
this.cpl = this.dpl = 0;
|
|
||||||
this.addrDesc = X86.ADDR_INVALID;
|
this.addrDesc = X86.ADDR_INVALID;
|
||||||
this.addrSize = 2;
|
this.cpl = this.dpl = 0;
|
||||||
this.addrMask = 0xffff;
|
|
||||||
}
|
}
|
||||||
this.dataSize = this.addrSize;
|
|
||||||
this.dataMask = this.addrMask;
|
|
||||||
return fProt;
|
return fProt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue