v1.19.3: More Windows 95-related updates

This commit is contained in:
Jeff Parsons 2015-09-01 16:19:20 -07:00
commit 1a67c7acbd
33 changed files with 8907 additions and 1741 deletions

View file

@ -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 || ';';

View file

@ -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
};

View file

@ -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;

View file

@ -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)
*

View file

@ -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().
*/

View file

@ -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;
}