Added server support for running uncompiled machines

This commit is contained in:
Jeff Parsons 2015-01-17 13:11:31 -08:00 committed by jeffpar
commit c5d9b7dfcc
22 changed files with 2939 additions and 2753 deletions

View file

@ -1738,14 +1738,10 @@ HTMLOut.prototype.getRandomString = function(sIndent)
*
* At a minimum, each machine object should contain the following properties:
*
* 'class' (ie, a machine class, such as "pc" or "c1p")
* 'version' (eg, "1.10", or "*" to select the current version; "*" is the default)
* 'class' (eg, a machine class, such as "pc" or "c1p")
* 'version' (eg, "1.10", "*" to select the current version, or "uncompiled"; "*" is the default)
* 'debugger' (eg, true or false; false is the default)
*
* Additional properties can include:
*
* 'compiled' (eg, true or false); if not defined, we choose a value based on the module's fDebug setting
*
* @this {HTMLOut}
* @param {Array} aMachines is an array of objects containing information about each machine on the current page
* @param {function()} done
@ -1760,20 +1756,18 @@ HTMLOut.prototype.processMachines = function(aMachines, done)
var sClass = infoMachine['class']; // aka the machine class
var fCompiled = !this.fDebug;
var sVersion = infoMachine['version'];
if (sVersion === undefined) sVersion = "*"; // default to newest version
if (sVersion === undefined || sVersion == '*') {
sVersion = pkg.version;
} else {
fCompiled = (sVersion != "uncompiled");
}
var fDebugger = infoMachine['debugger'];
if (fDebugger === undefined) fDebugger = false; // default to no debugger
var fCompiled = infoMachine['compiled'];
if (fCompiled === undefined) fCompiled = !this.fDebug;
if (sVersion == "*") {
sVersion = pkg.version;
} else {
fCompiled = true; // use of a specific version requires using the compiled version
}
var fNoDebug = !fCompiled && net.hasParm(net.GORT_COMMAND, net.GORT_NODEBUG, this.req);
var fNoDebug = !this.fDebug || net.hasParm(net.GORT_COMMAND, net.GORT_NODEBUG, this.req);
var sScriptEmbed = "";
if (infoMachine['func']) {

View file

@ -120,8 +120,8 @@ var aExternalRedirects = {
};
var aExternalRedirectPatterns = {
"^/configs/c1p/machines/(.*)": "/devices/c1p/machine/$1",
"^/configs/pc/machines/(.*)": "/devices/pc/machine/$1"
"^/configs/c1p/machines/?(.*)": "/devices/c1p/machine/$1",
"^/configs/pc/machines/?(.*)": "/devices/pc/machine/$1"
};
/*

View file

@ -967,13 +967,13 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
cMatches++;
this.addMachine({
'class': sMachineClass,
'class': sMachineClass, // eg, a machine class, such as "pc" or "c1p"
'func': sMachineFunc,
'id': sMachineID,
'xml': sMachineXMLFile,
'xsl': sMachineXSLFile,
'version': sMachineVersion,
'debugger': fDebugger,
'version': sMachineVersion, // eg, "1.10", "*" to select the current version, or "uncompiled"; "*" is the default
'debugger': fDebugger, // eg, true or false; false is the default
'state': sMachineState}
);
}

View file

@ -169,7 +169,7 @@ function Bus(parmsBus, cpu, dbg)
*
* obj: a reference to the source object (eg, ROM object, Sector object)
* off: the offset within the source object that this object refers to
* slot: the slot in abtObjects which this object currently occupies
* slot: the slot (+1) in abtObjects which this object currently occupies
* refs: the number of memory references, as recorded by writeBackTrack()
*/
this.abtObjects = [];
@ -184,13 +184,54 @@ function Bus(parmsBus, cpu, dbg)
Component.subclass(Component, Bus);
if (BACKTRACK) {
/*
* BackTrack indexes are 31-bit values, where bits 0-8 store an object offset (0-511) and bits 16-30 store
* an object number (1-32767). Object number 0 is reserved for dynamic data (ie, data created independent
* of any source); examples include zero values produced by instructions such as "SUB AX,AX" or "XOR AX,AX".
* We must special-case instructions like that, because even though AX will almost certainly contain some source
* data prior to the instruction, the result no longer has any connection to the source. Similarly, "SBB AX,AX"
* may produce 0 or -1, depending on carry, but since we don't track the source of individual bits (including the
* carry flag), AX is now source-less. TODO: This is an argument for maintaining source info on selected flags,
* even though it would be rather expensive.
*
* The 7 middle bits (9-15) record type and access information, as follows:
*
* bit 15: set to indicate a "data" byte, clear to indicate a "code" byte
*
* All bytes start out as "data" bytes; only once they've been executed do they become "code" bytes. For code
* bytes, the remaining 6 middle bits (9-14) represent an execution count that starts at 1 (on the byte's initial
* transition from data to code) and tops out at 63.
*
* For data bytes, the remaining middle bits indicate any transformations the data has undergone; eg:
*
* bit 14: ADD/SUB/INC/DEC
* bit 13: MUL/DIV
* bit 12: OR/AND/XOR/NOT
*
* We make no attempt to record the original data or the transformation data, only that the transformation occurred.
*
* Other middle bits indicate whether the data was ever read and/or written:
*
* bit 11: READ
* bit 10: WRITE
*
* Bit 9 is reserved for now.
*/
Bus.BACKTRACK = {
SLOT_MAX: 32768,
SLOT_SHIFT: 16,
GEN_START: 1,
GEN_MAX: 64,
GEN_SHIFT: 9,
OFF_MAX: 512
SLOT_MAX: 32768,
SLOT_SHIFT: 16,
TYPE_DATA: 0x8000,
TYPE_ADDSUB: 0x4000,
TYPE_MULDIV: 0x2000,
TYPE_LOGICAL: 0x1000,
TYPE_READ: 0x0800,
TYPE_WRITE: 0x0400,
TYPE_COUNT_INC: 0x0200,
TYPE_COUNT_MAX: 0x7E00,
TYPE_MASK: 0xFE00,
TYPE_SHIFT: 9,
OFF_MAX: 512,
OFF_MASK: 0x1FF
};
}
@ -602,7 +643,8 @@ Bus.prototype.addBackTrackObject = function(obj, bto, off)
*/
}
this.assert(slot < Bus.BACKTRACK.SLOT_MAX);
this.ibtLastAlloc = bto.slot = slot;
this.ibtLastAlloc = slot;
bto.slot = slot + 1;
if (slot == cbtObjects) {
this.abtObjects.push(bto);
} else {
@ -626,7 +668,7 @@ Bus.prototype.getBackTrackIndex = function(bto, off)
{
var bti = 0;
if (BACKTRACK && bto) {
bti = (bto.slot << Bus.BACKTRACK.SLOT_SHIFT) | (Bus.BACKTRACK.GEN_START << Bus.BACKTRACK.GEN_SHIFT) | (off - bto.off);
bti = (bto.slot << Bus.BACKTRACK.SLOT_SHIFT) | Bus.BACKTRACK.TYPE_DATA | (off - bto.off);
}
return bti;
};
@ -643,7 +685,7 @@ Bus.prototype.writeBackTrackObject = function(addr, bto, off)
{
if (BACKTRACK && bto) {
this.assert(off - bto.off >= 0 && off - bto.off < Bus.BACKTRACK.OFF_MAX);
var bti = (bto.slot << Bus.BACKTRACK.SLOT_SHIFT) | (Bus.BACKTRACK.GEN_START << Bus.BACKTRACK.GEN_SHIFT) | (off - bto.off);
var bti = (bto.slot << Bus.BACKTRACK.SLOT_SHIFT) | Bus.BACKTRACK.TYPE_DATA | (off - bto.off);
this.writeBackTrack(addr, bti);
}
};
@ -677,8 +719,8 @@ Bus.prototype.writeBackTrack = function(addr, bti)
var btiPrev = this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
var slotPrev = btiPrev >>> Bus.BACKTRACK.SLOT_SHIFT;
if (slot != slotPrev) {
if (btiPrev) {
var btoPrev = this.abtObjects[slotPrev];
if (btiPrev && slotPrev) {
var btoPrev = this.abtObjects[slotPrev-1];
if (!btoPrev) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.WARN)) {
this.dbg.message("writeBackTrack(%" + str.toHex(addr) + ',' + str.toHex(bti) + "): previous index (" + str.toHex(btiPrev) + ") refers to empty slot (" + slotPrev + ")");
@ -698,7 +740,7 @@ Bus.prototype.writeBackTrack = function(addr, bti)
* slots with a ref count of zero; in the latter case, it should again check for weak references,
* after which we can re-use the slot if all its weak references are now gone.
*/
if (!this.isBackTrackWeak(btiPrev)) this.abtObjects[slotPrev] = null;
if (!this.isBackTrackWeak(btiPrev)) this.abtObjects[slotPrev-1] = null;
/*
* TODO: Consider what the appropriate trigger should be for resetting ibtLastDelete to zero;
* if we don't OCCASIONALLY set it to zero, we may never clear out obsolete weak references,
@ -707,11 +749,11 @@ Bus.prototype.writeBackTrack = function(addr, bti)
*
* I'd prefer to do something like this:
*
* if (this.ibtLastDelete > slotPrev) this.ibtLastDelete = slotPrev;
* if (this.ibtLastDelete > slotPrev-1) this.ibtLastDelete = slotPrev-1;
*
* or even this:
*
* if (this.ibtLastDelete > slotPrev) this.ibtLastDelete = 0;
* if (this.ibtLastDelete > slotPrev-1) this.ibtLastDelete = 0;
*
* But neither one of those guarantees that we will at least occasionally scan the entire table.
*/
@ -719,8 +761,8 @@ Bus.prototype.writeBackTrack = function(addr, bti)
this.cbtDeletions++;
}
}
if (bti) {
var bto = this.abtObjects[slot];
if (bti && slot) {
var bto = this.abtObjects[slot-1];
if (bto) {
this.assert(slot == bto.slot);
bto.refs++;
@ -739,24 +781,83 @@ Bus.prototype.writeBackTrack = function(addr, bti)
Bus.prototype.isBackTrackWeak = function(bti)
{
var bt = this.cpu.backTrack;
var slot = bti >>> Bus.BACKTRACK.SLOT_SHIFT;
return (bt.btiAL >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiAH >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBL >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBH >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiCL >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiCH >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDL >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDH >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBPLo >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBPHi >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiSILo >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiSIHi >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDILo >>> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDIHi >>> Bus.BACKTRACK.SLOT_SHIFT == slot
var slot = bti >> Bus.BACKTRACK.SLOT_SHIFT;
return (bt.btiAL >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiAH >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBL >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBH >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiCL >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiCH >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDL >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDH >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBPLo >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiBPHi >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiSILo >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiSIHi >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDILo >> Bus.BACKTRACK.SLOT_SHIFT == slot ||
bt.btiDIHi >> Bus.BACKTRACK.SLOT_SHIFT == slot
);
};
/**
* updateBackTrackCode(addr, bti)
*
* @this {Bus}
* @param {number} addr is a physical (non-segmented) address
* @param {number} bti
*/
Bus.prototype.updateBackTrackCode = function(addr, bti)
{
if (BACKTRACK) {
if (bti & Bus.BACKTRACK.TYPE_DATA) {
bti = (bti & ~Bus.BACKTRACK.TYPE_MASK) | Bus.BACKTRACK.TYPE_COUNT_INC;
} else if ((bti & Bus.BACKTRACK.TYPE_MASK) < Bus.BACKTRACK.TYPE_COUNT_MAX) {
bti += Bus.BACKTRACK.TYPE_COUNT_INC;
} else {
return;
}
this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
}
};
/**
* getBackTrackInfo(bti)
*
* @this {Bus}
* @param {number} bti
* @return {string|null}
*/
Bus.prototype.getBackTrackInfo = function(bti)
{
if (BACKTRACK) {
var slot = bti >>> Bus.BACKTRACK.SLOT_SHIFT;
if (slot) {
var off = bti & Bus.BACKTRACK.OFF_MASK;
var bto = this.abtObjects[slot-1];
var file = bto.obj.file;
if (file) {
this.assert(!bto.off);
return file.sName + '[' + (bto.obj.offFile + off) + ']';
}
return bto.obj.idComponent + '[' + (bto.off + off) + ']';
}
}
return null;
};
/**
* getBackTrackInfoFromAddr(addr)
*
* @this {Bus}
* @param {number} addr
* @return {string|null}
*/
Bus.prototype.getBackTrackInfoFromAddr = function(addr)
{
var bti = this.readBackTrack(addr);
return this.getBackTrackInfo(bti);
};
/**
* saveMemory()
*

View file

@ -258,17 +258,17 @@ if (DEBUGGER) {
FCOM: 40, FCOMP: 41, FDIV: 42, FDIVR: 43, FIADD: 44, FICOM: 45, FICOMP: 46, FIDIV: 47,
FIDIVR: 48, FILD: 49, FIMUL: 50, FIST: 51, FISTP: 52, FISUB: 53, FISUBR: 54, FLD: 55,
FLDCW: 56, FLDENV: 57, FMUL: 58, FNSAVE: 59, FNSTCW: 60, FNSTENV:61, FNSTSW: 62, FRSTOR: 63,
FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GBP: 69, GS: 70, HLT: 71,
IDIV: 72, IMUL: 73, IN: 74, INC: 75, INS: 76, INT: 77, INT3: 78, INTO: 79,
IRET: 80, JBE: 81, JC: 82, JCXZ: 83, JG: 84, JGE: 85, JL: 86, JLE: 87,
JMP: 88, JNBE: 89, JNC: 90, JNO: 91, JNP: 92, JNS: 93, JNZ: 94, JO: 95,
JP: 96, JS: 97, JZ: 98, LAHF: 99, LAR: 100, LDS: 101, LEA: 102, LEAVE: 103,
LES: 104, LFS: 105, LGDT: 106, LGS: 107, LIDT: 108, LLDT: 109, LMSW: 110, LOADALL:111,
LOCK: 112, LODSB: 113, LODSW: 114, LOOP: 115, LOOPNZ: 116, LOOPZ: 117, LSL: 118, LSS: 119,
LTR: 120, MOV: 121, MOVSB: 122, MOVSW: 123, MOVSX: 124, MOVZX: 125, MUL: 126, NEG: 127,
NOP: 128, NOT: 129, OR: 130, OSIZE: 131, OUT: 132, OUTS: 133, POP: 134, POPA: 135,
POPF: 136, PUSH: 137, PUSHA: 138, PUSHF: 139, RCL: 140, RCR: 141, REPNZ: 142, REPZ: 143,
RET: 144, RETF: 145, ROL: 146, ROR: 147, SAHF: 148, SAR: 149, SBB: 150, SCASB: 151,
FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GS: 69, HLT: 70, IDIV: 71,
IMUL: 72, IN: 73, INC: 74, INS: 75, INT: 76, INT3: 77, INTO: 78, IRET: 79,
JBE: 80, JC: 81, JCXZ: 82, JG: 83, JGE: 84, JL: 85, JLE: 86, JMP: 87,
JNBE: 88, JNC: 89, JNO: 90, JNP: 91, JNS: 92, JNZ: 93, JO: 94, JP: 95,
JS: 96, JZ: 97, LAHF: 98, LAR: 99, LDS: 100, LEA: 101, LEAVE: 102, LES: 103,
LFS: 104, LGDT: 105, LGS: 106, LIDT: 107, LLDT: 108, LMSW: 109, LOADALL:110, LOCK: 111,
LODSB: 112, LODSW: 113, LOOP: 114, LOOPNZ: 115, LOOPZ: 116, LSL: 117, LSS: 118, LTR: 119,
MOV: 120, MOVSB: 121, MOVSW: 122, MOVSX: 123, MOVZX: 124, MUL: 125, NEG: 126, NOP: 127,
NOT: 128, OR: 129, OSIZE: 130, OUT: 131, OUTS: 132, POP: 133, POPA: 134, POPF: 135,
PUSH: 136, PUSHA: 137, PUSHF: 138, RCL: 139, RCR: 140, REPNZ: 141, REPZ: 142, RET: 143,
RETF: 144, ROL: 145, ROR: 146, SAHF: 147, SALC: 148, SAR: 149, SBB: 150, SCASB: 151,
SCASW: 152, SETBE: 153, SETC: 154, SETG: 155, SETGE: 156, SETL: 157, SETLE: 158, SETNBE: 159,
SETNC: 160, SETNO: 161, SETNP: 162, SETNS: 163, SETNZ: 164, SETO: 165, SETP: 166, SETS: 167,
SETZ: 168, SGDT: 169, SHL: 170, SHLD: 171, SHR: 172, SHRD: 173, SIDT: 174, SLDT: 175,
@ -290,17 +290,17 @@ if (DEBUGGER) {
"FCOM", "FCOMP", "FDIV", "FDIVR", "FIADD", "FICOM", "FICOMP", "FIDIV",
"FIDIVR", "FILD", "FIMUL", "FIST", "FISTP", "FISUB", "FISUBR", "FLD",
"FLDCW", "FLDENV", "FMUL", "FNSAVE", "FNSTCW", "FNSTENV","FNSTSW", "FRSTOR",
"FS:", "FST", "FSTP", "FSUB", "FSUBR", "GBP", "GS:", "HLT",
"IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO",
"IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE",
"JMP", "JNBE", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO",
"JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE",
"LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL",
"LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS",
"LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG",
"NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA",
"POPF", "PUSH", "PUSHA", "PUSHF", "RCL", "RCR", "REPNZ", "REPZ",
"RET", "RETF", "ROL", "ROR", "SAHF", "SAR", "SBB", "SCASB",
"FS:", "FST", "FSTP", "FSUB", "FSUBR", "GS:", "HLT", "IDIV",
"IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO", "IRET",
"JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE", "JMP",
"JNBE", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO", "JP",
"JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE", "LES",
"LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL","LOCK",
"LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS", "LTR",
"MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG", "NOP",
"NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA", "POPF",
"PUSH", "PUSHA", "PUSHF", "RCL", "RCR", "REPNZ", "REPZ", "RET",
"RETF", "ROL", "ROR", "SAHF", "SALC", "SAR", "SBB", "SCASB",
"SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL", "SETLE", "SETNBE",
"SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO", "SETP", "SETS",
"SETZ", "SGDT", "SHL", "SHLD", "SHR", "SHRD", "SIDT", "SLDT",
@ -812,7 +812,7 @@ if (DEBUGGER) {
/* 0xD3 */ [Debugger.INS.GRP2WC,Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_BOTH, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN],
/* 0xD4 */ [Debugger.INS.AAM, Debugger.TYPE_IMM | Debugger.TYPE_BYTE],
/* 0xD5 */ [Debugger.INS.AAD, Debugger.TYPE_IMM | Debugger.TYPE_BYTE],
/* 0xD6 */ [Debugger.INS.GBP],
/* 0xD6 */ [Debugger.INS.SALC],
/* 0xD7 */ [Debugger.INS.XLAT],
/* 0xD8 */ [Debugger.INS.ESC, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN],
@ -3643,6 +3643,7 @@ if (DEBUGGER) {
sDumpers += ",state,symbols";
this.println("\ndump commands:");
this.println("\tdb [a] [#] dump # bytes at address a");
if (BACKTRACK) this.println("\tdi [a] dump backtrack info at address a");
this.println("\tdw [a] [#] dump # words at address a");
if (sDumpers.length) this.println("dump extensions:\n\t" + sDumpers);
return;
@ -3669,43 +3670,42 @@ if (DEBUGGER) {
var aAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
if (aAddr[0] == null) return;
var cLines = 0;
var fWords = (sCmd == "dw");
if (sLen !== undefined) {
if (sLen.charAt(0) == "l")
sLen = sLen.substr(1);
cLines = parseInt(sLen, 10);
if (cLines) {
if (fWords)
cLines = (cLines + 7) >> 3;
else
cLines = (cLines + 15) >> 4;
}
}
var sDump = "";
if (!cLines) cLines = 8;
for (var iLine = 0; iLine < cLines; iLine++) {
var sBytes = "";
var sChars = "";
sAddr = this.hexAddr(aAddr);
var bPrev = 0;
for (var i = 0; i < 16; i++) {
var b = this.getByte(aAddr, 1);
if (fWords) {
if (i & 0x1) {
sBytes += str.toHexWord(bPrev | (b << 8)) + (i == 7? " - " : " ");
}
}
else {
sBytes += str.toHexByte(b) + (i == 7? "-" : " ");
}
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : ".");
bPrev = b;
if (sCmd == "di") {
var addr = this.getAddr(aAddr);
sDump += '%' + str.toHex(addr) + ": ";
var sInfo = this.bus.getBackTrackInfoFromAddr(addr);
sDump += sInfo || "no information";
}
else {
var cLines = 0;
var fWords = (sCmd == "dw");
if (sLen !== undefined) {
if (sLen.charAt(0) == "l") sLen = sLen.substr(1);
cLines = parseInt(sLen, 10);
if (cLines) cLines = fWords? ((cLines + 7) >> 3) : ((cLines + 15) >> 4);
}
if (!cLines) cLines = 8;
for (var iLine = 0; iLine < cLines; iLine++) {
var bPrev = 0;
var sBytes = "", sChars = "";
sAddr = this.hexAddr(aAddr);
for (var i = 0; i < 16; i++) {
var b = this.getByte(aAddr, 1);
if (fWords) {
if (i & 0x1) {
sBytes += str.toHexWord(bPrev | (b << 8)) + (i == 7? " - " : " ");
}
}
else {
sBytes += str.toHexByte(b) + (i == 7? "-" : " ");
}
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : ".");
bPrev = b;
}
if (sDump) sDump += "\n";
sDump += sAddr + " " + sBytes + " " + sChars;
}
if (sDump) sDump += "\n";
sDump += sAddr + " " + sBytes + " " + sChars;
}
if (sDump) this.println(sDump);
this.aAddrNextData = aAddr;
@ -4746,7 +4746,7 @@ if (DEBUGGER) {
if (this.cmp) this.cmp.reset();
return true;
case "ver":
this.println((APPNAME || "PCjs") + " version " + APPVERSION + " (" + (COMPILED? "release" : (DEBUG? "debug" : "nodebug")) + (PREFETCH? ",prefetch" : ",noprefetch") + (EAFUNCS? "eafuncs" : ",eatests") + (TYPEDARRAYS? ",typedarrays" : (FATARRAYS? ",fatarrays" : ",dwordarrays")) + ")");
this.println((APPNAME || "PCjs") + " version " + APPVERSION + " (" + (COMPILED? "RELEASE" : (DEBUG? "DEBUG" : "NODEBUG")) + (PREFETCH? ",PREFETCH" : ",NOPREFETCH") + (EAFUNCS? "EAFUNCS" : ",EATESTS") + (TYPEDARRAYS? ",TYPEDARRAYS" : (FATARRAYS? ",FATARRAYS" : ",DWORDARRAYS")) + ")");
return true;
default:
ch0 = sCmd.charAt(0);

View file

@ -1404,8 +1404,8 @@ HDC.prototype.inATCData = function(port, addrFrom)
/*
* TODO: We could define a cached BTO that's reset prior to a new ATC command, and then pass that
* to addBackTrackObject() here instead of null; but for now, we're going to rely on that function's
* simplistic MRU cache. If that fails, the worst that will (or should) happen is we'll burn through
* more BackTrack wrapper objects than necessary, and run the risk of running out.
* simplistic MRU logic. If that fails, the worst that will (or should) happen is we'll burn through
* more BackTrack wrapper objects than necessary, and risk running out.
*/
var bto = hdc.bus.addBackTrackObject(obj, null, off);
hdc.cpu.backTrack.btiIO = hdc.bus.getBackTrackIndex(bto, off);

View file

@ -104,7 +104,6 @@ if (typeof module !== 'undefined') {
function Memory(addr, size, fReadOnly, controller)
{
var i;
this.cb = size || 0;
this.adw = null;
this.offset = 0;
@ -113,7 +112,7 @@ function Memory(addr, size, fReadOnly, controller)
this.fDirty = this.fDirtyEver = false;
if (BACKTRACK) {
if (this.fReadOnly || !size || controller) {
if (!size || controller) {
this.readBackTrack = Memory.readBackTrackNone;
this.writeBackTrack = Memory.writeBackTrackNone;
} else {

View file

@ -68,10 +68,11 @@ function ROM(parmsROM)
this.addrROM = parmsROM['addr'];
this.sizeROM = parmsROM['size'];
this.addrROMAlias = parmsROM['alias'];
this.sFileName = parmsROM['file'];
this.sFilePath = parmsROM['file'];
this.sFileName = str.getBaseName(this.sFilePath);
this.idNotify = parmsROM['notify'];
if (this.sFileName) {
var sFileURL = this.sFileName;
if (this.sFilePath) {
var sFileURL = this.sFilePath;
if (DEBUG) this.log('load("' + sFileURL + '")');
/*
* If the selected ROM file has a ".json" extension, then we assume it's pre-converted
@ -80,7 +81,7 @@ function ROM(parmsROM)
*/
var sFileExt = str.getExtension(this.sFileName);
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFileName + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
}
web.loadResource(sFileURL, true, null, this, ROM.prototype.onLoadROM);
}
@ -259,7 +260,7 @@ ROM.prototype.onLoadROM = function(sROMFile, sROMData, nErrorCode)
ROM.prototype.copyROM = function()
{
if (!this.isReady()) {
if (!this.sFileName) {
if (!this.sFilePath) {
this.setReady();
}
else if (this.abROM && this.bus) {
@ -317,11 +318,11 @@ ROM.prototype.addROM = function(addr)
if (this.bus.addMemory(addr, this.sizeROM, true)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexAddr(addr) + " (0x" + str.toHex(this.abROM.length) + " bytes)");
var bto = null;
for (var i = 0; i < this.abROM.length; i++) {
this.bus.setByteDirect(addr + i, this.abROM[i]);
for (var off = 0; off < this.abROM.length; off++) {
this.bus.setByteDirect(addr + off, this.abROM[off]);
if (BACKTRACK) {
bto = this.bus.addBackTrackObject(this, bto, i);
this.bus.writeBackTrackObject(addr + i, bto, i);
bto = this.bus.addBackTrackObject(this, bto, off);
this.bus.writeBackTrackObject(addr + off, bto, off);
}
}
return true;

View file

@ -168,7 +168,8 @@ function Video(parmsVideo, canvas, context, textarea)
this.addrBuffer = this.sizeBuffer = 0;
/*
* aFonts is an array of font objects indexed by FONT ID. Font characters are * arranged in 16x16 grids, with one grid per canvas object in the aCanvas array of each font object.
* aFonts is an array of font objects indexed by FONT ID. Font characters are arranged
* in 16x16 grids, with one grid per canvas object in the aCanvas array of each font object.
*
* Each element is a Font object that describes the font size and provides bitmaps for all the font
* color permutations. aFonts.length will be non-zero if ANY fonts are loaded, but do NOT assume
@ -273,12 +274,12 @@ Video.TRAPALL = true; // monitor all I/O by default (not just deltas)
* In fact, there's special logic in setDimensions() to ignore fScaleFont in certain cases (eg,
* 40-column modes, to improve sharpness and avoid stretching the font beyond readability).
*
* Graphics modes, on the other hand, are always scaled to the window size. Pixels are captured
* Graphics modes, on the other hand, are always scaled to the window size. Pixels are captured
* in an off-screen buffer, which is then drawn to match the size of the virtual display window.
*
* TODO: Whenever there are borders, they should be filled with the CGA's overscan colors. However,
* in the case of graphics modes (and text modes whenever font scaling is enabled), we don't reserve
* any space for borders, so if borders are important, explicit support will be required.
* any space for borders, so if borders are important, explicit border support will be required.
*
* EGA Support
*
@ -1977,7 +1978,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
this.kbd.setBinding(this.textareaScreen? "textarea" : "canvas", "kbd", this.inputScreen);
}
this.bEGASW = 0x9; // our default "switches" setting (see aEGAMonitorSwitches)
this.bEGASW = 0x09; // our default "switches" setting (see aEGAMonitorSwitches)
this.chipset = cmp.getComponentByType("ChipSet");
if (this.chipset && this.sEGASW) {
this.bEGASW = this.chipset.parseSwitches(this.sEGASW, this.bEGASW);
@ -2262,6 +2263,7 @@ Video.prototype.processTouchEvent = function(event, fStart)
* retains focus, preventDefault() will always be called.
*/
if (this.fHasFocus) event.preventDefault();
/*
* Touch coordinates (that is, the pageX and pageY properties) are relative to the page, so to make
* them relative to the canvas, we must subtract the canvas's left and top positions. This Apple web page:
@ -2280,12 +2282,14 @@ Video.prototype.processTouchEvent = function(event, fStart)
yTouchOffset += eCurrent.offsetTop;
}
} while ((eCurrent = eCurrent.offsetParent));
/*
* Due to the responsive nature of our pages, the displayed size of the canvas may be smaller than the allocated
* size, and the coordinates we receive from touch events are based on the currently displayed size.
*/
var xScale = this.cxScreen / this.canvasScreen.offsetWidth;
var yScale = this.cyScreen / this.canvasScreen.offsetHeight;
/**
* @name Event
* @property {Array} targetTouches
@ -2302,6 +2306,7 @@ Video.prototype.processTouchEvent = function(event, fStart)
yTouch = ((yTouch - yTouchOffset) * yScale);
var xThird = (xTouch / (this.cxScreen / 3)) | 0;
var yThird = (yTouch / (this.cyScreen / 3)) | 0;
/*
* At this point, xThird and yThird should both be one of 0, 1 or 2, indicating which horizontal and vertical
* third of the virtual screen the touch event occurred.
@ -2623,30 +2628,30 @@ Video.prototype.onLoadSetFonts = function(sFontFile, sFontData, nErrorCode)
* 0 0 0 0 0 0 0 0 <== 00 from offset 0x080E
* 0 0 0 0 0 0 0 0 <== 00 from offset 0x080F
*
* In the second 2K chunk, we observe that the last two bytes of every font cell definition are zero;
* this confirms our understanding that MDA font cell size is 8x14.
* In the second 2K chunk, we observe that the last two bytes of every font cell definition are zero;
* this confirms our understanding that MDA font cell size is 8x14.
*
* Finally, there's the issue of screen cell size, which is actually 9x14 on the MDA. We compensate for that
* by building a 9x14 font, even though there's only 8x14 bits of data. As http://www.seasip.info/VintagePC/mda.html
* explains:
* Finally, there's the issue of screen cell size, which is actually 9x14 on the MDA. We compensate for that
* by building a 9x14 font, even though there's only 8x14 bits of data. As http://www.seasip.info/VintagePC/mda.html
* explains:
*
* "For characters C0h-DFh, the ninth pixel column is a duplicate of the eighth; for others, it's blank."
*
* This last point is confirmed by "The IBM Personal Computer From The Inside Out", p.295:
* This last point is confirmed by "The IBM Personal Computer From The Inside Out", p.295:
*
* "Another unique feature of the monochrome adapter is a set of line-drawing and area-fill characters that give
* continuous lines and filled areas. This is unusual for a display with a 9x14 character box because the character
* generator provides a row only eight dots wide. On most displays, a blank 9th dot is then inserted between characters.
* On the monochrome display, there is circuitry that duplicates the 8th dot into the 9th dot position for characters
* whose ASCII codes are 0xB0 through 0xDF."
* "Another unique feature of the monochrome adapter is a set of line-drawing and area-fill characters
* that give continuous lines and filled areas. This is unusual for a display with a 9x14 character box
* because the character generator provides a row only eight dots wide. On most displays, a blank 9th
* dot is then inserted between characters. On the monochrome display, there is circuitry that duplicates
* the 8th dot into the 9th dot position for characters whose ASCII codes are 0xB0 through 0xDF."
*
* The only question is: is the range actually 0xC0-0xDF, or 0xB0-0xDF??? I'll assume the latter, since 0xB0 is where
* the line-drawing/area-fill characters appear to begin.
* The only question is: is the range actually 0xC0-0xDF, or 0xB0-0xDF??? I'll assume the latter, since
* 0xB0 is where the line-drawing/area-fill characters appear to begin.
*
* The CGA font is part of the same ROM. In fact, there are TWO CGA fonts in the ROM: a thin 5x7 "single dot" font
* located at offset 0x1000, and a thick 7x7 "double dot" font at offset 0x1800. The latter is the default font,
* unless overridden by a jumper setting on the CGA card, so it is our default CGA font as well (although someday we
* may provide a virtual jumper setting that allows you to select the thinner font).
* The CGA font is part of the same ROM. In fact, there are TWO CGA fonts in the ROM: a thin 5x7 "single dot"
* font located at offset 0x1000, and a thick 7x7 "double dot" font at offset 0x1800. The latter is the default
* font, unless overridden by a jumper setting on the CGA card, so it is our default CGA font as well (although
* someday we may provide a virtual jumper setting that allows you to select the thinner font).
*
* The first offset we pass to setFontData() is the offset of the MDA font. For the second (CGA) font offset,
* we choose the thicker "double dot" CGA font at 0x1800 (which was the PC's default font as well), instead
@ -2672,8 +2677,7 @@ Video.prototype.onLoadSetFonts = function(sFontFile, sFontData, nErrorCode)
/**
* onROMLoad(abRom)
*
* Called by ROM.prototype.copyImage() whenever a ROM with a 'notify' attribute set to our component ID
* has been loaded.
* Called by copyROM() whenever a ROM with a 'notify' attribute set to our component ID has been loaded.
*
* If model is "ega", then we assume the associated ROM is the original IBM EGA ROM, which stores
* its 8x14 font data at 0x2230 (and unlike the MDA/CGA character generator ROM, which splits the first
@ -2700,8 +2704,8 @@ Video.prototype.onROMLoad = function(abROM)
{
if (this.model == "ega") {
/*
* TODO: Unlike the MDA/CGA font data, we may want to hang onto this data, so that we can regenerate
* the color font(s) whenever the foreground and/or background colors have been changed.
* TODO: Unlike the MDA/CGA font data, we may want to hang onto this data, so that we can
* regenerate the color font(s) whenever the foreground and/or background colors have changed.
*/
if (DEBUG) this.printMessage("onROMLoad(): EGA fonts loaded");
this.setFontData(abROM, [0x2230, 0x3160], 8);

View file

@ -721,8 +721,8 @@ X86CPU.prototype.initProcessor = function()
this.aOps[X86.OPCODE.INSW] = X86OpXX.opINSw;
this.aOps[X86.OPCODE.OUTSB] = X86OpXX.opOUTSb;
this.aOps[X86.OPCODE.OUTSW] = X86OpXX.opOUTSw;
this.aOps[0xC0] = X86OpXX.opGrp2ab;
this.aOps[0xC1] = X86OpXX.opGrp2aw;
this.aOps[0xC0] = X86OpXX.opGrp2bi;
this.aOps[0xC1] = X86OpXX.opGrp2wi;
this.aOps[X86.OPCODE.ENTER] = X86OpXX.opENTER;
this.aOps[X86.OPCODE.LEAVE] = X86OpXX.opLEAVE;
this.aOps[0xF1] = X86OpXX.opINT1;
@ -858,6 +858,8 @@ X86CPU.prototype.resetRegs = function()
btiSIHi: 0,
btiDILo: 0,
btiDIHi: 0,
btiEALo: 0,
btiEAHi: 0,
btiMemLo: 0,
btiMemHi: 0,
btiIO: 0
@ -1175,8 +1177,8 @@ X86CPU.prototype.restore = function(data)
this.setPS(a[6]);
this.setIP(a[0]);
a = data[2];
this.segData = this.getSeg(a[0]);
this.segStack = this.getSeg(a[1]);
this.segData = a[0] != null && this.getSeg(a[0]) || this.segDS;
this.segStack = a[1] != null && this.getSeg(a[1]) || this.segSS;
this.opFlags = a[2];
this.opPrefixes = a[3];
this.intFlags = a[4];
@ -1971,7 +1973,9 @@ X86CPU.prototype.getEAByteEnabled = function getEAByteEnabled(seg, off)
this.segEA = seg;
this.regEA = seg.checkRead(this.offEA = off, 0);
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOREAD)) return 0;
return this.getByte(this.regEA);
var b = this.getByte(this.regEA);
if (BACKTRACK) this.backTrack.btiEALo = this.backTrack.btiMemLo;
return b;
};
/**
@ -1987,7 +1991,12 @@ X86CPU.prototype.getEAWordEnabled = function getEAWordEnabled(seg, off)
this.segEA = seg;
this.regEA = seg.checkRead(this.offEA = off, 1);
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOREAD)) return 0;
return this.getWord(this.regEA);
var w = this.getWord(this.regEA);
if (BACKTRACK) {
this.backTrack.btiEALo = this.backTrack.btiMemLo;
this.backTrack.btiEAHi = this.backTrack.btiMemHi;
}
return w;
};
/**
@ -2003,7 +2012,9 @@ X86CPU.prototype.modEAByteEnabled = function modEAByteEnabled(seg, off)
this.segEA = seg;
this.regEAWrite = this.regEA = seg.checkRead(this.offEA = off, 0);
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOREAD)) return 0;
return this.getByte(this.regEA);
var b = this.getByte(this.regEA);
if (BACKTRACK) this.backTrack.btiEALo = this.backTrack.btiMemLo;
return b;
};
/**
@ -2019,7 +2030,12 @@ X86CPU.prototype.modEAWordEnabled = function modEAWordEnabled(seg, off)
this.segEA = seg;
this.regEAWrite = this.regEA = seg.checkRead(this.offEA = off, 1);
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOREAD)) return 0;
return this.getWord(this.regEA);
var w = this.getWord(this.regEA);
if (BACKTRACK) {
this.backTrack.btiEALo = this.backTrack.btiMemLo;
this.backTrack.btiEAHi = this.backTrack.btiMemHi;
}
return w;
};
/**
@ -2031,6 +2047,7 @@ X86CPU.prototype.modEAWordEnabled = function modEAWordEnabled(seg, off)
X86CPU.prototype.setEAByteEnabled = function setEAByteEnabled(b)
{
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOWRITE)) return;
if (BACKTRACK) this.backTrack.btiMemLo = this.backTrack.btiEALo;
this.setByte(this.segEA.checkWrite(this.offEA, 0), b);
};
@ -2043,6 +2060,10 @@ X86CPU.prototype.setEAByteEnabled = function setEAByteEnabled(b)
X86CPU.prototype.setEAWordEnabled = function setEAWordEnabled(w)
{
if (!EAFUNCS && (this.opFlags & X86.OPFLAG.NOWRITE)) return;
if (BACKTRACK) {
this.backTrack.btiMemLo = this.backTrack.btiEALo;
this.backTrack.btiMemHi = this.backTrack.btiEAHi;
}
this.setWord(this.segEA.checkWrite(this.offEA, 1), w);
};
@ -2239,45 +2260,45 @@ X86CPU.prototype.advancePrefetch = function(inc)
/**
* getIPByte()
*
* NOTE: We don't need to mask the incoming regEIP, because regEIP is always masked after update.
*
* @this {X86CPU}
* @return {number} byte at the current IP; IP advanced by 1
*/
X86CPU.prototype.getIPByte = function()
{
var b = (PREFETCH? this.getBytePrefetch(this.regEIP) : this.getByte(this.regEIP));
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 1) & 0xffff); // this.advanceIP(1)
if (BACKTRACK) this.bus.updateBackTrackCode(this.regEIP, this.backTrack.btiMemLo);
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 1) & 0xffff); // this.advanceIP(1)
return b;
};
/**
* getIPDisp()
*
* NOTE: We don't need to mask the incoming regEIP, because regEIP is always masked after update.
*
* @this {X86CPU}
* @return {number} sign-extended value from the byte at the current IP; IP advanced by 1
*/
X86CPU.prototype.getIPDisp = function()
{
var b = ((PREFETCH? this.getBytePrefetch(this.regEIP) : this.getByte(this.regEIP)) << 24) >> 24;
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 1) & 0xffff); // this.advanceIP(1)
if (BACKTRACK) this.bus.updateBackTrackCode(this.regEIP, this.backTrack.btiMemLo);
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 1) & 0xffff); // this.advanceIP(1)
return b & 0xffff;
};
/**
* getIPWord()
*
* NOTE: We don't need to mask the incoming regEIP, because regEIP is always masked after update.
*
* @this {X86CPU}
* @return {number} word at the current IP; IP advanced by 2
*/
X86CPU.prototype.getIPWord = function()
{
var w = (PREFETCH? this.getWordPrefetch(this.regEIP) : this.getWord(this.regEIP));
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 2) & 0xffff); // this.advanceIP(2)
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regEIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regEIP + 1, this.backTrack.btiMemHi);
}
this.regEIP = this.segCS.base + (this.regIP = (this.regIP + 2) & 0xffff); // this.advanceIP(2)
return w;
};

View file

@ -1257,14 +1257,13 @@ var X86Grps = {
* use a mod/reg/rm byte, where the reg field of that byte selects a function rather than a register.
*
* I start with the groupings used by Intel's "Pentium Processor User's Manual (Volume 3: Architecture
* and Programming Manual)", but I deviate slightly, mostly by subdividing their groups with the use
* of suffixes:
* and Programming Manual)", but I deviate slightly, mostly by subdividing their groups with letter suffixes:
*
* Opcodes Intel PCjs PC Mag TechRef
* ------- ----- ---- --------------
* 0x80-0x83 Grp1 Grp1b, Grp1w, Grp1b, and Grp1sw Group A
* 0xC0-0xC1 Grp2a Grp2ab and Grp2aw Group B
* 0xD0-0xD3 Grp2 Grp2b and Grp2w Group B
* 0xC0-0xC1 Grp2 Grp2b and Grp2w (opGrp2bi/wi) Group B
* 0xD0-0xD3 Grp2 Grp2b and Grp2w (opGrp2b1/w1 and opGrp2bCL/wCL) Group B
* 0xF6-0xF7 Grp3 Grp3b and Grp3w Group C
* 0xFE Grp4 Grp4b Group D
* 0xFF Grp5 Grp4w Group E
@ -1278,6 +1277,11 @@ var X86Grps = {
* JMP and PUSH instructions, which are not in Grp4b, but there's nothing in Grp4b that conflicts with
* Grp4w, so I think my nomenclature makes more sense. To compensate, I don't use Grp5, so that the
* remaining group numbers remain in sync with Intel's.
*
* To the above list, I also add these "groups of 1": opcode 0x8F uses GrpPOPw, and opcodes 0xC6/0xC7 use
* GrpMOVImm. In both of these groups, the only valid (documented) instruction is where reg=0x0.
*
* TODO: Test what happens on real hardware when the reg field is non-zero for opcodes 0x8F and 0xC6/0xC7.
*/
X86Grps.aOpGrp1b = [
X86Grps.opGrpADDb, X86Grps.opGrpORb, X86Grps.opGrpADCb, X86Grps.opGrpSBBb, // 0x80/0x82(reg=0x0-0x3)
@ -1329,17 +1333,4 @@ X86Grps.aOpGrp4w = [
X86Grps.opGrpJMPw, X86Grps.opGrpJMPFdw, X86Grps.opGrpPUSHw, X86Grps.opGrpFault // 0xFF(reg=0x4-0x7)
];
/*
* The following are for 80186/80188 and up...
*/
X86Grps.aOpGrp2ab = [
X86Grps.opGrpROLb, X86Grps.opGrpRORb, X86Grps.opGrpRCLb, X86Grps.opGrpRCRb, // 0xC0(reg=0x0-0x3)
X86Grps.opGrpSHLb, X86Grps.opGrpSHRb, X86Grps.opGrpUndefined, X86Grps.opGrpSARb // 0xC0(reg=0x4-0x7)
];
X86Grps.aOpGrp2aw = [
X86Grps.opGrpROLw, X86Grps.opGrpRORw, X86Grps.opGrpRCLw, X86Grps.opGrpRCRw, // 0xC1(reg=0x0-0x3)
X86Grps.opGrpSHLw, X86Grps.opGrpSHRw, X86Grps.opGrpUndefined, X86Grps.opGrpSARw // 0xC1(reg=0x4-0x7)
];
if (typeof module !== 'undefined') module.exports = X86Grps;

View file

@ -176,6 +176,15 @@ var X86Help = {
*/
opHelpLEA: function(dst, src) {
if (this.regEA < 0) {
/*
* TODO: After reading http://www.os2museum.com/wp/undocumented-8086-opcodes/, it seems that this
* form of LEA (eg, "LEA AX,DX") simply returns the last calculated EA. Since we always reset regEA
* at the start of a new instruction, we would need to preserve the previous EA if we want to mimic
* that (undocumented) behavior.
*
* And for completeness, we would have to extend EA tracking beyond the usual ModRM instructions
* (eg, XLAT, instructions that modify the stack pointer, and string instructions). Anything else?
*/
X86Help.opHelpUndefined.call(this);
return dst;
}
@ -314,6 +323,24 @@ var X86Help = {
return dst;
},
/**
* opHelpXCHGrb(dst, src)
*
* If an instruction like "XCHG AL,AH" was a traditional "op dst,src" instruction, dst would contain AL,
* src would contain AH, and we would return src, which the caller would then store in AL, and we'd be done.
*
* However, that's only half of what XCHG does, so THIS function must perform the other half; in the previous
* example, that entails storing AL (dst) into AH (src).
*
* BACKTRACK support is incomplete without also passing bti values as parameters, because the caller will
* store btiAH in btiAL, but the original btiAL will be lost. Similarly, if src is a memory operand, the
* caller will store btiEALo in btiAL, but again, the original btiAL will be lost.
*
* BACKTRACK support for memory operands could be fixed by decoding the dst register in order to determine the
* corresponding bti and then temporarily storing it in btiEALo around the setEAByte() call below. Register-only
* XCHGs would require a more extensive hack. For now, I'm going to live with one-way BACKTRACK support here.
*
* TODO: Implement full BACKTRACK support for XCHG instructions.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -321,6 +348,9 @@ var X86Help = {
*/
opHelpXCHGrb: function(dst, src) {
if (this.regEA < 0) {
//
// Decode which register was src
//
switch (this.bModRM & 0x7) {
case 0x0: // AL
this.regAX = (this.regAX & ~0xff) | dst;
@ -351,10 +381,11 @@ var X86Help = {
}
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRR;
} else {
/*
* This is a case where the ModRM decoder that's calling us didn't know it should have called modEAByte()
* instead of getEAByte(), so we compensate by updating regEAWrite.
*/
//
// This is a case where the ModRM decoder that's calling us didn't know it should have called modEAByte()
// instead of getEAByte(), so we compensate by updating regEAWrite. However, setEAByte() has since been
// changed to revalidate the write using segEA:offEA, so updating regEAWrite here isn't strictly necessary.
//
this.regEAWrite = this.regEA;
this.setEAByte(dst);
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRM;
@ -362,6 +393,16 @@ var X86Help = {
return src;
},
/**
* opHelpXCHGrw(dst, src)
*
* If an instruction like "XCHG AX,DX" was a traditional "op dst,src" instruction, dst would contain AX,
* src would contain DX, and we would return src, which the caller would then store in AX, and we'd be done.
*
* However, that's only half of what XCHG does, so THIS function must perform the other half; in the previous
* example, that entails storing AX (dst) into DX (src).
*
* TODO: Implement full BACKTRACK support for XCHG instructions (see opHelpXCHGrb comments).
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -369,6 +410,9 @@ var X86Help = {
*/
opHelpXCHGrw: function(dst, src) {
if (this.regEA < 0) {
//
// Decode which register was src
//
switch (this.bModRM & 0x7) {
case 0x0: // AX
this.regAX = dst;
@ -399,10 +443,11 @@ var X86Help = {
}
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRR;
} else {
/*
* This is a case where the ModRM decoder that's calling us didn't know it should have called modEAByte()
* instead of getEAByte(), so we compensate by updating regEAWrite.
*/
//
// This is a case where the ModRM decoder that's calling us didn't know it should have called modEAWord()
// instead of getEAWord(), so we compensate by updating regEAWrite. However, setEAWord() has since been
// changed to revalidate the write using segEA:offEA, so updating regEAWrite here isn't strictly necessary.
//
this.regEAWrite = this.regEA;
this.setEAWord(dst);
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRM;

File diff suppressed because it is too large Load diff

View file

@ -1643,8 +1643,8 @@ var X86OpXX = {
*
* op=0x86 (xchgb reg,rm)
*
* NOTE: The XCHG instruction is unique in that both src and dst are both read and written
* (and therefore, if regEA is set, then regEAWrite must be set as well).
* NOTE: The XCHG instruction is unique in that both src and dst are both read and written;
* see opHelpXCHGrb() for how we deal with this special case.
*/
opXCHGrb: function() {
/*
@ -1672,8 +1672,8 @@ var X86OpXX = {
*
* op=0x87 (xchgw reg,rm)
*
* NOTE: The XCHG instruction is unique in that both src and dst are both read and written
* (and therefore, if regEA is set, then regEAWrite must be set as well).
* NOTE: The XCHG instruction is unique in that both src and dst are both read and written;
* see opHelpXCHGrw() for how we deal with this special case.
*/
opXCHGrw: function() {
X86Mods.aOpModsRegWord[this.bModRM = this.getIPByte()].call(this, X86Help.opHelpXCHGrw);
@ -2642,18 +2642,18 @@ var X86OpXX = {
/**
* @this {X86CPU}
*
* op=0xC0 (grp2ab rm) (80186/80188 and up)
* op=0xC0 (grp2bi rm) (80186/80188 and up)
*/
opGrp2ab: function() {
X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp2ab, X86Grps.opGrp2CountImm);
opGrp2bi: function() {
X86Mods.aOpModsGrpByte[this.getIPByte()].call(this, X86Grps.aOpGrp2b, X86Grps.opGrp2CountImm);
},
/**
* @this {X86CPU}
*
* op=0xC1 (grp2aw rm) (80186/80188 and up)
* op=0xC1 (grp2wi rm) (80186/80188 and up)
*/
opGrp2aw: function() {
X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp2aw, X86Grps.opGrp2CountImm);
opGrp2wi: function() {
X86Mods.aOpModsGrpWord[this.getIPByte()].call(this, X86Grps.aOpGrp2w, X86Grps.opGrp2CountImm);
},
/**
* @this {X86CPU}
@ -3232,7 +3232,7 @@ var X86OpXX = {
*
* op=0xF6 (grp3b rm)
*
* The MUL instruction is problematic in two cases:
* The MUL byte instruction is problematic in two cases:
*
* 0xF6 0xE0: MUL AL
* 0xF6 0xE4: MUL AH
@ -3258,7 +3258,7 @@ var X86OpXX = {
*
* op=0xF7 (grp3w rm)
*
* The MUL instruction is problematic in two cases:
* The MUL word instruction is problematic in two cases:
*
* 0xF7 0xE0: MUL AX
* 0xF7 0xE2: MUL DX

View file

@ -321,7 +321,14 @@ function embedMachine(sName, sVersion, idElement, sXMLFile, sXSLFile, sStateFile
if (eMachine) {
var sAppClass = sName.toLowerCase(); // eg, "pcjs" or "c1pjs"
if (!sXSLFile) {
if (DEBUG && sVersion == "1.x.x") {
/*
* Now that PCjs is an open-source project, we can make the following test more flexible,
* and revert to the internal template if DEBUG *or* internal version (instead of *and*).
*
* Third-party sites that don't use the PCjs server will ALWAYS want to specify a fully-qualified
* path to the XSL file, unless they choose to mirror our folder structure.
*/
if (DEBUG || sVersion == "1.x.x") {
sXSLFile = "/modules/" + sAppClass + "/templates/components.xsl";
} else {
sXSLFile = "/versions/" + sAppClass + "/" + sVersion + "/components.xsl";