From f36b79cd257b6f99842debb3b0b3553e97616aa3 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Tue, 5 May 2015 16:16:57 -0700 Subject: [PATCH] Debugger must use CPU memory functions now, in preparation for paging --- modules/pcjs/lib/debugger.js | 49 ++++++++++-------- modules/pcjs/lib/x86func.js | 2 +- modules/pcjs/lib/x86seg.js | 21 +++++++- tests/pc/80386/tests.nasm | 97 +++++++++++++++++++++++++++++++++--- 4 files changed, 138 insertions(+), 31 deletions(-) diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 06228c0a5..ea6ddf420 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -1513,10 +1513,10 @@ if (DEBUGGER) { { this.println("id physaddr blkaddr used size type"); this.println("-------- --------- -------- ------ ------ ----"); - for (var i = 0; i < this.bus.aMemBlocks.length; i++) { - var block = this.bus.aMemBlocks[i]; + for (var i = 0; i < this.cpu.aMemBlocks.length; i++) { + var block = this.cpu.aMemBlocks[i]; if (block.type === Memory.TYPE.NONE) continue; - this.println(str.toHex(block.id) + " %" + str.toHex(i << this.bus.blockShift) + ": " + str.toHex(block.addr) + " " + str.toHexWord(block.used) + " " + str.toHexWord(block.size) + " " + Memory.TYPE.NAMES[block.type]); + 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]); } }; @@ -1636,7 +1636,7 @@ if (DEBUGGER) { for (var sField in Debugger.aTSSFields) { var off = Debugger.aTSSFields[sField]; var ch = (sField.length < 8? ' ' : ''); - var w = this.bus.getShortDirect(seg.base + off); + var w = this.cpu.getShort(seg.base + off); if (sDump) sDump += '\n'; sDump += str.toHexWord(off) + " " + sField + ": " + ch + str.toHexWord(w); } @@ -2450,7 +2450,7 @@ if (DEBUGGER) { */ if (nState >= 0 && this.aaOpcodeCounts.length) { this.cInstructions++; - var bOpcode = this.bus.getByteDirect(addr); + var bOpcode = this.cpu.getByte(addr); this.aaOpcodeCounts[bOpcode][1]++; var a = this.aOpcodeHistory[this.iOpcodeHistory]; a[0] = this.cpu.getIP(); @@ -2602,8 +2602,7 @@ if (DEBUGGER) { /** * getByte(aAddr, inc) * - * getByte() should be used for all Debugger memory reads (eg, doDump, doUnassemble), to ensure - * all notification handlers are bypassed for physical addresses. + * We must route all our memory requests through the CPU now, in case paging is enabled. * * @this {Debugger} * @param {Array} aAddr @@ -2615,8 +2614,9 @@ if (DEBUGGER) { var b = 0xff; var addr = this.getAddr(aAddr, false, 1); if (addr !== X86.ADDR_INVALID) { - b = this.bus.getByteDirect(addr); - this.assert((b == (b & 0xff)), "invalid byte (" + b + ") at address: " + this.hexAddr(aAddr)); + this.nSuppress++; + b = this.cpu.getByte(addr); + this.nSuppress--; if (inc !== undefined) this.incAddr(aAddr, inc); } return b; @@ -2651,8 +2651,9 @@ if (DEBUGGER) { var w = 0xffff; var addr = this.getAddr(aAddr, false, 2); if (addr !== X86.ADDR_INVALID) { - w = this.bus.getShortDirect(addr); - this.assert((w == (w & 0xffff)), "invalid word (" + w + ") at address: " + this.hexAddr(aAddr)); + this.nSuppress++; + w = this.cpu.getShort(addr); + this.nSuppress--; if (inc !== undefined) this.incAddr(aAddr, inc); } return w; @@ -2671,7 +2672,9 @@ if (DEBUGGER) { var l = -1; var addr = this.getAddr(aAddr, false, 4); if (addr !== X86.ADDR_INVALID) { - l = this.bus.getLongDirect(addr); + this.nSuppress++; + l = this.cpu.getLong(addr); + this.nSuppress--; if (inc !== undefined) this.incAddr(aAddr, inc); } return l; @@ -2680,10 +2683,6 @@ if (DEBUGGER) { /** * setByte(aAddr, b, inc) * - * setByte() should be used for all Debugger memory writes (eg, doAssemble, doEdit), to insure - * all memory notification handlers are bypassed; in addition, we want the Debugger to be able to - * change the contents of the simulated ROM images. - * * @this {Debugger} * @param {Array} aAddr * @param {number} b @@ -2693,7 +2692,9 @@ if (DEBUGGER) { { var addr = this.getAddr(aAddr, true, 1); if (addr !== X86.ADDR_INVALID) { - this.bus.setByteDirect(addr, b); + this.nSuppress++; + this.cpu.setByte(addr, b); + this.nSuppress--; if (inc !== undefined) this.incAddr(aAddr, inc); this.cpu.updateCPU(); } @@ -2711,7 +2712,9 @@ if (DEBUGGER) { { var addr = this.getAddr(aAddr, true, 2); if (addr !== X86.ADDR_INVALID) { - this.bus.setShortDirect(addr, w); + this.nSuppress++; + this.cpu.setShort(addr, w); + this.nSuppress--; if (inc !== undefined) this.incAddr(aAddr, inc); this.cpu.updateCPU(); } @@ -2738,6 +2741,7 @@ if (DEBUGGER) { } } this.aBreakWrite = ["write"]; + this.nSuppress = 0; }; /** @@ -2921,6 +2925,8 @@ if (DEBUGGER) { */ Debugger.prototype.checkBreakpoint = function(addr, aBreak, fTemp) { + if (this.nSuppress) return false; + /* * Time to check for execution breakpoints; note that this should be done BEFORE updating frequency * or history data (see checkInstruction), since we might not actually execute the current instruction. @@ -3536,8 +3542,8 @@ if (DEBUGGER) { */ Debugger.prototype.parseAddr = function(sAddr, type) { + var aAddr; var aAddrNext = (type == Debugger.ADDR_DATA? this.aAddrNextData : this.aAddrNextCode); - var off = aAddrNext[0], seg = aAddrNext[1], addr = aAddrNext[2]; if (sAddr !== undefined) { @@ -3549,7 +3555,7 @@ if (DEBUGGER) { addr = 0; } - var aAddr = this.findSymbolAddr(sAddr); + aAddr = this.findSymbolAddr(sAddr); if (aAddr && aAddr.length) return aAddr; var iColon = sAddr.indexOf(":"); @@ -3567,7 +3573,8 @@ if (DEBUGGER) { addr = null; } } - var aAddr = [off, seg, addr]; + + aAddr = [off, seg, addr]; this.checkLimit(aAddr); return aAddr; }; diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 917cd7d1f..62485067b 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -3643,7 +3643,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite) X86.fnFaultMessage = function(nFault, nError, fHalt) { var bitsMessage = Messages.FAULT; - var bOpcode = this.bus.getByteDirect(this.regLIP); + var bOpcode = this.getByte(this.regLIP); /* * OS/2 1.0 uses an INT3 (0xCC) opcode in conjunction with an invalid IDT to trigger a triple-fault diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 122e8131c..bb80f8c7f 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -605,6 +605,13 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) var fGate, regPSMask, nFaultError, regSP; var rpl = sel & X86.SEL.RPL; var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; + + if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) { + if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel); + base = X86.ADDR_INVALID; + break; + } + /* * Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic cpu.segCS.cpl */ @@ -713,7 +720,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) } else if (this.id == X86Seg.ID.DATA) { if (selMasked) { - if (type < X86.DESC.ACC.TYPE.DATA_READONLY || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { + if (!(acc & X86.DESC.ACC.PRESENT)) { + if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel); + base = X86.ADDR_INVALID; + break; + } + if (type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { /* * OS/2 1.0 triggers this "Empty Descriptor" GP_FAULT multiple times during boot; eg: * @@ -742,7 +754,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress) } } else if (this.id == X86Seg.ID.STACK) { - if (!selMasked || type < X86.DESC.ACC.TYPE.DATA_READONLY || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { + if (!(acc & X86.DESC.ACC.PRESENT)) { + if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel); + base = X86.ADDR_INVALID; + break; + } + if (!selMasked || type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.WRITABLE)) != X86.DESC.ACC.TYPE.WRITABLE) { if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true); base = X86.ADDR_INVALID; break; diff --git a/tests/pc/80386/tests.nasm b/tests/pc/80386/tests.nasm index 035952149..4b39c08ac 100644 --- a/tests/pc/80386/tests.nasm +++ b/tests/pc/80386/tests.nasm @@ -14,14 +14,32 @@ DPL2 equ 2 DPL3 equ 3 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 +ACC_TYPE_CODE_READABLE equ (0x1a00 | ACC_PRESENT) +ACC_TYPE_DATA_WRITABLE equ (0x1200 | ACC_PRESENT) EXT_BIG equ 0x0040 +; +; We build some data structures in the first page (0x0000-0x0fff) of RAM: +; +; 0x0000-0x03ff Real-mode IDT (256*4) +; 0x0400-0x0bff Prot-mode IDT (256*8) +; 0x0c00-0x0cff GDT (enough room for 32 selectors) +; 0x0d00-0x0d07 IDTR +; 0x0d08-0x0d0f GDTR +; 0x0d10-0x0fff reserved +; +; And in the second page (0x1000-0x1fff), let's 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). +; +RAM_GDT equ 0x0c00 +RAM_IDTR equ 0x0d00 +RAM_GDTR equ 0x0d08 + CSEG_REAL equ 0xf000 CSEG_PROT equ 0x0008 DSEG_PROT equ 0x0010 @@ -29,22 +47,77 @@ DSEG_PROT equ 0x0010 CR0_MSW_PE equ 0x0001 ; -; descDT defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and ext (%5) +; set initializes a register to the specified value (eg, "set eax,0") ; -%macro defDesc 1-5 0,0,0,0 +%macro set 2 + %if %2 = 0 + sub %1,%1 + %else + mov %1,%2 + %endif +%endmacro + +; +; defDesc defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and ext (%5) +; +%macro defDesc 1-4 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), dpl (%4), and ext (%5) +; +%assign selDesc 0 +%macro setDesc 1-4 0,0,0,none + set ebx,%1 + set ecx,%2 + set edx,%3 + call storeDesc + %assign %4 selDesc + %assign selDesc selDesc+8 +%endmacro -start: mov eax,0x44332211 +start: cli ; disable all interrupts + mov al,0xff ; and ensure that no hardware interrupts can sneak in + out 0x21,al ; if interrupts become enabled later + out 0xa1,al + + sub ax,ax + mov ds,ax + mov es,ax + mov ss,ax + mov sp,0x1000 + + mov eax,0x44332211 + mov ebx,eax mov ecx,0x88776655 mul ecx div ecx - jnz near goProt ; apparently we have to tell NASM "near" because this is a forward reference + cmp eax,ebx + je near initRAM ; 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 +; +; storeDesc(EBX=base, ECX=limit, EDX=type, EDI=target) +; +storeDesc: + cld + mov ax,cx + stosw ; store the low 16 bits of limit from ECX + mov ax,bx + stosw ; store the low 16 bits of base from EBX + mov ax,dx + shr ebx,16 + mov al,bl + stosw + shr ecx,16 + mov al,cl + and al,0xf + mov ah,bh + stosw + ret 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) @@ -54,7 +127,17 @@ romGDT: defDesc 0 ; the first descriptor in any descriptor table is always a d defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE romGDTEnd: -goProt: o32 lgdt [cs:addrGDT] +initRAM: + set edi,RAM_GDT + mov [RAM_GDTR+2],edi + setDesc 0,0,0,NULL + setDesc 0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,CSEG_PROT + setDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,DSEG_PROT + sub edi,RAM_GDT + dec edi + mov [RAM_GDTR],di + +goProt: o32 lgdt [RAM_GDTR] mov eax,cr0 or eax,CR0_MSW_PE mov cr0,eax