Debugger must use CPU memory functions now, in preparation for paging

This commit is contained in:
Jeff Parsons 2015-05-05 16:16:57 -07:00 • committed by jeffpar
commit f36b79cd25
4 changed files with 138 additions and 31 deletions

View file

@ -1513,10 +1513,10 @@ if (DEBUGGER) {
{ {
this.println("id physaddr blkaddr used size type"); this.println("id physaddr blkaddr used size type");
this.println("-------- --------- -------- ------ ------ ----"); this.println("-------- --------- -------- ------ ------ ----");
for (var i = 0; i < this.bus.aMemBlocks.length; i++) { for (var i = 0; i < this.cpu.aMemBlocks.length; i++) {
var block = this.bus.aMemBlocks[i]; var block = this.cpu.aMemBlocks[i];
if (block.type === Memory.TYPE.NONE) continue; 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) { for (var sField in Debugger.aTSSFields) {
var off = Debugger.aTSSFields[sField]; var off = Debugger.aTSSFields[sField];
var ch = (sField.length < 8? ' ' : ''); 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'; if (sDump) sDump += '\n';
sDump += str.toHexWord(off) + " " + sField + ": " + ch + str.toHexWord(w); sDump += str.toHexWord(off) + " " + sField + ": " + ch + str.toHexWord(w);
} }
@ -2450,7 +2450,7 @@ if (DEBUGGER) {
*/ */
if (nState >= 0 && this.aaOpcodeCounts.length) { if (nState >= 0 && this.aaOpcodeCounts.length) {
this.cInstructions++; this.cInstructions++;
var bOpcode = this.bus.getByteDirect(addr); var bOpcode = this.cpu.getByte(addr);
this.aaOpcodeCounts[bOpcode][1]++; this.aaOpcodeCounts[bOpcode][1]++;
var a = this.aOpcodeHistory[this.iOpcodeHistory]; var a = this.aOpcodeHistory[this.iOpcodeHistory];
a[0] = this.cpu.getIP(); a[0] = this.cpu.getIP();
@ -2602,8 +2602,7 @@ if (DEBUGGER) {
/** /**
* getByte(aAddr, inc) * getByte(aAddr, inc)
* *
* getByte() should be used for all Debugger memory reads (eg, doDump, doUnassemble), to ensure * We must route all our memory requests through the CPU now, in case paging is enabled.
* all notification handlers are bypassed for physical addresses.
* *
* @this {Debugger} * @this {Debugger}
* @param {Array} aAddr * @param {Array} aAddr
@ -2615,8 +2614,9 @@ if (DEBUGGER) {
var b = 0xff; var b = 0xff;
var addr = this.getAddr(aAddr, false, 1); var addr = this.getAddr(aAddr, false, 1);
if (addr !== X86.ADDR_INVALID) { if (addr !== X86.ADDR_INVALID) {
b = this.bus.getByteDirect(addr); this.nSuppress++;
this.assert((b == (b & 0xff)), "invalid byte (" + b + ") at address: " + this.hexAddr(aAddr)); b = this.cpu.getByte(addr);
this.nSuppress--;
if (inc !== undefined) this.incAddr(aAddr, inc); if (inc !== undefined) this.incAddr(aAddr, inc);
} }
return b; return b;
@ -2651,8 +2651,9 @@ if (DEBUGGER) {
var w = 0xffff; var w = 0xffff;
var addr = this.getAddr(aAddr, false, 2); var addr = this.getAddr(aAddr, false, 2);
if (addr !== X86.ADDR_INVALID) { if (addr !== X86.ADDR_INVALID) {
w = this.bus.getShortDirect(addr); this.nSuppress++;
this.assert((w == (w & 0xffff)), "invalid word (" + w + ") at address: " + this.hexAddr(aAddr)); w = this.cpu.getShort(addr);
this.nSuppress--;
if (inc !== undefined) this.incAddr(aAddr, inc); if (inc !== undefined) this.incAddr(aAddr, inc);
} }
return w; return w;
@ -2671,7 +2672,9 @@ if (DEBUGGER) {
var l = -1; var l = -1;
var addr = this.getAddr(aAddr, false, 4); var addr = this.getAddr(aAddr, false, 4);
if (addr !== X86.ADDR_INVALID) { 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); if (inc !== undefined) this.incAddr(aAddr, inc);
} }
return l; return l;
@ -2680,10 +2683,6 @@ if (DEBUGGER) {
/** /**
* setByte(aAddr, b, inc) * 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} * @this {Debugger}
* @param {Array} aAddr * @param {Array} aAddr
* @param {number} b * @param {number} b
@ -2693,7 +2692,9 @@ if (DEBUGGER) {
{ {
var addr = this.getAddr(aAddr, true, 1); var addr = this.getAddr(aAddr, true, 1);
if (addr !== X86.ADDR_INVALID) { 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); if (inc !== undefined) this.incAddr(aAddr, inc);
this.cpu.updateCPU(); this.cpu.updateCPU();
} }
@ -2711,7 +2712,9 @@ if (DEBUGGER) {
{ {
var addr = this.getAddr(aAddr, true, 2); var addr = this.getAddr(aAddr, true, 2);
if (addr !== X86.ADDR_INVALID) { 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); if (inc !== undefined) this.incAddr(aAddr, inc);
this.cpu.updateCPU(); this.cpu.updateCPU();
} }
@ -2738,6 +2741,7 @@ if (DEBUGGER) {
} }
} }
this.aBreakWrite = ["write"]; this.aBreakWrite = ["write"];
this.nSuppress = 0;
}; };
/** /**
@ -2921,6 +2925,8 @@ if (DEBUGGER) {
*/ */
Debugger.prototype.checkBreakpoint = function(addr, aBreak, fTemp) 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 * 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. * 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) Debugger.prototype.parseAddr = function(sAddr, type)
{ {
var aAddr;
var aAddrNext = (type == Debugger.ADDR_DATA? this.aAddrNextData : this.aAddrNextCode); var aAddrNext = (type == Debugger.ADDR_DATA? this.aAddrNextData : this.aAddrNextCode);
var off = aAddrNext[0], seg = aAddrNext[1], addr = aAddrNext[2]; var off = aAddrNext[0], seg = aAddrNext[1], addr = aAddrNext[2];
if (sAddr !== undefined) { if (sAddr !== undefined) {
@ -3549,7 +3555,7 @@ if (DEBUGGER) {
addr = 0; addr = 0;
} }
var aAddr = this.findSymbolAddr(sAddr); aAddr = this.findSymbolAddr(sAddr);
if (aAddr && aAddr.length) return aAddr; if (aAddr && aAddr.length) return aAddr;
var iColon = sAddr.indexOf(":"); var iColon = sAddr.indexOf(":");
@ -3567,7 +3573,8 @@ if (DEBUGGER) {
addr = null; addr = null;
} }
} }
var aAddr = [off, seg, addr];
aAddr = [off, seg, addr];
this.checkLimit(aAddr); this.checkLimit(aAddr);
return aAddr; return aAddr;
}; };

View file

@ -3643,7 +3643,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
X86.fnFaultMessage = function(nFault, nError, fHalt) X86.fnFaultMessage = function(nFault, nError, fHalt)
{ {
var bitsMessage = Messages.FAULT; 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 * OS/2 1.0 uses an INT3 (0xCC) opcode in conjunction with an invalid IDT to trigger a triple-fault

View file

@ -605,6 +605,13 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
var fGate, regPSMask, nFaultError, regSP; var fGate, regPSMask, nFaultError, regSP;
var rpl = sel & X86.SEL.RPL; var rpl = sel & X86.SEL.RPL;
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; 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 * 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) { else if (this.id == X86Seg.ID.DATA) {
if (selMasked) { 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: * 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) { 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); if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = X86.ADDR_INVALID; base = X86.ADDR_INVALID;
break; break;

View file

@ -14,14 +14,32 @@ DPL2 equ 2
DPL3 equ 3 DPL3 equ 3
ACC_TYPE_SEG equ 0x1000 ACC_TYPE_SEG equ 0x1000
ACC_PRESENT equ 0x8000
ACC_TYPE_CODE equ 0x0800 ACC_TYPE_CODE equ 0x0800
ACC_TYPE_READABLE equ 0x0200 ACC_TYPE_READABLE equ 0x0200
ACC_TYPE_WRITABLE equ 0x0200 ACC_TYPE_WRITABLE equ 0x0200
ACC_TYPE_CODE_READABLE equ 0x1a00 ACC_TYPE_CODE_READABLE equ (0x1a00 | ACC_PRESENT)
ACC_TYPE_DATA_WRITABLE equ 0x1200 ACC_TYPE_DATA_WRITABLE equ (0x1200 | ACC_PRESENT)
EXT_BIG equ 0x0040 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_REAL equ 0xf000
CSEG_PROT equ 0x0008 CSEG_PROT equ 0x0008
DSEG_PROT equ 0x0010 DSEG_PROT equ 0x0010
@ -29,22 +47,77 @@ DSEG_PROT equ 0x0010
CR0_MSW_PE equ 0x0001 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 (%2 & 0x0000ffff)
dw (%1 & 0x0000ffff) dw (%1 & 0x0000ffff)
dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13) dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13)
dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16) dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16)
%endmacro %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 mov ecx,0x88776655
mul ecx mul ecx
div 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 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 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) 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 defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE
romGDTEnd: 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 mov eax,cr0
or eax,CR0_MSW_PE or eax,CR0_MSW_PE
mov cr0,eax mov cr0,eax