Some improvements for FOOTBALL, including support for the 386 LOADALL instruction
This commit is contained in:
parent
763b02794b
commit
a5b9e0cae7
27 changed files with 4431 additions and 3981 deletions
39
_posts/2016-01-23-early-os2-artifacts.md
Normal file
39
_posts/2016-01-23-early-os2-artifacts.md
Normal 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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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}]'/>
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
277
pubs/pc/reference/intel/80386/loadall/README.md
Normal file
277
pubs/pc/reference/intel/80386/loadall/README.md
Normal 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
Loading…
Reference in a new issue