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

25
blog/2015/01/17/README.md Normal file
View file

@ -0,0 +1,25 @@
PCjs Uncompiled
---
Most machines on [pcjs.org](http://www.pcjs.org/) run with a compiled version of PCjs, which is produced
by running PCjs JavaScript source code through Google's Closure Compiler, yielding a smaller (minified) version
that loads and runs much faster than the original source code.
However, certain features are disabled in the compiled versions, including a new BACKTRACK feature that
makes it possible to track the contents of memory locations and registers back to their source (eg, to a ROM
or file location). Once the BACKTRACK feature is finished, it will be folded into the compiled code, but until
then, the only way to experiment with it is by running the uncompiled code.
To make it easier to launch machines with the uncompiled code, the PCjs server now allows the *version* field
of a PCjs machine link to specify "uncompiled"; previously, the only *version* values allowed were a specific
version number (eg, "1.16.5") or an asterisk (which would default to the latest compiled version).
Here's how you define an uncompiled machine in a PCjs Markdown file:
[Embedded IBM PC](/devices/pc/machine/5170/ega/1152kb/rev3/ "PCjs:at-ega-1152k-rev3::uncompiled:debugger")
And here's that example in action, using the current Markdown (README.md) file:
[Embedded IBM PC](/devices/pc/machine/5170/ega/1152kb/rev3/ "PCjs:at-ega-1152k-rev3::uncompiled:debugger")
*[@jeffpar](http://twitter.com/jeffpar)*
*January 17, 2015*

View file

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.16.5/machine.xsl"?>
<machine id="ibm5170" class="pc" border="1" pos="center" style="background-color:#FAEBD7">
<name>IBM PC AT (6Mhz), 128Kb EGA, 1152Kb RAM, 20Mb Hard Disk</name>
<computer id="at-ega-1152k" name="IBM PC AT" buswidth="24"/>
<computer id="at-ega-1152k-rev1" name="IBM PC AT" buswidth="24"/>
<cpu id="cpu286" model="80286"/>
<ram id="ramLow" addr="0x00000" test="true" size="0xa0000" comment="ROM BIOS memory test has NOT been disabled"/>
<ram id="ramExt" addr="0x100000" size="0x80000" comment=""/>

View file

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.16.5/machine.xsl"?>
<machine id="ibm5170" class="pc" border="1" pos="center" style="background-color:#FAEBD7">
<name>IBM PC AT (8Mhz), 128Kb EGA, 1152Kb RAM, 20Mb Hard Disk</name>
<computer id="at-ega-1152k" name="IBM PC AT" buswidth="24"/>
<computer id="at-ega-1152k-rev3" name="IBM PC AT" buswidth="24"/>
<cpu id="cpu286" model="80286" cycles="8000000"/>
<ram id="ramLow" addr="0x00000" test="false" size="0xa0000" comment="ROM BIOS memory test has been disabled"/>
<ram id="ramExt" addr="0x100000" size="0x80000" comment=""/>
@ -10,7 +10,7 @@
<rom id="romBIOS" addr="0xf0000" size="0x10000" alias="0xff0000" file="/devices/pc/bios/5170/1985-11-15.json"/>
<video ref="/devices/pc/video/video-ega-128kb-lock.xml"/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal-functions.xml"/>
<debugger id="debugger" messages="fault|keys" commands=""/>
<debugger id="debugger" messages="fault" commands=""/>
<panel ref="/devices/pc/panel/wide.xml"/>
<fdc ref="/disks/pc/library.xml" width="340px" automount='{A: {name: "PC-DOS 3.20 (Disk 1)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK1.json"}, B: {name: "PC-DOS 3.20 (Disk 2)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK2.json"}}'/>
<hdc id="hdcAT" type="at" drives='[{name:"20Mb Hard Disk",type:2}]'/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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
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,28 +3670,26 @@ if (DEBUGGER) {
var aAddr = this.parseAddr(sAddr, Debugger.ADDR_DATA);
if (aAddr[0] == null) return;
var sDump = "";
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);
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;
if (cLines) cLines = fWords? ((cLines + 7) >> 3) : ((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;
var sBytes = "", sChars = "";
sAddr = this.hexAddr(aAddr);
for (var i = 0; i < 16; i++) {
var b = this.getByte(aAddr, 1);
if (fWords) {
@ -3707,6 +3706,7 @@ if (DEBUGGER) {
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
@ -278,7 +279,7 @@ Video.TRAPALL = true; // monitor all I/O by default (not just deltas)
*
* 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.
@ -2634,19 +2639,19 @@ Video.prototype.onLoadSetFonts = function(sFontFile, sFontData, nErrorCode)
*
* 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,14 +2260,13 @@ 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));
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;
};
@ -2254,14 +2274,13 @@ X86CPU.prototype.getIPByte = function()
/**
* 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;
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;
};
@ -2269,14 +2288,16 @@ X86CPU.prototype.getIPDisp = function()
/**
* 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));
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";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long