From f3646d206f56d0ad9493a1692d47042920a343e1 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Thu, 7 May 2015 16:00:38 -0700 Subject: [PATCH] Fixed MOVSX,MOVZX, and passed initial paging test --- modules/pcjs/bin/romtests.json | 1 + modules/pcjs/lib/debugger.js | 31 ++-- modules/pcjs/lib/x86cpu.js | 2 +- modules/pcjs/lib/x86op0f.js | 4 +- modules/pcjs/lib/x86seg.js | 31 ++-- tests/pc/80386/makefile | 4 +- tests/pc/80386/tests.nasm | 281 ++++++++++++++++++++++----------- tests/pc/inc/dos.inc | 141 +++++++++++++++++ tests/pc/inc/misc.inc | 17 ++ tests/pc/inc/x86.inc | 23 +++ 10 files changed, 405 insertions(+), 130 deletions(-) create mode 100644 tests/pc/inc/dos.inc create mode 100644 tests/pc/inc/misc.inc create mode 100644 tests/pc/inc/x86.inc diff --git a/modules/pcjs/bin/romtests.json b/modules/pcjs/bin/romtests.json index 744e42325..a40b43ce4 100644 --- a/modules/pcjs/bin/romtests.json +++ b/modules/pcjs/bin/romtests.json @@ -78,6 +78,7 @@ "debugger": { "id": "pc386.debugger", "name": "", + "commands": "", "messages": "" } } diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index be2965783..fb32d5c7f 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -744,9 +744,9 @@ if (DEBUGGER) { /* 0x89 */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_OUT, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_IN], /* 0x8A */ [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_BYTE | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN], /* 0x8B */ [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_IN], - /* 0x8C */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_OUT, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_IN], + /* 0x8C */ [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_OUT, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_IN], /* 0x8D */ [Debugger.INS.LEA, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_OUT, Debugger.TYPE_MEM | Debugger.TYPE_WORDV], - /* 0x8E */ [Debugger.INS.MOV, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN], + /* 0x8E */ [Debugger.INS.MOV, Debugger.TYPE_SEGREG | Debugger.TYPE_WORD | Debugger.TYPE_OUT, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_IN], /* 0x8F */ [Debugger.INS.POP, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_OUT], /* 0x90 */ [Debugger.INS.NOP], @@ -883,8 +883,8 @@ if (DEBUGGER) { 0x03: [Debugger.INS.LSL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_OUT | Debugger.TYPE_80286, Debugger.TYPE_MEM | Debugger.TYPE_WORD | Debugger.TYPE_IN], 0x05: [Debugger.INS.LOADALL,Debugger.TYPE_80286], 0x06: [Debugger.INS.CLTS, Debugger.TYPE_80286], - 0x20: [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN], - 0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_IN], + 0x20: [Debugger.INS.MOV, Debugger.TYPE_MODRM | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN], + 0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_DWORD | Debugger.TYPE_IN], 0x80: [Debugger.INS.JO, Debugger.TYPE_IMMREL | Debugger.TYPE_WORDV | Debugger.TYPE_IN | Debugger.TYPE_80386], 0x81: [Debugger.INS.JNO, Debugger.TYPE_IMMREL | Debugger.TYPE_WORDV | Debugger.TYPE_IN | Debugger.TYPE_80386], 0x82: [Debugger.INS.JC, Debugger.TYPE_IMMREL | Debugger.TYPE_WORDV | Debugger.TYPE_IN | Debugger.TYPE_80386], @@ -910,7 +910,9 @@ if (DEBUGGER) { 0xAC: [Debugger.INS.SHRD, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN], 0xAD: [Debugger.INS.SHRD, Debugger.TYPE_MODRM | Debugger.TYPE_WORDV | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN], 0xB6: [Debugger.INS.MOVZX, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN], - 0xB7: [Debugger.INS.MOVZX, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN] + 0xB7: [Debugger.INS.MOVZX, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN], + 0xBE: [Debugger.INS.MOVSX, Debugger.TYPE_REG | Debugger.TYPE_WORDV | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN], + 0xBF: [Debugger.INS.MOVSX, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN] }; Debugger.aaGrpDescs = [ @@ -1243,12 +1245,6 @@ if (DEBUGGER) { this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); }); this.setReady(); - - if (this.sInitCommands) { - var a = this.parseCommand(this.sInitCommands); - delete this.sInitCommands; - for (var s in a) this.doCommand(a[s]); - } }; /** @@ -1514,7 +1510,7 @@ if (DEBUGGER) { this.println("id physaddr blkaddr used size type"); this.println("-------- --------- -------- ------ ------ ----"); for (var i = 0; i < this.cpu.aMemBlocks.length; i++) { - var block = this.cpu.aMemBlocks[i]; + var block = this.cpu.aBusBlocks[i]; if (block.type === Memory.TYPE.NONE) continue; this.println(str.toHex(block.id) + " %" + str.toHex(i << this.cpu.blockShift) + ": " + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + Memory.TYPE.NAMES[block.type]); } @@ -2046,6 +2042,11 @@ if (DEBUGGER) { { this.println("Type ? for list of debugger commands"); this.updateStatus(); + if (this.sInitCommands) { + var a = this.parseCommand(this.sInitCommands); + delete this.sInitCommands; + for (var s in a) this.doCommand(a[s]); + } }; /** @@ -2225,7 +2226,9 @@ if (DEBUGGER) { * we always reset and then perform a (potentially limited) restore. */ this.reset(true); - this.println(data? "resuming" : "powering up"); + + // this.println(data? "resuming" : "powering up"); + if (data && this.restore) { if (!this.restore(data)) return false; } @@ -5358,7 +5361,7 @@ if (DEBUGGER) { } } } catch(e) { - this.println("debugger sad: " + e.message); + this.println("debugger error: " + (e.stack || e.message)); result = false; } return result; diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index fe2fda5b2..e62692f7f 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -793,7 +793,7 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite) * Now we can create a new PAGED Memory block and record the physical block info using setPhysBlock(). */ var addrPage = addr & ~X86.LADDR.OFFSET; - var blockPage = new Memory(addrPage, 0, this.blockSize, Memory.TYPE.PAGED); + var blockPage = new Memory(addrPage, 0, 0, Memory.TYPE.PAGED); blockPage.setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE); var iBlock = addr >>> this.blockShift; diff --git a/modules/pcjs/lib/x86op0f.js b/modules/pcjs/lib/x86op0f.js index dd76514b5..04809f1dc 100644 --- a/modules/pcjs/lib/x86op0f.js +++ b/modules/pcjs/lib/x86op0f.js @@ -313,11 +313,9 @@ X86.opMOVcr = function MOVcr() break; case 0x2: temp = this.regEDX; - if (DEBUG) this.stopCPU(); break; case 0x3: temp = this.regEBX; - if (DEBUG) this.stopCPU(); break; default: X86.opInvalid.call(this); @@ -1128,6 +1126,7 @@ X86.opMOVZXb = function MOVZXb() X86.opMOVZXw = function MOVZXw() { var bModRM = this.getIPByte(); + this.setDataSize(2); this.aOpModRegWord[bModRM].call(this, X86.fnMOVX); switch((bModRM & 0x38) >> 3) { case 0x0: @@ -1287,6 +1286,7 @@ X86.opMOVSXb = function MOVSXb() X86.opMOVSXw = function MOVSXw() { var bModRM = this.getIPByte(); + this.setDataSize(2); this.aOpModRegWord[bModRM].call(this, X86.fnMOVX); switch((bModRM & 0x38) >> 3) { case 0x0: diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index e22cc77c4..a5abe9579 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -887,22 +887,15 @@ X86Seg.prototype.updateMode = function(fLoad, fProt) if (fProt === undefined) { fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE); } + /* * The following properties are used for STACK segments only (ie, segSS); we want to make it easier * for setSS() to set stack lower and upper limits, which requires knowing whether or not the segment is * marked as EXPDOWN. */ this.fExpDown = false; + if (fProt) { - /* - * If we've just transitioned from real-mode, then CPL (which is meaningful for segCS only) - * should be left alone (ie, should remain at zero). On the first protected-mode CS selector - * load (normally an intersegment JMP immediately following the protected-mode switch), CPL - * (along with the rest of the segment settings) will be properly set. - */ - if (this.load !== this.loadReal) { - this.cpl = this.sel & X86.SEL.RPL; - } this.load = this.loadProt; this.loadIDT = this.loadIDTProt; this.checkRead = this.checkReadProt; @@ -929,13 +922,13 @@ X86Seg.prototype.updateMode = function(fLoad, fProt) this.fExpDown = true; } } - this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; if (fLoad) { /* - * Any update to the OPERAND and ADDRESS sizes should happen only on segment loads, not simply when - * we're updating the segment register as part of a mode change. Note that there is no counterpart to - * this for real-mode, because real-mode segment loads never change these attributes. + * Any update to the following properties must occur only on segment loads, not simply when + * we're updating segment registers as part of a mode change. */ + this.cpl = this.sel & X86.SEL.RPL; + this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; if (this.cpu.model < X86.MODEL_80386 || !(this.ext & X86.DESC.EXT.BIG)) { this.dataSize = 2; this.dataMask = 0xffff; @@ -957,19 +950,15 @@ X86Seg.prototype.updateMode = function(fLoad, fProt) * * See http://www.os2museum.com/wp/himem-sys-unreal-mode-and-loadall/ for more details. * - * Ditto for other attributes such as acc, type, ext, and the OPERAND and ADDRESS sizes, which are derived - * from the ext property. Even an explicit segment load in real-mode does not alter these properties. - * - * this.limit = 0xffff; - * this.acc = this.type = this.ext = 0; - * this.dataSize = this.addrSize = 2; - * this.dataMask = this.addrMask = 0xffff; + * Ditto for other attributes such as acc, type, and ext (and the OPERAND and ADDRESS sizes, which + * are derived from the ext property). Even an explicit segment load in real-mode does not alter those + * properties. * * TODO: The checkReadReal() and checkWriteReal() functions need to generate GP faults for offsets * beyond the current real-mode limit. */ - this.addrDesc = X86.ADDR_INVALID; this.cpl = this.dpl = 0; + this.addrDesc = X86.ADDR_INVALID; } return fProt; }; diff --git a/tests/pc/80386/makefile b/tests/pc/80386/makefile index ef68f7ba8..77045f7cc 100644 --- a/tests/pc/80386/makefile +++ b/tests/pc/80386/makefile @@ -1,7 +1,7 @@ all: tests.json -tests.com: tests.nasm - nasm -f bin tests.nasm -l tests.lst -o tests.com +tests.com: tests.nasm ../inc/dos.inc ../inc/misc.inc ../inc/x86.inc + nasm -i../inc/ -f bin tests.nasm -l tests.lst -o tests.com tests.json: tests.com node ../../../modules/filedump/bin/filedump --file=tests.com --output=tests.json --overwrite diff --git a/tests/pc/80386/tests.nasm b/tests/pc/80386/tests.nasm index 1f8a252e0..321d28315 100644 --- a/tests/pc/80386/tests.nasm +++ b/tests/pc/80386/tests.nasm @@ -4,48 +4,41 @@ ; ; 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. +; address (%0xfffffff0), which will transfer control to 0xf000:0x0100. From that point on, +; all memory accesses should remain within the first 1Mb. ; ; 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. +; See the machine definition file in /modules/pcjs/bin/romtests.json for a configuration that can +; load this file as a ROM image. ; -; PMODE_32BIT Notes +; PROT32 Notes ; ----------------- -; PMODE_32BIT is NOT enabled by default, because based on what I've seen in VirtualBox (and notes +; PROT32 is NOT enabled by default, because based on what I've seen in VirtualBox (and notes ; at http://geezer.osdevbrasil.net/johnfine/segments.htm), if CS is loaded with a 32-bit code segment -; while in protected-mode and we then return to real-mode, even if we immediately perform a FAR JMP -; with a real-mode CS, the base and limit of CS will be updated, but other CS attributes, like EXT_BIG, -; will NOT be updated. As a result, the processor will crash as soon as it starts executing 16-bit -; real-mode code, because it's being misinterpreted as 32-bit code, and there doesn't appear to be -; anything you can do about it from real-mode. +; while in protected-mode and we then return to real-mode, even if we immediately perform a FAR jump +; with a real-mode CS, the base of CS will be updated, but all the other segment attributes, like +; EXT_BIG, remain unchanged. As a result, the processor will crash as soon as it starts executing +; 16-bit real-mode code, because it's being misinterpreted as 32-bit code, and there doesn't appear +; to be anything you can do about it from real-mode. ; -; The work-around: switch to a 16-bit code segment BEFORE returning to real-mode. +; The work-around: load CS with a 16-bit code segment BEFORE returning to real-mode. ; ; "Unreal mode" works by setting other segment registers (eg, DS, ES) to 32-bit segments, not CS. ; SS should not be set to a 32-bit segment either, because that causes implicit pushes to use ESP ; instead of SP, even in real-mode. -; -; TODO: Verify that a 80386 cannot successfully return to real-mode when CS contains a 32-bit code segment. ; cpu 386 org 0x100 section .text + %include "dos.inc" + %include "misc.inc" + %include "x86.inc" + bits 16 -ACC_TYPE_SEG equ 0x1000 -ACC_PRESENT equ 0x8000 -ACC_TYPE_CODE equ 0x0800 -ACC_TYPE_READABLE equ 0x0200 -ACC_TYPE_WRITABLE equ 0x0200 -ACC_TYPE_CODE_READABLE equ 0x1a00 -ACC_TYPE_DATA_WRITABLE equ 0x1200 - -EXT_NONE equ 0x0000 -EXT_BIG equ 0x0040 - ; ; If we built our data structures in RAM, we might use the first page of RAM (0x0000-0x0fff) like so: ; @@ -66,25 +59,9 @@ EXT_BIG equ 0x0040 ;RAM_RETF equ 0x0d10 CSEG_REAL equ 0xf000 -CSEG_PROT equ 0x0008 -DSEG_PROT equ 0x0010 - -CR0_MSW_PE equ 0x0001 - -; -; The "set" macro initializes a register to the specified value (eg, "set eax,0") -; -%macro set 2 - %ifnum %2 - %if %2 = 0 - xor %1,%1 - %else - mov %1,%2 - %endif - %else - mov %1,%2 - %endif -%endmacro +CSEG_PROT16 equ 0x0008 +CSEG_PROT32 equ 0x0010 +DSEG_PROT16 equ 0x0018 ; ; The "defDesc" macro defines a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5) @@ -118,14 +95,29 @@ CR0_MSW_PE equ 0x0001 %endmacro start: cli + ; + ; Test unsigned 32-bit multiplication and division + ; mov eax,0x44332211 mov ebx,eax mov ecx,0x88776655 mul ecx div ecx cmp eax,ebx - je near initGDT ; apparently we have to tell NASM "near" because this is a forward reference + jne near exitErr ; apparently we have to tell NASM "near" because this is a forward reference + + xor dx,dx + mov ds,dx ; DS -> 0x0000 + ; + ; Test moving a segment register to a 32-bit register + ; + mov eax,ds + test eax,eax + jnz near exitErr + + jmp initGDT 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) ; @@ -150,19 +142,15 @@ storeDesc: ret addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT - dw myGDT, 0xffff ; 32-bit base address of myGDT (works as long as we're aliased at 0xffff0000) + dw myGDT, 0x000f ; 32-bit base address of myGDT ; ; 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) - %ifdef PMODE_32BIT - defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG - defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_BIG - %else - defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE - defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE - %endif + defDesc CSEG_PROT16,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE + defDesc CSEG_PROT32,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG + defDesc DSEG_PROT16,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE myGDTEnd: initGDT: @@ -173,8 +161,9 @@ initGDT: xor eax,eax mov ax,cs shl eax,4 - setDesc CSEG_PROT,eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE - setDesc DSEG_PROT,0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE + setDesc CSEG_PROT16,eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE + setDesc CSEG_PROT32,eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG + setDesc DSEG_PROT16,0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE sub edi,RAM_GDT dec edi mov [RAM_GDTR],di @@ -182,65 +171,177 @@ initGDT: mov word [RAM_RETF+2],cs %else ; - ; This code will have no effect if we're in ROM (but in that case, everything should already be initialized correctly) + ; This code fixes the GDT and all our FAR jumps if we're running in RAM ; xor eax,eax mov ax,cs - shl eax,4 ; EAX = base address of the current 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 + mov [cs:myGDT+CSEG_PROT16+2],ax ; update the base portions of the descriptor for CSEG_PROT16 and CSEG_PROT32 + mov [cs:myGDT+CSEG_PROT32+2],ax shr eax,16 - mov [cs:myGDT+CSEG_PROT+4],al - mov [cs:myGDT+CSEG_PROT+7],ah + mov [cs:myGDT+CSEG_PROT16+4],al + mov [cs:myGDT+CSEG_PROT32+4],al + mov [cs:myGDT+CSEG_PROT16+7],ah + mov [cs:myGDT+CSEG_PROT32+7],ah mov eax,edx ; recover the base address of the current CS - add eax,myGDT ; EAX = physical address of myGDT + add eax,myGDT ; EAX == physical address of myGDT mov [cs:addrGDT+2],eax ; update the 32-bit base address of myGDT in addrGDT - %ifdef PMODE_32BIT - mov [cs:jmpReal+5],cs ; update the segment of the far jmp that returns us to real-mode - %else - mov [cs:jmpReal+3],cs - %endif - mov [cs:jmpStart+3],cs ; ditto for the far jmp that returns us to the start of the image + mov ax,cs + %ifdef PROT32 + mov [cs:jmpReal+5],ax ; update the segment of the FAR jump that returns us to real-mode + %else + mov [cs:jmpReal+3],ax + %endif + mov [cs:jmpStart+3],ax ; ditto for the FAR jump that returns us to the start of the image %endif + ; + ; Now we want to build a page directory and a page table, but we need two pages of + ; 4K-aligned physical memory. We can use hard-coded addresses in we're running in ROM, + ; otherwise we ask DOS for some memory. + ; + cmp ax,CSEG_REAL + mov edi,0x1000 ; default to the 2nd physical page in low memory + je initPages + mov bx,0x1000 ; 4K paragraphs == 64K bytes + mov ah,DOS_SETBLOCK ; resize the current block so we can allocate a new block + int INT_DOS + jnc allocPages +exitErrDOSMem: + mov dx,errDOSMem +exitErrDOS: + mov ah,DOS_STD_CON_STRING_OUTPUT + int INT_DOS + int INT_DOSEXIT + +errDOSMem: + db "Insufficient memory",CR,LF,'$' + +exitErr:nop + int3 + jmp exitErr + +allocPages: + mov bx,0x1000 ; 4K paragraphs == 64K bytes + mov ah,DOS_ALLOC + int INT_DOS + jc errDOSMem + ; + ; AX == segment of 64K memory block + ; + movzx eax,ax + shl eax,4 + add eax,0xfff + and eax,~0xfff + mov edi,eax ; EDI == first physical 4K-aligned page within the 64K + +initPages: + mov esi,edi ; ESI == saved copy of EDI + ; + ; Build a page directory at EDI with only 1 valid PDE (the first one) + ; + cld + mov eax,edi + add eax,0x1000 ; EAX == page frame address (of the next page) + or eax,PTE_USER | PTE_READWRITE | PTE_PRESENT + stosd + mov ecx,1024-1 ; ECX == number of (remaining) PDEs to write + sub eax,eax + rep stosd + ; + ; Build a page table at EDI with only 256 (out of 1024) valid PTEs, which will map the first 1Mb of the + ; first 4Mb as linear == physical. + ; + mov eax,PTE_USER | PTE_READWRITE | PTE_PRESENT + mov ecx,256 ; ECX == number of PTEs to write +initPT: stosd + add eax,0x1000 + loop initPT + mov ecx,1024-256 ; ECX == number of (remaining) PTEs to write + sub eax,eax + rep stosd goProt: o32 lgdt [cs:addrGDT] + mov cr3,esi mov eax,cr0 + %ifdef PAGING + or eax,CR0_MSW_PE | CR0_PG + %else or eax,CR0_MSW_PE - mov cr0,eax - nop -jmpProt: - jmp CSEG_PROT:toProt - -toProt: - %ifdef PMODE_32BIT - bits 32 ; only if we define the CSEG_PROT descriptor with EXT_BIG %endif - mov ax,DSEG_PROT + mov cr0,eax +jmpProt: + jmp CSEG_PROT32:toProt32 + +toProt32: + bits 32 + + mov ax,DSEG_PROT16 mov ds,ax mov es,ax -; -; Do some protected-mode tests now... -; + ; + ; Test moving a segment register to a 32-bit memory location + ; + mov edx,[0x0000] ; save the DWORD at 0x0000:0x0000 in EDX + or eax,-1 + mov [0x0000],eax + mov [0x0000],ds + mov eax,ds + cmp eax,[0x0000] +err1: jne err1 + mov [0x0000],edx ; restore the DWORD at 0x0000:0x0000 from EDX + + ; + ; Test moving a byte to a 32-bit register with sign-extension + ; + movsx eax,byte [0xfffff] + cmp eax,0xffffff80 +err2: jne err2 + + ; + ; Test moving a word to a 32-bit register with sign-extension + ; + movsx eax,word [0xffffe] + cmp eax,0xffff80fc +err3: jne err3 + + ; + ; Test moving a byte to a 32-bit register with zero-extension + ; + movzx eax,byte [0xfffff] + cmp eax,0x00000080 +err4: jne err4 + + ; + ; Test moving a word to a 32-bit register with zero-extension + ; + movzx eax,word [0xffffe] + cmp eax,0x000080fc +err5: jne err5 + + ; + ; Return to real-mode now, after first loading CS with a 16-bit code segment + ; + jmp CSEG_PROT16:toProt16 + +toProt16: + bits 16 + goReal: mov eax,cr0 - and eax,~CR0_MSW_PE + and eax,~(CR0_MSW_PE | CR0_PG) mov cr0,eax - nop jmpReal: jmp CSEG_REAL:toReal toReal: - %ifdef PMODE_32BIT - bits 16 ; only if we define the CSEG_PROT descriptor with EXT_BIG - %endif 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. - ; Note that we subtract 0x100 from the target offset because we're ORG'ed at 0x100. - ; + je near jmpStart ; yes, so loop around, only because we have nowhere else to go + int INT_DOSEXIT ; no, so assume we're running under DOS and exit + ; + ; Fill the remaining space with NOPs until we get to target offset 0xFFF0. + ; Note that we subtract 0x100 from the target offset because we're ORG'ed at 0x100. + ; times 0xfff0-0x100-($-$$) nop jmpStart: @@ -249,4 +350,4 @@ jmpStart: db 0x20 db '04/04/15' db 0xFC ; 0000FFFE FC (Model ID byte) - db 0x00 ; 0000FFFF 00 (location of checksum byte) + db 0x80 ; 0000FFFF 80 (normally, location of a checksum byte) diff --git a/tests/pc/inc/dos.inc b/tests/pc/inc/dos.inc new file mode 100644 index 000000000..4cc61fffb --- /dev/null +++ b/tests/pc/inc/dos.inc @@ -0,0 +1,141 @@ +INT_DOSEXIT EQU 20H +INT_DOS EQU 21H +DOS_ABORT EQU 00H +DOS_STD_CON_INPUT EQU 01H +DOS_STD_CON_OUTPUT EQU 02H +DOS_STD_AUX_INPUT EQU 03H +DOS_STD_AUX_OUTPUT EQU 04H +DOS_STD_PRINTER_OUTPUT EQU 05H +DOS_RAW_CON_IO EQU 06H +DOS_RAW_CON_INPUT EQU 07H +DOS_STD_CON_INPUT_NO_ECHO EQU 08H +DOS_STD_CON_STRING_OUTPUT EQU 09H +DOS_STD_CON_STRING_INPUT EQU 0AH +DOS_STD_CON_INPUT_STATUS EQU 0BH +DOS_STD_CON_INPUT_FLUSH EQU 0CH +DOS_DISK_RESET EQU 0DH +DOS_SET_DEFAULT_DRIVE EQU 0EH +DOS_FCB_OPEN EQU 0FH +DOS_FCB_CLOSE EQU 10H +DOS_DIR_SEARCH_FIRST EQU 11H +DOS_DIR_SEARCH_NEXT EQU 12H +DOS_FCB_DELETE EQU 13H +DOS_FCB_SEQ_READ EQU 14H +DOS_FCB_SEQ_WRITE EQU 15H +DOS_FCB_CREATE EQU 16H +DOS_FCB_RENAME EQU 17H +DOS_RESERVED18 EQU 18H +DOS_GET_DEFAULT_DRIVE EQU 19H +DOS_SET_DMA EQU 1AH +DOS_RESERVED1B EQU 1BH +DOS_RESERVED1C EQU 1CH +DOS_RESERVED1D EQU 1DH +DOS_RESERVED1E EQU 1EH +DOS_GET_DEFAULT_DPB EQU 1FH +DOS_RESERVED20 EQU 20H +DOS_FCB_RANDOM_READ EQU 21H +DOS_FCB_RANDOM_WRITE EQU 22H +DOS_GET_FCB_FILE_LENGTH EQU 23H +DOS_GET_FCB_POSITION EQU 24H +DOS_SET_INTERRUPT_VECTOR EQU 25H +DOS_CREATE_PDB EQU 26H +DOS_FCB_RANDOM_READ_BLOCK EQU 27H +DOS_FCB_RANDOM_WRITE_BLOCK EQU 28H +DOS_PARSE_FILE_DESCRIPTOR EQU 29H +DOS_GET_DATE EQU 2AH +DOS_SET_DATE EQU 2BH +DOS_GET_TIME EQU 2CH +DOS_SET_TIME EQU 2DH +DOS_SET_VERIFY_ON_WRITE EQU 2EH +DOS_GET_DMA EQU 2FH +DOS_GET_VERSION EQU 30H +DOS_KEEP_PROCESS EQU 31H +DOS_GET_DPB EQU 32H +DOS_SET_CTRL_C_TRAPPING EQU 33H +DOS_GET_INDOS_FLAG EQU 34H +DOS_GET_INTERRUPT_VECTOR EQU 35H +DOS_GET_DRIVE_FREESPACE EQU 36H +DOS_CHAR_OPER EQU 37H +DOS_INTERNATIONAL_SUPPORT EQU 38H +DOS_MKDIR EQU 39H +DOS_RMDIR EQU 3AH +DOS_CHDIR EQU 3BH +DOS_CREAT EQU 3CH +DOS_OPEN EQU 3DH +DOS_CLOSE EQU 3EH +DOS_READ EQU 3FH +DOS_WRITE EQU 40H +DOS_UNLINK EQU 41H +DOS_LSEEK EQU 42H +DOS_CHMOD EQU 43H +DOS_IOCTL EQU 44H +DOS_XDUP EQU 45H +DOS_XDUP2 EQU 46H +DOS_GETDIR EQU 47H +DOS_ALLOC EQU 48H +DOS_DEALLOC EQU 49H +DOS_SETBLOCK EQU 4AH +DOS_EXEC EQU 4BH +DOS_EXIT EQU 4CH +DOS_WAIT EQU 4DH +DOS_FIND_FIRST EQU 4EH +DOS_FIND_NEXT EQU 4FH +DOS_SET_CURRENT_PDB EQU 50H +DOS_GET_CURRENT_PDB EQU 51H +DOS_GET_SYSINIT_VARS EQU 52H +DOS_SET_DPB EQU 53H +DOS_GET_VERIFY_ON_WRITE EQU 54H +DOS_DUP_PDB EQU 55H +DOS_RENAME EQU 56H +DOS_FILE_TIMES EQU 57H +DOS_RESERVED58 EQU 58H +DOS_GET_EXTENDED_ERROR EQU 59H +DOS_CREATE_TEMP_FILE EQU 5AH +DOS_CREATE_NEW_FILE EQU 5BH +DOS_LOCK_OPER EQU 5CH +DOS_SERVER_CALL EQU 5DH +DOS_USER_OPER EQU 5EH +DOS_ASSIGN_OPER EQU 5FH +DOS_XNAMETRANS EQU 60H +DOS_JSA EQU 61H +DOS_GET_CURRENT_PSP EQU 62H +DOS_HONGEUL EQU 63H +DOS_SYSINITSLEAZE EQU 64H +DOSERR_NONE EQU 00H +DOSERR_BADFUNCTION EQU 01H +DOSERR_NOFILE EQU 02H +DOSERR_NOPATH EQU 03H +DOSERR_NOHANDLES EQU 04H +DOSERR_NOACCESS EQU 05H +DOSERR_BADHANDLE EQU 06H +DOSERR_BADARENA EQU 07H +DOSERR_NOMEMORY EQU 08H +DOSERR_BADADDRESS EQU 09H +DOSERR_BADENVIRONMENT EQU 0AH +DOSERR_BADFORMAT EQU 0BH +DOSERR_BADACCESS EQU 0CH +DOSERR_BADDATA EQU 0DH +DOSERR_BADUNIT EQU 0EH +DOSERR_BADDISK EQU 0FH +DOSERR_DELETECURDIR EQU 10H +DOSERR_NOTSAMEDEVICE EQU 11H +DOSERR_NOMOREFILES EQU 12H +DOSERR_WRITEPROTECT EQU 13H +DOSERR_UNKNOWNUNIT EQU 14H +DOSERR_DRIVENOTREADY EQU 15H +DOSERR_BADCOMMAND EQU 16H +DOSERR_DATA EQU 17H +DOSERR_BADREQUEST EQU 18H +DOSERR_SEEK EQU 19H +DOSERR_UNKNOWNMEDIA EQU 1AH +DOSERR_BADSECTOR EQU 1BH +DOSERR_NOPAPER EQU 1CH +DOSERR_WRITEFAULT EQU 1DH +DOSERR_READFAULT EQU 1EH +DOSERR_GENERALFAILURE EQU 1FH +DOSERR_NOTSHARED EQU 20H +DOSERR_LOCKED EQU 21H +DOSERR_DISKCHANGE EQU 22H +SEEK_SET EQU 00H +SEEK_CUR EQU 01H +SEEK_END EQU 02H diff --git a/tests/pc/inc/misc.inc b/tests/pc/inc/misc.inc new file mode 100644 index 000000000..d6acef726 --- /dev/null +++ b/tests/pc/inc/misc.inc @@ -0,0 +1,17 @@ +CR equ 0x0d +LF equ 0x0a + +; +; The "set" macro initializes a register to the specified value (eg, "set eax,0") +; +%macro set 2 + %ifnum %2 + %if %2 = 0 + xor %1,%1 + %else + mov %1,%2 + %endif + %else + mov %1,%2 + %endif +%endmacro diff --git a/tests/pc/inc/x86.inc b/tests/pc/inc/x86.inc new file mode 100644 index 000000000..cd952913f --- /dev/null +++ b/tests/pc/inc/x86.inc @@ -0,0 +1,23 @@ +; +; X86.DESC defines (see x86.js) +; +ACC_TYPE_SEG equ 0x1000 +ACC_PRESENT equ 0x8000 +ACC_TYPE_CODE equ 0x0800 +ACC_TYPE_READABLE equ 0x0200 +ACC_TYPE_WRITABLE equ 0x0200 +ACC_TYPE_CODE_READABLE equ 0x1a00 +ACC_TYPE_DATA_WRITABLE equ 0x1200 + +EXT_NONE equ 0x0000 +EXT_BIG equ 0x0040 + +CR0_MSW_PE equ 0x0001 +CR0_PG equ 0x80000000 ; set if paging enabled + +PTE_FRAME equ 0xfffff000 +PTE_DIRTY equ 0x00000040 ; page has been modified +PTE_ACCESSED equ 0x00000020 ; page has been accessed +PTE_USER equ 0x00000004 ; set for user level (CPL 3), clear for supervisor level (CPL 0-2) +PTE_READWRITE equ 0x00000002 ; set for read/write, clear for read-only (affects CPL 3 only) +PTE_PRESENT equ 0x00000001 ; set for present page, clear for not-present page