Support for 80386 register dumps

This commit is contained in:
Jeff Parsons 2015-02-23 12:02:06 -08:00 committed by jeffpar
commit 5303d2fe20
12 changed files with 384 additions and 124 deletions

View file

@ -23,10 +23,14 @@ I'm still on the lookout for a "Version 1" motherboard from 1986, so that I can
filedump --file=109592-001.hex --merge=109591-001.hex --output=1988-01-28.json
For a human-readable dump, use this command:
For a more human-readable dump, use this command:
filedump --file=109592-001.hex --merge=109591-001.hex --comments
And for those who prefer binary files, the FileDump API can be used to recreate raw binary data from JSON data:
> [http://www.pcjs.org/api/v1/dump?file=http://www.pcjs.org/devices/pc/bios/compaq/deskpro386/1988-01-28.json&format=rom](http://www.pcjs.org/api/v1/dump?file=http://www.pcjs.org/devices/pc/bios/compaq/deskpro386/1988-01-28.json&format=rom)
The *.hex* files were produced by running [eeprom_read](http://github.com/phooky/PROM/blob/master/tools/eeprom_read/eeprom_read.pde)
on a [chipKIT Uno32](http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,892,893&Prod=CHIPKIT-UNO32) Arduino-compatible
prototyping board, and capturing the serial port output on my MacBook Pro -- as outlined in

View file

@ -202,7 +202,7 @@ C1PROM.prototype.copyImage = function()
this.setError("ROM image size (" + str.toHexWord(cbImage) + ") does not match component-specified size (" + str.toHexWord(this.cbROM) + ")");
return;
}
if (DEBUG) this.log("copyImage(): copying ROM to " + str.toHexAddr(this.offROM) + " (0x" + str.toHexWord(cbImage) + " bytes)");
if (DEBUG) this.log("copyImage(): copying ROM to 0x" + str.toHexWord(this.offROM) + " (0x" + str.toHexWord(cbImage) + " bytes)");
for (var i=0; i < cbImage; i++) {
this.abMem[this.offROM + i] = this.abImage[i];
}

View file

@ -235,7 +235,8 @@ FileDump.prototype.loadFile = function(sFile, iStart, nSkip, done)
{
var obj = this;
var options = {encoding: str.endsWith(sFile, ".hex")? "utf8" : null};
var sExt = str.getExtension(sFile);
var options = {encoding: sExt == DumpAPI.FORMAT.JSON || sExt == DumpAPI.FORMAT.HEX? "utf8" : null};
var sFilePath = net.isRemote(sFile)? sFile : path.join(this.sServerRoot, sFile);
@ -277,22 +278,46 @@ FileDump.prototype.loadFile = function(sFile, iStart, nSkip, done)
*/
FileDump.prototype.setData = function(buf, iStart, nSkip)
{
var b, i, s;
var b, i, j, s;
if (typeof buf == "string") {
/*
* We've received a string that must be converted to a Buffer before we continue.
*
* At the moment, the only string-based file format we support is a ".hex" file,
* which contains a series of byte values encoded in hex, separated by whitespace.
*/
var ab = [];
var as = buf.split(/\s+/);
for (i = 0; i < as.length; i++) {
s = as[i];
if (!s.length) continue;
if (isNaN(b = parseInt(s, 16))) break;
ab.push(b);
if (buf.indexOf('{') >= 0) {
/*
* Treat the incoming string data as JSON data.
*/
var json;
try {
json = JSON.parse(buf);
} catch (e) {
json = null;
}
if (json && json.data && json.data.length) {
for (i = 0; i < json.data.length; i++) {
var dw = json.data[i];
for (j = 0; j < 4; j++) {
ab.push(dw & 0xff);
dw >>>= 8;
}
}
}
}
else {
/*
* Treat the incoming string data as HEX (ie, a series of byte values encoded in hex, separated by whitespace)
*/
var as = buf.split(/\s+/);
for (i = 0; i < as.length; i++) {
s = as[i];
if (!s.length) continue;
if (isNaN(b = parseInt(s, 16))) break;
ab.push(b);
}
}
/*
* If we didn't recognize the data, then simply return, leaving this.buf undefined.
*/
if (!ab.length) return;
buf = new Buffer(ab);
}
if (!this.buf) {
@ -310,6 +335,16 @@ FileDump.prototype.setData = function(buf, iStart, nSkip)
}
};
/**
* getData()
*
* @this {FileDump}
* @return {Buffer|null}
*/
FileDump.prototype.getData = function()
{
return this.buf;
};
/**
* dumpLine(nIndent, sLine, sComment)

View file

@ -173,6 +173,7 @@ var asNonDirectories = [
var asExtsPlainText = [
"65v",
"bas",
"hex",
"map",
"txt"
];

View file

@ -208,7 +208,7 @@ HTTPAPI.redirect = function(req, res, next)
}
for (sPath in aInternalRedirectPatterns) {
var re = new RegExp(sPath);
re = new RegExp(sPath);
if (re.exec(req.url)) {
req.url = req.url.replace(re, aInternalRedirectPatterns[sPath]);
break;
@ -757,18 +757,7 @@ HTTPAPI.processDumpAPI = function(req, res)
}
var file = new FileDump(sFormat, fComments, fDecimal, sServerRoot);
file.loadFile(sFile, 0, 0, function(err) {
if (!err) {
file.convertToJSON(function(err, str) {
if (!err) {
nResponse = 200;
} else {
str = err.message;
}
res.status(nResponse).send(str);
});
} else {
res.status(nResponse).send(err.message);
}
HTTPAPI.dumpFile(err, file, res);
});
return true;
}
@ -789,10 +778,10 @@ HTTPAPI.dumpDisk = function(err, disk, res)
var nResponse = 400; // default to "Bad Request"
var sMIMEType = null;
var sAttachment = null;
if (err) {
/*
* TODO: Do a better job of mapping the underlying error (eg, err.errno) to
* the appropriate HTTP response error.
* TODO: Do a better job of mapping the underlying error (eg, err.errno) to an appropriate HTTP response error.
*/
nResponse = 404;
sResponse = err.message;
@ -827,6 +816,63 @@ HTTPAPI.dumpDisk = function(err, disk, res)
res.status(nResponse).send(sResponse);
};
/**
* dumpFile(err, file, res)
*
* @param {Error} err
* @param {FileDump} file
* @param {Object} res
*/
HTTPAPI.dumpFile = function(err, file, res)
{
var sResponse = "";
var nResponse = 400; // default to "Bad Request"
var sMIMEType = null;
var sAttachment = null;
if (err) {
/*
* TODO: Do a better job of mapping the underlying error (eg, err.errno) to an appropriate HTTP response error.
*/
nResponse = 404;
sResponse = err.message;
} else {
if (file.sFormat == DumpAPI.FORMAT.ROM) {
// sMIMEType = "application/octet-stream";
sMIMEType = "application/x-download";
sAttachment = path.basename(file.sFilePath);
var i = sAttachment.lastIndexOf('.');
if (i > 0) sAttachment = sAttachment.substring(0, i+1) + DumpAPI.FORMAT.ROM;
sResponse = file.getData();
} else {
file.convertToJSON(function(err, str) {
if (!err) {
nResponse = 200;
} else {
str = err.message;
}
res.status(nResponse).send(str);
});
return;
}
/*
* Return a successful response ONLY if the file conversion call returned any data
*/
if (sResponse) {
nResponse = 200;
} else {
sResponse = "unable to convert file image: " + file.sFilePath;
}
}
if (sMIMEType) {
res.set("Content-Type", sMIMEType);
}
if (sAttachment) {
res.set("Content-Disposition", 'attachment; filename="' + sAttachment + '"');
}
res.status(nResponse).send(sResponse);
};
/**
* processReportAPI(req, res)
*

View file

@ -3323,7 +3323,7 @@ ChipSet.prototype.getIRRVector = function(iPIC)
var nIRQ = pic.nIRQBase + nIRL;
if (DEBUG && this.messageEnabled(this.messageBitsIRQ(nIRQ))) {
this.printMessage("getIRRVector(): IRQ " + nIRQ + " interrupting @" + str.toHexAddr(this.cpu.getIP(), this.cpu.getCS()) + " stack=" + str.toHexAddr(this.cpu.getSP(), this.cpu.getSS()), true);
this.printMessage("getIRRVector(): IRQ " + nIRQ + " interrupting @" + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " stack=" + this.dbg.hexOffset(this.cpu.getSP(), this.cpu.getSS()), true);
}
if (MAXDEBUG && DEBUGGER) {
this.acInterrupts[nIRQ]++;

View file

@ -83,8 +83,11 @@ function Debugger(parmsDbg)
this.cInstructions = -1;
/*
* Default number of hex chars in a physical address (ie, for real-mode); updated by initBus().
* Default number of hex chars in a register and a physical address (ie, for real-mode);
* updated by initBus().
*/
this.cchReg = 4;
this.maskReg = 0xffff;
this.cchAddr = 5;
/*
@ -1186,9 +1189,15 @@ if (DEBUGGER) {
this.aaOpDescs[0x0F] = Debugger.aOpDescUndefined;
if (this.cpu.model >= X86.MODEL_80286) {
this.aaOpDescs[0x0F] = Debugger.aOpDesc0F;
if (this.cpu.model >= X86.MODEL_80386) {
this.cchReg = 8;
this.maskReg = 0xffffffff|0;
}
}
}
this.selectRegs();
this.messageDump(Messages.DESC, function onDumpDesc(s) { dbg.dumpDesc(s); });
this.messageDump(Messages.TSS, function onDumpTSS(s) { dbg.dumpTSS(s); });
this.messageDump(Messages.DOS, function onDumpDOS(s) { dbg.dumpDOS(s); });
@ -1301,6 +1310,43 @@ if (DEBUGGER) {
return false;
};
/**
* selectRegs()
*
* @this {Debugger}
*/
Debugger.prototype.selectRegs = function()
{
if (this.cchReg <= 4) {
this.regs = {
AX: "AX",
BX: "BX",
CX: "CX",
DX: "DX",
SP: "SP",
BP: "BP",
SI: "SI",
DI: "DI",
IP: "IP",
MS: "MS"
}
} else {
this.regs = {
AX: "EAX",
BX: "EBX",
CX: "ECX",
DX: "EDX",
SP: "ESP",
BP: "EBP",
SI: "ESI",
DI: "EDI",
IP: "EIP",
MS: "CR0"
}
}
this.regs.PS = "PS";
};
/**
* setFocus()
*
@ -1339,7 +1385,7 @@ if (DEBUGGER) {
var wPID = this.getShort(aAddr, 2);
var wParas = this.getShort(aAddr, 5);
if (bSig != 0x4D && bSig != 0x5A) break;
this.println(str.toHexAddr(0, seg) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.dumpSZ(aAddr, 8) + '"');
this.println(this.hexOffset(0, seg) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.dumpSZ(aAddr, 8) + '"');
seg += 1 + wParas;
}
};
@ -1593,7 +1639,7 @@ if (DEBUGGER) {
Debugger.prototype.message = function(sMessage, fAddress)
{
if (fAddress) {
sMessage += " @" + str.toHexAddr(this.cpu.getIP(), this.cpu.getCS());
sMessage += " @" + this.hexOffset(this.cpu.getIP(), this.cpu.getCS());
}
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
@ -1659,7 +1705,7 @@ if (DEBUGGER) {
* at the moment. If that changes, then this will have to change as well.
*/
addr -= 2;
this.message("INT 0x" + str.toHexByte(nInt) + ": AH=" + str.toHexByte(AH) + " @" + str.toHexAddr(addr - this.cpu.segCS.base, this.cpu.getCS()) + sFunc);
this.message("INT 0x" + str.toHexByte(nInt) + ": AH=" + str.toHexByte(AH) + " @" + this.hexOffset(addr - this.cpu.segCS.base, this.cpu.getCS()) + sFunc);
}
return fMessage;
};
@ -1699,7 +1745,7 @@ if (DEBUGGER) {
segFrom = this.cpu.getCS();
addrFrom -= this.cpu.segCS.base;
}
this.message(component.idComponent + "." + (bOut != null? "outPort" : "inPort") + "(0x" + str.toHexWord(port) + "," + (name? name : "unknown") + (bOut != null? ",0x" + str.toHexByte(bOut) : "") + ")" + (bIn != null? (": 0x" + str.toHexByte(bIn)) : "") + (addrFrom != null? (" @" + str.toHexAddr(addrFrom, segFrom)) : ""));
this.message(component.idComponent + "." + (bOut != null? "outPort" : "inPort") + "(0x" + str.toHexWord(port) + "," + (name? name : "unknown") + (bOut != null? ",0x" + str.toHexByte(bOut) : "") + ")" + (bIn != null? (": 0x" + str.toHexByte(bIn)) : "") + (addrFrom != null? (" @" + this.hexOffset(addrFrom, segFrom)) : ""));
}
};
@ -1737,7 +1783,7 @@ if (DEBUGGER) {
if (this.traceEnabled !== undefined && this.traceEnabled[prop]) {
var trace = Debugger.TRACE[prop];
var len = (trace.size >> 2);
var s = str.toHexAddr(this.cpu.opLIP - this.cpu.segCS.base, this.cpu.getCS()) + " " + Debugger.asIns[trace.ins] + "(" + str.toHex(dst, len) + "," + str.toHex(src, len) + "," + (flagsIn === null? "-" : str.toHexWord(flagsIn)) + ") " + str.toHex(result, len) + "," + (flagsOut === null? "-" : str.toHexWord(flagsOut));
var s = this.hexOffset(this.cpu.opLIP - this.cpu.segCS.base, this.cpu.getCS()) + " " + Debugger.asIns[trace.ins] + "(" + str.toHex(dst, len) + "," + str.toHex(src, len) + "," + (flagsIn === null? "-" : str.toHexWord(flagsIn)) + ") " + str.toHex(result, len) + "," + (flagsOut === null? "-" : str.toHexWord(flagsOut));
if (!this.aTraceBuffer.length) this.aTraceBuffer = new Array(Debugger.TRACE_LIMIT);
this.aTraceBuffer[this.iTraceBuffer++] = s;
if (this.iTraceBuffer >= this.aTraceBuffer.length) {
@ -2416,7 +2462,23 @@ if (DEBUGGER) {
*/
Debugger.prototype.hexAddr = function(aAddr)
{
return aAddr[1] == null? ("%" + str.toHex(aAddr[2])) : str.toHexAddr(aAddr[0], aAddr[1]);
return aAddr[1] == null? ("%" + str.toHex(aAddr[2])) : this.hexOffset(aAddr[0], aAddr[1]);
};
/**
* hexOffset(off, sel)
*
* @this {Debugger}
* @param {number} off
* @param {number} [sel]
* @return {string} the hex representation of off (or sel:off)
*/
Debugger.prototype.hexOffset = function(off, sel)
{
if (sel !== undefined) {
return str.toHexWord(sel) + ":" + str.toHex(off, this.cchAddr < 8? 4 : 8);
}
return str.toHex(off);
};
/**
@ -2434,11 +2496,10 @@ if (DEBUGGER) {
}
if (aAddr[1] != null) {
aAddr[0] += inc;
/*
* TODO: Shouldn't we be using the segment (aAddr[1]) limit instead of 0xffff?
*/
if (aAddr[0] != (aAddr[0] & 0xffff)) {
aAddr[0] = aAddr[0] & 0xffff;
var limit = this.getSegment(aAddr[1]).limit;
if (aAddr[0] > limit) {
this.assert(false);
aAddr[0] = 0;
aAddr[2] = null;
}
}
@ -2995,7 +3056,20 @@ if (DEBUGGER) {
b = 0;
break;
}
return " " + sFlag + (b? "1" : "0");
return sFlag + (b? '1' : '0') + ' ';
};
/**
* getRegStr(sName, reg)
*
* @this {Debugger}
* @param {string} sName
* @param {number} reg
* @return {string}
*/
Debugger.prototype.getRegStr = function(sName, reg)
{
return sName + '=' + str.toHex(reg, this.cchReg) + ' ';
};
/**
@ -3027,38 +3101,90 @@ if (DEBUGGER) {
};
/**
* getRegStr(fProt)
* getRegDump(fProt)
*
* Example of 8086 and 80286 real-mode register dump:
*
* AX=0000 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
* SS=0000 DS=0000 ES=0000 PS=0002 V0 D0 I0 T0 S0 Z0 A0 P0 C0
* F000:FFF0 EA5BE000F0 JMP F000:E05B
*
* Example of 80386 real-mode register dump:
*
* EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
* ESP=00000000 EBP=00000000 ESI=00000000 EDI=00000000
* SS=0000 DS=0000 ES=0000 PS=00000002 V0 D0 I0 T0 S0 Z0 A0 P0 C0
* F000:0000FFF0 EA05F900F0 JMP F000:0000F905
*
* Example of 80286 protected-mode register dump:
*
* AX=0000 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
* SS=0000[000000,FFFF] DS=0000[000000,FFFF] ES=0000[000000,FFFF] A20=ON
* CS=F000[FF0000,FFFF] LD=0000[000000,FFFF] GD=[000000,FFFF] ID=[000000,03FF]
* TR=0000 MS=FFF0 PS=0002 V0 D0 I0 T0 S0 Z0 A0 P0 C0
* F000:FFF0 EA5BE000F0 JMP F000:E05B
*
* Example of 80386 protected-mode register dump:
*
* EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
* ESP=00000000 EBP=00000000 ESI=00000000 EDI=00000000
* SS=0000[00000000,FFFF] DS=0000[00000000,FFFF] ES=0000[00000000,FFFF]
* CS=F000[FFFF0000,FFFF] FS=0000[00000000,FFFF] GS=0000[00000000,FFFF]
* LD=0000[00000000,FFFF] GD=[00000000,FFFF] ID=[00000000,03FF] TR=0000 A20=ON
* CR0=00000010 CR2=00000000 CR3=00000000 PS=00000002 V0 D0 I0 T0 S0 Z0 A0 P0 C0
* F000:0000FFF0 EA05F900F0 JMP F000:0000F905
*
* We no longer include CS in real-mode (or EIP in any mode), because that information can be obtained from the
* first line of disassembly, which an "r" or "rp" command will also display.
*
* Note that even when the processor is in real mode, you can always use the "rp" command to force a protected-mode
* dump, in case you need to verify any selector base or limit values, since those do affect real-mode operation.
*
* @this {Debugger}
* @param {boolean} [fProt]
* @return {string}
*/
Debugger.prototype.getRegStr = function(fProt)
Debugger.prototype.getRegDump = function(fProt)
{
if (fProt === undefined) {
fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE);
}
var s = "AX=" + str.toHexWord(this.cpu.regEAX) +
" BX=" + str.toHexWord(this.cpu.regEBX) +
" CX=" + str.toHexWord(this.cpu.regECX) +
" DX=" + str.toHexWord(this.cpu.regEDX) +
" SP=" + str.toHexWord(this.cpu.getSP()) +
" BP=" + str.toHexWord(this.cpu.regEBP) +
" SI=" + str.toHexWord(this.cpu.regESI) +
" DI=" + str.toHexWord(this.cpu.regEDI) + '\n';
s += this.getSegStr(this.cpu.segDS, fProt) + ' ' + this.getSegStr(this.cpu.segES, fProt) + ' ' + this.getSegStr(this.cpu.segSS, fProt);
s += (fProt? '\n' : ' ');
s += this.getSegStr(this.cpu.segCS, fProt) + " IP=" + str.toHexWord(this.cpu.getIP()) +
this.getFlagStr("V") + this.getFlagStr("D") + this.getFlagStr("I") + this.getFlagStr("T") +
this.getFlagStr("S") + this.getFlagStr("Z") + this.getFlagStr("A") + this.getFlagStr("P") + this.getFlagStr("C") +
" PS=" + str.toHexWord(this.cpu.getPS());
var s = this.getRegStr(this.regs.AX, this.cpu.regEAX) +
this.getRegStr(this.regs.BX, this.cpu.regEBX) +
this.getRegStr(this.regs.CX, this.cpu.regECX) +
this.getRegStr(this.regs.DX, this.cpu.regEDX) + (this.cchReg > 4? '\n' : '') +
this.getRegStr(this.regs.SP, this.cpu.getSP()) +
this.getRegStr(this.regs.BP, this.cpu.regEBP) +
this.getRegStr(this.regs.SI, this.cpu.regESI) +
this.getRegStr(this.regs.DI, this.cpu.regEDI) + '\n';
s += this.getSegStr(this.cpu.segSS, fProt) + ' ' +
this.getSegStr(this.cpu.segDS, fProt) + ' ' +
this.getSegStr(this.cpu.segES, fProt) + ' ';
if (fProt) {
s += " MS=" + str.toHexWord(this.cpu.regCR0) + '\n' +
this.getDTRStr("LD", this.cpu.segLDT.sel, this.cpu.segLDT.base, this.cpu.segLDT.base + this.cpu.segLDT.limit) + ' ' +
this.getDTRStr("GD", null, this.cpu.addrGDT, this.cpu.addrGDTLimit) + ' ' +
this.getDTRStr("ID", null, this.cpu.addrIDT, this.cpu.addrIDTLimit) + " TR=" + str.toHexWord(this.cpu.segTSS.sel) +
" A20=" + (this.bus.getA20()? "ON" : "OFF");
var sTR = "TR=" + str.toHexWord(this.cpu.segTSS.sel);
var sA20 = "A20=" + (this.bus.getA20()? "ON " : "OFF ");
if (this.cpu.model < X86.MODEL_80386) {
sTR = '\n' + sTR;
s += sA20; sA20 = '';
}
s += '\n' + this.getSegStr(this.cpu.segCS, fProt) + ' ';
if (this.cpu.model >= X86.MODEL_80386) {
sA20 += '\n';
s += this.getSegStr(this.cpu.segFS, fProt) + ' ' +
this.getSegStr(this.cpu.segGS, fProt) + '\n';
}
s += this.getDTRStr("LD", this.cpu.segLDT.sel, this.cpu.segLDT.base, this.cpu.segLDT.base + this.cpu.segLDT.limit) + ' ' +
this.getDTRStr("GD", null, this.cpu.addrGDT, this.cpu.addrGDTLimit) + ' ' +
this.getDTRStr("ID", null, this.cpu.addrIDT, this.cpu.addrIDTLimit) + ' ';
s += sTR + ' ' + sA20;
s += this.getRegStr(this.regs.MS, this.cpu.regCR0);
if (this.cpu.model >= X86.MODEL_80386) {
s += this.getRegStr("CR2", this.cpu.regCR2) + this.getRegStr("CR3", this.cpu.regCR3);
}
}
s += this.getRegStr(this.regs.PS, this.cpu.getPS()) +
this.getFlagStr("V") + this.getFlagStr("D") + this.getFlagStr("I") + this.getFlagStr("T") +
this.getFlagStr("S") + this.getFlagStr("Z") + this.getFlagStr("A") + this.getFlagStr("P") + this.getFlagStr("C");
return s;
};
@ -3139,27 +3265,51 @@ if (DEBUGGER) {
sValue = sValue.toUpperCase();
switch (sValue) {
case "AX":
value = this.cpu.regEAX & 0xffff;
break;
case "EAX":
value = this.cpu.regEAX;
break;
case "BX":
value = this.cpu.regEBX & 0xffff;
break;
case "EBX":
value = this.cpu.regEBX;
break;
case "CX":
value = this.cpu.regECX & 0xffff;
break;
case "ECX":
value = this.cpu.regECX;
break;
case "DX":
value = this.cpu.regEDX & 0xffff;
break;
case "EDX":
value = this.cpu.regEDX;
break;
case "SI":
value = this.cpu.regESI & 0xffff;
break;
case "ESI":
value = this.cpu.regESI;
break;
case "DI":
value = this.cpu.regEDI & 0xffff;
break;
case "EDI":
value = this.cpu.regEDI;
break;
case "BP":
value = this.cpu.regEBP & 0xffff;
break;
case "EBP":
value = this.cpu.regEBP;
break;
case "SP":
value = this.cpu.getSP() & 0xffff;
break;
case "ESP":
value = this.cpu.getSP();
break;
case "CS":
@ -3179,6 +3329,9 @@ if (DEBUGGER) {
* treat "PC" as an alias for the 16-bit flags register. So for purposes of parseValue(), "PC" has been removed.
*/
case "IP":
value = this.cpu.getIP() & 0xffff;
break;
case "EIP":
value = this.cpu.getIP();
break;
default:
@ -3319,7 +3472,7 @@ if (DEBUGGER) {
if (seg === undefined) seg = (addr >>> 4);
var sSymbolOrig = aSymbols[sSymbol]['l'];
if (sSymbolOrig) sSymbol = sSymbolOrig;
this.println(str.toHexAddr(off, seg) + " " + sSymbol);
this.println(this.hexOffset(off, seg) + " " + sSymbol);
}
}
};
@ -3967,13 +4120,13 @@ if (DEBUGGER) {
sDelta = "";
nDelta = aAddr[0] - aSymbol[1];
if (nDelta) sDelta = " + " + str.toHexWord(nDelta);
this.println(aSymbol[0] + " (" + str.toHexAddr(aSymbol[1], aAddr[1]) + ")" + sDelta);
this.println(aSymbol[0] + " (" + this.hexOffset(aSymbol[1], aAddr[1]) + ")" + sDelta);
}
if (aSymbol.length > 4 && aSymbol[4]) {
sDelta = "";
nDelta = aSymbol[5] - aAddr[0];
if (nDelta) sDelta = " - " + str.toHexWord(nDelta);
this.println(aSymbol[4] + " (" + str.toHexAddr(aSymbol[5], aAddr[1]) + ")" + sDelta);
this.println(aSymbol[4] + " (" + this.hexOffset(aSymbol[5], aAddr[1]) + ")" + sDelta);
}
} else {
this.println("no symbols");
@ -4285,55 +4438,82 @@ if (DEBUGGER) {
}
var w = parseInt(sValue, 16);
if (!isNaN(w)) {
sReg = sReg.toUpperCase();
switch (sReg) {
var sRegMatch = sReg.toUpperCase();
if (sRegMatch.charAt(0) == 'E' && this.cchReg <= 4) {
sRegMatch = null;
}
switch (sRegMatch) {
case "AL":
this.cpu.regEAX = (this.cpu.regEAX & 0xff00) | (w & 0xff);
this.cpu.regEAX = (this.cpu.regEAX & ~0xff) | (w & 0xff);
break;
case "AH":
this.cpu.regEAX = (this.cpu.regEAX & 0x00ff) | ((w << 8) & 0xff);
this.cpu.regEAX = (this.cpu.regEAX & ~0xff00) | ((w << 8) & 0xff);
break;
case "AX":
this.cpu.regEAX = (w & 0xffff);
this.cpu.regEAX = (this.cpu.regEAX & ~0xffff) | (w & 0xffff);
break;
case "EAX":
this.cpu.regEAX = w;
break;
case "BL":
this.cpu.regEBX = (this.cpu.regEBX & 0xff00) | (w & 0xff);
this.cpu.regEBX = (this.cpu.regEBX & ~0xff) | (w & 0xff);
break;
case "BH":
this.cpu.regEBX = (this.cpu.regEBX & 0x00ff) | ((w << 8) & 0xff);
this.cpu.regEBX = (this.cpu.regEBX & ~0xff00) | ((w << 8) & 0xff);
break;
case "BX":
this.cpu.regEBX = (w & 0xffff);
this.cpu.regEBX = (this.cpu.regEBX & ~0xffff) | (w & 0xffff);
break;
case "EBX":
this.cpu.regEBX = w;
break;
case "CL":
this.cpu.regECX = (this.cpu.regECX & 0xff00) | (w & 0xff);
this.cpu.regECX = (this.cpu.regECX & ~0xff) | (w & 0xff);
break;
case "CH":
this.cpu.regECX = (this.cpu.regECX & 0x00ff) | ((w << 8) & 0xff);
this.cpu.regECX = (this.cpu.regECX & ~0xff00) | ((w << 8) & 0xff);
break;
case "CX":
this.cpu.regECX = (w & 0xffff);
this.cpu.regECX = (this.cpu.regECX & ~0xffff) | (w & 0xffff);
break;
case "ECX":
this.cpu.regECX = w;
break;
case "DL":
this.cpu.regEDX = (this.cpu.regEDX & 0xff00) | (w & 0xff);
this.cpu.regEDX = (this.cpu.regEDX & ~0xff) | (w & 0xff);
break;
case "DH":
this.cpu.regEDX = (this.cpu.regEDX & 0x00ff) | ((w << 8) & 0xff);
this.cpu.regEDX = (this.cpu.regEDX & ~0xff00) | ((w << 8) & 0xff);
break;
case "DX":
this.cpu.regEDX = (w & 0xffff);
this.cpu.regEDX = (this.cpu.regEDX & ~0xffff) | (w & 0xffff);
break;
case "EDX":
this.cpu.regEDX = w;
break;
case "SP":
this.cpu.setSP((this.cpu.getSP() & ~0xffff) | (w & 0xffff));
break;
case "ESP":
this.cpu.setSP(w);
break;
case "BP":
this.cpu.regEBP = (w & 0xffff);
this.cpu.regEBP = (this.cpu.regEBP & ~0xffff) | (w & 0xffff);
break;
case "EBP":
this.cpu.regEBP = w;
break;
case "SI":
this.cpu.regESI = (w & 0xffff);
this.cpu.regESI = (this.cpu.regESI & ~0xffff) | (w & 0xffff);
break;
case "ESI":
this.cpu.regESI = w;
break;
case "DI":
this.cpu.regEDI = (w & 0xffff);
this.cpu.regEDI = (this.cpu.regEDI & ~0xffff) | (w & 0xffff);
break;
case "EDI":
this.cpu.regEDI = w;
break;
case "DS":
this.cpu.setDS(w);
@ -4390,7 +4570,7 @@ if (DEBUGGER) {
var fUnknown = true;
if (this.cpu.model >= X86.MODEL_80286) {
fUnknown = false;
switch(sReg){
switch(sRegMatch){
case "MS":
X86Help.opHelpLMSW.call(this.cpu, w);
break;
@ -4422,7 +4602,7 @@ if (DEBUGGER) {
}
}
this.println((fCompact? '' : '\n') + this.getRegStr(fProt));
this.println((fCompact? '' : '\n') + this.getRegDump(fProt));
if (fIns) {
this.aAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS());

View file

@ -232,7 +232,7 @@ Memory.TYPE = {
Memory.readNone = function readNone(off)
{
if (DEBUGGER && this.dbg.messageEnabled(Messages.MEM) /* && !off */) {
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr) + " from " + str.toHexAddr(this.cpu.getIP(), this.cpu.getCS()));
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr) + " from " + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()));
}
return 0;
};

View file

@ -317,7 +317,7 @@ ROM.prototype.addROM = function(addr)
{
if (addr == null) return true;
if (this.bus.addMemory(addr, this.sizeROM, Memory.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexAddr(addr) + " (0x" + str.toHex(this.abROM.length) + " bytes)");
if (DEBUG) this.log("addROM(): copying ROM to 0x" + str.toHex(addr) + " (0x" + str.toHex(this.abROM.length) + " bytes)");
var bto = null;
for (var off = 0; off < this.abROM.length; off++) {
this.bus.setByteDirect(addr + off, this.abROM[off]);

View file

@ -713,8 +713,10 @@ X86CPU.prototype.initProcessor = function()
if (this.model >= X86.MODEL_80186) {
/*
* TODO: I don't go out of my way to make 80186/80188 cycle times accurate, since no IBM PC models used
* those processors; beyond the 8086, the next priority is the 80286, but we may revisit the 80186 someday.
* I don't go out of my way to make 80186/80188 cycle times accurate, since I'm not aware of any
* IBM PC models that used those processors; beyond the 8086, my next priorities are the 80286 and
* 80386, but I might revisit the 80186 someday.
*
* Instruction handlers that contain "hard-coded" 80286 cycle times include: opINSb, opINSw, opOUTSb,
* opOUTSw, opENTER, and opLEAVE.
*/
@ -805,8 +807,11 @@ X86CPU.prototype.reset = function()
* or setIP(), whichever is appropriate; in unusual cases where only segCS is changing (eg, undocumented 8086
* opcodes), use setCS().
*
* The other segment registers (DS, SS and ES) have similar setters (for segDS, segSS and segES), but those
* functions do not mirror any segment:offset values in the same way that regLIP mirrors CS:IP.
* Similarly, regLSP mirrors the linear address corresponding to SS:SP, and therefore you must rely on getSP()
* to read the current SP, and setSP() and setSS() to update SP and SS.
*
* The other segment registers, such as segDS and segES, have similar getters and setters, but they do not mirror
* any segment:offset values in the same way that regLIP mirrors CS:IP, or that regLSP mirrors SS:SP.
*
* @this {X86CPU}
*/
@ -822,8 +827,8 @@ X86CPU.prototype.resetRegs = function()
this.regEDI = 0;
/*
* The following are internal "registers" that are used to capture intermediate values inside selected helper
* functions and use them if they've been modified (or are known to always change); for example, the MUL and DIV
* The following are internal "registers" used to capture intermediate values inside selected helper
* functions and use them if they've been modified (or are known to change); for example, the MUL and DIV
* instructions perform calculations that must be propagated to specific registers (eg, AX and/or DX), which
* the ModRM decoder functions don't know about. We initialize them here mainly for documentation purposes.
*/
@ -831,15 +836,15 @@ X86CPU.prototype.resetRegs = function()
/*
* Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode
* handlers so that the helper function can have access to the instruction's bModRM without resorting to a closure
* (which, in the Chrome V8 engine, for example, seems to cause constant recompilation).
* handlers so that the helper function can have access to the instruction's bModRM without resorting to a
* closure (which, in the Chrome V8 engine, for example, seems to cause constant recompilation).
*/
this.bModRM = 0;
/*
* NOTE: Even though the MSW and IDTR are 80286-specific, we initialize them for ALL CPUs, so that
* functions like X86Help.opHelpINT() can use the same code for both. The 8086/8088 have no direct way
* of accessing or changing them, so this internal change should be perfectly safe for those processors.
* NOTE: Even though the 8086 doesn't have CR0 (aka MSW) and IDTR, we initialize them for ALL CPUs, so
* that functions like X86Help.opHelpINT() can use the same code for both. The 8086/8088 have no direct
* way of accessing or changing them, so this internal change should be perfectly safe for those processors.
*/
this.regCR0 = X86.CR0.MSW.ON;
this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
@ -847,15 +852,14 @@ X86CPU.prototype.resetRegs = function()
/*
* This is set by opHelpFault() and reset (to -1) by resetRegs() and opIRET(); its initial purpose is to
* "help" opHelpFault() determine when a nested fault should be converted into either a double-fault
* (DF_FAULT) or a triple-fault (ie, a processor reset).
* "help" opHelpFault() determine when a nested fault should be converted into either a double-fault (DF_FAULT)
* or a triple-fault (ie, a processor reset).
*/
this.nFault = -1;
/*
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the
* segment number and base physical address, respectively), but all segment registers are now defined
* as X86Seg objects.
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the segment
* number and base physical address, respectively), but all segment registers are now defined as X86Seg objects.
*/
this.segCS = new X86Seg(this, X86Seg.ID.CODE, "CS");
this.segDS = new X86Seg(this, X86Seg.ID.DATA, "DS");
@ -865,7 +869,12 @@ X86CPU.prototype.resetRegs = function()
this.setSS(0);
if (I386 && this.model >= X86.MODEL_80386) {
this.regCR0 = X86.CR0.ET;
this.regCR0 = X86.CR0.ET; // formerly MSW
this.regCR1 = 0; // reserved
this.regCR2 = 0; // page fault linear address (PFLA)
this.regCR3 = 0; // page directory base register (PDBR)
this.aRegDR = new Array(8); // Debug Registers DR0-DR7
this.aRegTR = new Array(8); // Test Registers TR0-TR7
this.segFS = new X86Seg(this, X86Seg.ID.DATA, "FS");
this.segGS = new X86Seg(this, X86Seg.ID.DATA, "GS");
}

View file

@ -728,7 +728,7 @@ var X86Help = {
}
if (this.messageEnabled(bitsMessage) || fHalt) {
var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(bOpcode) + " at " + str.toHexAddr(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")";
var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")";
var fRunning = this.aFlags.fRunning;
if (this.printMessage(sMessage, bitsMessage)) {
if (fHalt) {
@ -767,7 +767,7 @@ var X86Help = {
*/
opHelpUndefined: function() {
this.setIP(this.opLIP - this.segCS.base);
this.setError("Undefined opcode 0x" + str.toHexByte(this.bus.getByteDirect(this.regLIP)) + " at " + str.toHexAddr(this.getIP(), this.getCS()));
this.setError("Undefined opcode 0x" + str.toHexByte(this.bus.getByteDirect(this.regLIP)) + " at 0x" + str.toHex(this.regLIP));
this.stopCPU();
}
};

View file

@ -166,21 +166,6 @@ str.toHexWord = function(w)
return str.toHex(w, 4);
};
/**
* toHexAddr(off, sel)
*
* @param {number} off
* @param {number} [sel]
* @return {string} the hex representation of sel:off
*/
str.toHexAddr = function(off, sel)
{
if (sel !== undefined) {
return str.toHexWord(sel) + ":" + str.toHexWord(off);
}
return str.toHex(off);
};
/**
* getBaseName(sFileName, fStripExt)
*