Added support for segment wrap-around in 8086/8088 real-mode (all stack operations, instruction fetches, and operand accesses should wrap now); real-mode wrap-around on newer processors should trigger a GP fault instead
This commit is contained in:
parent
d7dd746e2c
commit
6a3f7f4a5d
15 changed files with 3156 additions and 2906 deletions
|
|
@ -205,8 +205,8 @@ any foreseeable problems to future generations, because we've got more important
|
|||
And on PC-DOS 1.00, does `SPEEDUP` actually speed things up? In the real world, it presumably did, but in
|
||||
the world of emulation, there isn't any measurable difference, because hardware features like stepping rates and
|
||||
head settling times are rarely simulated. As I suggested in my [last blog post](/blog/2017/07/15/), this is a
|
||||
degree of authenticity that all emulators should strive for, and PCjs is no exception to that rule -- I just haven't
|
||||
gotten around to it yet.
|
||||
degree of authenticity that all emulators should strive for, and PCjs emulators are no exception to that rule -- I
|
||||
just haven't gotten around to it yet.
|
||||
|
||||
### Epilogue
|
||||
|
||||
|
|
@ -220,25 +220,27 @@ implemented, along with the proper queuing of all *four* possible types of seria
|
|||
|
||||
NOTE: Serial I/O from DOS (ie, redirection to or from a COM port) already worked because DOS performs polled I/O
|
||||
rather than interrupt-driven I/O. Additionally, when DOS terminates a line, it outputs both CR (0Dh) and LF (0Ah)
|
||||
bytes, whereas BASIC outputs only CR; so, when a machine uses a <textarea> control for serial I/O, we
|
||||
automatically convert transform stand-alone CR bytes into LF bytes.
|
||||
bytes, whereas BASIC outputs only CR; so, when a machine uses a <textarea> control for serial I/O, PCx86
|
||||
automatically adds an LF after any stand-alone CR.
|
||||
|
||||
Second, when `SPEEDUP` crashed on newer versions of DOS, an invalid stack pointer would be loaded, and I noticed
|
||||
that I had neglected to fully implement stack wrap-around on 8086/8088 processors; specifically, in the case where the
|
||||
stack pointer is *odd*.
|
||||
|
||||
Normally, when the stack pointer is *even*, it will smoothly auto-decrement from 0000h to FFFEh or auto-increment
|
||||
from FFFEh to 0000h. However, when the stack pointer is *odd* and it reaches 0001h, the next PUSH on a 8086/8088 must
|
||||
automatically store the high byte at 0000h and the low byte at FFFFh; similarly, when the stack pointer is FFFFh,
|
||||
the next POP must fetch the low byte from FFFFh and the high byte from 0000h.
|
||||
Normally, when the stack pointer is *even*, it will cross the stack segment's 64Kb boundary without incident, smoothly
|
||||
auto-decrementing from 0000h to FFFEh or auto-incrementing from FFFEh to 0000h. However, when the stack pointer is
|
||||
*odd* and it reaches 0001h, the next PUSH on an 8088 must automatically store the high byte at 0000h and the low
|
||||
byte at FFFFh; similarly, when the stack pointer is FFFFh, the next POP must fetch the low byte from FFFFh and the high
|
||||
byte from 0000h.
|
||||
|
||||
Most emulators simulating an 8086/8088 in real-mode actually use V86-mode, which doesn't support stack operands that
|
||||
wrap around the top of the stack segment; V86-mode will throw an exception, and the machine is toast. PCjs makes more
|
||||
of an effort simulate real-mode, so any PUSH or POP should work smoothly, regardless whether the stack pointer is even
|
||||
or odd.
|
||||
Most emulators simulating an 8086/8088 (aka real-mode) actually use V86-mode, which doesn't support stack operands that
|
||||
straddle the top of the stack segment; V86-mode will throw an exception, and the machine is toast.
|
||||
|
||||
PCjs is still far from perfect. For example, instruction fetches that cross a 64K boundary are supposed to wrap around
|
||||
to 0000h as well. That's not implemented yet, but stay tuned.
|
||||
PCx86 makes more of an effort simulate real-mode, so any PUSH or POP should work properly, regardless whether the stack
|
||||
pointer is even or odd. In fact, the same is true for any real-mode 16-bit access across a 64Kb boundary, stack-based
|
||||
or otherwise. PCx86 will even execute real-mode instructions that wrap around a 64Kb boundary.
|
||||
|
||||
PCx86 does it all (except when it doesn't)!
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Jul 21, 2017*
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -492,6 +492,8 @@ MarkOut.prototype.convertMD = function(sIndent)
|
|||
cProps = 0;
|
||||
} else {
|
||||
if (cProps++) sValue += ',';
|
||||
var match = sPropValue.match(/^"(.*)"$/);
|
||||
if (match) sPropValue = match[1];
|
||||
sValue += '"' + sPropName + '":"' + sPropValue + '"';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3776,7 +3776,7 @@ class DebuggerX86 extends Debugger {
|
|||
} while (dbgAddrIns.addr != dbgAddr.addr);
|
||||
}
|
||||
|
||||
sLine += Str.pad(sBytes, dbgAddrIns.fAddr32? 24 : 16);
|
||||
sLine += Str.pad(sBytes, dbgAddrIns.fAddr32? 25 : 17);
|
||||
sLine += Str.pad(sOpcode, 8);
|
||||
if (sOperands) sLine += ' ' + sOperands;
|
||||
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ var X86 = {
|
|||
NOREAD: 0x0001, // disable memory reads for the remainder of the current instruction
|
||||
NOWRITE: 0x0002, // disable memory writes for the remainder of the current instruction
|
||||
NOINTR: 0x0004, // a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
|
||||
WRAP: 0x0008, // a segment wrap-around has occurred (relevant to 8086/8088 only)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2266,11 +2266,12 @@ class X86CPU extends CPU {
|
|||
var newLIP = (this.regLIP >>> 0) + inc;
|
||||
if (newLIP > this.regLIPMax) {
|
||||
/*
|
||||
* There's no such thing as a GP fault on the 8086/8088, and I'm assuming that,
|
||||
* on newer processors, when the segment limit is the maximum, it's OK for IP to wrap.
|
||||
* There's no such thing as a GP fault on the 8086/8088, and I'm now assuming that,
|
||||
* on newer processors, all attempts to fetch opcodes beyond the limit trigger a fault.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || this.segCS.limit == this.segCS.maskAddr) {
|
||||
if (this.model <= X86.MODEL_8088 /* || this.segCS.limit == this.segCS.maskAddr */) {
|
||||
newLIP = this.segCS.base + ((newLIP - this.regLIPMax) & (I386? this.maskData : 0xffff));
|
||||
if (inc == 2) this.opFlags |= X86.OPFLAG.WRAP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
}
|
||||
|
|
@ -3326,15 +3327,25 @@ class X86CPU extends CPU {
|
|||
* @this {X86CPU}
|
||||
* @param {X86Seg} seg register (eg, segDS)
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @return {number} word (16-bit) value at that address
|
||||
* @return {number} word (16-bit or 32-bit) value at that address
|
||||
*/
|
||||
getEAWord(seg, off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = seg;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = seg.checkRead(this.offEA, (I386? this.sizeData : 2));
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getWord(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(seg.checkRead(0, 1)) << 8);
|
||||
}
|
||||
else {
|
||||
w = this.getWord(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -3351,11 +3362,22 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getEAShortData(off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = this.segData;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = this.segEA.checkRead(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getShort(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(this.segEA.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getShort(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -3372,11 +3394,22 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getEAShortStack(off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = this.segStack;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = this.segEA.checkRead(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getShort(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(this.segEA.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getShort(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -3452,7 +3485,19 @@ class X86CPU extends CPU {
|
|||
this.backTrack.btiMem0 = this.backTrack.btiEALo;
|
||||
this.backTrack.btiMem1 = this.backTrack.btiEAHi;
|
||||
}
|
||||
this.setShort(this.segEA.checkWrite(this.offEA, 2), w);
|
||||
var addr = this.segEA.checkWrite(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(this.segEA.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setShort(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3484,10 +3529,18 @@ class X86CPU extends CPU {
|
|||
this.backTrack.btiMem0 = this.backTrack.btiEALo;
|
||||
this.backTrack.btiMem1 = this.backTrack.btiEAHi;
|
||||
}
|
||||
if (!I386) {
|
||||
this.setShort(this.segEA.checkWrite(this.offEA, 2), w);
|
||||
} else {
|
||||
this.setWord(this.segEA.checkWrite(this.offEA, this.sizeData), w);
|
||||
var addr = this.segEA.checkWrite(this.offEA, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(this.segEA.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setWord(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3518,11 +3571,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getSOWord(seg, off)
|
||||
{
|
||||
if (!I386) {
|
||||
return this.getShort(seg.checkRead(off, 2));
|
||||
} else {
|
||||
return this.getWord(seg.checkRead(off, this.sizeData));
|
||||
var w;
|
||||
var addr = seg.checkRead(off, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(addr) | (this.getByte(seg.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getWord(addr);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3552,10 +3614,18 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
setSOWord(seg, off, w)
|
||||
{
|
||||
if (!I386) {
|
||||
this.setShort(seg.checkWrite(off, 2), w);
|
||||
} else {
|
||||
this.setWord(seg.checkWrite(off, this.sizeData), w);
|
||||
var addr = seg.checkWrite(off, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(seg.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setWord(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3696,8 +3766,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPShort()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(2);
|
||||
var w = (PREFETCH? this.getShortPrefetch() : this.getShort(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getShortPrefetch();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getShort(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(2), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
@ -3714,8 +3796,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPAddr()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(this.sizeAddr);
|
||||
var w = (PREFETCH? this.getAddr() : this.getAddr(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getAddr();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getAddr(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
@ -3732,8 +3826,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPWord()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(this.sizeData);
|
||||
var w = (PREFETCH? this.getWordPrefetch() : this.getWord(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getWordPrefetch();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getWord(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ class X86Seg {
|
|||
this.maskData = this.maskAddr = 0xffff;
|
||||
|
||||
this.loadV86 = this.loadReal;
|
||||
this.checkReadV86 = this.checkReadReal;
|
||||
this.checkWriteV86 = this.checkWriteReal;
|
||||
this.checkReadV86 = this.checkReadWriteReal;
|
||||
this.checkWriteV86 = this.checkReadWriteReal;
|
||||
|
||||
/*
|
||||
* Preallocated object for "probed" segment loads
|
||||
|
|
@ -109,14 +109,20 @@ class X86Seg {
|
|||
*
|
||||
* loadIDT() sets fCall to true unconditionally in protected-mode (fCall has no meaning in real-mode).
|
||||
*/
|
||||
if (this.id == 1) { // X86Seg.ID.CODE (don't use until it's defined, or the Closure Compiler won't inline it)
|
||||
if (this.id == 1 /* X86Seg.ID.CODE */) { // don't use X86Seg.ID.CODE until it's defined, or the Closure Compiler won't inline it
|
||||
this.offIP = 0;
|
||||
this.fCall = null;
|
||||
this.fStackSwitch = false;
|
||||
this.awParms = new Array(32);
|
||||
this.aCallBreaks = [];
|
||||
}
|
||||
|
||||
this.updateMode(true, fProt);
|
||||
|
||||
if (this.id == 0 /* X86Seg.ID.NULL */) {
|
||||
this.checkRead = this.checkReadWriteNone;
|
||||
this.checkWrite = this.checkReadWriteNone;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -301,34 +307,39 @@ class X86Seg {
|
|||
}
|
||||
|
||||
/**
|
||||
* checkReadReal(off, cb)
|
||||
*
|
||||
* TODO: Invoke X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
|
||||
* also, determine whether helpFault() call should include an error code, since this is happening in real-mode.
|
||||
* checkReadWriteNone(off, cb)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb is number of bytes to check (1, 2 or 4)
|
||||
* @return {number} corresponding linear address if valid, or X86.ADDR_INVALID if error (TODO: No error conditions yet)
|
||||
* @return {number} corresponding linear address
|
||||
*/
|
||||
checkReadReal(off, cb)
|
||||
checkReadWriteNone(off, cb)
|
||||
{
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
|
||||
/**
|
||||
* checkWriteReal(off, cb)
|
||||
*
|
||||
* TODO: Invoke X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
|
||||
* also, determine whether helpFault() call should include an error code, since this is happening in real-mode.
|
||||
* checkReadWriteReal(off, cb)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb is number of bytes to check (1, 2 or 4)
|
||||
* @return {number} corresponding linear address if valid, or X86.ADDR_INVALID if error (TODO: No error conditions yet)
|
||||
* @return {number} corresponding linear address
|
||||
*/
|
||||
checkWriteReal(off, cb)
|
||||
checkReadWriteReal(off, cb)
|
||||
{
|
||||
/*
|
||||
* Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
|
||||
* it to an unsigned value using ">>>"; offMax was already converted at segment load time.
|
||||
*/
|
||||
if ((off >>> 0) + cb > this.offMax) {
|
||||
if (this.cpu.model <= X86.MODEL_8088) {
|
||||
this.cpu.opFlags |= X86.OPFLAG.WRAP;
|
||||
} else {
|
||||
X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT);
|
||||
}
|
||||
}
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
|
||||
|
|
@ -1542,8 +1553,8 @@ class X86Seg {
|
|||
*/
|
||||
this.load = this.loadReal;
|
||||
this.loadIDT = this.loadIDTReal;
|
||||
this.checkRead = this.checkReadReal;
|
||||
this.checkWrite = this.checkWriteReal;
|
||||
this.checkRead = this.checkReadWriteReal;
|
||||
this.checkWrite = this.checkReadWriteReal;
|
||||
this.cpl = this.dpl = 0;
|
||||
this.addrDesc = X86.ADDR_INVALID;
|
||||
this.fStackSwitch = false;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@ permalink: /pubs/pc/reference/intel/80186/
|
|||
---
|
||||
|
||||
Intel 80186/80188 CPU Information
|
||||
---
|
||||
---------------------------------
|
||||
|
||||
80186 Errata
|
||||
---
|
||||
------------
|
||||
|
||||
* TBD
|
||||
|
||||
80186 Undocumented Instructions
|
||||
---
|
||||
-------------------------------
|
||||
|
||||
* TBD
|
||||
|
||||
Assorted Publications
|
||||
---
|
||||
---------------------
|
||||
|
||||
* TBD
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ permalink: /pubs/pc/reference/intel/80286/real_mode/
|
|||
---
|
||||
|
||||
Intel 80286 CPU: Real Mode Emulation
|
||||
---
|
||||
------------------------------------
|
||||
|
||||
[The following information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction",
|
||||
pp. 4-12]
|
||||
|
|
@ -148,7 +148,7 @@ cache base and limit loaded by [LOADALL](../loadall/) is used to generate physic
|
|||
The WAIT instructions required by the 8087 before ESC instructions can be safely executed by the 80287.
|
||||
|
||||
Discrepancies from an iAPX 86/88 Using Emulation
|
||||
---
|
||||
------------------------------------------------
|
||||
|
||||
An 80286 cannot exactly emulate an 8086/88 in all possible cases. Most differences are due to the extra protection
|
||||
checks made in the 80286 which are not made in the 8086/88. The discrepancies listed here are minor enough that very
|
||||
|
|
@ -250,7 +250,7 @@ few programs will be affected.
|
|||
RCR instructions are not restartable if their memory-based operand is in a write-protected segment.
|
||||
|
||||
Extending the Address Space of Current iAPX 86 Software
|
||||
---
|
||||
-------------------------------------------------------
|
||||
|
||||
Current iAPX 86 real mode programs can use the extended address space of the iAPX 286 in a limited manner.
|
||||
To address the extended memory, [LOADALL](../loadall/) must be used to load the descriptor cache with an base address
|
||||
|
|
@ -285,7 +285,7 @@ A semaphore must be placed around software that writes into the [LOADALL](../loa
|
|||
the software can execute [LOADALL](../loadall/) without interruption.
|
||||
|
||||
Mixing Real Mode and Protected Mode
|
||||
---
|
||||
-----------------------------------
|
||||
|
||||
The 80286 can alternate between real mode and protected mode. Some programs could be executed in real mode in the
|
||||
bottom megabyte of memory, while others execute in protected mode in the upper 15 Mbytes of memory. An external OR
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -4689,6 +4689,7 @@ var X86 = {
|
|||
NOREAD: 0x0001, // disable memory reads for the remainder of the current instruction
|
||||
NOWRITE: 0x0002, // disable memory writes for the remainder of the current instruction
|
||||
NOINTR: 0x0004, // a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
|
||||
WRAP: 0x0008, // a segment wrap-around has occurred (relevant to 8086/8088 only)
|
||||
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)
|
||||
|
|
@ -12899,8 +12900,8 @@ class X86Seg {
|
|||
this.maskData = this.maskAddr = 0xffff;
|
||||
|
||||
this.loadV86 = this.loadReal;
|
||||
this.checkReadV86 = this.checkReadReal;
|
||||
this.checkWriteV86 = this.checkWriteReal;
|
||||
this.checkReadV86 = this.checkReadWriteReal;
|
||||
this.checkWriteV86 = this.checkReadWriteReal;
|
||||
|
||||
/*
|
||||
* Preallocated object for "probed" segment loads
|
||||
|
|
@ -12925,14 +12926,20 @@ class X86Seg {
|
|||
*
|
||||
* loadIDT() sets fCall to true unconditionally in protected-mode (fCall has no meaning in real-mode).
|
||||
*/
|
||||
if (this.id == 1) { // X86Seg.ID.CODE (don't use until it's defined, or the Closure Compiler won't inline it)
|
||||
if (this.id == 1 /* X86Seg.ID.CODE */) { // don't use X86Seg.ID.CODE until it's defined, or the Closure Compiler won't inline it
|
||||
this.offIP = 0;
|
||||
this.fCall = null;
|
||||
this.fStackSwitch = false;
|
||||
this.awParms = new Array(32);
|
||||
this.aCallBreaks = [];
|
||||
}
|
||||
|
||||
this.updateMode(true, fProt);
|
||||
|
||||
if (this.id == 0 /* X86Seg.ID.NULL */) {
|
||||
this.checkRead = this.checkReadWriteNone;
|
||||
this.checkWrite = this.checkReadWriteNone;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -13117,34 +13124,39 @@ class X86Seg {
|
|||
}
|
||||
|
||||
/**
|
||||
* checkReadReal(off, cb)
|
||||
*
|
||||
* TODO: Invoke X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
|
||||
* also, determine whether helpFault() call should include an error code, since this is happening in real-mode.
|
||||
* checkReadWriteNone(off, cb)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb is number of bytes to check (1, 2 or 4)
|
||||
* @return {number} corresponding linear address if valid, or X86.ADDR_INVALID if error (TODO: No error conditions yet)
|
||||
* @return {number} corresponding linear address
|
||||
*/
|
||||
checkReadReal(off, cb)
|
||||
checkReadWriteNone(off, cb)
|
||||
{
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
|
||||
/**
|
||||
* checkWriteReal(off, cb)
|
||||
*
|
||||
* TODO: Invoke X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off+cb is beyond offMax on 80186 and up;
|
||||
* also, determine whether helpFault() call should include an error code, since this is happening in real-mode.
|
||||
* checkReadWriteReal(off, cb)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb is number of bytes to check (1, 2 or 4)
|
||||
* @return {number} corresponding linear address if valid, or X86.ADDR_INVALID if error (TODO: No error conditions yet)
|
||||
* @return {number} corresponding linear address
|
||||
*/
|
||||
checkWriteReal(off, cb)
|
||||
checkReadWriteReal(off, cb)
|
||||
{
|
||||
/*
|
||||
* Since off could be a 32-bit value with the sign bit (bit 31) set, we must convert
|
||||
* it to an unsigned value using ">>>"; offMax was already converted at segment load time.
|
||||
*/
|
||||
if ((off >>> 0) + cb > this.offMax) {
|
||||
if (this.cpu.model <= X86.MODEL_8088) {
|
||||
this.cpu.opFlags |= X86.OPFLAG.WRAP;
|
||||
} else {
|
||||
X86.helpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT);
|
||||
}
|
||||
}
|
||||
return (this.base + off)|0;
|
||||
}
|
||||
|
||||
|
|
@ -14358,8 +14370,8 @@ class X86Seg {
|
|||
*/
|
||||
this.load = this.loadReal;
|
||||
this.loadIDT = this.loadIDTReal;
|
||||
this.checkRead = this.checkReadReal;
|
||||
this.checkWrite = this.checkWriteReal;
|
||||
this.checkRead = this.checkReadWriteReal;
|
||||
this.checkWrite = this.checkReadWriteReal;
|
||||
this.cpl = this.dpl = 0;
|
||||
this.addrDesc = X86.ADDR_INVALID;
|
||||
this.fStackSwitch = false;
|
||||
|
|
@ -16737,11 +16749,12 @@ class X86CPU extends CPU {
|
|||
var newLIP = (this.regLIP >>> 0) + inc;
|
||||
if (newLIP > this.regLIPMax) {
|
||||
/*
|
||||
* There's no such thing as a GP fault on the 8086/8088, and I'm assuming that,
|
||||
* on newer processors, when the segment limit is the maximum, it's OK for IP to wrap.
|
||||
* There's no such thing as a GP fault on the 8086/8088, and I'm now assuming that,
|
||||
* on newer processors, all attempts to fetch opcodes beyond the limit trigger a fault.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || this.segCS.limit == this.segCS.maskAddr) {
|
||||
if (this.model <= X86.MODEL_8088 /* || this.segCS.limit == this.segCS.maskAddr */) {
|
||||
newLIP = this.segCS.base + ((newLIP - this.regLIPMax) & (I386? this.maskData : 0xffff));
|
||||
if (inc == 2) this.opFlags |= X86.OPFLAG.WRAP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||
}
|
||||
|
|
@ -17797,15 +17810,25 @@ class X86CPU extends CPU {
|
|||
* @this {X86CPU}
|
||||
* @param {X86Seg} seg register (eg, segDS)
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @return {number} word (16-bit) value at that address
|
||||
* @return {number} word (16-bit or 32-bit) value at that address
|
||||
*/
|
||||
getEAWord(seg, off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = seg;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = seg.checkRead(this.offEA, (I386? this.sizeData : 2));
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getWord(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(seg.checkRead(0, 1)) << 8);
|
||||
}
|
||||
else {
|
||||
w = this.getWord(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -17822,11 +17845,22 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getEAShortData(off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = this.segData;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = this.segEA.checkRead(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getShort(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(this.segEA.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getShort(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -17843,11 +17877,22 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getEAShortStack(off)
|
||||
{
|
||||
var w;
|
||||
this.segEA = this.segStack;
|
||||
this.offEA = off & (I386? this.maskAddr : 0xffff);
|
||||
this.regEA = this.segEA.checkRead(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
var w = this.getShort(this.regEA);
|
||||
if (this.opFlags & (X86.OPFLAG.NOREAD | X86.OPFLAG.WRAP)) {
|
||||
if (this.opFlags & X86.OPFLAG.NOREAD) return 0;
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regEA) | (this.getByte(this.segEA.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getShort(this.regEA);
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiEALo = this.backTrack.btiMem0;
|
||||
this.backTrack.btiEAHi = this.backTrack.btiMem1;
|
||||
|
|
@ -17923,7 +17968,19 @@ class X86CPU extends CPU {
|
|||
this.backTrack.btiMem0 = this.backTrack.btiEALo;
|
||||
this.backTrack.btiMem1 = this.backTrack.btiEAHi;
|
||||
}
|
||||
this.setShort(this.segEA.checkWrite(this.offEA, 2), w);
|
||||
var addr = this.segEA.checkWrite(this.offEA, 2);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(this.segEA.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setShort(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -17955,10 +18012,18 @@ class X86CPU extends CPU {
|
|||
this.backTrack.btiMem0 = this.backTrack.btiEALo;
|
||||
this.backTrack.btiMem1 = this.backTrack.btiEAHi;
|
||||
}
|
||||
if (!I386) {
|
||||
this.setShort(this.segEA.checkWrite(this.offEA, 2), w);
|
||||
} else {
|
||||
this.setWord(this.segEA.checkWrite(this.offEA, this.sizeData), w);
|
||||
var addr = this.segEA.checkWrite(this.offEA, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(this.segEA.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setWord(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -17989,11 +18054,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getSOWord(seg, off)
|
||||
{
|
||||
if (!I386) {
|
||||
return this.getShort(seg.checkRead(off, 2));
|
||||
} else {
|
||||
return this.getWord(seg.checkRead(off, this.sizeData));
|
||||
var w;
|
||||
var addr = seg.checkRead(off, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkReadReal(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(addr) | (this.getByte(seg.checkRead(0, 1)) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
w = this.getWord(addr);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -18023,10 +18097,18 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
setSOWord(seg, off, w)
|
||||
{
|
||||
if (!I386) {
|
||||
this.setShort(seg.checkWrite(off, 2), w);
|
||||
} else {
|
||||
this.setWord(seg.checkWrite(off, this.sizeData), w);
|
||||
var addr = seg.checkWrite(off, this.sizeData);
|
||||
if (this.opFlags & X86.OPFLAG.WRAP) {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkWriteReal(), so we also know that we're dealing with
|
||||
* a 16-bit write, which allows us to make some simplifications here.
|
||||
*/
|
||||
this.setByte(addr, w);
|
||||
this.setByte(seg.checkWrite(0, 1), w >> 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
else {
|
||||
this.setWord(addr, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18167,8 +18249,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPShort()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(2);
|
||||
var w = (PREFETCH? this.getShortPrefetch() : this.getShort(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getShortPrefetch();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getShort(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(2), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
@ -18185,8 +18279,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPAddr()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(this.sizeAddr);
|
||||
var w = (PREFETCH? this.getAddr() : this.getAddr(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getAddr();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getAddr(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
@ -18203,8 +18309,20 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
getIPWord()
|
||||
{
|
||||
var w;
|
||||
var newLIP = this.checkIP(this.sizeData);
|
||||
var w = (PREFETCH? this.getWordPrefetch() : this.getWord(this.regLIP));
|
||||
if (PREFETCH) {
|
||||
w = this.getWordPrefetch();
|
||||
} else if (!(this.opFlags & X86.OPFLAG.WRAP)) {
|
||||
w = this.getWord(this.regLIP);
|
||||
} else {
|
||||
/*
|
||||
* The WRAP flag must have been set by checkIP(), so we also know that we're dealing with
|
||||
* a 16-bit read, which allows us to make some simplifications here.
|
||||
*/
|
||||
w = this.getByte(this.regLIP) | (this.getByte(newLIP - 1) << 8);
|
||||
this.opFlags &= ~X86.OPFLAG.WRAP;
|
||||
}
|
||||
if (BACKTRACK) {
|
||||
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMem0);
|
||||
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMem1);
|
||||
|
|
@ -70181,7 +70299,7 @@ class DebuggerX86 extends Debugger {
|
|||
} while (dbgAddrIns.addr != dbgAddr.addr);
|
||||
}
|
||||
|
||||
sLine += Str.pad(sBytes, dbgAddrIns.fAddr32? 24 : 16);
|
||||
sLine += Str.pad(sBytes, dbgAddrIns.fAddr32? 25 : 17);
|
||||
sLine += Str.pad(sOpcode, 8);
|
||||
if (sOperands) sLine += ' ' + sOperands;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue