diff --git a/modules/pcjs/lib/bus.js b/modules/pcjs/lib/bus.js index 8bfcd360f..223a4a5b1 100644 --- a/modules/pcjs/lib/bus.js +++ b/modules/pcjs/lib/bus.js @@ -1185,7 +1185,7 @@ Bus.prototype.getSymbol = function(addr, fNearest) /** * saveMemory() * - * The only memory blocks we save are those marked as dirty; most likely all of RAM will have been marked dirty, + * The only memory blocks we save are those marked as dirty, but most likely all of RAM will have been marked dirty, * and even if our dirty-memory flags were as smart as our dirty-sector flags (ie, were set only when a write changed * what was already there), it's unlikely that would reduce the number of RAM blocks we must save/restore. At least * all the ROM blocks should be clean (except in the unlikely event that the Debugger was used to modify them). @@ -1214,6 +1214,15 @@ Bus.prototype.saveMemory = function() { var i = 0; var a = []; + + /* + * A quick-and-dirty work-around for 32-bit bus machines, to ensure that all blocks in the 2nd Mb are + * mapped in before we save. We do this by forcing A20 on, and then turning it back off again before we + * leave. + */ + var fA20 = this.getA20(); + if (!fA20) this.setA20(true); + for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) { var block = this.aMemBlocks[iBlock]; /* @@ -1226,7 +1235,10 @@ Bus.prototype.saveMemory = function() a[i++] = State.compress(block.save()); } } - a[i] = this.getA20(); + + if (!fA20) this.setA20(false); + a[i] = fA20; + return a; }; diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js index 6f2e72b90..1d8176777 100644 --- a/modules/pcjs/lib/chipset.js +++ b/modules/pcjs/lib/chipset.js @@ -4299,6 +4299,7 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom) this.printMessageIO(port, bOut, addrFrom, "8042_INBUF.DATA", null, Messages.C8042); if (this.b8042Status & ChipSet.KBC.STATUS.CMD_FLAG) { + switch (this.b8042InBuff) { case ChipSet.KBC.CMD.WRITE_CMD: @@ -4653,6 +4654,10 @@ ChipSet.prototype.set8042OutBuff = function(b, fNoDelay) /** * set8042OutPort(b) * + * When ChipSet.KBC.CMD.WRITE_OUTPORT (0xD1) is written to port 0x64, the next byte written to port 0x60 comes here, + * to the KBC's OUTPORT. One of the most important bits in the OUTPORT is the A20_ON bit (0x02): set it to turn A20 on, + * clear it to turn A20 off. + * * @this {ChipSet} * @param {number} b */ diff --git a/modules/pcjs/lib/computer.js b/modules/pcjs/lib/computer.js index 611c50f82..54a1913a5 100644 --- a/modules/pcjs/lib/computer.js +++ b/modules/pcjs/lib/computer.js @@ -790,8 +790,8 @@ Computer.prototype.powerOff = function(fSave, fShutdown) stateComputer.set(Computer.STATE_BROWSER, web.getUserAgent()); /* - * Always power the CPU "down" first, just to insure it doesn't ask other - * components to do anything after they're no longer ready. + * Always power the CPU "down" first, just to help insure it doesn't ask other components to do anything + * after they're no longer ready. */ if (this.cpu && this.cpu.powerDown) { if (fShutdown) this.cpu.stopCPU(); @@ -960,50 +960,53 @@ Computer.prototype.stop = function(ms, nCycles) Computer.prototype.setBinding = function(sHTMLType, sBinding, control) { var computer = this; + switch (sBinding) { - case "save": - this.bindings[sBinding] = control; - control.onclick = function onClickSave() { - var sUserID = computer.queryUserID(true); - if (sUserID) { - /* - * I modified the test to include a check for sStatePath so that I could save new states - * for machines with existing states; otherwise, I'd have no (easy) way of capturing and - * updating their state. Making the machine (even temporarily) resumable would have been - * one work-around, but it's not appropriate for some machines, as their state is simply - * too large (for localStorage anyway, which is the default storage solution). - */ - var fSave = !!(computer.resume && !computer.sResumePath || computer.sStatePath); - var sState = computer.powerOff(fSave); - if (fSave) { - computer.saveServerState(sUserID, sState); - } else { - computer.notice("Resume disabled, machine state not saved"); - } - } + case "save": + this.bindings[sBinding] = control; + control.onclick = function onClickSave() { + var sUserID = computer.queryUserID(true); + if (sUserID) { /* - * This seemed like a handy alternative, but it turned out to be a no-go, at least for large states: - * - * var sState = computer.powerOff(true); - * if (sState) { - * sState = "data:text/json;charset=utf-8," + encodeURIComponent(sState); - * window.open(sState); - * } - * - * Perhaps if I embedded the data in a link on the current page instead; eg: - * - * $('Download').appendTo('#container'); + * I modified the test to include a check for sStatePath so that I could save new states + * for machines with existing states; otherwise, I'd have no (easy) way of capturing and + * updating their state. Making the machine (even temporarily) resumable would have been + * one work-around, but it's not appropriate for some machines, as their state is simply + * too large (for localStorage anyway, which is the default storage solution). */ - }; - return true; - case "reset": - this.bindings[sBinding] = control; - control.onclick = function onClickReset() { - computer.onReset(); - }; - return true; - default: - break; + var fSave = !!(computer.resume && !computer.sResumePath || computer.sStatePath); + var sState = computer.powerOff(fSave); + if (fSave) { + computer.saveServerState(sUserID, sState); + } else { + computer.notice("Resume disabled, machine state not saved"); + } + } + /* + * This seemed like a handy alternative, but it turned out to be a no-go, at least for large states: + * + * var sState = computer.powerOff(true); + * if (sState) { + * sState = "data:text/json;charset=utf-8," + encodeURIComponent(sState); + * window.open(sState); + * } + * + * Perhaps if I embedded the data in a link on the current page instead; eg: + * + * $('Download').appendTo('#container'); + */ + }; + return true; + + case "reset": + this.bindings[sBinding] = control; + control.onclick = function onClickReset() { + computer.onReset(); + }; + return true; + + default: + break; } return false; }; diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index d3364366f..20e26f252 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -5186,7 +5186,7 @@ if (DEBUGGER) { * doClear(sCmd) * * @this {Debugger} - * @param {string} sCmd (eg, "cls" or "clear") + * @param {string} [sCmd] (eg, "cls" or "clear") */ Debugger.prototype.doClear = function(sCmd) { @@ -5251,6 +5251,7 @@ if (DEBUGGER) { */ console.log(s); } else { + this.doClear(); this.println(s); } return; @@ -5728,6 +5729,7 @@ if (DEBUGGER) { * "biggest" being that the large disk images really need to be compressed first, because they * get "inflated" with use. See the dump() method in the Disk component for more details. */ + this.doClear(); this.println(drive.disk.toJSON()); return; } diff --git a/modules/pcjs/lib/hdc.js b/modules/pcjs/lib/hdc.js index 0620582f9..3a97afa14 100644 --- a/modules/pcjs/lib/hdc.js +++ b/modules/pcjs/lib/hdc.js @@ -683,6 +683,8 @@ HDC.prototype.initController = function(data, fHard) var i = 0; var fSuccess = true; + this.iDrive = -1; + /* * At this point, it's worth calling into question my decision to NOT split the HDC component into separate XTC * and ATC components, given all the differences, and given that I'm about to write some "if (ATC) else (XTC) ..." @@ -702,7 +704,7 @@ HDC.prototype.initController = function(data, fHard) * or vice versa), we're under no obligation to use the same number of registers, or save/restore format, etc, * as the original XT controller. */ - if (data == null) data = [0, 0, 0, 0, 0, 0, 0, 0, HDC.ATC.STATUS.READY, 0]; + if (data == null) data = [0, 0, 0, 0, 0, 0, 0, HDC.ATC.STATUS.READY, 0, [0, -1]]; this.regError = data[i++]; this.regWPreC = data[i++]; this.regSecCnt = data[i++]; @@ -713,6 +715,11 @@ HDC.prototype.initController = function(data, fHard) this.regStatus = data[i++]; this.regCommand = data[i++]; this.regFDR = data[i++]; + if (typeof this.regFDR == "object") { + var a = this.regFDR; + this.regFDR = a[0]; + this.iDrive = a[1]; + } /* * Additional state is maintained by the Drive object (eg, abSector, ibSector) */ @@ -763,6 +770,11 @@ HDC.prototype.initController = function(data, fHard) this.regConfig |= (drive.type & 0x3) << ((1 - iDrive) << 1); } } + + if (this.iDrive >= 0) { + this.drive = this.aDrives[this.iDrive]; + } + if (DEBUG && this.messageEnabled()) { this.printMessage("HDC initialized for " + this.aDrives.length + " drive(s)"); } @@ -789,7 +801,7 @@ HDC.prototype.saveController = function() data[i++] = this.regDrvHd; data[i++] = this.regStatus; data[i++] = this.regCommand; - data[i++] = this.regFDR; + data[i++] = [this.regFDR, this.iDrive]; } else { data[i++] = this.regConfig; data[i++] = this.regStatus; @@ -1841,6 +1853,7 @@ HDC.prototype.doATC = function() var nSector = this.regSecNum; var nSectors = this.regSecCnt || 256; + this.iDrive = -1; this.drive = null; this.regError = HDC.ATC.ERROR.NONE; this.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK; @@ -1869,6 +1882,7 @@ HDC.prototype.doATC = function() drive.sector = null; drive.ibSector = 0; drive.errorCode = 0; + this.iDrive = iDrive; this.drive = drive; } diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 9ad5a7d6f..bb5f0a0e6 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -1467,19 +1467,25 @@ X86.fnLDS = function LDS(dst, src) */ X86.fnLEA = function LEA(dst, src) { + /* + * TODO: Until I bite the bullet and choose a truly invalid value for X86.ADDR_INVALID (eg, null), + * this code must be disabled, because otherwise an instruction like "LEA ECX,[EAX-1]" will fail when + * EAX is zero. And we can't have that. + * if (this.regEA === X86.ADDR_INVALID) { - /* - * 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? - */ + // + // 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? + // X86.opUndefined.call(this); return dst; } + */ this.nStepCycles -= this.cycleCounts.nOpCyclesLEA; return this.regEA; };