diff --git a/devices/pc/bios/compaq/deskpro386/README.md b/devices/pc/bios/compaq/deskpro386/README.md index a3ccf185a..c82244a01 100644 --- a/devices/pc/bios/compaq/deskpro386/README.md +++ b/devices/pc/bios/compaq/deskpro386/README.md @@ -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 diff --git a/modules/c1pjs/lib/rom.js b/modules/c1pjs/lib/rom.js index 8d11513a5..1ab232895 100644 --- a/modules/c1pjs/lib/rom.js +++ b/modules/c1pjs/lib/rom.js @@ -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]; } diff --git a/modules/filedump/lib/filedump.js b/modules/filedump/lib/filedump.js index fcecf8b41..ef82eb727 100644 --- a/modules/filedump/lib/filedump.js +++ b/modules/filedump/lib/filedump.js @@ -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) diff --git a/modules/htmlout/lib/htmlout.js b/modules/htmlout/lib/htmlout.js index dcfde5c01..a198c0b49 100644 --- a/modules/htmlout/lib/htmlout.js +++ b/modules/htmlout/lib/htmlout.js @@ -173,6 +173,7 @@ var asNonDirectories = [ var asExtsPlainText = [ "65v", "bas", + "hex", "map", "txt" ]; diff --git a/modules/htmlout/lib/httpapi.js b/modules/htmlout/lib/httpapi.js index d762e7a01..edf1c61ff 100644 --- a/modules/htmlout/lib/httpapi.js +++ b/modules/htmlout/lib/httpapi.js @@ -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) * diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js index 5f2280637..17fbf8eae 100644 --- a/modules/pcjs/lib/chipset.js +++ b/modules/pcjs/lib/chipset.js @@ -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]++; diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 094ed1f5c..7789b313e 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -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()); diff --git a/modules/pcjs/lib/memory.js b/modules/pcjs/lib/memory.js index 3788365f1..ea05aeea0 100644 --- a/modules/pcjs/lib/memory.js +++ b/modules/pcjs/lib/memory.js @@ -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; }; diff --git a/modules/pcjs/lib/rom.js b/modules/pcjs/lib/rom.js index 72316c4fa..3c5dc153a 100644 --- a/modules/pcjs/lib/rom.js +++ b/modules/pcjs/lib/rom.js @@ -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]); diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index b38c8ee01..8fbc58d6c 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -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"); } diff --git a/modules/pcjs/lib/x86help.js b/modules/pcjs/lib/x86help.js index 1fb90be3f..8bdc22975 100644 --- a/modules/pcjs/lib/x86help.js +++ b/modules/pcjs/lib/x86help.js @@ -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(); } }; diff --git a/modules/shared/lib/strlib.js b/modules/shared/lib/strlib.js index ea8f43bd8..26a52bac3 100644 --- a/modules/shared/lib/strlib.js +++ b/modules/shared/lib/strlib.js @@ -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) *