v1.19.3: More Windows 95-related updates
This commit is contained in:
parent
77b7ebc85c
commit
1a67c7acbd
33 changed files with 8907 additions and 1741 deletions
|
|
@ -56,7 +56,7 @@ if (DEBUGGER) {
|
|||
* off offset, if any
|
||||
* sel selector, if any (if null, addr should be set to a linear address)
|
||||
* addr linear address, if any (if null, addr will be recomputed from sel:off)
|
||||
* fProt true if protected-mode address
|
||||
* type one of the Debugger.ADDR.* values
|
||||
* fData32 true if 32-bit operand size in effect
|
||||
* fAddr32 true if 32-bit address size in effect
|
||||
* cOverrides non-zero if any overrides were processed with this address
|
||||
|
|
@ -69,7 +69,7 @@ if (DEBUGGER) {
|
|||
* off:(number|null|undefined),
|
||||
* sel:(number|null|undefined),
|
||||
* addr:(number|null|undefined),
|
||||
* fProt:(boolean|undefined),
|
||||
* type:(number|undefined),
|
||||
* fData32:(boolean|undefined),
|
||||
* fAddr32:(boolean|undefined),
|
||||
* cOverrides:(number|undefined),
|
||||
|
|
@ -294,10 +294,27 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/*
|
||||
* Address types for parseAddr(), to help choose between dbgAddrNextCode and dbgAddrNextData
|
||||
* Supported address types; the type field in a DbgAddr object may contain ONE of:
|
||||
*
|
||||
* NONE, REAL, PROT, V86, LINEAR or PHYSICAL
|
||||
*
|
||||
* along with ONE of:
|
||||
*
|
||||
* CODE or DATA
|
||||
*
|
||||
* REAL and V86 addresses are specifed with a '&' prefix, PROT addresses with a '#' prefix,
|
||||
* LINEAR addresses with '%', and PHYSICAL addresses with '%%'.
|
||||
*/
|
||||
Debugger.ADDR_CODE = 1;
|
||||
Debugger.ADDR_DATA = 2;
|
||||
Debugger.ADDR = {
|
||||
NONE: 0x00,
|
||||
REAL: 0x01,
|
||||
PROT: 0x02,
|
||||
V86: 0x03,
|
||||
LINEAR: 0x04,
|
||||
PHYSICAL: 0x05,
|
||||
CODE: 0x10,
|
||||
DATA: 0x20
|
||||
};
|
||||
|
||||
/*
|
||||
* Instruction ordinals
|
||||
|
|
@ -1247,9 +1264,10 @@ if (DEBUGGER) {
|
|||
this.messageDump(Messages.TSS, function onDumpTSS(s) { dbg.dumpTSS(s); });
|
||||
this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); });
|
||||
|
||||
this.fWinDbg = this.dbgAddrWinDbg = null;
|
||||
this.cpu.addIntNotify(Interrupts.WINDBG.VECTOR, this.intWindowsDebugger.bind(this));
|
||||
|
||||
if (Interrupts.WINDBG.ENABLED) {
|
||||
this.fWinDbg = null;
|
||||
this.cpu.addIntNotify(Interrupts.WINDBG.VECTOR, this.intWindowsDebugger.bind(this));
|
||||
}
|
||||
if (Interrupts.WINDBGRM.ENABLED) {
|
||||
this.fWinDbgRM = null;
|
||||
this.cpu.addIntNotify(Interrupts.WINDBGRM.VECTOR, this.intWindowsDebuggerRM.bind(this));
|
||||
|
|
@ -1258,72 +1276,79 @@ if (DEBUGGER) {
|
|||
this.setReady();
|
||||
};
|
||||
|
||||
/**
|
||||
* intWindowsDebugger()
|
||||
*
|
||||
* This intercepts calls to the Windows Debugger protected-mode interface (INT 0x41).
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} addr
|
||||
* @return {boolean} true to proceed with the INT 0x41 software interrupt, false to skip
|
||||
*/
|
||||
Debugger.prototype.intWindowsDebugger = function(addr)
|
||||
{
|
||||
if (this.fWinDbg === false) return true;
|
||||
if (Interrupts.WINDBG.ENABLED) {
|
||||
/**
|
||||
* intWindowsDebugger()
|
||||
*
|
||||
* This intercepts calls to the Windows Debugger protected-mode interface (INT 0x41).
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} addr
|
||||
* @return {boolean} true to proceed with the INT 0x41 software interrupt, false to skip
|
||||
*/
|
||||
Debugger.prototype.intWindowsDebugger = function(addr)
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
var AX = cpu.regEAX & 0xffff;
|
||||
var BX = cpu.regEBX & 0xffff;
|
||||
var CX = cpu.regECX & 0xffff;
|
||||
var DX = cpu.regEDX & 0xffff;
|
||||
var SI = cpu.regESI & 0xffff;
|
||||
var DI = cpu.regEDI & 0xffff;
|
||||
var ES = cpu.segES.sel;
|
||||
|
||||
var seg, limit;
|
||||
var cpu = this.cpu;
|
||||
var AX = cpu.regEAX & 0xffff;
|
||||
var BX = cpu.regEBX & 0xffff;
|
||||
var CX = cpu.regECX & 0xffff;
|
||||
var DX = cpu.regEDX & 0xffff;
|
||||
var SI = cpu.regESI & 0xffff;
|
||||
var DI = cpu.regEDI & 0xffff;
|
||||
var ES = cpu.segES.sel;
|
||||
var seg, limit, sModule;
|
||||
|
||||
if (!this.fWinDbg) {
|
||||
if (AX == Interrupts.WINDBG.IS_LOADED) {
|
||||
/*
|
||||
* We're only going to respond to this function if no one else did, in which case,
|
||||
* we'll set fWinDbg to true and handle additional notifications.
|
||||
*/
|
||||
cpu.addIntReturn(addr, function(dbg) {
|
||||
return function onInt41Return(nLevel) {
|
||||
if ((cpu.regEAX & 0xffff) != Interrupts.WINDBG.LOADED) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
dbg.println("INT 0x41 processing enabled");
|
||||
dbg.dbgAddrWinDbg = dbg.newAddr();
|
||||
dbg.fWinDbg = true;
|
||||
} else {
|
||||
dbg.println("INT 0x41 processing disabled");
|
||||
dbg.fWinDbg = false;
|
||||
}
|
||||
};
|
||||
}(this));
|
||||
if (this.fWinDbg == null) {
|
||||
if (AX == Interrupts.WINDBG.IS_LOADED) {
|
||||
/*
|
||||
* We're only going to respond to this function if no one else did, in which case,
|
||||
* we'll set fWinDbg to true and handle additional notifications.
|
||||
*/
|
||||
cpu.addIntReturn(addr, function(dbg) {
|
||||
return function onInt41Return(nLevel) {
|
||||
if ((cpu.regEAX & 0xffff) != Interrupts.WINDBG.LOADED) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
dbg.println("INT 0x41 handling enabled");
|
||||
dbg.fWinDbg = true;
|
||||
} else {
|
||||
dbg.println("INT 0x41 monitoring enabled");
|
||||
dbg.fWinDbg = false;
|
||||
}
|
||||
};
|
||||
}(this));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(AX) {
|
||||
case Interrupts.WINDBG.IS_LOADED:
|
||||
if (this.fWinDbg) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
}
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBG.LOAD_SEG:
|
||||
/*
|
||||
* The following output should completely mimic WDEB386....
|
||||
*/
|
||||
sModule = this.getSZ(this.newAddr(DI, ES));
|
||||
limit = (seg = this.getSegment(CX))? seg.limit : 0;
|
||||
if (!this.fWinDbg) {
|
||||
this.println(sModule + "!undefined " + ((SI & 0x1)? "data" : "code") + '(' + str.toHex(BX+1, 4) + ")=#" + str.toHex(CX, 4) + " len " + str.toHex(limit+1));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbg) {
|
||||
this.println("INT 0x41: " + str.toHexWord(AX));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(AX) {
|
||||
case Interrupts.WINDBG.IS_LOADED:
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBG.LOAD_SEG:
|
||||
/*
|
||||
* The following output should completely mimic WDEB386....
|
||||
*/
|
||||
limit = (seg = this.getSegment(CX))? seg.limit : 0;
|
||||
this.println(this.getSZ(this.setAddr(this.dbgAddrWinDbg, DI, ES)) + "!undefined " + ((SI & 0x1)? "data" : "code") + '(' + str.toHex(BX+1, 4) + ")=#" + str.toHex(CX, 4) + " len " + str.toHex(limit+1));
|
||||
break;
|
||||
|
||||
default:
|
||||
// this.println("INT 0x41: " + str.toHexWord(AX));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (Interrupts.WINDBGRM.ENABLED) {
|
||||
/**
|
||||
|
|
@ -1337,12 +1362,14 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.intWindowsDebuggerRM = function(addr)
|
||||
{
|
||||
if (this.fWinDbgRM === false) return true;
|
||||
|
||||
var dbgAddr;
|
||||
var cpu = this.cpu;
|
||||
var AH = this.cpu.regEAX & 0xff;
|
||||
var AL = cpu.regEAX & 0xff;
|
||||
var AH = (cpu.regEAX >> 8) & 0xff;
|
||||
var DI = cpu.regEDI & 0xffff;
|
||||
var ES = cpu.segES.sel;
|
||||
|
||||
if (!this.fWinDbgRM) {
|
||||
if (this.fWinDbgRM == null) {
|
||||
if (AH == Interrupts.WINDBGRM.IS_LOADED) {
|
||||
/*
|
||||
* We're only going to respond to this function if no one else did, in which case,
|
||||
|
|
@ -1352,10 +1379,10 @@ if (DEBUGGER) {
|
|||
return function onInt68Return(nLevel) {
|
||||
if ((cpu.regEAX & 0xffff) != Interrupts.WINDBGRM.LOADED) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
dbg.println("INT 0x68 processing enabled");
|
||||
dbg.println("INT 0x68 handling enabled");
|
||||
dbg.fWinDbgRM = true;
|
||||
} else {
|
||||
dbg.println("INT 0x68 processing disabled");
|
||||
dbg.println("INT 0x68 monitoring enabled");
|
||||
dbg.fWinDbgRM = false;
|
||||
}
|
||||
};
|
||||
|
|
@ -1366,11 +1393,67 @@ if (DEBUGGER) {
|
|||
|
||||
switch(AH) {
|
||||
case Interrupts.WINDBGRM.IS_LOADED:
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
if (this.fWinDbgRM) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
}
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBGRM.LOAD_SEG:
|
||||
if (AL == 0x20) {
|
||||
/*
|
||||
* Real-mode EXE
|
||||
* CX == paragraph
|
||||
* ES:DI -> module name
|
||||
*/
|
||||
}
|
||||
else if (AL < 0x80) {
|
||||
/*
|
||||
* AL == segment type:
|
||||
* 0x00 code selector
|
||||
* 0x01 data selector
|
||||
* 0x10 code segment
|
||||
* 0x11 data segment
|
||||
* 0x40 code segment & sel
|
||||
* 0x41 data segment & sel
|
||||
* BX == segment #
|
||||
* CX == actual segment/selector
|
||||
* DX == actual selector (if 0x40 or 0x41)
|
||||
* ES:DI -> module name
|
||||
*/
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* AL == segment type:
|
||||
* 0x80 device driver code seg
|
||||
* 0x81 device driver data seg
|
||||
* ES:DI -> D386_Device_Params structure:
|
||||
* DD_logical_seg dw ? ; logical segment # from map
|
||||
* DD_actual_sel dw ? ; actual selector value
|
||||
* DD_base dd ? ; linear address offset for start of segment
|
||||
* DD_length dd ? ; actual length of segment
|
||||
* DD_name df ? ; 16:32 ptr to null terminated device name
|
||||
* DD_sym_name df ? ; 16:32 ptr to null terminated symbolic module name (i.e. Win386)
|
||||
* DD_alias_sel dw ? ; alias selector value (0 = none)
|
||||
*/
|
||||
dbgAddr = this.newAddr(DI, ES);
|
||||
var nSeg = this.getShort(dbgAddr, 2);
|
||||
var sel = this.getShort(dbgAddr, 2);
|
||||
var off = this.getLong(dbgAddr, 4);
|
||||
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));
|
||||
var selAlias = this.getShort(dbgAddr, 2) || sel;
|
||||
if (!this.fWinDbgRM) {
|
||||
this.log(this.getSZ(dbgAddrModule) + '!' + this.getSZ(dbgAddrDevice) + "!undefined " + ((AL & 0x1)? "data" : "code") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(selAlias, 4) + ':' + str.toHex(off) + " len " + str.toHex(len));
|
||||
}
|
||||
}
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
break;
|
||||
|
||||
default:
|
||||
// this.println("INT 0x68: " + str.toHexByte(AH));
|
||||
if (this.fWinDbgRM) {
|
||||
this.log("INT 0x68: " + str.toHexByte(AH));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1488,18 +1571,29 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* getProtMode()
|
||||
* getCurrentMode()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {boolean}
|
||||
* @return {boolean} (true if protected mode, false if not)
|
||||
*/
|
||||
Debugger.prototype.getProtMode = function()
|
||||
Debugger.prototype.getCurrentMode = function()
|
||||
{
|
||||
return this.cpu && !!(this.cpu.regCR0 & X86.CR0.MSW.PE) && !(this.cpu.regPS & X86.PS.VM);
|
||||
};
|
||||
|
||||
/**
|
||||
* getSegment(sel, fProt)
|
||||
* getCurrentType()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {number}
|
||||
*/
|
||||
Debugger.prototype.getCurrentType = function()
|
||||
{
|
||||
return this.getCurrentMode()? Debugger.ADDR.PROT : Debugger.ADDR.REAL;
|
||||
};
|
||||
|
||||
/**
|
||||
* getSegment(sel, type)
|
||||
*
|
||||
* If the selector matches that of any of the CPU segment registers, then return the CPU's segment
|
||||
* register, instead of using our own segDebugger segment register. This makes it possible for us to
|
||||
|
|
@ -1509,15 +1603,16 @@ if (DEBUGGER) {
|
|||
*
|
||||
* @this {Debugger}
|
||||
* @param {number|null|undefined} sel
|
||||
* @param {boolean} [fProt]
|
||||
* @param {number} [type] (defaults to getCurrentType())
|
||||
* @return {X86Seg|null} seg
|
||||
*/
|
||||
Debugger.prototype.getSegment = function(sel, fProt)
|
||||
Debugger.prototype.getSegment = function(sel, type)
|
||||
{
|
||||
var fProtMode = this.getProtMode();
|
||||
if (fProt === undefined) fProt = fProtMode;
|
||||
var typeDefault = this.getCurrentType();
|
||||
|
||||
if (fProt == fProtMode) {
|
||||
if (!type) type = typeDefault;
|
||||
|
||||
if (type == typeDefault) {
|
||||
if (sel === this.cpu.getCS()) return this.cpu.segCS;
|
||||
if (sel === this.cpu.getDS()) return this.cpu.segDS;
|
||||
if (sel === this.cpu.getES()) return this.cpu.segES;
|
||||
|
|
@ -1530,10 +1625,10 @@ if (DEBUGGER) {
|
|||
* Even if nSuppressBreaks is set, we'll allow the call in real-mode,
|
||||
* because a loadReal() request using segDebugger should generally be safe.
|
||||
*/
|
||||
if (this.nSuppressBreaks && fProt || !this.segDebugger) return null;
|
||||
if (this.nSuppressBreaks && type == Debugger.ADDR.PROT || !this.segDebugger) return null;
|
||||
}
|
||||
var seg = this.segDebugger;
|
||||
if (!fProt) {
|
||||
if (type != Debugger.ADDR.PROT) {
|
||||
seg.loadReal(sel);
|
||||
seg.limit = 0xffff; // although an ACTUAL real-mode segment load would not modify the limit,
|
||||
seg.offMax = 0x10000; // proper segDebugger operation requires that we update the limit ourselves
|
||||
|
|
@ -1564,7 +1659,7 @@ if (DEBUGGER) {
|
|||
if (addr == null) {
|
||||
addr = X86.ADDR_INVALID;
|
||||
if (dbgAddr) {
|
||||
var seg = this.getSegment(dbgAddr.sel, dbgAddr.fProt);
|
||||
var seg = this.getSegment(dbgAddr.sel, dbgAddr.type);
|
||||
if (seg) {
|
||||
if (!fWrite) {
|
||||
addr = seg.checkReadDebugger(dbgAddr.off || 0, nb || 1);
|
||||
|
|
@ -1690,7 +1785,7 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* newAddr(off, sel, addr, fProt, fData32, fAddr32)
|
||||
* newAddr(off, sel, addr, type, fData32, fAddr32)
|
||||
*
|
||||
* Returns a NEW DbgAddr object, initialized with specified values and/or defaults.
|
||||
*
|
||||
|
|
@ -1698,18 +1793,43 @@ if (DEBUGGER) {
|
|||
* @param {number|null|undefined} [off] (default is zero)
|
||||
* @param {number|null|undefined} [sel] (default is undefined)
|
||||
* @param {number|null|undefined} [addr] (default is undefined)
|
||||
* @param {boolean} [fProt] (default is the current CPU mode)
|
||||
* @param {number} [type] (default is based on current CPU mode)
|
||||
* @param {boolean} [fData32] (default is the current CPU operand size)
|
||||
* @param {boolean} [fAddr32] (default is the current CPU address size)
|
||||
* @return {DbgAddr}
|
||||
*/
|
||||
Debugger.prototype.newAddr = function(off, sel, addr, fProt, fData32, fAddr32)
|
||||
Debugger.prototype.newAddr = function(off, sel, addr, type, fData32, fAddr32)
|
||||
{
|
||||
return this.setAddr({}, off, sel, addr, fProt, fData32, fAddr32);
|
||||
return this.setAddr({}, off, sel, addr, type, fData32, fAddr32);
|
||||
};
|
||||
|
||||
/**
|
||||
* setAddr(dbgAddr, off, sel, addr, fProt, fData32, fAddr32)
|
||||
* getAddrPrefix(dbgAddr)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.getAddrPrefix = function(dbgAddr)
|
||||
{
|
||||
var ch = '';
|
||||
switch (dbgAddr.type) {
|
||||
case Debugger.ADDR.REAL:
|
||||
case Debugger.ADDR.V86:
|
||||
ch = '&';
|
||||
break;
|
||||
case Debugger.ADDR.PROT:
|
||||
ch = '#';
|
||||
break;
|
||||
case Debugger.ADDR.LINEAR:
|
||||
ch = '%';
|
||||
break;
|
||||
}
|
||||
return ch;
|
||||
};
|
||||
|
||||
/**
|
||||
* setAddr(dbgAddr, off, sel, addr, type, fData32, fAddr32)
|
||||
*
|
||||
* Updates an EXISTING DbgAddr object, initialized with specified values and/or defaults.
|
||||
*
|
||||
|
|
@ -1718,17 +1838,17 @@ if (DEBUGGER) {
|
|||
* @param {number|null|undefined} [off] (default is zero)
|
||||
* @param {number|null|undefined} [sel] (default is undefined)
|
||||
* @param {number|null|undefined} [addr] (default is undefined)
|
||||
* @param {boolean} [fProt] (default is the current CPU mode)
|
||||
* @param {number} [type] (default is based on current CPU mode)
|
||||
* @param {boolean} [fData32] (default is the current CPU operand size)
|
||||
* @param {boolean} [fAddr32] (default is the current CPU address size)
|
||||
* @return {DbgAddr}
|
||||
*/
|
||||
Debugger.prototype.setAddr = function(dbgAddr, off, sel, addr, fProt, fData32, fAddr32)
|
||||
Debugger.prototype.setAddr = function(dbgAddr, off, sel, addr, type, fData32, fAddr32)
|
||||
{
|
||||
dbgAddr.off = off || 0;
|
||||
dbgAddr.sel = sel;
|
||||
dbgAddr.addr = addr;
|
||||
dbgAddr.fProt = (fProt != null)? fProt : this.getProtMode();
|
||||
dbgAddr.type = type || this.getCurrentType();
|
||||
dbgAddr.fData32 = (fData32 != null)? fData32 : (this.cpu && this.cpu.segCS.sizeData == 4);
|
||||
dbgAddr.fAddr32 = (fAddr32 != null)? fAddr32 : (this.cpu && this.cpu.segCS.sizeAddr == 4);
|
||||
dbgAddr.fTempBreak = false;
|
||||
|
|
@ -1764,22 +1884,27 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* checkLimit(dbgAddr)
|
||||
* checkLimit(dbgAddr, fUpdate)
|
||||
*
|
||||
* Used by incAddr() and parseAddr() to ensure that the (updated) dbgAddr offset is within segment bounds.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {DbgAddr} dbgAddr
|
||||
* @param {boolean} [fUpdate] (true to update segment info)
|
||||
* @return {boolean}
|
||||
*/
|
||||
Debugger.prototype.checkLimit = function(dbgAddr)
|
||||
Debugger.prototype.checkLimit = function(dbgAddr, fUpdate)
|
||||
{
|
||||
if (dbgAddr.sel != null) {
|
||||
var seg = this.getSegment(dbgAddr.sel, dbgAddr.fProt);
|
||||
var seg = this.getSegment(dbgAddr.sel, dbgAddr.type);
|
||||
if (seg) {
|
||||
var off = dbgAddr.off & seg.maskAddr;
|
||||
if ((off >>> 0) >= seg.offMax) return false;
|
||||
dbgAddr.off = off;
|
||||
if (fUpdate) {
|
||||
dbgAddr.off = off;
|
||||
dbgAddr.fData32 = (seg.sizeData == 4);
|
||||
dbgAddr.fAddr32 = (seg.sizeAddr == 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1833,7 +1958,8 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.hexAddr = function(dbgAddr)
|
||||
{
|
||||
return dbgAddr.sel == null? ('%' + str.toHex(dbgAddr.addr)) : this.hexOffset(dbgAddr.off, dbgAddr.sel, dbgAddr.fAddr32);
|
||||
var ch = this.getAddrPrefix(dbgAddr);
|
||||
return dbgAddr.sel == null? ('%' + str.toHex(dbgAddr.addr)) : (ch + this.hexOffset(dbgAddr.off, dbgAddr.sel, dbgAddr.fAddr32));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1947,7 +2073,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
var sInfo = "no information";
|
||||
if (BACKTRACK) {
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE, true, true);
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true, true);
|
||||
if (dbgAddr) {
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
sInfo = '%' + str.toHex(addr) + ": " + (this.bus.getSymbol(addr, true) || sInfo);
|
||||
|
|
@ -2024,7 +2150,7 @@ if (DEBUGGER) {
|
|||
return;
|
||||
}
|
||||
|
||||
var seg = this.getSegment(sel, true);
|
||||
var seg = this.getSegment(sel, Debugger.ADDR.PROT);
|
||||
this.println("dumpDesc(" + str.toHexWord(seg? seg.sel : sel) + "): %" + str.toHex(seg? seg.addrDesc : null, this.cchAddr));
|
||||
if (!seg) return;
|
||||
|
||||
|
|
@ -2135,8 +2261,6 @@ if (DEBUGGER) {
|
|||
*
|
||||
* If you re-enable this protection, be sure to re-enable the decrement below, too.
|
||||
*/
|
||||
var fData32 = null, fAddr32 = null;
|
||||
|
||||
while (nLines > 0 && iHistory != this.iOpcodeHistory) {
|
||||
|
||||
var dbgAddr = aHistory[iHistory++];
|
||||
|
|
@ -2146,7 +2270,8 @@ if (DEBUGGER) {
|
|||
* We must create a new dbgAddr from the address in aHistory, because dbgAddr was
|
||||
* a reference, not a copy, and we don't want getInstruction() modifying the original.
|
||||
*/
|
||||
dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.fProt, fData32 == null? dbgAddr.fData32 : fData32, fAddr32 == null? dbgAddr.fAddr32 : fAddr32);
|
||||
dbgAddr = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.type, dbgAddr.fData32, dbgAddr.fAddr32);
|
||||
|
||||
var sInstruction = this.getInstruction(dbgAddr, "history", nPrev--);
|
||||
if (!aFilters.length || sInstruction.indexOf(aFilters[0]) >= 0) {
|
||||
this.println(sInstruction);
|
||||
|
|
@ -2156,11 +2281,8 @@ if (DEBUGGER) {
|
|||
* If there were OPERAND or ADDRESS overrides on the previous instruction, getInstruction()
|
||||
* will have automatically disassembled additional bytes, so skip additional history entries.
|
||||
*/
|
||||
if (!dbgAddr.cOverrides) {
|
||||
fData32 = fAddr32 = null;
|
||||
} else {
|
||||
if (dbgAddr.cOverrides) {
|
||||
iHistory += dbgAddr.cOverrides; nLines -= dbgAddr.cOverrides; nPrev -= dbgAddr.cOverrides;
|
||||
fData32 = dbgAddr.fData32; fAddr32 = dbgAddr.fAddr32;
|
||||
}
|
||||
|
||||
if (iHistory >= aHistory.length) iHistory = 0;
|
||||
|
|
@ -2256,7 +2378,7 @@ if (DEBUGGER) {
|
|||
this.println("invalid task selector: " + s);
|
||||
return;
|
||||
}
|
||||
seg = this.getSegment(sel, true);
|
||||
seg = this.getSegment(sel, Debugger.ADDR.PROT);
|
||||
}
|
||||
|
||||
this.println("dumpTSS(" + str.toHexWord(seg? seg.sel : sel) + "): %" + str.toHex(seg? seg.base : null, this.cchAddr));
|
||||
|
|
@ -3712,30 +3834,33 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.getInstruction = function(dbgAddr, sComment, nSequence)
|
||||
{
|
||||
var dbgAddrIns = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.fProt);
|
||||
var dbgAddrIns = this.newAddr(dbgAddr.off, dbgAddr.sel, dbgAddr.addr, dbgAddr.type);
|
||||
|
||||
var bOpcode = this.getByte(dbgAddr, 1);
|
||||
|
||||
/*
|
||||
* Incorporate OS and AS prefixes into the current instruction.
|
||||
* Incorporate OPERAND and ADDRESS size prefixes into the current instruction.
|
||||
*
|
||||
* And the verdict is in: redundant OS and AS prefixes must be ignored;
|
||||
* see opOS() and opAS() for details. We limit the amount of redundancy
|
||||
* to something reasonable (ie, 4).
|
||||
* And the verdict is in: redundant OPERAND and ADDRESS prefixes must be ignored;
|
||||
* see opOS() and opAS() for details. We limit the amount of redundancy to something
|
||||
* reasonable (ie, 4).
|
||||
*/
|
||||
var cMax = 4;
|
||||
var cMaxOverrides = 4, cOverrides = 0;
|
||||
var fDataPrefix = false, fAddrPrefix = false;
|
||||
while ((bOpcode == X86.OPCODE.OS || bOpcode == X86.OPCODE.AS) && cMax--) {
|
||||
|
||||
while ((bOpcode == X86.OPCODE.OS || bOpcode == X86.OPCODE.AS) && cMaxOverrides--) {
|
||||
if (bOpcode == X86.OPCODE.OS) {
|
||||
if (!fDataPrefix) {
|
||||
dbgAddr.fData32 = !dbgAddr.fData32;
|
||||
fDataPrefix = true;
|
||||
}
|
||||
cOverrides++;
|
||||
} else {
|
||||
if (!fAddrPrefix) {
|
||||
dbgAddr.fAddr32 = !dbgAddr.fAddr32;
|
||||
fAddrPrefix = true;
|
||||
}
|
||||
cOverrides++;
|
||||
}
|
||||
bOpcode = this.getByte(dbgAddr, 1);
|
||||
}
|
||||
|
|
@ -3889,7 +4014,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
|
||||
this.initAddrSize(dbgAddr, fNonPrefix, (fDataPrefix? 1 : 0) + (fAddrPrefix? 1 : 0));
|
||||
this.initAddrSize(dbgAddr, fNonPrefix, cOverrides);
|
||||
return sLine;
|
||||
};
|
||||
|
||||
|
|
@ -3931,7 +4056,7 @@ if (DEBUGGER) {
|
|||
sOperand = str.toHex(this.getShort(dbgAddr, 2), 4);
|
||||
break;
|
||||
case Debugger.TYPE_FARP:
|
||||
dbgAddr = this.newAddr(this.getWord(dbgAddr, true), this.getShort(dbgAddr, 2), null, dbgAddr.fProt, dbgAddr.fData32, dbgAddr.fAddr32);
|
||||
dbgAddr = this.newAddr(this.getWord(dbgAddr, true), this.getShort(dbgAddr, 2), null, dbgAddr.type, dbgAddr.fData32, dbgAddr.fAddr32);
|
||||
sOperand = this.hexAddr(dbgAddr);
|
||||
var aSymbol = this.findSymbol(dbgAddr);
|
||||
if (aSymbol[0]) sOperand += " (" + aSymbol[0] + ")";
|
||||
|
|
@ -4263,7 +4388,8 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.getRegDump = function(fProt)
|
||||
{
|
||||
var s;
|
||||
if (fProt === undefined) fProt = this.getProtMode();
|
||||
if (fProt === undefined) fProt = this.getCurrentMode();
|
||||
|
||||
s = this.getRegOutput(Debugger.REG_AX) +
|
||||
this.getRegOutput(Debugger.REG_BX) +
|
||||
this.getRegOutput(Debugger.REG_CX) +
|
||||
|
|
@ -4275,6 +4401,7 @@ if (DEBUGGER) {
|
|||
this.getSegOutput(this.cpu.segSS, fProt) + ' ' +
|
||||
this.getSegOutput(this.cpu.segDS, fProt) + ' ' +
|
||||
this.getSegOutput(this.cpu.segES, fProt) + ' ';
|
||||
|
||||
if (fProt) {
|
||||
var sTR = "TR=" + str.toHex(this.cpu.segTSS.sel, 4);
|
||||
var sA20 = "A20=" + (this.bus.getA20()? "ON " : "OFF ");
|
||||
|
|
@ -4302,9 +4429,11 @@ if (DEBUGGER) {
|
|||
this.getSegOutput(this.cpu.segGS, fProt) + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
s += this.getRegOutput(Debugger.REG_PS) +
|
||||
this.getFlagOutput('V') + this.getFlagOutput('D') + this.getFlagOutput('I') + this.getFlagOutput('T') +
|
||||
this.getFlagOutput('S') + this.getFlagOutput('Z') + this.getFlagOutput('A') + this.getFlagOutput('P') + this.getFlagOutput('C');
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
|
|
@ -4332,7 +4461,7 @@ if (DEBUGGER) {
|
|||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
* @param {number|undefined} [type] is the address segment type, in case sAddr doesn't specify a segment
|
||||
* @param {number|undefined} [type] is either CODE or DATA, in case sAddr doesn't specify a segment
|
||||
* @param {boolean} [fNoChecks] (eg, true when setting breakpoints that may not be valid now, but will be later)
|
||||
* @param {boolean} [fQuiet]
|
||||
* @return {DbgAddr|null|undefined}
|
||||
|
|
@ -4340,24 +4469,45 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.parseAddr = function(sAddr, type, fNoChecks, fQuiet)
|
||||
{
|
||||
var dbgAddr, fPrint;
|
||||
var dbgAddrNext = (type === Debugger.ADDR_CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
var dbgAddrNext = (type === Debugger.ADDR.CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
|
||||
|
||||
var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr;
|
||||
|
||||
type = fNoChecks? Debugger.ADDR.NONE : dbgAddrNext.type;
|
||||
|
||||
if (fQuiet) fPrint = false;
|
||||
|
||||
if (sAddr !== undefined) {
|
||||
|
||||
if (sAddr.charAt(0) == '%') {
|
||||
var ch = sAddr.charAt(0);
|
||||
var iColon = sAddr.indexOf(':');
|
||||
|
||||
switch(ch) {
|
||||
case '&':
|
||||
type = Debugger.ADDR.REAL;
|
||||
break;
|
||||
case '#':
|
||||
type = Debugger.ADDR.PROT;
|
||||
break;
|
||||
case '%':
|
||||
type = Debugger.ADDR.LINEAR;
|
||||
off = addr = 0;
|
||||
sel = null; // we still have code that relies on this crutch, instead of the type field
|
||||
break;
|
||||
default:
|
||||
if (iColon >= 0) type = Debugger.ADDR.NONE;
|
||||
ch = '';
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch) {
|
||||
sAddr = sAddr.substr(1);
|
||||
off = 0;
|
||||
sel = null;
|
||||
addr = 0;
|
||||
iColon--;
|
||||
}
|
||||
|
||||
dbgAddr = this.findSymbolAddr(sAddr);
|
||||
if (dbgAddr) return dbgAddr;
|
||||
|
||||
var iColon = sAddr.indexOf(':');
|
||||
if (iColon < 0) {
|
||||
if (sel != null) {
|
||||
off = this.parseExpression(sAddr, fPrint);
|
||||
|
|
@ -4375,8 +4525,8 @@ if (DEBUGGER) {
|
|||
}
|
||||
|
||||
if (off != null) {
|
||||
dbgAddr = this.newAddr(off, sel, addr);
|
||||
if (!fNoChecks && !this.checkLimit(dbgAddr)) {
|
||||
dbgAddr = this.newAddr(off, sel, addr, type);
|
||||
if (!fNoChecks && !this.checkLimit(dbgAddr, true)) {
|
||||
this.println("invalid offset: " + this.hexAddr(dbgAddr));
|
||||
dbgAddr = null;
|
||||
}
|
||||
|
|
@ -5045,7 +5195,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.doAssemble = function(asArgs)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(asArgs[1], Debugger.ADDR_CODE);
|
||||
var dbgAddr = this.parseAddr(asArgs[1], Debugger.ADDR.CODE);
|
||||
if (!dbgAddr) return;
|
||||
|
||||
this.dbgAddrAssemble = dbgAddr;
|
||||
|
|
@ -5124,17 +5274,10 @@ if (DEBUGGER) {
|
|||
}
|
||||
var dbgAddr = {};
|
||||
if (sAddr != '*') {
|
||||
dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE, true);
|
||||
dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE, true);
|
||||
if (!dbgAddr) return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want our breakpoints to be "mode-less"; ie, independent of the processor mode at the
|
||||
* time they're set. Therefore, it's critical that we erase the fProt setting that parseAddr(),
|
||||
* via newAddr(), initialized dbgAddr with.
|
||||
*/
|
||||
dbgAddr.fProt = undefined;
|
||||
|
||||
sAddr = (dbgAddr.off == null? sAddr : str.toHexWord(dbgAddr.off));
|
||||
|
||||
if (sParm == 'c') {
|
||||
|
|
@ -5310,7 +5453,7 @@ if (DEBUGGER) {
|
|||
return;
|
||||
}
|
||||
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.DATA);
|
||||
if (!dbgAddr || dbgAddr.sel == null && dbgAddr.addr == null) return;
|
||||
|
||||
var cb = 0; // 0 is not a default; it triggers the appropriate default below
|
||||
|
|
@ -5375,7 +5518,7 @@ if (DEBUGGER) {
|
|||
return;
|
||||
}
|
||||
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.DATA);
|
||||
if (!dbgAddr) return;
|
||||
|
||||
for (var i = 2; i < asArgs.length; i++) {
|
||||
|
|
@ -5629,7 +5772,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.doList = function(sSymbol)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(sSymbol, Debugger.ADDR_CODE);
|
||||
var dbgAddr = this.parseAddr(sSymbol, Debugger.ADDR.CODE);
|
||||
if (!dbgAddr) return;
|
||||
|
||||
var addr = this.getAddr(dbgAddr);
|
||||
|
|
@ -5690,7 +5833,7 @@ if (DEBUGGER) {
|
|||
var fJSON = (asArgs[1] == "json");
|
||||
var iDrive, iSector = 0, nSectors = 0;
|
||||
|
||||
var dbgAddr = (fJSON? {} : this.parseAddr(asArgs[1], Debugger.ADDR_DATA));
|
||||
var dbgAddr = (fJSON? {} : this.parseAddr(asArgs[1], Debugger.ADDR.DATA));
|
||||
if (!dbgAddr) return;
|
||||
|
||||
iDrive = this.parseValue(asArgs[2], "drive #");
|
||||
|
|
@ -6273,13 +6416,13 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doRun = function(sAddr, sOptions, fQuiet)
|
||||
{
|
||||
if (sAddr !== undefined) {
|
||||
var dbgAddr = this.parseAddr(this.parseReference(sAddr), Debugger.ADDR_CODE);
|
||||
var dbgAddr = this.parseAddr(this.parseReference(sAddr), Debugger.ADDR.CODE);
|
||||
if (!dbgAddr) return;
|
||||
this.parseAddrOptions(dbgAddr, sOptions);
|
||||
this.setTempBreakpoint(dbgAddr);
|
||||
}
|
||||
if (!fQuiet && !this.runCPU(true)) {
|
||||
this.println('cpu busy, "g" command ignored');
|
||||
this.println('cpu busy or unavailable, command ignored');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6432,7 +6575,7 @@ if (DEBUGGER) {
|
|||
var sCall = null;
|
||||
var off = dbgAddr.off;
|
||||
var offOrig = off;
|
||||
for (var n = 1; n <= 6; n++) {
|
||||
for (var n = 1; n <= 6 && !!off; n++) {
|
||||
if (n > 2) {
|
||||
dbgAddr.off = off;
|
||||
dbgAddr.addr = null;
|
||||
|
|
@ -6442,7 +6585,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!--off) break;
|
||||
off--;
|
||||
}
|
||||
dbgAddr.off = offOrig;
|
||||
return sCall;
|
||||
|
|
@ -6537,7 +6680,7 @@ if (DEBUGGER) {
|
|||
/*
|
||||
* Use cOverrides to record whether we previously processed any OPERAND or ADDRESS overrides.
|
||||
*/
|
||||
dbgAddr.cOverrides = cOverrides;
|
||||
dbgAddr.cOverrides = cOverrides || 0;
|
||||
/*
|
||||
* For proper disassembly of instructions preceded by an OPERAND (0x66) size prefix, we set
|
||||
* dbgAddr.fData32 to true whenever the operand size is 32-bit; similarly, for an ADDRESS (0x67)
|
||||
|
|
@ -6545,8 +6688,10 @@ if (DEBUGGER) {
|
|||
* both fields must be set to match the size of the current code segment.
|
||||
*/
|
||||
if (fNonPrefix) {
|
||||
dbgAddr.fData32 = (this.cpu.segCS.sizeData == 4);
|
||||
dbgAddr.fAddr32 = (this.cpu.segCS.sizeAddr == 4);
|
||||
if (dbgAddr.fData32Orig != null) dbgAddr.fData32 = dbgAddr.fData32Orig;
|
||||
if (dbgAddr.fAddr32Orig != null) dbgAddr.fAddr32 = dbgAddr.fAddr32Orig;
|
||||
dbgAddr.fData32Orig = dbgAddr.fData32;
|
||||
dbgAddr.fAddr32Orig = dbgAddr.fAddr32;
|
||||
}
|
||||
/*
|
||||
* We also use dbgAddr.fComplete to record whether the caller (ie, getInstruction()) is reporting that
|
||||
|
|
@ -6577,16 +6722,15 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.doUnassemble = function(sAddr, sAddrEnd, n)
|
||||
{
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE);
|
||||
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR.CODE);
|
||||
if (!dbgAddr) return;
|
||||
|
||||
if (n === undefined) n = 1;
|
||||
var dbgAddrEnd = this.newAddr(this.maskReg, dbgAddr.sel, this.bus.nBusLimit);
|
||||
|
||||
var cb = 0x100;
|
||||
var dbgAddrEnd, cb = 0x100;
|
||||
if (sAddrEnd !== undefined) {
|
||||
|
||||
dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR_CODE);
|
||||
dbgAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR.CODE);
|
||||
if (!dbgAddrEnd || dbgAddrEnd.off < dbgAddr.off) return;
|
||||
|
||||
cb = dbgAddrEnd.off - dbgAddr.off;
|
||||
|
|
@ -6601,6 +6745,9 @@ if (DEBUGGER) {
|
|||
}
|
||||
n = -1;
|
||||
}
|
||||
else {
|
||||
dbgAddrEnd = this.newAddr(this.maskReg, dbgAddr.sel, this.bus.nBusLimit, dbgAddr.type, dbgAddr.fData32, dbgAddr.fAddr32);
|
||||
}
|
||||
|
||||
var cLines = 0;
|
||||
this.initAddrSize(dbgAddr, true);
|
||||
|
|
@ -6629,7 +6776,7 @@ if (DEBUGGER) {
|
|||
var sInstruction = this.getInstruction(dbgAddr, sComment, nSequence);
|
||||
|
||||
/*
|
||||
* If getInstruction() reported that it did not yet process a complete instruction (via dbgAddr.fComplete),
|
||||
* If getInstruction() reported that it did not process a complete instruction (via dbgAddr.fComplete),
|
||||
* then bump the instruction count by one, so that we display one more line (and hopefully the complete
|
||||
* instruction).
|
||||
*/
|
||||
|
|
@ -6677,7 +6824,12 @@ if (DEBUGGER) {
|
|||
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||
*
|
||||
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
|
||||
*
|
||||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
chSep = chSep || ';';
|
||||
|
|
|
|||
|
|
@ -63,13 +63,18 @@ var Interrupts = {
|
|||
VECTOR: 0x41,
|
||||
IS_LOADED: 0x004F, // AX command
|
||||
LOADED: 0xF386, // returned in AX if Windows Debugger loaded
|
||||
LOAD_SEG: 0x0050 // SI==0 if code, 1 if data; BX==segnum-1; CX==selector; ES:[E]DI->module name
|
||||
LOAD_SEG: 0x0050, // SI==0 if code, 1 if data; BX==segnum-1; CX==selector; ES:[E]DI->module name
|
||||
ENABLED: true // support for WINDBGRM interrupts can be disabled
|
||||
},
|
||||
WINDBGRM: { // Windows Debugger real-mode interface
|
||||
VECTOR: 0x68,
|
||||
IS_LOADED: 0x43, // AH command
|
||||
LOADED: 0xF386, // returned in AX if Windows Debugger loaded
|
||||
ENABLED: false // support for WINDBGRM interrupts is NOT enabled by default
|
||||
LOAD_SEG: 0x50, // AL=segment type, ES:DI->D386_Device_Params
|
||||
/*
|
||||
* This must be disabled until we're able to respond intelligently to requests like D386_Prepare_PMode (0x44)
|
||||
*/
|
||||
ENABLED: false // support for WINDBGRM interrupts can be disabled
|
||||
},
|
||||
FUNCS: {} // filled in only if DEBUGGER is true
|
||||
};
|
||||
|
|
|
|||
|
|
@ -520,8 +520,18 @@ Video.MODE = {
|
|||
VGA_640X480_MONO: 0x11, // mapped at A000:0000, mono, 2bpp, planar
|
||||
VGA_640X480: 0x12, // mapped at A000:0000, color, 4bpp, planar
|
||||
VGA_320X200: 0x13, // mapped at A000:0000, color, 8bpp, linear
|
||||
VGA_320X240: 0x78, // mapped at A000:0000, color, 8bpp, planar ("Mode X")
|
||||
VGA_320X400: 0x7A, // mapped at A000:0000, color, 8bpp, planar
|
||||
/*
|
||||
* The remaining mode identifiers are for internal use only; there is no correlation with any
|
||||
* publicly defined BIOS modes, and overlap with any third-party mode numbers is purely coincidental.
|
||||
*/
|
||||
VGA_320X240: 0x14, // mapped at A000:0000, color, 8bpp, planar ("Mode X")
|
||||
VGA_320X400: 0x15, // mapped at A000:0000, color, 8bpp, planar
|
||||
/*
|
||||
* Here's where we might assign additional identifiers to certain unique combinations, like the
|
||||
* fTextGraphicsHybrid 320x400 mode that Windows 95 uses (ie, when the buffer is mapped to B800:0000
|
||||
* instead of A000:0000 and is configured for text mode access, but graphics are still being displayed
|
||||
* from the second half of video memory).
|
||||
*/
|
||||
UNKNOWN: 0xFF
|
||||
};
|
||||
|
||||
|
|
@ -733,8 +743,8 @@ Video.FONT = {
|
|||
* and it might not always be the best fit.
|
||||
*/
|
||||
Video.aModeParms = []; // Mode
|
||||
Video.aModeParms[Video.MODE.CGA_40X25] = [ 40, 25, 1, 0, Video.FONT.CGA]; // 0x00
|
||||
Video.aModeParms[Video.MODE.CGA_80X25] = [ 80, 25, 1, 0, Video.FONT.CGA]; // 0x02
|
||||
Video.aModeParms[Video.MODE.CGA_40X25] = [ 40, 25, 1, 0, Video.FONT.CGA]; // 0x01
|
||||
Video.aModeParms[Video.MODE.CGA_80X25] = [ 80, 25, 1, 0, Video.FONT.CGA]; // 0x03
|
||||
Video.aModeParms[Video.MODE.CGA_320X200] = [320, 200, 8, 192]; // 0x04
|
||||
Video.aModeParms[Video.MODE.CGA_640X200] = [640, 200, 16, 192]; // 0x06
|
||||
Video.aModeParms[Video.MODE.MDA_80X25] = [ 80, 25, 1, 0, Video.FONT.MDA]; // 0x07
|
||||
|
|
@ -745,11 +755,11 @@ Video.aModeParms[Video.MODE.EGA_640X350] = [640, 350, 8];
|
|||
Video.aModeParms[Video.MODE.VGA_640X480_MONO] = [640, 480, 8]; // 0x11
|
||||
Video.aModeParms[Video.MODE.VGA_640X480] = [640, 480, 8]; // 0x12
|
||||
Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 1]; // 0x13
|
||||
Video.aModeParms[Video.MODE.VGA_320X240] = [320, 240, 4]; // 0x78
|
||||
Video.aModeParms[Video.MODE.VGA_320X400] = [320, 400, 4]; // 0x7A
|
||||
Video.aModeParms[Video.MODE.VGA_320X240] = [320, 240, 4]; // 0x14
|
||||
Video.aModeParms[Video.MODE.VGA_320X400] = [320, 400, 4]; // 0x15
|
||||
|
||||
Video.aModeParms[Video.MODE.CGA_40X25_BW] = Video.aModeParms[Video.MODE.CGA_40X25]; // 0x01
|
||||
Video.aModeParms[Video.MODE.CGA_80X25_BW] = Video.aModeParms[Video.MODE.CGA_80X25]; // 0x03
|
||||
Video.aModeParms[Video.MODE.CGA_40X25_BW] = Video.aModeParms[Video.MODE.CGA_40X25]; // 0x00
|
||||
Video.aModeParms[Video.MODE.CGA_80X25_BW] = Video.aModeParms[Video.MODE.CGA_80X25]; // 0x02
|
||||
Video.aModeParms[Video.MODE.CGA_320X200_BW] = Video.aModeParms[Video.MODE.CGA_320X200]; // 0x05
|
||||
|
||||
/*
|
||||
|
|
@ -3887,9 +3897,8 @@ Video.prototype.setFontData = function(abFontData, aFontOffsets, cxFontChar)
|
|||
* Calls to buildFonts() should not be expensive though: the underlying createFont() function rebuilds a font only
|
||||
* if its color has actually changed.
|
||||
*
|
||||
* TODO: We should avoid rebuilding fonts when palette registers change in graphics modes. More importantly, our
|
||||
* font code is still written with the assumption that, like the MDA/CGA, the underlying font data never changes.
|
||||
* The EGA, however, stores its fonts in plane 2, which means fonts are dynamic; this needs to be fixed.
|
||||
* TODO: Our font code is still written with the assumption that, like the MDA/CGA, the underlying font data never
|
||||
* changes. The EGA, however, stores its fonts in plane 2, which means fonts are dynamic; this needs to be fixed.
|
||||
*
|
||||
* Supporting dynamic EGA fonts should not be hard though. We can get rid of abFontData and simply build a
|
||||
* temporary snapshot of all the font bytes in plane 2 of the EGA's video buffer (adwMemory), and pass that on to
|
||||
|
|
@ -3901,16 +3910,21 @@ Video.prototype.setFontData = function(abFontData, aFontOffsets, cxFontChar)
|
|||
* for 43-line mode, and so on).
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {boolean} [fRebuild] (true if this is a rebuild, not an initial build)
|
||||
* @return {boolean} true if any or all fonts were (re)built, false if nothing changed
|
||||
*/
|
||||
Video.prototype.buildFonts = function()
|
||||
Video.prototype.buildFonts = function(fRebuild)
|
||||
{
|
||||
var fChanges = false;
|
||||
|
||||
/*
|
||||
* There's no point building any fonts if this is a non-windowed (eg, command-line) environment OR no font data was loaded.
|
||||
* There's no point building fonts if this is a non-windowed (command-line) environment
|
||||
* OR no font data is available (OR this is a rebuild AND we're currently in a graphics mode).
|
||||
*
|
||||
* In other words, build fonts if this IS a windowed environment AND font data is available
|
||||
* AND this is not a rebuild, OR it IS a rebuild but we're in a graphics mode (ie, nFont is zero).
|
||||
*/
|
||||
if (window && this.abFontData) {
|
||||
if (window && this.abFontData && (!fRebuild || this.nFont)) {
|
||||
|
||||
var offSplit = 0x0000;
|
||||
var cxChar = this.cxFontChar? this.cxFontChar : 8;
|
||||
|
|
@ -4254,7 +4268,7 @@ Video.prototype.checkCursor = function()
|
|||
*/
|
||||
var iCellCursor = (this.cardActive.regCRTData[Card.CRTC.CURSOR_ADDR_LO] + ((this.cardActive.regCRTData[Card.CRTC.CURSOR_ADDR_HI] & Card.CRTC.ADDR_HI_MASK) << 8));
|
||||
if (this.iCellCursor != iCellCursor) {
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
if (MAXDEBUG && this.messageEnabled()) {
|
||||
this.printMessage("checkCursor(): cursor moved from " + this.iCellCursor + " to " + iCellCursor);
|
||||
}
|
||||
this.removeCursor();
|
||||
|
|
@ -4309,7 +4323,7 @@ Video.prototype.removeCursor = function()
|
|||
*/
|
||||
this.updateChar(col, row, data);
|
||||
}
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
if (MAXDEBUG && this.messageEnabled()) {
|
||||
this.printMessage("removeCursor(): removed from " + row + "," + col);
|
||||
}
|
||||
this.aCellCache[this.iCellCursor] = data;
|
||||
|
|
@ -4467,6 +4481,8 @@ Video.prototype.setCardAccess = function(nAccess)
|
|||
/**
|
||||
* setDimensions()
|
||||
*
|
||||
* This is the workhorse of setMode()
|
||||
*
|
||||
* @this {Video}
|
||||
*/
|
||||
Video.prototype.setDimensions = function()
|
||||
|
|
@ -4652,7 +4668,7 @@ Video.prototype.setDimensions = function()
|
|||
* checkMode(fForce)
|
||||
*
|
||||
* Called whenever the MDA/CGA's mode register (eg, Card.MDA.MODE.PORT, Card.CGA.MODE.PORT) is updated,
|
||||
* or whenever the EGA's GRC Misc register is updated, or when we've just finished a restore().
|
||||
* or whenever the EGA/VGA's GRC.MISC register is updated, or when we've just finished a restore().
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {boolean} [fForce] is used to force a mode update, if we recognize the current mode
|
||||
|
|
@ -4723,55 +4739,81 @@ Video.prototype.checkMode = function(fForce)
|
|||
* so that as support is added for even more modes (eg, "Mode X" variations, monochrome modes, etc), it
|
||||
* doesn't get totally out of control.
|
||||
*/
|
||||
var fSEQDotClock = (card.regSEQData[Card.SEQ.CLOCKING.INDX] & Card.SEQ.CLOCKING.DOTCLOCK);
|
||||
var nCRTCVertTotal = card.regCRTData[Card.CRTC.EGA.VTOTAL];
|
||||
var fSEQDotClock, nCRTCVertTotal, nCRTCMaxScan;
|
||||
var regGRCMode = card.regGRCData[Card.GRC.MODE.INDX];
|
||||
|
||||
/*
|
||||
* This text/graphics hybrid test detects the way Windows 95 reprograms the VGA on boot (ie, switching
|
||||
* to graphics mode 0x13 (320x200) without disturbing the text buffer contents, then reprogramming it
|
||||
* to enable graphics mode 0x15 (320x400), then drawing a logo in the 2nd half of the video memory, and
|
||||
* finally reprogramming regGRCMode and regGRCMisc to move the frame buffer back to its original text mode
|
||||
* location.
|
||||
*/
|
||||
var fTextGraphicsHybrid = (regGRCMode & (Card.GRC.MODE.COLOR256 | Card.GRC.MODE.EVENODD)) == (Card.GRC.MODE.COLOR256 | Card.GRC.MODE.EVENODD);
|
||||
if (fTextGraphicsHybrid) {
|
||||
/*
|
||||
* When fTextGraphicsHybrid is true, we should be at the end of the above process, so addrBuffer
|
||||
* will have changed. Since we don't (yet) assign a special mode to that configuration, we must at
|
||||
* least set fForce to true, so that setMode() will notice the buffer address change and remap it.
|
||||
*/
|
||||
if (card.addrBuffer != this.addrBuffer || card.sizeBuffer != this.sizeBuffer) {
|
||||
fForce = true;
|
||||
}
|
||||
}
|
||||
|
||||
fSEQDotClock = (card.regSEQData[Card.SEQ.CLOCKING.INDX] & Card.SEQ.CLOCKING.DOTCLOCK);
|
||||
nCRTCVertTotal = card.regCRTData[Card.CRTC.EGA.VTOTAL];
|
||||
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VTOTAL_BIT8)? 0x100 : 0);
|
||||
if (card.nCard == Video.CARD.VGA) {
|
||||
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VTOTAL_BIT9)? 0x200 : 0);
|
||||
}
|
||||
var nCRTCMaxScan = card.regCRTData[Card.CRTC.EGA.MAX_SCAN.INDX];
|
||||
nCRTCMaxScan = card.regCRTData[Card.CRTC.EGA.MAX_SCAN.INDX];
|
||||
|
||||
if (nMode != Video.MODE.UNKNOWN) {
|
||||
if (!(regGRCMisc & Card.GRC.MISC.GRAPHICS)) {
|
||||
if (fSEQDotClock) nMode -= 2;
|
||||
/*
|
||||
* Here's where we handle text modes; since nMode will have been assigned a default
|
||||
* of either 0x02 or 0x03, convert that to either 0x05 or 0x04 if we're in a low-res
|
||||
* graphics mode, 0x06 otherwise.
|
||||
*/
|
||||
nMode -= (fSEQDotClock? 2 : 0);
|
||||
}
|
||||
else if (card.addrBuffer != 0xA0000 && !fTextGraphicsHybrid) {
|
||||
/*
|
||||
* Here's where we handle CGA graphics modes; since nMode will have been assigned a
|
||||
* default of either 0x02 or 0x03, convert that to either 0x05 or 0x04 if we're in a
|
||||
* low-res graphics mode, 0x06 otherwise.
|
||||
*/
|
||||
nMode = fSEQDotClock? (7 - nMode) : Video.MODE.CGA_640X200;
|
||||
} else {
|
||||
if (card.addrBuffer == 0xB8000) {
|
||||
/*
|
||||
* Since nMode will have been assigned a default of either 0x02 or 0x03, convert that to
|
||||
* either 0x05 or 0x04 if we're in a low-res graphics mode, 0x06 otherwise.
|
||||
*/
|
||||
nMode = fSEQDotClock? (7 - nMode) : Video.MODE.CGA_640X200;
|
||||
} else {
|
||||
/*
|
||||
* card.addrBuffer must be 0xA0000, so we need to discriminate among modes 0x0D and up;
|
||||
* we've already defaulted to either 0x0F or 0x10. If COLOR256 is set, then select mode
|
||||
* 0x13 (or greater), else if 200-to-400 scan-line conversion is in effect, select either
|
||||
* mode 0x0D or 0x0E, else if VGA resolution is set, select either mode 0x11 or 0x12.
|
||||
*/
|
||||
if (card.regGRCData[Card.GRC.MODE.INDX] & Card.GRC.MODE.COLOR256) {
|
||||
if (nCRTCMaxScan & Card.CRTC.EGA.MAX_SCAN.SCAN_LINE) {
|
||||
if (card.regCRTData[Card.CRTC.EGA.VDISP_END] <= 0x8F) {
|
||||
nMode = Video.MODE.VGA_320X200;
|
||||
}
|
||||
else { /* (card.regCRTData[Card.CRTC.EGA.VDISP_END] == 0xDF) */
|
||||
nMode = Video.MODE.VGA_320X240;
|
||||
}
|
||||
} else {
|
||||
nMode = Video.MODE.VGA_320X400;
|
||||
/*
|
||||
* Here's where we handle EGA/VGA graphics modes, discriminating among modes 0x0D and up;
|
||||
* we've already defaulted to either 0x0F or 0x10. If COLOR256 is set, then select mode
|
||||
* 0x13 (or greater), else if 200-to-400 scan-line conversion is in effect, select either
|
||||
* mode 0x0D or 0x0E, else if VGA resolution is set, select either mode 0x11 or 0x12.
|
||||
*/
|
||||
if (card.regGRCData[Card.GRC.MODE.INDX] & Card.GRC.MODE.COLOR256) {
|
||||
if (nCRTCMaxScan & Card.CRTC.EGA.MAX_SCAN.SCAN_LINE) {
|
||||
if (card.regCRTData[Card.CRTC.EGA.VDISP_END] <= 0x8F) {
|
||||
nMode = Video.MODE.VGA_320X200;
|
||||
}
|
||||
else { /* (card.regCRTData[Card.CRTC.EGA.VDISP_END] == 0xDF) */
|
||||
nMode = Video.MODE.VGA_320X240;
|
||||
}
|
||||
} else {
|
||||
nMode = Video.MODE.VGA_320X400;
|
||||
}
|
||||
else if (nCRTCMaxScan & Card.CRTC.EGA.MAX_SCAN.CONVERT400) {
|
||||
nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200);
|
||||
} else if (nCRTCVertTotal > 500) {
|
||||
nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODE.VGA_640X480_MONO : Video.MODE.VGA_640X480);
|
||||
}
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("checkMode(): nCRTCVertTotal=" + nCRTCVertTotal + ", mode=" + str.toHexByte(nMode));
|
||||
}
|
||||
}
|
||||
else if (nCRTCMaxScan & Card.CRTC.EGA.MAX_SCAN.CONVERT400) {
|
||||
nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200);
|
||||
} else if (nCRTCVertTotal > 500) {
|
||||
nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODE.VGA_640X480_MONO : Video.MODE.VGA_640X480);
|
||||
}
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("checkMode(): nCRTCVertTotal=" + nCRTCVertTotal + ", mode=" + str.toHexByte(nMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nAccess = this.getCardAccess();
|
||||
}
|
||||
}
|
||||
|
|
@ -4812,8 +4854,8 @@ Video.prototype.checkMode = function(fForce)
|
|||
/**
|
||||
* setMode(nMode, fForce)
|
||||
*
|
||||
* Set fForce to true to update the mode regardless of previous mode, or false to perform a normal update
|
||||
* that bypasses updateScreen() but still calls initCellCache().
|
||||
* Set fForce to true to update the mode regardless of previous mode, or false to perform
|
||||
* a normal update that bypasses updateScreen() but still calls initCellCache().
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {number|null} nMode
|
||||
|
|
@ -4825,7 +4867,7 @@ Video.prototype.setMode = function(nMode, fForce)
|
|||
if (nMode != null && (nMode != this.nMode || fForce)) {
|
||||
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("setMode(" + str.toHexByte(nMode) + (fForce? ",force" : "") + ")");
|
||||
this.printMessage("setMode(" + str.toHexByte(nMode) + (fForce? ",force" : "") + ")", true, true);
|
||||
}
|
||||
|
||||
this.cUpdates = 0; // count updateScreen() calls as a means of driving blink updates
|
||||
|
|
@ -5162,12 +5204,28 @@ Video.prototype.updateScreen = function(fForce)
|
|||
|
||||
/*
|
||||
* Calculate the VISIBLE start of screen memory (addrScreen), not merely the PHYSICAL start,
|
||||
* as well as the extent of it (cbScreen) and use those values for all addressing operations
|
||||
* to follow. FYI, in these calculations, offScreen does not refer to "off-screen" memory,
|
||||
* but rather the "offset" of the start of visible screen memory.
|
||||
* as well as the extent of it (cbScreen) and use those values for all addressing operations to follow.
|
||||
* FYI, in these calculations, offScreen does not refer to "off-screen" memory, but rather the "offset"
|
||||
* of the start of visible screen memory.
|
||||
*/
|
||||
var addrScreen = card.addrBuffer;
|
||||
var addrScreenLimit = addrScreen + card.sizeBuffer;
|
||||
var addrBuffer = this.addrBuffer;
|
||||
var addrScreen = addrBuffer;
|
||||
var addrScreenLimit = addrScreen + this.sizeBuffer;
|
||||
|
||||
/*
|
||||
* HACK: To deal with the fTextGraphicsHybrid 320x400 mode that Windows 95 uses (ie, when the buffer
|
||||
* is mapped to B800:0000 instead of A000:0000 and is configured for text mode access, but graphics are
|
||||
* still being displayed from the second half of video memory), we must ignore the programmed address.
|
||||
*
|
||||
* In that case, the hard-coded address range below isn't actually active either, but it doesn't matter;
|
||||
* we just have to get through the rest of this function and make it to the updateScreenGraphicsVGA() call,
|
||||
* which will draw from our video buffer (adwMemory) directly; these addresses are only used for bounds
|
||||
* checking.
|
||||
*/
|
||||
if (this.nMode >= Video.MODE.VGA_320X200) {
|
||||
addrBuffer = addrScreen = 0xA0000;
|
||||
addrScreenLimit = addrScreen + 0x10000;
|
||||
}
|
||||
|
||||
/*
|
||||
* HACK: The CRTC's START_ADDR_HI and START_ADDR_LO registers are supposed to be "latched" into
|
||||
|
|
@ -5190,7 +5248,6 @@ Video.prototype.updateScreen = function(fForce)
|
|||
|
||||
/*
|
||||
* Any screen (aka "page") offset must be doubled for text modes, due to the attribute bytes.
|
||||
*
|
||||
* TODO: Come up with a more robust method of deciding when any screen offset should be doubled.
|
||||
*/
|
||||
if (this.nFont) offScreen <<= 1;
|
||||
|
|
@ -5259,13 +5316,22 @@ Video.prototype.updateScreen = function(fForce)
|
|||
}
|
||||
}
|
||||
else if (this.cbSplit) {
|
||||
/*
|
||||
* All CGA graphics modes have the goofy split-buffer layout, hence the simple test above.
|
||||
*/
|
||||
this.updateScreenGraphicsCGA(addrScreen, addrScreenLimit);
|
||||
}
|
||||
else if (!this.fColor256) {
|
||||
this.updateScreenGraphicsEGA(addrScreen, addrScreenLimit);
|
||||
/*
|
||||
* All EGA graphics modes are taken care of here, including all 16-color VGA graphics modes.
|
||||
*/
|
||||
this.updateScreenGraphicsEGA(addrBuffer, addrScreen, addrScreenLimit);
|
||||
}
|
||||
else {
|
||||
this.updateScreenGraphicsVGA(addrScreen, addrScreenLimit);
|
||||
/*
|
||||
* Finally, all 256-color VGA modes are processed here.
|
||||
*/
|
||||
this.updateScreenGraphicsVGA(addrBuffer, addrScreen, addrScreenLimit);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -5422,14 +5488,15 @@ Video.prototype.updateScreenGraphicsCGA = function(addrScreen, addrScreenLimit)
|
|||
};
|
||||
|
||||
/**
|
||||
* updateScreenGraphicsEGA(addrScreen, addrScreenLimit)
|
||||
* updateScreenGraphicsEGA(addrBuffer, addrScreen, addrScreenLimit)
|
||||
*
|
||||
* TODO: Add support for blinking graphics (ATC.MODE.BLINK_ENABLE)
|
||||
*
|
||||
* @param addrScreen
|
||||
* @param addrScreenLimit
|
||||
* @param {number} addrBuffer
|
||||
* @param {number} addrScreen
|
||||
* @param {number} addrScreenLimit
|
||||
*/
|
||||
Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
||||
Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrScreenLimit)
|
||||
{
|
||||
var addr, data;
|
||||
|
||||
|
|
@ -5450,7 +5517,7 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
var nRowAdjust = (this.nColsLogical > this.nCols? ((this.nColsLogical - this.nCols - iPixelFirst) >> 3) : 0);
|
||||
|
||||
while (addr < addrScreenLimit) {
|
||||
var idw = addr++ - this.addrBuffer;
|
||||
var idw = addr++ - addrBuffer;
|
||||
this.assert(idw >= 0 && idw < adwMemory.length);
|
||||
data = adwMemory[idw];
|
||||
|
||||
|
|
@ -5541,7 +5608,7 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
};
|
||||
|
||||
/**
|
||||
* updateScreenGraphicsVGA(addrScreen, addrScreenLimit)
|
||||
* updateScreenGraphicsVGA(addrBuffer, addrScreen, addrScreenLimit)
|
||||
*
|
||||
* This function name is a slight misnomer: updateScreenGraphicsEGA() takes care of all the 4bpp video modes
|
||||
* (first introduced by the EGA and later expanded by the VGA), where each pixel's bits are spread across the 4
|
||||
|
|
@ -5551,10 +5618,11 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
*
|
||||
* TODO: Add support for blinking graphics (ATC.MODE.BLINK_ENABLE)
|
||||
*
|
||||
* @param addrScreen
|
||||
* @param addrScreenLimit
|
||||
* @param {number} addrBuffer
|
||||
* @param {number} addrScreen
|
||||
* @param {number} addrScreenLimit
|
||||
*/
|
||||
Video.prototype.updateScreenGraphicsVGA = function(addrScreen, addrScreenLimit)
|
||||
Video.prototype.updateScreenGraphicsVGA = function(addrBuffer, addrScreen, addrScreenLimit)
|
||||
{
|
||||
var addr, data;
|
||||
|
||||
|
|
@ -5576,7 +5644,7 @@ Video.prototype.updateScreenGraphicsVGA = function(addrScreen, addrScreenLimit)
|
|||
var nRowAdjust = (this.nColsLogical > this.nCols? ((this.nColsLogical - this.nCols - iPixelFirst) >> 3) : 0);
|
||||
|
||||
while (addr < addrScreenLimit) {
|
||||
var idw = addr - this.addrBuffer;
|
||||
var idw = addr - addrBuffer;
|
||||
this.assert(idw >= 0 && idw < adwMemory.length);
|
||||
data = adwMemory[idw];
|
||||
|
||||
|
|
@ -5849,7 +5917,7 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
|
|||
this.printMessageIO(port, bOut, addrFrom, "ATC.INDX");
|
||||
card.fATCData = true;
|
||||
if ((bOut & Card.ATC.INDX_PAL_ENABLE) && !fPalEnabled) {
|
||||
if (!this.buildFonts()) {
|
||||
if (!this.buildFonts(true)) {
|
||||
if (DEBUG && (!addrFrom || this.messageEnabled())) {
|
||||
this.printMessage("outATC(" + str.toHexByte(bOut) + "): no font changes required");
|
||||
}
|
||||
|
|
@ -5860,9 +5928,31 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
|
|||
this.updateScreen(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* TODO: We need a screen blanking function, suitable for any mode, when INDX_PAL_ENABLE transitions off.
|
||||
* powerDown() might like to use such a function, too. updateScreen() already disables any further screen
|
||||
* updates while INDX_PAL_ENABLE is clear (except when fForce is true), but that's all we currently do.
|
||||
*
|
||||
* if (!(bOut & Card.ATC.INDX_PAL_ENABLE) && fPalEnabled) this.blankScreen();
|
||||
*
|
||||
* However, there also needs to be a delay, because when the IBM VGA BIOS changes the mode, it updates
|
||||
* the ATC palette registers in such a way that INDX_PAL_ENABLE is constantly toggled; here's one iteration:
|
||||
*
|
||||
* C000:2B39 EC IN AL,DX
|
||||
* C000:2B3A B2C0 MOV DL,C0
|
||||
* C000:2B3C 8BC3 MOV AX,BX
|
||||
* C000:2B3E 86C4 XCHG AL,AH
|
||||
* C000:2B40 EE OUT DX,AL <-- this ATC index value does NOT contain 0x20
|
||||
* C000:2B41 86C4 XCHG AL,AH
|
||||
* C000:2B43 EE OUT DX,AL
|
||||
* C000:2B44 B020 MOV AL,20
|
||||
* C000:2B46 EE OUT DX,AL <-- this ATC index value obviously DOES contain 0x20
|
||||
*/
|
||||
}
|
||||
/*
|
||||
* HACK: offStartAddr is supposed to be "latched" ONLY at the start of every VRETRACE interval,
|
||||
* but other "triggers" are helpful; see updateScreen() for details.
|
||||
* HACK: offStartAddr is supposed to be "latched" ONLY at the start of every VRETRACE interval, but
|
||||
* other "triggers" are helpful; see updateScreen() for details.
|
||||
*/
|
||||
card.offStartAddr = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
|
||||
card.nVertPeriodsStartAddr = 0;
|
||||
|
|
|
|||
|
|
@ -1635,7 +1635,7 @@ X86CPU.prototype.checkIntNotify = function(nInt)
|
|||
var aNotify = this.aIntNotify[nInt];
|
||||
if (aNotify !== undefined) {
|
||||
for (var i = 0; i < aNotify.length; i++) {
|
||||
if (!aNotify[i](this.regLIP)) {
|
||||
if (!aNotify[i](aNotify[i][0], this.regLIP)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3907,6 +3907,40 @@ X86CPU.prototype.popWord = function()
|
|||
return w;
|
||||
};
|
||||
|
||||
/**
|
||||
* pushData(d, size)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} d is the data to push at current SP; SP decreased by size
|
||||
* @param {number} size is the size of the data to push (must be either 2 or 4)
|
||||
*/
|
||||
X86CPU.prototype.pushData = function(d, size)
|
||||
{
|
||||
this.regLSP = (this.regLSP - size)|0;
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
*/
|
||||
if (((this.regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ this.regLSP) >= 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
this.setSP((this.regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
} else {
|
||||
X86.fnFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
}
|
||||
}
|
||||
if (size == 2) {
|
||||
this.setShort(this.regLSP, d);
|
||||
} else {
|
||||
this.setLong(this.regLSP, d);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* pushWord(w)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -376,6 +376,15 @@ X86.fnCALLw = function CALLw(dst, src)
|
|||
* For protected-mode, this function must attempt to load the new code segment first, because if the new segment
|
||||
* requires a change in privilege level, the return address must be pushed on the NEW stack, not the current stack.
|
||||
*
|
||||
* Also, we rely on a new function, pushData(), instead of pushWord(), to accommodate the outgoing segment size,
|
||||
* which may differ from the incoming segment. For example, when a 32-bit code segment performs a 16:32 call to a
|
||||
* 16-bit code segment, we must push 32-bit segment and offset values.
|
||||
*
|
||||
* TODO: Since setCSIP() already informs the segCS load() function when it's making a call, the load() function
|
||||
* could automatically push the old CS and IP values *before* segCS is updated (which would be a better time to do
|
||||
* this); unfortunately, load() is also used by loadIDT(), and loadIDT() has different requirements (eg, pushing
|
||||
* flags first), so it's not a trivial change.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} off
|
||||
* @param {number} sel
|
||||
|
|
@ -384,9 +393,10 @@ X86.fnCALLF = function CALLF(off, sel)
|
|||
{
|
||||
var oldCS = this.getCS();
|
||||
var oldIP = this.getIP();
|
||||
var oldSize = this.segCS.sizeData;
|
||||
if (this.setCSIP(off, sel, true) != null) {
|
||||
this.pushWord(oldCS);
|
||||
this.pushWord(oldIP);
|
||||
this.pushData(oldCS, oldSize);
|
||||
this.pushData(oldIP, oldSize);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1241,7 +1251,6 @@ X86.fnINT = function INT(nIDT, nError, nCycles)
|
|||
this.pushWord(oldIP);
|
||||
if (nError != null) this.pushWord(nError);
|
||||
this.nFault = -1;
|
||||
|
||||
/*
|
||||
* TODO: Should this code be factored into a setLIP() function? The other primary client would be setCSIP().
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -687,12 +687,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE286_INT || type == X86.DESC.ACC.TYPE.GATE386_INT) {
|
||||
fGate = true;
|
||||
regPSClear = (X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
cpu.assert(!(acc & 0x1f));
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP || type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
|
||||
fGate = true;
|
||||
regPSClear = (X86.PS.NT | X86.PS.TF);
|
||||
regPSClear = (X86.PS.VM | X86.PS.NT | X86.PS.TF);
|
||||
cpu.assert(!(acc & 0x1f));
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
|
||||
|
|
@ -725,22 +725,6 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
limit = limitOrig | (ext << 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* At a minimum, we need to clear X86.PS.VM now, so that the following load will initialize the
|
||||
* new code segment properly.
|
||||
*/
|
||||
var regPS = cpu.regPS;
|
||||
cpu.regPS &= ~regPSClear;
|
||||
if (cpu.regPS & X86.PS.VM) {
|
||||
/*
|
||||
* TODO: This seems a bit suspect in retrospect (ie, altering CPU flags before we know whether
|
||||
* the load() will succeed or generate a fault). Take another look.
|
||||
*/
|
||||
cpu.assert(false);
|
||||
cpu.regPS &= ~X86.PS.VM;
|
||||
cpu.setProtMode(true, false);
|
||||
}
|
||||
|
||||
var cplNew = (selCode & X86.SEL.RPL), selStack = 0, offStack = 0;
|
||||
|
||||
/*
|
||||
|
|
@ -772,6 +756,15 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
offStack = (lenSP == 2)? cpu.getShort(addrTSS + offSP) : cpu.getLong(addrTSS + offSP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we're past all the probes, it should be safe to clear all flags that need clearing.
|
||||
*/
|
||||
var regPS = cpu.regPS;
|
||||
cpu.regPS &= ~regPSClear;
|
||||
if (regPS & X86.PS.VM) {
|
||||
cpu.setProtMode(true, false);
|
||||
}
|
||||
|
||||
if (this.loadProt(selCode) === X86.ADDR_INVALID) {
|
||||
return X86.ADDR_INVALID;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue