Some improvements to Debugger message support

This commit is contained in:
Jeff Parsons 2014-10-15 16:14:54 -07:00 committed by jeffpar
commit ba5f2fe41b
15 changed files with 1429 additions and 1285 deletions

View file

@ -237,7 +237,8 @@ function ChipSet(parmsChipSet)
/*
* I used to defer ChipSet's reset() to powerUp(), which then gave us the option of doing either
* reset() OR restore(), instead of both. However, on MODEL_5170 machines, the initial CMOS data
* needs to be created earlier, so that if/when the HDC calls setCMOSDriveType(), we'll be ready.
* needs to be created earlier, so that when other components are initializing their state (eg, when
* HDC calls setCMOSDriveType() or RAM calls addCMOSMemory()), the CMOS will be ready to take their calls.
*/
this.reset();
@ -597,11 +598,11 @@ ChipSet.PPI_SW = {
SHIFT: 6
},
COPROC: 0x02, // MODEL_5150: reserved; MODEL_5160: coprocessor installed
MEMORY: {
X1: 0x00, // MODEL_5150: "X" is 16Kb; MODEL_5160: "X" is 64Kb
X2: 0x04,
X3: 0x08,
X4: 0x0C,
MEMORY: { // MODEL_5150: "X" is 16Kb; MODEL_5160: "X" is 64Kb
X1: 0x00, // 16Kb or 64Kb
X2: 0x04, // 32Kb or 128Kb
X3: 0x08, // 48Kb or 192Kb
X4: 0x0C, // 64Kb or 256Kb
MASK: 0x0C,
SHIFT: 2
},
@ -864,7 +865,7 @@ ChipSet.COPROC = { // TODO: Define a variable for this
* ChipSet-related BIOS interrupts, functions, and other parameters
*/
ChipSet.BIOS = {
RTC_INT: 0x1A
INT_RTC: 0x1A
};
/**
@ -949,12 +950,12 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
{
chipset.dumpTimer();
});
dbg.messageDump(ChipSet.MESSAGE_CHIPSET, function onDumpCMOS()
dbg.messageDump(ChipSet.MESSAGE_CMOS, function onDumpCMOS()
{
chipset.dumpCMOS();
});
}
cpu.addInterruptNotify(ChipSet.BIOS.RTC_INT, this, this.intBIOSRTC);
cpu.addIntNotify(ChipSet.BIOS.INT_RTC, this, this.intBIOSRTC);
}
};
@ -970,7 +971,7 @@ ChipSet.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data) {
this.reset();
this.reset(true);
} else {
if (!this.restore(data)) return false;
}
@ -991,11 +992,12 @@ ChipSet.prototype.powerDown = function(fSave)
};
/**
* reset()
* reset(fSoft)
*
* @this {ChipSet}
* @param {boolean} [fSoft] is true if "soft" reset, otherwise "hard" reset (see below for details)
*/
ChipSet.prototype.reset = function()
ChipSet.prototype.reset = function(fSoft)
{
/*
* We propagate the sw1Init/sw2Init values to sw1/sw2 at reset; the user is only
@ -1070,13 +1072,12 @@ ChipSet.prototype.reset = function()
this.bCMOSAddr = 0; // NMI is enabled, since the ChipSet.CMOS.ADDR.NMI_DISABLE bit is not set in bCMOSAddr
/*
* Now that we call reset() from the ChipSet constructor, enabling other components can to update
* their CMOS information, we must not allow a reset() from powerUp() to toss that information, so
* we allocate abCMOSData only if it hasn't already been allocated.
* Now that we call reset() from the ChipSet constructor, enabling other components to update
* their own CMOS information as needed, we must distinguish between the initial ("hard") reset
* and any later ("soft") resets (eg, from powerUp() calls), and make sure the latter preserves
* existing CMOS information.
*/
if (!this.abCMOSData) {
this.abCMOSData = new Array(ChipSet.CMOS.ADDR.TOTAL);
}
if (!fSoft) this.abCMOSData = new Array(ChipSet.CMOS.ADDR.TOTAL);
this.initRTCDate(this.sRTCDate);
@ -2458,7 +2459,7 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
var addr = (channel.bPage << 16) | (channel.addrCurrent[1] << 8) | channel.addrCurrent[0];
if (DEBUG && DEBUGGER && channel.sAddrDebug === null) {
channel.sAddrDebug = str.toHex(addr >> 4, 4) + ":" + str.toHex(addr & 0xf, 4);
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_DMA | (iDMAChannel == ChipSet.DMA_FDC? this.dbg.MESSAGE_FDC : (iDMAChannel == ChipSet.DMA_HDC? this.dbg.MESSAGE_HDC : this.dbg.MESSAGE_LOG))) && channel.xfer != ChipSet.DMA_MODE.XFER_WRITE) {
if (this.dbg && this.dbg.messageEnabled(ChipSet.MESSAGE_DMA | (iDMAChannel == ChipSet.DMA_FDC? ChipSet.MESSAGE_FDC : (iDMAChannel == ChipSet.DMA_HDC? ChipSet.MESSAGE_HDC : ChipSet.MESSAGE_LOG))) && channel.xfer != ChipSet.DMA_MODE.XFER_WRITE) {
this.dbg.message("advanceDMA(" + iDMAChannel + ") transferring " + channel.cbDebug + " bytes from " + channel.sAddrDebug);
this.dbg.doDump("db", channel.sAddrDebug, "l" + Math.floor((channel.cbDebug + 15) / 16));
}
@ -2568,7 +2569,7 @@ ChipSet.prototype.updateDMA = function(channel)
channel.component = channel.obj = null;
}
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_DMA | (iDMAChannel == ChipSet.DMA_FDC? this.dbg.MESSAGE_FDC : (iDMAChannel == ChipSet.DMA_HDC? this.dbg.MESSAGE_HDC : this.dbg.MESSAGE_LOG))) && channel.xfer == ChipSet.DMA_MODE.XFER_WRITE && channel.sAddrDebug) {
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(ChipSet.MESSAGE_DMA | (iDMAChannel == ChipSet.DMA_FDC? ChipSet.MESSAGE_FDC : (iDMAChannel == ChipSet.DMA_HDC? ChipSet.MESSAGE_HDC : ChipSet.MESSAGE_LOG))) && channel.xfer == ChipSet.DMA_MODE.XFER_WRITE && channel.sAddrDebug) {
this.dbg.message("updateDMA(" + iDMAChannel + ") transferred " + channel.cbDebug + " bytes to " + channel.sAddrDebug);
this.dbg.doDump("db", channel.sAddrDebug, "l" + Math.floor((channel.cbDebug + 15) / 16));
}
@ -3438,7 +3439,7 @@ ChipSet.prototype.updateTimer = function(iTimer, fCycleReset)
}
}
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_TIMER)) {
if (DEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(ChipSet.MESSAGE_TIMER)) {
this.log("TIMER" + iTimer + " count: " + count + ", ticks: " + ticks + ", fired: " + (fFired? "true" : "false"));
}
@ -3602,7 +3603,7 @@ ChipSet.prototype.inPPIC = function(port, addrFrom)
* The ROM BIOS polls this port incessantly during its memory tests, checking for memory parity errors
* (which of course we never report), so we further restrict these port messages to MESSAGE_MEM.
*/
this.messagePort(port, null, addrFrom, "PPI_C", ChipSet.MESSAGE_MEM | ChipSet.MESSAGE_CHIPSET, b);
this.messagePort(port, null, addrFrom, "PPI_C", ChipSet.MESSAGE_CHIPSET | ChipSet.MESSAGE_MEM, b);
return b;
};
@ -3660,7 +3661,7 @@ ChipSet.prototype.outPPICtrl = function(port, bOut, addrFrom)
ChipSet.prototype.in8042OutBuff = function(port, addrFrom)
{
var b = this.b8042OutBuff;
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, b);
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_8042, b);
this.b8042Status &= ~(ChipSet.KBC.STATUS.OUTBUFF_FULL | ChipSet.KBC.STATUS.OUTBUFF_DELAY);
var bNext = this.kbd && this.kbd.readScanCode(true);
if (bNext) this.set8042OutBuff(bNext);
@ -3681,7 +3682,7 @@ ChipSet.prototype.in8042OutBuff = function(port, addrFrom)
*/
ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
{
this.messagePort(port, bOut, addrFrom, "8042_INBUF.DATA", ChipSet.MESSAGE_CHIPSET);
this.messagePort(port, bOut, addrFrom, "8042_INBUF.DATA", ChipSet.MESSAGE_8042);
if (this.b8042Status & ChipSet.KBC.STATUS.CMD_FLAG) {
switch (this.b8042InBuff) {
@ -3790,29 +3791,24 @@ ChipSet.prototype.in8042RWReg = function(port, addrFrom)
*
* However, the MODEL_5170_REV3 BIOS not only checks REFRESH_BIT in "TEST.09", but includes
* an additional test right before "TEST.11A", which requires the bit change "a bit less"
* frequently.
* frequently. This new test sets CX to zero, and at the end of the test (@F000:05B8), CX
* must be in the narrow range of 0xF600 through 0xF9FD.
*
* QUESTION: Did IBM throw in this additional REFRESH_BIT test in an attempt to either tie
* their revised BIOS to their own hardware OR to insure that the processor was running at a
* "condoned" speed? Note that this new test sets CX to zero, and at the end of the test
* (@F000:05B8), CX must be in the narrow range of 0xF600 through 0xF9FD.
* In fact, the new "WAITF" function @F000:1A3A tells us exactly how frequently REFRESH_BIT
* is expected to change now. That function performs a "FIXED TIME WAIT", where CX is a
* "COUNT OF 15.085737us INTERVALS TO WAIT".
*
* So now we tie the state of the REFRESH_BIT to bit 6 of the current CPU cycle count,
* effectively toggling the bit after every 64 cycles, or roughly every 4th read, and yielding
* a count of 0xF815 in CX, safely within the required range. I also confirmed that using
* the next highest bit (bit 7) created too much of a delay (CX was 0xF015).
*
* NOTE: the "WAITF" function @F000:1A3A relies on REFRESH_BIT to achieve a "FIXED TIME WAIT",
* where CX is a "COUNT OF 15.085737us INTERVALS TO WAIT". By toggling REFRESH_BIT every 64
* cycles, on an 8Mhz CPU that can do 8 cycles in 1us, 64 cycles represents 8us, so this might
* be 7us too fast? But I think we're close enough.
* So we now tie the state of the REFRESH_BIT to bit 6 of the current CPU cycle count,
* effectively toggling the bit after every 64 cycles. On an 8Mhz CPU that can do 8 cycles
* in 1us, 64 cycles represents 8us, so that might be a bit fast for "WAITF", but bit 6
* is the only choice that also satisfies the pre-"TEST.11A" test as well.
*/
var b = this.bPPIB & ~(ChipSet.KBC.RWREG.PARITY_ERR | ChipSet.KBC.RWREG.REFRESH_BIT) | ((this.cpu.getCycles() & 0x40)? ChipSet.KBC.RWREG.REFRESH_BIT : 0);
/*
* Thanks to the WAITF function, this has become a very "busy" port, so let's not generate messages
* unless both MESSAGE_CHIPSET *and* MESSAGE_LOG are set.
* unless both MESSAGE_8042 *and* MESSAGE_LOG are set.
*/
this.messagePort(port, null, addrFrom, "8042_RWREG", ChipSet.MESSAGE_CHIPSET | ChipSet.MESSAGE_LOG, b);
this.messagePort(port, null, addrFrom, "8042_RWREG", ChipSet.MESSAGE_8042 | ChipSet.MESSAGE_LOG, b);
return b;
};
@ -3826,7 +3822,7 @@ ChipSet.prototype.in8042RWReg = function(port, addrFrom)
*/
ChipSet.prototype.out8042RWReg = function(port, bOut, addrFrom)
{
this.messagePort(port, bOut, addrFrom, "8042_RWREG", ChipSet.MESSAGE_CHIPSET);
this.messagePort(port, bOut, addrFrom, "8042_RWREG", ChipSet.MESSAGE_8042);
this.updatePPIB(bOut);
};
@ -3840,7 +3836,7 @@ ChipSet.prototype.out8042RWReg = function(port, bOut, addrFrom)
*/
ChipSet.prototype.in8042Status = function(port, addrFrom)
{
this.messagePort(port, null, addrFrom, "8042_STATUS", ChipSet.MESSAGE_CHIPSET, this.b8042Status);
this.messagePort(port, null, addrFrom, "8042_STATUS", ChipSet.MESSAGE_8042, this.b8042Status);
var b = this.b8042Status & 0xff;
/*
* There's code in the 5170 BIOS (F000:03BF) that writes an 8042 command (0xAA), waits for
@ -3878,7 +3874,7 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
*/
ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
{
this.messagePort(port, bOut, addrFrom, "8042_INBUFF.CMD", ChipSet.MESSAGE_CHIPSET);
this.messagePort(port, bOut, addrFrom, "8042_INBUFF.CMD", ChipSet.MESSAGE_8042);
Component.assert(!(this.b8042Status & ChipSet.KBC.STATUS.INBUFF_FULL));
this.b8042InBuff = bOut;
@ -4034,7 +4030,7 @@ ChipSet.prototype.set8042OutPort = function(b)
*/
ChipSet.prototype.inCMOSAddr = function(port, addrFrom)
{
this.messagePort(port, null, addrFrom, "CMOS_ADDR", ChipSet.MESSAGE_CHIPSET, this.bCMOSAddr);
this.messagePort(port, null, addrFrom, "CMOS_ADDR", ChipSet.MESSAGE_CMOS, this.bCMOSAddr);
return this.bCMOSAddr;
};
@ -4048,7 +4044,7 @@ ChipSet.prototype.inCMOSAddr = function(port, addrFrom)
*/
ChipSet.prototype.outCMOSAddr = function(port, bOut, addrFrom)
{
this.messagePort(port, bOut, addrFrom, "CMOS_ADDR", ChipSet.MESSAGE_CHIPSET);
this.messagePort(port, bOut, addrFrom, "CMOS_ADDR", ChipSet.MESSAGE_CMOS);
this.bCMOSAddr = bOut;
this.bNMI = (bOut & ChipSet.CMOS.ADDR.NMI_DISABLE)? ChipSet.NMI.DISABLE : ChipSet.NMI.ENABLE;
};
@ -4065,7 +4061,7 @@ ChipSet.prototype.inCMOSData = function(port, addrFrom)
{
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
var bIn = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.getRTCByte(bAddr) : this.abCMOSData[bAddr]);
this.messagePort(port, null, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CHIPSET, bIn);
this.messagePort(port, null, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CMOS, bIn);
return bIn;
};
@ -4080,7 +4076,7 @@ ChipSet.prototype.inCMOSData = function(port, addrFrom)
ChipSet.prototype.outCMOSData = function(port, bOut, addrFrom)
{
var bAddr = this.bCMOSAddr & ChipSet.CMOS.ADDR.MASK;
this.messagePort(port, bOut, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CHIPSET);
this.messagePort(port, bOut, addrFrom, "CMOS_DATA[" + str.toHexByte(bAddr) + "]", ChipSet.MESSAGE_CMOS);
this.abCMOSData[bAddr] = (bAddr <= ChipSet.CMOS.ADDR.RTC_STATUSD? this.setRTCByte(bAddr, bOut) : bOut);
};
@ -4152,22 +4148,22 @@ ChipSet.prototype.intBIOSRTC = function(addr)
{
if (DEBUGGER) {
var AH = this.cpu.regAX >> 8;
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_CHIPSET)) {
this.dbg.message("ChipSet.intBIOSRTC(AH=" + str.toHexByte(AH) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
this.cpu.addInterruptReturn(addr, function(chipset, nCycles) {
if (this.dbg && this.dbg.messageEnabled(ChipSet.MESSAGE_RTC)) {
this.dbg.messageInt(ChipSet.BIOS.INT_RTC, addr);
this.cpu.addIntReturn(addr, function(chipset, nCycles) {
return function onBIOSRTCReturn(nLevel) {
nCycles = chipset.cpu.getCycles() - nCycles;
var sResult = "C=" + (chipset.cpu.getCF()? 1 : 0);
var sResult;
var CL = chipset.cpu.regDX & 0xff;
var CH = chipset.cpu.regDX >> 8;
var DL = chipset.cpu.regDX & 0xff;
var DH = chipset.cpu.regDX >> 8;
if (AH == 0x02 || AH == 0x03) {
sResult += " CH(hour)=" + str.toHexWord(CH) + " CL(min)=" + str.toHexByte(CL) + " DH(sec)=" + str.toHexByte(DH);
sResult = " CH(hour)=" + str.toHexWord(CH) + " CL(min)=" + str.toHexByte(CL) + " DH(sec)=" + str.toHexByte(DH);
} else if (AH == 0x04 || AH == 0x05) {
sResult += " CX(year)=" + str.toHexWord(chipset.cpu.regCX) + " DH(month)=" + str.toHexByte(DH) + " DL(day)=" + str.toHexByte(DL);
sResult = " CX(year)=" + str.toHexWord(chipset.cpu.regCX) + " DH(month)=" + str.toHexByte(DH) + " DL(day)=" + str.toHexByte(DL);
}
chipset.messageDebugger("ChipSet.intBIOSRTC(" + nLevel + "): " + sResult + " (cycles=" + nCycles + ")");
chipset.dbg.messageIntReturn(ChipSet.BIOS.INT_RTC, nLevel, nCycles, sResult);
};
}(this, this.cpu.getCycles()));
}

View file

@ -1,5 +1,5 @@
/**
* @fileoverview Implements the PCjs Debugger component.
* @fileoverview Implements the PCjs Debugger component.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* @suppress {missingProperties}
@ -138,56 +138,21 @@ function Debugger(parmsDbg)
* This ensures that, by default, the CPU runs as fast as possible.
*/
this.initHistory();
/*
* Initialize Debugger message support
*/
this.messageInit(this, parmsDbg['messages'], true);
/*
* Message categories supported by the messageEnabled() function and other assorted message
* functions. Each category has a corresponding bit value that can be combined (ie, OR'ed) as
* needed. The Debugger's message command ("m") is used to turn message categories on and off,
* like so:
*
* m port on
* m port off
* ...
*
* Every caller of messageInit() receives all the MESSAGE_* properties as bit values; for example,
* after ChipSet calls messageInit(ChipSet), ChipSet.MESSAGE_MEM will be 0x0001, and so on.
*
* We also call messageInit() on behalf the current Debugger instance so that other components have
* the option of accessing the properties indirectly (eg, this.dbg.MESSAGE_MEM), since the Debugger
* component is not a required component.
*
* WARNING: The order of these categories can be rearranged, alphabetized, etc, as long as the bit
* values aren't changed; otherwise, you risk breaking saved Debugger states (not a huge concern,
* just something to be aware of).
* This object is filled in by updateRegValues() whenever we need a fresh snapshot.
*/
this.msgCategories = {
MESSAGE_MEM: {bit: 0x00000001, op: "mem"},
MESSAGE_PORT: {bit: 0x00000002, op: "port"},
MESSAGE_DMA: {bit: 0x00000004, op: "dma"},
MESSAGE_PIC: {bit: 0x00000008, op: "pic"},
MESSAGE_TIMER: {bit: 0x00000010, op: "timer"},
MESSAGE_KBD: {bit: 0x00000020, op: "keyboard"},
MESSAGE_VIDEO: {bit: 0x00000040, op: "video"},
MESSAGE_FDC: {bit: 0x00000080, op: "fdc"},
MESSAGE_HDC: {bit: 0x00000100, op: "hdc"},
MESSAGE_DISK: {bit: 0x00000200, op: "disk"},
MESSAGE_SERIAL: {bit: 0x00000400, op: "serial"},
MESSAGE_SPEAKER:{bit: 0x00000800, op: "speaker"},
MESSAGE_CHIPSET:{bit: 0x00001000, op: "chipset"}, // ie, ChipSet ports (PPI, NMI, etc)
MESSAGE_STATE: {bit: 0x00002000, op: "state"},
MESSAGE_MOUSE: {bit: 0x00004000, op: "mouse"},
MESSAGE_CMP: {bit: 0x00008000, op: "computer"},
MESSAGE_DOS: {bit: 0x00010000, op: "dos"},
MESSAGE_LOG: {bit: 0x00020000, op: "log"},
/*
* Now we're into message actions rather than message types; for example, turning "halt"
* on or off doesn't enable "halt" messages, but rather halts the CPU on any other message.
*/
MESSAGE_HALT: {bit: 0x00040000, op: "halt"}
this.aRegValues = {
"AL":0, "CL":0, "DL":0, "BL":0, "AH":0, "CH":0, "DH":0, "BH":0,
"AX":0, "CX":0, "DX":0, "BX":0, "SP":0, "BP":0, "SI":0, "DI":0,
"ES":0, "CS":0, "SS":0, "DS":0, "IP":0
};
this.messageInit(this, parmsDbg['messages']);
/*
* The instruction trace buffer is a lightweight logging mechanism with minimal impact
* on the browser (unlike printing to either console.log or an HTML control, which can
@ -207,27 +172,6 @@ function Debugger(parmsDbg)
if (DEBUGGER) {
/**
* @class Debugger
* @property {number} MESSAGE_MEM
* @property {number} MESSAGE_PORT
* @property {number} MESSAGE_DMA
* @property {number} MESSAGE_PIC
* @property {number} MESSAGE_TIMER
* @property {number} MESSAGE_KBD
* @property {number} MESSAGE_VIDEO
* @property {number} MESSAGE_FDC
* @property {number} MESSAGE_HDC
* @property {number} MESSAGE_DISK
* @property {number} MESSAGE_SERIAL
* @property {number} MESSAGE_SPEAKER
* @property {number} MESSAGE_CHIPSET
* @property {number} MESSAGE_STATE
* @property {number} MESSAGE_MOUSE
* @property {number} MESSAGE_LOG
* @property {number} MESSAGE_DOS
*/
Component.subclass(Component, Debugger);
Debugger.aCommands = {
@ -469,7 +413,87 @@ if (DEBUGGER) {
Debugger.TYPE_186 = (Debugger.CPU_186 << 14);
Debugger.TYPE_286 = (Debugger.CPU_286 << 14);
Debugger.TYPE_386 = (Debugger.CPU_386 << 14);
/**
* @class Debugger
* @property {Object} MESSAGES
* @property {number} MESSAGE_MEM
* @property {number} MESSAGE_PORT
* @property {number} MESSAGE_DMA
* @property {number} MESSAGE_PIC
* @property {number} MESSAGE_TIMER
* @property {number} MESSAGE_CMOS
* @property {number} MESSAGE_RTC
* @property {number} MESSAGE_8042
* @property {number} MESSAGE_CHIPSET
* @property {number} MESSAGE_KBD
* @property {number} MESSAGE_VIDEO
* @property {number} MESSAGE_FDC
* @property {number} MESSAGE_HDC
* @property {number} MESSAGE_DISK
* @property {number} MESSAGE_SERIAL
* @property {number} MESSAGE_SPEAKER
* @property {number} MESSAGE_STATE
* @property {number} MESSAGE_MOUSE
* @property {number} MESSAGE_CMP
* @property {number} MESSAGE_CPU
* @property {number} MESSAGE_DOS
* @property {number} MESSAGE_INT
* @property {number} MESSAGE_LOG
*/
/*
* Message categories supported by the messageEnabled() function and other assorted message
* functions. Each category has a corresponding bit value that can be combined (ie, OR'ed) as
* needed. The Debugger's message command ("m") is used to turn message categories on and off,
* like so:
*
* m port on
* m port off
* ...
*
* Every caller of messageInit() receives all the MESSAGE_* properties as bit values; for example,
* after ChipSet calls messageInit(ChipSet), ChipSet.MESSAGE_MEM will be 0x0001, and so on.
*
* We also call messageInit() on behalf the current Debugger instance so that other components have
* the option of accessing the properties indirectly (eg, this.dbg.MESSAGE_MEM), since the Debugger
* component is not a required component.
*
* NOTE: The order of these categories can be rearranged, alphabetized, etc, as desired; just be
* aware that changing the bit values could break saved Debugger states (not a huge concern, just
* something to be aware of).
*/
Debugger.MESSAGES = {
MESSAGE_MEM: {BIT: 0x00000001, OP: "mem"},
MESSAGE_PORT: {BIT: 0x00000002, OP: "port"},
MESSAGE_DMA: {BIT: 0x00000004, OP: "dma"},
MESSAGE_PIC: {BIT: 0x00000008, OP: "pic"},
MESSAGE_TIMER: {BIT: 0x00000010, OP: "timer"},
MESSAGE_CMOS: {BIT: 0x00000020, OP: "cmos"},
MESSAGE_RTC: {BIT: 0x00000040, OP: "rtc"},
MESSAGE_8042: {BIT: 0x00000080, OP: "8042"},
MESSAGE_CHIPSET:{BIT: 0x00000100, OP: "chipset"}, // ie, anything else in ChipSet besides DMA, PIC, TIMER, CMOS, RTC and 8042
MESSAGE_KBD: {BIT: 0x00000200, OP: "keyboard"},
MESSAGE_VIDEO: {BIT: 0x00000400, OP: "video"},
MESSAGE_FDC: {BIT: 0x00000800, OP: "fdc"},
MESSAGE_HDC: {BIT: 0x00001000, OP: "hdc"},
MESSAGE_DISK: {BIT: 0x00002000, OP: "disk"},
MESSAGE_SERIAL: {BIT: 0x00004000, OP: "serial"},
MESSAGE_SPEAKER:{BIT: 0x00008000, OP: "speaker"},
MESSAGE_STATE: {BIT: 0x00010000, OP: "state"},
MESSAGE_MOUSE: {BIT: 0x00020000, OP: "mouse"},
MESSAGE_CMP: {BIT: 0x00040000, OP: "computer"},
MESSAGE_CPU: {BIT: 0x00080000, OP: "cpu"},
MESSAGE_DOS: {BIT: 0x00100000, OP: "dos"},
MESSAGE_INT: {BIT: 0x00200000, OP: "int"},
MESSAGE_LOG: {BIT: 0x01000000, OP: "log"},
/*
* Now we turn to message actions rather than message types; for example, setting "halt"
* on or off doesn't enable "halt" messages, but rather halts the CPU on any above message.
*/
MESSAGE_HALT: {BIT: 0x10000000, OP: "halt"}
};
/*
* Instruction trace categories supported by the traceLog() function. The Debugger's info
* command ("n") is used to turn trace categories on and off, like so:
@ -873,7 +897,7 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN]
],
[
@ -884,7 +908,7 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN]
],
[
@ -895,7 +919,7 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN]
],
[
@ -906,7 +930,7 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_ONE | Debugger.TYPE_BYTE | Debugger.TYPE_IN]
],
[
@ -917,7 +941,7 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN]
],
[
@ -928,13 +952,13 @@ if (DEBUGGER) {
[Debugger.INS.RCR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
[Debugger.INS.SHL, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
[Debugger.INS.SHR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.SAR, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN]
],
[
/* GRP3B */
[Debugger.INS.TEST, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.NOT, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH],
[Debugger.INS.NEG, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH],
[Debugger.INS.MUL, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
@ -945,7 +969,7 @@ if (DEBUGGER) {
[
/* GRP3W */
[Debugger.INS.TEST, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.NOT, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH],
[Debugger.INS.NEG, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH],
[Debugger.INS.MUL, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
@ -957,12 +981,12 @@ if (DEBUGGER) {
/* GRP4B */
[Debugger.INS.INC, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH],
[Debugger.INS.DEC, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined
],
[
/* GRP4W */
@ -973,7 +997,7 @@ if (DEBUGGER) {
[Debugger.INS.JMP, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
[Debugger.INS.JMP, Debugger.TYPE_MODRM | Debugger.TYPE_FARP | Debugger.TYPE_IN],
[Debugger.INS.PUSH, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
Debugger.aOpDescUndefined
Debugger.aOpDescUndefined
],
[ /* OP0F */ ],
[
@ -984,8 +1008,8 @@ if (DEBUGGER) {
[Debugger.INS.LTR, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
[Debugger.INS.VERR, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
[Debugger.INS.VERW, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined
],
[
/* GRP7 */
@ -994,107 +1018,136 @@ if (DEBUGGER) {
[Debugger.INS.LGDT, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
[Debugger.INS.LIDT, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
[Debugger.INS.SMSW, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_OUT],
Debugger.aOpDescUndefined,
Debugger.aOpDescUndefined,
[Debugger.INS.LMSW, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_IN],
Debugger.aOpDescUndefined
Debugger.aOpDescUndefined
]
];
/*
* DOS interrupts, for tracing DOS operations
* Information regarding interrupts of interest
*/
Debugger.DOS_INT = 0x21;
Debugger.INT_DOS = 0x21;
Debugger.aDOSFuncDesc = {
0x00: "terminate program",
0x01: "read character (AL>) from STDIN with echo",
0x02: "write character (<DL) to STDOUT",
0x03: "read character (AL>) from STDAUX", // eg, COM1
0x04: "write character (<DL) to STDAUX", // eg, COM1
0x05: "write character (<DL) to STDPRN", // eg, LPT1
0x06: "direct console output (or input if DL=FF)",
0x07: "direct console input without echo",
0x08: "read character (AL>) from STDIN without echo",
0x09: "write $-terminated string (<DS:DX) to STDOUT",
0x0A: "buffered input (DS:DX>)", // byte 0 is maximum chars, byte 1 is number of previous characters, byte 2 is number of characters read
0x0B: "get STDIN status",
0x0C: "flush buffer and read STDIN", // AL is a function # (0x01, 0x06, 0x07, 0x08, or 0x0A)
0x0D: "disk reset",
0x0E: "select default drive (<DL)", // returns # of available drives in AL
0x0F: "open file using FCB (<DS:DX)", // DS:DX -> unopened File Control Block
0x10: "close file using FCB (<DS:DX)",
0x11: "find first matching file using FCB (<DS:DX)",
0x12: "find next matching file using FCB (<DS:DX)",
0x13: "delete file using FCB (<DS:DX)",
0x14: "sequential read from file using FCB (<DS:DX)",
0x15: "sequential write to file using FCB (<DS:DX)",
0x16: "create or truncate file using FCB (<DS:DX)",
0x17: "rename file using FCB (<DS:DX)",
0x19: "get current default drive (AL>)",
0x1A: "set disk transfer area address (DTA<DS:DX)",
0x1B: "get allocation information for default drive",
0x1C: "get allocation information for specific drive (<DL)",
0x1F: "get drive parameter block for default drive",
0x21: "read random record from file using FCB (<DS:DX)",
0x22: "write random record to file using FCB (<DS:DX)",
0x23: "get file size using FCB (<DS:DX)",
0x24: "set random record number for FCB (<DS:DX)",
0x25: "set address (<DS:DX) of interrupt vector (<AL)",
0x26: "create new program segment prefix (PSP) at segment (<DX)",
0x27: "random block read from file using FCB (<DS:DX)",
0x28: "random block write to file using FCB (<DS:DX)",
0x29: "parse filename (<DS:SI) into FCB (<ES:DI) using (<AL)",
0x2A: "get system date (year>CX, mon>DH, day>DL)",
0x2B: "set system date (year<CX, mon<DH, day<DL)",
0x2C: "get system time (hour>CH, min>CL, sec>DH, hundredths>DL)",
0x2D: "set system time (hour<CH, min<CL, sec<DH, hundredths<DL)",
0x2E: "set verify flag (<AL)",
0x2F: "get disk transfer area address (DTA>ES:BX)", // DOS 2.00+
0x30: "get DOS version (major>AL, minor>AH)",
0x31: "terminate and stay resident",
0x32: "get drive parameter block (DPB>DS:BX) for drive (<DL)",
0x33: "extended break check",
0x34: "get address (ES:BX>) of InDOS flag",
0x35: "get address (ES:BX>) of interrupt vector (<AL)",
0x36: "get free disk space of drive (<DL)",
0x37: "get/set (AL=0/1) switch character (DL)",
0x38: "get country-specific information",
0x39: "create subdirectory (<DS:DX)",
0x3A: "remove subdirectory (<DS:DX)",
0x3B: "set current directory (<DS:DX)",
0x3C: "create or truncate file (<DS:DX) with attributes (<CX)",
0x3D: "open existing file (<DS:DX) with mode (<AL)",
0x3E: "close file (<BX)",
0x3F: "read (<CX) bytes from file (<BX) into buffer (<DS:DX)",
0x40: "write (<CX) bytes to file (<BX) from buffer (<DS:DX)",
0x41: "delete file (<DS:DX)",
0x42: "set position (<CX:DX) of file (<BX) relative to (<AL)",
0x43: "get/set (AL=0/1) attributes (CX) of file (<DS:DX)",
0x44: "get device information (IOCTL)",
0x45: "duplicate file handle (<BX)",
0x46: "force file handle (<CX) to duplicate file handle (<BX)",
0x47: "get current directory (DS:SI>) for drive (<DL)",
0x48: "allocate memory segment with (<BX) paragraphs",
0x49: "free memory segment (<ES)",
0x4A: "resize memory segment (<ES) to (<BX) paragraphs",
0x4B: "load program (<DS:DX) using parameter block (<ES:BX)",
0x4C: "terminate with return code (<AL)",
0x4D: "get return code (AL>)",
0x4E: "find first matching file (<DS:DX) with attributes (<CX)",
0x4F: "find next matching file",
0x50: "set current PSP (<BX)",
0x51: "get current PSP (BX>)",
0x52: "get system variables (ES:BX>)",
0x53: "translate BPB (<DS:SI) to DPB (ES:BP>)",
0x54: "get verify flag (AL>)",
0x55: "create child PSP at segment (<DX)",
0x56: "rename file (<DS:DX) to name (<ES:DI)",
0x57: "get/set (AL=0/1) file date (DX>) and time (CX>)",
0x58: "get/set (AL=0/1) memory allocation strategy", // DOS 2.11+
0x59: "get extended error information", // DOS 3.00+
0x5A: "create temporary file (<DS:DX) with attributes (<CX)", // DOS 3.00+
0x5B: "create file (<DS:DX) with attributes (<CX)", // DOS 3.00+ (doesn't truncate existing files like 0x3C)
0x5C: "lock/unlock (AL=0/1) file (<BX) region (<CX:DX) length (<SI:DI)" // DOS 3.00+
Debugger.INT_FUNCS = {
0x13: {
0x00: "disk reset",
0x01: "get status",
0x02: "read drive DL, cyl CH, head DH, sec CL, AL total",
0x03: "write drive DL, cyl CH, head DH, sec CL, AL total",
0x04: "verify drive DL, cyl CH, head DH, sec CL, AL total",
0x05: "format drive DL",
0x08: "read drive DL parameters",
0x15: "get drive DL DASD type",
0x16: "get drive DL change line status",
0x17: "set drive DL DASD type",
0x18: "set drive DL media type"
},
0x15: {
0x80: "open device",
0x81: "close device",
0x82: "program termination",
0x83: "wait CX:DXus for event",
0x84: "joystick support",
0x85: "SYSREQ pressed",
0x86: "wait CX:DXus",
0x87: "move block (CX words)",
0x88: "get extended memory size",
0x89: "processor to virtual mode",
0x90: "device busy loop",
0x91: "interrupt complete flag set"
},
0x21: {
0x00: "terminate program",
0x01: "read character (al) from stdin with echo",
0x02: "write character DL to stdout",
0x03: "read character (al) from stdaux", // eg, COM1
0x04: "write character DL to stdaux", // eg, COM1
0x05: "write character DL to stdprn", // eg, LPT1
0x06: "direct console output (input if DL=FF)",
0x07: "direct console input without echo",
0x08: "read character (al) from stdin without echo",
0x09: "write $-terminated string DS:DX to stdout",
0x0A: "buffered input (ds:dx)", // byte 0 is maximum chars, byte 1 is number of previous characters, byte 2 is number of characters read
0x0B: "get stdin status",
0x0C: "flush buffer and read stdin", // AL is a function # (0x01, 0x06, 0x07, 0x08, or 0x0A)
0x0D: "disk reset",
0x0E: "select default drive DL", // returns # of available drives in AL
0x0F: "open file using fcb DS:DX", // DS:DX -> unopened File Control Block
0x10: "close file using fcb DS:DX",
0x11: "find first matching file using fcb DS:DX",
0x12: "find next matching file using fcb DS:DX",
0x13: "delete file using fcb DS:DX",
0x14: "sequential read from file using fcb DS:DX",
0x15: "sequential write to file using fcb DS:DX",
0x16: "create or truncate file using fcb DS:DX",
0x17: "rename file using fcb DS:DX",
0x19: "get current default drive (al)",
0x1A: "set disk transfer area (dta) DS:DX",
0x1B: "get allocation information for default drive",
0x1C: "get allocation information for specific drive DL",
0x1F: "get drive parameter block for default drive",
0x21: "read random record from file using fcb DS:DX",
0x22: "write random record to file using fcb DS:DX",
0x23: "get file size using fcb DS:DX",
0x24: "set random record number for fcb DS:DX",
0x25: "set address DS:DX of interrupt vector AL",
0x26: "create new program segment prefix (psp) at segment DX",
0x27: "random block read from file using fcb DS:DX",
0x28: "random block write to file using fcb DS:DX",
0x29: "parse filename DS:SI into fcb ES:DI using AL",
0x2A: "get system date (year=cx, mon=dh, day=dl)",
0x2B: "set system date (year=CX, mon=DH, day=DL)",
0x2C: "get system time (hour=ch, min=cl, sec=dh, 100ths=dl)",
0x2D: "set system time (hour=CH, min=CL, sec=DH, 100ths=DL)",
0x2E: "set verify flag AL",
0x2F: "get disk transfer area address (es:bx)", // DOS 2.00+
0x30: "get DOS version (al=major, ah=minor)",
0x31: "terminate and stay resident",
0x32: "get drive parameter block (dpb=ds:bx) for drive DL",
0x33: "extended break check",
0x34: "get address (es:bx) of InDOS flag",
0x35: "get address (es:bx) of interrupt vector AL",
0x36: "get free disk space of drive DL",
0x37: "get(0)/set(1) switch character DL (AL)",
0x38: "get country-specific information",
0x39: "create subdirectory DS:DX",
0x3A: "remove subdirectory DS:DX",
0x3B: "set current directory DS:DX",
0x3C: "create or truncate file DS:DX with attributes CX",
0x3D: "open existing file DS:DX with mode AL",
0x3E: "close file BX",
0x3F: "read CX bytes from file BX into buffer DS:DX",
0x40: "write CX bytes to file BX from buffer DS:DX",
0x41: "delete file DS:DX",
0x42: "set position CX:DX of file BX relative to AL",
0x43: "get(0)/set(1) attributes CX of file DS:DX (AL)",
0x44: "get device information (IOCTL)",
0x45: "duplicate file handle BX",
0x46: "force file handle CX to duplicate file handle BX",
0x47: "get current directory (ds:si) for drive DL",
0x48: "allocate memory segment with BX paragraphs",
0x49: "free memory segment ES",
0x4A: "resize memory segment ES to BX paragraphs",
0x4B: "load program DS:DX using parameter block ES:BX",
0x4C: "terminate with return code AL",
0x4D: "get return code (al)",
0x4E: "find first matching file DS:DX with attributes CX",
0x4F: "find next matching file",
0x50: "set current psp BX",
0x51: "get current psp (bx)",
0x52: "get system variables (es:bx)",
0x53: "translate bpb DS:SI to dpb (es:bp)",
0x54: "get verify flag (al)",
0x55: "create child psp at segment DX",
0x56: "rename file DS:DX to name ES:DI",
0x57: "get(0)/set(1) file date DX and time CX (AL)",
0x58: "get(0)/set(1) memory allocation strategy (AL)", // DOS 2.11+
0x59: "get extended error information", // DOS 3.00+
0x5A: "create temporary file DS:DX with attributes CX", // DOS 3.00+
0x5B: "create file DS:DX with attributes CX", // DOS 3.00+ (doesn't truncate existing files like 0x3C)
0x5C: "lock(0)/unlock(1) file BX region CX:DX length SI:DI (AL)" // DOS 3.00+
}
};
/**
@ -1124,7 +1177,7 @@ if (DEBUGGER) {
}
}
this.cpu.addInterruptNotify(Debugger.DOS_INT, this, this.intDOSCall);
this.cpu.addIntNotify(Debugger.INT_DOS, this, this.intDOSCall);
this.setReady();
@ -1235,21 +1288,23 @@ if (DEBUGGER) {
};
/**
* messageInit(o, sEnable)
* messageInit(o, sEnable, fDebugger)
*
* @this {Debugger}
* @param {Object} o
* @param {string} [sEnable] contains zero or more message categories to enable, separated by '|' or ';'
* @param {boolean} [fDebugger] is true to perform Debugger-specific initialization (eg, array of dumpers)
*/
Debugger.prototype.messageInit = function(o, sEnable)
Debugger.prototype.messageInit = function(o, sEnable, fDebugger)
{
if (fDebugger) this.afnDumpers = [];
var bitsEnable = 0;
var aEnable = this.parseCommand(sEnable);
for (var m in this.msgCategories) {
o[m] = this.msgCategories[m].bit;
if (aEnable.indexOf(this.msgCategories[m].op) >= 0) {
bitsEnable |= this.msgCategories[m].bit;
this.println(this.msgCategories[m].op + " messages enabled");
for (var m in Debugger.MESSAGES) {
o[m] = Debugger.MESSAGES[m].BIT;
if (aEnable.indexOf(Debugger.MESSAGES[m].OP) >= 0) {
bitsEnable |= Debugger.MESSAGES[m].BIT;
this.println(Debugger.MESSAGES[m].OP + " messages enabled");
}
}
if (this.bitsMessageEnabled === undefined) this.bitsMessageEnabled = 0;
@ -1266,9 +1321,9 @@ if (DEBUGGER) {
*/
Debugger.prototype.messageDump = function(bitMessage, fnDumper)
{
for (var m in this.msgCategories) {
if (bitMessage == this.msgCategories[m].bit) {
this.msgCategories[m].fnDumper = fnDumper;
for (var m in Debugger.MESSAGES) {
if (bitMessage == Debugger.MESSAGES[m].BIT) {
this.afnDumpers[m] = fnDumper;
return true;
}
}
@ -1290,6 +1345,70 @@ if (DEBUGGER) {
return ((this.bitsMessageEnabled & bitsMessage) === bitsMessage);
};
/**
* updateRegValues()
*
* @this {Debugger}
*/
Debugger.prototype.updateRegValues = function() {
var cpu = this.cpu;
var asRegs = Debugger.asRegs;
this.aRegValues[asRegs[0]] = str.toHexByte(cpu.regAX & 0xff);
this.aRegValues[asRegs[1]] = str.toHexByte(cpu.regCX & 0xff);
this.aRegValues[asRegs[2]] = str.toHexByte(cpu.regDX & 0xff);
this.aRegValues[asRegs[3]] = str.toHexByte(cpu.regBX & 0xff);
this.aRegValues[asRegs[4]] = str.toHexByte(cpu.regAX >> 8);
this.aRegValues[asRegs[5]] = str.toHexByte(cpu.regCX >> 8);
this.aRegValues[asRegs[6]] = str.toHexByte(cpu.regDX >> 8);
this.aRegValues[asRegs[7]] = str.toHexByte(cpu.regBX >> 8);
this.aRegValues[asRegs[8]] = str.toHexWord(cpu.regAX);
this.aRegValues[asRegs[9]] = str.toHexWord(cpu.regCX);
this.aRegValues[asRegs[10]] = str.toHexWord(cpu.regDX);
this.aRegValues[asRegs[11]] = str.toHexWord(cpu.regBX);
this.aRegValues[asRegs[12]] = str.toHexWord(cpu.regSP);
this.aRegValues[asRegs[13]] = str.toHexWord(cpu.regBP);
this.aRegValues[asRegs[14]] = str.toHexWord(cpu.regSI);
this.aRegValues[asRegs[15]] = str.toHexWord(cpu.regDI);
this.aRegValues[asRegs[16]] = str.toHexWord(cpu.segES.sel);
this.aRegValues[asRegs[17]] = str.toHexWord(cpu.segCS.sel);
this.aRegValues[asRegs[18]] = str.toHexWord(cpu.segSS.sel);
this.aRegValues[asRegs[19]] = str.toHexWord(cpu.segDS.sel);
this.aRegValues[asRegs[20]] = str.toHexWord(cpu.regIP);
};
/**
* messageInt(nInt, addr)
*
* @this {Debugger}
* @param {number} nInt
* @param {number} addr
*/
Debugger.prototype.messageInt = function(nInt, addr)
{
var AH = this.cpu.regAX >> 8;
var aFuncs = Debugger.INT_FUNCS[nInt];
var sFunc = (aFuncs && aFuncs[AH]) || "";
if (sFunc) {
this.updateRegValues();
sFunc = " " + str.replaceArray(this.aRegValues, sFunc);
}
this.message("INT 0x" + str.toHexByte(nInt) + ": AH=" + str.toHexByte(AH) + " at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel) + sFunc);
};
/**
* messageIntReturn(nInt, nLevel, nCycles)
*
* @this {Debugger}
* @param {number} nInt
* @param {number} nLevel
* @param {number} nCycles
* @param {string} [sResult]
*/
Debugger.prototype.messageIntReturn = function(nInt, nLevel, nCycles, sResult)
{
this.message("INT 0x" + str.toHexByte(nInt) + "(" + nLevel + "): C=" + (this.cpu.getCF()? 1 : 0) + (sResult || "") + " (cycles=" + nCycles + ")");
};
/**
* messageMem(component, addr, fWrite, addrFrom, name, bitsMessage)
*
@ -1306,7 +1425,7 @@ if (DEBUGGER) {
Debugger.prototype.messageMem = function(component, addr, fWrite, addrFrom, name, bitsMessage)
{
if (!bitsMessage) bitsMessage = 0;
bitsMessage |= this.msgCategories.MESSAGE_MEM.bit;
bitsMessage |= Debugger.MESSAGES.MESSAGE_MEM.BIT;
if (addrFrom == null || (this.bitsMessageEnabled & bitsMessage) == bitsMessage) {
var b = this.bus.getByteDirect(addr);
this.message(component.idComponent + "." + (fWrite? "setByte" : "getByte") + "(0x" + str.toHexAddr(addr) + ")" + (addrFrom != null? (" at " + str.toHexAddr(addrFrom)) : "") + ": " + (name? (name + "=") : "") + str.toHexByte(b));
@ -1329,7 +1448,7 @@ if (DEBUGGER) {
Debugger.prototype.messagePort = function(component, port, bOut, addrFrom, name, bitsMessage, bIn)
{
if (!bitsMessage) bitsMessage = 0;
bitsMessage |= this.msgCategories.MESSAGE_PORT.bit;
bitsMessage |= Debugger.MESSAGES.MESSAGE_PORT.BIT;
if (addrFrom == null || (this.bitsMessageEnabled & bitsMessage) == bitsMessage) {
var segFrom = null;
if (addrFrom != null) {
@ -1351,7 +1470,7 @@ if (DEBUGGER) {
this.println(sMessage); // + " (" + this.cpu.getCycles() + " cycles)"
if (this.cpu) {
if (this.bitsMessageEnabled & this.msgCategories.MESSAGE_HALT.bit) {
if (this.bitsMessageEnabled & Debugger.MESSAGES.MESSAGE_HALT.BIT) {
this.cpu.haltCPU();
}
/*
@ -1427,28 +1546,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.intDOSCall = function(addr)
{
var AH = this.cpu.regAX >> 8;
if (this.messageEnabled(this.MESSAGE_DOS)) {
/*
* TODO: Replace "<" and ">" specifiers in the DOS function descriptors with actual register value(s)
*/
var sFuncDesc = Debugger.aDOSFuncDesc[AH];
sFuncDesc = sFuncDesc? ": " + sFuncDesc.replace(/\((<|>)/g, "(") : "";
this.message("INT 0x21: AH=" + str.toHexByte(AH) + " at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel) + sFuncDesc);
/*
* Use this code to halt the CPU and/or catch the interrupt's return....
*
this.cpu.haltCPU();
this.cpu.addInterruptReturn(addr, function(dbg, nCycles) {
return function onDOSCallReturn(nLevel) {
dbg.intDOSCallReturn(nCycles, nLevel);
};
}(this, this.cpu.getCycles()));
*/
}
if (this.messageEnabled(this.MESSAGE_DOS)) this.messageInt(Debugger.INT_DOS, addr);
return true;
};
@ -1459,7 +1557,6 @@ if (DEBUGGER) {
*/
Debugger.prototype.init = function()
{
// this.doHelp();
this.println("Type ? for list of debugger commands");
};
@ -1825,7 +1922,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.checksEnabled = function(fBreak)
{
return ((DEBUG && !fBreak)? true : (this.aBreakExec.length > 1 /* || this.aBreakRead.length > 1 || this.aBreakWrite.length > 1 */));
return ((DEBUG && !fBreak)? true : (this.aBreakExec.length > 1 || this.messageEnabled(this.MESSAGE_INT) /* || this.aBreakRead.length > 1 || this.aBreakWrite.length > 1 */));
};
/**
@ -1851,31 +1948,39 @@ if (DEBUGGER) {
return true;
}
this.cInstructions++;
var bOpcode = this.bus.getByteDirect(addr);
this.aaOpcodeCounts[bOpcode][1]++;
/*
* This is a good example of what NOT to do in a high-frequency function, and defeats
* the entire purpose of preallocating and preinitializing the history array in initHistory():
*
* this.aOpcodeHistory[this.iOpcodeHistory] = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel, addr);
*
* As the name implies, newAddr() returns a new "Addr" (Array) object every time it's called.
* The rest of the instruction tracking logic can only be performed if initHistory() has allocated
* the necessary data structures; note that there is no explicit UI for enabling/disabling history,
* other than adding/removing breakpoints, simply because it's breakpoints that trigger the call to
* checkInstruction() -- well, OK, and a few other things now, like enabling MESSAGE_INT messages.
*/
var a = this.aOpcodeHistory[this.iOpcodeHistory];
a[0] = this.cpu.regIP;
a[1] = this.cpu.segCS.sel;
a[2] = addr;
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
if (this.aaOpcodeCounts.length) {
this.cInstructions++;
var bOpcode = this.bus.getByteDirect(addr);
this.aaOpcodeCounts[bOpcode][1]++;
/*
* This is a good example of what NOT to do in a high-frequency function, and defeats
* the entire purpose of preallocating and preinitializing the history array in initHistory():
*
* this.aOpcodeHistory[this.iOpcodeHistory] = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel, addr);
*
* As the name implies, newAddr() returns a new "Addr" (Array) object every time it's called.
*/
var a = this.aOpcodeHistory[this.iOpcodeHistory];
a[0] = this.cpu.regIP;
a[1] = this.cpu.segCS.sel;
a[2] = addr;
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
}
return false;
};
/**
* checkMemoryRead(addr)
*
* This "check" function is called by the CPU to inform us that a memory read occurred, giving us an
* This "check" function is called by a Memory block to inform us that a memory read occurred, giving us an
* opportunity to track the read if we want, and look for a matching "read" breakpoint, if any.
*
* @this {Debugger}
@ -1894,7 +1999,7 @@ if (DEBUGGER) {
/**
* checkMemoryWrite(addr)
*
* This "check" function is called by the CPU to inform us that a memory write occurred, giving us an
* This "check" function is called by a Memory block to inform us that a memory write occurred, giving us an
* opportunity to track the write if we want, and look for a matching "write" breakpoint, if any.
*
* @this {Debugger}
@ -1913,7 +2018,7 @@ if (DEBUGGER) {
/**
* checkPortInput(port, bIn)
*
* This "check" function is called by the CPU to inform us that port input occurred.
* This "check" function is called by the Bus component to inform us that port input occurred.
*
* @this {Debugger}
* @param {number} port
@ -1933,7 +2038,7 @@ if (DEBUGGER) {
/**
* checkPortOutput(port, bOut)
*
* This "check" function is called by the CPU to inform us that port output occurred.
* This "check" function is called by the Bus component to inform us that port output occurred.
*
* @this {Debugger}
* @param {number} port
@ -2753,7 +2858,7 @@ if (DEBUGGER) {
* done later, by getAddr(), which returns a negative result (-1) for invalid segments, out-of-range offsets,
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
* Bus.addrLimit; in the case of -1, that will generally refer to the last byte of physical memory.
* Bus.addrLimit; in the case of -1, that will generally refer to the last byte of physical address space.
*
* @this {Debugger}
* @param {string|undefined} sAddr
@ -2847,8 +2952,8 @@ if (DEBUGGER) {
value = this.cpu.segSS.sel;
break;
/*
* I used to alias "PC" to "IP", until I discovered that early (perhaps even ALL?) versions of DEBUG.COM
* treat "PC" as an alias for the 16-bit flags register. TODO: Add support for "PC".
* I used to alias "PC" to "IP", until I discovered that early (perhaps even ALL) versions of DEBUG.COM
* treat "PC" as an alias for the 16-bit flags register. TODO: Add support for "PC" as the flags register.
*/
case "IP":
value = this.cpu.regIP;
@ -3303,10 +3408,10 @@ if (DEBUGGER) {
var m;
if (sAddr == "?") {
var sDumpers = "symbols";
for (m in this.msgCategories) {
if (this.msgCategories[m].fnDumper !== undefined) {
for (m in Debugger.MESSAGES) {
if (this.afnDumpers[m]) {
if (sDumpers.length) sDumpers += ",";
sDumpers = sDumpers + this.msgCategories[m].op;
sDumpers = sDumpers + Debugger.MESSAGES[m].OP;
}
}
sDumpers += ",state";
@ -3325,9 +3430,14 @@ if (DEBUGGER) {
this.dumpSymbols();
return;
}
for (m in this.msgCategories) {
if (sAddr == this.msgCategories[m].op) {
this.msgCategories[m].fnDumper(sLen);
for (m in Debugger.MESSAGES) {
if (sAddr == Debugger.MESSAGES[m].OP) {
var fnDumper = this.afnDumpers[m];
if (fnDumper) {
fnDumper(sLen);
} else {
this.println("no dump registered for " + sAddr);
}
return;
}
}
@ -3802,9 +3912,9 @@ if (DEBUGGER) {
fCriteria = false;
sCategory = null;
} else {
for (m in this.msgCategories) {
if (sCategory == this.msgCategories[m].op) {
bitsMessage = this.msgCategories[m].bit;
for (m in Debugger.MESSAGES) {
if (sCategory == Debugger.MESSAGES[m].OP) {
bitsMessage = Debugger.MESSAGES[m].BIT;
fCriteria = !!(this.bitsMessageEnabled & bitsMessage);
break;
}
@ -3831,14 +3941,14 @@ if (DEBUGGER) {
*/
var n = 0;
var sCategories = "";
for (m in this.msgCategories) {
if (!sCategory || sCategory == this.msgCategories[m].op) {
var bitMessage = this.msgCategories[m].bit;
for (m in Debugger.MESSAGES) {
if (!sCategory || sCategory == Debugger.MESSAGES[m].OP) {
var bitMessage = Debugger.MESSAGES[m].BIT;
var fEnabled = !!(this.bitsMessageEnabled & bitMessage);
if (fCriteria !== null && fCriteria != fEnabled) continue;
if (sCategories) sCategories += ",";
if (!(++n % 10)) sCategories += "\n\t"; // jshint ignore:line
sCategories += this.msgCategories[m].op;
sCategories += Debugger.MESSAGES[m].OP;
}
}
@ -4121,13 +4231,8 @@ if (DEBUGGER) {
*/
Debugger.prototype.doProcStep = function(sCmd)
{
var fRegs = (sCmd == "pr"? 1 : 0);
/*
* TODO: Add some UI for setting fCallStep (I used to set it to !fRegs, but no one's
* going to understand why "p" steps over CALL instructions but "pr" does not; that's
* behavior that was simply convenient for code I was debugging at one time).
*/
var fCallStep = true;
var fRegs = (sCmd == "pr"? 1 : 0);
/*
* Set up the value for this.fProcStep (ie, 1 or 2) depending on whether the user wants
* a subsequent register dump ("pr") or not ("p").

View file

@ -397,8 +397,9 @@ FDC.aCmdInfo = {
/*
* FDC BIOS interrupts, functions, and other parameters
*/
FDC.BIOS = {};
FDC.BIOS.DISKETTE_INT = 0x13;
FDC.BIOS = {
INT_DISKETTE: 0x13
};
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
@ -547,7 +548,7 @@ FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addPortInputTable(this, FDC.aPortInput);
bus.addPortOutputTable(this, FDC.aPortOutput);
if (DEBUGGER) cpu.addInterruptNotify(FDC.BIOS.DISKETTE_INT, this, this.intBIOSDiskette);
if (DEBUGGER) cpu.addIntNotify(FDC.BIOS.INT_DISKETTE, this, this.intBIOSDiskette);
};
/**
@ -2224,18 +2225,12 @@ FDC.prototype.writeFormat = function(drive, b)
FDC.prototype.intBIOSDiskette = function(addr)
{
if (DEBUGGER) {
var AL = this.cpu.regAX & 0xff;
var AH = this.cpu.regAX >> 8;
var CL = this.cpu.regCX & 0xff;
var CH = this.cpu.regCX >> 8;
var DL = this.cpu.regDX & 0xff;
var DH = this.cpu.regDX >> 8;
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_FDC) && DL < 0x80) {
this.dbg.message("\nFDC.intBIOS(AH=" + str.toHexByte(AH) + ",drv=" + str.toHexByte(DL) + ",cyl=" + str.toHexByte(CH) + ",hd=" + str.toHexByte(DH) + ",sec=" + str.toHexByte(CL) + ",num=" + str.toHexByte(AL) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
this.cpu.addInterruptReturn(addr, function(fdc, nCycles) {
this.dbg.messageInt(FDC.BIOS.INT_DISKETTE, addr);
this.cpu.addIntReturn(addr, function(fdc, nCycles) {
return function onBIOSDisketteReturn(nLevel) {
nCycles = fdc.cpu.getCycles() - nCycles;
fdc.messageDebugger("FDC.intBIOS(" + nLevel + "): C=" + (fdc.cpu.getCF()? 1 : 0) + " (cycles=" + nCycles + ")");
fdc.dbg.messageIntReturn(FDC.BIOS.INT_DISKETTE, nLevel, fdc.cpu.getCycles() - nCycles);
};
}(this, this.cpu.getCycles()));
}

View file

@ -452,10 +452,13 @@ if (DEBUG) {
* in the INT 0x40 vector.
*/
HDC.BIOS = {
DISK_INT: 0x13,
DISKETTE_INT: 0x40
INT_DISK: 0x13,
INT_DISKETTE: 0x40
};
/*
* NOTE: These are useful values for reference, but they're not actually used for anything at the moment.
*/
HDC.BIOS.DISK_CMD = {
RESET: 0x00,
GET_STATUS: 0x01,
@ -524,8 +527,8 @@ HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addPortOutputTable(this, this.fATC? HDC.aATCPortOutput : HDC.aXTCPortOutput);
if (DEBUGGER) {
cpu.addInterruptNotify(HDC.BIOS.DISK_INT, this, this.intBIOSDisk);
cpu.addInterruptNotify(HDC.BIOS.DISKETTE_INT, this, this.intBIOSDiskette);
cpu.addIntNotify(HDC.BIOS.INT_DISK, this, this.intBIOSDisk);
cpu.addIntNotify(HDC.BIOS.INT_DISKETTE, this, this.intBIOSDiskette);
}
/*
@ -2578,11 +2581,10 @@ HDC.prototype.intBIOSDisk = function(addr)
if (!AH && DL > 0x80) this.iDriveAllowFail = DL - 0x80;
if (DEBUGGER) {
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_HDC) && DL >= 0x80) {
this.dbg.message("HDC.intBIOSDisk(AX=" + str.toHexWord(this.cpu.regAX) + ",DL=" + str.toHexByte(DL) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
this.cpu.addInterruptReturn(addr, function (hdc, nCycles) {
this.dbg.messageInt(HDC.BIOS.INT_DISK, addr);
this.cpu.addIntReturn(addr, function (hdc, nCycles) {
return function onBIOSDiskReturn(nLevel) {
nCycles = hdc.cpu.getCycles() - nCycles;
hdc.messageDebugger("HDC.intBIOSDisk(" + nLevel + "): C=" + (hdc.cpu.getCF()? 1 : 0) + " (cycles=" + nCycles + ")");
hdc.dbg.messageIntReturn(HDC.BIOS.INT_DISK, nLevel, hdc.cpu.getCycles() - nCycles);
};
}(this, this.cpu.getCycles()));
}

View file

@ -167,6 +167,9 @@ Memory.prototype = {
* @return {number}
*/
readNone: function(off) {
if (DEBUGGER && this.dbg.messageEnabled(this.dbg.MESSAGE_MEM) && !off) {
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr) + " from " + str.toHexAddr(this.cpu.regIP, this.cpu.segCS.sel));
}
return 0;
},
/**
@ -177,6 +180,9 @@ Memory.prototype = {
* @param {number} v (could be either a byte or word value, since we use the same handler for both kinds of accesses)
*/
writeNone: function(off, v) {
if (DEBUGGER && this.dbg.messageEnabled(this.dbg.MESSAGE_MEM) && !off) {
this.dbg.message("attempt to write 0x" + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr) + " from " + str.toHexAddr(this.cpu.regIP, this.cpu.segCS.sel));
}
},
/**
* readByteTypedArray(off)

View file

@ -1901,8 +1901,9 @@ Video.cardSpecs[Video.CARDS.EGA] = ["EGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x0
/*
* BIOS video interrupts, modes, and other parameters
*/
Video.BIOS = {};
Video.BIOS.VIDEO_INT = 0x10;
Video.BIOS = {
INT_VIDEO: 0x10
};
/**
* initBus(cmp, bus, cpu, dbg)
@ -1932,7 +1933,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
if (DEBUGGER && dbg) {
var video = this;
this.cpu.addInterruptNotify(Video.BIOS.VIDEO_INT, this, this.intBIOSVideo);
this.cpu.addIntNotify(Video.BIOS.INT_VIDEO, this, this.intBIOSVideo);
dbg.messageDump(dbg.MESSAGE_VIDEO, function onDumpVideo(sParm) {
video.dumpVideo(sParm);
});
@ -1967,11 +1968,10 @@ Video.prototype.intBIOSVideo = function(addr)
{
if (DEBUGGER) {
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO)) {
this.dbg.message("Video.intBIOS(AX=" + str.toHexWord(this.cpu.regAX) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
this.cpu.addInterruptReturn(addr, function (video, nCycles) {
this.dbg.messageInt(Video.BIOS.INT_VIDEO, addr);
this.cpu.addIntReturn(addr, function (video, nCycles) {
return function onBIOSVideoReturn(nLevel) {
nCycles = video.cpu.getCycles() - nCycles;
video.messageDebugger("Video.intBIOSReturn(" + nLevel + ") (cycles=" + nCycles + ")");
video.dbg.messageIntReturn(Video.BIOS.INT_VIDEO, nLevel, video.cpu.getCycles() - nCycles);
};
}(this, this.cpu.getCycles()));
}

View file

@ -108,7 +108,7 @@ function X86CPU(parmsCPU) {
this.initProcessor();
/*
* List of software interrupt notification functions: aInterruptNotify is an array, indexed by
* List of software interrupt notification functions: aIntNotify is an array, indexed by
* interrupt number, of 2-element sub-arrays that, in turn, contain:
*
* [0]: registered component
@ -122,22 +122,29 @@ function X86CPU(parmsCPU) {
* "INT 0x00" generated by a divide-by-zero or any other kind of interrupt (nor any interrupt simulated
* with "PUSHF/CALLF").
*
* aInterruptReturn is a hash of return address notifications set up by software interrupt notification
* aIntReturn is a hash of return address notifications set up by software interrupt notification
* functions that want to receive return notifications. A software interrupt function must call
* cpu.addInterruptReturn(fn).
* cpu.addIntReturn(fn).
*
* WARNING: There's no mechanism in place to insure that software interrupt return notifications don't
* get "orphaned" if an interrupt handler bypasses the normal return path (INT 0x24 is one example of an
* "evil" software interrupt).
*/
this.aInterruptNotify = [];
this.aInterruptReturn = [];
this.aIntNotify = [];
this.aIntReturn = [];
/*
* Since aReturnNotify is a "sparse array", this global count gives the CPU a quick way of knowing whether
* or not RETF or IRET instructions need to bother calling checkInterruptReturn().
* or not RETF or IRET instructions need to bother calling checkIntReturn().
*/
this.cInterruptReturn = 0;
this.cIntReturn = 0;
/*
* A variety of stepCPU() state variables that don't strictly need to be initialized before the first
* stepCPU() call, but it's good form to do so.
*/
this.nBurstCycles = 0;
this.fComplete = this.fDebugCheck = false;
/*
* We're just declaring aMemBlocks and associated Bus parameters here; they'll be initialized by initMemory()
@ -871,7 +878,7 @@ X86CPU.prototype.getChecksum = function()
};
/**
* addInterruptNotify(nInt, component, fn)
* addIntNotify(nInt, component, fn)
*
* Add an software interrupt notification handler to the CPU's list of such handlers.
*
@ -880,29 +887,47 @@ X86CPU.prototype.getChecksum = function()
* @param {Component} component
* @param {function(number)} fn is called with the EIP value following the software interrupt
*/
X86CPU.prototype.addInterruptNotify = function(nInt, component, fn)
X86CPU.prototype.addIntNotify = function(nInt, component, fn)
{
if (fn !== undefined) {
if (this.aInterruptNotify[nInt] === undefined)
this.aInterruptNotify[nInt] = [];
this.aInterruptNotify[nInt].push([component, fn]);
if (MAXDEBUG) this.log("addInterruptNotify(" + str.toHexWord(nInt) + "," + component.id + ")");
if (this.aIntNotify[nInt] === undefined) {
this.aIntNotify[nInt] = [];
}
this.aIntNotify[nInt].push([component, fn]);
if (MAXDEBUG) this.log("addIntNotify(" + str.toHexWord(nInt) + "," + component.id + ")");
}
};
/**
* checkInterruptNotify(nInt)
* checkIntNotify(nInt)
*
* NOTE: This is called only for "INT N" instructions, not "INT 3" or "INTO" or the "INT 0x00" generated by a
* divide-by-zero or any other kind of interrupt or simulation thereof (eg, "PUSHF/CALLF").
* NOTE: This is called ONLY for "INT N" instructions -- not "INTO" or breakpoint or single-step interrupts
* or divide exception interrupts, or hardware interrupts, or any simulation of an interrupt (eg, "PUSHF/CALLF").
*
* @this {X86CPU}
* @param {number} nInt
* @return {boolean} true if software interrupt may proceed, false if software interrupt should be skipped
*/
X86CPU.prototype.checkInterruptNotify = function(nInt)
X86CPU.prototype.checkIntNotify = function(nInt)
{
var aNotify = this.aInterruptNotify[nInt];
/*
* Enabling MESSAGE_INT messages is one of the criteria that's also included in fDebugCheck, so for maximum
* speed, check fDebugCheck first.
*/
if (DEBUGGER && this.fDebugCheck) {
/*
* TODO: Filtering out the hard-coded interrupt numbers below should be optional; this is very quick-and-dirty.
*/
if (nInt < 0x20 && nInt != 0x10 && nInt != 0x16 && nInt != 0x1C && this.dbg.messageEnabled(this.dbg.MESSAGE_INT)) {
this.dbg.messageInt(nInt, this.regEIP);
this.addIntReturn(this.regEIP, function(cpu, nCycles) {
return function onIntReturn(nLevel) {
cpu.dbg.messageIntReturn(nInt, nLevel, cpu.getCycles() - nCycles);
};
}(this, this.getCycles()));
}
}
var aNotify = this.aIntNotify[nInt];
if (aNotify !== undefined) {
for (var i = 0; i < aNotify.length; i++) {
if (!aNotify[i][1].call(aNotify[i][0], this.regEIP)) {
@ -914,7 +939,7 @@ X86CPU.prototype.checkInterruptNotify = function(nInt)
};
/**
* addInterruptReturn(addr, fn)
* addIntReturn(addr, fn)
*
* Add a return notification handler to the CPU's list of such handlers.
*
@ -929,31 +954,31 @@ X86CPU.prototype.checkInterruptNotify = function(nInt)
* @param {number} addr is a physical (non-segmented) address
* @param {function(number)} fn is an interrupt-return notification function
*/
X86CPU.prototype.addInterruptReturn = function(addr, fn)
X86CPU.prototype.addIntReturn = function(addr, fn)
{
if (fn !== undefined) {
if (this.aInterruptReturn[addr] == null) {
this.cInterruptReturn++;
if (this.aIntReturn[addr] == null) {
this.cIntReturn++;
}
this.aInterruptReturn[addr] = fn;
this.aIntReturn[addr] = fn;
}
};
/**
* checkInterruptReturn(addr)
* checkIntReturn(addr)
*
* It is expected (though not required) that callers will check cInterruptReturn and avoid calling
* It is expected (though not required) that callers will check cIntReturn and avoid calling
* this function if the count is zero.
*
* @this {X86CPU}
* @param {number} addr is a physical (non-segmented) address
*/
X86CPU.prototype.checkInterruptReturn = function(addr)
X86CPU.prototype.checkIntReturn = function(addr)
{
var fn = this.aInterruptReturn[addr];
var fn = this.aIntReturn[addr];
if (fn != null) {
fn(--this.cInterruptReturn);
delete this.aInterruptReturn[addr];
fn(--this.cIntReturn);
delete this.aIntReturn[addr];
}
};

View file

@ -443,12 +443,13 @@ var X86Help = {
* @param {number} [nError]
*/
opHelpFault: function(nFault, nError) {
if (this.dbg) {
if (DEBUGGER && this.dbg) {
/*
* By using Debugger.message(), you have the option of setting "m halt on" and halting on messages
* like this.
* NOTE: By using Debugger.message(), we have the option of setting "m halt on" and halting on messages like this.
*/
this.dbg.message("Fault 0x" + str.toHexByte(nFault) + (nError != null? " (0x" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(this.bus.getByteDirect(this.regEIP)) + " at " + str.toHexAddr(this.regIP, this.segCS.sel));
if (this.dbg.messageEnabled(this.dbg.MESSAGE_CPU)) {
this.dbg.message("Fault 0x" + str.toHexByte(nFault) + (nError != null? " (0x" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(this.bus.getByteDirect(this.regEIP)) + " at " + str.toHexAddr(this.regIP, this.segCS.sel));
}
}
if (this.model >= X86.MODEL_80186) {
this.setIP(this.opEA - this.segCS.base);

View file

@ -1288,7 +1288,7 @@ var X86OpXX = {
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
* low priority.
* low priority. TODO: Fix this someday.
*/
var nCycles = 5;
@ -1331,7 +1331,7 @@ var X86OpXX = {
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
* low priority.
* low priority. TODO: Fix this someday.
*/
var nCycles = 5;
@ -2753,7 +2753,7 @@ var X86OpXX = {
/*
* NOTE: 11 is the minimum cycle time for the 80286; the 80186/80188 has different cycle times: 15, 25 and
* 22 + 16 * (bLevel - 1) for bLevel 0, 1 and > 1, respectively. However, accurate cycle times for the 80186/80188
* is low priority.
* is low priority. TODO: Fix this someday.
*/
this.nStepCycles -= 11;
this.pushWord(this.regBP);
@ -2781,7 +2781,7 @@ var X86OpXX = {
this.regBP = this.popWord();
/*
* NOTE: 5 is the cycle time for the 80286; the 80186/80188 has a cycle time of 8. However, accurate cycle
* counts for the 80186/80188 is low priority.
* counts for the 80186/80188 is low priority. TODO: Fix this someday.
*/
this.nStepCycles -= 5;
},
@ -2794,7 +2794,7 @@ var X86OpXX = {
var n = this.getIPWord();
this.setCSIP(this.popWord(), this.popWord());
this.regSP = (this.regSP + n) & 0xffff;
if (this.cInterruptReturn) this.checkInterruptReturn(this.regEIP);
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
this.nStepCycles -= this.nOpCyclesRetFn;
},
/**
@ -2821,7 +2821,7 @@ var X86OpXX = {
*/
opINTn: function() {
var nInt = this.getIPByte();
if (this.checkInterruptNotify(nInt)) {
if (this.checkIntNotify(nInt)) {
X86Help.opHelpINT.call(this, nInt, null, 0);
return;
}
@ -2847,7 +2847,7 @@ var X86OpXX = {
opIRET: function() {
this.setCSIP(this.popWord(), this.popWord());
this.setPS(this.popWord());
if (this.cInterruptReturn) this.checkInterruptReturn(this.regEIP);
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
/*
* NOTE: I'm assuming that neither POPF nor IRET are required to set NOINTR like STI does.
*/

View file

@ -647,10 +647,10 @@ Component.prototype = {
return function printPanel(s, type) {
s = (type !== undefined? (type + ": ") : "") + (s || "");
/*
* In non-DEBUG builds, prevent the <textarea> from getting too large;
* In COMPILED builds, prevent the <textarea> from getting too large;
* otherwise, printing becomes slower and slower.
*/
if (!DEBUG) {
if (COMPILED) {
if (control.value.length > 8192) {
control.value = control.value.substr(control.value.length - 4096);
}