diff --git a/configs/pc/machines/5170/ega/1152kb/rev1/machine.xml b/configs/pc/machines/5170/ega/1152kb/rev1/machine.xml index dd20efcdf..f56486003 100644 --- a/configs/pc/machines/5170/ega/1152kb/rev1/machine.xml +++ b/configs/pc/machines/5170/ega/1152kb/rev1/machine.xml @@ -4,7 +4,7 @@ IBM PC AT, 128Kb EGA, 1152Kb RAM, 20Mb Hard Disk - + diff --git a/devices/pc/bios/5170/1985-11-15.json b/devices/pc/bios/5170/1985-11-15.json index 12b43218f..fc497aab8 100644 --- a/devices/pc/bios/5170/1985-11-15.json +++ b/devices/pc/bios/5170/1985-11-15.json @@ -1023,4 +1023,4 @@ 0,0,0,0,-822083584,4958953,216530944,-1259812051,1156107052,-773207253,53,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,14703594,791753200,942617905,-1979973579] -,"symbols":{"POST1_TEST01":{"o":80,"s":61440,"c":"TEST.01: 286 PROCESSOR TEST (REAL MODE)"},"POST1_TEST02":{"o":391,"s":61440,"c":"TEST.02: ROM CHECKSUM TEST"},"POST1_TEST03":{"o":419,"s":61440,"c":"TEST.03: VERIFY CMOS SHUTDOWN BYTE"},"POST1_TEST04":{"o":457,"s":61440,"c":"TEST.04: 8254 CHECK TIMER 1 (ALL BITS ON)"},"POST1_TEST05":{"o":503,"s":61440,"c":"TEST.05: 8254 CHECK TIMER 1 (ALL BITS OFF)"},"POST1_TEST06":{"o":541,"s":61440,"c":"TEST.06: 8237 DMA 0 INITIALIZATION"},"POST1_TEST07":{"o":607,"s":61440,"c":"TEST.07: 8237 DMA 1 INITIALIZATION"},"POST1_TEST08":{"o":724,"s":61440,"c":"TEST.08: DMA PAGE REGISTER TEST"},"POST1_TEST09":{"o":807,"s":61440,"c":"TEST.09: STORAGE REFRESH TEST"},"POST1_TEST10":{"o":829,"s":61440,"c":"TEST.10: 8042 INTERFACE TEST"},"POST1_TEST11":{"o":951,"s":61440,"c":"TEST.11: BASE 64K R/W STORAGE TEST"},"POST1_TEST11A":{"o":1494,"s":61440,"c":"TEST.11A: VERIFY GDT/IDT INSTRUCTIONS"},"POST1_TEST12":{"o":1726,"s":61440,"c":"TEST.12: VERIFY CMOS CHECKSUM"},"POST1_TEST13":{"o":1920,"s":61440,"c":"TEST.13: PROTECTED MODE TEST"},"POST1_TEST13A":{"o":2158,"s":61440,"c":"TEST.13A: MEMORY SIZE TEST (ABOVE 1024K)"},"POST1_TEST14":{"o":2713,"s":61440,"c":"TEST.14: INITIALIZE CRT CONTROLLER"},"POST1_TEST15":{"o":2829,"s":61440,"c":"TEST.15: VIDEO LINE TEST"},"POST1_TEST16":{"o":2845,"s":61440,"c":"TEST.16: CRT INTERFACE LINES TEST"},"MFG_BOOT":{"o":3109,"s":61440,"c":"MANUFACTURING BOOT TEST CODE ROUTINE"},"POST2":{"o":3220,"s":61440},"POST2_CHK_HFNUM":{"o":5372,"s":61440,"c":"CHECK FOR SECOND FIXED DISK PRESENT BUT NOT DEFINED"},"POST3":{"o":5749,"s":61440},"POST4":{"o":6469,"s":61440},"CMOS_READ":{"o":6469,"s":61440},"CMOS_WRITE":{"o":6495,"s":61440},"DDS":{"o":6521,"s":61440},"E_MSG":{"o":6529,"s":61440},"ERR_BEEP":{"o":6582,"s":61440},"BEEP":{"o":6644,"s":61440},"WAITF":{"o":6714,"s":61440,"c":"FIXED TIME WAIT (CX = COUNT OF 15.085737us INTERVALS TO WAIT)"},"CONFIG_BAD":{"o":6729,"s":61440},"XPC_BYTE":{"o":6749,"s":61440},"PRT_SEG":{"o":6772,"s":61440},"PROT_PRT":{"o":6793,"s":61440},"ROM_CHECKSUM":{"o":6837,"s":61440},"ROM_CHECK":{"o":6849,"s":61440},"KBD_RESET":{"o":6899,"s":61440},"BLINK_INT":{"o":6942,"s":61440},"SET_TOD":{"o":6956,"s":61440},"POST5":{"o":7230,"s":61440},"POST6":{"o":7867,"s":61440},"XMIT_8042":{"o":8167,"s":61440},"BOOT_STRAP_1":{"o":8232,"s":61440},"DISKETTE_IO_1":{"o":8521,"s":61440},"DISK_RESET":{"o":8656,"s":61440},"SEND_SPEC":{"o":9397,"s":61440},"SETUP_END":{"o":10462,"s":61440},"SETUP_DBL":{"o":10488,"s":61440,"c":"CHECK DOUBLE STEP"},"READ_ID":{"o":10588,"s":61440},"MOTOR_ON":{"o":10655,"s":61440},"NEC_OUTPUT":{"o":10877,"s":61440},"SEEK":{"o":10921,"s":61440},"RESULTS":{"o":11118,"s":61440},"WAIT_INT":{"o":11078,"s":61440},"DISK_INT_1":{"o":11230,"s":61440},"DSKETTE_SETUP":{"o":11253,"s":61440},"DISK_SETUP":{"o":11337,"s":61440},"DISK_IO":{"o":11762,"s":61440},"TST_RDY":{"o":12472,"s":61440,"c":"TEST HARD DISK READY (AH = 0x10)"},"NOT_BUSY":{"o":12892,"s":61440},"CHECK_ST":{"o":12971,"s":61440}}} \ No newline at end of file +,"symbols":{"POST1_TEST01":{"o":80,"s":61440,"c":"TEST.01: 286 PROCESSOR TEST (REAL MODE)"},"POST1_TEST02":{"o":391,"s":61440,"c":"TEST.02: ROM CHECKSUM TEST"},"POST1_TEST03":{"o":419,"s":61440,"c":"TEST.03: VERIFY CMOS SHUTDOWN BYTE"},"POST1_TEST04":{"o":457,"s":61440,"c":"TEST.04: 8254 CHECK TIMER 1 (ALL BITS ON)"},"POST1_TEST05":{"o":503,"s":61440,"c":"TEST.05: 8254 CHECK TIMER 1 (ALL BITS OFF)"},"POST1_TEST06":{"o":541,"s":61440,"c":"TEST.06: 8237 DMA 0 INITIALIZATION"},"POST1_TEST07":{"o":607,"s":61440,"c":"TEST.07: 8237 DMA 1 INITIALIZATION"},"POST1_TEST08":{"o":724,"s":61440,"c":"TEST.08: DMA PAGE REGISTER TEST"},"POST1_TEST09":{"o":807,"s":61440,"c":"TEST.09: STORAGE REFRESH TEST"},"POST1_TEST10":{"o":829,"s":61440,"c":"TEST.10: 8042 INTERFACE TEST"},"POST1_TEST11":{"o":951,"s":61440,"c":"TEST.11: BASE 64K R/W STORAGE TEST"},"POST1_TEST11A":{"o":1494,"s":61440,"c":"TEST.11A: VERIFY GDT/IDT INSTRUCTIONS"},"POST1_TEST12":{"o":1726,"s":61440,"c":"TEST.12: VERIFY CMOS CHECKSUM"},"POST1_TEST13":{"o":1920,"s":61440,"c":"TEST.13: PROTECTED MODE TEST"},"POST1_TEST13A":{"o":2158,"s":61440,"c":"TEST.13A: MEMORY SIZE TEST (ABOVE 1024K)"},"POST1_TEST14":{"o":2713,"s":61440,"c":"TEST.14: INITIALIZE CRT CONTROLLER"},"POST1_TEST15":{"o":2829,"s":61440,"c":"TEST.15: VIDEO LINE TEST"},"POST1_TEST16":{"o":2845,"s":61440,"c":"TEST.16: CRT INTERFACE LINES TEST"},"MFG_BOOT":{"o":3109,"s":61440,"c":"MANUFACTURING BOOT TEST CODE ROUTINE"},"POST2":{"o":3220,"s":61440},"POST2_CHK_HFNUM":{"o":5372,"s":61440,"c":"CHECK FOR SECOND FIXED DISK PRESENT BUT NOT DEFINED"},"POST3":{"o":5749,"s":61440},"POST4":{"o":6469,"s":61440},"CMOS_READ":{"o":6469,"s":61440},"CMOS_WRITE":{"o":6495,"s":61440},"DDS":{"o":6521,"s":61440},"E_MSG":{"o":6529,"s":61440},"ERR_BEEP":{"o":6582,"s":61440},"BEEP":{"o":6644,"s":61440},"WAITF":{"o":6714,"s":61440,"c":"FIXED TIME WAIT (CX = COUNT OF 15.085737us INTERVALS TO WAIT)"},"CONFIG_BAD":{"o":6729,"s":61440},"XPC_BYTE":{"o":6749,"s":61440},"PRT_SEG":{"o":6772,"s":61440},"PROT_PRT":{"o":6793,"s":61440},"ROM_CHECKSUM":{"o":6837,"s":61440},"ROM_CHECK":{"o":6849,"s":61440},"KBD_RESET":{"o":6899,"s":61440},"BLINK_INT":{"o":6942,"s":61440},"SET_TOD":{"o":6956,"s":61440},"POST5":{"o":7230,"s":61440},"POST6":{"o":7867,"s":61440},"XMIT_8042":{"o":8167,"s":61440},"BOOT_STRAP_1":{"o":8232,"s":61440},"DISKETTE_IO_1":{"o":8521,"s":61440},"DISK_RESET":{"o":8656,"s":61440},"DISK_STATUS":{"o":8741,"s":61440},"DISK_READ":{"o":8753,"s":61440},"DISK_WRITE":{"o":8765,"s":61440},"DISK_VERF":{"o":8777,"s":61440},"DISK_FORMAT":{"o":8789,"s":61440},"DISK_PARMS":{"o":8891,"s":61440},"DISK_CHANGE":{"o":9110,"s":61440,"c":"RETURNS THE STATE OF THE DISK CHANGE LINE"},"FORMAT_SET":{"o":9153,"s":61440},"DISK_TYPE":{"o":9076,"s":61440},"SET_MEDIA":{"o":9250,"s":61440},"SEND_SPEC":{"o":9397,"s":61440},"XLAT_NEW":{"o":9452,"s":61440},"XLAT_OLD":{"o":9490,"s":61440},"SETUP_END":{"o":10462,"s":61440},"SETUP_DBL":{"o":10488,"s":61440,"c":"CHECK DOUBLE STEP"},"READ_ID":{"o":10588,"s":61440},"MOTOR_ON":{"o":10655,"s":61440},"NEC_OUTPUT":{"o":10877,"s":61440},"SEEK":{"o":10921,"s":61440},"RESULTS":{"o":11118,"s":61440},"READ_DSKCHNG":{"o":11181,"s":61440},"WAIT_INT":{"o":11078,"s":61440},"DISK_INT_1":{"o":11230,"s":61440},"DSKETTE_SETUP":{"o":11253,"s":61440},"DISK_SETUP":{"o":11337,"s":61440},"DISK_IO":{"o":11762,"s":61440},"TST_RDY":{"o":12472,"s":61440,"c":"TEST HARD DISK READY (AH = 0x10)"},"NOT_BUSY":{"o":12892,"s":61440},"CHECK_ST":{"o":12971,"s":61440}}} \ No newline at end of file diff --git a/devices/pc/bios/5170/1985-11-15.map b/devices/pc/bios/5170/1985-11-15.map index 6f02931aa..b56ef0af0 100644 --- a/devices/pc/bios/5170/1985-11-15.map +++ b/devices/pc/bios/5170/1985-11-15.map @@ -67,7 +67,20 @@ 20E9 + F000:0060 @ DISKETTE_IO_1 F000:00E7 @ DISK_RESET + F000:013C @ DISK_STATUS + F000:0148 @ DISK_READ + F000:0154 @ DISK_WRITE + F000:0160 @ DISK_VERF + F000:016C @ DISK_FORMAT + F000:00E7 @ DISK_RESET + F000:01D2 @ DISK_PARMS + F000:02AD @ DISK_CHANGE ; RETURNS THE STATE OF THE DISK CHANGE LINE + F000:02D8 @ FORMAT_SET + F000:028B @ DISK_TYPE + F000:0339 @ SET_MEDIA F000:03CC @ SEND_SPEC + F000:0403 @ XLAT_NEW + F000:0429 @ XLAT_OLD F000:07F5 @ SETUP_END F000:080F @ SETUP_DBL ; CHECK DOUBLE STEP F000:0873 @ READ_ID @@ -75,6 +88,7 @@ F000:0994 @ NEC_OUTPUT F000:09C0 @ SEEK F000:0A85 @ RESULTS + F000:0AC4 @ READ_DSKCHNG F000:0A5D @ WAIT_INT F000:0AF5 @ DISK_INT_1 F000:0B0C @ DSKETTE_SETUP diff --git a/my_modules/pcjs-client/lib/chipset.js b/my_modules/pcjs-client/lib/chipset.js index e6149b361..dc596aa84 100644 --- a/my_modules/pcjs-client/lib/chipset.js +++ b/my_modules/pcjs-client/lib/chipset.js @@ -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())); } diff --git a/my_modules/pcjs-client/lib/debugger.js b/my_modules/pcjs-client/lib/debugger.js index aa3fb91a3..1b9ad874b 100644 --- a/my_modules/pcjs-client/lib/debugger.js +++ b/my_modules/pcjs-client/lib/debugger.js @@ -1,5 +1,5 @@ /** - * @fileoverview Implements the PCjs Debugger component. + * @fileoverview Implements the PCjs Debugger component. * @author Jeff Parsons * @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 () from STDAUX", // eg, COM1 - 0x04: "write character () from STDIN without echo", - 0x09: "write $-terminated string ()", // 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 ( unopened File Control Block - 0x10: "close file using FCB ()", - 0x1A: "set disk transfer area address (DTACX, mon>DH, day>DL)", - 0x2B: "set system date (yearCH, min>CL, sec>DH, hundredths>DL)", - 0x2D: "set system time (hourES: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 () of InDOS flag", - 0x35: "get address (ES:BX>) of interrupt vector () for drive ()", - 0x4E: "find first matching file ()", - 0x52: "get system variables (ES:BX>)", - 0x53: "translate BPB ()", - 0x54: "get verify flag (AL>)", - 0x55: "create child PSP at segment () 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 ( 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"). diff --git a/my_modules/pcjs-client/lib/fdc.js b/my_modules/pcjs-client/lib/fdc.js index 396e7e390..9ef41a585 100644 --- a/my_modules/pcjs-client/lib/fdc.js +++ b/my_modules/pcjs-client/lib/fdc.js @@ -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())); } diff --git a/my_modules/pcjs-client/lib/hdc.js b/my_modules/pcjs-client/lib/hdc.js index 8eb5706d6..a2dbc96ec 100644 --- a/my_modules/pcjs-client/lib/hdc.js +++ b/my_modules/pcjs-client/lib/hdc.js @@ -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())); } diff --git a/my_modules/pcjs-client/lib/mem.js b/my_modules/pcjs-client/lib/mem.js index c01a74a27..638800d51 100644 --- a/my_modules/pcjs-client/lib/mem.js +++ b/my_modules/pcjs-client/lib/mem.js @@ -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) diff --git a/my_modules/pcjs-client/lib/video.js b/my_modules/pcjs-client/lib/video.js index 8b899d243..41c15e35c 100644 --- a/my_modules/pcjs-client/lib/video.js +++ b/my_modules/pcjs-client/lib/video.js @@ -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())); } diff --git a/my_modules/pcjs-client/lib/x86cpu.js b/my_modules/pcjs-client/lib/x86cpu.js index 389ffe37d..672ce921c 100644 --- a/my_modules/pcjs-client/lib/x86cpu.js +++ b/my_modules/pcjs-client/lib/x86cpu.js @@ -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]; } }; diff --git a/my_modules/pcjs-client/lib/x86help.js b/my_modules/pcjs-client/lib/x86help.js index da035f181..06793df3f 100644 --- a/my_modules/pcjs-client/lib/x86help.js +++ b/my_modules/pcjs-client/lib/x86help.js @@ -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); diff --git a/my_modules/pcjs-client/lib/x86opxx.js b/my_modules/pcjs-client/lib/x86opxx.js index c13d11805..fff2720a5 100644 --- a/my_modules/pcjs-client/lib/x86opxx.js +++ b/my_modules/pcjs-client/lib/x86opxx.js @@ -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. */ diff --git a/my_modules/shared/lib/component.js b/my_modules/shared/lib/component.js index 4c01eb11e..3fff57e2d 100644 --- a/my_modules/shared/lib/component.js +++ b/my_modules/shared/lib/component.js @@ -647,10 +647,10 @@ Component.prototype = { return function printPanel(s, type) { s = (type !== undefined? (type + ": ") : "") + (s || ""); /* - * In non-DEBUG builds, prevent the