Updated the 5170 BIOS map, updated 8042 info, fixed some 8042 controller commands, made HLT work, and fixed the debugger's history buffer when executing a mix of real-mode and protected-mode instructions
This commit is contained in:
parent
f2627107d5
commit
93b81c47b7
13 changed files with 804 additions and 287 deletions
|
|
@ -43,6 +43,11 @@ var DumpAPI = require("../../shared/lib/dumpapi");
|
|||
/**
|
||||
* FileDump()
|
||||
*
|
||||
* TODO: Consider adding a "map" option that allows the user to supply a MAP filename (via a "map" API parameter
|
||||
* or a "--map" command-line option), which in turn triggers a call to loadMap(). Note that loadMap() will need
|
||||
* to be a bit more general and use a worker function that calls either net.getFile() or fs.readFile(), similar
|
||||
* to what our loadFile() function already does.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string|undefined} sFormat should be one of "json"|"data"|"hex"|"bytes"|"rom" (see the FORMAT constants)
|
||||
* @param {boolean|string|undefined} fComments enables comments and other readability enhancements in the JSON output
|
||||
|
|
|
|||
|
|
@ -555,75 +555,91 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
|
|||
* not clear whether that port is managed by the 8042 or independent circuitry.
|
||||
*
|
||||
* PPI_B on a MODEL_5170 is also bi-directional: at one point, the BIOS reads bit 5 (PPI_B.DISABLE_RW_MEM) to verify
|
||||
* that it's alternating (the BIOS calls that bit "REFRESH_BIT").
|
||||
* that it's alternating (the BIOS refers to it as "REFRESH_BIT").
|
||||
*
|
||||
* PPI_C and PPI_CTRL are neither documented nor used by the MODEL_5170 BIOS, so I'm assuming they're obsolete.
|
||||
*
|
||||
* NOTE: For more information on the 8042 Controller, including information on undocumented commands, refer to the
|
||||
* documents in /devices/pc/keyboard/, as well as the following websites:
|
||||
*
|
||||
* http://halicery.com/8042/8042_INTERN_TXT.htm
|
||||
* http://www.os2museum.com/wp/?p=589 ("IBM PC/AT 8042 Keyboard Controller Commands")
|
||||
*/
|
||||
ChipSet.KBD_DATA = {}; // this.b8042OutBuff
|
||||
ChipSet.KBD_DATA.PORT = 0x60;
|
||||
ChipSet.KBD_DATA = { // this.b8042OutBuff
|
||||
PORT: 0x60
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.CMD = {}; // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
|
||||
ChipSet.KBD_DATA.CMD.PC_COMPAT = 0x40; // generate IBM PC-compatible scan codes
|
||||
ChipSet.KBD_DATA.CMD.PC_MODE = 0x20;
|
||||
ChipSet.KBD_DATA.CMD.NO_CLOCK = 0x10; // disable keyboard by driving "clock" line low
|
||||
ChipSet.KBD_DATA.CMD.NO_INHIBIT = 0x08; // disable inhibit function
|
||||
ChipSet.KBD_DATA.CMD.SYS_FLAG = 0x04; // this value is propagated to ChipSet.KBD_STATUS.SYS_FLAG
|
||||
ChipSet.KBD_DATA.CMD.INT_ENABLE = 0x01; // generate an interrupt when the controller places data in the output buffer
|
||||
ChipSet.KBD_DATA.CMD = { // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
|
||||
PC_COMPAT: 0x40, // generate IBM PC-compatible scan codes
|
||||
PC_MODE: 0x20,
|
||||
NO_CLOCK: 0x10, // disable keyboard by driving "clock" line low
|
||||
NO_INHIBIT: 0x08, // disable inhibit function
|
||||
SYS_FLAG: 0x04, // this value is propagated to ChipSet.KBD_STATUS.SYS_FLAG
|
||||
INT_ENABLE: 0x01 // generate an interrupt when the controller places data in the output buffer
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.SELF_TEST = {};
|
||||
ChipSet.KBD_DATA.SELF_TEST.OK = 0x55;
|
||||
ChipSet.KBD_DATA.SELF_TEST = {
|
||||
OK: 0x55
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.INTF_TEST = {};
|
||||
ChipSet.KBD_DATA.INTF_TEST.OK = 0x00;
|
||||
ChipSet.KBD_DATA.INTF_TEST.CSLO = 0x01;
|
||||
ChipSet.KBD_DATA.INTF_TEST.CSHI = 0x02;
|
||||
ChipSet.KBD_DATA.INTF_TEST.DSLO = 0x03;
|
||||
ChipSet.KBD_DATA.INTF_TEST.DSHI = 0x04;
|
||||
ChipSet.KBD_DATA.INTF_TEST = { // result of ChipSet.KBD_CMD.INTF_TEST command (0xAB)
|
||||
OK: 0x00, // no error
|
||||
KBD_CLOCK_LO: 0x01, // keyboard clock line stuck low
|
||||
KBD_CLOCK_HI: 0x02, // keyboard clock line stuck high
|
||||
KBD_DATA_LO: 0x03, // keyboard data line stuck low
|
||||
KBD_DATA_HI: 0x04 // keyboard data line stuck high
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.INPORT = {}; // this.b8042InPort
|
||||
ChipSet.KBD_DATA.INPORT.EN256KB = 0x10; // enable 2nd 256Kb of system board RAM
|
||||
ChipSet.KBD_DATA.INPORT.MFG_OFF = 0x20; // manufacturing jumper not installed
|
||||
ChipSet.KBD_DATA.INPORT.MONO = 0x40; // monochrome monitor is primary display
|
||||
ChipSet.KBD_DATA.INPORT.KBD_ON = 0x80; // keyboard unlocked
|
||||
ChipSet.KBD_DATA.INPORT = { // this.b8042InPort
|
||||
UNDEFINED: 0x0F, // undefined
|
||||
ENABLE_256KB: 0x10, // enable 2nd 256Kb of system board RAM
|
||||
MFG_OFF: 0x20, // manufacturing jumper not installed
|
||||
MONO: 0x40, // monochrome monitor is primary display
|
||||
KBD_ON: 0x80 // keyboard not inhibited
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.OUTPORT = {}; // this.b8042OutPort
|
||||
ChipSet.KBD_DATA.OUTPORT.RESET = 0x01;
|
||||
ChipSet.KBD_DATA.OUTPORT.A20 = 0x02;
|
||||
ChipSet.KBD_DATA.OUTPORT.OBFULL = 0x10;
|
||||
ChipSet.KBD_DATA.OUTPORT.IBEMPTY= 0x20;
|
||||
ChipSet.KBD_DATA.OUTPORT.KBCLK = 0x40;
|
||||
ChipSet.KBD_DATA.OUTPORT.KBDATA = 0x80;
|
||||
ChipSet.KBD_DATA.OUTPORT = { // this.b8042OutPort
|
||||
NO_RESET: 0x01, // set by default
|
||||
A20_ON: 0x02, // set by default
|
||||
OUTBUFF_FULL: 0x10, // output buffer full
|
||||
INBUFF_EMPTY: 0x20, // input buffer empty
|
||||
KBD_CLOCK: 0x40, // keyboard clock (output)
|
||||
KBD_DATA: 0x80 // keyboard data (output)
|
||||
};
|
||||
|
||||
ChipSet.KBD_DATA.TESTPORT = {}; // generated "on the fly"
|
||||
ChipSet.KBD_DATA.TESTPORT.CLOCK = 0x01;
|
||||
ChipSet.KBD_DATA.TESTPORT.DATA = 0x02;
|
||||
ChipSet.KBD_DATA.TESTPORT = { // generated "on the fly"
|
||||
KBD_CLOCK: 0x01, // keyboard clock (input)
|
||||
KBD_DATA: 0x02 // keyboard data (input)
|
||||
};
|
||||
|
||||
ChipSet.KBD_CMD = {}; // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
|
||||
ChipSet.KBD_CMD.PORT = 0x64;
|
||||
ChipSet.KBD_CMD.READ_CMD = 0x20;
|
||||
ChipSet.KBD_CMD.WRITE_CMD = 0x60; // followed by a command byte written to KBD_DATA.PORT (see KBD_DATA.CMD)
|
||||
ChipSet.KBD_CMD.SELF_TEST = 0xAA; // self-test (KBD_DATA.SELF_TEST_OK is placed in the output buffer if no errors)
|
||||
ChipSet.KBD_CMD.INTF_TEST = 0xAB; // interface test
|
||||
ChipSet.KBD_CMD.DIAG_DUMP = 0xAC; // diagnostic dump
|
||||
ChipSet.KBD_CMD.DISABLE_KBD = 0xAD; // disable keyboard
|
||||
ChipSet.KBD_CMD.ENABLE_KBD = 0xAE; // enable keyboard
|
||||
ChipSet.KBD_CMD.READ_INPORT = 0xC0; // read input port and place data in output buffer (use only if output buffer empty)
|
||||
ChipSet.KBD_CMD.READ_OUTPORT = 0xD0; // read output port and place data in output buffer (use only if output buffer empty)
|
||||
ChipSet.KBD_CMD.WRITE_OUTPORT = 0xD1; // next byte written to KBD_DATA.PORT (port 0x60) is placed in the output port (see KBD_DATA.OUTPUT)
|
||||
ChipSet.KBD_CMD.READ_TEST = 0xE0;
|
||||
ChipSet.KBD_CMD.PULSE_OUTPORT = 0xF0; // this is the 1st of 16 commands (0xF0-0xFF) that pulse bits 0-3 of the output port
|
||||
ChipSet.KBD_CMD = { // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
|
||||
PORT: 0x64,
|
||||
READ_CMD: 0x20,
|
||||
WRITE_CMD: 0x60, // followed by a command byte written to KBD_DATA.PORT (see KBD_DATA.CMD)
|
||||
SELF_TEST: 0xAA, // self-test (KBD_DATA.SELF_TEST_OK is placed in the output buffer if no errors)
|
||||
INTF_TEST: 0xAB, // interface test
|
||||
DIAG_DUMP: 0xAC, // diagnostic dump
|
||||
DISABLE_KBD: 0xAD, // disable keyboard
|
||||
ENABLE_KBD: 0xAE, // enable keyboard
|
||||
READ_INPORT: 0xC0, // read input port and place data in output buffer (use only if output buffer empty)
|
||||
READ_OUTPORT: 0xD0, // read output port and place data in output buffer (use only if output buffer empty)
|
||||
WRITE_OUTPORT: 0xD1, // next byte written to KBD_DATA.PORT (port 0x60) is placed in the output port (see KBD_DATA.OUTPUT)
|
||||
READ_TEST: 0xE0,
|
||||
PULSE_OUTPORT: 0xF0 // this is the 1st of 16 commands (0xF0-0xFF) that pulse bits 0-3 of the output port
|
||||
};
|
||||
|
||||
ChipSet.KBD_STATUS = {}; // this.b8042Status (on read from port 0x64)
|
||||
ChipSet.KBD_STATUS.PORT = 0x64;
|
||||
ChipSet.KBD_STATUS.OUTBUFF_FULL = 0x01;
|
||||
ChipSet.KBD_STATUS.INBUFF_FULL = 0x02; // set if the controller has received but not yet read data written to the input buffer (not normally set)
|
||||
ChipSet.KBD_STATUS.SYS_FLAG = 0x04;
|
||||
ChipSet.KBD_STATUS.CMD_FLAG = 0x08; // set on write to KBD_CMD (port 0x64), clear on write to KBD_DATA (port 0x60)
|
||||
ChipSet.KBD_STATUS.NO_INHIBIT = 0x10;
|
||||
ChipSet.KBD_STATUS.XMT_TIMEOUT = 0x20;
|
||||
ChipSet.KBD_STATUS.RCV_TIMEOUT = 0x40;
|
||||
ChipSet.KBD_STATUS.PARITY_ERR = 0x80; // last byte of data received had EVEN parity (ODD parity is normally expected)
|
||||
ChipSet.KBD_STATUS.OUTBUFF_DELAY= 0x100;
|
||||
ChipSet.KBD_STATUS = { // this.b8042Status (on read from port 0x64)
|
||||
PORT: 0x64,
|
||||
OUTBUFF_FULL: 0x01,
|
||||
INBUFF_FULL: 0x02, // set if the controller has received but not yet read data written to the input buffer (not normally set)
|
||||
SYS_FLAG: 0x04,
|
||||
CMD_FLAG: 0x08, // set on write to KBD_CMD (port 0x64), clear on write to KBD_DATA (port 0x60)
|
||||
NO_INHIBIT: 0x10,
|
||||
XMT_TIMEOUT: 0x20,
|
||||
RCV_TIMEOUT: 0x40,
|
||||
PARITY_ERR: 0x80, // last byte of data received had EVEN parity (ODD parity is normally expected)
|
||||
OUTBUFF_DELAY: 0x100
|
||||
};
|
||||
|
||||
/*
|
||||
* MC146818A RTC/CMOS Ports (MODEL_5170)
|
||||
|
|
@ -632,7 +648,7 @@ ChipSet.KBD_STATUS.OUTBUFF_DELAY= 0x100;
|
|||
*
|
||||
* The ADDR port also controls NMI: write an address with bit 7 clear to enable NMI or set to disable NMI.
|
||||
*/
|
||||
ChipSet.CMOS_ADDR = {}; // this.bCMOSAddr
|
||||
ChipSet.CMOS_ADDR = {}; // this.bCMOSAddr
|
||||
ChipSet.CMOS_ADDR.PORT = 0x70;
|
||||
ChipSet.CMOS_ADDR.RTC_SEC = 0x00;
|
||||
ChipSet.CMOS_ADDR.RTC_SEC_ALRM = 0x01;
|
||||
|
|
@ -966,17 +982,17 @@ ChipSet.prototype.reset = function()
|
|||
*/
|
||||
this.b8042Status = ChipSet.KBD_STATUS.NO_INHIBIT;
|
||||
this.b8042InBuff = 0;
|
||||
this.b8042CmdData = 0;
|
||||
this.b8042CmdData = ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
this.b8042OutBuff = 0;
|
||||
|
||||
/*
|
||||
* TODO: Provide more control over these 8042 "Input Port" bits (eg, the keyboard lock)
|
||||
*/
|
||||
this.b8042InPort = ChipSet.KBD_DATA.INPORT.MFG_OFF | ChipSet.KBD_DATA.INPORT.KBD_ON;
|
||||
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.EN256KB;
|
||||
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.ENABLE_256KB;
|
||||
if (this.getSW1VideoMonitor() == ChipSet.MONITOR.MONO) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.MONO;
|
||||
|
||||
this.b8042OutPort = ChipSet.KBD_DATA.OUTPORT.A20;
|
||||
this.b8042OutPort = ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON;
|
||||
this.bCMOSAddr = 0; // NMI is enabled, since the ChipSet.CMOS_ADDR.NMI_DISABLE bit is not set in bCMOSAddr
|
||||
this.abCMOSData = new Array(ChipSet.CMOS_ADDR.TOTAL);
|
||||
this.initRTCDate(this.sRTCDate);
|
||||
|
|
@ -2563,7 +2579,7 @@ ChipSet.prototype.outPICH = function(iPIC, bOut, addrFrom)
|
|||
*/
|
||||
this.cpu.delayINTR();
|
||||
/*
|
||||
* Alas, we need an even longer delay for the MODEL_5170's "KBD_RESET" function, which must drop
|
||||
* Alas, we need a longer delay for the MODEL_5170's "KBD_RESET" function (F000:17D2), which must drop
|
||||
* into a loop and decrement CX at least once after unmasking the KBD IRQ. The "KBD_RESET" function on
|
||||
* previous models could be handled with a 4-instruction delay provided by the Keyboard.resetDevice() call
|
||||
* to setIRR(), but the MODEL_5170 needs a roughly 6-instruction delay after it unmasks the KBD IRQ.
|
||||
|
|
@ -2659,14 +2675,14 @@ ChipSet.prototype.checkIMR = function(nIRQ)
|
|||
/**
|
||||
* getIRRVector()
|
||||
*
|
||||
* getIRRVector() is called by the CPU whenever PS_IF is set and OP_NOINTR is clear. Ordinarily, an immediate response would
|
||||
* seem perfectly reasonable, but unfortunately, there are places in the ROM BIOS (eg, the "KBD_RESET" function @F000:E688)
|
||||
* that enable interrupts but still expect nothing to happen for several more instructions.
|
||||
* getIRRVector() is called by the CPU whenever PS_IF is set and OP_NOINTR is clear. Ordinarily, an immediate
|
||||
* response would seem perfectly reasonable, but unfortunately, there are places in the original ROM BIOS like
|
||||
* "KBD_RESET" (F000:E688) that enable interrupts but still expect nothing to happen for several more instructions.
|
||||
*
|
||||
* So, in addition to the two normal responses (an IDT vector #, or -1 indicating no pending interrupts), we must support
|
||||
* a third response (-2) that basically means: don't change the CPU interrupt state, just keep calling until we return one
|
||||
* of the first two responses. The number of times we delay our normal response is determined by the component that originally
|
||||
* called setIRR with an optional delay parameter.
|
||||
* So, in addition to the two normal responses (an IDT vector #, or -1 indicating no pending interrupts), we must
|
||||
* support a third response (-2) that basically means: don't change the CPU interrupt state, just keep calling until
|
||||
* we return one of the first two responses. The number of times we delay our normal response is determined by the
|
||||
* component that originally called setIRR with an optional delay parameter.
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} [iPIC]
|
||||
|
|
@ -2863,7 +2879,7 @@ ChipSet.prototype.outTimer = function(iTimer, bOut, addrFrom)
|
|||
ChipSet.prototype.inTimerCtrl = function(port, addrFrom)
|
||||
{
|
||||
this.messagePort(port, null, addrFrom, "TIMER_CTRL", ChipSet.MESSAGE_TIMER);
|
||||
if (DEBUG) this.messageDebugger("Timer[CTRL]: Read-Back command not supported (yet)", ChipSet.MESSAGE_TIMER);
|
||||
if (DEBUG) this.messageDebugger("TIMER_CTRL: Read-Back command not supported (yet)", ChipSet.MESSAGE_TIMER);
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
@ -3432,17 +3448,11 @@ ChipSet.prototype.outPPICtrl = function(port, bOut, addrFrom)
|
|||
*/
|
||||
ChipSet.prototype.in8042OutBuff = function(port, addrFrom)
|
||||
{
|
||||
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, this.b8042OutBuff);
|
||||
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
var b = this.b8042OutBuff;
|
||||
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, b);
|
||||
this.b8042Status &= ~(ChipSet.KBD_STATUS.OUTBUFF_FULL | ChipSet.KBD_STATUS.OUTBUFF_DELAY);
|
||||
var bNext = this.kbd && this.kbd.readScanCode(true);
|
||||
if (bNext) {
|
||||
this.b8042OutBuff = bNext;
|
||||
/*
|
||||
* TODO: Determine why setting OUTBUFF_DELAY instead of OUTBUFF_FULL here causes "AA 301-Keyboard Error" during POST
|
||||
*/
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
}
|
||||
if (bNext) this.set8042OutBuff(bNext);
|
||||
return b;
|
||||
};
|
||||
|
||||
|
|
@ -3472,20 +3482,7 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.WRITE_OUTPORT:
|
||||
this.b8042OutPort = bOut;
|
||||
this.bus.setA20(!!(this.b8042OutPort & ChipSet.KBD_DATA.OUTPORT.A20));
|
||||
if (!(this.b8042OutPort & ChipSet.KBD_DATA.OUTPORT.RESET)) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
|
||||
* KBD_CMD.PULSE_OUTPORT command, so if a RESET is detected via this command, we should try to
|
||||
* determine if that's what the caller intended.
|
||||
*/
|
||||
if (DEBUG) {
|
||||
this.messageDebugger("unexpected 8042 output port reset: " + str.toHexByte(this.b8042OutPort));
|
||||
this.cpu.haltCPU();
|
||||
}
|
||||
this.cpu.resetRegs();
|
||||
}
|
||||
this.set8042OutPort(bOut);
|
||||
break;
|
||||
|
||||
/*
|
||||
|
|
@ -3531,8 +3528,8 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
* F000:1B62 83E901 SUB CX,0001 ; EXIT WITH SUCCESS (CX != 0)
|
||||
* F000:1B65 C3 RET
|
||||
*
|
||||
* But WAIT, the FUN doesn't end there. After this function returns, KBD_RESET waits for a Keyboard interrupt
|
||||
* to occur, hoping for a 0xAA scan code as the Keyboard's final response. KBD_RESET also returns CX to the caller,
|
||||
* But WAIT, the FUN doesn't end there. After this function returns, "KBD_RESET" waits for a Keyboard interrupt
|
||||
* to occur, hoping for a 0xAA scan code as the Keyboard's final response. "KBD_RESET" also returns CX to the caller,
|
||||
* and the caller ("TEST.21") assumes there was no interrupt if CX is zero.
|
||||
*
|
||||
* MOV AL,0FDH
|
||||
|
|
@ -3552,13 +3549,8 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
* CX can be zero not only if the loop exhausted it, but also if no looping was required!
|
||||
*/
|
||||
default:
|
||||
if (this.kbd) {
|
||||
var b = this.kbd.sendCmd(bOut);
|
||||
if (b >= 0) {
|
||||
this.b8042OutBuff = b;
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
}
|
||||
}
|
||||
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
if (this.kbd) this.set8042OutBuff(this.kbd.sendCmd(bOut));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3589,9 +3581,9 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
|
|||
* (which is outside the 0xff range of bits we return); when we see KBD_STATUS.OUTBUFF_DELAY,
|
||||
* we clear it and set KBD_STATUS.OUTBUFF_FULL, which will be returned on the next read.
|
||||
*
|
||||
* This provides a single-poll delay, so that the aforementioned "flush" won't occur. If longer
|
||||
* delays are needed down the road, we may need to set a delay count in the upper (hidden) bits
|
||||
* of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
|
||||
* This provides a single poll delay, so that the aforementioned "flush" won't toss our response.
|
||||
* If longer delays are needed down the road, we may need to set a delay count in the upper (hidden)
|
||||
* bits of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
|
||||
*/
|
||||
if (this.b8042Status & ChipSet.KBD_STATUS.OUTBUFF_DELAY) {
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
|
|
@ -3630,37 +3622,41 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
}
|
||||
|
||||
switch (this.b8042InBuff) {
|
||||
/*
|
||||
* No further action is required for this first group of commands; more data is expected via out8042InBuffData().
|
||||
*/
|
||||
case ChipSet.KBD_CMD.WRITE_CMD: // 0x60
|
||||
case ChipSet.KBD_CMD.WRITE_OUTPORT: // 0xD1
|
||||
/*
|
||||
* No further action required for this first group of commands; more data is expected via out8042InBuffData()
|
||||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.READ_INPORT: // 0xC0
|
||||
this.b8042OutBuff = this.b8042InPort;
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
this.set8042OutBuff(this.b8042InPort);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.DISABLE_KBD: // 0xAD
|
||||
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard disabled", ChipSet.MESSAGE_KBD);
|
||||
/*
|
||||
* TODO: Determine where to honor KBD_DATA.CMD.NO_CLOCK; note that the MODEL_5170 BIOS calls "KBD_RESET" (F000:17D2)
|
||||
* while the keyboard interface is disabled, yet we must still deliver the Keyboard's CMDRES.BATSUCCESS response code.
|
||||
*/
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.ENABLE_KBD: // 0xAE
|
||||
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard re-enabled", ChipSet.MESSAGE_KBD);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.SELF_TEST: // 0xAA
|
||||
this.b8042OutBuff = ChipSet.KBD_DATA.SELF_TEST.OK;
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
if (this.kbd) this.kbd.shiftScanCode(true);
|
||||
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
if (DEBUG) this.messageDebugger("keyboard disabled on reset", ChipSet.MESSAGE_KBD);
|
||||
this.set8042OutBuff(ChipSet.KBD_DATA.SELF_TEST.OK);
|
||||
this.set8042OutPort(ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.READ_TEST: // 0xE0
|
||||
/*
|
||||
* TODO: Do we need to "OR" anything here for KBD_DATA.TESTPORT.DATA?
|
||||
*/
|
||||
this.b8042OutBuff = ((this.b8042CmdData & ChipSet.KBD_DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBD_DATA.TESTPORT.CLOCK);
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
this.set8042OutBuff((this.b8042CmdData & ChipSet.KBD_DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBD_DATA.TESTPORT.KBD_CLOCK);
|
||||
break;
|
||||
|
||||
case ChipSet.KBD_CMD.PULSE_OUTPORT: // 0xF0-0xFF
|
||||
|
|
@ -3675,12 +3671,53 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
break;
|
||||
|
||||
default:
|
||||
this.messageDebugger("unrecognized 8042 command: " + str.toHexByte(this.b8042InBuff));
|
||||
this.cpu.haltCPU();
|
||||
if (DEBUG && DEBUGGER && this.dbg) {
|
||||
this.dbg.message("unrecognized 8042 command: " + str.toHexByte(this.b8042InBuff));
|
||||
this.cpu.haltCPU();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* set8042OutBuff(b)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} b
|
||||
*/
|
||||
ChipSet.prototype.set8042OutBuff = function(b)
|
||||
{
|
||||
if (b >= 0) {
|
||||
this.b8042OutBuff = b;
|
||||
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_FULL;
|
||||
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* set8042OutPort(b)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} b
|
||||
*/
|
||||
ChipSet.prototype.set8042OutPort = function(b)
|
||||
{
|
||||
this.b8042OutPort = b;
|
||||
this.bus.setA20(!!(b & ChipSet.KBD_DATA.OUTPORT.A20_ON));
|
||||
if (!(b & ChipSet.KBD_DATA.OUTPORT.NO_RESET)) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
|
||||
* KBD_CMD.PULSE_OUTPORT command, so if a RESET is detected via this command, we should try to
|
||||
* determine if that's what the caller intended.
|
||||
*/
|
||||
if (DEBUG && DEBUGGER && this.dbg) {
|
||||
this.dbg.message("unexpected 8042 output port reset: " + str.toHexByte(b));
|
||||
this.cpu.haltCPU();
|
||||
}
|
||||
this.cpu.resetRegs();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* inCMOSAddr(port, addrFrom)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -250,7 +250,16 @@ CPU.prototype.powerUp = function(data, fRepower)
|
|||
/*
|
||||
* Give the Debugger a chance to do/print something once we've powered up (TODO: Review the necessity of this)
|
||||
*/
|
||||
if (DEBUGGER && this.dbg) this.dbg.init();
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.init();
|
||||
} else {
|
||||
/*
|
||||
* TODO: Once we get rid of those nasty Component method overrides, this test will have to be revised as well
|
||||
*/
|
||||
if (Component.controlPrint) {
|
||||
this.warning("No debugger detected");
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fPowered = true;
|
||||
if (!this.autoStart() && this.dbg) {
|
||||
|
|
@ -1081,27 +1090,6 @@ CPU.prototype.updateCPU = function()
|
|||
this.displayStatus();
|
||||
};
|
||||
|
||||
/**
|
||||
* waitCPU()
|
||||
*
|
||||
* Similar to haltCPU() with regard to how it resets various cycle countdown values, but the CPU
|
||||
* remains in a "running" state, without yielding.
|
||||
*
|
||||
* TODO: This was originally used by opHLT(), but this seems rather pointless in hindsight, because
|
||||
* this call will only end the current stepCPU() iteration; we'll immediately go back into stepCPU(),
|
||||
* except that X86.INTFLAG.HALT will be set, so we won't execute any more instructions, not even opHLT(),
|
||||
* until a hardware interrupt is acknowledged. However, it would still be nice if we could reduce CPU
|
||||
* overhead while in a halted state.
|
||||
*
|
||||
* @this {CPU}
|
||||
*
|
||||
CPU.prototype.waitCPU = function()
|
||||
{
|
||||
this.nBurstCycles -= this.nStepCycles;
|
||||
this.nStepCycles = 0; // this will break us out of stepCPU()
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* yieldCPU()
|
||||
*
|
||||
|
|
|
|||
|
|
@ -132,14 +132,12 @@ function Debugger(parmsDbg)
|
|||
this.clearBreakpoints();
|
||||
|
||||
/*
|
||||
* Instead of pre-allocating these arrays, we wait until the reset() function is called.
|
||||
* These arrays are updated in checkInstruction(), but the CPU will never actually call it
|
||||
* unless checksEnabled() returns true, and that won't happen until one or more breakpoints
|
||||
* have been set. This ensures that, by default, the CPU runs as fast as possible.
|
||||
* Execution history is allocated by initHistory() whenever checksEnabled() conditions change.
|
||||
* Execution history is updated whenever the CPU calls checkInstruction(), which will happen only
|
||||
* when checksEnabled() returns true (eg, whenever one or more breakpoints have been set).
|
||||
* This ensures that, by default, the CPU runs as fast as possible.
|
||||
*/
|
||||
this.iStepHistory = 0;
|
||||
this.aStepHistory = [];
|
||||
this.aaOpcodeFreqs = [];
|
||||
this.initHistory();
|
||||
|
||||
/*
|
||||
* Message categories supported by the messageEnabled() function and other assorted message
|
||||
|
|
@ -516,22 +514,25 @@ if (DEBUGGER) {
|
|||
* Based on the active CPU model, we make every effort to execute and disassemble this (and every other)
|
||||
* opcode appropriately, by setting the opcode's entry in aaOpDescs accordingly. 0x0F defaults to the 8086
|
||||
* entry: aOpDescPopCS.
|
||||
*
|
||||
* Note that we do NOT modify aaOpDescs directly; this.aaOpDescs is a reference to it if the processor
|
||||
* is an 8086, otherwise we make a copy of the array and THEN modify it.
|
||||
*/
|
||||
Debugger.aOpDescPopCS = [Debugger.INS.POP, Debugger.TYPE_CS | Debugger.TYPE_OUT];
|
||||
Debugger.aOpDescUndefined = [Debugger.INS.NONE, Debugger.TYPE_NONE];
|
||||
Debugger.aOpDesc0F = [Debugger.INS.OP0F, Debugger.TYPE_WORD | Debugger.TYPE_BOTH];
|
||||
|
||||
/*
|
||||
* The aaOpDescs array is indexed by opcode, and each element is a sub-array (aOpDesc)
|
||||
* that describes the corresponding opcode. The sub-elements are as follows:
|
||||
* The aaOpDescs array is indexed by opcode, and each element is a sub-array (aOpDesc) that describes
|
||||
* the corresponding opcode. The sub-elements are as follows:
|
||||
*
|
||||
* [0]: {number} of the opcode name (see INS.*)
|
||||
* [1]: {number} containing the destination operand descriptor bit(s)
|
||||
* [2]: {number} containing the source operand descriptor bit(s)
|
||||
*
|
||||
* These sub-elements are all optional. If [0] is not present, the opcode is undefined;
|
||||
* if [1] is not present (or contains zero), the opcode has no (or only implied) operands;
|
||||
* and if [2] is not present, the opcode has only a single operand.
|
||||
* These sub-elements are all optional. If [0] is not present, the opcode is undefined; if [1] is not
|
||||
* present (or contains zero), the opcode has no (or only implied) operands; and if [2] is not present,
|
||||
* the opcode has only a single operand.
|
||||
*/
|
||||
Debugger.aaOpDescs = [
|
||||
/* 0x00 */ [Debugger.INS.ADD, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_REG | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
|
||||
|
|
@ -1114,10 +1115,12 @@ if (DEBUGGER) {
|
|||
this.hdc = cmp.getComponentByType("HDC");
|
||||
if (MAXDEBUG) this.chipset = cmp.getComponentByType("ChipSet");
|
||||
|
||||
this.aaOpDescs = Debugger.aaOpDescs;
|
||||
if (this.cpu.model >= X86.MODEL_80186) {
|
||||
Debugger.aaOpDescs[0x0F] = Debugger.aOpDescUndefined;
|
||||
this.aaOpDescs = Debugger.aaOpDescs.slice();
|
||||
this.aaOpDescs[0x0F] = Debugger.aOpDescUndefined;
|
||||
if (this.cpu.model >= X86.MODEL_80286) {
|
||||
Debugger.aaOpDescs[0x0F] = Debugger.aOpDesc0F;
|
||||
this.aaOpDescs[0x0F] = Debugger.aOpDesc0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1459,7 +1462,47 @@ if (DEBUGGER) {
|
|||
// this.doHelp();
|
||||
this.println("Type ? for list of debugger commands");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* initHistory()
|
||||
*
|
||||
* This function is intended to be called by the constructor, reset(), addBreakpoint(), findBreakpoint()
|
||||
* and any other function that changes the checksEnabled() criteria used to decide whether checkInstruction()
|
||||
* should be called.
|
||||
*
|
||||
* That is, if the history arrays need to be allocated and haven't already been allocated, then allocate them,
|
||||
* and if the arrays are no longer needed, then deallocate them.
|
||||
*
|
||||
* @this {Debugger}
|
||||
*/
|
||||
Debugger.prototype.initHistory = function()
|
||||
{
|
||||
var i;
|
||||
if (!this.checksEnabled()) {
|
||||
this.iOpcodeHistory = 0;
|
||||
this.aOpcodeHistory = [];
|
||||
this.aaOpcodeCounts = [];
|
||||
return;
|
||||
}
|
||||
if (!this.aOpcodeHistory || !this.aOpcodeHistory.length) {
|
||||
this.aOpcodeHistory = new Array(10000);
|
||||
for (i = 0; i < this.aOpcodeHistory.length; i++) {
|
||||
/*
|
||||
* Preallocate dummy Addr (Array) objects in every history slot, so that checkInstruction()
|
||||
* doesn't need to call newAddr() on every instruction check.
|
||||
*/
|
||||
this.aOpcodeHistory[i] = [0, 0, 0];
|
||||
}
|
||||
this.iOpcodeHistory = 0;
|
||||
}
|
||||
if (!this.aaOpcodeCounts || !this.aaOpcodeCounts.length) {
|
||||
this.aaOpcodeCounts = new Array(256);
|
||||
for (i = 0; i < this.aaOpcodeCounts.length; i++) {
|
||||
this.aaOpcodeCounts[i] = [i, 0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* runCPU(fOnClick)
|
||||
*
|
||||
|
|
@ -1625,19 +1668,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.reset = function(fQuiet)
|
||||
{
|
||||
var i;
|
||||
if (!this.aStepHistory.length) {
|
||||
this.aStepHistory = new Array(10000);
|
||||
}
|
||||
for (i = 0; i < this.aStepHistory.length; i++) {
|
||||
this.aStepHistory[i] = [];
|
||||
}
|
||||
if (!this.aaOpcodeFreqs.length) {
|
||||
this.aaOpcodeFreqs = new Array(256);
|
||||
}
|
||||
for (i = 0; i < this.aaOpcodeFreqs.length; i++) {
|
||||
this.aaOpcodeFreqs[i] = [i, 0];
|
||||
}
|
||||
this.initHistory();
|
||||
this.cInstructions = 0;
|
||||
this.nCycles = 0;
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
|
|
@ -1651,7 +1682,7 @@ if (DEBUGGER) {
|
|||
this.clearTempBreakpoint();
|
||||
if (!fQuiet) this.updateStatus();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
|
|
@ -1798,29 +1829,39 @@ if (DEBUGGER) {
|
|||
* @this {Debugger}
|
||||
* @param {number} addr
|
||||
* @param {boolean} [fSkipBP] is true to skip breakpoint check
|
||||
* @return {boolean} true to proceed, false to halt
|
||||
* @return {boolean} true if breakpoint hit, false if not
|
||||
*/
|
||||
Debugger.prototype.checkInstruction = function(addr, fSkipBP)
|
||||
{
|
||||
var fBreak = false;
|
||||
/*
|
||||
* Assert that general-purpose register contents remain within their respective ranges;
|
||||
* this isn't intended to be complete, just a spot-check.
|
||||
*/
|
||||
Component.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds");
|
||||
|
||||
if (!fSkipBP && this.checkBreakpoint(addr, this.aBreakExec))
|
||||
fBreak = true;
|
||||
else {
|
||||
this.cInstructions++;
|
||||
var bOpcode = this.bus.getByteDirect(addr);
|
||||
this.aaOpcodeFreqs[bOpcode][1]++;
|
||||
this.aStepHistory[this.iStepHistory++] = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
if (this.iStepHistory == this.aStepHistory.length) {
|
||||
this.iStepHistory = 0;
|
||||
}
|
||||
if (!fSkipBP && this.checkBreakpoint(addr, this.aBreakExec)) {
|
||||
return true;
|
||||
}
|
||||
return !fBreak;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1835,12 +1876,11 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.checkMemoryRead = function(addr)
|
||||
{
|
||||
var fBreak = false;
|
||||
if (this.checkBreakpoint(addr, this.aBreakRead)) {
|
||||
this.cpu.haltCPU(true);
|
||||
fBreak = true;
|
||||
return true;
|
||||
}
|
||||
return fBreak;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1855,12 +1895,11 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.checkMemoryWrite = function(addr)
|
||||
{
|
||||
var fBreak = false;
|
||||
if (this.checkBreakpoint(addr, this.aBreakWrite)) {
|
||||
this.cpu.haltCPU(true);
|
||||
fBreak = true;
|
||||
return true;
|
||||
}
|
||||
return fBreak;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2081,16 +2120,17 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* newAddr(off, seg)
|
||||
* newAddr(off, seg, addr)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} off
|
||||
* @param {number} seg
|
||||
* @return {Array} containing [off, seg]
|
||||
* @param {number} [addr] is the physical address, if known
|
||||
* @return {Array} containing [off, seg, addr]
|
||||
*/
|
||||
Debugger.prototype.newAddr = function(off, seg)
|
||||
Debugger.prototype.newAddr = function(off, seg, addr)
|
||||
{
|
||||
return [off, seg];
|
||||
return [off, seg, addr];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2141,6 +2181,7 @@ if (DEBUGGER) {
|
|||
this.bus.addMemoryBreakpoint(this.getAddr(aAddr), aBreak == this.aBreakWrite);
|
||||
}
|
||||
if (!fTemp) this.println("breakpoint enabled: " + this.hexAddr(aAddr) + " (" + aBreak[0] + ")");
|
||||
this.initHistory();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -2169,6 +2210,7 @@ if (DEBUGGER) {
|
|||
this.bus.removeMemoryBreakpoint(addr, aBreak == this.aBreakWrite);
|
||||
}
|
||||
if (!aAddrBreak[3]) this.println("breakpoint cleared: " + this.hexAddr(aAddrBreak) + " (" + aBreak[0] + ")");
|
||||
this.initHistory();
|
||||
break;
|
||||
}
|
||||
this.println("breakpoint exists: " + this.hexAddr(aAddrBreak) + " (" + aBreak[0] + ")");
|
||||
|
|
@ -2309,7 +2351,7 @@ if (DEBUGGER) {
|
|||
var aAddrIns = this.newAddr(aAddr[0], aAddr[1]);
|
||||
|
||||
var bOpcode = this.getByte(aAddr, 1);
|
||||
var aOpDesc = Debugger.aaOpDescs[bOpcode];
|
||||
var aOpDesc = this.aaOpDescs[bOpcode];
|
||||
var iIns = aOpDesc[0];
|
||||
var bModRM = -1;
|
||||
|
||||
|
|
@ -3399,10 +3441,10 @@ if (DEBUGGER) {
|
|||
}
|
||||
var i;
|
||||
var cData = 0;
|
||||
if (this.aaOpcodeFreqs) {
|
||||
if (this.aaOpcodeCounts) {
|
||||
if (sParm == "clear") {
|
||||
for (i = 0; i < this.aaOpcodeFreqs.length; i++)
|
||||
this.aaOpcodeFreqs[i] = [i, 0];
|
||||
for (i = 0; i < this.aaOpcodeCounts.length; i++)
|
||||
this.aaOpcodeCounts[i] = [i, 0];
|
||||
this.println("frequency data cleared");
|
||||
cData++;
|
||||
}
|
||||
|
|
@ -3411,15 +3453,15 @@ if (DEBUGGER) {
|
|||
cData++;
|
||||
}
|
||||
else {
|
||||
var aaSortedOpcodeFreqs = this.aaOpcodeFreqs.slice();
|
||||
aaSortedOpcodeFreqs.sort(function(p, q) {
|
||||
var aaSortedOpcodeCounts = this.aaOpcodeCounts.slice();
|
||||
aaSortedOpcodeCounts.sort(function(p, q) {
|
||||
return q[1] - p[1];
|
||||
});
|
||||
for (i = 0; i < aaSortedOpcodeFreqs.length; i++) {
|
||||
var bOpcode = aaSortedOpcodeFreqs[i][0];
|
||||
var cFreq = aaSortedOpcodeFreqs[i][1];
|
||||
for (i = 0; i < aaSortedOpcodeCounts.length; i++) {
|
||||
var bOpcode = aaSortedOpcodeCounts[i][0];
|
||||
var cFreq = aaSortedOpcodeCounts[i][1];
|
||||
if (cFreq) {
|
||||
this.println((Debugger.asIns[Debugger.aaOpDescs[bOpcode][0]] + " ").substr(0, 5) + " (" + str.toHexByte(bOpcode) + "): " + cFreq + " times");
|
||||
this.println((Debugger.asIns[this.aaOpDescs[bOpcode][0]] + " ").substr(0, 5) + " (" + str.toHexByte(bOpcode) + "): " + cFreq + " times");
|
||||
cData++;
|
||||
}
|
||||
}
|
||||
|
|
@ -3446,8 +3488,8 @@ if (DEBUGGER) {
|
|||
}
|
||||
var sMore = "";
|
||||
var cLines = 10;
|
||||
var iHistory = this.iStepHistory;
|
||||
var aHistory = this.aStepHistory;
|
||||
var iHistory = this.iOpcodeHistory;
|
||||
var aHistory = this.aOpcodeHistory;
|
||||
if (aHistory !== undefined) {
|
||||
var n = (sCount === undefined? this.nextHistory : parseInt(sCount, 10));
|
||||
if (isNaN(n))
|
||||
|
|
@ -3470,14 +3512,14 @@ if (DEBUGGER) {
|
|||
if (sCount !== undefined) {
|
||||
this.println(n + " instructions earlier:");
|
||||
}
|
||||
while (cLines && iHistory != this.iStepHistory) {
|
||||
while (cLines && iHistory != this.iOpcodeHistory) {
|
||||
var aAddr = aHistory[iHistory];
|
||||
if (!aAddr.length) break;
|
||||
/*
|
||||
* We must create a new aAddr from the address we obtained from aHistory, because
|
||||
* it was a reference, not a copy, and we don't want getInstruction() modifying the original.
|
||||
*/
|
||||
aAddr = this.newAddr(aAddr[0], aAddr[1]);
|
||||
aAddr = this.newAddr(aAddr[0], aAddr[1], aAddr[2]);
|
||||
this.println(this.getInstruction(aAddr, "history", -n));
|
||||
if (++iHistory == aHistory.length) iHistory = 0;
|
||||
this.nextHistory = --n;
|
||||
|
|
|
|||
|
|
@ -582,7 +582,7 @@ Keyboard.prototype.resetDevice = function()
|
|||
* TODO: There's more to reset, like LED indicators, default type rate, and emptying the scan code buffer.
|
||||
*/
|
||||
this.messageDebugger("keyboard reset", true);
|
||||
this.abScanBuffer = [0xAA];
|
||||
this.abScanBuffer = [Keyboard.CMDRES.BATSUCCESS];
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD, 4);
|
||||
};
|
||||
|
||||
|
|
@ -638,6 +638,8 @@ Keyboard.prototype.sendCmd = function(bCmd)
|
|||
b = Keyboard.CMDRES.ACK;
|
||||
this.resetDevice();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return b;
|
||||
};
|
||||
|
|
@ -648,7 +650,7 @@ Keyboard.prototype.sendCmd = function(bCmd)
|
|||
* This is the ChipSet's interface for reading scan codes.
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @param {boolean} [fShift]
|
||||
* @param {boolean} [fShift] is used by the MODEL_5170 8042 Keyboard Controller (supersedes the old setEnable() interface)
|
||||
* @return {number} next scan code, or 0 if none
|
||||
*/
|
||||
Keyboard.prototype.readScanCode = function(fShift)
|
||||
|
|
@ -663,23 +665,32 @@ Keyboard.prototype.readScanCode = function(fShift)
|
|||
};
|
||||
|
||||
/**
|
||||
* shiftScanCode()
|
||||
* shiftScanCode(fFlush)
|
||||
*
|
||||
* This is the ChipSet's interface to advance scan codes.
|
||||
* This is the ChipSet's interface to advance (or flush) scan codes.
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @param {boolean} [fFlush] is true to completely flush the keyboard buffer
|
||||
*/
|
||||
Keyboard.prototype.shiftScanCode = function()
|
||||
Keyboard.prototype.shiftScanCode = function(fFlush)
|
||||
{
|
||||
if (this.abScanBuffer.length > 0) {
|
||||
/*
|
||||
* The keyboard interrupt service routine toggles the enable bit after reading a scan code, so
|
||||
* presumably this is the proper point at which to shift the last scan code out, and then assert
|
||||
* another interrupt if more scan codes exist.
|
||||
*/
|
||||
this.abScanBuffer.shift();
|
||||
if (this.abScanBuffer.length > 0) {
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD);
|
||||
if (fFlush) {
|
||||
/*
|
||||
* This is now called after receipt of an 8042 self-test command, to ensure we don't
|
||||
* overwrite the self-test response byte with left-over scan codes.
|
||||
*/
|
||||
this.abScanBuffer = [];
|
||||
} else {
|
||||
/*
|
||||
* The keyboard interrupt service routine toggles the enable bit after reading a scan code, so
|
||||
* presumably this is the proper point at which to shift the last scan code out, and then assert
|
||||
* another interrupt if more scan codes exist.
|
||||
*/
|
||||
this.abScanBuffer.shift();
|
||||
if (this.abScanBuffer.length > 0) {
|
||||
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2248,7 +2248,7 @@ X86CPU.prototype.pushWord = function(w)
|
|||
*
|
||||
* ERRATA: I do recall that early revisions of the 8086/8088 failed to suppress hardware interrupts (and
|
||||
* possibly also Trap acknowledgements) after an SS load, but that Intel corrected the problem at some point;
|
||||
* however, I don't know exactly when that change was made or which IBM PC models may have been affected, if any.
|
||||
* however, I don't know when that change was made or which IBM PC models may have been affected, if any.
|
||||
* TODO: More research required.
|
||||
*
|
||||
* WARNING: There is also a priority consideration here. On the 8086/8088, hardware interrupts have higher
|
||||
|
|
@ -2268,7 +2268,7 @@ X86CPU.prototype.checkINTR = function()
|
|||
if (!(this.opFlags & X86.OPFLAG.NOINTR)) {
|
||||
if ((this.intFlags & X86.INTFLAG.INTR) && (this.regPS & X86.PS.IF)) {
|
||||
var nIDT = this.chipset.getIRRVector();
|
||||
if (nIDT != -2) {
|
||||
if (nIDT >= -1) {
|
||||
this.intFlags &= ~X86.INTFLAG.INTR;
|
||||
if (nIDT >= 0) {
|
||||
this.intFlags &= ~X86.INTFLAG.HALT;
|
||||
|
|
@ -2473,8 +2473,8 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
if (this.intFlags) {
|
||||
if (this.checkINTR()) {
|
||||
/*
|
||||
* ASSERT: If it's never possible to have !nMinCycles WITHOUT the Debugger, then all
|
||||
* we need to check is !nMinCycles.
|
||||
* ASSERT: If it's never possible to have !nMinCycles WITHOUT the Debugger, then all we need
|
||||
* to check is !nMinCycles.
|
||||
*/
|
||||
if (DEBUGGER && !nMinCycles) {
|
||||
this.opFlags = 0;
|
||||
|
|
@ -2483,25 +2483,28 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
}
|
||||
if (this.intFlags & X86.INTFLAG.HALT) {
|
||||
/*
|
||||
* Even though we're technically "halted", we still need to keep the cycle count moving;
|
||||
* otherwise the whole point of staying in the runCPU() loop (ie, to continue calling
|
||||
* video.updateScreen() from runCPU(), as well as chipset.updateAllTimers() from stepCPU())
|
||||
* is lost, because both those functions depend on movement in the cycle count.
|
||||
*
|
||||
* TODO: Another option here would be to decrement IP and execute the HLT repeatedly,
|
||||
* but that had a surprisingly bad impact on performance; seems like a better idea would
|
||||
* be to simply keep pretending that we just executed the remaining nStepCycles, and let
|
||||
* runCPU() sleep for the remainder of the burst, so that we get some power savings.
|
||||
* As discussed in opHLT(), the CPU is never REALLY halted by a HLT instruction; instead,
|
||||
* opHLT() sets X86.INTFLAG.HALT, signalling to us that we're free to end the current burst
|
||||
* AND that we should not execute any more instructions until checkINTR() indicates a hardware
|
||||
* interrupt has been requested.
|
||||
*
|
||||
* One downside to this approach is that it *might* appear to the careful observer that we
|
||||
* executed a full complement of instructions during bursts where X86.INTFLAG.HALT was set,
|
||||
* when in fact we did not. However, the steady advance of the overall cycle count, and thus
|
||||
* the steady series calls to stepCPU(), is needed to ensure that timer updates, video updates,
|
||||
* etc, all continue to occur at the expected rates.
|
||||
*
|
||||
* If necessary, we can add another bookkeeping cycle counter (eg, one that keeps tracks of the
|
||||
* number of cycles during which we did not actually execute any instructions).
|
||||
*/
|
||||
// this.advanceIP(-1);
|
||||
this.nStepCycles -= 2;
|
||||
this.nStepCycles = 0;
|
||||
this.opFlags = 0;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUGGER && this.fDebugCheck && !this.dbg.checkInstruction(this.regEIP)) {
|
||||
if (DEBUGGER && this.fDebugCheck && this.dbg.checkInstruction(this.regEIP)) {
|
||||
this.haltCPU();
|
||||
break;
|
||||
}
|
||||
|
|
@ -2524,19 +2527,19 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
|
||||
if (DEBUG) {
|
||||
/*
|
||||
* Some opcode helpers are required to temporarily redirect getEAByte/getEAWord or setEAByte/setEAWord to null
|
||||
* functions, effectively disabling a memory read that's unnecessary (or a memory write that could be destructive).
|
||||
* However, they weren't originally required to restore those memory functions when they were done; we would
|
||||
* simply reset all the memory functions here, after every single instruction.
|
||||
* Some opcode helpers are required to temporarily redirect getEAByte/getEAWord or setEAByte/setEAWord
|
||||
* to null functions, effectively disabling a memory read that's unnecessary (or a memory write that could
|
||||
* be destructive). However, they weren't originally required to restore those memory functions when they
|
||||
* were done; we would simply reset all the memory functions here, after every single instruction.
|
||||
*
|
||||
* That's no longer the case. Those opcode helpers (or their callers) are now required to restore the memory
|
||||
* access functions to their defaults, so that we don't have to waste time resetting them here, on every instruction.
|
||||
* The DEBUG-only verifyMemoryEnabled() simply confirms that everyone's doing their job.
|
||||
* That's no longer the case. Those opcode helpers (or their callers) are now required to restore the
|
||||
* memory access functions to their defaults, so that we don't have to waste time resetting them here, on
|
||||
* every instruction. The DEBUG-only verifyMemoryEnabled() simply confirms that everyone's doing their job.
|
||||
*/
|
||||
this.verifyMemoryEnabled();
|
||||
|
||||
/*
|
||||
* Make sure every instruction is assessing a cycle cost, and that the cost is a net positive.
|
||||
* Make sure that every instruction is assessing a cycle cost, and that the cost is a net positive.
|
||||
*/
|
||||
if (this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG.PREFIXES)) {
|
||||
this.println("cycle miscount: " + (this.nSnapCycles - this.nStepCycles));
|
||||
|
|
|
|||
|
|
@ -3192,45 +3192,34 @@ var X86OpXX = {
|
|||
* @this {X86CPU}
|
||||
*
|
||||
* op=0xF4 (hlt)
|
||||
*
|
||||
* WARNING: Because other components "thrive" on the CPU's runCPU() loop notifications,
|
||||
* (eg, the Video component's blinking elements, and the Chipset component's timers),
|
||||
* we can't really stop. What we do instead is set INTFLAG.HALT and "wait" for INTFLAG.INTR
|
||||
* to be set; since stepCPU() is already monitoring intFlags, this INTFLAG.HALT bit doesn't
|
||||
* impact performance.
|
||||
*
|
||||
* All stepCPU() has to do when INTFLAG.HALT is set is advance the cycle count without
|
||||
* advancing the program counter. That continues indefinitely until stepCPU() finally detects
|
||||
* and acknowledges a INTFLAG.INTR notification, at which point INTFLAG.HALT is cleared.
|
||||
*/
|
||||
opHLT: function() {
|
||||
/*
|
||||
* The CPU is never REALLY halted by a HLT instruction; instead, by setting X86.INTFLAG.HALT,
|
||||
* we are signalling to stepCPU() that it's free to end the current burst AND that it should not
|
||||
* execute any more instructions until checkINTR() indicates a hardware interrupt is requested.
|
||||
*/
|
||||
this.intFlags |= X86.INTFLAG.HALT;
|
||||
this.nStepCycles -= 2;
|
||||
/*
|
||||
* We halt the machine only if a Debugger is present AND Debugger checks are enabled (eg,
|
||||
* one or more breakpoints are set, or the global DEBUG flag is set, etc), on the theory that
|
||||
* whoever's using the Debugger might like to see halts; we also halt the machine if interrupts
|
||||
* have been disabled, since that means it's dead in the water (we have no NMI generation
|
||||
* mechanism at the moment).
|
||||
*
|
||||
* Otherwise, HLT is treated like any other instruction.
|
||||
* If a Debugger is present AND Debugger checks are enabled (eg, one or more breakpoints are set,
|
||||
* or the global DEBUG flag is set, etc), then we REALLY halt the CPU, on the theory that whoever's
|
||||
* using the Debugger would like to see HLTs.
|
||||
*/
|
||||
if (DEBUGGER && this.dbg && this.dbg.checksEnabled(true)) {
|
||||
this.advanceIP(-1); // this is purely for the Debugger's benefit, to show the HLT
|
||||
this.haltCPU();
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* We also REALLY halt the machine if interrupts have been disabled, since that means it's dead
|
||||
* in the water (we have no NMI generation mechanism at the moment).
|
||||
*/
|
||||
if (!this.getIF()) {
|
||||
if (DEBUGGER && this.dbg) this.advanceIP(-1);
|
||||
this.haltCPU();
|
||||
// return;
|
||||
}
|
||||
/*
|
||||
* Per my discussion of waitCPU() in cpu.js, this seems rather pointless, so I don't call it anymore.
|
||||
* If you re-enable this, make sure you re-enable the return statement above, too.
|
||||
*
|
||||
this.waitCPU();
|
||||
*/
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
|
|
|
|||
|
|
@ -635,6 +635,11 @@ Component.prototype = {
|
|||
if (!this.bindings[sBinding]) {
|
||||
this.bindings[sBinding] = control;
|
||||
control.value = ""; // this was added for Firefox (Safari automatically clears the <textarea> on a page reload, but Firefox does not)
|
||||
/*
|
||||
* TODO: Get rid of these Component method overrides, because they're going to cause issues
|
||||
* if the day ever comes (and it WILL) that we want multiple machines on a single page with their
|
||||
* own Control Panels.
|
||||
*/
|
||||
Component.println = (function(control) {
|
||||
return function printControl(s, type) {
|
||||
s = (type !== undefined? (type + ": ") : "") + (s || "");
|
||||
|
|
|
|||
Loading…
Reference in a new issue