v1.19.5 update (fixes a CALLF bug and a VGA ATC bug, and adds improved WDEB386-style load notifications)
This commit is contained in:
parent
cb9e330cfa
commit
b00b524e01
8 changed files with 1349 additions and 1267 deletions
|
|
@ -1275,6 +1275,7 @@ if (DEBUGGER) {
|
|||
|
||||
if (Interrupts.WINDBG.ENABLED || Interrupts.WINDBGRM.ENABLED) {
|
||||
this.fWinDbg = null;
|
||||
this.cpu.addIntNotify(Interrupts.WINCB.VECTOR, this.intWindowsCallBack.bind(this));
|
||||
this.cpu.addIntNotify(Interrupts.WINDBG.VECTOR, this.intWindowsDebugger.bind(this));
|
||||
}
|
||||
if (Interrupts.WINDBGRM.ENABLED) {
|
||||
|
|
@ -1336,13 +1337,13 @@ if (DEBUGGER) {
|
|||
var len = this.getLong(dbgAddr, 4);
|
||||
var dbgAddrDevice = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var dbgAddrModule = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
sel = this.getShort(dbgAddr, 2) || sel;
|
||||
// sel = this.getShort(dbgAddr, 2) || sel;
|
||||
var sModule = this.getSZ(dbgAddrModule).toUpperCase();
|
||||
var sDevice = this.getSZ(dbgAddrDevice).toUpperCase();
|
||||
var sSection = (fCode? "_CODE" : "_DATA") + str.toHex(nSeg, 2);
|
||||
if (fPrint) {
|
||||
/*
|
||||
* Mimic WDEB386 output
|
||||
* Mimics WDEB386 output, except that WDEB386 only displays a linear address, omitting the selector.
|
||||
*/
|
||||
this.println(sModule + '!' + sDevice + "!undefined " + (fCode? "code" : "data") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(sel, 4) + ':' + str.toHex(off) + " len " + str.toHex(len));
|
||||
}
|
||||
|
|
@ -1351,6 +1352,55 @@ if (DEBUGGER) {
|
|||
this.addSymbols(sel, off, len, aSymbols);
|
||||
};
|
||||
|
||||
/**
|
||||
* intWindowsCallBack()
|
||||
*
|
||||
* This intercepts calls to Windows callback addresses, which use INT 0x30 (aka Transfer Space Fault).
|
||||
*
|
||||
* We're only interested in one particular callback: the one specifying VW32_Int41Dispatch (0x002A002A)
|
||||
* in EAX.
|
||||
*
|
||||
* At the time that INT 0x30 occurs, a far 32-bit call has been made, preceded by a near 32-bit call,
|
||||
* preceded by a 32-bit push of the Windows Debugger function # that would normally be in EAX if this had
|
||||
* been an actual INT 0x41.
|
||||
*
|
||||
* NOTE: Regardless whether we're "handling" INT 0x41 or merely "monitoring" INT 0x41, as far as THIS
|
||||
* interrupt is concerned, we always let the system process it, because execution never continues at the
|
||||
* instruction following an INT 0x30; in fact, execution doesn't even continue after the far 32-bit call
|
||||
* (even though the kernel places a "RET 4" after that call). So, rather than recreate all that automatic
|
||||
* address popping, we let the system do it for us, since it's designed to work whether a debugger (eg,
|
||||
* WDEB386's Debug VXD) is installed or not.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} addr
|
||||
* @return {boolean} true to proceed with the INT 0x30 software interrupt
|
||||
*/
|
||||
Debugger.prototype.intWindowsCallBack = function(addr)
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
|
||||
if (this.fWinDbg != null && cpu.regEAX == 0x002A002A) {
|
||||
|
||||
var DX = cpu.regEDX & 0xffff;
|
||||
var SI = cpu.regESI & 0xffff;
|
||||
var dbgAddr = this.newAddr(cpu.getSP() + 0x0C, cpu.getSS());
|
||||
var EAX = this.getLong(dbgAddr);
|
||||
|
||||
switch(EAX) {
|
||||
case Interrupts.WINDBG.LOAD_SEG32:
|
||||
/*
|
||||
* SI == segment type:
|
||||
* 0x0 code selector
|
||||
* 0x1 data selector
|
||||
* DX:EBX -> D386_Device_Params structure (see addSectionInfo() for details)
|
||||
*/
|
||||
this.addSectionInfo(this.newAddr(cpu.regEBX, DX), !SI, this.fWinDbg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* intWindowsDebugger()
|
||||
*
|
||||
|
|
@ -1430,7 +1480,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbg) {
|
||||
if (MAXDEBUG && this.fWinDbg) {
|
||||
this.println("INT 0x41: " + str.toHexWord(AX));
|
||||
}
|
||||
break;
|
||||
|
|
@ -1561,7 +1611,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbgRM) {
|
||||
if (MAXDEBUG && this.fWinDbgRM) {
|
||||
this.println("INT 0x68: " + str.toHexByte(AH));
|
||||
}
|
||||
break;
|
||||
|
|
@ -1616,7 +1666,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
var cpu = this.cpu;
|
||||
var AL = cpu.regEAX & 0xff;
|
||||
this.println("INT 0x68 callback: " + str.toHexByte(AL));
|
||||
if (MAXDEBUG) this.println("INT 0x68 callback: " + str.toHexByte(AL));
|
||||
if (AL == 5) {
|
||||
cpu.regECX = cpu.regESI = 0; // our in-machine debugger footprint is zero
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,10 @@ var Interrupts = {
|
|||
MOUSE: 0x33,
|
||||
ALT_DISK: 0x40, // HDC BIOS saves original FDC BIOS vector here
|
||||
ALT_VIDEO: 0x6D, // IBM VGA BIOS saves original video BIOS vector here
|
||||
WINDBG: { // Windows Debugger protected-mode interface
|
||||
WINCB: {
|
||||
VECTOR: 0x30 // Windows PM call-back interface (aka Transfer Space Fault)
|
||||
},
|
||||
WINDBG: { // Windows Debugger protected-mode interface
|
||||
VECTOR: 0x41, // (AX==command)
|
||||
IS_LOADED: 0x004F, // DS_DebLoaded
|
||||
LOADED: 0xF386, // DS_DebPresent (returned in AX if Windows Debugger loaded)
|
||||
|
|
@ -69,7 +72,7 @@ var Interrupts = {
|
|||
LOAD_SEG32: 0x0150, // DS_LoadSeg_32 (SI==0 if code, 1 if data; DX:EBX->D386_Device_Params)
|
||||
ENABLED: true // support for WINDBG interrupts can be disabled (but NOT if WINDBGRM is enabled)
|
||||
},
|
||||
WINDBGRM: { // Windows Debugger real-mode interface
|
||||
WINDBGRM: { // Windows Debugger real-mode interface
|
||||
VECTOR: 0x68, // (AH==command)
|
||||
IS_LOADED: 0x43, // D386_Identify
|
||||
LOADED: 0xF386, // D386_Id (returned in AX if Windows Debugger loaded)
|
||||
|
|
@ -77,7 +80,7 @@ var Interrupts = {
|
|||
LOAD_SEG: 0x50, // D386_Load_Segment (AL=segment type, ES:DI->D386_Device_Params)
|
||||
ENABLED: true // support for WINDBGRM interrupts can be disabled
|
||||
},
|
||||
FUNCS: {} // filled in only if DEBUGGER is true
|
||||
FUNCS: {} // filled in only if DEBUGGER is true
|
||||
};
|
||||
|
||||
if (DEBUGGER) {
|
||||
|
|
|
|||
|
|
@ -2461,6 +2461,7 @@ Card.prototype.dumpVideoCard = function()
|
|||
this.dumpRegs(" GRC", this.regGRCIndx, this.regGRCData, this.asGRCRegs);
|
||||
this.dumpRegs(" SEQ", this.regSEQIndx, this.regSEQData, this.asSEQRegs);
|
||||
this.dumpRegs(" ATC", this.regATCIndx, this.regATCData, this.asATCRegs);
|
||||
this.dumpRegs(" ATCINDX", this.regATCIndx);
|
||||
this.dbg.println(" ATCDATA: " + this.fATCData);
|
||||
this.dumpRegs(" FEAT", this.regFeat);
|
||||
this.dumpRegs(" MISC", this.regMisc);
|
||||
|
|
@ -5894,20 +5895,44 @@ Video.prototype.outFeat = function(port, bOut, addrFrom)
|
|||
};
|
||||
|
||||
/**
|
||||
* inATC(port, addrFrom)
|
||||
* inATCIndx(port, addrFrom)
|
||||
*
|
||||
* Technically, port 0x3C0 is readable only on a VGA, but we allow reads on an EGA as well,
|
||||
* primarily for debugging purposes. Moreover, ATC port reads do NOT toggle the ATC address/data
|
||||
* flip-flop; only writes have that effect.
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {number} port (0x3C0)
|
||||
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
|
||||
* @return {number}
|
||||
*/
|
||||
Video.prototype.inATC = function(port, addrFrom)
|
||||
Video.prototype.inATCIndx = function(port, addrFrom)
|
||||
{
|
||||
var b = this.cardEGA.fATCData? this.cardEGA.regATCData[this.cardEGA.regATCIndx & Card.ATC.INDX_MASK] : this.cardEGA.regATCIndx;
|
||||
var b = this.cardEGA.regATCIndx;
|
||||
if (!addrFrom || this.messageEnabled()) {
|
||||
this.printMessageIO(Card.ATC.PORT, null, addrFrom, "ATC." + (this.cardEGA.fATCData? this.cardEGA.asATCRegs[this.cardEGA.regATCIndx & Card.ATC.INDX_MASK] : "INDX"), b);
|
||||
this.printMessageIO(Card.ATC.PORT, null, addrFrom, "ATC.INDX", b);
|
||||
}
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* inATCData(port, addrFrom)
|
||||
*
|
||||
* Technically, port 0x3C0 is readable only on a VGA, but we allow reads on an EGA as well,
|
||||
* primarily for debugging purposes. Moreover, ATC port reads do NOT toggle the ATC address/data
|
||||
* flip-flop; only writes have that effect.
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {number} port (0x3C1)
|
||||
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
|
||||
* @return {number}
|
||||
*/
|
||||
Video.prototype.inATCData = function(port, addrFrom)
|
||||
{
|
||||
var b = this.cardEGA.regATCData[this.cardEGA.regATCIndx & Card.ATC.INDX_MASK];
|
||||
if (!addrFrom || this.messageEnabled()) {
|
||||
this.printMessageIO(Card.ATC.PORT, null, addrFrom, "ATC." + this.cardEGA.asATCRegs[this.cardEGA.regATCIndx & Card.ATC.INDX_MASK], b);
|
||||
}
|
||||
this.cardEGA.fATCData = !this.cardEGA.fATCData;
|
||||
return b;
|
||||
};
|
||||
|
||||
|
|
@ -6880,8 +6905,8 @@ Video.aCGAPortOutput = {
|
|||
};
|
||||
|
||||
Video.aEGAPortInput = {
|
||||
0x3C0: Video.prototype.inATC, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
0x3C1: Video.prototype.inATC, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
0x3C0: Video.prototype.inATCIndx, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
0x3C1: Video.prototype.inATCData, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
0x3C2: Video.prototype.inStatus0,
|
||||
0x3C4: Video.prototype.inSEQIndx, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
0x3C5: Video.prototype.inSEQData, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too
|
||||
|
|
@ -6892,7 +6917,7 @@ Video.aEGAPortInput = {
|
|||
/*
|
||||
* WARNING: Unlike the EGA, a standard VGA does not support writes to 0x3C1, but it's easier for me to leave that
|
||||
* ability in place, treating the VGA as a superset of the EGA as much as possible; will any code break because word
|
||||
* I/O to port 0x3C0 actually works? Possibly, but highly unlikely.
|
||||
* OUTs to port 0x3C0 (and/or byte OUTs to port 0x3C1) actually work? Possibly, but highly unlikely.
|
||||
*/
|
||||
Video.aEGAPortOutput = {
|
||||
0x3BA: Video.prototype.outFeat,
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@ X86.fnCALLF = function CALLF(off, sel)
|
|||
{
|
||||
var oldCS = this.getCS();
|
||||
var oldIP = this.getIP();
|
||||
var oldSize = this.segCS.sizeData;
|
||||
var oldSize = (I386? this.sizeData : 2);
|
||||
if (this.setCSIP(off, sel, true) != null) {
|
||||
this.pushData(oldCS, oldSize);
|
||||
this.pushData(oldIP, oldSize);
|
||||
|
|
@ -1415,6 +1415,10 @@ X86.fnINT = function INT(nIDT, nError, nCycles)
|
|||
var oldIP = this.getIP();
|
||||
var addr = this.segCS.loadIDT(nIDT);
|
||||
if (addr !== X86.ADDR_INVALID) {
|
||||
/*
|
||||
* TODO: Harmonize this with the code in fnCALLF(), which relies on the OPERAND size in
|
||||
* effect at the time of the call, NOT the size of the new segCS.
|
||||
*/
|
||||
var size = this.segCS.sizeFrame;
|
||||
this.pushData(oldPS, size);
|
||||
this.pushData(oldCS, size);
|
||||
|
|
|
|||
Loading…
Reference in a new issue