Fixed the Debugger opcode history buffer (was broken by DbgAddr changes)

This commit is contained in:
Jeff Parsons 2015-05-09 07:52:03 -07:00 committed by jeffpar
commit ac862f3b1d
3 changed files with 78 additions and 70 deletions

View file

@ -51,29 +51,23 @@ if (DEBUGGER) {
}
/**
* DbgAddr object definition
* Debugger Address Object
*
* off: offset, if any
* sel: selector, if any
* addr: linear address, if any
* fData32: true if 32-bit operand size in effect
* fAddr32: true if 32-bit address size in effect
* fOverride: true if any overrides were processed with this address
* fComplete: true if a complete instruction was processed with this address
* fTempBreak: true if temporary breakpoint address
* NOTE: I originally tried to define DbgAddr as a record typedef, which allowed me to reference the
* type as {DbgAddr} instead of {{DbgAddr}}, but my IDE (WebStorm) did not recognize all instances of {DbgAddr}.
* Using this @class definition is a bit cleaner, and it makes both WebStorm and the Closure Compiler happier,
* at the expense of making all references {{DbgAddr}}. Defining a typedef based on this class doesn't help.
*
* @typedef {{
* off: (number|null|undefined),
* sel: (number|null|undefined),
* addr: (number|null|undefined),
* fData32: (boolean|undefined),
* fAddr32: (boolean|undefined),
* fOverride: (boolean|undefined),
* fComplete: (boolean|undefined),
* fTempBreak: (boolean|undefined)
* }}
* @class DbgAddr
* @property {number|null|undefined} off (offset, if any)
* @property {number|null|undefined} sel (selector, if any)
* @property {number|null|undefined} addr (linear address, if any)
* @property {boolean|undefined} fData32 (true if 32-bit operand size in effect)
* @property {boolean|undefined} fAddr32 (true if 32-bit address size in effect)
* @property {boolean|undefined} fOverride (true if any overrides were processed with this address)
* @property {boolean|undefined} fComplete (true if a complete instruction was processed with this address)
* @property {boolean|undefined} fTempBreak (true if this is a temporary breakpoint address)
*/
var DbgAddr;
/**
* Debugger(parmsDbg)
@ -1418,7 +1412,7 @@ if (DEBUGGER) {
* getAddr(dbgAddr, fWrite, cb)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fWrite]
* @param {number} [cb] is number of bytes to check (1, 2 or 4); default is 1
* @return {number} is the corresponding linear address, or X86.ADDR_INVALID
@ -1450,7 +1444,7 @@ if (DEBUGGER) {
* We must route all our memory requests through the CPU now, in case paging is enabled.
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} [inc]
* @return {number}
*/
@ -1471,7 +1465,7 @@ if (DEBUGGER) {
* getWord(dbgAddr, fAdvance)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fAdvance]
* @return {number}
*/
@ -1487,7 +1481,7 @@ if (DEBUGGER) {
* getShort(dbgAddr, inc)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} [inc]
* @return {number}
*/
@ -1508,7 +1502,7 @@ if (DEBUGGER) {
* getLong(dbgAddr, inc)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} [inc]
* @return {number}
*/
@ -1529,7 +1523,7 @@ if (DEBUGGER) {
* setByte(dbgAddr, b, inc)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} b
* @param {number} [inc]
*/
@ -1549,7 +1543,7 @@ if (DEBUGGER) {
* setShort(dbgAddr, w, inc)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} w
* @param {number} [inc]
*/
@ -1569,25 +1563,25 @@ if (DEBUGGER) {
* newAddr(off, sel, addr, fData32, fAddr32)
*
* @this {Debugger}
* @param {number|null|undefined} [off]
* @param {number|null|undefined} [sel]
* @param {number|null|undefined} [addr]
* @param {boolean} [fData32]
* @param {boolean} [fAddr32]
* @return {DbgAddr}
* @param {number|null|undefined} [off] (default is zero)
* @param {number|null|undefined} [sel] (default is null)
* @param {number|null|undefined} [addr] (default is zero)
* @param {boolean} [fData32] (default is false)
* @param {boolean} [fAddr32] (default is false)
* @return {{DbgAddr}}
*/
Debugger.prototype.newAddr = function(off, sel, addr, fData32, fAddr32)
{
if (fData32 === undefined) fData32 = (this.cpu && this.cpu.segCS.dataSize == 4);
if (fAddr32 === undefined) fAddr32 = (this.cpu && this.cpu.segCS.addrSize == 4);
return {off: off, sel: sel, addr: addr, fTempBreak: false, fData32: fData32 || false, fAddr32: fAddr32 || false};
return {off: off || 0, sel: sel || null, addr: addr || 0, fTempBreak: false, fData32: fData32 || false, fAddr32: fAddr32 || false};
};
/**
* packAddr(dbgAddr)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {Array}
*/
Debugger.prototype.packAddr = function(dbgAddr)
@ -1600,7 +1594,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {Array} aAddr
* @return {DbgAddr}
* @return {{DbgAddr}}
*/
Debugger.prototype.unpackAddr = function(aAddr)
{
@ -1611,7 +1605,7 @@ if (DEBUGGER) {
* checkLimit(dbgAddr)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
*/
Debugger.prototype.checkLimit = function(dbgAddr)
{
@ -1628,7 +1622,7 @@ if (DEBUGGER) {
* incAddr(dbgAddr, inc)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number|undefined} inc contains value to increment dbgAddr by (default is 1)
*/
Debugger.prototype.incAddr = function(dbgAddr, inc)
@ -1664,7 +1658,7 @@ if (DEBUGGER) {
* hexAddr(dbgAddr)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {string} the hex representation of the address
*/
Debugger.prototype.hexAddr = function(dbgAddr)
@ -1678,7 +1672,7 @@ if (DEBUGGER) {
* Dump helper for zero-terminated strings.
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {number} [cchMax]
* @return {string} (and dbgAddr advanced past the terminating zero)
*/
@ -2311,7 +2305,7 @@ if (DEBUGGER) {
* Preallocate dummy Addr (Array) objects in every history slot, so that
* checkInstruction() doesn't need to call newAddr() on every slot update.
*/
this.aOpcodeHistory[i] = [0, null, 0];
this.aOpcodeHistory[i] = this.newAddr();
}
this.iOpcodeHistory = 0;
}
@ -2690,10 +2684,10 @@ if (DEBUGGER) {
var bOpcode = this.cpu.probeAddr(addr);
if (bOpcode != null) {
this.aaOpcodeCounts[bOpcode][1]++;
var a = this.aOpcodeHistory[this.iOpcodeHistory];
a[0] = this.cpu.getIP();
a[1] = this.cpu.getCS();
a[2] = addr;
var dbgAddr = this.aOpcodeHistory[this.iOpcodeHistory];
dbgAddr.off = this.cpu.getIP();
dbgAddr.sel = this.cpu.getCS();
dbgAddr.addr = addr;
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
}
}
@ -2807,7 +2801,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {Array} aBreak
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fTempBreak]
* @return {boolean} true if breakpoint added, false if already exists
*/
@ -2841,7 +2835,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {Array} aBreak
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fRemove]
* @return {boolean} true if found, false if not
*/
@ -2921,7 +2915,7 @@ if (DEBUGGER) {
* setTempBreakpoint(dbgAddr)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr of new temp breakpoint
* @param {{DbgAddr}} dbgAddr of new temp breakpoint
*/
Debugger.prototype.setTempBreakpoint = function(dbgAddr)
{
@ -3030,7 +3024,7 @@ if (DEBUGGER) {
* getInstruction(dbgAddr, sComment, nSequence)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {string} [sComment] is an associated comment
* @param {number} [nSequence] is an associated sequence number, undefined if none
* @return {string} (and dbgAddr is updated to the next instruction)
@ -3191,7 +3185,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {number} type
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {string} operand
*/
Debugger.prototype.getImmOperand = function(type, dbgAddr)
@ -3238,7 +3232,7 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {number} bReg
* @param {number} type
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {string} operand
*/
Debugger.prototype.getRegOperand = function(bReg, type, dbgAddr)
@ -3271,7 +3265,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {number} bMod
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {string} operand
*/
Debugger.prototype.getSIBOperand = function(bMod, dbgAddr)
@ -3298,7 +3292,7 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {number} bModRM
* @param {number} type
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @return {string} operand
*/
Debugger.prototype.getModRMOperand = function(bModRM, type, dbgAddr)
@ -3391,7 +3385,7 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {string} sOp
* @param {string|undefined} sOperand
* @param {DbgAddr} dbgAddr of memory where this instruction is being assembled
* @param {{DbgAddr}} dbgAddr of memory where this instruction is being assembled
* @return {Array.<number>} of opcode bytes; if the instruction can't be parsed, the array will be empty
*/
Debugger.prototype.parseInstruction = function(sOp, sOperand, dbgAddr)
@ -3606,7 +3600,7 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {string|undefined} sAddr
* @param {number|undefined} type is the address segment type, in case sAddr doesn't specify a segment
* @return {DbgAddr}
* @return {{DbgAddr}}
*/
Debugger.prototype.parseAddr = function(sAddr, type)
{
@ -3821,7 +3815,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {string} sSymbol
* @return {DbgAddr|null} a valid dbgAddr if a valid symbol, an empty dbgAddr if an unknown symbol, or null if not a symbol
* @return {{DbgAddr}|null} a valid dbgAddr if a valid symbol, an empty dbgAddr if an unknown symbol, or null if not a symbol
*/
Debugger.prototype.findSymbolAddr = function(sSymbol)
{
@ -3869,7 +3863,7 @@ if (DEBUGGER) {
* entries: [0]-[3] will refer to closest preceding symbol, and [4]-[7] will refer to the closest subsequent symbol.
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} [fNearest]
* @return {Array|null} where [0] == symbol name, [1] == symbol value, [2] == any annotation, and [3] == any associated comment
*/
@ -5137,7 +5131,7 @@ if (DEBUGGER) {
* initAddrSize(dbgAddr, fNonPrefix)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {{DbgAddr}} dbgAddr
* @param {boolean} fNonPrefix
*/
Debugger.prototype.initAddrSize = function(dbgAddr, fNonPrefix)

View file

@ -38,6 +38,19 @@ if (typeof module !== 'undefined') {
var X86 = require("./x86");
}
/**
* @class X86Seg
* @property {number} sel
* @property {number} limit (in protected-mode, this comes from descriptor word 0x0)
* @property {number} base (in protected-mode, this comes from descriptor word 0x2)
* @property {number} acc (in protected-mode, this comes from descriptor word 0x4, masked with 0xff00; bits 0-7 supplement base bits 16-23)
* @property {number} ext (in protected-mode, this is descriptor word 0x6, 80386 only; supplements limit bits 16-19 and base bits 24-31)
*
* TODO: Determine what good, if any, these class annotations are for either an IDE like WebStorm or a tool like
* the Closure Compiler. More importantly, what good do they do at runtime? Is it better to simply ensure that all
* object properties are explicitly initialized in the constructor, and document them there instead?
*/
/**
* X86Seg(cpu, sName)
*
@ -54,10 +67,10 @@ function X86Seg(cpu, id, sName, fProt)
this.id = id;
this.sName = sName || "";
this.sel = 0;
this.limit = 0xffff; // in protected-mode, this is descriptor word 0x0
this.base = 0; // in protected-mode, this is descriptor word 0x2
this.acc = this.type = 0; // in protected-mode, this is descriptor word 0x4, masked with 0xff00 (bits 0-7 supplement base bits 16-23)
this.ext = 0; // in protected-mode, this is descriptor word 0x6 (80386 only; supplements limit bits 16-19 and base bits 24-31)
this.limit = 0xffff;
this.base = 0;
this.acc = this.type = 0;
this.ext = 0;
this.cpl = this.dpl = 0;
this.addrDesc = X86.ADDR_INVALID;
this.dataSize = this.addrSize = 2;
@ -108,7 +121,7 @@ X86Seg.ID = {
* @this {X86Seg}
* @param {number} sel
* @param {boolean} [fSuppress] is true to suppress any errors
* @return {number} base address of selected segment, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} base address of selected segment, or ADDR_INVALID if error (TODO: No error conditions yet)
*/
X86Seg.prototype.loadReal = function loadReal(sel, fSuppress)
{
@ -122,7 +135,7 @@ X86Seg.prototype.loadReal = function loadReal(sel, fSuppress)
* this.dataSize = this.addrSize = 2;
* this.dataMask = this.addrMask = 0xffff;
*
* Only the selector and base portions of a segment register can be changed in real-mode.
* In short, only the selector and base portions of a segment register can be changed in real-mode.
*/
return this.base = this.sel << 4;
};
@ -209,7 +222,7 @@ X86Seg.prototype.loadProt = function loadProt(sel, fSuppress)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number} address from selected vector, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} address from selected vector, or ADDR_INVALID if error (TODO: No error conditions yet)
*/
X86Seg.prototype.loadIDTReal = function loadIDTReal(nIDT)
{
@ -238,7 +251,7 @@ X86Seg.prototype.loadIDTReal = function loadIDTReal(nIDT)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number} address from selected vector, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} address from selected vector, or ADDR_INVALID if error (TODO: No error conditions yet)
*/
X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
{
@ -264,7 +277,7 @@ X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
* @param {number} off is a segment-relative offset
* @param {number} cb is number of bytes to check (1, 2 or 4)
* @param {boolean} [fSuppress] is true to suppress any errors
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions yet)
*/
X86Seg.prototype.checkReadReal = function checkReadReal(off, cb, fSuppress)
{
@ -281,7 +294,7 @@ X86Seg.prototype.checkReadReal = function checkReadReal(off, cb, fSuppress)
* @param {number} off is a segment-relative offset
* @param {number} cb is number of bytes to check (1, 2 or 4)
* @param {boolean} [fSuppress] is true to suppress any errors
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions exist yet)
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions yet)
*/
X86Seg.prototype.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
{

View file

@ -52,8 +52,8 @@ PAGING equ 0
; 0x0d10-0x0d13 RAM_RETF (Real-mode return address)
; 0x0d14-0x0fff reserved
;
; And in the second page (0x1000-0x1fff), we might build a page directory, followed by a single page table that
; allows us to map up to 4Mb (although we'd likely only create PTEs for the first 1Mb).
; And in the second page (0x1000-0x1fff), we might build a page directory, followed by a single page table
; that allows us to map up to 4Mb (although we'd likely only create PTEs for the first 1Mb).
;
;RAM_GDT equ 0x0c00
;RAM_IDTR equ 0x0d00
@ -243,6 +243,7 @@ initPages:
; Build a page directory at EDI with only 1 valid PDE (the first one)
;
cld
cli ; make sure interrupts are still off (in case any DOS calls turned them back on)
mov eax,edi
add eax,0x1000 ; EAX == page frame address (of the next page)
or eax,PTE_USER | PTE_READWRITE | PTE_PRESENT