Fixed 5170 memory test and debugger addressing issues
This commit is contained in:
parent
93b81c47b7
commit
32bf809645
8 changed files with 937 additions and 857 deletions
|
|
@ -483,7 +483,7 @@ ChipSet.PPI_A = {}; // this.bPPIA
|
|||
ChipSet.PPI_A.PORT = 0x60; // INPUT: keyboard scan code (PPI_B_CLEAR_KBD must be clear)
|
||||
|
||||
ChipSet.PPI_B = {}; // this.bPPIB
|
||||
ChipSet.PPI_B.PORT = 0x61; // OUTPUT (although it has to be treated as INPUT, too: the keyboard interrupt handler reads it, OR's PPI_B_CLEAR_KBD, writes it, and then rewrites the original read value)
|
||||
ChipSet.PPI_B.PORT = 0x61; // OUTPUT (although it has to be treated as INPUT, too: the keyboard interrupt handler reads it, OR's PPI_B.CLEAR_KBD, writes it, and then rewrites the original read value)
|
||||
ChipSet.PPI_B.CLK_TIMER2 = 0x01; // ALL: set to enable clock to TIMER2
|
||||
ChipSet.PPI_B.SPK_TIMER2 = 0x02; // ALL: set to connect output of TIMER2 to speaker (MODEL_5150: clear for cassette)
|
||||
ChipSet.PPI_B.ENABLE_SW2 = 0x04; // MODEL_5150: set to enable SW2[1-4] through PPI_C.PORT, clear to enable SW2[5]; MODEL_5160: unused (there is no SW2 switch block on the MODEL_5160 motherboard)
|
||||
|
|
@ -513,6 +513,7 @@ ChipSet.PPI_CTRL.A_MODE = 0x60;
|
|||
|
||||
/*
|
||||
* On the MODEL_5150, the following PPI_SW bits are exposed through PPI_A.
|
||||
*
|
||||
* On the MODEL_5160, either the low or high 4 bits are exposed through PPI_C_SW, if PPI_B_ENABLE_SW_HI is clear or set.
|
||||
*/
|
||||
ChipSet.PPI_SW = {};
|
||||
|
|
@ -542,20 +543,20 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
|
|||
/*
|
||||
* 8042 Keyboard Controller I/O ports (MODEL_5170)
|
||||
*
|
||||
* On the MODEL_5170, port 0x60 is treated as KBD_DATA rather than PPI_A (although the 5170 BIOS also refers
|
||||
* to it as "PORT_A"). This is the 8042's output buffer and should be read only when KBD_STATUS.OUTBUFF_FULL set.
|
||||
* On the MODEL_5170, port 0x60 is designated KBD_DATA rather than PPI_A, although the BIOS also refers to it
|
||||
* as "PORT_A: 8042 KEYBOARD SCAN/DIAG OUTPUTS"). This is the 8042's output buffer and should be read only when
|
||||
* KBD_STATUS.OUTBUFF_FULL is set.
|
||||
*
|
||||
* The MODEL_5170 also uses port 0x61 (PPI_B), which the BIOS refers to as "8042 READ WRITE REGISTER (PORT_B)",
|
||||
* but it is not discussed in the MODEL_5170 TechRef's 8042 documentation. There are brief references to bits 0
|
||||
* and 1 (PPI_B.CLK_TIMER2 and PPI_B.SPK_TIMER2), and the BIOS sets bits 3-7 to "DISABLE PARITY CHECKERS"
|
||||
* (principally PPI_B.DISABLE_RW_MEM and PPI_B.DISABLE_IO_CHK, which are bits 4 and 5); why the BIOS also sets
|
||||
* bits 3 and 6-7 is unclear and undocumented, since it uses 11111100B rather than defined constants.
|
||||
* Similarly, port 0x61 is designated KBD_RWREG rather than PPI_B; the BIOS also refers to it as "PORT_B: 8042
|
||||
* READ WRITE REGISTER", but it is not otherwise discussed in the MODEL_5170 TechRef's 8042 documentation.
|
||||
* There are brief references to bits 0 and 1 (KBD_RWREG.CLK_TIMER2 and KBD_RWREG.SPK_TIMER2), and the BIOS sets
|
||||
* bits 2-7 to "DISABLE PARITY CHECKERS" (principally KBD_RWREG.DISABLE_CHK, which are bits 2 and 3); why the BIOS
|
||||
* also sets bits 4-7 (or if those bits are even settable) is unclear, since it uses 11111100B rather than defined
|
||||
* constants.
|
||||
*
|
||||
* The bottom line is, even on a MODEL_5170, PPI_B is still used for speaker control and parity checking. It's
|
||||
* 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 refers to it as "REFRESH_BIT").
|
||||
* The bottom line: on a MODEL_5170, port 0x61 is still used for speaker control and parity checking, so we use
|
||||
* the same register (bPPIB) but install different I/O handlers. It's also bi-directional: at one point, the BIOS
|
||||
* reads KBD_RWREG.REFRESH_BIT (bit 4) to verify that it's alternating.
|
||||
*
|
||||
* PPI_C and PPI_CTRL are neither documented nor used by the MODEL_5170 BIOS, so I'm assuming they're obsolete.
|
||||
*
|
||||
|
|
@ -565,7 +566,7 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
|
|||
* 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 = { // this.b8042OutBuff (PPI_A on previous models, still referred to as "PORT A" by the MODEL_5170 BIOS)
|
||||
PORT: 0x60
|
||||
};
|
||||
|
||||
|
|
@ -612,6 +613,26 @@ ChipSet.KBD_DATA.TESTPORT = { // generated "on the fly"
|
|||
KBD_DATA: 0x02 // keyboard data (input)
|
||||
};
|
||||
|
||||
ChipSet.KBD_RWREG = { // this.bPPIB (since CLK_TIMER2 and SPK_TIMER2 are in both PPI_B and KBD_RWREG)
|
||||
PORT: 0x61,
|
||||
CLK_TIMER2: 0x01, // set to enable clock to TIMER2
|
||||
SPK_TIMER2: 0x02, // set to connect output of TIMER2 to speaker
|
||||
DISABLE_CHK: 0x0C, // set these bits to disable I/O and RAM parity checks, clear them to enable checks
|
||||
REFRESH_BIT: 0x10, // indicates memory refresh
|
||||
IO_CHK: 0x40, // indicates I/O check
|
||||
PARITY_CHK: 0x80, // indicates RAM parity check
|
||||
PARITY_ERR: 0xC0
|
||||
};
|
||||
|
||||
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_CMD = { // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
|
||||
PORT: 0x64,
|
||||
READ_CMD: 0x20,
|
||||
|
|
@ -1245,12 +1266,13 @@ ChipSet.prototype.initCMOSData = function()
|
|||
this.abCMOSData[ChipSet.CMOS_ADDR.EQUIP] = this.sw1 & (ChipSet.PPI_SW.MONITOR.MASK | ChipSet.PPI_SW.COPROC | ChipSet.PPI_SW.FDRIVE.IPL | ChipSet.PPI_SW.FDRIVE.MASK);
|
||||
|
||||
/*
|
||||
* TODO: We default all floppy diskette drives to non-High Capacity (double-density) drives, but this will have to change.
|
||||
* TODO: We default all floppy diskette drives to High Capacity, but MODEL_5170 machines will need more control
|
||||
* over settings like this.
|
||||
*/
|
||||
var bDisketteTypes = 0;
|
||||
var cDisketteDrives = this.getSW1FloppyDrives();
|
||||
if (cDisketteDrives > 0) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D0_DS;
|
||||
if (cDisketteDrives > 1) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D1_DS;
|
||||
if (cDisketteDrives > 0) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D0_HC;
|
||||
if (cDisketteDrives > 1) bDisketteTypes |= ChipSet.CMOS_FDRIVE.D1_HC;
|
||||
this.abCMOSData[ChipSet.CMOS_ADDR.FDRIVE] = bDisketteTypes;
|
||||
|
||||
var wBaseMemKb = this.getSWMemorySize();
|
||||
|
|
@ -2266,7 +2288,10 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
|
|||
if (DEBUG) obj.messageDebugger("advanceDMA(" + iDMAChannel + ") ran out of data, assuming 0xff", ChipSet.MESSAGE_DMA);
|
||||
channel.fWarning = true;
|
||||
}
|
||||
b = 0xff; // TODO: Determine whether to abort, as we do for DMA_MODE_XFER_READ
|
||||
/*
|
||||
* TODO: Determine whether to abort, as we do for DMA_MODE_XFER_READ.
|
||||
*/
|
||||
b = 0xff;
|
||||
}
|
||||
if (!channel.masked) {
|
||||
/*
|
||||
|
|
@ -3263,6 +3288,29 @@ ChipSet.prototype.updateAllTimers = function(fCycleReset)
|
|||
if (this.model >= ChipSet.MODEL_5170) this.updateRTCDate();
|
||||
};
|
||||
|
||||
/**
|
||||
* updateSpeaker(bOut)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} bOut
|
||||
*/
|
||||
ChipSet.prototype.updateSpeaker = function(bOut)
|
||||
{
|
||||
var fNewSpeaker = !!(bOut & ChipSet.PPI_B.SPK_TIMER2);
|
||||
var fOldSpeaker = !!(this.bPPIB & ChipSet.PPI_B.SPK_TIMER2);
|
||||
this.bPPIB = bOut;
|
||||
if (fNewSpeaker != fOldSpeaker) {
|
||||
/*
|
||||
* Originally, this code didn't catch the "ERROR_BEEP" case @F000:EC34, which first turns both PPI_B_CLK_TIMER2 (0x01)
|
||||
* and PPI_B_SPK_TIMER2 (0x02) off, then turns on only PPI_B_SPK_TIMER2 (0x02), then restores the original port value.
|
||||
*
|
||||
* So, when the ROM BIOS keyboard buffer got full, we didn't issue a BEEP alert. I've fixed that by limiting the test
|
||||
* to PPI_B_SPK_TIMER2 and ignoring PPI_B_CLK_TIMER2.
|
||||
*/
|
||||
this.setSpeaker(fNewSpeaker);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* inPPIA(port, addrFrom)
|
||||
*
|
||||
|
|
@ -3302,7 +3350,7 @@ ChipSet.prototype.outPPIA = function(port, bOut, addrFrom)
|
|||
|
||||
/**
|
||||
* inPPIB(port, addrFrom)
|
||||
*
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x61)
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to read the specified port)
|
||||
|
|
@ -3311,18 +3359,13 @@ ChipSet.prototype.outPPIA = function(port, bOut, addrFrom)
|
|||
ChipSet.prototype.inPPIB = function(port, addrFrom)
|
||||
{
|
||||
var b = this.bPPIB;
|
||||
/*
|
||||
* "TEST.09" of the MODEL_5170 BIOS expects the following bit ("REFRESH_BIT") to alternate, so we oblige;
|
||||
* hopefully this won't affect MODEL_5150 or MODEL_5160, because we didn't used to do this.
|
||||
*/
|
||||
this.bPPIB ^= ChipSet.PPI_B.DISABLE_RW_MEM;
|
||||
this.messagePort(port, null, addrFrom, "PPI_B", ChipSet.MESSAGE_CHIPSET, b);
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* outPPIB(port, bOut, addrFrom)
|
||||
*
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x61)
|
||||
* @param {number} bOut
|
||||
|
|
@ -3331,19 +3374,7 @@ ChipSet.prototype.inPPIB = function(port, addrFrom)
|
|||
ChipSet.prototype.outPPIB = function(port, bOut, addrFrom)
|
||||
{
|
||||
this.messagePort(port, bOut, addrFrom, "PPI_B", ChipSet.MESSAGE_CHIPSET);
|
||||
var fNewSpeaker = !!(bOut & ChipSet.PPI_B.SPK_TIMER2);
|
||||
var fOldSpeaker = !!(this.bPPIB & ChipSet.PPI_B.SPK_TIMER2);
|
||||
this.bPPIB = bOut;
|
||||
if (fNewSpeaker != fOldSpeaker) {
|
||||
/*
|
||||
* Originally, this code didn't catch the "ERROR_BEEP" case @F000:EC34, which first turns both PPI_B_CLK_TIMER2 (0x01)
|
||||
* and PPI_B_SPK_TIMER2 (0x02) off, then turns on only PPI_B_SPK_TIMER2 (0x02), then restores the original port value.
|
||||
*
|
||||
* So, when the ROM BIOS keyboard buffer got full, we didn't issue a BEEP alert. I've fixed that by limiting the test
|
||||
* to PPI_B_SPK_TIMER2 and ignoring PPI_B_CLK_TIMER2.
|
||||
*/
|
||||
this.setSpeaker(fNewSpeaker);
|
||||
}
|
||||
this.updateSpeaker(bOut);
|
||||
if (this.kbd) this.kbd.setEnable((bOut & ChipSet.PPI_B.CLEAR_KBD)? false : true, (bOut & ChipSet.PPI_B.CLK_KBD)? true : false);
|
||||
};
|
||||
|
||||
|
|
@ -3529,7 +3560,7 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
* 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,
|
||||
* to occur, hoping for scan code 0xAA 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
|
||||
|
|
@ -3546,7 +3577,8 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
* ...
|
||||
*
|
||||
* However, if [INTR_FLAG] is set immediately, the above code will exit immediately, without ever decrementing CX.
|
||||
* CX can be zero not only if the loop exhausted it, but also if no looping was required!
|
||||
* CX can be zero not only if the loop exhausted it, but also if no looping was required; the latter is not an
|
||||
* error, but "TEST.21" assumes that it is.
|
||||
*/
|
||||
default:
|
||||
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
|
||||
|
|
@ -3558,6 +3590,43 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
|
|||
this.b8042Status &= ~ChipSet.KBD_STATUS.CMD_FLAG;
|
||||
};
|
||||
|
||||
/**
|
||||
* in8042RWReg(port, addrFrom)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x61)
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to read the specified port)
|
||||
* @return {number} simulated port value
|
||||
*/
|
||||
ChipSet.prototype.in8042RWReg = function(port, addrFrom)
|
||||
{
|
||||
/*
|
||||
* Normally, we return whatever was last written to this port, but we do need to mask the
|
||||
* two upper-most bits (KBD_RWREG.PARITY_ERR), because we never want to report a parity error.
|
||||
*/
|
||||
var b = this.bPPIB & ~ChipSet.KBD_RWREG.PARITY_ERR;
|
||||
this.messagePort(port, null, addrFrom, "8042_RWREG", ChipSet.MESSAGE_CHIPSET, b);
|
||||
/*
|
||||
* "TEST.09" of the MODEL_5170 BIOS expects the following bit ("REFRESH_BIT") to alternate, so we oblige.
|
||||
*/
|
||||
this.bPPIB ^= ChipSet.KBD_RWREG.REFRESH_BIT;
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* out8042RWReg(port, bOut, addrFrom)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x61)
|
||||
* @param {number} bOut
|
||||
* @param {number|undefined} addrFrom (not defined if the Debugger is trying to read the specified port)
|
||||
*/
|
||||
ChipSet.prototype.out8042RWReg = function(port, bOut, addrFrom)
|
||||
{
|
||||
this.messagePort(port, bOut, addrFrom, "8042_RWREG", ChipSet.MESSAGE_CHIPSET);
|
||||
this.updateSpeaker(bOut);
|
||||
};
|
||||
|
||||
/**
|
||||
* in8042Status(port, addrFrom)
|
||||
*
|
||||
|
|
@ -3975,7 +4044,7 @@ ChipSet.aPortInput5150 = {
|
|||
|
||||
ChipSet.aPortInput5170 = {
|
||||
0x60: ChipSet.prototype.in8042OutBuff,
|
||||
0x61: ChipSet.prototype.inPPIB,
|
||||
0x61: ChipSet.prototype.in8042RWReg,
|
||||
0x64: ChipSet.prototype.in8042Status,
|
||||
0x70: ChipSet.prototype.inCMOSAddr,
|
||||
0x71: ChipSet.prototype.inCMOSData,
|
||||
|
|
@ -4045,7 +4114,7 @@ ChipSet.aPortOutput5150 = {
|
|||
|
||||
ChipSet.aPortOutput5170 = {
|
||||
0x60: ChipSet.prototype.out8042InBuffData,
|
||||
0x61: ChipSet.prototype.outPPIB,
|
||||
0x61: ChipSet.prototype.out8042RWReg,
|
||||
0x64: ChipSet.prototype.out8042InBuffCmd,
|
||||
0x70: ChipSet.prototype.outCMOSAddr,
|
||||
0x71: ChipSet.prototype.outCMOSData,
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ function Debugger(parmsDbg)
|
|||
* or aAddrNextData when no address has been given. doDump() and doUnassemble(), in turn,
|
||||
* update aAddrNextData and aAddrNextCode, respectively, when they're done.
|
||||
*
|
||||
* The format of all aAddr variables is [off, seg], where seg:off is a segmented address if
|
||||
* seg is defined, and off is a physical address if seg is undefined. For some segmented
|
||||
* addresses (eg, breakpoint addresses), we also pre-compute the physical address and save that
|
||||
* in aAddr[2], so that the breakpoint will still operate as intended even if the mode changes
|
||||
* later (eg, from real-mode to protected-mode).
|
||||
* The format of all aAddr variables is [off, seg, addr], where seg:off is the segmented
|
||||
* address and addr is the corresponding physical address. For some segmented addresses
|
||||
* (eg, breakpoint addresses), we pre-compute the physical address and save that in aAddr[2],
|
||||
* so that the breakpoint will still operate as intended even if the mode changes later
|
||||
* (eg, from real-mode to protected-mode).
|
||||
*
|
||||
* Finally, for TEMPORARY breakpoint addresses, we set aAddr[3] to true, so that they can be
|
||||
* automatically cleared when they're hit.
|
||||
|
|
@ -1491,7 +1491,7 @@ if (DEBUGGER) {
|
|||
* 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.aOpcodeHistory[i] = [0, null, 0];
|
||||
}
|
||||
this.iOpcodeHistory = 0;
|
||||
}
|
||||
|
|
@ -1973,7 +1973,7 @@ if (DEBUGGER) {
|
|||
* @this {Debugger}
|
||||
* @param {Array} aAddr
|
||||
* @param {boolean} [fWrite]
|
||||
* @param {number} [cb]
|
||||
* @param {number} [cb] is number of extra bytes to check (0 or 1)
|
||||
* @return {number} is the corresponding physical address, or -1 if there's an error
|
||||
*/
|
||||
Debugger.prototype.getAddr = function(aAddr, fWrite, cb)
|
||||
|
|
@ -1986,15 +1986,11 @@ if (DEBUGGER) {
|
|||
*/
|
||||
var addr = aAddr[2];
|
||||
if (addr == null) {
|
||||
if (aAddr[1] == null) {
|
||||
addr = (aAddr[0] <= this.bus.addrLimit? aAddr[0] : -1);
|
||||
var seg = this.getSegment(aAddr[1]);
|
||||
if (!fWrite) {
|
||||
addr = seg.checkRead(aAddr[0], cb || 0, true);
|
||||
} else {
|
||||
var seg = this.getSegment(aAddr[1]);
|
||||
if (!fWrite) {
|
||||
addr = seg.checkRead(aAddr[0], cb || 0, true);
|
||||
} else {
|
||||
addr = seg.checkWrite(aAddr[0], cb || 0, true);
|
||||
}
|
||||
addr = seg.checkWrite(aAddr[0], cb || 0, true);
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
|
@ -2020,7 +2016,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.getByte = function(aAddr, inc)
|
||||
{
|
||||
var b = 0xff;
|
||||
var addr = this.getAddr(aAddr, false, 1);
|
||||
var addr = this.getAddr(aAddr, false, 0);
|
||||
if (addr >= 0) {
|
||||
b = this.bus.getByteDirect(addr);
|
||||
Component.assert((b == (b & 0xff)), "invalid byte (" + b + ") at address: " + this.hexAddr(aAddr));
|
||||
|
|
@ -2040,7 +2036,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.getWord = function(aAddr, inc)
|
||||
{
|
||||
var w = 0xffff;
|
||||
var addr = this.getAddr(aAddr, false, 2);
|
||||
var addr = this.getAddr(aAddr, false, 1);
|
||||
if (addr >= 0) {
|
||||
w = this.bus.getWordDirect(addr);
|
||||
Component.assert((w == (w & 0xffff)), "invalid word (" + w + ") at address: " + this.hexAddr(aAddr));
|
||||
|
|
@ -2063,7 +2059,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.setByte = function(aAddr, b, inc)
|
||||
{
|
||||
var addr = this.getAddr(aAddr, true, 1);
|
||||
var addr = this.getAddr(aAddr, true, 0);
|
||||
if (addr >= 0) {
|
||||
this.bus.setByteDirect(addr, b);
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
|
|
@ -2081,7 +2077,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.setWord = function(aAddr, w, inc)
|
||||
{
|
||||
var addr = this.getAddr(aAddr, true, 2);
|
||||
var addr = this.getAddr(aAddr, true, 1);
|
||||
if (addr >= 0) {
|
||||
this.bus.setWordDirect(addr, w);
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
|
|
@ -2098,24 +2094,29 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.hexAddr = function(aAddr)
|
||||
{
|
||||
return aAddr[1] == null? ("%" + str.toHex(aAddr[0])) : str.toHexAddr(aAddr[0], aAddr[1]);
|
||||
return aAddr[1] == null? ("%" + str.toHex(aAddr[2])) : str.toHexAddr(aAddr[0], aAddr[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* incAddr(aAddr, inc)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {Array} aAddr containing [off, seg]
|
||||
* @param {Array} aAddr containing [off, seg, addr]
|
||||
* @param {number|undefined} inc contains value to increment by (default is 1)
|
||||
*/
|
||||
Debugger.prototype.incAddr = function(aAddr, inc)
|
||||
{
|
||||
inc = (inc === undefined? 1 : inc);
|
||||
aAddr[0] += inc;
|
||||
if (aAddr[2] != null) aAddr[2] += inc;
|
||||
if (aAddr[0] != (aAddr[0] & 0xffff)) {
|
||||
aAddr[0] = aAddr[0] & 0xffff;
|
||||
aAddr[2] = null;
|
||||
if (aAddr[2] != null) {
|
||||
aAddr[2] += inc;
|
||||
}
|
||||
if (aAddr[1] != null) {
|
||||
aAddr[0] += inc;
|
||||
// TODO: Shouldn't we be using the segment (aAddr[1]) limit instead of 0xffff?
|
||||
if (aAddr[0] != (aAddr[0] & 0xffff)) {
|
||||
aAddr[0] = aAddr[0] & 0xffff;
|
||||
aAddr[2] = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2223,6 +2224,9 @@ if (DEBUGGER) {
|
|||
/**
|
||||
* listBreakpoints(aBreak)
|
||||
*
|
||||
* TODO: We may need to start listing the physical addresses of breakpoints, because
|
||||
* segmented address can be ambiguous.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {Array} aBreak
|
||||
* @return {number} of breakpoints listed, 0 if none
|
||||
|
|
@ -2348,7 +2352,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.getInstruction = function(aAddr, sComment, nSequence)
|
||||
{
|
||||
var aAddrIns = this.newAddr(aAddr[0], aAddr[1]);
|
||||
var aAddrIns = this.newAddr(aAddr[0], aAddr[1], aAddr[2]);
|
||||
|
||||
var bOpcode = this.getByte(aAddr, 1);
|
||||
var aOpDesc = this.aaOpDescs[bOpcode];
|
||||
|
|
@ -2716,8 +2720,8 @@ if (DEBUGGER) {
|
|||
/**
|
||||
* parseAddr(sAddr, type)
|
||||
*
|
||||
* As discussed above, the format of all aAddr variables is [off, seg]; they represent a segmented
|
||||
* address (seg:off) when seg is defined or a physical address (off) when seg is undefined (or null).
|
||||
* As discussed above, the format of aAddr variables is [off, seg, addr]; they represent a segmented
|
||||
* address (seg:off) when seg is defined or a physical address (addr) when seg is undefined (or null).
|
||||
*
|
||||
* To create a segmented address, specify two values separated by ":"; for a physical address, use
|
||||
* a "%" prefix. We check for ":" after "%", so if for some strange reason you specify both, the
|
||||
|
|
@ -2743,13 +2747,14 @@ if (DEBUGGER) {
|
|||
{
|
||||
var aAddrNext = (type == Debugger.ADDR_DATA? this.aAddrNextData : this.aAddrNextCode);
|
||||
|
||||
var off = aAddrNext[0], seg = aAddrNext[1];
|
||||
var off = aAddrNext[0], seg = aAddrNext[1], addr = aAddrNext[2];
|
||||
|
||||
if (sAddr !== undefined) {
|
||||
|
||||
if (sAddr.charAt(0) == '%') {
|
||||
sAddr = sAddr.substr(1);
|
||||
seg = null;
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
var aAddr = this.findSymbolAddr(sAddr);
|
||||
|
|
@ -2757,14 +2762,18 @@ if (DEBUGGER) {
|
|||
|
||||
var iColon = sAddr.indexOf(":");
|
||||
if (iColon < 0) {
|
||||
off = this.parseValue(sAddr);
|
||||
if (addr == null) {
|
||||
off = this.parseValue(sAddr);
|
||||
} else {
|
||||
addr = this.parseValue(sAddr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
seg = this.parseValue(sAddr.substring(0, iColon));
|
||||
off = this.parseValue(sAddr.substring(iColon + 1));
|
||||
}
|
||||
}
|
||||
return [off, seg];
|
||||
return [off, seg, addr];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3502,7 +3511,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
iHistory -= n;
|
||||
if (iHistory < 0) {
|
||||
if (aHistory[aHistory.length - 1].length) {
|
||||
if (aHistory[aHistory.length - 1][1] != null) {
|
||||
iHistory += aHistory.length;
|
||||
} else {
|
||||
n = iHistory + n;
|
||||
|
|
@ -3514,10 +3523,10 @@ if (DEBUGGER) {
|
|||
}
|
||||
while (cLines && iHistory != this.iOpcodeHistory) {
|
||||
var aAddr = aHistory[iHistory];
|
||||
if (!aAddr.length) break;
|
||||
if (aAddr[1] == null) 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 was a reference, not a copy, and we don't want getInstruction() modifying the original.
|
||||
*/
|
||||
aAddr = this.newAddr(aAddr[0], aAddr[1], aAddr[2]);
|
||||
this.println(this.getInstruction(aAddr, "history", -n));
|
||||
|
|
@ -4229,7 +4238,7 @@ if (DEBUGGER) {
|
|||
return;
|
||||
|
||||
if (n === undefined) n = 1;
|
||||
var aAddrEnd = this.newAddr(this.bus.addrLimit, aAddr[1]);
|
||||
var aAddrEnd = this.newAddr(0xffff, aAddr[1], this.bus.addrLimit);
|
||||
|
||||
if (sAddrEnd !== undefined) {
|
||||
aAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR_CODE);
|
||||
|
|
@ -4250,7 +4259,7 @@ if (DEBUGGER) {
|
|||
|
||||
var fBlank = (aAddr[0] != this.aAddrNextCode[0]);
|
||||
|
||||
while (n-- && aAddr[0] < aAddrEnd[0]) {
|
||||
while (n-- && (aAddr[1] != null? (aAddr[0] < aAddrEnd[0]) : (aAddr[2] < aAddrEnd[2]))) {
|
||||
/*
|
||||
* I pass nCycles instead of cInstructions to getInstruction() now, to assist with visual
|
||||
* verification of the accuracy (or inaccuracy) of instruction cycle counts.
|
||||
|
|
|
|||
|
|
@ -1374,9 +1374,14 @@ FDC.prototype.outFDCControl = function(port, bOut, addrFrom)
|
|||
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("FDC.intBIOS(AX=" + str.toHexWord(this.cpu.regAX) + ",DL=" + str.toHexByte(DL) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
|
||||
this.dbg.message("FDC.intBIOS(AH=" + str.toHexByte(AH) + ",D=" + str.toHexByte(DL) + ",C=" + str.toHexByte(CH) + ",H=" + str.toHexByte(DH) + ",S=" + str.toHexByte(CL) + ",N=" + str.toHexByte(AL) + ") at " + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.segCS.sel));
|
||||
// this.cpu.haltCPU();
|
||||
this.cpu.addInterruptReturn(addr, function (fdc, nCycles) {
|
||||
return function onBIOSDisketteReturn(nLevel) {
|
||||
|
|
|
|||
|
|
@ -1810,6 +1810,9 @@ X86CPU.prototype.modEAByteDisabled = function(seg, off)
|
|||
{
|
||||
this.segEA = seg;
|
||||
this.offEA = off;
|
||||
/*
|
||||
* TODO: Should this not also set regEA? Optimization or oversight?
|
||||
*/
|
||||
this.regEAWrite = seg.base + off;
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -1909,13 +1912,6 @@ X86CPU.prototype.modEAWordEnabled = function(seg, off)
|
|||
X86CPU.prototype.setEAByteEnabled = function(b)
|
||||
{
|
||||
if (!FASTDISABLE && (this.opFlags & X86.OPFLAG.NOWRITE)) return;
|
||||
/*
|
||||
* TODO: We used to call checkEAWrite(this.segEA, this.offEA, 1), which would simply return
|
||||
* regEAWrite in real-mode (or recalculate the address in prot-mode and verify that it still matched
|
||||
* regEAWrite); determine if there's any value in reinstating that feature with the new X86Seg class.
|
||||
*
|
||||
* this.setByte(this.checkEAWrite(this.segEA, this.offEA, 1), b);
|
||||
*/
|
||||
this.setByte(this.segEA.checkWrite(this.offEA, 1), b);
|
||||
};
|
||||
|
||||
|
|
@ -1928,13 +1924,6 @@ X86CPU.prototype.setEAByteEnabled = function(b)
|
|||
X86CPU.prototype.setEAWordEnabled = function(w)
|
||||
{
|
||||
if (!FASTDISABLE && (this.opFlags & X86.OPFLAG.NOWRITE)) return;
|
||||
/*
|
||||
* TODO: We used to call checkEAWrite(this.segEA, this.offEA, 2), which would simply return
|
||||
* regEAWrite in real-mode (or recalculate the address in prot-mode and verify that it still matched
|
||||
* regEAWrite); determine if there's any value in reinstating that feature with the new X86Seg class.
|
||||
*
|
||||
* this.setByte(this.checkEAWrite(this.segEA, this.offEA, 1), b);
|
||||
*/
|
||||
this.setWord(this.segEA.checkWrite(this.offEA, 2), w);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
@ -179,7 +179,7 @@ X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
@ -193,7 +193,7 @@ X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
@ -210,7 +210,7 @@ X86Seg.checkReadProtEnabled = function checkReadProtEnabled(off, cb, fSuppress)
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
@ -227,7 +227,7 @@ X86Seg.checkReadProtDisabled = function checkReadProtDisabled(off, cb, fSuppress
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
@ -244,7 +244,7 @@ X86Seg.checkWriteProtEnabled = function checkWriteProtEnabled(off, cb, fSuppress
|
|||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off is a segment-relative offset
|
||||
* @param {number} cb
|
||||
* @param {number} cb is number of extra bytes to check (0 or 1)
|
||||
* @param {boolean} [fSuppress] is true to suppress any errors
|
||||
* @return {number} corresponding physical address if valid, -1 if not
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue