Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-11-18 10:47:04 -08:00
commit a83ae4c0be
35 changed files with 2739 additions and 1648 deletions

View file

@ -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");

View file

@ -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;
}
}

View file

@ -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);
};
/**

View file

@ -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");
};
/**

View file

@ -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));
};

View file

@ -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) {

View file

@ -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") {

View file

@ -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[],

View file

@ -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"],

View file

@ -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,

View file

@ -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;
};
/**

View file

@ -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);

View file

@ -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}

View file

@ -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;

View file

@ -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;
}

View file

@ -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 {

View file

@ -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