New Debugger command to dump Bus memory allocations

This commit is contained in:
Jeff Parsons 2015-04-08 09:45:13 -07:00 committed by jeffpar
commit fa88aa6a2d
9 changed files with 316 additions and 72 deletions

View file

@ -273,6 +273,7 @@ Bus.prototype.initMemory = function()
for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) {
var addr = iBlock * this.blockSize;
var block = this.aMemBlocks[iBlock] = new Memory(addr);
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
}
this.cpu.initMemory(this.aMemBlocks, this.blockShift, this.blockLimit, this.blockMask);
@ -597,6 +598,7 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
block = aBlocks[i++];
} else {
block = new Memory(addr);
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
block.clone(aBlocks[i++], type);
}
this.aMemBlocks[iBlock++] = block;

View file

@ -41,6 +41,7 @@ if (DEBUGGER) {
var Interrupts = require("./interrupts");
var Messages = require("./messages");
var Bus = require("./bus");
var Memory = require("./memory");
var Keyboard = require("./keyboard");
var State = require("./state");
var CPU = require("./cpu");
@ -487,6 +488,7 @@ if (DEBUGGER) {
"tss": Messages.TSS,
"int": Messages.INT,
"fault": Messages.FAULT,
"bus": Messages.BUS,
"mem": Messages.MEM,
"port": Messages.PORT,
"dma": Messages.DMA,
@ -560,11 +562,12 @@ if (DEBUGGER) {
* On the 80286, it introduced a new (and growing) series of two-byte opcodes
*
* Based on the active CPU model, we make every effort to execute and disassemble this (and every other)
* opcode appropriately, by setting the opcode's entry in aaOpDescs accordingly. 0x0F defaults to the 8086
* entry: aOpDescPopCS.
* opcode appropriately, by setting the opcode's entry in aaOpDescs accordingly. 0x0F in aaOpDescs points
* to the 8086 table: aOpDescPopCS.
*
* Note that we do NOT modify aaOpDescs directly; this.aaOpDescs is a reference to it if the processor
* is an 8086, otherwise we make a copy of the array and THEN modify it.
* Note that we must NOT modify aaOpDescs directly. this.aaOpDescs will point to Debugger.aaOpDescs
* if the processor is an 8086, because that's the processor that the hard-coded contents of the table
* represent; for all other processors, this.aaOpDescs will contain a copy of the table that we can modify.
*/
Debugger.aOpDescPopCS = [Debugger.INS.POP, Debugger.TYPE_CS | Debugger.TYPE_OUT];
Debugger.aOpDescUndefined = [Debugger.INS.NONE, Debugger.TYPE_NONE];
@ -575,13 +578,13 @@ if (DEBUGGER) {
* the corresponding opcode. The sub-elements are as follows:
*
* [0]: {number} of the opcode name (see INS.*)
* [1]: {number} containing the destination operand descriptor bit(s)
* [2]: {number} containing the source operand descriptor bit(s)
* [3]: {number} containing optional third operand descriptor bit(s)
* [1]: {number} containing the destination operand descriptor bit(s), if any
* [2]: {number} containing the source operand descriptor bit(s), if any
* [3]: {number} containing the occasional third operand descriptor bit(s), if any
*
* These sub-elements are all optional. If [0] is not present, the opcode is undefined; if [1] is not
* present (or contains zero), the opcode has no (or only implied) operands; and if [2] is not present,
* the opcode has only a single operand.
* present (or contains zero), the opcode has no (or only implied) operands; if [2] is not present, the
* opcode has only a single operand. And so on.
*/
Debugger.aaOpDescs = [
/* 0x00 */ [Debugger.INS.ADD, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_REG | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
@ -1216,6 +1219,7 @@ if (DEBUGGER) {
}
}
this.messageDump(Messages.BUS, function onDumpBus(s) { dbg.dumpBus(s); });
this.messageDump(Messages.DESC, function onDumpDesc(s) { dbg.dumpDesc(s); });
this.messageDump(Messages.TSS, function onDumpTSS(s) { dbg.dumpTSS(s); });
this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); });
@ -1464,6 +1468,25 @@ if (DEBUGGER) {
"TASK_LDT": 0x2a
};
/**
* dumpBus(s)
*
* This dumps Bus allocations.
*
* @this {Debugger}
* @param {string} [s]
*/
Debugger.prototype.dumpBus = function(s)
{
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];
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]);
}
};
/**
* dumpDesc(s)
*
@ -2703,21 +2726,6 @@ if (DEBUGGER) {
Debugger.prototype.addBreakpoint = function(aBreak, aAddr, fTemp)
{
if (!this.findBreakpoint(aBreak, aAddr)) {
/*
* We used to calculate the physical address of the breakpoint address now, at the
* time the breakpoint is added, and save it in aAddr[2], so that a breakpoint set in
* one mode (eg, in real-mode) would still work as intended if the mode changed later
* (eg, to protected-mode).
*
* However, that creates difficulties setting protected-mode breakpoints in segments
* that might not be defined yet, or that may move in physical memory; so we're not
* doing this anymore:
*
* aAddr[2] = this.getAddr(aAddr);
*
* The way to create a real-mode breakpoint that will break regardless of mode is to
* use the physical address of the real-mode memory location instead.
*/
aAddr[3] = fTemp;
aBreak.push(aAddr);
if (aBreak != this.aBreakExec) {
@ -2884,7 +2892,28 @@ if (DEBUGGER) {
var fBreak = false;
addr = this.mapBreakpoint(addr);
for (var i = 1; i < aBreak.length; i++) {
var aAddrBreak = aBreak[i];
/*
* We need to zap the physical address field of the breakpoint address before
* calling getAddr(), to force it to recalculate the physical address every time,
* unless this is a breakpoint on a physical address (as indicated by a -1 offset).
*/
if (aAddrBreak[0] != -1) aAddrBreak[2] = null;
/*
* We used to calculate the physical address of the breakpoint at the time the
* breakpoint was added, so that a breakpoint set in one mode (eg, in real-mode)
* would still work as intended if the mode changed later (eg, to protected-mode).
*
* However, that created difficulties setting protected-mode breakpoints in segments
* that might not be defined yet, or that could move in physical memory.
*
* If you want to create a real-mode breakpoint that will break regardless of mode,
* use the physical address of the real-mode memory location instead.
*/
if (addr == this.mapBreakpoint(this.getAddr(aAddrBreak))) {
if (aAddrBreak[3]) {
this.findBreakpoint(aBreak, aAddrBreak, true);

View file

@ -113,6 +113,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
function Memory(addr, used, size, type, controller)
{
var i;
this.id = (Memory.idBlock += 2);
this.adw = null;
this.offset = 0;
this.addr = addr;
@ -223,6 +224,11 @@ Memory.TYPE = {
COLORS: ["black", "blue", "green", "cyan"]
};
/*
* Last used block ID
*/
Memory.idBlock = 0;
Memory.prototype = {
constructor: Memory,
parent: null,
@ -237,6 +243,12 @@ Memory.prototype = {
* @param {number} [type]
*/
clone: function(mem, type) {
/*
* Original memory block IDs are even; cloned memory block IDs are odd;
* the original ID of the current block is lost, but that's OK, since it was
* presumably produced merely to become a clone.
*/
this.id = mem.id | 0x1;
this.size = mem.size;
if (type) {
this.type = type;

View file

@ -55,31 +55,32 @@ var Messages = {
TSS: 0x00000008,
INT: 0x00000010,
FAULT: 0x00000020,
MEM: 0x00000040,
PORT: 0x00000080,
DMA: 0x00000100,
PIC: 0x00000200,
TIMER: 0x00000400,
CMOS: 0x00000800,
RTC: 0x00001000,
C8042: 0x00002000,
CHIPSET: 0x00004000,
KEYBOARD: 0x00008000,
KEYS: 0x00010000,
VIDEO: 0x00020000,
FDC: 0x00040000,
HDC: 0x00080000,
DISK: 0x00100000,
SERIAL: 0x00200000,
SPEAKER: 0x00400000,
STATE: 0x00800000,
MOUSE: 0x01000000,
COMPUTER: 0x02000000,
DOS: 0x04000000,
DATA: 0x08000000,
LOG: 0x10000000,
WARN: 0x20000000,
HALT: 0x40000000
BUS: 0x00000040,
MEM: 0x00000080,
PORT: 0x00000100,
DMA: 0x00000200,
PIC: 0x00000400,
TIMER: 0x00000800,
CMOS: 0x00001000,
RTC: 0x00002000,
C8042: 0x00004000,
CHIPSET: 0x00008000,
KEYBOARD: 0x00010000,
KEYS: 0x00020000,
VIDEO: 0x00040000,
FDC: 0x00080000,
HDC: 0x00100000,
DISK: 0x00200000,
SERIAL: 0x00400000,
SPEAKER: 0x00800000,
STATE: 0x01000000,
MOUSE: 0x02000000,
COMPUTER: 0x04000000,
DOS: 0x08000000,
DATA: 0x10000000,
LOG: 0x20000000,
WARN: 0x40000000,
HALT: 0x80000000|0
};
if (typeof module !== 'undefined') module.exports = Messages;

View file

@ -330,7 +330,7 @@ CompaqController.writeByte = function writeCompaqControllerByte(off, b)
{
var aBlocks;
var controller = this.controller;
var bus = controller.bus;
var bus = controller.ram.bus;
if (b != controller.bMappings) {
if (!(b & CompaqController.MAPPINGS.UNMAPPED)) {
if (!controller.aBlocksDst) {

View file

@ -2025,8 +2025,6 @@ X86CPU.prototype.getPF = function()
/**
* getAF()
*
* Notes regarding auxiliary carry following an I386 addition:
*
* To determine if there's been a carry out of the low 4 bits of an arithmetic operation,
* we look at all the possible inputs for bit 4, and calculate AF = PS^(D^S):
*

View file

@ -1130,9 +1130,9 @@ X86.fnLFS = function LFS(dst, src)
*
* 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 ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
* limit, so we must fetch the remaining 24 bits ourselves.
* The 80286 LGDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address
* (or a 32-bit address in 32-bit mode); the ModRM decoder has already supplied the first word of the
* operand (in dst), which corresponds to the limit, so we must fetch the remaining bits ourselves.
*
* @this {X86CPU}
* @param {number} dst
@ -1144,7 +1144,11 @@ X86.fnLGDT = function LGDT(dst, src)
if (this.regEA === X86.ADDR_INVALID) {
X86.opInvalid.call(this);
} else {
this.addrGDT = this.getShort(this.regEA + 2) | (this.getByte(this.regEA + 4) << 16);
/*
* It shouldn't hurt to always fetch 32 bits of physical memory, which we'll then
* mask with either a 24-bit or a 32-bit mask.
*/
this.addrGDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
this.addrGDTLimit = this.addrGDT + dst;
this.opFlags |= X86.OPFLAG.NOWRITE;
this.nStepCycles -= 11;
@ -1176,9 +1180,9 @@ X86.fnLGS = function LGS(dst, src)
*
* 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 ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
* limit, so we must fetch the remaining 24 bits ourselves.
* The 80286 LIDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address
* (or a 32-bit address in 32-bit mode); the ModRM decoder has already supplied the first word of the
* operand (in dst), which corresponds to the limit, so we must fetch the remaining bits ourselves.
*
* @this {X86CPU}
* @param {number} dst
@ -1190,7 +1194,11 @@ X86.fnLIDT = function LIDT(dst, src)
if (this.regEA === X86.ADDR_INVALID) {
X86.opInvalid.call(this);
} else {
this.addrIDT = this.getShort(this.regEA + 2) | (this.getByte(this.regEA + 4) << 16);
/*
* It shouldn't hurt to always fetch 32 bits of physical memory, which we'll then
* mask with either a 24-bit or a 32-bit mask.
*/
this.addrIDT = this.getLong(this.regEA + 2) & (this.dataMask | (this.dataMask << 8));
this.addrIDTLimit = this.addrIDT + dst;
this.opFlags |= X86.OPFLAG.NOWRITE;
this.nStepCycles -= 12;
@ -2182,7 +2190,6 @@ X86.fnSGDT = function SGDT(dst, src)
* calls us does that automatically with the value we return (dst).
*/
dst = this.addrGDTLimit - this.addrGDT;
this.setShort(this.regEA + 2, this.addrGDT);
/*
* We previously left the 6th byte of the target operand "undefined". But it turns out we have to set
* it to *something*, because there's processor detection in PC-DOS 7.0 (at least in the SETUP portion)
@ -2211,12 +2218,10 @@ X86.fnSGDT = function SGDT(dst, src)
* 145E:4BC2 9D POPF
* 145E:4BC3 CB RETF
*
* This code is expecting SGDT on an 80286 to set the 6th "undefined" byte to 0xFF. So we use setShort()
* instead of setByte() and force the upper byte to 0xFF.
*
* TODO: Remove the 0xFF00 below on post-80286 processors; also, determine whether this behavior is unique to real-mode.
* This code is expecting SGDT on an 80286 to set the 6th "undefined" byte to 0xFF.
*/
this.setShort(this.regEA + 4, 0xFF00 | (this.addrGDT >> 16));
var addr = this.addrGDT | (this.model == X86.MODEL_80286? 0xff000000 : 0);
this.setLong(this.regEA + 2, addr);
this.nStepCycles -= 11;
}
return dst;
@ -2522,14 +2527,12 @@ X86.fnSIDT = function SIDT(dst, src)
* us does that automatically with the value we return (dst).
*/
dst = this.addrIDTLimit - this.addrIDT;
this.setShort(this.regEA + 2, this.addrIDT);
/*
* As with SGDT, the 6th byte is technically "undefined" on an 80286, but we now set it to 0xFF, for the
* same reasons discussed in SGDT (above).
*
* TODO: Remove the 0xFF00 below on post-80286 processors; also, determine whether this behavior is unique to real-mode.
*/
this.setShort(this.regEA + 4, 0xFF00 | (this.addrIDT >> 16));
var addr = this.addrIDT | (this.model == X86.MODEL_80286? 0xff000000 : 0);
this.setLong(this.regEA + 2, addr);
this.nStepCycles -= 12;
}
return dst;

View file

@ -2079,9 +2079,22 @@ X86.opMOVwsr = function MOVwsr()
case 0x3:
this.regMD16 = this.segDS.sel;
break;
case 0x4:
if (I386 && this.model >= X86.MODEL_80386) {
this.regMD16 = this.segFS.sel;
break;
}
X86.opInvalid.call(this);
break;
case 0x5:
if (I386 && this.model >= X86.MODEL_80386) {
this.regMD16 = this.segGS.sel;
break;
}
/* falls through */
default:
X86.opUndefined.call(this);
return;
X86.opInvalid.call(this);
break;
}
/*
* Like other MOV operations, the destination does not need to be read, just written.