Some improvements for FOOTBALL, including support for the 386 LOADALL instruction

This commit is contained in:
Jeff Parsons 2016-01-28 22:22:36 -08:00
commit a5b9e0cae7
27 changed files with 4431 additions and 3981 deletions

View file

@ -0,0 +1,39 @@
---
layout: post
title: Early OS/2 Artifacts
date: 2016-01-23 14:00:00
category: OS/2
permalink: /blog/2016/01/23/
---
Before OS/2 was named **OS/2** by IBM on April 2, 1987, the operating system was known by many different names at
Microsoft as it evolved, including **DOS5**, **MT-DOS**, **CP-DOS**, and **ADOS**.
In late 1986, Microsoft began working on a couple different branches. One was called **SIZZLE**, where a variety of
performance improvements were tested before being merged back into the main branch.
Another branch was **FOOTBALL** (aka **PIGSKIN**), an early 80386-based prototype intended to test the viability
of the running multiple DOS applications in V86-mode. Sometimes this 80386 version was also called **386DOS**,
to distinguish it from **286DOS**. More details are in this
[FOOTBALL Design Document](/disks/pc/os2/misc/football/87058/#football-design-document).
To shed some light on those efforts, I recently added a few [OS/2 Prototype Disks](/disks/pc/os2/misc/): a small
collection of early (mostly pre-1.0) OS/2 boot disks that provide a glimpse of what some of those early OS/2 builds
looked like.
Getting these early versions of OS/2 to run in **PCjs** has been a bit of a challenge. There have been some successes
but also some lingering issues. Debugging continues.
Part of the problem is that these pre-1.0 builds still contain a few bugs. Also, the original
[OS/2 FOOTBALL Boot Disk](/disks/pc/os2/misc/football/87058/) from February 1987 was developed and
tested exclusively on Compaq DeskPro 386 machines from late 1986, so it has some uncommon 80386 dependencies:
* The [80386 LOADALL](/pubs/pc/reference/intel/80386/loadall/) instruction
* 32-bit segment register writes must modify only 16 bits of memory
**FOOTBALL** also had some specific video hardware requirements: CGA or EGA. Note that the VGA, which is what most
emulators use by default these days, did not exist in 1986. The VGA was introduced in April 1987, when IBM
unveiled their new PS/2 hardware line -- and announced OS/2.
*[@jeffpar](http://twitter.com/jeffpar)*
*January 23, 2016*

View file

@ -1,22 +0,0 @@
---
layout: post
title: Unearthing OS/2
date: 2016-01-23 14:00:00
category: OS/2
permalink: /blog/2016/01/23/
---
Before OS/2 was named **OS/2** by IBM on April 2, 1987 (as part of IBM's "Divide By Two And Conquer" initiative),
OS/2 was known variously as **CP-DOS**, **MT-DOS**, **DOS5**, or **ADOS**. Or, when referring to 80286-only and
80386-only versions: **286DOS** and **386DOS**.
And then there were all the early codes-names, like **SIZZLE**, **PIGSKIN**, and **FOOTBALL**, which were little-known
projects to help improve and/or shape the direction that OS/2 was headed.
To shed some light on those efforts, I recently added a small collection of early (mostly pre-1.0) OS/2 boot disks
that provide a glimpse of what early OS/2 builds looked like.
More details are available in [OS/2 Prototype Disks](/disks/pc/os2/misc/).
*[@jeffpar](http://twitter.com/jeffpar)*
*January 23, 2016*

View file

@ -10,8 +10,8 @@
<rom id="romEGA" addr="0xc0000" size="0x4000" file="/devices/pc/video/ibm/ega/ibm-ega.json" notify="videoEGA"/>
<rom id="romBIOS" addr="0xf8000" size="0x8000" alias="[0xf0000,0xffff0000,0xffff8000]" file="/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.json"/>
<video ref="/devices/pc/video/ibm/ega/ibm-ega-128kb-autolockfs.xml"/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal-functions.xml"/>
<debugger id="debugger" messages="fault" commands=""/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal-sysreq.xml"/>
<debugger id="debugger" messages="" commands=""/>
<panel ref="/devices/pc/panel/wide386.xml"/>
<fdc ref="/disks/pc/library.xml" automount='{A: {name: "PC-DOS 3.20 (Disk 1)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK1.json"}, B: {name: "PC-DOS 3.20 (Disk 2)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK2.json"}}'/>
<hdcNone id="hdcAT" type="at" drives='[{name:"20Mb Hard Disk",type:2}]'/>

View file

@ -26,6 +26,7 @@ Instruction documentation is also available:
Our [Publication Archive](/pubs/) includes these PC-related resources:
* [Datasheets](/pubs/pc/datasheets/)
* [Intel CPU Documents](/pubs/pc/reference/intel/)
* [Magazines](/pubs/pc/magazines/)
* [Programming Guides](/pubs/pc/programming/) (eg, [OS/2](/pubs/pc/programming/os2/))
* [Reference Manuals](/pubs/pc/reference/)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -25,10 +25,10 @@ it is a BCD digit.
The following example shows how to add BCD numbers then adjust the result:
MOV AH,0 ;Clear AH for most significant digit
MOV AL,6 ;BCD 6 in AL
ADD AL,5 ;Add BCD 5 to digit in AL
AAA ;AH=1, AL=1 representing BCD 11.
MOV AH,0 ; Clear AH for most significant digit
MOV AL,6 ; BCD 6 in AL
ADD AL,5 ; Add BCD 5 to digit in AL
AAA ; AH=1, AL=1 representing BCD 11.
### Algorithm

View file

@ -4793,6 +4793,14 @@ if (DEBUGGER) {
if (typeCPU == null) typeCPU = type >> Debugger.TYPE_CPU_SHIFT;
if (iIns == Debugger.INS.LOADALL) {
if (typeCPU == Debugger.CPU_80286) {
sOperands = "[%800]";
} else if (typeCPU == Debugger.CPU_80386) {
sOperands = "ES:[" + (dbgAddr.fAddr32? 'E':'') + "DI]";
}
}
var typeSize = type & Debugger.TYPE_SIZE;
if (typeSize == Debugger.TYPE_NONE) {
continue;

View file

@ -193,7 +193,7 @@ var X86 = {
SEL: {
RPL: 0x0003, // requested privilege level (0-3)
LDT: 0x0004, // table indicator (0: GDT, 1: LDT)
MASK: 0xFFF8 // table index
MASK: 0xFFF8 // table offset
},
DESC: { // Descriptor Table Entry
LIMIT: { // LIMIT bits 0-15 (or OFFSET if this is an INTERRUPT or TRAP gate)

View file

@ -1123,8 +1123,10 @@ X86CPU.prototype.resetRegs = function()
* More recently, opCS was added to selectively snapshot an instruction's original CS in case an
* exception occurs accessing the stack after a new CS has been loaded, allowing the exception handler
* to recover the old CS and make instructions like CALLF restartable; otherwise, opCS should remain -1.
*
* Ditto for opSS and the SS register.
*/
this.opCS = -1;
this.opCS = this.opSS = -1;
this.opLIP = this.opLSP = X86.ADDR_INVALID;
/*
@ -2131,7 +2133,13 @@ X86CPU.prototype.setLIP = function(addr)
{
this.regLIP = addr|0;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;
/*
* TODO: Verify the proper source for CPL. Should it come from segCS.cpl or segCS.dpl?
* Also, note that LOADALL386 wants it to come from segSS.dpl.
*/
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
if (I386) this.resetSizes();
/*
* Here, we need to additionally test whether the prefetch buffer (adwPrefetch) has been allocated yet,
@ -2983,7 +2991,7 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
* probeAddr(addr, size, fLinear)
*
* Used by the Debugger to probe addresses without risk of triggering a page fault, and by internal
* functions, like fnFaultMessage(), that must also avoid triggering faults, since they're not part of
* functions, like fnCheckFault(), that must also avoid triggering faults, since they're not part of
* standard CPU operation.
*
* Since originally written, I've also relaxed the requirement that the request be contained entirely
@ -3767,10 +3775,21 @@ X86CPU.prototype.popWord = function()
/**
* pushData(d, width, size)
*
* This function serves two very limited purposes: 1) the ability to push data according to a previous
* operand size (width), and 2) the ability to write fewer bytes than the width if necessary (size).
*
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
* and offset) must still be pushed as 32-bit values.
*
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
* only the low 16 bits should be written (size must be 2). For all other kinds of pushes, width and size are
* impliedly the same.
*
* @this {X86CPU}
* @param {number} d is the data to push at current SP; SP decreased by size
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
* @param {number} size is the size of the data to push, in bytes (must be > 0 and <= width)
* @param {number} size is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
*/
X86CPU.prototype.pushData = function(d, width, size)
{
@ -3800,13 +3819,13 @@ X86CPU.prototype.pushData = function(d, width, size)
switch(size) {
case 1:
this.setByte(regLSP, d);
break
break;
case 2:
this.setShort(regLSP, d);
break
break;
case 4:
this.setLong(regLSP, d);
break
break;
default:
this.assert(false);
break;

View file

@ -554,11 +554,12 @@ X86.fnCALLw = function(dst, src)
X86.fnCALLF = function(off, sel)
{
/*
* Since we always push the return address AFTER calling setCSIP(), and since either push could trigger
* Since we always push the return address AFTER calling setCSIP(), and since either push could trigger a
* fault (eg, segment fault, page fault, etc), we must not only snapshot regLSP into opLSP, but also the
* current CS into opCS, so that fnFault() can always make CALLF restartable.
* current CS into opCS, so that fnFault() can always make CALLF restartable. Ditto for opSS and the SS register.
*/
this.opCS = this.getCS();
this.opSS = this.getSS();
this.opLSP = this.regLSP;
var oldIP = this.getIP();
var oldSize = (I386? this.sizeData : 2);
@ -572,7 +573,7 @@ X86.fnCALLF = function(off, sel)
this.pushData(oldIP, oldSize, oldSize);
}
this.opLSP = X86.ADDR_INVALID;
this.opCS = -1;
this.opCS = this.opSS = -1;
};
/**
@ -3942,8 +3943,7 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
* Prior to each new burst of instructions, stepCPU() sets fComplete to true, and the only (normal) way
* for fComplete to become false is through stopCPU(), which isn't ordinarily called, except by the Debugger.
*/
this.resetSizes();
this.setIP(this.opLIP - this.segCS.base);
this.setLIP(this.opLIP);
}
else if (this.model >= X86.MODEL_80186) {
@ -3951,16 +3951,24 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
if (this.nFault < 0) {
/*
* Single-fault (error code is passed through, and the responsible instruction is restartable;
* the call to resetSizes() is critical, otherwise setIP() may update IP with the wrong size if
* the current instruction contains an OPERAND size override).
* Single-fault (error code is passed through, and the responsible instruction is restartable.
*/
this.resetSizes();
if (this.opCS != -1) {
/*
* HACK: We must slam 3 into this.segCS.cpl to ensure that loading the original CS segment doesn't
* fail. For example, if we faulted in the middle of a ring transition that loaded CS with a higher
* privilege (lower CPL) code segment, then our attempt here to reload the lower privilege (higher CPL)
* code segment could be viewed as a privilege violation (which it would be outside this context).
*/
this.segCS.cpl = 3;
this.setCS(this.opCS);
this.opCS = -1;
}
this.setIP(this.opLIP - this.segCS.base);
this.setLIP(this.opLIP);
if (this.opSS != -1) {
this.setSS(this.opSS);
this.opSS = -1;
}
if (this.opLSP !== X86.ADDR_INVALID) {
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
this.opLSP = X86.ADDR_INVALID;
@ -3970,22 +3978,24 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
/*
* Double-fault (error code is always zero, and the responsible instruction is not restartable)
*/
nError = 0; nFault = X86.EXCEPTION.DF_FAULT;
nError = 0;
nFault = X86.EXCEPTION.DF_FAULT;
}
else {
/*
* Triple-fault (usually referred to in Intel literature as a "shutdown", but at least on the 80286,
* it's actually a "reset")
*/
nFault = -1; nError = 0;
nError = 0;
nFault = -1;
this.resetRegs();
fDispatch = fHalt = false;
}
}
if (X86.fnFaultMessage.call(this, nFault, nError, fHalt)) {
if (X86.fnCheckFault.call(this, nFault, nError, fHalt)) {
/*
* If this is a fault that would normally be dispatched BUT fnFaultMessage() wants us to halt,
* If this is a fault that would normally be dispatched BUT fnCheckFault() wants us to halt,
* then we throw a bogus fault number (-1), simply to interrupt the current instruction in exactly
* the same way that a dispatched fault would interrupt it.
*/
@ -4060,7 +4070,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
};
/**
* fnFaultMessage(nFault, nError, fHalt)
* fnCheckFault(nFault, nError, fHalt)
*
* Aside from giving the Debugger an opportunity to report every fault, this also gives us the ability to
* halt exception processing in tracks: return true to prevent the fault handler from being dispatched.
@ -4076,7 +4086,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
* @param {boolean} [fHalt] (true to halt the CPU, false to not, undefined if "it depends")
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
*/
X86.fnFaultMessage = function(nFault, nError, fHalt)
X86.fnCheckFault = function(nFault, nError, fHalt)
{
var bitsMessage = Messages.FAULT;

View file

@ -161,6 +161,11 @@ X86.opLOADALL286 = function()
this.segCS.loadDesc6(0x83C, this.getShort(0x822));
this.segSS.loadDesc6(0x842, this.getShort(0x820));
this.segDS.loadDesc6(0x848, this.getShort(0x81E));
/*
* Unlike LOADALL386, there's no requirement for calling setPS() before loading segment registers;
* in fact, since we're not passing a CPL to setPS(), it may be preferable to have CS (and perhaps SS)
* already loaded, so that setPS() can query the CPL. TODO: Verify that CPL is set correctly.
*/
this.setPS(this.getShort(0x818));
/*
* It's important to call setIP() and setSP() *after* the segCS and segSS loads, so that the CPU's
@ -178,15 +183,15 @@ X86.opLOADALL286 = function()
*/
this.addrGDT = this.getShort(0x84E) | (this.getByte(0x850) << 16);
this.addrGDTLimit = this.addrGDT + this.getShort(0x852);
this.segLDT.loadDesc6(0x854, this.getShort(0x81C));
this.addrIDT = this.getShort(0x85A) | (this.getByte(0x85C) << 16);
this.addrIDTLimit = this.addrIDT + this.getShort(0x85E);
this.segLDT.loadDesc6(0x854, this.getShort(0x81C));
this.segTSS.loadDesc6(0x860, this.getShort(0x816));
/*
* Oddly, the above Intel document gives two contradictory cycle counts for LOADALL: 190 and 195. I go with 195,
* since both the PC Magazine Programmer's Technical Reference and Robert Collins (http://www.rcollins.org/articles/loadall/tspec_a3_doc.html)
* agree.
* Oddly, the above Intel document gives two contradictory cycle counts for LOADALL: 190 and 195.
* I'm going with 195, since both the PC Magazine Programmer's Technical Reference and Robert Collins
* (http://www.rcollins.org/articles/loadall/tspec_a3_doc.html) agree.
*/
this.nStepCycles -= 195;
@ -221,7 +226,8 @@ X86.opCLTS = function()
*
* op=0x0F,0x07 (LOADALL ES:[EDI])
*
* Excerpt from Intel Internal Correspondence on "386 LOADALL Instruction" (undated):
* Excerpt from Intel Internal Correspondence on "386 LOADALL Instruction" (undated), available as part of the
* PCjs Project at http://www.pcjs.org/pubs/pc/reference/intel/80386/loadall/
*
* 1.5. 386 LOADALL Memory Format
*
@ -234,6 +240,7 @@ X86.opCLTS = function()
* be DWORD aligned.
*
* Offset Register
* ------ --------
* 0x00 CR0
* 0x04 EFLAGS
* 0x08 EIP
@ -247,44 +254,44 @@ X86.opCLTS = function()
* 0x28 EAX
* 0x2C DR6
* 0x30 DR7
* 0x34 TR (TSS Selector--Word)
* 0x38 LDTR (LDT Selector--Word)
* 0x34 TSSR(TSSSelector-Word)
* 0x38 LDTR(LDTSelector-Word)
* 0x3C GS
* 0x40 FS
* 0x44 DS
* 0x48 SS
* 0x4C CS
* 0x50 ES
* 0x54 TSS (AR)
* 0x58 TSS (BASE)
* 0x5C TSS (LIMIT)
* 0x60 IDT (AR)
* 0x64 IDT (BASE)
* 0x68 IDT (LIMIT)
* 0x6C GDT (AR)
* 0x70 GDT (BASE)
* 0x74 GDT (LIMIT)
* 0x78 LDT (AR)
* 0x7C LDT (BASE)
* 0x80 LDT (LIMIT)
* 0x84 GS (AR)
* 0x88 GS (BASE)
* 0x8C GS (LIMIT)
* 0x90 FS (AR)
* 0x94 FS (BASE)
* 0x98 FS (LIMIT)
* 0x9C DS (AR)
* 0xA0 DS (BASE)
* 0xA4 DS (LIMIT)
* 0xA8 SS (AR)
* 0xAC SS (BASE)
* 0xB0 SS (LIMIT)
* 0xB4 CS (AR)
* 0xB8 CS (BASE)
* 0xBC CS (LIMIT)
* 0xC0 ES (AR)
* 0xC4 ES (BASE)
* 0xC8 ES (LIMIT)
* 0x54 TSS(AR)
* 0x58 TSS(BASE)
* 0x5C TSS(LIMIT)
* 0x60 IDT(AR)
* 0x64 IDT(BASE)
* 0x68 IDT(LIMIT)
* 0x6C GDT(AR)
* 0x70 GDT(BASE)
* 0x74 GDT(LIMIT)
* 0x78 LDT(AR)
* 0x7C LDT(BASE)
* 0x80 LDT(LIMIT)
* 0x84 GS(AR)
* 0x88 GS(BASE)
* 0x8C GS(LIMIT)
* 0x90 FS(AR)
* 0x94 FS(BASE)
* 0x98 FS(LIMIT)
* 0x9C DS(AR)
* 0xA0 DS(BASE)
* 0xA4 DS(LIMIT)
* 0xA8 SS(AR)
* 0xAC SS(BASE)
* 0xB0 SS(LIMIT)
* 0xB4 CS(AR)
* 0xB8 CS(BASE)
* 0xBC CS(LIMIT)
* 0xC0 ES(AR)
* 0xC4 ES(BASE)
* 0xC8 ES(LIMIT)
*
* Each descriptor entry consists of 3 pieces:
*
@ -292,10 +299,11 @@ X86.opCLTS = function()
* BASE
* LIMIT
*
* The AR part has the same format as the second dword of a segment descriptor except that only the AR byte (bits 8-15)
* and the G and B/D bits (bits 23 and 22) are used. All other bits in the AR field are ignored. The BASE and LIMIT parts
* contain full 32-bit values, fully expanded and unscrambled from the 386 descriptor. In particular, the LIMIT field
* loaded for a page granular segment gives a byte granular limit, so should contain the page limit*4096 plus 4095.
* The AR part has the same format as the second dword of a segment descriptor except that only the AR byte
* (bits 8-15) and the G and B/D bits (bits 23 and 22) are used. All other bits in the AR field are ignored.
* The BASE and LIMIT parts contain full 32-bit values, fully expanded and unscrambled from the 386 descriptor.
* In particular, the LIMIT field loaded for a page granular segment gives a byte granular limit, so should
* contain the page limit*4096 plus 4095.
*
* @this {X86CPU}
*/
@ -308,16 +316,59 @@ X86.opLOADALL386 = function()
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, 0, true);
return;
}
/*
* TODO: Implement
*/
X86.opUndefined.call(this);
var addr = this.segES.checkRead(this.regEDI & this.maskAddr, 0xCC);
if (addr !== X86.ADDR_INVALID) {
X86.fnLCR0.call(this, this.getLong(addr));
/*
* We need to call setPS() before loading any segment registers, because if the Virtual 8086 Mode (VM)
* bit is set in EFLAGS, the segment registers need to know that.
*/
var accSS = this.getLong(addr + 0xA8);
var cpl = (accSS & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
this.setPS(this.getLong(addr + 0x04), cpl);
/*
* TODO: We have no use for the GDT(AR) at offset 0x6C or the IDT(AR) at offset 0x60, because
* we don't manage them as segment registers. Should we?
*/
this.addrGDT = this.getLong(addr + 0x70);
this.addrGDTLimit = this.addrGDT + this.getLong(addr + 0x74);
this.addrIDT = this.getLong(addr + 0x64);
this.addrIDTLimit = this.addrIDT + this.getLong(addr + 0x68);
this.segLDT.loadDesc(this.getLong(addr + 0x38), this.getLong(addr + 0x78), this.getLong(addr + 0x7C), this.getLong(addr + 0x80));
this.segTSS.loadDesc(this.getLong(addr + 0x34), this.getLong(addr + 0x54), this.getLong(addr + 0x58), this.getLong(addr + 0x5C));
this.regEDI = this.getLong(addr + 0x0C);
this.regESI = this.getLong(addr + 0x10);
this.regEBP = this.getLong(addr + 0x14);
this.regEBX = this.getLong(addr + 0x1C);
this.regEDX = this.getLong(addr + 0x20);
this.regECX = this.getLong(addr + 0x24);
this.regEAX = this.getLong(addr + 0x28);
this.segGS.loadDesc(this.getLong(addr + 0x3C), this.getLong(addr + 0x84), this.getLong(addr + 0x88), this.getLong(addr + 0x8C));
this.segFS.loadDesc(this.getLong(addr + 0x40), this.getLong(addr + 0x90), this.getLong(addr + 0x94), this.getLong(addr + 0x98));
this.segDS.loadDesc(this.getLong(addr + 0x44), this.getLong(addr + 0x9C), this.getLong(addr + 0xA0), this.getLong(addr + 0xA4));
this.segSS.loadDesc(this.getLong(addr + 0x48), accSS, this.getLong(addr + 0xAC), this.getLong(addr + 0xB0));
this.segCS.loadDesc(this.getLong(addr + 0x4C), this.getLong(addr + 0xB4), this.getLong(addr + 0xB8), this.getLong(addr + 0xBC));
this.segES.loadDesc(this.getLong(addr + 0x50), this.getLong(addr + 0xC0), this.getLong(addr + 0xC4), this.getLong(addr + 0xC8));
/*
* It's important to call setIP() and setSP() *after* the segCS and segSS loads, so that the CPU's
* linear IP and SP registers (regLIP and regLSP) will be updated properly. Ordinarily that would be
* taken care of by simply using the CPU's setCS() and setSS() functions, but those functions call the
* default descriptor load() functions, and obviously here we must use loadDesc() instead.
*/
this.setIP(this.getLong(addr + 0x08));
this.setSP(this.getLong(addr + 0x18));
/*
* TODO: We need to factor out the code that updates DR6 and DR7 from X86.opMOVdr(), so that we can
* more easily update DR6 and DR7 (which we're simply ignoring for now).
*/
}
/*
* According to Robert Collins (http://www.rcollins.org/articles/loadall/tspec_a3_doc.html), the 80386 LOADALL
* takes 122 cycles.
* takes 122 cycles. Also, according the above-mentioned Intel document, if the memory buffer is not DWORD aligned,
* execution time will DOUBLE.
*/
this.nStepCycles -= 122;
this.nStepCycles -= (122 << ((addr & 0x3)? 1 : 0));
};
/**
@ -547,10 +598,18 @@ X86.opMOVrt = function()
var bModRM = this.getIPByte();
var iSrc = (bModRM & 0x38) >> 3;
/*
* Only TR6 and TR7 are defined, and only for the 80386 and 80486. From the PC Magazine Prog. TechRef, p.64:
*
* "The 80386 provides two 32-bit test registers, TR6 and TR7, as a mechanism for programmers to verify proper
* operation of the Translation Lookaside Buffer (TLB) when power is applied to the chip. The TLB is a cache used
* internally by the 80386 to translate linear addresses to physical addresses."
*/
if (iSrc < 6) {
X86.opUndefined.call(this);
return;
}
this.setReg(bModRM & 0x7, this.regTR[iSrc]);
this.nStepCycles -= 12;
@ -585,6 +644,13 @@ X86.opMOVtr = function()
var bModRM = this.getIPByte();
var iDst = (bModRM & 0x38) >> 3;
/*
* Only TR6 and TR7 are defined, and only for the 80386 and 80486. From the PC Magazine Prog. TechRef, p.64:
*
* "The 80386 provides two 32-bit test registers, TR6 and TR7, as a mechanism for programmers to verify proper
* operation of the Translation Lookaside Buffer (TLB) when power is applied to the chip. The TLB is a cache used
* internally by the 80386 to translate linear addresses to physical addresses."
*/
if (iDst < 6) {
X86.opUndefined.call(this);
return;
@ -1679,7 +1745,7 @@ X86.aOps0F[0xFF] = X86.opInvalid;
if (I386) {
X86.aOps0F386 = [];
X86.aOps0F386[0x05] = X86.opInvalid; // the 80286 LOADALL opcode is invalid on the 80386
X86.aOps0F386[0x05] = X86.opInvalid; // the 80286 LOADALL opcode (LOADALL286) is invalid on the 80386
X86.aOps0F386[0x07] = X86.opLOADALL386;
X86.aOps0F386[0x20] = X86.opMOVrc;
X86.aOps0F386[0x21] = X86.opMOVrd;

View file

@ -4457,7 +4457,7 @@ X86.opInvalid = function()
X86.opUndefined = function()
{
this.setIP(this.opLIP - this.segCS.base);
this.setError("Undefined opcode " + str.toHexByte(this.bus.getByteDirect(this.regLIP)) + " at " + str.toHexLong(this.regLIP));
this.setError("Undefined opcode " + str.toHexByte(this.getByte(this.regLIP)) + " at " + str.toHexLong(this.regLIP));
this.stopCPU();
};

View file

@ -551,10 +551,44 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
};
*/
/**
* loadDesc(sel, acc, base, limit)
*
* Used to manually load a segment register from the data provided (see LOADALL386).
*
* @this {X86Seg}
* @param {number} sel
* @param {number} acc
* @param {number} base
* @param {number} limit
*/
X86Seg.prototype.loadDesc = function(sel, acc, base, limit)
{
this.sel = sel;
this.base = base;
this.limit = limit;
this.offMax = (limit >>> 0) + 1;
this.acc = acc;
this.type = (acc & X86.DESC.ACC.TYPE.MASK);
this.ext = (acc >> 16) & (X86.DESC.EXT.BIG | X86.DESC.EXT.LIMITPAGES);
var addrDT = (sel & X86.SEL.LDT)? this.cpu.segLDT.base : this.cpu.addrGDT;
this.addrDesc = (addrDT + (sel & X86.SEL.MASK))|0;
/*
* NOTE: This code must take care to leave the mode of the TSS, LDT, and VER segment registers alone;
* in particular, we must not allow a real-mode LOADALL to modify their mode, because the rest of PCjs
* assumes that their mode will never change (they were allocated with fProt set to true).
*/
if (this.id < X86Seg.ID.TSS) this.updateMode(true);
if (DEBUG) this.messageSeg(sel, base, limit, this.type);
};
/**
* loadDesc6(addrDesc, sel)
*
* Used to load a protected-mode selector that refers to a 6-byte "descriptor cache" (aka LOADALL) entry:
* Used to load a protected-mode selector that refers to a 6-byte "descriptor cache" entry (see LOADALL286):
*
* word 0: base address low
* word 1: base address high (0-7), segment type (8-11), descriptor type (12), DPL (13-14), present bit (15)
@ -584,8 +618,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
/*
* NOTE: This code must take care to leave the mode of the TSS, LDT, and VER segment registers alone;
* in particular, we must not allow a real-mode LOADALL to modify their mode, because the rest of PCjs
* assumes that their mode will never change (they were allocated with fProt set to true), so there's
* no code to force them back into protected-mode.
* assumes that their mode will never change (they were allocated with fProt set to true).
*/
if (this.id < X86Seg.ID.TSS) this.updateMode(true);
@ -706,44 +739,48 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
var rpl = sel & X86.SEL.RPL;
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
var sizeGate, selCode, cplOld, cplNew, fIDT;
var sizeGate = -1, selCode, cplOld, cplNew, fIDT;
var addrTSS, offSP, lenSP, regSPPrev, regSSPrev, regPSClear, regSP;
/*
* TODO: As discussed below for X86Seg.ID.DATA, it's likely that testing the PRESENT bit should
* be performed *after* checking the other, more serious potential problems.
*/
if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) {
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
if (!selMasked) {
/*
* selMasked is really the descriptor table offset, and a zero offset is fine for the IDT;
* it MAY even be OK for the LDT. But it's definitely not OK for the GDT; a null selector
* is allowed in any of DS, ES, SS, FS, or GS, but never CS). Since there's no parameter
* that tells us which table we're using, we have to check manually.
*
* If we ARE attempting to load a null selector from the GDT, then we zero type, which ensures
* that sizeGate will remain invalid, triggering a GP_FAULT below.
*/
if (addrDesc >= cpu.addrGDT && addrDesc < cpu.addrGDTLimit) type = 0;
}
/*
* Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic cpu.segCS.cpl
*/
if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY) {
sizeGate = 0;
if (rpl > this.cpl) {
/*
/*.
* If fCall is false, then we must have a RETF to a less privileged segment, which is OK.
*
* Otherwise, we must be dealing with a CALLF or JMPF to a less privileged segment, in which
* case either DPL == CPL *or* the new segment is conforming and DPL <= CPL.
*/
if (fCall !== false && !(dpl == this.cpl || (type & X86.DESC.ACC.TYPE.CONFORMING) && dpl <= this.cpl)) {
return X86.ADDR_INVALID;
sizeGate = -1;
if (fCall === false || dpl == this.cpl || (type & X86.DESC.ACC.TYPE.CONFORMING) && dpl <= this.cpl) {
/*
* It's critical that any stack switch occur with the operand size in effect at the time of
* the current instruction, BEFORE any calls to updateMode() and resetSizes(), otherwise the
* operand size (or operand override) in effect on an instruction like IRETD will be ignored.
*/
regSP = cpu.popWord();
cpu.setSS(cpu.popWord(), true);
cpu.setSP(regSP);
this.fStackSwitch = true;
sizeGate = 0;
}
/*
* It's critical that any stack switch occur with the operand size in effect at the time of
* the current instruction, BEFORE any calls to updateMode() and resetSizes(), otherwise the
* operand size (or operand override) in effect on an instruction like IRETD will be ignored.
*/
regSP = cpu.popWord();
cpu.setSS(cpu.popWord(), true);
cpu.setSP(regSP);
this.fStackSwitch = true;
}
sizeGate = 0;
}
else if (type == X86.DESC.ACC.TYPE.TSS286 || type == X86.DESC.ACC.TYPE.TSS386) {
if (!this.switchTSS(sel, fCall)) {
@ -788,7 +825,9 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
return this.base;
}
if (sizeGate) {
if (sizeGate > 0 && !(acc & X86.DESC.ACC.PRESENT)) sizeGate = 0;
if (sizeGate > 0) {
/*
* Note that since GATE_INT/GATE_TRAP descriptors should appear in the IDT only, that means sel
* will actually be nIDT * 8, which means the rpl will always be zero; additionally, the nWords
@ -843,7 +882,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
offSP = (cplNew << 2) + X86.TSS286.CPL0_SP;
lenSP = 2;
} else {
offSP = (cplNew << 2) + X86.TSS386.CPL0_ESP;
offSP = (cplNew << 3) + X86.TSS386.CPL0_ESP;
lenSP = 4;
}
selStack = cpu.getShort(addrTSS + offSP + lenSP);
@ -952,9 +991,13 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
}
}
if (sizeGate !== 0) {
var nError = (sel & X86.ERRCODE.SELMASK) | (fIDT? X86.ERRCODE.IDT : 0);
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nError);
if (sizeGate != 0) {
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, (sel & X86.ERRCODE.SELMASK) | (fIDT? X86.ERRCODE.IDT : 0));
return X86.ADDR_INVALID;
}
if (!(acc & X86.DESC.ACC.PRESENT)) {
X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, (sel & X86.ERRCODE.SELMASK) | (fIDT? X86.ERRCODE.IDT : 0));
return X86.ADDR_INVALID;
}
break;
@ -1011,14 +1054,14 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
break;
case X86Seg.ID.STACK:
if (!(acc & X86.DESC.ACC.PRESENT)) {
X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
}
if (!selMasked || type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.WRITABLE)) != X86.DESC.ACC.TYPE.WRITABLE) {
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
}
if (!(acc & X86.DESC.ACC.PRESENT)) {
X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel & X86.ERRCODE.SELMASK);
return X86.ADDR_INVALID;
}
break;
case X86Seg.ID.TSS:
@ -1270,7 +1313,7 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
offSS = X86.TSS386.TASK_SS;
offSP = X86.TSS386.TASK_ESP;
if (this.cpl < cplOld) {
offSP = (this.cpl << 2) + X86.TSS386.CPL0_ESP;
offSP = (this.cpl << 3) + X86.TSS386.CPL0_ESP;
offSS = offSP + 4;
}
cpu.setSS(cpu.getShort(addrNew + offSS), true);

View file

@ -9,6 +9,6 @@ PC Reference Manuals
An assortment of PC reference manuals, including:
* [IBM PC References](ibm/)
* [IBM PC Manuals](ibm/)
* [Intel CPU Documents](intel/)
* [Microsoft Specifications](microsoft/)

View file

@ -9,6 +9,7 @@ Intel 80286 CPU Information
### 80286 Errata
* [Early 80286 Errata of Interest](early_errata/#early-80286-errata-of-interest)
* [ARPL Behavior](arpl/)
* [Coprocessor Operand Beyond Segment Limit](b2_b3_info/#coprocessor-operand-partially-beyond-limit-of-erc-segment)
* [Instructions Longer than 10 Bytes](extra_prefixes/)
@ -16,9 +17,8 @@ Intel 80286 CPU Information
* [Non-Restartable Protection Violations](b2_b3_info/#non-restartable-protection-violations)
* [POPF Behavior](b2_b3_info/#popf-behavior)
* [REP MOVS and REP INS Restartability](rep_restart/)
* [Early 80286 Errata of Interest](early_errata/#early-80286-errata-of-interest)
### 80286 Undocumented Opcodes
### 80286 Undocumented Instructions
* [LOADALL](loadall/)

View file

@ -1,12 +1,14 @@
---
layout: page
title: "Intel 80286 CPU Documentation: ARPL"
title: "Intel 80286 CPU Errata: ARPL"
permalink: /pubs/pc/reference/intel/80286/arpl/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Errata: ARPL
---
[The following information is from an Intel document titled "80286 ARPL and Overlength Instructions, 15 October 1984"]
### ARPL (63H)
When the second operand of the ARPL instruction (as described in the iAPX286 Programmer's Reference Manual) is a null
@ -15,5 +17,3 @@ but will be described in future revisions of the "iAPX286 Programmer's Reference
This functionality of the ARPL is not believed not to be a problem, and there are no plans to change this functionality
of the ARPL instruction.
[This information is from an Intel document titled "80286 ARPL and Overlength Instructions, 15 October 1984"]

View file

@ -1,12 +1,14 @@
---
layout: page
title: "Intel 80286 CPU Documentation: B-2/B-3 Steppings"
title: "Intel 80286 CPU Errata: B-2/B-3 Steppings"
permalink: /pubs/pc/reference/intel/80286/b2_b3_info/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Errata: B-2/B-3 Steppings
---
[The following information is from a 7-page Intel document titled "80286(B-2/B-3) Information Sheet, 21 November 1984"]
### 80286 B-2/B-3 Steppings
This applies to S-spec #40093 and to S-spec #54012 of the 80286, since they contain a (B-2/B-3) stepping of the 80286.
@ -197,5 +199,3 @@ handled if the 80286 interrupts are always disabled before programming the inter
is performed no unmasked interrupt is active. The interrupt handler for IR7 can read ISR7 of the 8259A to tell if this was a real
interrupt on IR7. The ISR7 bit will be 0 if at the time the interrupt was acknowledged, no unmasked IR inputs of the 8259A were
active ("phantom" interrupt).
[This information is from a 7-page Intel document titled "80286(B-2/B-3) Information Sheet, 21 November 1984"]

View file

@ -1,12 +1,16 @@
---
layout: page
title: "Intel 80286 CPU Documentation: Early Errata"
title: "Intel 80286 CPU Errata: Early Problems"
permalink: /pubs/pc/reference/intel/80286/early_errata/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Errata: Early Problems
---
[The following information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction".
NOTE: The initial reference to LOADALL as "opcode 0F04H" and the subsequent references to the "0F05H opcode" are exactly
as they appear in the original document. Whether or not they were mistakes is unknown.]
### Exceptions from Undefined Opcodes and String Instructions
The exception 13 handler will probably use a lookup table for the opcode byte of the instruction causing exception
@ -101,5 +105,3 @@ steppings of the 80286 and are fixed in later steppings of the 80286.
0-wait memory for the data values or be sure bit 0 of memory location 804H is zero. HOLD requests and processor
extension data transfers should be inhibited while [LOADALL](../loadall/) is running. Later steppings of the 80286 will correctly
load the MSW during [LOADALL](../loadall/) with HOLD and processor extension transfers.
[This information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]

View file

@ -1,12 +1,14 @@
---
layout: page
title: "Intel 80286 CPU Documentation: Extra Prefixes"
title: "Intel 80286 CPU Errata: Extra Prefixes"
permalink: /pubs/pc/reference/intel/80286/extra_prefixes/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Errata: Extra Prefixes
---
[The following information is from an Intel document titled "80286 ARPL and Overlength Instructions, 15 October 1984"]
### Instructions Longer than 10 Bytes
When the CPU detects an instruction that is illegal due to being greater than 10 bytes in length, it generates an
@ -16,5 +18,3 @@ greater than ten bytes can occur is by using the assembler to intentionally plac
There are no plans to change this functionality of the 80286 and future editions of the "iAPX286 Programmer's Reference
Manual" and 80286 datasheet will accurately describe how 80286 reacts to instructions greater than 10 bytes in length.
[This information is from an Intel document titled "80286 ARPL and Overlength Instructions, 15 October 1984"]

View file

@ -1,12 +1,14 @@
---
layout: page
title: "Intel 80286 CPU Documentation: LOADALL"
title: "Intel 80286 LOADALL Instruction"
permalink: /pubs/pc/reference/intel/80286/loadall/
---
Intel 80286 CPU Documentation
Intel 80286 LOADALL Instruction
---
[The following information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]
### LOADALL (0F05H)
The iAPX 286 microprocessor (part number 80286) has an undocumented instruction used by Intel test programs to
@ -154,5 +156,3 @@ For proper protected mode operation, the following is required:
4. The DPL fields of the ES and DS descriptors should be 3 to prevent their being zeroed by RET or IRET
instructions.
[This information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]

View file

@ -4,9 +4,11 @@ title: "Intel 80286 CPU Documentation: Real Mode"
permalink: /pubs/pc/reference/intel/80286/real_mode/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Documentation: Real Mode
---
[The following information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]
### Executing Real Mode Programs in Protected Mode
An iAPX 86/88 program using real mode addressing can be executed in protected mode with full protection between
@ -300,5 +302,3 @@ Interrupts must be handled specially. Interrupt handlers for both real mode and
times. If an interrupt handler needs to access a data area, that data area must be addressable from both real and
protected mode. The real mode interrupt table would be would be kept at location 000000H. The protected mode IDT could
be anywhere. [LOADALL](../loadall/) will switch to the protected interrupt table.
[This information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]

View file

@ -1,12 +1,14 @@
---
layout: page
title: "Intel 80286 CPU Documentation: REP Restart"
title: "Intel 80286 CPU Errata: REP Restart"
permalink: /pubs/pc/reference/intel/80286/rep_restart/
---
Intel 80286 CPU Documentation
Intel 80286 CPU Errata: REP Restart
---
[The following information is from an Intel document titled "80286 REP MOVS and REP INS Restartability, 15 October 1984"]
### 80286 REP MOVS and REP INS Restartability
The repeated string instructions on 80286 were made restartable after protection violations on the (B-2/B-3) and
@ -46,5 +48,3 @@ The above three problems are being corrected in all future steppings of the 8028
register will hold 0ffffH when the CPU detects a violation due to the conditions above. Note that even when REP MOVS and
REP INS restartability is corrected, CX will correctly continue to be 0000H when these instructions complete without generating
a protection violation.
[This information is from an Intel document titled "80286 REP MOVS and REP INS Restartability, 15 October 1984"]

View file

@ -4,26 +4,27 @@ title: Intel 80386 CPU Information
permalink: /pubs/pc/reference/intel/80386/
---
Intel 80386 CPU Information
---
* [A0 Stepping](#a0-stepping)
* [A1 Stepping](#a1-stepping)
* [A2 Stepping](#a2-stepping)
* [B0 Stepping](#b0-stepping)
* [B1 Stepping](#b1-stepping)
* [C0 Stepping](#c0-stepping)
* [D0 Stepping](#d0-stepping)
* [D1 Stepping](#d1-stepping)
* [D2 Stepping](#d2-stepping)
80386 Instructions
80386 Errata
---
* [IBTS and XBTS](ibts_xbts/)
* [A0 Stepping](#a0-stepping)
* [A1 Stepping](#a1-stepping)
* [A2 Stepping](#a2-stepping)
* [B0 Stepping](#b0-stepping)
* [B1 Stepping](#b1-stepping)
* [C0 Stepping](#c0-stepping)
* [D0 Stepping](#d0-stepping)
* [D1 Stepping](#d1-stepping)
* [D2 Stepping](#d2-stepping)
80386 Undocumented Instructions
---
* [LOADALL](loadall/)
* [IBTS and XBTS](ibts_xbts/)
80386 Steppings
---

View file

@ -0,0 +1,277 @@
---
layout: page
title: "Intel 80386 LOADALL Instruction"
permalink: /pubs/pc/reference/intel/80386/loadall/
---
Intel 80386 LOADALL Instruction
---
[The following information is from an undated 6-page Intel "Internal Correspondence" document]
### LOADALL (0F07H)
### 1. 386 LOADALL Instruction
The 80386 implements a LOADALL instruction capable of loading all visible machine state from an area in memory.
The function of this instruction is the same as the [80286 LOADALL](/pubs/pc/reference/intel/80286/loadall/)
instruction [RASH]. Due to the architecture, and implementation of the 386, the format of the memory area
used by the two processors is different. Conversion between these formats is possible, requiring an algorithm
to 'map' between the two formats, and compensate for the architectural differences.
The attached tables illustrate the memory area used by LOADALL. The location of this area in memory is dynamic,
not fixed at 800H as in the 80286. The LOADALL instruction uses ES:EDI for the base of the LOADALL memory image.
Note that the normal address computation scheme applies to LOADALL; if protection is enabled (CR0.PE), and possibly
paging is enabled (CR0.PG), the physical address of the memory area will be computed by both segment, and page
relocation. A typical sequence to perform the LOADALL would be:
MOV AX,Dump_Area_Selector
MOV ES,AX
MOV EDI,offset Dump_Area
LOADALL
### 1.1 Background
As part of marketing the 286, most of the details of one of the 286 test instructions were published to selected
customers and ISVs in a note entitled "[[Undocumented] iAPX 286 LOADALL Instruction](/pubs/pc/reference/intel/80286/loadall/)",
by Bill Rash. The test instruction was named LOADALL for public consumption. The instruction was published to provide
two functions: to allow REAL mode code to access memory above 1 Meg, and to allow a protected mode OS to simulate 8086
semantics for segment register loads as a key part of implementing a "virtual 8086 mode" on the 286. When the details
of the "LOADALL" instruction were released, we made it quite clear that the instruction would not be supported on the
386. However, regardless of our warnings, by publishing the details of the 286 LOADALL we effectively made it a part
of the 286 architecture. People will use the instruction anyway, and we must support the 286 LOADALL instruction on
the 386.
The 386 has similar test instructions, which are "culturally compatible" with the 286 test instructions. The
instructions on both machines reload internal machine state from a block of memory. However, the format of the memory
area is quite different, and the 386 LOADALL loads more state. Things that were 16-bits on the 286 were stretched
to 32 bits on the 386.
### 1.2 Emulating 286 LOADALL with 386 LOADALL
As noted above, the 386 LOADALL function is pretty close the the 286 LOADALL instruction, and it is possible to
reformat the 286 image to a 386 image and use the 386 instruction to provide emulation capability. This requires the
following:
+ The 386 must trap the 286 LOADALL opcode.
+ Need code to translate 286 LOADALL format to 386 format.
The 386 traps the 286 LOADALL opcode, since the 286 LOADALL opcode (0F05) an illegal opcode on the 386. The 386
LOADALL has a different opcode (0F07). A 386 OS that wishes to emulate the 286 LOADALL can include an interrupt 6
(invalid opcode fault) handler to emulate the 286 LOADALL. The invalid opcode fault handler will be invoked whenever
the 286 LOADALL instruction occurs in the instruction stream. The fault handler can decode the invalid instruction,
and if it is the 286 LOADALL, can reformat the memory image in a new area, and execute a 386 LOADALL. Because the
386 LOADALL can load from an arbitrary address, the block at 800H can be left undisturbed, with the reformatting done
in a part of the address space not accessible on the 286 (above 16 Meg. for example).
The information loaded by the 286 LOADALL falls into 3 categories: programmer visible registers, 286 specific temp
registers, and "invisible" descriptor cache registers. There is no problem with emulating the loading of the programmer
visible registers, as these are compatibly implemented on the 386 (and the 486, 586, ...). The 286 also loads a number
of temp registers, but the values in these registers are "dead" when the next instruction (other than STOREALL)
begins execution. Consequently, the values loaded into the temp registers can have no effecton 286 program execution,
so these can be ignored. As long as the 386 temps are also "dead" when the next instruction begins (except for STOREALL),
there will be no problems with 386-specific temp registers.
In order to ease the burden of supporting LOADALL on future processors, the temp register dump/load area is at the
top of the LOADALL block, so it can grow and shrink as required for future implementations. Several difficulties occur
when attempting to emulate the loading of the invisible descriptor cache registers. Unfortunately, this is the main
reason why the 286 loadall is an interesting instruction....
To verify that the 386 LOADALL can emulate the 286 LOADALL, we need to verify that all of the invisible 286 descriptor
cache entries modified by the 286 LOADALL are also modified by the 386 LOADALL. Any extra state can just be loaded with
the 286 compatible values. Some "don't care" values on the 286 are now recognized by the 386, and so these may need to
be parsed and reformatted.
### 1.2.1 Translating 286 LOADALL format to 386 LOADALL
This section sketches the algorithm for translating the 286 LOADALL format to the format
required for 386 LOADALL. Special considerations for each field in the 286 descriptor cache are given below:
**MSW**
> Only the lower 4 bits are defined on the 286. Bit 0 is sticky on the 286 LOADALL, so software must OR in the
current setting of bit 0 of CR0 with the 286 LOADALL image to get the CR0 image for the 386 LOADALL. The ET bit is
new on the 386, as is the PG bit. Both of these bits should remain unchanged, and so should be copied from the
current CR0 value. This can be done with the following code sequence:
MOV EAX,CR0
AND EAX,80000011H
MOV CX,LD286MSW ; Load 286 MSW image AND ECX. OFh ; mask low bits
OR EAX,ECX
MOV LD386,CR0 ; value for 386 LOADALL
**TR, LDT, DS, SS, CS, ES Selectors**
> These can be copied directly.
**FLAGS**
> The 386 defines two new flags. The 286 flags can be copied directly to the low order 16 bits of the 386 flags.
The RF bit can be set to 0, and the VM bit set if the emulated program is in Virtual 8086 mode (if the LOADALL was
trapped in a Virtual 8086 program), and cleared otherwise. The VM bit can be copied from the EFLAGS image pushed
when the invalid opcode trap is taken.
**IP**
> The 286 IP register is copied to the low order 16 bits of the 386 EIP image, and the upper 16 bits of the EIP
image cleared.
**AX...SI**
> The 286 register images are copied to the lower 16 bits of the 386 32-bit registers. The upper 16 bits can be
random trash, or could be set to 0 for tidyness. Only EIP needs to have its upper bits cleared.
**ES...DS Descriptor Cache Entries**
> These can be reformatted to 386 descriptor cache entries. The base and limit values translate directly to the 386,
with extra high-order zeros. However, the AR values may not translate.
> The 286 uses only bit 1, bit 2, bit 3, and bit 7 of the AR byte to do protection checks. The 386 uses these bits,
plus the "G" and "B" bits to perform checks. If the "G" and "B" bits are 0, the checks are 100% compatible. The 286
and 386 may behave differently if the DPL field of the SS and CS descriptor entries are not equal, or if the RPL
fields of the SS and CS selectors do not match the DPL field of the SS and CS descriptors. The 286 action in this
case is described as "undefined" in the LOADALL description. CPL is loaded from the AR byte for the SS register
(SS, not CS). If LOADALL is executed in protected mode, errors may occur in subsequent instructions if the RPL field
of SS or CS selectors, or the DPL of the CS descriptor, do not match the DPL field of the SS descriptor.
**GDTR, IDTR Registers**
> These translate directly. The low 5 bytes of the 286 LOADALL image are moved to the 3S6 image, and the upper byte
is set to 0.
**LDT Descriptor Cache**
> Similar to other descriptor cache entries, except that only a subset of the AR bits are on the 286 and 386.
Only the P bit is supported on both machines, so this descriptor entry should translate directly.
**TSS Descriptor Cache**
> Similar to other descriptor cache entries, except for the treatment of AR bits again. The 286 recognizes none
of the AR bits. The 386 recognizes bit 3 to distinguish 286 TSS types from 386 TSS types. The 286 descriptor can
be copied to the 386 LOADALL image directly, with bit 3 of the AR field set to 0.
To summarize, the 386 LOADALL instruction can be used to emulate the 286 LOADALL except for some questionable areas
involving the Access Rights bvtes in the "hidden" descriptor entries. The 286 LOADALL can be used to load these AR
fields with inconsistent values, in which case we don't know what the 286 will do, let alone whether the 386 matches
the 286 semantics. The 286 LOADALL can be emulated by the 386 LOADALL provided that the DPL and RPL values of CS and
SS are all equal. Other cases are undefined by the 286 (and cannot be induced by executing normal instructions).
### 1.3 LOADALL used to switch Modes
Any of the 80386 operating modes may be selected with LOADALL. The PE (Protection Enabled) bit is not 'Sticky' as in
the 286. By setting the appropriate bits in the memory area, the processor will resume execution in the selected mode
after LOADALL. For example, a LOADALL could be performed to a page protected VM86 task by setting the following
conditions in the memory image:
* PE and PG bits set to 1
* VM bit set in the extended flags register
* 8086 style segment register values into the memory image
* Descriptor bases to (Segment Reg SHL 4)
* Access rights (Present, ByteGranular, etc.)
* LIMIT set to 0000FFFFH.
As another example, RESET can be emulated with LOADALL, by loading the processor's initial values into the memory
image as follows:
* All registers=0
* GS,FS,DS,ES,SS=0
* CS=F000, EIP=FFF0, CS-Base-FFFF0000
* GS,FS,DS,ES,SS-Base=0
* GS,FS,DS,ES,SS-AR=Present
* GS,FS,DS,ES,SS-Limit=FFFF
* CR0=0
* All other control registers=0
* Debug Registers=0
* EFLAGS=0
After the LOADALL instruction, the processor state will be identical to reset, could be set to run at a CPL <> 0
while in real mode. Combinations such as these are possible, but may have unexpected results. Placing the processor
into other than a 'Natural' state should be avoided.
### 1.4 Caveats
Note that LOADALL provides no error checking. It is important that the descriptor entries match the selector entries
(Unless the intent is that they do not; for example to give a REAL mode program access to the extended address space).
After LOADALL. the CPL of the processor will be set to the DPL of the SS Descriptor entry. This is necessary to
accomodate conforming segments. The IOPL will be set from the IOPL value in the EFLAGS image.
LOADALL is not restartable. If a page or segment fault occurs during execution of LOADALL, the processor will be left
in an undefined state. LOADALL is a privileged instruction, so it can be executed only at level 0. It is assumed that
the OS kernel will restrict use of this instruction, and will check for addressability and for consistent semantics
before executing the LOADALL.
### 1.5 386 LOADALL Memory Format
The following tables define the LOADALL memory format. The LOADALL instruction uses a 512-byte block of memory,
where the lowest addressed byte is given in ES:[(E)DI]. The area above offset CC hex is used for processor dependent
registers (temporaries, invisible registers). These are loaded into the processor, but will not affect normal program
execution. All values in the memory area are read from a four byte field, to keep the memory format DWORD aligned,
but it is possible to locate memory area at a non-aligned address. In this case, the execution time of LOADALL will
DOUBLE. For this reason, the memory dump area should always be DWORD aligned.
Each descriptor entry consists of 3 pieces:
* AR
* Base
* Limit
The AR part has the same format as the second dword of a segment descriptor except that only the AR byte (bits 8-15)
and the G and B/D bits (bits 23 and 22) are used. All other bits in the AR field are ignored. The Base and Limit parts
contain full 32-bit values, fully expanded and unscrambled from the 386 descriptor. In particular, the Limit field
loaded for a page granular segment gives a byte granular limit, so should contain the page limit*4096 plus 4095.
**80386 LOADALL Memory Format**
Offset | Register
:--------: | :--------:
000 | CR0
004 | EFLAGS
008 | EIP
00C | EDI
010 | ESI
014 | EBP
018 | ESP
01C | EBX
020 | EDX
024 | ECX
028 | EAX
02C | DR6
030 | DR7
034 | TSSR(TSSSelector-Word)
038 | LDTR(LDTSelector-Word)
03C | GS
040 | FS
044 | DS
048 | SS
04C | CS
050 | ES
054 | TSS(AR)
058 | TSS(BASE)
05C | TSS(LIMIT)
060 | IDT(AR)
064 | IDT(BASE)
068 | IDT(LIMIT)
06C | GDT(AR)
070 | GDT(BASE)
074 | GDT(LIMIT)
078 | LDT(AR)
07C | LDT(BASE)
080 | LDT(LIMIT)
084 | GS(AR)
088 | GS(BASE)
08C | GS(LIMIT)
090 | FS(AR)
094 | FS(BASE)
098 | FS(LIMIT)
09C | DS(AR)
0A0 | DS(BASE)
0A4 | DS(LIMIT)
0A8 | SS(AR)
0AC | SS(BASE)
0B0 | SS(LIMIT)
0B4 | CS(AR)
0B8 | CS(BASE)
0BC | CS(LIMIT)
0C0 | ES(AR)
0C4 | ES(BASE)
0C8 | ES(LIMIT)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff