Merge branch 'next-release'
This commit is contained in:
commit
a83ae4c0be
35 changed files with 2739 additions and 1648 deletions
|
|
@ -7086,12 +7086,12 @@ if (DEBUGGER) {
|
|||
DebuggerX86.prototype.doStep = function(sCmd)
|
||||
{
|
||||
var fCallStep = true;
|
||||
var fRegs = (sCmd == "pr"? 1 : 0);
|
||||
var nRegs = (sCmd == "pr"? 1 : 0);
|
||||
/*
|
||||
* Set up the value for this.nStep (ie, 1 or 2) depending on whether the user wants
|
||||
* a subsequent register dump ("pr") or not ("p").
|
||||
*/
|
||||
var nStep = 1 + fRegs;
|
||||
var nStep = 1 + nRegs;
|
||||
if (!this.nStep) {
|
||||
var fPrefix;
|
||||
var fRepeat = false;
|
||||
|
|
@ -7186,7 +7186,7 @@ if (DEBUGGER) {
|
|||
* stopped for reasons unrelated to the temporary breakpoint, but that's OK.
|
||||
*/
|
||||
} else {
|
||||
this.doTrace(fRegs? "tr" : "t");
|
||||
this.doTrace(nRegs? "tr" : "t");
|
||||
}
|
||||
} else {
|
||||
this.println("step in progress");
|
||||
|
|
|
|||
|
|
@ -166,11 +166,12 @@ Component.subclass(BusPDP11);
|
|||
|
||||
BusPDP11.IOPAGE_16BIT = 0xE000; /*000160000*/ // eg, PDP-11/20
|
||||
BusPDP11.IOPAGE_18BIT = 0x3E000; /*000760000*/ // eg, PDP-11/45
|
||||
BusPDP11.IOPAGE_UNIBUS = 0x3C0000; /*017000000*/
|
||||
BusPDP11.IOPAGE_22BIT = 0x3FE000; /*017760000*/ // eg, PDP-11/70
|
||||
BusPDP11.IOPAGE_LENGTH = 0x2000; // ie, 8Kb
|
||||
BusPDP11.IOPAGE_MASK = BusPDP11.IOPAGE_LENGTH - 1;
|
||||
BusPDP11.MAX_MEMORY = BusPDP11.IOPAGE_UNIBUS - 16384; // Maximum memory address (need less memory for BSD 2.9 boot)
|
||||
|
||||
BusPDP11.UNIBUS_22BIT = 0x3C0000; /*017000000*/
|
||||
BusPDP11.MAX_MEMORY = BusPDP11.UNIBUS_22BIT - 16384; // Maximum memory address (need less memory for BSD 2.9 boot)
|
||||
|
||||
BusPDP11.ERROR = {
|
||||
RANGE_INUSE: 1,
|
||||
|
|
@ -319,15 +320,11 @@ BusPDP11.IOController = {
|
|||
fWrite = true;
|
||||
}
|
||||
/*
|
||||
* If a writeWord() handler exists, call the readWord() handler first to get the original data,
|
||||
* then call writeWord() with the new data pre-inserted in the original data.
|
||||
*
|
||||
* WARNING: Whenever we call readWord() under these circumstances, we zero the address parameter,
|
||||
* so that the handler can distinguish this case. Thus, if we're dealing with a special register
|
||||
* where a byte write operation modifies the entire register, the handler can simply return zero.
|
||||
* If a writeWord() handler exists, call the readWord() handler first to get the original data
|
||||
* (with fPreWrite set to true) and call writeWord() with the new data inserted into the original data.
|
||||
*/
|
||||
else if (afn[BusPDP11.IOHANDLER.WRITE_WORD]) {
|
||||
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](0) : 0;
|
||||
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked, true) : 0;
|
||||
if (!(addrMasked & 0x1)) {
|
||||
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & ~0xff) | b, addrMasked);
|
||||
fWrite = true;
|
||||
|
|
@ -339,18 +336,14 @@ BusPDP11.IOController = {
|
|||
} else if (addrMasked & 0x1) {
|
||||
/*
|
||||
* If no handler existed, and this address was odd, then perhaps a handler exists for the even address;
|
||||
* if so, call the readWord() handler first to get the original data, then call writeWord() with the new
|
||||
* data pre-inserted in (the high byte of) the original data.
|
||||
*
|
||||
* WARNING: Whenever we call readWord() under these circumstances, we zero the address parameter,
|
||||
* so that the handler can distinguish this case. Thus, if we're dealing with a special register
|
||||
* where a byte write operation modifies the entire register, the handler can simply return zero.
|
||||
* if so, call the readWord() handler first to get the original data (with fPreWrite set to true) and call
|
||||
* writeWord() with the new data inserted into (the high byte of) the original data.
|
||||
*/
|
||||
afn = bus.aIOHandlers[off & ~0x1];
|
||||
if (afn) {
|
||||
if (afn[BusPDP11.IOHANDLER.WRITE_WORD]) {
|
||||
addrMasked &= ~0x1;
|
||||
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](0) : 0;
|
||||
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked, true) : 0;
|
||||
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & 0xff) | (b << 8), addrMasked);
|
||||
fWrite = true;
|
||||
} else if (afn[BusPDP11.IOHANDLER.WRITE_BYTE]) {
|
||||
|
|
@ -930,11 +923,11 @@ BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
|||
BusPDP11.prototype.getByte = function(addr)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
|
||||
|
|
@ -952,11 +945,11 @@ BusPDP11.prototype.getByte = function(addr)
|
|||
BusPDP11.prototype.getByteDirect = function(addr)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
this.fFault = false;
|
||||
|
|
@ -976,11 +969,11 @@ BusPDP11.prototype.getByteDirect = function(addr)
|
|||
BusPDP11.prototype.getWord = function(addr)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
var off = addr & this.nBlockLimit;
|
||||
|
|
@ -1003,11 +996,11 @@ BusPDP11.prototype.getWord = function(addr)
|
|||
BusPDP11.prototype.getWordDirect = function(addr)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
var w;
|
||||
|
|
@ -1034,11 +1027,11 @@ BusPDP11.prototype.getWordDirect = function(addr)
|
|||
BusPDP11.prototype.setByte = function(addr, b)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
|
||||
|
|
@ -1057,11 +1050,11 @@ BusPDP11.prototype.setByte = function(addr, b)
|
|||
BusPDP11.prototype.setByteDirect = function(addr, b)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
this.fFault = false;
|
||||
|
|
@ -1080,11 +1073,11 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
|
|||
BusPDP11.prototype.setWord = function(addr, w)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
var off = addr & this.nBlockLimit;
|
||||
|
|
@ -1110,11 +1103,11 @@ BusPDP11.prototype.setWord = function(addr, w)
|
|||
BusPDP11.prototype.setWordDirect = function(addr, w)
|
||||
{
|
||||
/*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
|
||||
* UNIBUS relocation map.
|
||||
*/
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) {
|
||||
addr = this.cpu.mapUnibus(addr);
|
||||
}
|
||||
var off = addr & this.nBlockLimit;
|
||||
|
|
@ -1272,7 +1265,7 @@ BusPDP11.prototype.getMemorySize = function(type)
|
|||
};
|
||||
|
||||
/**
|
||||
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
|
||||
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory, sName)
|
||||
*
|
||||
* Add I/O notification handlers to the master list (aIOHandlers). The start and end addresses are typically
|
||||
* relative to the starting IOPAGE address, but they can also be absolute; we simply mask all addresses with
|
||||
|
|
@ -1281,7 +1274,9 @@ BusPDP11.prototype.getMemorySize = function(type)
|
|||
* CAVEATS: If a conflict is reported, a partial set of handlers may still have been added. There is no mechanism
|
||||
* for removing handlers, since this is considered an initialization function. And finally, when a range of addresses
|
||||
* is used, each successive address is advanced by 2, so if you really want to add a handler for a "+1" (usually odd)
|
||||
* address, then you must add it individually.
|
||||
* address, then you must add it individually. Failure to do is not necessarily fatal, because the IOController's
|
||||
* fallback behavior for an odd address is to call the byte handler for the preceding even address, but the byte
|
||||
* handler must be prepared for that (the handlers installed by ROM component's addROM() function are a good example).
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} start address
|
||||
|
|
@ -1309,18 +1304,16 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte,
|
|||
};
|
||||
|
||||
/**
|
||||
* addIOTable(component, table, msgCategory, sName)
|
||||
* addIOTable(component, table)
|
||||
*
|
||||
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers).
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {Component} component
|
||||
* @param {Object} table
|
||||
* @param {number} [msgCategory] (default is BUS)
|
||||
* @param {string} [sName]
|
||||
* @return {boolean} (true if entire range successfully registered, false if any conflicts)
|
||||
*/
|
||||
BusPDP11.prototype.addIOTable = function(component, table, msgCategory, sName)
|
||||
BusPDP11.prototype.addIOTable = function(component, table)
|
||||
{
|
||||
for (var port in table) {
|
||||
var addr = +port;
|
||||
|
|
@ -1336,7 +1329,6 @@ BusPDP11.prototype.addIOTable = function(component, table, msgCategory, sName)
|
|||
var fnWriteByte = afn[1]? afn[1].bind(component) : null;
|
||||
var fnReadWord = afn[2]? afn[2].bind(component) : null;
|
||||
var fnWriteWord = afn[3]? afn[3].bind(component) : null;
|
||||
var nRegs = afn[5] || 1;
|
||||
|
||||
/*
|
||||
* As discussed in the IOController comments above, when handlers are being registered for these
|
||||
|
|
@ -1360,9 +1352,11 @@ BusPDP11.prototype.addIOTable = function(component, table, msgCategory, sName)
|
|||
}
|
||||
|
||||
var sReg = afn[4];
|
||||
var nRegs = afn[5] || 1;
|
||||
|
||||
for (var iReg = 0; iReg < nRegs; iReg++, addr += 2) {
|
||||
if (sReg && nRegs > 1) sReg = afn[4] + iReg;
|
||||
if (!this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory || MessagesPDP11.BUS, sReg || sName)) {
|
||||
if (!this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, afn[7] || component.bitsMessage || MessagesPDP11.BUS, sReg || component.idComponent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1067,8 +1067,8 @@ ComputerPDP11.prototype.updateDisplays = function(nUpdate)
|
|||
* nUpdate will also be -1 whenever the Debugger has modified the state of the machine, implying that we're
|
||||
* not sure what, if anything, actually changed.
|
||||
*/
|
||||
if (this.cpu) this.cpu.updateDisplay(nUpdate);
|
||||
if (this.panel) this.panel.updateDisplay(nUpdate);
|
||||
if (this.cpu) this.cpu.updateDisplay(nUpdate || 0);
|
||||
if (this.panel) this.panel.updateDisplay(nUpdate || 0);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ function CPUPDP11(parmsCPU, nCyclesDefault)
|
|||
|
||||
var nMultiplier = parmsCPU['multiplier'] || 1;
|
||||
|
||||
this.nDisplayCount = 0;
|
||||
this.nDisplayLimit = 30;
|
||||
this.nCyclesPerSecond = nCycles;
|
||||
|
||||
/*
|
||||
|
|
@ -525,12 +527,17 @@ CPUPDP11.prototype.updateDisplays = function(nUpdate)
|
|||
* However, this should be called via the Computer's updateDisplays() interface, not directly.
|
||||
*
|
||||
* @this {CPUPDP11}
|
||||
* @param {number} [nUpdate] (1 for periodic, -1 for forced, 0 or undefined otherwise)
|
||||
* @param {number} [nUpdate] (1 for periodic, -1 for forced, 0 otherwise)
|
||||
*/
|
||||
CPUPDP11.prototype.updateDisplay = function(nUpdate)
|
||||
{
|
||||
var controlSpeed = this.bindings["speed"];
|
||||
if (controlSpeed) controlSpeed.textContent = this.getSpeedCurrent();
|
||||
if (controlSpeed) {
|
||||
if (nUpdate <= 0 || (this.nDisplayCount += nUpdate) >= this.nDisplayLimit) {
|
||||
controlSpeed.textContent = this.getSpeedCurrent();
|
||||
this.nDisplayCount = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -684,7 +691,7 @@ CPUPDP11.prototype.getSpeedCurrent = function()
|
|||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return ((this.flags.running && this.mhz)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
return ((this.flags.running)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1471,6 +1471,42 @@ PDP11.opRESET = function(opCode)
|
|||
{
|
||||
if (!(this.regPSW & PDP11.PSW.CMODE)) {
|
||||
this.resetCPU();
|
||||
|
||||
if (this.panel) {
|
||||
/*
|
||||
* The PDP-11/70 XXDP test "EKBBF0" reports the following, with PANEL messages on ("m panel on"):
|
||||
*
|
||||
* CNSW.writeWord(177570,000101) @033502
|
||||
* CNSW.readWord(177570): 000000 @032114
|
||||
* LOOK AT THE CONSOLE LIGHTS
|
||||
* THE DATA LIGHTS SHOULD READ 166667
|
||||
* THE ADDRESS LIGHTS SHOULD READ CNSW.readWord(177570): 000000 @032150
|
||||
* 032236
|
||||
* CHANGE SWITCH 7 TO CONTINUE
|
||||
* CNSW.readWord(177570): 000000 @032236
|
||||
* stopped (31518011 instructions, 358048873 cycles, 58644 ms, 6105465 hz)
|
||||
* R0=166667 R1=002362 R2=000000 R3=000000 R4=000000 R5=026642
|
||||
* SP=001074 PC=032236 PS=000344 SR=00000000 T0 N0 Z1 V0 C0
|
||||
* 032236: 032737 000200 177570 BIT #200,@#177570
|
||||
* >> tr
|
||||
* CNSW.readWord(177570): 000000 @032236 (cpu halted)
|
||||
* R0=166667 R1=002362 R2=000000 R3=000000 R4=000000 R5=026642
|
||||
* SP=001074 PC=032244 PS=000344 SR=00000000 T0 N0 Z1 V0 C0
|
||||
* 032244: 001773 BEQ 032234 ;cycles=0
|
||||
* >> tr
|
||||
* R0=166667 R1=002362 R2=000000 R3=000000 R4=000000 R5=026642
|
||||
* SP=001074 PC=032234 PS=000344 SR=00000000 T0 N0 Z1 V0 C0
|
||||
* 032234: 000005 RESET ;cycles=5
|
||||
*
|
||||
* It's a little hard to see why the DATA lights should read 166667, since the PANEL messages indicate
|
||||
* that the last CNSW.writeWord(177570) was for 000101, not 166667. So I'm guessing that the RESET
|
||||
* instruction is supposed to propagate R0 to the console's DISPLAY register.
|
||||
*
|
||||
* This is similar to what we do for the HALT instruction on the PDP-11/20. None of these Console features
|
||||
* seem to be very well documented (assuming they exist).
|
||||
*/
|
||||
this.panel.setData(this.regsGen[0], true);
|
||||
}
|
||||
}
|
||||
this.nStepCycles -= 667; // TODO: Review (but it's definitely a big number)
|
||||
};
|
||||
|
|
@ -1739,7 +1775,7 @@ PDP11.opSWAB = function(opCode)
|
|||
*/
|
||||
PDP11.opSXT = function(opCode)
|
||||
{
|
||||
this.updateNZVFlags(this.writeDstWord(opCode, this.getNF? 0xffff : 0));
|
||||
this.updateNZVFlags(this.writeDstWord(opCode, this.getNF()? 0xffff : 0));
|
||||
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
|
||||
};
|
||||
|
||||
|
|
@ -1846,7 +1882,8 @@ PDP11.opWAIT = function(opCode)
|
|||
*/
|
||||
PDP11.opXOR = function(opCode)
|
||||
{
|
||||
this.updateDstWord(opCode, this.readSrcWord(opCode), PDP11.fnXOR);
|
||||
var reg = (opCode >> PDP11.SRCMODE.SHIFT) & PDP11.OPREG.MASK;
|
||||
this.updateDstWord(opCode, this.regsGen[reg], PDP11.fnXOR);
|
||||
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -169,9 +169,10 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
/*
|
||||
* TODO: Verify the initial state of all PDP-11 flags and registers (are they well-documented?)
|
||||
*/
|
||||
var f = 0xffff;
|
||||
this.flagC = 0x10000; // PSW C bit
|
||||
this.flagV = 0x8000; // PSW V bit
|
||||
this.flagZ = 0xffff; // ~ PSW Z bit (TODO: Why do we clear instead of set Z, like other flags?)
|
||||
this.flagZ = f; // ~ PSW Z bit (TODO: Why do we clear instead of set Z, like other flags?)
|
||||
this.flagN = 0x8000; // PSW N bit
|
||||
this.regPSW = 0x000f; // PSW other bits (TODO: What's the point of setting the flag bits here, too?)
|
||||
this.regsGen = [ // General R0 - R7
|
||||
|
|
@ -180,24 +181,23 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
this.regsAlt = [ // Alternate R0 - R5
|
||||
0, 0, 0, 0, 0, 0
|
||||
];
|
||||
this.regsAltStack = [ // Alternate R6 stack pointers (KERNEL, SUPER, illegal, USER)
|
||||
this.regsAltStack = [ // Alternate R6 stack pointers (KERNEL, SUPER, UNUSED, USER)
|
||||
0, 0, 0, 0
|
||||
];
|
||||
this.mmuMode = 0; // current memory management mode (see PDP11.MODE.KERNEL | SUPER | UNUSED | USER)
|
||||
this.mmuLastPage = 0;
|
||||
this.mmuLastVirtual = 0;
|
||||
this.mapMMR3 = [4,2,0,1]; // map from mode to MMR3 I/D bit
|
||||
this.mmuPDR = [ // memory management PDR registers by mode
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // kernel 0
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // super 1
|
||||
[0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff], // mode 2 with illegal PDRs
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // user 3
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPDR regs followed by 8 KDPDR regs)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
|
||||
[f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f], // mode 2 (not used)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
|
||||
];
|
||||
this.mmuPAR = [ // memory management PAR registers by mode
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // kernel 0
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // super 1
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPAR regs followed by 8 KDPAR regs)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // mode 2 (not used)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // user 3
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
|
||||
];
|
||||
this.unibusMap = [ // 32 unibus map registers
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
|
@ -222,7 +222,7 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
this.dstMode = this.dstReg = this.dstAddr = 0;
|
||||
|
||||
this.trapPSW = -1;
|
||||
this.resetRegs();
|
||||
this.resetMMU();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -233,35 +233,48 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
CPUStatePDP11.prototype.resetCPU = function()
|
||||
{
|
||||
this.bus.reset();
|
||||
this.resetRegs();
|
||||
this.resetMMU();
|
||||
};
|
||||
|
||||
/**
|
||||
* resetRegs()
|
||||
* resetMMU()
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
*/
|
||||
CPUStatePDP11.prototype.resetRegs = function()
|
||||
CPUStatePDP11.prototype.resetMMU = function()
|
||||
{
|
||||
this.regSL = 0xff; // 177774
|
||||
this.regErr = 0; // 177766
|
||||
this.regPIR = 0; // 177772
|
||||
this.regMMR0 = 0; // 177572
|
||||
this.regMMR1 = 0; // 177574
|
||||
this.regMMR2 = 0; // 177576
|
||||
this.regMMR3 = 0; // 172516
|
||||
this.regErr = 0; // 177766
|
||||
this.regPIR = 0; // 177772
|
||||
this.regSL = 0xff; // 177774
|
||||
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
|
||||
this.mmuLastMode = 0;
|
||||
this.mmuMask = 0x3ffff;
|
||||
|
||||
this.lastAI = 0; // last auto-incs and/or auto-decs; this gets propagated to MMR1
|
||||
this.lastPC = 0; // last PC at the time of getOpcode(); this get propagated to MMR2
|
||||
this.lastAddr = 0; // this is queried by the Panel when it's not using its own ADDRESS register
|
||||
this.lastOp = 0; // stores the PC and any auto-incs or auto-decs from the last opcode; used to update MMR1 and MMR2
|
||||
|
||||
this.resetTriggers();
|
||||
|
||||
if (this.bus) this.setMemoryAccess();
|
||||
};
|
||||
|
||||
/**
|
||||
* getMMUState()
|
||||
*
|
||||
* Returns bit 0 set if 22-bit, bit 1 set if 18-bit, or bit 2 set if 16-bit; used by the Panel component.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getMMUState = function()
|
||||
{
|
||||
return this.mmuEnable? ((this.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
|
||||
};
|
||||
|
||||
/**
|
||||
* setMemoryAccess()
|
||||
*
|
||||
|
|
@ -277,12 +290,14 @@ CPUStatePDP11.prototype.setMemoryAccess = function()
|
|||
{
|
||||
if (this.mmuEnable) {
|
||||
this.addrDSpace = PDP11.ACCESS.DSPACE;
|
||||
this.addrIOPage = (this.regMMR3 & PDP11.MMR3.MMU_22BIT)? BusPDP11.IOPAGE_22BIT : BusPDP11.IOPAGE_18BIT;
|
||||
this.getAddr = this.getVirtualAddrByMode;
|
||||
this.readWord = this.readWordFromVirtual;
|
||||
this.writeWord = this.writeWordToVirtual;
|
||||
this.bus.setIOPageRange((this.regMMR3 & PDP11.MMR3.MMU_22BIT)? 22 : 18);
|
||||
} else {
|
||||
this.addrDSpace = 0;
|
||||
this.addrIOPage = BusPDP11.IOPAGE_16BIT;
|
||||
this.getAddr = this.getPhysicalAddrByMode;
|
||||
this.readWord = this.readWordFromPhysical;
|
||||
this.writeWord = this.writeWordToPhysical;
|
||||
|
|
@ -293,6 +308,8 @@ CPUStatePDP11.prototype.setMemoryAccess = function()
|
|||
/**
|
||||
* getMMR0()
|
||||
*
|
||||
* NOTE: It's OK to bypass this function if you're only interested in bits that always stored directly in MMR0.
|
||||
*
|
||||
* 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 MMR0
|
||||
* nonr leng read trap unus unus ena mnt cmp -mode- i/d --page-- enable
|
||||
*
|
||||
|
|
@ -301,7 +318,11 @@ CPUStatePDP11.prototype.setMemoryAccess = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.getMMR0 = function()
|
||||
{
|
||||
return (this.regMMR0 & 0xf381) | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
||||
var data = this.regMMR0;
|
||||
if (!(data & PDP11.MMR0.ABORT)) {
|
||||
data = (data & ~(PDP11.MMR0.UNUSED | PDP11.MMR0.PAGE | PDP11.MMR0.MODE)) | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -312,8 +333,22 @@ CPUStatePDP11.prototype.getMMR0 = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.setMMR0 = function(newMMR0)
|
||||
{
|
||||
newMMR0 &= 0xf381;
|
||||
newMMR0 &= ~PDP11.MMR0.UNUSED;
|
||||
|
||||
if (this.regMMR0 != newMMR0) {
|
||||
if (newMMR0 & PDP11.MMR0.ABORT) {
|
||||
/*
|
||||
* If updates to MMR0[1-7], MMR1, and MMR2 are being shut off (ie, MMR0.ABORT bits are transitioning
|
||||
* from clear to set), then do one final sync with their real-time counterparts in lastOp.
|
||||
*/
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR1 = (this.lastOp >> 16) & 0xffff;
|
||||
this.regMMR2 = this.lastOp & 0xffff;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* NOTE: We are not protecting the read-only state of the COMPLETED bit here; that's handled by writeMMR0().
|
||||
*/
|
||||
this.regMMR0 = newMMR0;
|
||||
this.mmuLastMode = (newMMR0 >> 5) & 3;
|
||||
this.mmuLastPage = (newMMR0 >> 1) & 0xf;
|
||||
|
|
@ -339,8 +374,12 @@ CPUStatePDP11.prototype.setMMR0 = function(newMMR0)
|
|||
*/
|
||||
CPUStatePDP11.prototype.getMMR1 = function()
|
||||
{
|
||||
/*
|
||||
* If updates to MMR1 have not been shut off (ie, MMR0.ABORT bits are clear),
|
||||
* then we are allowed to sync MMR1 with its real-time counterpart in lastOp.
|
||||
*/
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR1 = this.lastAI & 0xffff;
|
||||
this.regMMR1 = (this.lastOp >> 16) & 0xffff;
|
||||
}
|
||||
var result = this.regMMR1;
|
||||
if (result & 0xff00) {
|
||||
|
|
@ -357,8 +396,12 @@ CPUStatePDP11.prototype.getMMR1 = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.getMMR2 = function()
|
||||
{
|
||||
/*
|
||||
* If updates to MMR2 have not been shut off (ie, MMR0.ABORT bits are clear),
|
||||
* then we are allowed to sync MMR2 with its real-time counterpart in lastOp.
|
||||
*/
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR2 = this.lastPC;
|
||||
this.regMMR2 = this.lastOp & 0xffff;
|
||||
}
|
||||
return this.regMMR2;
|
||||
};
|
||||
|
|
@ -602,8 +645,7 @@ CPUStatePDP11.prototype.setNF = function()
|
|||
CPUStatePDP11.prototype.getOpcode = function()
|
||||
{
|
||||
var pc = this.regsGen[PDP11.REG.PC];
|
||||
this.lastAI = 0;
|
||||
this.lastPC = pc;
|
||||
this.lastOp = pc;
|
||||
/*
|
||||
* If PC is unaligned, a BUS trap will be generated, and because it will generate an
|
||||
* exception, the next line (the equivalent of advancePC(2)) will not be executed, ensuring that
|
||||
|
|
@ -643,6 +685,17 @@ CPUStatePDP11.prototype.getPC = function()
|
|||
return this.regsGen[PDP11.REG.PC];
|
||||
};
|
||||
|
||||
/**
|
||||
* getLastAddr()
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getLastAddr = function()
|
||||
{
|
||||
return this.lastAddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* getLastPC()
|
||||
*
|
||||
|
|
@ -651,7 +704,7 @@ CPUStatePDP11.prototype.getPC = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.getLastPC = function()
|
||||
{
|
||||
return this.lastPC;
|
||||
return this.lastOp & 0xffff;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1225,19 +1278,6 @@ CPUStatePDP11.prototype.updateSubFlags = function(result, src, dst)
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* panic(reason)
|
||||
*
|
||||
* TODO: Something.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} reason
|
||||
*/
|
||||
CPUStatePDP11.prototype.panic = function(reason)
|
||||
{
|
||||
console.log("panic(" + reason + ")");
|
||||
};
|
||||
|
||||
/**
|
||||
* trap(vector, flag, reason)
|
||||
*
|
||||
|
|
@ -1277,8 +1317,19 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
reason = PDP11.REASON.RED;
|
||||
}
|
||||
|
||||
this.lastPC = vector;
|
||||
this.lastAI = 0;
|
||||
/*
|
||||
* NOTE: Pre-setting the auto-dec values for MMR1 to 0xF6F6 is a work-around for an "EKBEE1"
|
||||
* diagnostic (PC 056710), which tests what happens when a misaligned read triggers a BUS trap,
|
||||
* and that trap then triggers an MMU trap during the first pushWord() below.
|
||||
*
|
||||
* One would think it would be fine to zero those bits by setting lastOp to vector alone,
|
||||
* and then letting each of the pushWord() calls below shift their own 0xF6 auto-dec value into
|
||||
* lastOp. When the first pushWord() triggers an MMU trap, we obviously won't get to the second
|
||||
* pushWord(), yet the diagnostic expects TWO auto-decs to be recorded. I'm puzzled why the
|
||||
* hardware apparently indicates TWO auto-decs, if SP wasn't actually decremented twice, but who
|
||||
* am I to judge.
|
||||
*/
|
||||
this.lastOp = vector | 0xf6f60000;
|
||||
|
||||
/*
|
||||
* Read from kernel D space
|
||||
|
|
@ -1381,7 +1432,7 @@ CPUStatePDP11.prototype.getTrapStatus = function()
|
|||
/**
|
||||
* mapUnibus(addr)
|
||||
*
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
|
||||
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.UNIBUS_22BIT aka 0x3C0000),
|
||||
* then we have a 22-bit address pointing to the top 256Kb range, so if the UNIBUS relocation map is enabled,
|
||||
* we must pass the lower 18 bits of that address through the map.
|
||||
*
|
||||
|
|
@ -1418,8 +1469,19 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
|
|||
var idx = (addr >> 13) & 0x1f;
|
||||
if (idx < 31) {
|
||||
if (this.regMMR3 & PDP11.MMR3.UNIBUS_MAP) {
|
||||
/*
|
||||
* The UNIBUS map relocation is enabled
|
||||
*/
|
||||
addr = (this.unibusMap[idx] + (addr & 0x1ffe)) & 0x3ffffe;
|
||||
if (addr >= BusPDP11.IOPAGE_UNIBUS && addr < BusPDP11.IOPAGE_22BIT) this.panic(898);
|
||||
this.assert(addr < BusPDP11.UNIBUS_22BIT || addr >= BusPDP11.IOPAGE_22BIT);
|
||||
} else {
|
||||
/*
|
||||
* Since UNIBUS map relocation is NOT enabled, then as explained above:
|
||||
*
|
||||
* If the UNIBUS map relocation is not enabled, an incoming 18-bit UNIBUS address has 4 leading zeroes added for
|
||||
* referencing a 22-bit physical address. The lower 18 bits are the same. No relocation is performed.
|
||||
*/
|
||||
addr &= ~BusPDP11.UNIBUS_22BIT;
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
|
|
@ -1490,53 +1552,85 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but *not* the ENABLED bit.
|
||||
*/
|
||||
if (!(accessFlags & this.mmuEnable)) {
|
||||
return virtualAddress;
|
||||
physicalAddress = virtualAddress & 0xffff;
|
||||
if (physicalAddress >= BusPDP11.IOPAGE_16BIT) {
|
||||
physicalAddress |= this.addrIOPage;
|
||||
}
|
||||
return physicalAddress;
|
||||
}
|
||||
|
||||
this.mmuLastVirtual = virtualAddress;
|
||||
|
||||
page = virtualAddress >> 13;
|
||||
if (!(this.regMMR3 & this.mapMMR3[this.mmuMode])) page &= 7;
|
||||
pdr = this.mmuPDR[this.mmuMode][page];
|
||||
physicalAddress = ((this.mmuPAR[this.mmuMode][page] << 6) + (virtualAddress & 0x1fff)) & this.mmuMask;
|
||||
|
||||
var errorMask = 0;
|
||||
switch (pdr & 0x7) {
|
||||
case 1: // read-only with trap
|
||||
errorMask = PDP11.MMR0.TRAP_MMU;
|
||||
if (this.nDisableTraps) return physicalAddress;
|
||||
|
||||
/*
|
||||
* This next bit is the weirdness that Paul mentions in the function description above:
|
||||
*
|
||||
* As an aside it turns out that it is the memory management unit that does odd address and
|
||||
* non-existent memory trapping: who knew? :-) I thought these would have been handled at access time.
|
||||
*
|
||||
* I haven't imported the non-existent memory checks he performed (yet); I would like to see the problems
|
||||
* in action first.
|
||||
*
|
||||
* As for the ODDADDR error that's supposed to generate a BUS error rather than an MMU error, this happens
|
||||
* in TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076456).
|
||||
*/
|
||||
if ((physicalAddress & 0x1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
this.regErr |= PDP11.CPUERR.ODDADDR;
|
||||
this.trap(PDP11.TRAP.BUS, 0, physicalAddress);
|
||||
}
|
||||
|
||||
var newMMR0 = 0;
|
||||
switch (pdr & PDP11.PDR.ACF.MASK) {
|
||||
|
||||
case PDP11.PDR.ACF.RO1: // 0x1: read-only, abort on write attempt, memory management trap on read (11/70 only)
|
||||
newMMR0 = PDP11.MMR0.TRAP_MMU;
|
||||
/* falls through */
|
||||
case 2: // read-only
|
||||
pdr |= 0x80; // Set A bit
|
||||
|
||||
case PDP11.PDR.ACF.RO: // 0x2: read-only, abort on write attempt
|
||||
pdr |= PDP11.PDR.ACCESSED;
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
errorMask = PDP11.MMR0.ABORT_RO;
|
||||
newMMR0 = PDP11.MMR0.ABORT_RO;
|
||||
}
|
||||
break;
|
||||
case 4: // read-write with read-write trap
|
||||
errorMask = PDP11.MMR0.TRAP_MMU;
|
||||
|
||||
case PDP11.PDR.ACF.RW1: // 0x4: read/write, memory management trap upon completion of a read or write
|
||||
newMMR0 = PDP11.MMR0.TRAP_MMU;
|
||||
/* falls through */
|
||||
case 5: // read-write with write trap
|
||||
|
||||
case PDP11.PDR.ACF.RW2: // 0x5: read/write, memory management trap upon completion of a write (11/70 only)
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
errorMask = PDP11.MMR0.TRAP_MMU;
|
||||
newMMR0 = PDP11.MMR0.TRAP_MMU;
|
||||
}
|
||||
/* falls through */
|
||||
case 6: // read-write: set A & W bits
|
||||
pdr |= ((accessFlags & PDP11.ACCESS.WRITE) ? 0xc0 : 0x80);
|
||||
|
||||
case PDP11.PDR.ACF.RW: // 0x6: read/write, no system trap/abort action
|
||||
pdr |= ((accessFlags & PDP11.ACCESS.WRITE) ? (PDP11.PDR.ACCESSED | PDP11.PDR.MODIFIED) : PDP11.PDR.ACCESSED);
|
||||
break;
|
||||
default:
|
||||
errorMask = PDP11.MMR0.ABORT_NR;
|
||||
|
||||
default: // 0x0 (non-resident, abort all accesses) or 0x3 or 0x7 (unused, abort all accesses)
|
||||
newMMR0 = PDP11.MMR0.ABORT_NR;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pdr & 0x7f08) != 0x7f00) { // skip checking most common case (hopefully)
|
||||
if (pdr & 0x8) { // expand downwards
|
||||
if (pdr & 0x7f00) {
|
||||
if ((virtualAddress & 0x1fc0) < ((pdr >> 2) & 0x1fc0)) {
|
||||
errorMask |= PDP11.MMR0.ABORT_PL;
|
||||
if ((pdr & (PDP11.PDR.PLF | PDP11.PDR.ED)) != PDP11.PDR.PLF) { // skip checking most common case (hopefully)
|
||||
/*
|
||||
* The Page Descriptor Register (PDR) Page Length Field (PLF) is a 7-bit block number, where a block
|
||||
* is 64 bytes. Since the bit 0 of the block number is located at bit 8 of the PDR, we shift the PDR
|
||||
* right 2 bits and then clear the bottom 6 bits by masking it with 0x1FC0.
|
||||
*/
|
||||
if (pdr & PDP11.PDR.ED) {
|
||||
if (pdr & PDP11.PDR.PLF) {
|
||||
if ((virtualAddress & 0x1FC0) < ((pdr >> 2) & 0x1FC0)) {
|
||||
newMMR0 |= PDP11.MMR0.ABORT_PL;
|
||||
}
|
||||
}
|
||||
} else { // expand upwards
|
||||
if ((virtualAddress & 0x1fc0) > ((pdr >> 2) & 0x1fc0)) {
|
||||
errorMask |= PDP11.MMR0.ABORT_PL;
|
||||
} else {
|
||||
if ((virtualAddress & 0x1FC0) > ((pdr >> 2) & 0x1FC0)) {
|
||||
newMMR0 |= PDP11.MMR0.ABORT_PL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1551,14 +1645,22 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
this.mmuLastPage = page;
|
||||
}
|
||||
|
||||
var fAbort = false;
|
||||
if (errorMask) {
|
||||
if (errorMask & PDP11.MMR0.ABORT) {
|
||||
if (this.trapPSW >= 0) errorMask |= 0x80; // Instruction complete
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR0 |= errorMask | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
||||
if (newMMR0) {
|
||||
if (newMMR0 & PDP11.MMR0.ABORT) {
|
||||
if (this.trapPSW >= 0) {
|
||||
newMMR0 |= PDP11.MMR0.COMPLETED;
|
||||
}
|
||||
fAbort = true;
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
newMMR0 |= (this.regMMR0 & PDP11.MMR0.TRAP_MMU) | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
||||
this.assert(!(newMMR0 & ~PDP11.MMR0.UPDATE));
|
||||
this.setMMR0((this.regMMR0 & ~PDP11.MMR0.UPDATE) | (newMMR0 & PDP11.MMR0.UPDATE));
|
||||
}
|
||||
/*
|
||||
* TODO: In unusual circumstances, if regMMR0 already indicated an ABORT condition above,
|
||||
* we run the risk of infinitely looping; eg, we call trap(), which calls mapVirtualToPhysical()
|
||||
* on the trap vector, which faults again, etc. We should add some safeguards against that.
|
||||
*/
|
||||
this.trap(PDP11.TRAP.MMU, PDP11.OPFLAG.TRAP_MMU, PDP11.REASON.ABORT);
|
||||
}
|
||||
if (!(this.regMMR0 & (PDP11.MMR0.ABORT | PDP11.MMR0.TRAP_MMU))) {
|
||||
/*
|
||||
|
|
@ -1572,9 +1674,6 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
}
|
||||
}
|
||||
}
|
||||
if (fAbort) { // don't abort until the end, because it throws an exception
|
||||
this.trap(PDP11.TRAP.MMU, 0, PDP11.REASON.ABORT);
|
||||
}
|
||||
}
|
||||
return physicalAddress;
|
||||
};
|
||||
|
|
@ -1627,7 +1726,7 @@ CPUStatePDP11.prototype.pushWord = function(data)
|
|||
{
|
||||
var virtualAddress = (this.regsGen[6] - 2) & 0xffff;
|
||||
this.regsGen[6] = virtualAddress; // BSD needs SP updated before any fault :-(
|
||||
this.lastAI = (this.lastAI << 8) | 0xf6;
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | (0x00f6 << 16);
|
||||
this.checkStackLimit(0, virtualAddress);
|
||||
this.writeWord(virtualAddress, data);
|
||||
};
|
||||
|
|
@ -1812,7 +1911,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
}
|
||||
|
||||
this.regsGen[reg] = (this.regsGen[reg] + step) & 0xffff;
|
||||
this.lastAI = (this.lastAI << 8) | ((step << 3) & 0xf8) | reg;
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | ((((step << 3) & 0xf8) | reg) << 16);
|
||||
|
||||
/*
|
||||
* NOTE: We had to eliminate the test for "(accessFlags & PDP11.ACCESS.WRITE)", because DEC's
|
||||
|
|
@ -1951,7 +2050,7 @@ CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, accessFlags)
|
|||
*/
|
||||
CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
|
||||
{
|
||||
return this.bus.getWord(physicalAddress);
|
||||
return this.bus.getWord(this.lastAddr = physicalAddress);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1965,7 +2064,7 @@ CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
|
|||
*/
|
||||
CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
|
||||
{
|
||||
return this.bus.getWord(this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
|
||||
return this.bus.getWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1979,7 +2078,7 @@ CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
|
|||
*/
|
||||
CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
|
||||
{
|
||||
this.bus.setWord(physicalAddress, data & 0xffff);
|
||||
this.bus.setWord(this.lastAddr = physicalAddress, data & 0xffff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1993,7 +2092,7 @@ CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
|
|||
*/
|
||||
CPUStatePDP11.prototype.writeWordToVirtual = function(virtualAddress, data)
|
||||
{
|
||||
this.bus.setWord(this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.WRITE_WORD), data);
|
||||
this.bus.setWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.WRITE_WORD), data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2037,7 +2136,7 @@ CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, accessFlags)
|
|||
*/
|
||||
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, data)
|
||||
{
|
||||
this.lastAI = 0x0016;
|
||||
this.lastOp = (this.lastOp & 0xffff) | (0x0016 << 16);
|
||||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
|
||||
if (!mode) {
|
||||
|
|
|
|||
|
|
@ -266,7 +266,32 @@ if (DEBUGGER) {
|
|||
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt, and 16-19 for cpu.regsStack.
|
||||
*/
|
||||
DebuggerPDP11.REG_PSW = 20;
|
||||
DebuggerPDP11.REG_SR = 21; // SWITCH register; see Panel's getSR() and setSR()
|
||||
DebuggerPDP11.REG_PIR = 21;
|
||||
DebuggerPDP11.REG_ERR = 22;
|
||||
DebuggerPDP11.REG_SL = 23;
|
||||
DebuggerPDP11.REG_MMR0 = 24;
|
||||
DebuggerPDP11.REG_MMR1 = 25;
|
||||
DebuggerPDP11.REG_MMR2 = 26;
|
||||
DebuggerPDP11.REG_MMR3 = 27;
|
||||
DebuggerPDP11.REG_AR = 28; // ADDRESS register; see Panel's getAR() and setAR()
|
||||
DebuggerPDP11.REG_DR = 29; // DISPLAY/DATA register; see Panel's getDR() and setDR()
|
||||
DebuggerPDP11.REG_SR = 30; // SWITCH register; see Panel's getSR() and setSR()
|
||||
|
||||
DebuggerPDP11.REGS = {
|
||||
"SP": 6,
|
||||
"PC": 7,
|
||||
"PS": DebuggerPDP11.REG_PSW,
|
||||
"IR": DebuggerPDP11.REG_PIR,
|
||||
"ER": DebuggerPDP11.REG_ERR,
|
||||
"SL": DebuggerPDP11.REG_SL,
|
||||
"MMR0": DebuggerPDP11.REG_MMR0,
|
||||
"MMR1": DebuggerPDP11.REG_MMR1,
|
||||
"MMR2": DebuggerPDP11.REG_MMR2,
|
||||
"MMR3": DebuggerPDP11.REG_MMR3,
|
||||
"AR": DebuggerPDP11.REG_AR,
|
||||
"DR": DebuggerPDP11.REG_DR,
|
||||
"SR": DebuggerPDP11.REG_SR
|
||||
};
|
||||
|
||||
/*
|
||||
* Operand type masks; anything that's not covered by OP_SRC or OP_DST must be a OP_OTHER value.
|
||||
|
|
@ -322,7 +347,7 @@ if (DEBUGGER) {
|
|||
0x7200: [DebuggerPDP11.OPS.DIV, DebuggerPDP11.OP_DST, DebuggerPDP11.OP_SRCREG], // 071RSS
|
||||
0x7400: [DebuggerPDP11.OPS.ASH, DebuggerPDP11.OP_DST, DebuggerPDP11.OP_SRCREG], // 072RSS
|
||||
0x7600: [DebuggerPDP11.OPS.ASHC, DebuggerPDP11.OP_DST, DebuggerPDP11.OP_SRCREG], // 073RSS
|
||||
0x7800: [DebuggerPDP11.OPS.XOR, DebuggerPDP11.OP_SRCREG, DebuggerPDP11.OP_DST], // 074RSS
|
||||
0x7800: [DebuggerPDP11.OPS.XOR, DebuggerPDP11.OP_SRCREG, DebuggerPDP11.OP_DST], // 074RDD
|
||||
0x7E00: [DebuggerPDP11.OPS.SOB, DebuggerPDP11.OP_SRCREG, DebuggerPDP11.OP_DSTOFF] // 077Rnn
|
||||
},
|
||||
0xFF00: {
|
||||
|
|
@ -559,7 +584,7 @@ if (DEBUGGER) {
|
|||
var fCompleted = false;
|
||||
if (!dbg.isBusy(true)) {
|
||||
dbg.setBusy(true);
|
||||
fCompleted = dbg.stepCPU(fRepeat? 1 : 0);
|
||||
fCompleted = dbg.stepCPU(fRepeat? 1 : 0, null);
|
||||
dbg.setBusy(false);
|
||||
}
|
||||
return fCompleted;
|
||||
|
|
@ -583,7 +608,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
if (this.controlDebug) {
|
||||
/*
|
||||
* This seems to be recommended work-around to prevent the browser from scrolling the focused element
|
||||
* This is the recommended work-around to prevent the browser from scrolling the focused element
|
||||
* into view. The CPU is not a visual component, so when the CPU wants to set focus, the primary intent
|
||||
* is to ensure that keyboard input is fielded properly.
|
||||
*/
|
||||
|
|
@ -1118,24 +1143,14 @@ if (DEBUGGER) {
|
|||
*/
|
||||
DebuggerPDP11.prototype.getRegIndex = function(sReg, off)
|
||||
{
|
||||
var iReg = -1;
|
||||
sReg = sReg.toUpperCase();
|
||||
switch(sReg) {
|
||||
case "SP":
|
||||
iReg = 6;
|
||||
break;
|
||||
case "PC":
|
||||
iReg = 7;
|
||||
break;
|
||||
case "SR":
|
||||
iReg = 21;
|
||||
break;
|
||||
default:
|
||||
var iReg = DebuggerPDP11.REGS[sReg];
|
||||
if (iReg == null) {
|
||||
iReg = -1;
|
||||
if (sReg.charAt(0) == "R") {
|
||||
iReg = +sReg.charAt(1);
|
||||
if (iReg < 0 || iReg > 7) iReg = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return iReg;
|
||||
};
|
||||
|
|
@ -1156,7 +1171,7 @@ if (DEBUGGER) {
|
|||
* getRegValue(iReg)
|
||||
*
|
||||
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt,
|
||||
* 16-19 for cpu.regsAltStack, 20 for regPSW, and 21 for SR (SWITCH register).
|
||||
* 16-19 for cpu.regsAltStack, 20 for regPSW, etc.
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {number} iReg
|
||||
|
|
@ -1175,11 +1190,46 @@ if (DEBUGGER) {
|
|||
else if (iReg < 20) {
|
||||
value = this.cpu.regsAltStack[iReg-16];
|
||||
}
|
||||
else if (iReg == DebuggerPDP11.REG_PSW) {
|
||||
value = this.cpu.getPSW();
|
||||
}
|
||||
else if (iReg == DebuggerPDP11.REG_SR && this.panel && this.panel.hasSwitches()) {
|
||||
value = this.panel.getSR();
|
||||
else {
|
||||
var cpu = this.cpu;
|
||||
var panel = this.panel;
|
||||
switch(iReg) {
|
||||
case DebuggerPDP11.REG_PSW:
|
||||
value = this.cpu.getPSW();
|
||||
break;
|
||||
case DebuggerPDP11.REG_PIR:
|
||||
value = cpu.regPIR;
|
||||
break;
|
||||
case DebuggerPDP11.REG_ERR:
|
||||
value = cpu.regErr;
|
||||
break;
|
||||
case DebuggerPDP11.REG_SL:
|
||||
value = cpu.regSL;
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR0:
|
||||
value = cpu.getMMR0();
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR1:
|
||||
value = cpu.getMMR1();
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR2:
|
||||
value = cpu.getMMR2();
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR3:
|
||||
value = cpu.getMMR3();
|
||||
break;
|
||||
case DebuggerPDP11.REG_AR:
|
||||
if (panel) value = panel.getAR();
|
||||
break;
|
||||
case DebuggerPDP11.REG_DR:
|
||||
if (panel) value = panel.getDR();
|
||||
break;
|
||||
case DebuggerPDP11.REG_SR:
|
||||
if (panel && panel.hasSwitches()) {
|
||||
value = panel.getSR();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
@ -1220,9 +1270,10 @@ if (DEBUGGER) {
|
|||
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
|
||||
this.sMessagePrev = sMessage;
|
||||
|
||||
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && this.cpu.isRunning() || this.isBusy(true)) {
|
||||
var fRunning = false;
|
||||
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && (fRunning = this.cpu.isRunning()) || this.isBusy(true)) {
|
||||
this.stopCPU();
|
||||
sMessage += " (cpu halted)";
|
||||
if (fRunning) sMessage += " (cpu halted)";
|
||||
}
|
||||
|
||||
this.println(sMessage); // + " (" + this.cpu.getCycles() + " cycles)"
|
||||
|
|
@ -1317,7 +1368,7 @@ if (DEBUGGER) {
|
|||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {number} nCycles (0 for one instruction without checking breakpoints)
|
||||
* @param {boolean} [fRegs] is true to display registers after step (default is false)
|
||||
* @param {boolean|null} [fRegs] is true to display registers after step (default is false; use null for previous setting)
|
||||
* @param {boolean} [fUpdateDisplays] is false to disable Computer display updates (default is true)
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
|
@ -1325,6 +1376,10 @@ if (DEBUGGER) {
|
|||
{
|
||||
if (!this.checkCPU()) return false;
|
||||
|
||||
if (fRegs === null) {
|
||||
fRegs = (!this.sTraceCmdPrev || this.sTraceCmdPrev == "tr");
|
||||
}
|
||||
|
||||
this.nCycles = 0;
|
||||
|
||||
if (!nCycles) {
|
||||
|
|
@ -1367,6 +1422,7 @@ if (DEBUGGER) {
|
|||
* is calling us in a loop, in which case it will perform its own updateDisplays() when it's done.
|
||||
*/
|
||||
if (fUpdateDisplays !== false) {
|
||||
if (this.panel && this.panel.stop) this.panel.stop();
|
||||
this.cmp.updateDisplays(-1);
|
||||
}
|
||||
|
||||
|
|
@ -1583,9 +1639,9 @@ if (DEBUGGER) {
|
|||
if (this.checksEnabled()) {
|
||||
sStopped += this.cInstructions + " instructions, ";
|
||||
/*
|
||||
* $ops displays progress by calculating cOpcodes - cOpcodesStart, so before
|
||||
* zeroing cOpcodes, we should subtract cOpcodes from cOpcodesStart (since we're
|
||||
* effectively subtracting cOpcodes from cOpcodes as well).
|
||||
* $ops displays progress by calculating cInstructions - cInstructionsStart, so before
|
||||
* zeroing cInstructions, we should subtract cInstructions from cInstructionsStart (since
|
||||
* we're effectively subtracting cInstructions from cInstructions as well).
|
||||
*/
|
||||
this.cInstructionsStart -= this.cInstructions;
|
||||
this.cInstructions = 0;
|
||||
|
|
@ -2147,16 +2203,16 @@ if (DEBUGGER) {
|
|||
sOperands += (sOperand || "???");
|
||||
}
|
||||
|
||||
var sOpcodes = "";
|
||||
var sOpCodes = "";
|
||||
var sLine = this.toStrAddr(dbgAddrOp) + ":";
|
||||
if (dbgAddrOp.addr !== PDP11.ADDR_INVALID && dbgAddr.addr !== PDP11.ADDR_INVALID) {
|
||||
do {
|
||||
sOpcodes += ' ' + this.toStrBase(this.getWord(dbgAddrOp, 2));
|
||||
sOpCodes += ' ' + this.toStrBase(this.getWord(dbgAddrOp, 2));
|
||||
if (dbgAddrOp.addr == null) break;
|
||||
} while (dbgAddrOp.addr != dbgAddr.addr);
|
||||
}
|
||||
|
||||
sLine += str.pad(sOpcodes, 24);
|
||||
sLine += str.pad(sOpCodes, 24);
|
||||
sLine += str.pad(sOpName, 5);
|
||||
if (sOperands) sLine += ' ' + sOperands;
|
||||
|
||||
|
|
@ -2389,28 +2445,86 @@ if (DEBUGGER) {
|
|||
else if (iReg >= 16 && iReg < 20) {
|
||||
sReg = "S" + (iReg - 16) + '=' + this.toStrBase(cpu.regsAltStack[iReg - 16]);
|
||||
}
|
||||
else if (iReg == DebuggerPDP11.REG_PSW) {
|
||||
sReg = "PS=" + this.toStrBase(cpu.getPSW());
|
||||
}
|
||||
else if (iReg == DebuggerPDP11.REG_SR && this.panel && this.panel.hasSwitches()) {
|
||||
sReg = "SR=" + this.toStrBase(this.panel.getSR(), 3);
|
||||
else {
|
||||
switch(iReg) {
|
||||
case DebuggerPDP11.REG_PSW:
|
||||
sReg = "PS=" + this.toStrBase(cpu.getPSW());
|
||||
break;
|
||||
case DebuggerPDP11.REG_PIR:
|
||||
sReg = "IR=" + this.toStrBase(cpu.regPIR);
|
||||
break;
|
||||
case DebuggerPDP11.REG_ERR:
|
||||
sReg = "ER=" + this.toStrBase(cpu.regErr);
|
||||
break;
|
||||
case DebuggerPDP11.REG_SL:
|
||||
sReg = "SL=" + this.toStrBase(cpu.regSL);
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR0:
|
||||
sReg = "MMR0=" + this.toStrBase(cpu.getMMR0());
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR1:
|
||||
sReg = "MMR1=" + this.toStrBase(cpu.getMMR1());
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR2:
|
||||
sReg = "MMR2=" + this.toStrBase(cpu.getMMR2());
|
||||
break;
|
||||
case DebuggerPDP11.REG_MMR3:
|
||||
sReg = "MMR3=" + this.toStrBase(cpu.getMMR3());
|
||||
break;
|
||||
case DebuggerPDP11.REG_AR:
|
||||
if (this.panel) {
|
||||
sReg = "AR=" + this.toStrBase(this.panel.getAR(), 3);
|
||||
}
|
||||
break;
|
||||
case DebuggerPDP11.REG_DR:
|
||||
if (this.panel) {
|
||||
sReg = "DR=" + this.toStrBase(this.panel.getDR());
|
||||
}
|
||||
break;
|
||||
case DebuggerPDP11.REG_SR:
|
||||
if (this.panel && this.panel.hasSwitches()) {
|
||||
sReg = "SR=" + this.toStrBase(this.panel.getSR(), 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sReg) sReg += ' ';
|
||||
return sReg;
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegDump()
|
||||
* getMiscDump()
|
||||
*
|
||||
* Sample register dump:
|
||||
*
|
||||
* R0=xxxx R1=xxxx R2=xxxx R3=xxxx R4=xxxx R5=xxxx
|
||||
* SP=xxxx PC=xxxx PS=xxxx T0 N0 Z0 V0 C0
|
||||
* MMR0=xxxxxx MMR1=xxxxxx MMR2=xxxxxx MMR3=xxxxxx ER=xxxxxx
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @return {string}
|
||||
*/
|
||||
DebuggerPDP11.prototype.getRegDump = function()
|
||||
DebuggerPDP11.prototype.getMiscDump = function()
|
||||
{
|
||||
var sDump = "";
|
||||
sDump += this.getRegOutput(DebuggerPDP11.REG_MMR0) + this.getRegOutput(DebuggerPDP11.REG_MMR1);
|
||||
sDump += this.getRegOutput(DebuggerPDP11.REG_MMR2) + this.getRegOutput(DebuggerPDP11.REG_MMR3) + this.getRegOutput(DebuggerPDP11.REG_ERR);
|
||||
sDump += '\n';
|
||||
sDump += this.getRegOutput(DebuggerPDP11.REG_SR) + this.getRegOutput(DebuggerPDP11.REG_AR) + this.getRegOutput(DebuggerPDP11.REG_DR);
|
||||
return sDump;
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegDump(fMisc)
|
||||
*
|
||||
* Sample register dump:
|
||||
*
|
||||
* R0=xxxxxx R1=xxxxxx R2=xxxxxx R3=xxxxxx R4=xxxxxx R5=xxxxxx
|
||||
* SP=xxxxxx PC=xxxxxx PS=xxxxxx IR=xxxxxx SL=xxxxxx T0 N0 Z0 V0 C0
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {boolean} [fMisc] (true to include misc registers)
|
||||
* @return {string}
|
||||
*/
|
||||
DebuggerPDP11.prototype.getRegDump = function(fMisc)
|
||||
{
|
||||
var i;
|
||||
var sDump = "";
|
||||
|
|
@ -2419,8 +2533,9 @@ if (DEBUGGER) {
|
|||
}
|
||||
sDump += '\n';
|
||||
sDump += this.getRegOutput(PDP11.REG.SP) + this.getRegOutput(PDP11.REG.PC);
|
||||
sDump += this.getRegOutput(DebuggerPDP11.REG_PSW) + this.getRegOutput(DebuggerPDP11.REG_SR);
|
||||
sDump += this.getRegOutput(DebuggerPDP11.REG_PSW) + this.getRegOutput(DebuggerPDP11.REG_PIR) + this.getRegOutput(DebuggerPDP11.REG_SL);
|
||||
sDump += this.getFlagOutput('T') + this.getFlagOutput('N') + this.getFlagOutput('Z') + this.getFlagOutput('V') + this.getFlagOutput('C');
|
||||
if (fMisc) sDump += '\n' + this.getMiscDump();
|
||||
return sDump;
|
||||
};
|
||||
|
||||
|
|
@ -2773,12 +2888,12 @@ if (DEBUGGER) {
|
|||
{
|
||||
if (sAddr == '?') {
|
||||
this.println("breakpoint commands:");
|
||||
this.println("\tbp [a]\tset exec breakpoint at addr [a]");
|
||||
this.println("\tbr [a]\tset read breakpoint at addr [a]");
|
||||
this.println("\tbw [a]\tset write breakpoint at addr [a]");
|
||||
this.println("\tbc [a]\tclear breakpoint at addr [a]");
|
||||
this.println("\tbp [#]\tset exec breakpoint at addr #");
|
||||
this.println("\tbr [#]\tset read breakpoint at addr #");
|
||||
this.println("\tbw [#]\tset write breakpoint at addr #");
|
||||
this.println("\tbc [#]\tclear breakpoint at addr #");
|
||||
this.println("\tbl\tlist all breakpoints");
|
||||
this.println("\tbn [n]\tbreak after [n] instruction(s)");
|
||||
this.println("\tbn [#]\tbreak after # instruction(s)");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3386,77 +3501,115 @@ if (DEBUGGER) {
|
|||
if (asArgs && asArgs[1] == '?') {
|
||||
this.println("register commands:");
|
||||
this.println("\tr\tdump registers");
|
||||
this.println("\trm\tdump misc registers");
|
||||
this.println("\trx [#]\tset flag or register x to [#]");
|
||||
return;
|
||||
}
|
||||
|
||||
var fMisc = false;
|
||||
var cpu = this.cpu;
|
||||
if (fInstruction == null) fInstruction = true;
|
||||
|
||||
if (asArgs != null && asArgs.length > 1) {
|
||||
var sReg = asArgs[1];
|
||||
var sValue = null;
|
||||
var i = sReg.indexOf('=');
|
||||
if (i > 0) {
|
||||
sValue = sReg.substr(i + 1);
|
||||
sReg = sReg.substr(0, i);
|
||||
}
|
||||
else if (asArgs.length > 2) {
|
||||
sValue = asArgs[2];
|
||||
|
||||
if (sReg == 'm') {
|
||||
fMisc = true;
|
||||
}
|
||||
else {
|
||||
this.println("missing value for " + asArgs[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
var w = this.parseExpression(sValue);
|
||||
if (w === undefined) return;
|
||||
|
||||
var sRegMatch = sReg.toUpperCase();
|
||||
switch (sRegMatch) {
|
||||
case "SP":
|
||||
case "R6":
|
||||
cpu.setSP(w);
|
||||
break;
|
||||
case "PC":
|
||||
case "R7":
|
||||
cpu.setPC(w);
|
||||
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
|
||||
break;
|
||||
case "N":
|
||||
if (w) cpu.setNF(); else cpu.clearNF();
|
||||
break;
|
||||
case "Z":
|
||||
if (w) cpu.setZF(); else cpu.clearZF();
|
||||
break;
|
||||
case "V":
|
||||
if (w) cpu.setVF(); else cpu.clearVF();
|
||||
break;
|
||||
case "C":
|
||||
if (w) cpu.setCF(); else cpu.clearCF();
|
||||
break;
|
||||
case "SR":
|
||||
if (this.panel && this.panel.hasSwitches()) {
|
||||
this.panel.setSR(w);
|
||||
break;
|
||||
var sValue = null;
|
||||
var i = sReg.indexOf('=');
|
||||
if (i > 0) {
|
||||
sValue = sReg.substr(i + 1);
|
||||
sReg = sReg.substr(0, i);
|
||||
}
|
||||
/* falls through */
|
||||
default:
|
||||
if (sRegMatch.charAt(0) == 'R') {
|
||||
var iReg = +sRegMatch.charAt(1);
|
||||
if (iReg >= 0 && iReg < 6) {
|
||||
cpu.regsGen[iReg] = w & 0xffff;
|
||||
else if (asArgs.length > 2) {
|
||||
sValue = asArgs[2];
|
||||
}
|
||||
else {
|
||||
this.println("missing value for " + asArgs[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
var w = this.parseExpression(sValue);
|
||||
if (w === undefined) return;
|
||||
|
||||
var sRegMatch = sReg.toUpperCase();
|
||||
switch (sRegMatch) {
|
||||
case "SP":
|
||||
case "R6":
|
||||
cpu.setSP(w);
|
||||
break;
|
||||
case "PC":
|
||||
case "R7":
|
||||
cpu.setPC(w);
|
||||
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
|
||||
break;
|
||||
case "N":
|
||||
if (w) cpu.setNF(); else cpu.clearNF();
|
||||
break;
|
||||
case "Z":
|
||||
if (w) cpu.setZF(); else cpu.clearZF();
|
||||
break;
|
||||
case "V":
|
||||
if (w) cpu.setVF(); else cpu.clearVF();
|
||||
break;
|
||||
case "C":
|
||||
if (w) cpu.setCF(); else cpu.clearCF();
|
||||
break;
|
||||
case "PS":
|
||||
cpu.setPSW(w);
|
||||
break;
|
||||
case "IR":
|
||||
cpu.setPIR(w);
|
||||
break;
|
||||
case "ER":
|
||||
cpu.regErr = w;
|
||||
fMisc = true;
|
||||
break;
|
||||
case "SL":
|
||||
cpu.setSL(w);
|
||||
break;
|
||||
case "MMR0":
|
||||
cpu.setMMR0(w);
|
||||
fMisc = true;
|
||||
break;
|
||||
case "MMR3":
|
||||
cpu.setMMR3(w);
|
||||
fMisc = true;
|
||||
break;
|
||||
case "AR":
|
||||
if (this.panel) this.panel.setAR(w);
|
||||
fMisc = true;
|
||||
break;
|
||||
case "DR":
|
||||
if (this.panel) this.panel.setDR(w);
|
||||
fMisc = true;
|
||||
break;
|
||||
case "SR":
|
||||
if (this.panel && this.panel.hasSwitches()) {
|
||||
this.panel.setSR(w);
|
||||
fMisc = true;
|
||||
break;
|
||||
}
|
||||
/* falls through */
|
||||
default:
|
||||
if (sRegMatch.charAt(0) == 'R') {
|
||||
var iReg = +sRegMatch.charAt(1);
|
||||
if (iReg >= 0 && iReg < 6) {
|
||||
cpu.regsGen[iReg] = w & 0xffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.println("unknown register: " + sReg);
|
||||
return;
|
||||
}
|
||||
this.println("unknown register: " + sReg);
|
||||
return;
|
||||
this.cmp.updateDisplays();
|
||||
this.println("updated registers:");
|
||||
}
|
||||
this.cmp.updateDisplays();
|
||||
this.println("updated registers:");
|
||||
}
|
||||
|
||||
this.println(this.getRegDump());
|
||||
this.println(this.getRegDump(fMisc));
|
||||
|
||||
if (fInstruction) {
|
||||
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
|
||||
|
|
@ -3512,36 +3665,47 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* doStep(sCmd)
|
||||
* doStep(sCmd, sOption)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {string} [sCmd] "p" or "pr"
|
||||
* @param {string} [sOption]
|
||||
*/
|
||||
DebuggerPDP11.prototype.doStep = function(sCmd)
|
||||
DebuggerPDP11.prototype.doStep = function(sCmd, sOption)
|
||||
{
|
||||
if (sOption == '?') {
|
||||
this.println("step commands:");
|
||||
this.println("\tp\tstep over instruction");
|
||||
this.println("\tpr\tstep over instruction with register update");
|
||||
return;
|
||||
}
|
||||
|
||||
var fCallStep = true;
|
||||
var fRegs = (sCmd == "pr"? 1 : 0);
|
||||
var nRegs = (sCmd == "pr"? 1 : 0);
|
||||
/*
|
||||
* Set up the value for this.nStep (ie, 1 or 2) depending on whether the user wants
|
||||
* a subsequent register dump ("pr") or not ("p").
|
||||
*/
|
||||
var nStep = 1 + fRegs;
|
||||
var nStep = 1 + nRegs;
|
||||
|
||||
if (!this.nStep) {
|
||||
var dbgAddr = this.newAddr(this.cpu.getPC());
|
||||
var bOpcode = this.getByte(dbgAddr);
|
||||
var opCode = this.getWord(dbgAddr);
|
||||
|
||||
/*
|
||||
switch (bOpcode) {
|
||||
case PDP11.OPCODE.CALL:
|
||||
if (opCode == PDP11.OPCODE.BPT || opCode == PDP11.OPCODE.IOT ||
|
||||
(opCode & PDP11.OPCODE.EMT_MASK) == PDP11.OPCODE.EMT_OP ||
|
||||
(opCode & PDP11.OPCODE.TRAP_MASK) == PDP11.OPCODE.TRAP_OP) {
|
||||
if (fCallStep) {
|
||||
this.nStep = nStep;
|
||||
this.incAddr(dbgAddr, 2);
|
||||
}
|
||||
} else if ((opCode & PDP11.OPCODE.JSR_MASK) == PDP11.OPCODE.JSR_OP) {
|
||||
var s = this.getInstruction(dbgAddr);
|
||||
this.assert(s.indexOf("JSR") >= 0);
|
||||
if (fCallStep) {
|
||||
this.nStep = nStep;
|
||||
this.incAddr(dbgAddr, 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
if (this.nStep) {
|
||||
this.setTempBreakpoint(dbgAddr);
|
||||
|
|
@ -3555,7 +3719,7 @@ if (DEBUGGER) {
|
|||
* stopped for reasons unrelated to the temporary breakpoint, but that's OK.
|
||||
*/
|
||||
} else {
|
||||
this.doTrace(fRegs? "tr" : "t");
|
||||
this.doTrace(nRegs? "tr" : "t");
|
||||
}
|
||||
} else {
|
||||
this.println("step in progress");
|
||||
|
|
@ -3681,6 +3845,15 @@ if (DEBUGGER) {
|
|||
*/
|
||||
DebuggerPDP11.prototype.doTrace = function(sCmd, sCount)
|
||||
{
|
||||
if (sCount == '?') {
|
||||
this.println("trace commands:");
|
||||
this.println("\tt [#]\ttrace # instructions");
|
||||
this.println("\ttr [#]\ttrace # instructions with register updates");
|
||||
this.println("\ttc [#]\ttrace # cycles");
|
||||
this.println("note: bn [#] breaks after # instructions without updates");
|
||||
return;
|
||||
}
|
||||
|
||||
var dbg = this;
|
||||
var fRegs = (sCmd != "t");
|
||||
var nCount = this.parseValue(sCount, null, true) || 1;
|
||||
|
|
@ -3689,6 +3862,7 @@ if (DEBUGGER) {
|
|||
nCycles = nCount;
|
||||
nCount = 1;
|
||||
}
|
||||
this.sTraceCmdPrev = sCmd;
|
||||
web.onCountRepeat(
|
||||
nCount,
|
||||
function onCountStep() {
|
||||
|
|
@ -3702,7 +3876,7 @@ if (DEBUGGER) {
|
|||
* a final updateDisplays().
|
||||
*/
|
||||
if (dbg.panel && dbg.panel.stop) dbg.panel.stop();
|
||||
dbg.cmp.updateDisplays();
|
||||
dbg.cmp.updateDisplays(-1);
|
||||
dbg.setBusy(false);
|
||||
}
|
||||
);
|
||||
|
|
@ -3908,7 +4082,7 @@ if (DEBUGGER) {
|
|||
this.doPrint(sCmd.substr(5));
|
||||
break;
|
||||
}
|
||||
this.doStep(asArgs[0]);
|
||||
this.doStep(asArgs[0], asArgs[1]);
|
||||
break;
|
||||
case 'r':
|
||||
if (sCmd == "reset") {
|
||||
|
|
|
|||
|
|
@ -234,6 +234,14 @@ var PDP11 = {
|
|||
OPCODE: {
|
||||
HALT: 0x0000,
|
||||
WAIT: 0x0001,
|
||||
BPT: 0x0003,
|
||||
IOT: 0x0004,
|
||||
JSR_OP: 0x0800,
|
||||
JSR_MASK: 0xFE00,
|
||||
EMT_OP: 0x8800,
|
||||
EMT_MASK: 0xFF00,
|
||||
TRAP_OP: 0x8900,
|
||||
TRAP_MASK: 0xFF00,
|
||||
INVALID: 0xFFFF // far from the only invalid opcode, just a KNOWN invalid opcode
|
||||
},
|
||||
/*
|
||||
|
|
@ -311,44 +319,65 @@ var PDP11 = {
|
|||
BYTE: 0xff, // write byte normally
|
||||
SBYTE: 0xffff // sign-extend byte to word
|
||||
},
|
||||
CPUERR: {
|
||||
RED: 0x0004, // red zone stack limit
|
||||
YELLOW: 0x0008, // yellow zone stack limit
|
||||
TIMEOUT: 0x0010, // UNIBUS timeout error
|
||||
NOMEMORY: 0x0020, // non-existent memory error
|
||||
ODDADDR: 0x0040, // odd word address error (as in non-even, not strange)
|
||||
BADHALT: 0x0080 // HALT attempted in USER or SUPER modes
|
||||
CPUERR: { // 177766
|
||||
RED: 0x0004, // 000004 red zone stack limit
|
||||
YELLOW: 0x0008, // 000010 yellow zone stack limit
|
||||
TIMEOUT: 0x0010, // 000020 UNIBUS timeout error
|
||||
NOMEMORY: 0x0020, // 000040 non-existent memory error
|
||||
ODDADDR: 0x0040, // 000100 odd word address error (as in non-even, not strange)
|
||||
BADHALT: 0x0080 // 000200 HALT attempted in USER or SUPER modes
|
||||
},
|
||||
MMR0: {
|
||||
ENABLED: 0x0001, // address relocation enabled
|
||||
PAGE_NUM: 0x000E, // page number of last fault
|
||||
PAGE_D: 0x0010, // last fault occurred in D space
|
||||
PAGE_MODE: 0x0060, // processor mode as of last fault
|
||||
COMPLETED: 0x0080, // last instruction completed
|
||||
DSTMODE: 0x0100, // only destination mode references will be relocated (aka MAINT bit)
|
||||
MMU_TRAPS: 0x0200, // enable MMU traps
|
||||
UNUSED: 0x0C00,
|
||||
TRAP_MMU: 0x1000, // trap: MMU
|
||||
ABORT_RO: 0x2000, // abort: read-only
|
||||
ABORT_PL: 0x4000, // abort: page length
|
||||
ABORT_NR: 0x8000, // abort: non-resident
|
||||
ABORT: 0xE000
|
||||
MMR0: { // 177572
|
||||
ENABLED: 0x0001, // 000001 address relocation enabled
|
||||
PAGE_NUM: 0x000E, // 000016 page number of last fault
|
||||
PAGE_D: 0x0010, // 000020 last fault occurred in D space (11/44 and 11/70 only)
|
||||
PAGE: 0x001E, // 000176 (all of the PAGE bits)
|
||||
MODE: 0x0060, // 000140 processor mode as of last fault
|
||||
COMPLETED: 0x0080, // 000200 last instruction completed (R/O) (11/70 only)
|
||||
DSTMODE: 0x0100, // 000400 only destination mode references will be relocated (aka MAINT bit)
|
||||
MMU_TRAPS: 0x0200, // 001000 enable MMU traps (11/70 only)
|
||||
UNUSED: 0x0C00, // 006000
|
||||
TRAP_MMU: 0x1000, // 010000 trap: MMU (11/70 only)
|
||||
ABORT_RO: 0x2000, // 020000 abort: read-only
|
||||
ABORT_PL: 0x4000, // 040000 abort: page length
|
||||
ABORT_NR: 0x8000, // 100000 abort: non-resident
|
||||
ABORT: 0xE000, // 160000 (all of the ABORT bits)
|
||||
UPDATE: 0xF0FE // Includes all of: ABORT, TRAP, COMPLETED, MODE, and PAGE bits
|
||||
},
|
||||
MMR1: { // general purpose auto-inc/auto-dec register
|
||||
REG1_NUM: 0x0007,
|
||||
REG1_DELTA: 0x00F8,
|
||||
REG2_NUM: 0x0700,
|
||||
REG2_DELTA: 0xF800
|
||||
MMR1: { // 177574: general purpose auto-inc/auto-dec register (11/44 and 11/70 only)
|
||||
REG1_NUM: 0x0007, //
|
||||
REG1_DELTA: 0x00F8, //
|
||||
REG2_NUM: 0x0700, //
|
||||
REG2_DELTA: 0xF800 //
|
||||
},
|
||||
MMR2: { // virtual program counter register
|
||||
MMR2: { // 177576: virtual program counter register
|
||||
},
|
||||
MMR3: { // mapping register
|
||||
MMR3: { // 172516: mapping register (11/44 and 11/70 only)
|
||||
USER_D: 0x0001,
|
||||
SUPER_D: 0x0002,
|
||||
KERNEL_D: 0x0004,
|
||||
MMU_22BIT: 0x0010,
|
||||
UNIBUS_MAP: 0x0020 // UNIBUS map relocation enabled
|
||||
},
|
||||
PDR: {
|
||||
ACF: {
|
||||
NR: 0x0, // non-resident, abort all accesses
|
||||
RO1: 0x1, // read-only, abort on write attempt, memory management trap on read (11/70 only)
|
||||
RO: 0x2, // read-only, abort on write attempt
|
||||
U1: 0x3, // unused, abort all accesses--reserved for future use
|
||||
RW1: 0x4, // read/write, memory management trap upon completion of a read or write
|
||||
RW2: 0x5, // read/write, memory management trap upon completion of a write (11/70 only)
|
||||
RW: 0x6, // read/write, no system trap/abort action
|
||||
U2: 0x7, // unused, abort all accesses--reserved for future use
|
||||
MASK: 0x7
|
||||
},
|
||||
ED: 0x0008, // expansion direction (if set, the page expands downward from block number 127)
|
||||
UNUSED: 0x0030,
|
||||
MODIFIED: 0x0040, // page has been written (bit cleared when either PDR or PAR is written)
|
||||
ACCESSED: 0x0080, // page has been accessed (bit cleared when either PDR or PAR is written) (11/70 only)
|
||||
PLF: 0x7F00, // page length field
|
||||
BC: 0x8000 // bypass cache (11/44 only)
|
||||
},
|
||||
/*
|
||||
* Assorted special (UNIBUS) addresses
|
||||
*
|
||||
|
|
@ -364,11 +393,11 @@ var PDP11 = {
|
|||
* is enabled, the top 5 bits of an address select one of the 31 mapping registers, and the bottom 13 bits
|
||||
* are then added to the contents of the selected mapping register.
|
||||
*
|
||||
* ES6 ALERT: By using octal constants, this is the first place I'm dipping my toe into ECMAScript 6 waters.
|
||||
* If you're loading this raw source code into your browser, then by now (2016) you're almost certainly using
|
||||
* an ES6-aware browser. Everyone else should be using code compiled by Google's Closure Compiler, which
|
||||
* we configure to produce code that's backward-compatible with ES5 (for example, octal constants are
|
||||
* converted to decimal values).
|
||||
* ES6 ALERT: By using octal constants, I'm finally dipping my toe into ES6 (aka ECMAScript 2015) waters.
|
||||
* You'll even see a few binary constants below, too. If you're loading this raw source code into your browser,
|
||||
* then by now (2016) you're almost certainly using an ES6-aware browser. Production sites should be using code
|
||||
* compiled by Google's Closure Compiler, which we configure to produce code that's backward-compatible with ES5
|
||||
* (for example, all binary, octal, and hex constants are converted to decimal values).
|
||||
*
|
||||
* For more details: https://github.com/google/closure-compiler/wiki/ECMAScript6
|
||||
*/
|
||||
|
|
@ -498,22 +527,22 @@ var PDP11 = {
|
|||
UDPAR6: 0o177674, // User D Page Address Register 6
|
||||
UDPAR7: 0o177676, // User D Page Address Register 7
|
||||
|
||||
R0SET0: 0o177700,
|
||||
R1SET0: 0o177701,
|
||||
R2SET0: 0o177702,
|
||||
R3SET0: 0o177703,
|
||||
R4SET0: 0o177704,
|
||||
R5SET0: 0o177705,
|
||||
R6KERNEL: 0o177706,
|
||||
R7KERNEL: 0o177707,
|
||||
R0SET1: 0o177710,
|
||||
R1SET1: 0o177711,
|
||||
R2SET1: 0o177712,
|
||||
R3SET1: 0o177713,
|
||||
R4SET1: 0o177714,
|
||||
R5SET1: 0o177715,
|
||||
R6SUPER: 0o177716,
|
||||
R6USER: 0o177717,
|
||||
R0SET0: 0o177700, //
|
||||
R1SET0: 0o177701, //
|
||||
R2SET0: 0o177702, //
|
||||
R3SET0: 0o177703, //
|
||||
R4SET0: 0o177704, //
|
||||
R5SET0: 0o177705, //
|
||||
R6KERNEL: 0o177706, //
|
||||
R7KERNEL: 0o177707, //
|
||||
R0SET1: 0o177710, //
|
||||
R1SET1: 0o177711, //
|
||||
R2SET1: 0o177712, //
|
||||
R3SET1: 0o177713, //
|
||||
R4SET1: 0o177714, //
|
||||
R5SET1: 0o177715, //
|
||||
R6SUPER: 0o177716, //
|
||||
R6USER: 0o177717, //
|
||||
|
||||
/*
|
||||
* This next group of registers is largely ignored; all accesses are routed to regsControl[],
|
||||
|
|
|
|||
|
|
@ -130,9 +130,62 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
bus.addIOTable(this, DevicePDP11.UNIBUS_IOTABLE);
|
||||
bus.addResetHandler(this.reset.bind(this));
|
||||
|
||||
if (DEBUGGER && dbg) {
|
||||
dbg.messageDump(MessagesPDP11.MMU, function onDumpMMU(asArgs) {
|
||||
device.dumpMMU(asArgs);
|
||||
});
|
||||
}
|
||||
this.setReady();
|
||||
};
|
||||
|
||||
/**
|
||||
* dumpMMU(asArgs)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {Array.<string>} asArgs
|
||||
*/
|
||||
DevicePDP11.prototype.dumpMMU = function(asArgs)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var cpu = this.cpu;
|
||||
this.dumpRegs("KIPDR", cpu.mmuPDR[0], 0, asArgs[0]);
|
||||
this.dumpRegs("KDPDR", cpu.mmuPDR[0], 8, asArgs[0]);
|
||||
this.dumpRegs("KIPAR", cpu.mmuPAR[0], 0, asArgs[0]);
|
||||
this.dumpRegs("KDPAR", cpu.mmuPAR[0], 8, asArgs[0], true);
|
||||
this.dumpRegs("SIPDR", cpu.mmuPDR[1], 0, asArgs[0]);
|
||||
this.dumpRegs("SDPDR", cpu.mmuPDR[1], 8, asArgs[0]);
|
||||
this.dumpRegs("SIPAR", cpu.mmuPAR[1], 0, asArgs[0]);
|
||||
this.dumpRegs("SDPAR", cpu.mmuPAR[1], 8, asArgs[0], true);
|
||||
this.dumpRegs("UIPDR", cpu.mmuPDR[3], 0, asArgs[0]);
|
||||
this.dumpRegs("UDPDR", cpu.mmuPDR[3], 8, asArgs[0]);
|
||||
this.dumpRegs("UIPAR", cpu.mmuPAR[3], 0, asArgs[0]);
|
||||
this.dumpRegs("UDPAR", cpu.mmuPAR[3], 8, asArgs[0], true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* dumpRegs(sName, aRegs, offset, sFilter, fBreak)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {string} sName
|
||||
* @param {Array.<number>} aRegs
|
||||
* @param {number} offset
|
||||
* @param {string} sFilter
|
||||
* @param {boolean} [fBreak]
|
||||
*/
|
||||
DevicePDP11.prototype.dumpRegs = function(sName, aRegs, offset, sFilter, fBreak)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var dbg = this.dbg;
|
||||
if (sFilter && sName.indexOf(sFilter.toUpperCase()) < 0) return;
|
||||
var sDump = sName + ":";
|
||||
for (var i = 0; i < 8; i++) {
|
||||
sDump += ' ' + dbg.toStrBase(aRegs[offset + i]);
|
||||
}
|
||||
dbg.println(sDump + (fBreak? '\n' : ''));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
|
|
@ -219,7 +272,7 @@ DevicePDP11.prototype.readMMR0 = function(addr)
|
|||
*/
|
||||
DevicePDP11.prototype.writeMMR0 = function(data, addr)
|
||||
{
|
||||
this.cpu.setMMR0(data);
|
||||
this.cpu.setMMR0((data & ~PDP11.MMR0.COMPLETED) | (this.cpu.regMMR0 & PDP11.MMR0.COMPLETED));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -435,7 +488,10 @@ DevicePDP11.prototype.readKIPDR = function(addr)
|
|||
DevicePDP11.prototype.writeKIPDR = function(data, addr)
|
||||
{
|
||||
var reg = (addr >> 1) & 7;
|
||||
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
|
||||
this.cpu.mmuPDR[0][reg] = data &= 0xff0f;
|
||||
if (this.messageEnabled(MessagesPDP11.MMU)) {
|
||||
this.printMessage("writeKIPDR[" + reg + "]: " + str.toOct(data), true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -873,7 +929,8 @@ DevicePDP11.prototype.readSIZE = function(addr)
|
|||
/*
|
||||
* TODO: getMemorySize() returns an aggregate total, so if there are multiple discontiguous
|
||||
* chunks of RAM, this could return the wrong result; another interface, getHighestAddress(),
|
||||
* might be required.
|
||||
* might be required. Then again, perhaps multiple discontiguous chunks of RAM are not permitted
|
||||
* in PDP-11 machines.
|
||||
*/
|
||||
return addr == PDP11.UNIBUS.LSIZE? ((this.bus.getMemorySize(MemoryPDP11.TYPE.RAM) >> 6) - 1) : 0;
|
||||
};
|
||||
|
|
@ -961,20 +1018,23 @@ DevicePDP11.prototype.readMB = function(addr)
|
|||
*/
|
||||
DevicePDP11.prototype.writeMB = function(data, addr)
|
||||
{
|
||||
if (!(addr & 0x1)) data &= 0xff; // Required for KB11-CM without MFPT instruction
|
||||
if (!(addr & 0x1)) {
|
||||
data &= 0xff; // required for KB11-CM without MFPT instruction
|
||||
}
|
||||
this.cpu.regMB = data;
|
||||
};
|
||||
|
||||
/**
|
||||
* readPIR(addr)
|
||||
* readPIR(addr, fPreWrite)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.PIR or 177772)
|
||||
* @param {boolean} [fPreWrite]
|
||||
* @return {number}
|
||||
*/
|
||||
DevicePDP11.prototype.readPIR = function(addr)
|
||||
DevicePDP11.prototype.readPIR = function(addr, fPreWrite)
|
||||
{
|
||||
if (!addr) return 0; // the caller is preparing to write the low or high byte; these are the bits to return for the other byte
|
||||
if (fPreWrite) return 0;
|
||||
return this.cpu.getPIR();
|
||||
};
|
||||
|
||||
|
|
@ -991,15 +1051,16 @@ DevicePDP11.prototype.writePIR = function(data, addr)
|
|||
};
|
||||
|
||||
/**
|
||||
* readSL(addr)
|
||||
* readSL(addr, fPreWrite)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.SL or 177774)
|
||||
* @param {boolean} [fPreWrite]
|
||||
* @return {number}
|
||||
*/
|
||||
DevicePDP11.prototype.readSL = function(addr)
|
||||
DevicePDP11.prototype.readSL = function(addr, fPreWrite)
|
||||
{
|
||||
if (!addr) return 0; // the caller is preparing to write the low or high byte; these are the bits to return for the other byte
|
||||
if (fPreWrite) return 0;
|
||||
return this.cpu.getSL();
|
||||
};
|
||||
|
||||
|
|
@ -1069,23 +1130,23 @@ DevicePDP11.prototype.writeIgnored = function(data, addr)
|
|||
*/
|
||||
DevicePDP11.UNIBUS_IOTABLE = {
|
||||
[PDP11.UNIBUS.UNIMAP]: /* 170200 */ [null, null, DevicePDP11.prototype.readUNIMAP, DevicePDP11.prototype.writeUNIMAP, "UNIMAP", 64, PDP11.MODEL_1170],
|
||||
[PDP11.UNIBUS.SIPDR0]: /* 172200 */ [null, null, DevicePDP11.prototype.readSIPDR, DevicePDP11.prototype.writeSIPDR, "SIPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.SDPDR0]: /* 172220 */ [null, null, DevicePDP11.prototype.readSDPDR, DevicePDP11.prototype.writeSDPDR, "SDPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.SIPAR0]: /* 172240 */ [null, null, DevicePDP11.prototype.readSIPAR, DevicePDP11.prototype.writeSIPAR, "SIPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.SDPAR0]: /* 172260 */ [null, null, DevicePDP11.prototype.readSDPAR, DevicePDP11.prototype.writeSDPAR, "SDPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.KIPDR0]: /* 172300 */ [null, null, DevicePDP11.prototype.readKIPDR, DevicePDP11.prototype.writeKIPDR, "KIPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.KDPDR0]: /* 172320 */ [null, null, DevicePDP11.prototype.readKDPDR, DevicePDP11.prototype.writeKDPDR, "KDPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.KIPAR0]: /* 172340 */ [null, null, DevicePDP11.prototype.readKIPAR, DevicePDP11.prototype.writeKIPAR, "KIPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.KDPAR0]: /* 172360 */ [null, null, DevicePDP11.prototype.readKDPAR, DevicePDP11.prototype.writeKDPAR, "KDPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.MMR3]: /* 172516 */ [null, null, DevicePDP11.prototype.readMMR3, DevicePDP11.prototype.writeMMR3, "MMR3", 1, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.SIPDR0]: /* 172200 */ [null, null, DevicePDP11.prototype.readSIPDR, DevicePDP11.prototype.writeSIPDR, "SIPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.SDPDR0]: /* 172220 */ [null, null, DevicePDP11.prototype.readSDPDR, DevicePDP11.prototype.writeSDPDR, "SDPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.SIPAR0]: /* 172240 */ [null, null, DevicePDP11.prototype.readSIPAR, DevicePDP11.prototype.writeSIPAR, "SIPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.SDPAR0]: /* 172260 */ [null, null, DevicePDP11.prototype.readSDPAR, DevicePDP11.prototype.writeSDPAR, "SDPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.KIPDR0]: /* 172300 */ [null, null, DevicePDP11.prototype.readKIPDR, DevicePDP11.prototype.writeKIPDR, "KIPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.KDPDR0]: /* 172320 */ [null, null, DevicePDP11.prototype.readKDPDR, DevicePDP11.prototype.writeKDPDR, "KDPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.KIPAR0]: /* 172340 */ [null, null, DevicePDP11.prototype.readKIPAR, DevicePDP11.prototype.writeKIPAR, "KIPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.KDPAR0]: /* 172360 */ [null, null, DevicePDP11.prototype.readKDPAR, DevicePDP11.prototype.writeKDPAR, "KDPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.MMR3]: /* 172516 */ [null, null, DevicePDP11.prototype.readMMR3, DevicePDP11.prototype.writeMMR3, "MMR3", 1, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.LKS]: /* 177546 */ [null, null, DevicePDP11.prototype.readLKS, DevicePDP11.prototype.writeLKS, "LKS"],
|
||||
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0", 1, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.MMR1]: /* 177574 */ [null, null, DevicePDP11.prototype.readMMR1, DevicePDP11.prototype.writeIgnored, "MMR1", 1, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.MMR2]: /* 177576 */ [null, null, DevicePDP11.prototype.readMMR2, DevicePDP11.prototype.writeIgnored, "MMR2", 1, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.UIPDR0]: /* 177600 */ [null, null, DevicePDP11.prototype.readUIPDR, DevicePDP11.prototype.writeUIPDR, "UIPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.UDPDR0]: /* 177620 */ [null, null, DevicePDP11.prototype.readUDPDR, DevicePDP11.prototype.writeUDPDR, "UDPDR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.UIPAR0]: /* 177640 */ [null, null, DevicePDP11.prototype.readUIPAR, DevicePDP11.prototype.writeUIPAR, "UIPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.UDPAR0]: /* 177660 */ [null, null, DevicePDP11.prototype.readUDPAR, DevicePDP11.prototype.writeUDPAR, "UDPAR", 8, PDP11.MODEL_1145],
|
||||
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0", 1, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.MMR1]: /* 177574 */ [null, null, DevicePDP11.prototype.readMMR1, DevicePDP11.prototype.writeIgnored, "MMR1", 1, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.MMR2]: /* 177576 */ [null, null, DevicePDP11.prototype.readMMR2, DevicePDP11.prototype.writeIgnored, "MMR2", 1, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.UIPDR0]: /* 177600 */ [null, null, DevicePDP11.prototype.readUIPDR, DevicePDP11.prototype.writeUIPDR, "UIPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.UDPDR0]: /* 177620 */ [null, null, DevicePDP11.prototype.readUDPDR, DevicePDP11.prototype.writeUDPDR, "UDPDR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.UIPAR0]: /* 177640 */ [null, null, DevicePDP11.prototype.readUIPAR, DevicePDP11.prototype.writeUIPAR, "UIPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.UDPAR0]: /* 177660 */ [null, null, DevicePDP11.prototype.readUDPAR, DevicePDP11.prototype.writeUDPAR, "UDPAR", 8, PDP11.MODEL_1145, MessagesPDP11.MMU],
|
||||
[PDP11.UNIBUS.R0SET0]: /* 177700 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R0SET0"],
|
||||
[PDP11.UNIBUS.R1SET0]: /* 177701 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R1SET0"],
|
||||
[PDP11.UNIBUS.R2SET0]: /* 177702 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R2SET0"],
|
||||
|
|
|
|||
|
|
@ -38,8 +38,10 @@ var MessagesPDP11 = {
|
|||
FAULT: 0x00000020,
|
||||
BUS: 0x00000040,
|
||||
MEMORY: 0x00000080,
|
||||
ROM: 0x00000100,
|
||||
DEVICE: 0x00000200,
|
||||
MMU: 0x00000100,
|
||||
ROM: 0x00000200,
|
||||
DEVICE: 0x00000400,
|
||||
PANEL: 0x00000800,
|
||||
KEYBOARD: 0x00010000,
|
||||
KEYS: 0x00020000,
|
||||
PAPER: 0x00100000,
|
||||
|
|
@ -73,8 +75,10 @@ MessagesPDP11.CATEGORIES = {
|
|||
"fault": MessagesPDP11.FAULT,
|
||||
"bus": MessagesPDP11.BUS,
|
||||
"memory": MessagesPDP11.MEMORY,
|
||||
"mmu": MessagesPDP11.MMU,
|
||||
"rom": MessagesPDP11.ROM,
|
||||
"device": MessagesPDP11.DEVICE,
|
||||
"panel": MessagesPDP11.PANEL,
|
||||
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
|
||||
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"paper": MessagesPDP11.PAPER,
|
||||
|
|
|
|||
|
|
@ -33,13 +33,14 @@
|
|||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP11 = require("./defines");
|
||||
var BusPDP11 = require("./bus");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP11 = require("./defines");
|
||||
var BusPDP11 = require("./bus");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -53,15 +54,15 @@ if (NODE) {
|
|||
*/
|
||||
function PanelPDP11(parmsPanel)
|
||||
{
|
||||
Component.call(this, "Panel", parmsPanel, PanelPDP11);
|
||||
Component.call(this, "Panel", parmsPanel, PanelPDP11, MessagesPDP11.PANEL);
|
||||
|
||||
/*
|
||||
* If there are any live registers, LEDs, etc, to display, this will provide a count.
|
||||
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both).
|
||||
*/
|
||||
this.cLiveRegs = 0;
|
||||
this.nPeriodicCount = 0;
|
||||
this.nPeriodicLimit = 60;
|
||||
this.nDisplayCount = 0;
|
||||
this.nDisplayLimit = 60;
|
||||
this.fDisplayLiveRegs = true;
|
||||
|
||||
/*
|
||||
|
|
@ -175,6 +176,50 @@ PanelPDP11.LED = {
|
|||
B22: 'B22'
|
||||
};
|
||||
|
||||
/**
|
||||
* getAR()
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @return {number} (current ADDRESS register)
|
||||
*/
|
||||
PanelPDP11.prototype.getAR = function()
|
||||
{
|
||||
return this.regAddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* setAR(value)
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {number} value (new ADDRESS register)
|
||||
*/
|
||||
PanelPDP11.prototype.setAR = function(value)
|
||||
{
|
||||
this.updateAddr(this.regAddr = value);
|
||||
};
|
||||
|
||||
/**
|
||||
* getDR()
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @return {number} (current DISPLAY register)
|
||||
*/
|
||||
PanelPDP11.prototype.getDR = function()
|
||||
{
|
||||
return this.regDisplay;
|
||||
};
|
||||
|
||||
/**
|
||||
* setDR(value)
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {number} value (new DISPLAY register)
|
||||
*/
|
||||
PanelPDP11.prototype.setDR = function(value)
|
||||
{
|
||||
this.updateData(this.regDisplay = value);
|
||||
};
|
||||
|
||||
/**
|
||||
* getSR()
|
||||
*
|
||||
|
|
@ -573,8 +618,7 @@ PanelPDP11.prototype.processStart = function(value, index)
|
|||
* is depressed, "the computer system will be cleared." I take it to mean that it performs
|
||||
* the equivalent of a RESET instruction.
|
||||
*/
|
||||
this.bus.reset();
|
||||
this.cpu.resetRegs();
|
||||
this.cpu.resetCPU();
|
||||
/*
|
||||
* The PDP-11/70 Handbook goes on to say: "If the system needs to be initialized but execution
|
||||
* is not wanted, the START switch should be depressed while the HALT/ENABLE switch is in the HALT
|
||||
|
|
@ -655,7 +699,7 @@ PanelPDP11.prototype.processContinue = function(value, index)
|
|||
var dbg = this.dbg;
|
||||
if (dbg && !dbg.isBusy(true)) {
|
||||
dbg.setBusy(true);
|
||||
dbg.stepCPU(0);
|
||||
dbg.stepCPU(0, null);
|
||||
dbg.setBusy(false);
|
||||
}
|
||||
else {
|
||||
|
|
@ -968,7 +1012,7 @@ PanelPDP11.prototype.setData = function(value, fActive)
|
|||
* Called by the Computer component at intervals to update registers, LEDs, etc.
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {number} [nUpdate] (< 0 for forced, > 0 for periodic, undefined otherwise)
|
||||
* @param {number} [nUpdate] (< 0 for forced, > 0 for periodic, 0 otherwise)
|
||||
*/
|
||||
PanelPDP11.prototype.updateDisplay = function(nUpdate)
|
||||
{
|
||||
|
|
@ -985,7 +1029,7 @@ PanelPDP11.prototype.updateDisplay = function(nUpdate)
|
|||
* LEDs are considered cheap, register displays are not. So we'll skip the latter if this
|
||||
* is a periodic update AND our periodic update counter hasn't reached the periodic update limit.
|
||||
*/
|
||||
if (!(nUpdate > 0 && (this.nPeriodicCount += nUpdate) < this.nPeriodicLimit)) {
|
||||
if (nUpdate <= 0 || (this.nDisplayCount += nUpdate) >= this.nDisplayLimit) {
|
||||
for (var i = 0; i < this.cpu.regsGen.length; i++) {
|
||||
this.displayValue('R'+i, this.cpu.regsGen[i]);
|
||||
}
|
||||
|
|
@ -995,7 +1039,7 @@ PanelPDP11.prototype.updateDisplay = function(nUpdate)
|
|||
this.displayValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
|
||||
this.displayValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
|
||||
this.displayValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
|
||||
this.nPeriodicCount = 0;
|
||||
this.nDisplayCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1004,34 +1048,35 @@ PanelPDP11.prototype.updateDisplay = function(nUpdate)
|
|||
* TODO: There is currently no mechanism for selecting regData over regDisplay;
|
||||
* we are acting as if the DATASEL switch setting is locked to "DISPLAY REGISTER".
|
||||
*/
|
||||
this.updateAddr(nUpdate > 0 && fRunning && !fWaiting? this.cpu.getPC() : this.regAddr);
|
||||
this.updateAddr(nUpdate > 0 && fRunning && !fWaiting? this.cpu.getLastAddr() : this.regAddr);
|
||||
this.updateData(this.regDisplay);
|
||||
|
||||
var bits = this.cpu.getMMUState();
|
||||
/*
|
||||
* Set bit to 1 (22-bit), 2 (18-bit), or 4 (16-bit)
|
||||
* Bit 0 set if 22-bit, bit 1 set if 18-bit, bit 2 set if 16-bit
|
||||
*/
|
||||
var bit = this.cpu.mmuEnable? ((this.cpu.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
|
||||
this.updateLED(PanelPDP11.LED.B22, bit & 1);
|
||||
this.updateLED(PanelPDP11.LED.B18, bit & 2);
|
||||
this.updateLED(PanelPDP11.LED.B16, bit & 4);
|
||||
this.updateLED(PanelPDP11.LED.B22, bits & 1);
|
||||
this.updateLED(PanelPDP11.LED.B18, bits & 2);
|
||||
this.updateLED(PanelPDP11.LED.B16, bits & 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* readCNSW(addr)
|
||||
* readCNSW(addr, fPreWrite)
|
||||
*
|
||||
* If addr is set, then this a normal read, so we should return the SWITCH register (ie, regSwitches).
|
||||
* If fPreWrite, this is a read-before-write, so we must return the DISPLAY register (ie, regDisplay);
|
||||
* otherwise, this a normal read, so we should return the SWITCH register (ie, regSwitches).
|
||||
*
|
||||
* if addr is NOT set, then this is a read-before-write, so we must return the DISPLAY register (ie, regDisplay).
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.CNSW or 177570)
|
||||
* @param {boolean} [fPreWrite]
|
||||
* @return {number}
|
||||
*/
|
||||
PanelPDP11.prototype.readCNSW = function(addr)
|
||||
PanelPDP11.prototype.readCNSW = function(addr, fPreWrite)
|
||||
{
|
||||
return (addr? this.regSwitches : this.regDisplay) & 0xffff;
|
||||
return (fPreWrite? this.regDisplay : this.regSwitches) & 0xffff;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -489,8 +489,8 @@ PC11.prototype.load = function(sTapeName, sTapePath, nTapeTarget, file)
|
|||
|
||||
if (file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
pc11.doneRead(sTapeName, sTapePath, nTapeTarget, reader.result);
|
||||
reader.onload = function doneRead() {
|
||||
pc11.finishRead(sTapeName, sTapePath, nTapeTarget, reader.result);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
return true;
|
||||
|
|
@ -515,13 +515,13 @@ PC11.prototype.load = function(sTapeName, sTapePath, nTapeTarget, file)
|
|||
}
|
||||
}
|
||||
|
||||
return !!web.getResource(sTapeURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
pc11.doneLoad(sTapeName, sTapePath, nTapeTarget, sResponse, sURL, nErrorCode);
|
||||
return !!web.getResource(sTapeURL, null, true, function doneLoad(sURL, sResponse, nErrorCode) {
|
||||
pc11.finishLoad(sTapeName, sTapePath, nTapeTarget, sResponse, sURL, nErrorCode);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sTapeName, sTapePath, sTapeData, nTapeTarget, sURL, nErrorCode)
|
||||
* finishLoad(sTapeName, sTapePath, sTapeData, nTapeTarget, sURL, nErrorCode)
|
||||
*
|
||||
* @this {PC11}
|
||||
* @param {string} sTapeName
|
||||
|
|
@ -531,7 +531,7 @@ PC11.prototype.load = function(sTapeName, sTapePath, nTapeTarget, file)
|
|||
* @param {string} sURL
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
PC11.prototype.doneLoad = function(sTapeName, sTapePath, nTapeTarget, sTapeData, sURL, nErrorCode)
|
||||
PC11.prototype.finishLoad = function(sTapeName, sTapePath, nTapeTarget, sTapeData, sURL, nErrorCode)
|
||||
{
|
||||
var fPrintOnly = (nErrorCode < 0 && this.cmp && !this.cmp.flags.powered);
|
||||
|
||||
|
|
@ -547,7 +547,7 @@ PC11.prototype.doneLoad = function(sTapeName, sTapePath, nTapeTarget, sTapeData,
|
|||
}
|
||||
else {
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage('doneLoad("' + sTapePath + '")');
|
||||
this.printMessage('finishLoad("' + sTapePath + '")');
|
||||
}
|
||||
Component.addMachineResource(this.idMachine, sURL, sTapeData);
|
||||
var resource = web.parseMemoryResource(sURL, sTapeData);
|
||||
|
|
@ -564,7 +564,7 @@ PC11.prototype.doneLoad = function(sTapeName, sTapePath, nTapeTarget, sTapeData,
|
|||
};
|
||||
|
||||
/**
|
||||
* doneRead(sTapeName, sTapePath, nTapeTarget, buffer)
|
||||
* finishRead(sTapeName, sTapePath, nTapeTarget, buffer)
|
||||
*
|
||||
* @this {PC11}
|
||||
* @param {string} sTapeName
|
||||
|
|
@ -572,7 +572,7 @@ PC11.prototype.doneLoad = function(sTapeName, sTapePath, nTapeTarget, sTapeData,
|
|||
* @param {number} nTapeTarget
|
||||
* @param {?} buffer (we KNOW this is an ArrayBuffer, but we can't seem to convince the Closure Compiler)
|
||||
*/
|
||||
PC11.prototype.doneRead = function(sTapeName, sTapePath, nTapeTarget, buffer)
|
||||
PC11.prototype.finishRead = function(sTapeName, sTapePath, nTapeTarget, buffer)
|
||||
{
|
||||
if (buffer) {
|
||||
var aBytes = new Uint8Array(buffer, 0, buffer.byteLength);
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ function RAMPDP11(parmsRAM)
|
|||
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var ram = this;
|
||||
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
ram.doneLoad(sURL, sResponse, nErrorCode);
|
||||
web.getResource(sFileURL, null, true, function doneLoad(sURL, sResponse, nErrorCode) {
|
||||
ram.finishLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -166,14 +166,14 @@ RAMPDP11.prototype.powerDown = function(fSave, fShutdown)
|
|||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sData, nErrorCode)
|
||||
* finishLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
RAMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
||||
RAMPDP11.prototype.finishLoad = function(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
|
|
@ -197,8 +197,8 @@ RAMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
|||
/**
|
||||
* initRAM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the data. When both those
|
||||
* This function is called by both initBus() and finishLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND finishLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ RL11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
|
||||
this.triggerInterrupt = this.cpu.addTrigger(PDP11.RL11.VEC, PDP11.RL11.PRI);
|
||||
|
||||
bus.addIOTable(this, RL11.UNIBUS_IOTABLE, MessagesPDP11.DISK);
|
||||
bus.addIOTable(this, RL11.UNIBUS_IOTABLE);
|
||||
bus.addResetHandler(this.reset.bind(this));
|
||||
|
||||
this.addDisk("None", RL11.SOURCE.NONE, true);
|
||||
|
|
@ -549,7 +549,7 @@ RL11.prototype.loadDrive = function(iDrive, sDiskName, sDiskPath, fAutoMount, fi
|
|||
}
|
||||
drive.fLocal = !!file;
|
||||
var disk = new DiskPDP11(this, drive, DiskAPI.MODE.PRELOAD);
|
||||
if (disk.load(sDiskName, sDiskPath, file, this.doneLoadDrive)) {
|
||||
if (disk.load(sDiskName, sDiskPath, file, this.finishLoadDrive)) {
|
||||
nResult++;
|
||||
}
|
||||
}
|
||||
|
|
@ -558,7 +558,7 @@ RL11.prototype.loadDrive = function(iDrive, sDiskName, sDiskPath, fAutoMount, fi
|
|||
};
|
||||
|
||||
/**
|
||||
* doneLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
|
||||
* finishLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
|
||||
*
|
||||
* The disk parameter is set if the disk was successfully loaded, null if not.
|
||||
*
|
||||
|
|
@ -569,7 +569,7 @@ RL11.prototype.loadDrive = function(iDrive, sDiskName, sDiskPath, fAutoMount, fi
|
|||
* @param {string} sDiskPath
|
||||
* @param {boolean} [fAutoMount]
|
||||
*/
|
||||
RL11.prototype.doneLoadDrive = function onLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
|
||||
RL11.prototype.finishLoadDrive = function onLoadDrive(drive, disk, sDiskName, sDiskPath, fAutoMount)
|
||||
{
|
||||
var aDiskInfo;
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ if (NODE) {
|
|||
* file: name of ROM data file
|
||||
*
|
||||
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND
|
||||
* the ROM data file has finished loading (see doneLoad()).
|
||||
* the ROM data file has finished loading (see finishLoad()).
|
||||
*
|
||||
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM
|
||||
* you received is the ROM you expected.
|
||||
|
|
@ -65,7 +65,7 @@ if (NODE) {
|
|||
*/
|
||||
function ROMPDP11(parmsROM)
|
||||
{
|
||||
Component.call(this, "ROM", parmsROM, ROMPDP11);
|
||||
Component.call(this, "ROM", parmsROM, ROMPDP11, MessagesPDP11.ROM);
|
||||
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
|
|
@ -103,8 +103,8 @@ function ROMPDP11(parmsROM)
|
|||
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var rom = this;
|
||||
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
rom.doneLoad(sURL, sResponse, nErrorCode);
|
||||
web.getResource(sFileURL, null, true, function doneLoad(sURL, sResponse, nErrorCode) {
|
||||
rom.finishLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -183,14 +183,14 @@ ROMPDP11.prototype.powerDown = function(fSave, fShutdown)
|
|||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sData, nErrorCode)
|
||||
* finishLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
ROMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
||||
ROMPDP11.prototype.finishLoad = function(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load ROM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
|
|
@ -212,8 +212,8 @@ ROMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
|||
/**
|
||||
* initROM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the data. When both those
|
||||
* This function is called by both initBus() and finishLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND finishLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
|
|
@ -289,11 +289,16 @@ ROMPDP11.prototype.addROM = function(addr)
|
|||
* of the IOPAGE address space, by installing I/O handlers for the entire range that return the corresponding
|
||||
* bytes of the current ROM image on reads, and ignore any writes (which I'm only assuming is how a typical
|
||||
* ROM "device" deals with writes; if we remove the write handler, then writes will fault).
|
||||
*
|
||||
* TODO: It would be more efficient if we parsed ROM data as words rather than bytes, and then installed
|
||||
* only word handlers instead of only byte handlers. It was done this way purely for historical reasons (ie,
|
||||
* because that's how other PCjs machines parse their ROMs). For now, all this means is that executing code
|
||||
* out of ROM will be slower than out of RAM -- although that's often true in the real world as well.
|
||||
*/
|
||||
var IOTable = {
|
||||
[addr]: [ROMPDP11.prototype.readROMByte, ROMPDP11.prototype.writeROMByte, null, null, null, this.sizeROM >> 1]
|
||||
};
|
||||
if (this.bus.addIOTable(this, IOTable, MessagesPDP11.ROM, this.idComponent)) {
|
||||
if (this.bus.addIOTable(this, IOTable)) {
|
||||
this.fRetainROM = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,19 @@ SerialPortPDP11.prototype.setBinding = function(sType, sBinding, control, sValue
|
|||
return true;
|
||||
};
|
||||
|
||||
control.onpaste = function onKeyPress(event) {
|
||||
if (event.stopPropagation) event.stopPropagation();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
var clipboardData = event.clipboardData || window.clipboardData;
|
||||
if (clipboardData) {
|
||||
/*
|
||||
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
|
||||
* this is dealt with in receiveData() whenever it receives a string of characters.
|
||||
*/
|
||||
serial.receiveData(clipboardData.getData('Text'));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Now that we've added an onkeypress handler that calls preventDefault() for ALL keys, the control
|
||||
* itself no longer needs the "readonly" attribute; we primarily need to remove it for iOS browsers,
|
||||
|
|
@ -513,8 +526,20 @@ SerialPortPDP11.prototype.receiveData = function(data)
|
|||
this.abReceive.push(data);
|
||||
}
|
||||
else if (typeof data == "string") {
|
||||
var b = 0, bPrev;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
this.abReceive.push(data.charCodeAt(i));
|
||||
bPrev = b;
|
||||
b = data.charCodeAt(i);
|
||||
/*
|
||||
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
|
||||
* we convert them to CRs below. Windows may do something different, but in the worst case,
|
||||
* even if we receive CR/LF pairs, this code should keep the CRs and lose the LFs.
|
||||
*/
|
||||
if (b == str.ASCII.LF) {
|
||||
if (bPrev == str.ASCII.CR) continue;
|
||||
b = str.ASCII.CR;
|
||||
}
|
||||
this.abReceive.push(b);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -551,6 +551,10 @@ str.trim = function(s)
|
|||
/*
|
||||
* Any codes commented out in the following table are deemed "printable"
|
||||
*/
|
||||
str.ASCII = {
|
||||
LF: 0x0A,
|
||||
CR: 0x0D
|
||||
};
|
||||
str.aASCIICodes = {
|
||||
0x00: "NUL",
|
||||
0x01: "SOH", // (CTRL_A) Start of Heading
|
||||
|
|
|
|||
Loading…
Reference in a new issue