Fixed LGDT and LIDT, fixed read-only memory, and fixed mode transition tests
This commit is contained in:
parent
9ab1f92adc
commit
cfa4e0207f
5 changed files with 158 additions and 104 deletions
|
|
@ -593,7 +593,7 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readShortDefault: function readShortDefault(off, addr) {
|
readShortDefault: function readShortDefault(off, addr) {
|
||||||
return this.readByteDirect(off, addr) | (this.readByteDirect(off + 1, addr) << 8);
|
return this.readByte(off, addr) | (this.readByte(off + 1, addr) << 8);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readLongDefault(off, addr)
|
* readLongDefault(off, addr)
|
||||||
|
|
@ -604,7 +604,7 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readLongDefault: function readLongDefault(off, addr) {
|
readLongDefault: function readLongDefault(off, addr) {
|
||||||
return this.readByteDirect(off, addr) | (this.readByteDirect(off + 1, addr) << 8) | (this.readByteDirect(off + 2, addr) << 16) | (this.readByteDirect(off + 3, addr) << 24);
|
return this.readByte(off, addr) | (this.readByte(off + 1, addr) << 8) | (this.readByte(off + 2, addr) << 16) | (this.readByte(off + 3, addr) << 24);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeShortDefault(off, w, addr)
|
* writeShortDefault(off, w, addr)
|
||||||
|
|
@ -616,8 +616,8 @@ Memory.prototype = {
|
||||||
*/
|
*/
|
||||||
writeShortDefault: function writeShortDefault(off, w, addr) {
|
writeShortDefault: function writeShortDefault(off, w, addr) {
|
||||||
Component.assert(!(w & ~0xffff));
|
Component.assert(!(w & ~0xffff));
|
||||||
this.writeByteDirect(off, w & 0xff);
|
this.writeByte(off, w & 0xff);
|
||||||
this.writeByteDirect(off + 1, w >> 8);
|
this.writeByte(off + 1, w >> 8);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeLongDefault(off, w, addr)
|
* writeLongDefault(off, w, addr)
|
||||||
|
|
@ -628,10 +628,10 @@ Memory.prototype = {
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeLongDefault: function writeLongDefault(off, w, addr) {
|
writeLongDefault: function writeLongDefault(off, w, addr) {
|
||||||
this.writeByteDirect(off, w & 0xff);
|
this.writeByte(off, w & 0xff);
|
||||||
this.writeByteDirect(off + 1, (w >> 8) & 0xff);
|
this.writeByte(off + 1, (w >> 8) & 0xff);
|
||||||
this.writeByteDirect(off + 2, (w >> 16) & 0xff);
|
this.writeByte(off + 2, (w >> 16) & 0xff);
|
||||||
this.writeByteDirect(off + 3, (w >>> 24));
|
this.writeByte(off + 3, (w >>> 24));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readByteMemory(off, addr)
|
* readByteMemory(off, addr)
|
||||||
|
|
|
||||||
|
|
@ -1445,9 +1445,13 @@ X86.fnLFS = function LFS(dst, src)
|
||||||
*
|
*
|
||||||
* op=0x0F,0x01,reg=0x2 (GRP7:LGDT)
|
* op=0x0F,0x01,reg=0x2 (GRP7:LGDT)
|
||||||
*
|
*
|
||||||
* The 80286 LGDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address
|
* The 80286 LGDT instruction assumes a 40-bit operand: a 16-bit limit followed by a 24-bit base address;
|
||||||
* (or a 32-bit address in 32-bit mode); the ModRM decoder has already supplied the first word of the
|
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to
|
||||||
* operand (in dst), which corresponds to the limit, so we must fetch the remaining bits ourselves.
|
* the limit, so we must fetch the remaining bits ourselves.
|
||||||
|
*
|
||||||
|
* The 80386 LGDT instruction assumes a 48-bit operand: a 16-bit limit followed by a 32-bit base address,
|
||||||
|
* but it ignores the last 8 bits of the base address if the OPERAND size is 16 bits; we interpret that to
|
||||||
|
* mean that the 24-bit base address should be zero-extended to 32 bits.
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} dst
|
* @param {number} dst
|
||||||
|
|
@ -1460,10 +1464,15 @@ X86.fnLGDT = function LGDT(dst, src)
|
||||||
X86.opInvalid.call(this);
|
X86.opInvalid.call(this);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* It shouldn't hurt to always fetch 32 bits of physical memory, which we'll then
|
* Hopefully it won't hurt to always fetch a 32-bit base address (even on an 80286), which we then
|
||||||
* mask with either a 24-bit or a 32-bit mask.
|
* mask apppropriately.
|
||||||
*/
|
*/
|
||||||
this.addrGDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
|
this.addrGDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
|
||||||
|
/*
|
||||||
|
* An idiosyncrasy of our ModRM decoders is that, if the OPERAND size is 32 bits, then it will have
|
||||||
|
* fetched a 32-bit dst operand; we mask off those extra bits now.
|
||||||
|
*/
|
||||||
|
dst &= 0xffff;
|
||||||
this.addrGDTLimit = this.addrGDT + dst;
|
this.addrGDTLimit = this.addrGDT + dst;
|
||||||
this.opFlags |= X86.OPFLAG.NOWRITE;
|
this.opFlags |= X86.OPFLAG.NOWRITE;
|
||||||
this.nStepCycles -= 11;
|
this.nStepCycles -= 11;
|
||||||
|
|
@ -1495,9 +1504,13 @@ X86.fnLGS = function LGS(dst, src)
|
||||||
*
|
*
|
||||||
* op=0x0F,0x01,reg=0x3 (GRP7:LIDT)
|
* op=0x0F,0x01,reg=0x3 (GRP7:LIDT)
|
||||||
*
|
*
|
||||||
* The 80286 LIDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address
|
* The 80286 LIDT instruction assumes a 40-bit operand: a 16-bit limit followed by a 24-bit base address;
|
||||||
* (or a 32-bit address in 32-bit mode); the ModRM decoder has already supplied the first word of the
|
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to
|
||||||
* operand (in dst), which corresponds to the limit, so we must fetch the remaining bits ourselves.
|
* the limit, so we must fetch the remaining bits ourselves.
|
||||||
|
*
|
||||||
|
* The 80386 LIDT instruction assumes a 48-bit operand: a 16-bit limit followed by a 32-bit base address,
|
||||||
|
* but it ignores the last 8 bits of the base address if the OPERAND size is 16 bits; we interpret that to
|
||||||
|
* mean that the 24-bit base address should be zero-extended to 32 bits.
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} dst
|
* @param {number} dst
|
||||||
|
|
@ -1510,10 +1523,15 @@ X86.fnLIDT = function LIDT(dst, src)
|
||||||
X86.opInvalid.call(this);
|
X86.opInvalid.call(this);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* It shouldn't hurt to always fetch 32 bits of physical memory, which we'll then
|
* Hopefully it won't hurt to always fetch a 32-bit base address (even on an 80286), which we then
|
||||||
* mask with either a 24-bit or a 32-bit mask.
|
* mask apppropriately.
|
||||||
*/
|
*/
|
||||||
this.addrIDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
|
this.addrIDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
|
||||||
|
/*
|
||||||
|
* An idiosyncrasy of our ModRM decoders is that, if the OPERAND size is 32 bits, then it will have
|
||||||
|
* fetched a 32-bit dst operand; we mask off those extra bits now.
|
||||||
|
*/
|
||||||
|
dst &= 0xffff;
|
||||||
this.addrIDTLimit = this.addrIDT + dst;
|
this.addrIDTLimit = this.addrIDT + dst;
|
||||||
this.opFlags |= X86.OPFLAG.NOWRITE;
|
this.opFlags |= X86.OPFLAG.NOWRITE;
|
||||||
this.nStepCycles -= 12;
|
this.nStepCycles -= 12;
|
||||||
|
|
|
||||||
|
|
@ -2098,6 +2098,10 @@ X86.opMOVwsr = function MOVwsr()
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Like other MOV operations, the destination does not need to be read, just written.
|
* Like other MOV operations, the destination does not need to be read, just written.
|
||||||
|
*
|
||||||
|
* TODO: Confirm this instruction's behavior on the 80386; ie, if a 32-bit OPERAND size is
|
||||||
|
* in effect, does it still write only 16 bits? If so, we must add a setDataSize(2) override.
|
||||||
|
* Confirm for both register and memory destinations.
|
||||||
*/
|
*/
|
||||||
this.opFlags |= X86.OPFLAG.NOREAD;
|
this.opFlags |= X86.OPFLAG.NOREAD;
|
||||||
this.aOpModMemWord[bModRM].call(this, X86.fnMOVxx);
|
this.aOpModMemWord[bModRM].call(this, X86.fnMOVxx);
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,18 @@ X86Seg.prototype.loadProt = function loadProt(sel, fSuppress)
|
||||||
*/
|
*/
|
||||||
sel &= 0xffff;
|
sel &= 0xffff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When comparing descriptor addresses, we must be mindful that any addresses above 2Gb will be negative;
|
||||||
|
* that in itself is not a problem UNLESS the descriptor table straddles the 2Gb boundary, meaning the
|
||||||
|
* starting address is positive but the ending (limit) address is negative. Although that situation is
|
||||||
|
* highly unlikely, the safest thing to do is coerce the bounding addresses to unsigned values, using ">>> 0."
|
||||||
|
*/
|
||||||
if (!(sel & X86.SEL.LDT)) {
|
if (!(sel & X86.SEL.LDT)) {
|
||||||
addrDT = cpu.addrGDT;
|
addrDT = cpu.addrGDT >>> 0;
|
||||||
addrDTLimit = cpu.addrGDTLimit;
|
addrDTLimit = cpu.addrGDTLimit >>> 0;
|
||||||
} else {
|
} else {
|
||||||
addrDT = cpu.segLDT.base;
|
addrDT = cpu.segLDT.base >>> 0;
|
||||||
addrDTLimit = addrDT + cpu.segLDT.limit;
|
addrDTLimit = addrDT + cpu.segLDT.limit; // segment limit properties are already coerced unsigned
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT,
|
* The ROM BIOS POST executes some test code in protected-mode without properly initializing the LDT,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
;
|
;
|
||||||
; This file is designed to run as a ROM replacement, but it has a .COM extension because it's
|
; This file is designed to run both as a test ROM and as a DOS COM file (hence the "org 0x100"),
|
||||||
; also designed to run as a COM file under DOS (hence the "org 0x100").
|
; which is why it has a ".com" extension instead of the more typical ".rom" extension.
|
||||||
|
;
|
||||||
|
; When used as a ROM, it should be installed at physical address 983296 (0xf0100) and aliased at
|
||||||
|
; physical address 4294902016 (0xffff0100). The jump at jmpStart should align with the CPU reset
|
||||||
|
; address (%0xfffffff0), which will transfer control to 0xf000:0x0100.
|
||||||
|
;
|
||||||
|
; The code which attempts to update myGDT and addrGDT will have no effect when installed as a ROM,
|
||||||
|
; which is fine, because those data structures are predefined with appropriate ROM-based addresses.
|
||||||
|
;
|
||||||
|
; See the machine definition file in /modules/pcjs/bin/romtests.json for a sample ROM configuration.
|
||||||
;
|
;
|
||||||
cpu 386
|
cpu 386
|
||||||
org 0x100
|
org 0x100
|
||||||
|
|
@ -8,38 +17,35 @@
|
||||||
|
|
||||||
bits 16
|
bits 16
|
||||||
|
|
||||||
DPL0 equ 0
|
ACC_TYPE_SEG equ 0x1000
|
||||||
DPL1 equ 1
|
ACC_PRESENT equ 0x8000
|
||||||
DPL2 equ 2
|
ACC_TYPE_CODE equ 0x0800
|
||||||
DPL3 equ 3
|
ACC_TYPE_READABLE equ 0x0200
|
||||||
|
ACC_TYPE_WRITABLE equ 0x0200
|
||||||
|
ACC_TYPE_CODE_READABLE equ 0x1a00
|
||||||
|
ACC_TYPE_DATA_WRITABLE equ 0x1200
|
||||||
|
|
||||||
ACC_TYPE_SEG equ 0x1000
|
EXT_NONE equ 0x0000
|
||||||
ACC_PRESENT equ 0x8000
|
EXT_BIG equ 0x0040
|
||||||
ACC_TYPE_CODE equ 0x0800
|
|
||||||
ACC_TYPE_READABLE equ 0x0200
|
|
||||||
ACC_TYPE_WRITABLE equ 0x0200
|
|
||||||
ACC_TYPE_CODE_READABLE equ (0x1a00 | ACC_PRESENT)
|
|
||||||
ACC_TYPE_DATA_WRITABLE equ (0x1200 | ACC_PRESENT)
|
|
||||||
|
|
||||||
EXT_NONE equ 0x0000
|
|
||||||
EXT_BIG equ 0x0040
|
|
||||||
|
|
||||||
;
|
;
|
||||||
; We build some data structures in the first page (0x0000-0x0fff) of RAM:
|
; If we built our data structures in RAM, we might use the first page of RAM (0x0000-0x0fff) like so:
|
||||||
;
|
;
|
||||||
; 0x0000-0x03ff Real-mode IDT (256*4)
|
; 0x0000-0x03ff Real-mode IDT (256*4)
|
||||||
; 0x0400-0x0bff Prot-mode IDT (256*8)
|
; 0x0400-0x0bff Prot-mode IDT (256*8)
|
||||||
; 0x0c00-0x0cff GDT (enough room for 32 selectors)
|
; 0x0c00-0x0cff RAM_GDT (for 32 GDT selectors)
|
||||||
; 0x0d00-0x0d07 IDTR
|
; 0x0d00-0x0d07 RAM_IDTR
|
||||||
; 0x0d08-0x0d0f GDTR
|
; 0x0d08-0x0d0f RAM_GDTR
|
||||||
; 0x0d10-0x0fff reserved
|
; 0x0d10-0x0d13 RAM_RETF (Real-mode return address)
|
||||||
|
; 0x0d14-0x0fff reserved
|
||||||
;
|
;
|
||||||
; And in the second page (0x1000-0x1fff), we build a page directory, followed by a single page table that
|
; And in the second page (0x1000-0x1fff), we might build a page directory, followed by a single page table that
|
||||||
; will allow us to map up to 4Mb (although we'll only create entries for the first 1Mb).
|
; allows us to map up to 4Mb (although we'd likely only create PTEs for the first 1Mb).
|
||||||
;
|
;
|
||||||
RAM_GDT equ 0x0c00
|
;RAM_GDT equ 0x0c00
|
||||||
RAM_IDTR equ 0x0d00
|
;RAM_IDTR equ 0x0d00
|
||||||
RAM_GDTR equ 0x0d08
|
;RAM_GDTR equ 0x0d08
|
||||||
|
;RAM_RETF equ 0x0d10
|
||||||
|
|
||||||
CSEG_REAL equ 0xf000
|
CSEG_REAL equ 0xf000
|
||||||
CSEG_PROT equ 0x0008
|
CSEG_PROT equ 0x0008
|
||||||
|
|
@ -48,7 +54,7 @@ DSEG_PROT equ 0x0010
|
||||||
CR0_MSW_PE equ 0x0001
|
CR0_MSW_PE equ 0x0001
|
||||||
|
|
||||||
;
|
;
|
||||||
; set initializes a register to the specified value (eg, "set eax,0")
|
; The "set" macro initializes a register to the specified value (eg, "set eax,0")
|
||||||
;
|
;
|
||||||
%macro set 2
|
%macro set 2
|
||||||
%ifnum %2
|
%ifnum %2
|
||||||
|
|
@ -63,49 +69,45 @@ CR0_MSW_PE equ 0x0001
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
;
|
;
|
||||||
; defDesc defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and ext (%5)
|
; The "defDesc" macro defines a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5)
|
||||||
;
|
|
||||||
%macro defDesc 1-5 0,0,0,0,0
|
|
||||||
dw (%2 & 0x0000ffff)
|
|
||||||
dw (%1 & 0x0000ffff)
|
|
||||||
dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13)
|
|
||||||
dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16)
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
;
|
|
||||||
; setDesc creates a descriptor, given a base (%1), limit (%2), type (%3), ext (%4), and selector (%5)
|
|
||||||
;
|
;
|
||||||
%assign selDesc 0
|
%assign selDesc 0
|
||||||
%macro setDesc 1-5 0,0,0,0,none
|
|
||||||
set ebx,%1
|
%macro defDesc 1-5 none,0,0,0,0
|
||||||
set ecx,%2
|
%assign %1 selDesc
|
||||||
set dx,%3
|
dw (%3 & 0x0000ffff)
|
||||||
set ax,%4
|
dw (%2 & 0x0000ffff)
|
||||||
call storeDesc
|
%if selDesc = 0
|
||||||
%assign %5 selDesc
|
dw ((%2 & 0x00ff0000) >> 16) | %4 | (0 << 13)
|
||||||
|
%else
|
||||||
|
dw ((%2 & 0x00ff0000) >> 16) | %4 | (0 << 13) | ACC_PRESENT
|
||||||
|
%endif
|
||||||
|
dw ((%3 & 0x000f0000) >> 16) | %5 | ((%2 & 0xff000000) >> 16)
|
||||||
%assign selDesc selDesc+8
|
%assign selDesc selDesc+8
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
start: cli ; disable all interrupts
|
;
|
||||||
mov al,0xff ; and ensure that no hardware interrupts can sneak in
|
; The "setDesc" macro creates a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5)
|
||||||
out 0x21,al ; if interrupts become enabled later
|
;
|
||||||
out 0xa1,al
|
%macro setDesc 1-5 none,0,0,0,0
|
||||||
|
%assign %1 selDesc
|
||||||
sub ax,ax
|
set ebx,%2
|
||||||
mov ds,ax
|
set ecx,%3
|
||||||
mov es,ax
|
set dx,%4
|
||||||
mov ss,ax
|
set ax,%5
|
||||||
mov sp,0x1000
|
call storeDesc
|
||||||
|
%assign selDesc selDesc+8
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
start: nop
|
||||||
mov eax,0x44332211
|
mov eax,0x44332211
|
||||||
mov ebx,eax
|
mov ebx,eax
|
||||||
mov ecx,0x88776655
|
mov ecx,0x88776655
|
||||||
mul ecx
|
mul ecx
|
||||||
div ecx
|
div ecx
|
||||||
cmp eax,ebx
|
cmp eax,ebx
|
||||||
je near initRAM ; apparently we have to tell NASM "near" because this is a forward reference
|
je near initGDT ; apparently we have to tell NASM "near" because this is a forward reference
|
||||||
times 32768 nop ; lots of NOPs to force a 16-bit conditional jump
|
times 32768 nop ; lots of NOPs to force a 16-bit conditional jump
|
||||||
|
|
||||||
;
|
;
|
||||||
; storeDesc(EBX=base, ECX=limit, DX=type, AX=ext, DI=address of descriptor)
|
; storeDesc(EBX=base, ECX=limit, DX=type, AX=ext, DI=address of descriptor)
|
||||||
;
|
;
|
||||||
|
|
@ -119,6 +121,7 @@ storeDesc:
|
||||||
mov ax,dx
|
mov ax,dx
|
||||||
shr ebx,16
|
shr ebx,16
|
||||||
mov al,bl
|
mov al,bl
|
||||||
|
or ax,ACC_PRESENT
|
||||||
stosw
|
stosw
|
||||||
pop ax
|
pop ax
|
||||||
shr ecx,16
|
shr ecx,16
|
||||||
|
|
@ -128,56 +131,78 @@ storeDesc:
|
||||||
stosw
|
stosw
|
||||||
ret
|
ret
|
||||||
|
|
||||||
;
|
addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT
|
||||||
; The following ROM-based data structures are obsolete, because we build these data structures in RAM now.
|
dw myGDT, 0xffff ; 32-bit base address of myGDT (works as long as we're aliased at 0xffff0000)
|
||||||
;
|
|
||||||
addrGDT:dw romGDTEnd - romGDT - 1 ; 16-bit limit of romGDT
|
|
||||||
dw romGDT, 0xffff ; 32-bit base address of romGDT (works as long as we're aliased at 0xffff0000)
|
|
||||||
|
|
||||||
romGDT: defDesc 0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null descriptor)
|
|
||||||
defDesc 0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE
|
|
||||||
defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE
|
|
||||||
romGDTEnd:
|
|
||||||
;
|
;
|
||||||
; End of ROM-based data structures
|
; TODO: Why do I need to provide a 2nd parameter for "defDesc NULL"? Is this a NASM 0.98.x bug?
|
||||||
;
|
;
|
||||||
|
myGDT: defDesc NULL,0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null selector)
|
||||||
|
defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
|
||||||
|
defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
|
||||||
|
myGDTEnd:
|
||||||
|
|
||||||
initRAM:
|
initGDT:
|
||||||
|
%ifdef RAM_GDT
|
||||||
set edi,RAM_GDT
|
set edi,RAM_GDT
|
||||||
mov [RAM_GDTR+2],edi
|
mov [RAM_GDTR+2],edi
|
||||||
setDesc 0,0,0,0,NULL
|
setDesc NULL
|
||||||
mov eax,cs
|
xor eax,eax
|
||||||
|
mov ax,cs
|
||||||
shl eax,4
|
shl eax,4
|
||||||
setDesc eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG,CSEG_PROT
|
setDesc CSEG_PROT,eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
|
||||||
setDesc 0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_BIG,DSEG_PROT
|
setDesc DSEG_PROT,0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
|
||||||
sub edi,RAM_GDT
|
sub edi,RAM_GDT
|
||||||
dec edi
|
dec edi
|
||||||
mov [RAM_GDTR],di
|
mov [RAM_GDTR],di
|
||||||
|
mov word [RAM_RETF],toReal
|
||||||
|
mov word [RAM_RETF+2],cs
|
||||||
|
%else
|
||||||
|
;
|
||||||
|
; This code will have no effect if we're in ROM (in that case, myGDT et al should already be set correctly)
|
||||||
|
;
|
||||||
|
xor eax,eax
|
||||||
|
mov ax,cs
|
||||||
|
shl eax,4 ; EAX = base address of the current CS
|
||||||
|
mov edx,eax ; save it in EDX
|
||||||
|
mov [cs:myGDT+CSEG_PROT+2],ax ; update the base portions of the descriptor for CSEG_PROT
|
||||||
|
shr eax,16
|
||||||
|
mov [cs:myGDT+CSEG_PROT+4],al
|
||||||
|
mov [cs:myGDT+CSEG_PROT+7],ah
|
||||||
|
mov eax,edx ; recover the base address of the current CS
|
||||||
|
add eax,myGDT ; EAX = physical address of myGDT
|
||||||
|
mov [cs:addrGDT+2],eax ; update the 32-bit base address of myGDT in addrGDT
|
||||||
|
mov [cs:jmpReal+3],cs ; update the segment of the far jmp that returns us to real-mode
|
||||||
|
mov [cs:jmpStart+3],cs ; ditto for the far jmp that returns us to the start of the image
|
||||||
|
%endif
|
||||||
|
|
||||||
goProt: o32 lgdt [RAM_GDTR]
|
goProt: o32 lgdt [cs:addrGDT]
|
||||||
mov eax,cr0
|
mov eax,cr0
|
||||||
or eax,CR0_MSW_PE
|
or eax,CR0_MSW_PE
|
||||||
mov cr0,eax
|
mov cr0,eax
|
||||||
jmp CSEG_PROT:inProt
|
nop
|
||||||
inProt:
|
jmpProt:
|
||||||
bits 32
|
jmp CSEG_PROT:toProt
|
||||||
|
|
||||||
|
toProt: ; bits 32 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
||||||
mov ax,DSEG_PROT
|
mov ax,DSEG_PROT
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov es,ax
|
mov es,ax
|
||||||
mov ss,ax
|
|
||||||
|
|
||||||
;
|
;
|
||||||
; Do some protected-mode tests now...
|
; Do some protected-mode tests now...
|
||||||
;
|
;
|
||||||
|
|
||||||
goReal: mov eax,cr0
|
goReal: mov eax,cr0
|
||||||
and eax,~CR0_MSW_PE
|
and eax,~CR0_MSW_PE
|
||||||
mov cr0,eax
|
mov cr0,eax
|
||||||
bits 16
|
nop
|
||||||
jmp CSEG_REAL:inReal
|
jmpReal:
|
||||||
inReal:
|
jmp CSEG_REAL:toReal
|
||||||
or ax,1
|
|
||||||
jnz start ; apparently we do NOT have to say "near" here since this is a backward reference
|
toReal: ; bits 16 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
||||||
|
mov ax,cs
|
||||||
|
cmp ax,CSEG_REAL ; is CS equal to 0xf000?
|
||||||
|
je near jmpStart ; yes
|
||||||
|
int 0x20 ; no, so assume we're running under DOS and exit
|
||||||
|
|
||||||
;
|
;
|
||||||
; Fill the remaining space with NOPs until we get to target offset 0xFFF0.
|
; Fill the remaining space with NOPs until we get to target offset 0xFFF0.
|
||||||
|
|
@ -185,9 +210,10 @@ inReal:
|
||||||
;
|
;
|
||||||
times 0xfff0-0x100-($-$$) nop
|
times 0xfff0-0x100-($-$$) nop
|
||||||
|
|
||||||
|
jmpStart:
|
||||||
jmp CSEG_REAL:start
|
jmp CSEG_REAL:start
|
||||||
|
|
||||||
db 0x20
|
db 0x20
|
||||||
db '04/04/15'
|
db '04/04/15'
|
||||||
db 0xFC ; 0000FFFE FC (Model ID byte)
|
db 0xFC ; 0000FFFE FC (Model ID byte)
|
||||||
db 0x00 ; 0000FFFF 00 (location of checksum byte)
|
db 0x00 ; 0000FFFF 00 (location of checksum byte)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue