Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-12-02 17:32:15 -08:00
commit ccac473dec
13 changed files with 847 additions and 672 deletions

View file

@ -1221,6 +1221,7 @@ CPUPDP11.prototype.stopCPU = function(fComplete)
this.cmp.stop(usr.getTime(), this.getCycles());
}
fStopped = true;
if (!this.dbg) this.status("Stopped");
}
this.flags.complete = fComplete;
return fStopped;

View file

@ -161,8 +161,8 @@ CPUStatePDP11.prototype.initProcessor = function()
* finish()
*
* TODO: This function simply ensures that we don't leave any IRQs installed with unresolved floating
* (negative) vectors; properly assigning vectors according to device type (ie, auto-configuration) is an
* exercise left for another day.
* (negative) vectors; however, properly assigning vectors according to device type (ie, auto-configuration)
* is an exercise left for another day.
*
* @this {CPUStatePDP11}
*/
@ -185,7 +185,7 @@ CPUStatePDP11.prototype.finish = function()
*/
CPUStatePDP11.prototype.reset = function()
{
this.status("model " + this.model);
this.status("Model " + this.model);
if (this.flags.running) this.stopCPU();
this.initRegs();
this.resetCycles();
@ -220,8 +220,7 @@ CPUStatePDP11.prototype.initRegs = function()
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.pswMode = 0; // current memory management mode (see PDP11.MODE.KERNEL | SUPER | UNUSED | USER)
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 (8 KIPDR regs followed by 8 KDPDR regs)
@ -257,8 +256,9 @@ CPUStatePDP11.prototype.initRegs = function()
this.srcMode = this.srcReg = 0;
this.dstMode = this.dstReg = this.dstAddr = 0;
this.trapPSW = -1;
this.resetMMU();
this.pswTrap = -1;
this.resetRegs();
};
/**
@ -269,25 +269,30 @@ CPUStatePDP11.prototype.initRegs = function()
CPUStatePDP11.prototype.resetCPU = function()
{
this.bus.reset();
this.resetMMU();
this.resetRegs();
};
/**
* resetMMU()
* resetRegs()
*
* Reset all registers required as part of a RESET instruction.
*
* TODO: Do we ever need to automatically clear regErr, or is it cleared manually?
*
* @this {CPUStatePDP11}
*/
CPUStatePDP11.prototype.resetMMU = function()
CPUStatePDP11.prototype.resetRegs = function()
{
this.regMMR0 = 0; // 177572
this.regMMR1 = 0; // 177574
this.regMMR2 = 0; // 177576
this.regMMR3 = 0; // 172516
this.regErr = 0; // 177766 TODO: Do we ever need to automatically clear this, or is it manually cleared?
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.mmuLastPage = 0;
this.mmuMask = 0x3ffff;
this.addrLast = 0; // this is queried by the Panel when it's not using its own ADDRESS register
@ -317,8 +322,8 @@ CPUStatePDP11.prototype.getMMUState = function()
/**
* setMemoryAccess()
*
* Define handlers and DSPACE setting appropriate for the current MMU mode, in order to eliminate
* unnecessary calls to mapVirtualToPhysical().
* Define handlers and DSPACE setting appropriate for the current MMU mode, in order to eliminate unnecessary calls
* to mapVirtualToPhysical().
*
* TODO: We could further optimize readWord(), splitting it into readWordFromDSpace() and readWordFromISpace(),
* eliminating the need to OR the addrDSpace bit when we know that bit is zero, but that's a pretty tiny optimization.
@ -389,14 +394,12 @@ CPUStatePDP11.prototype.setMMR0 = function(newMMR0)
* 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;
this.mmuLastMode = (newMMR0 & PDP11.MMR0.MODE) >> PDP11.MMR0.SHIFT.MODE;
this.mmuLastPage = (newMMR0 & PDP11.MMR0.PAGE) >> PDP11.MMR0.SHIFT.PAGE;
var mmuEnable = 0;
if (newMMR0 & 0x101) {
if (newMMR0 & (PDP11.MMR0.ENABLED | PDP11.MMR0.MAINT)) {
mmuEnable = PDP11.ACCESS.WRITE;
if (newMMR0 & 0x1) {
mmuEnable |= PDP11.ACCESS.READ;
}
if (newMMR0 & PDP11.MMR0.ENABLED) mmuEnable |= PDP11.ACCESS.READ;
}
if (this.mmuEnable != mmuEnable) {
this.mmuEnable = mmuEnable;
@ -528,7 +531,33 @@ CPUStatePDP11.prototype.getChecksum = function()
CPUStatePDP11.prototype.save = function()
{
var state = new State(this);
state.set(0, []);
state.set(0, [
this.regsGen,
this.regsAlt,
this.regsAltStack,
this.regsUniMap,
this.regsControl,
this.regErr,
this.regMB,
this.regPIR,
this.regSL,
this.getPSW(),
this.pswTrap,
this.pswMode,
this.opFlags,
this.regMMR0,
this.regMMR1,
this.regMMR2,
this.regMMR3,
this.mmuLastMode,
this.mmuLastPage,
this.mmuPDR,
this.mmuPAR,
this.mmuEnable,
this.mmuMask,
this.addrLast,
this.opLast
]);
state.set(1, [this.nTotalCycles, this.getSpeed()]);
state.set(2, this.bus.saveMemory());
return state.data();
@ -1106,14 +1135,14 @@ CPUStatePDP11.prototype.setPSW = function(newPSW)
this.regsAlt[i] = tmp;
}
}
this.mmuMode = (newPSW >> PDP11.PSW.SHIFT.CMODE) & PDP11.MODE.MASK;
this.pswMode = (newPSW >> PDP11.PSW.SHIFT.CMODE) & PDP11.MODE.MASK;
var oldMode = (this.regPSW >> PDP11.PSW.SHIFT.CMODE) & PDP11.MODE.MASK;
if (this.mmuMode != oldMode) {
if (this.pswMode != oldMode) {
/*
* Swap stack pointers
*/
this.regsAltStack[oldMode] = this.regsGen[6];
this.regsGen[6] = this.regsAltStack[this.mmuMode];
this.regsGen[6] = this.regsAltStack[this.pswMode];
}
this.regPSW = newPSW;
@ -1337,9 +1366,9 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
if (this.nDisableTraps) return;
if (this.trapPSW < 0) {
this.trapPSW = this.getPSW();
} else if (!this.mmuMode) {
if (this.pswTrap < 0) {
this.pswTrap = this.getPSW();
} else if (!this.pswMode) {
reason = PDP11.REASON.RED; // double-fault (nested trap) forces a RED condition
}
@ -1374,16 +1403,16 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
/*
* Read from kernel D space
*/
this.mmuMode = 0;
this.pswMode = 0;
var newPC = this.readWord(vector | this.addrDSpace);
var newPSW = this.readWord(((vector + 2) & 0xffff) | this.addrDSpace);
/*
* Set new PSW with previous mode
*/
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.trapPSW >> 2) & PDP11.PSW.PMODE));
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.pswTrap >> 2) & PDP11.PSW.PMODE));
this.pushWord(this.trapPSW);
this.pushWord(this.pswTrap);
this.pushWord(this.regsGen[7]);
this.setPC(newPC);
}
@ -1428,7 +1457,7 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
this.opFlags &= ~(flag | PDP11.OPFLAG.TRAP_TF | PDP11.OPFLAG.IRQ_MASK);
this.opFlags |= PDP11.OPFLAG.IRQ_DELAY | PDP11.OPFLAG.TRAP_LAST;
this.trapPSW = -1; // reset flag that we have a trap within a trap
this.pswTrap = -1; // reset flag that we have a trap within a trap
/*
* These next properties (in conjunction with setting PDP11.OPFLAG.TRAP_LAST) are purely an aid for the Debugger;
@ -1539,55 +1568,88 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
return addr;
};
/**
* getAddrInfo(addrVirtual)
*
* @this {CPUStatePDP11}
* @param {number} addrVirtual
* @return {Array}
*/
CPUStatePDP11.prototype.getAddrInfo = function(addrVirtual)
{
var addr;
var a = [];
if (!this.mmuEnable) {
addr = addrVirtual & 0xffff;
if (addr >= BusPDP11.IOPAGE_16BIT) addr |= this.addrIOPage;
a.push(addr);
}
else {
var mode = this.pswMode << 1;
var page = addrVirtual >> 13;
if (page > 7) mode |= 1;
if (!(this.regMMR3 & this.mapMMR3[this.pswMode])) page &= 7;
var pdr = this.mmuPDR[this.pswMode][page];
var off = addrVirtual & 0x1fff;
var paf = (this.mmuPAR[this.pswMode][page] << 6);
addr = (paf + off) & this.mmuMask;
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
a.push(addr); // a[0]
a.push(off); // a[1]
a.push(mode); // a[2] (0=KI, 1=KD, 2=SI, 3=SD, 4=??, 5=??, 6=UI, 7=UD)
a.push(page & 7); // a[3]
a.push(paf); // a[4]
a.push(this.mmuMask); // a[5]
}
return a;
};
/**
* mapVirtualToPhysical(addrVirtual, access)
*
* mapVirtualToPhysical() does memory management. It converts a 17-bit I/D virtual address to a
* 22-bit physical address. A real PDP 11/70 memory management unit can be enabled separately
* for read and write for diagnostic purposes. This is handled here by having an enable mask
* (mmuEnable) which is tested against the operation access mask (access). If there is no
* match, then the virtual address is simply mapped as a 16 bit physical address with the upper
* page going to the IO address space. Significant access mask values used are PDP11.ACCESS.READ
* and PDP11.ACCESS.WRITE.
* mapVirtualToPhysical() does memory management. It converts a 17-bit I/D virtual address to a
* 22-bit physical address. A real PDP 11/70 memory management unit can be enabled separately for
* read and write for diagnostic purposes. This is handled here by having an enable mask (mmuEnable)
* which is tested against the operation access mask (access). If there is no match, then the virtual
* address is simply mapped as a 16 bit physical address with the upper page going to the IO address
* space. Significant access mask values used are PDP11.ACCESS.READ and PDP11.ACCESS.WRITE.
*
* 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.
*
* When doing mapping, mmuMode is used to decide what address space is to be used (0 = kernel,
* 1 = supervisor, 2 = illegal, 3 = user). Normally, mmuMode is set by the setPSW() function,
* but there are exceptions for instructions which move data between address spaces (MFPD, MFPI,
* MTPD, and MTPI) and trap(). These will modify mmuMode outside of setPSW() and then restore
* it again if all worked. If however something happens to cause a trap then no restore is
* done as setPSW() will have been invoked as part of the trap, which will resynchronize mmuMode.
* When doing mapping, pswMode is used to decide what address space is to be used (0 = kernel,
* 1 = supervisor, 2 = illegal, 3 = user). Normally, pswMode is set by the setPSW() function, but
* there are exceptions for instructions which move data between address spaces (MFPD, MFPI, MTPD,
* and MTPI) and trap(). These will modify pswMode outside of setPSW() and then restore it again if
* all worked. If however something happens to cause a trap then no restore is done as setPSW()
* will have been invoked as part of the trap, which will resynchronize pswMode.
*
* A PDP-11/70 is different from other PDP-11s in that the highest 18 bit space (017000000 & above)
* maps directly to UNIBUS space - including low memory. This doesn't appear to be particularly
* useful as it restricts maximum system memory - although it does appear to allow software
* testing of the UNIBUS map. This feature also appears to confuse some OSes which test consecutive
* memory locations to find maximum memory -- and on a full memory system find themselves accessing
* low memory again at high addresses.
* maps directly to UNIBUS space - including low memory. This doesn't appear to be particularly useful
* as it restricts maximum system memory - although it does appear to allow software testing of the
* UNIBUS map. This feature also appears to confuse some OSes which test consecutive memory locations
* to find maximum memory -- and on a full memory system find themselves accessing low memory again at
* high addresses.
*
* Construction of a Physical Address
* ----------------------------------
*
* Virtual Addr (VA) 12 11 10 9 8 7 6 5 4 3 2 1 0
* Page Addr Field (PAF) 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* + -----------------------------------------------------------------
* Physical Addr (PA) 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* + Page Addr Field (PAF) 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* -----------------------------------------------------------------
* = Physical Addr (PA) 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
*
* The Page Address Field (PAF) comes from a Page Address Register (PAR) that is selected by Virtual Address (VA)
* bits 15-13. You can see from the above alignments that the VA contributes to the low 13 bits, providing an 8Kb
* range.
* The Page Address Field (PAF) comes from a Page Address Register (PAR) that is selected by Virtual
* Address (VA) bits 15-13. You can see from the above alignments that the VA contributes to the low
* 13 bits, providing an 8Kb range.
*
* VA bits 0-5 pass directly through to the PA; those are also called the DIB (Displacement in Block) bits.
* VA bits 6-12 are added to the low 7 bits of the PAF and are also called the BN (Block Number) bits.
*
* You can also think of the entire PAF as a block number, where each block is 64 bytes. This is consistent with
* the LSIZE register at 177760, which is supposed to contain the number of 64-byte blocks of memory installed.
* You can also think of the entire PAF as a block number, where each block is 64 bytes. This is consistent
* with the LSIZE register at 177760, which is supposed to contain the block number of the last 64-byte block
* of memory installed.
*
* Note that if a PAR is initialized to zero, successively adding 0200 (0x80) to the PAR will advance the base
* physical address to the next 8Kb page.
* Note that if a PAR is initialized to zero, successively adding 0200 (0x80) to the PAR will advance the
* base physical address to the next 8Kb page.
*
* @this {CPUStatePDP11}
* @param {number} addrVirtual
@ -1599,7 +1661,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
var page, pdr, addr;
/*
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but not the ENABLED bit.
* This can happen when the MAINT bit of MMR0 is set but not the ENABLED bit.
*/
if (!(access & this.mmuEnable)) {
addr = addrVirtual & 0xffff;
@ -1608,17 +1670,17 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
}
page = addrVirtual >> 13;
if (!(this.regMMR3 & this.mapMMR3[this.mmuMode])) page &= 7;
pdr = this.mmuPDR[this.mmuMode][page];
addr = ((this.mmuPAR[this.mmuMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
if (!(this.regMMR3 & this.mapMMR3[this.pswMode])) page &= 7;
pdr = this.mmuPDR[this.pswMode][page];
addr = ((this.mmuPAR[this.pswMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
if (this.nDisableTraps) return addr;
/*
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers a NOMEMORY error
* using this instruction:
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers a NOMEMORY error using
* this instruction:
*
* 076170: 005037 140100 CLR @#140100
*
@ -1626,7 +1688,14 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
*
* 076356: 005037 140001 CLR @#140001
*
* These tests exercise the MMU checks that Paul mentions in the function description above.
* @paulnank: So it turns out that 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.
*
* @jeffpar: We're assuming, at least, that the MMU does its "NEXM" (NOMEMORY) non-existent memory test
* very simplistically, by range-checking the address against something like the memory SIZE registers,
* because otherwise the MMU would have to wait for a bus time-out: something so prohibitively expensive
* that the MMU could not afford to do it. I rely on addrInvalid, which is derived from the same Bus
* getMemoryLimit() service that the SIZE registers (177760--177762) use to derive their value.
*/
if (addr >= this.addrInvalid && addr < this.addrIOPage) {
this.regErr |= PDP11.CPUERR.NOMEMORY;
@ -1692,16 +1761,15 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
/*
* Aborts and traps: log FIRST trap and MOST RECENT abort
*/
this.mmuPDR[this.mmuMode][page] = pdr;
if (addr != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.mmuMode) {
this.mmuLastMode = this.mmuMode;
this.mmuPDR[this.pswMode][page] = pdr;
if (addr != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.pswMode) {
this.mmuLastMode = this.pswMode;
this.mmuLastPage = page;
}
if (newMMR0) {
if (newMMR0 & PDP11.MMR0.ABORT) {
if (this.trapPSW >= 0) {
if (this.pswTrap >= 0) {
newMMR0 |= PDP11.MMR0.COMPLETED;
}
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
@ -1710,9 +1778,13 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
this.setMMR0((this.regMMR0 & ~PDP11.MMR0.UPDATE) | (newMMR0 & PDP11.MMR0.UPDATE));
}
/*
* TODO: In unusual circumstances, if regMMR0 already indicated an ABORT condition above,
* NOTE: 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.
* on the trap vector, which faults again, etc.
*
* TODO: Determine what a real PDP-11 does in that situation; in our case, trap() deals with it
* by checking an internal OPFLAG (TRAP_RED) and turning the next trap into a PANIC, triggering an
* immediate HALT.
*/
this.trap(PDP11.TRAP.MMU, PDP11.OPFLAG.TRAP_MMU, PDP11.REASON.ABORT);
}
@ -1723,9 +1795,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(addrVirtual, access)
if (addr < ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.SIPDR0) & this.mmuMask) ||
addr > ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.UDPAR7 | 0x1) & this.mmuMask)) {
this.regMMR0 |= PDP11.MMR0.TRAP_MMU;
if (this.regMMR0 & PDP11.MMR0.MMU_TRAPS) {
this.opFlags |= PDP11.OPFLAG.TRAP_MMU;
}
if (this.regMMR0 & PDP11.MMR0.MMU_TRAPS) this.opFlags |= PDP11.OPFLAG.TRAP_MMU;
}
}
}
@ -1958,7 +2028,7 @@ CPUStatePDP11.prototype.checkStackLimit1120 = function(access, step, addr)
*
* so if the step parameter is positive, we let it go.
*/
if (!this.mmuMode && step <= 0 && addr <= this.regSL) {
if (!this.pswMode && step <= 0 && addr <= this.regSL) {
/*
* On older machines (eg, the PDP-11/20), there is no "YELLOW" and "RED" distinction, and the
* instruction is always allowed to complete, so the trap must always be issued in this fashion.
@ -1977,7 +2047,7 @@ CPUStatePDP11.prototype.checkStackLimit1120 = function(access, step, addr)
*/
CPUStatePDP11.prototype.checkStackLimit1145 = function(access, step, addr)
{
if (!this.mmuMode) {
if (!this.pswMode) {
/*
* NOTE: The 11/70 CPU Instruction Exerciser does NOT expect reads to trigger a stack overflow,
* so we check the access parameter.
@ -2191,9 +2261,9 @@ CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, access)
if (!(access & PDP11.ACCESS.DSPACE)) {
if ((this.regPSW & 0xf000) !== 0xf000) addr &= 0xffff;
}
this.mmuMode = (this.regPSW >> 12) & 3;
this.pswMode = (this.regPSW >> 12) & 3;
data = this.readWord(addr | (access & this.addrDSpace));
this.mmuMode = (this.regPSW >> 14) & 3;
this.pswMode = (this.regPSW >> 14) & 3;
}
return data;
};
@ -2221,14 +2291,14 @@ CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, access, data)
var addr = this.getAddrByMode(mode, reg, PDP11.ACCESS.WRITE_WORD);
if (!(access & PDP11.ACCESS.DSPACE)) addr &= 0xffff;
/*
* TODO: Consider replacing the following code with writeWord(), by adding optional mmuMode
* TODO: Consider replacing the following code with writeWord(), by adding optional pswMode
* parameters for each of the discrete mapVirtualToPhysical() and bus.setWord() operations, because
* as it stands, this is the only remaining call to mapVirtualToPhysical() outside of our
* setMemoryAccess() handlers.
*/
this.mmuMode = (this.regPSW >> 12) & 3;
this.pswMode = (this.regPSW >> 12) & 3;
addr = this.mapVirtualToPhysical(addr | (access & PDP11.ACCESS.DSPACE), PDP11.ACCESS.WRITE);
this.mmuMode = (this.regPSW >> 14) & 3;
this.pswMode = (this.regPSW >> 14) & 3;
this.bus.setWord(addr, data);
}
};

View file

@ -294,6 +294,8 @@ if (DEBUGGER) {
"SR": DebuggerPDP11.REG_SR
};
DebuggerPDP11.MODES = ["KI","KD","SI","SD","??","??","UI","UD"];
/*
* Operand type masks; anything that's not covered by OP_SRC or OP_DST must be a OP_OTHER value.
*/
@ -3013,6 +3015,7 @@ if (DEBUGGER) {
}
sDumpers += ",state,symbols";
this.println("dump memory commands:");
this.println("\tda [a] dump info for address a");
this.println("\tdb [a] [n] dump n bytes at address a");
this.println("\tdw [a] [n] dump n words at address a");
this.println("\tdd [a] [n] dump n dwords at address a");
@ -3077,6 +3080,27 @@ if (DEBUGGER) {
var dbgAddr = this.parseAddr(sAddr);
if (!dbgAddr) return;
if (sCmd == "da") {
/*
* Sample output ("da 23042"):
*
* 00,010,011,000,100,010 00000023042
* 0,011,000,100,010 00000003042
* + KIPAR[1]: 0,000,001,101,111,010,000,000 00000157200
* & MMU MASK: 1,111,111,111,111,111,111,111 00017777777
* = PHYSICAL: 0,000,001,110,010,010,100,010 00000162242
*/
var a = this.cpu.getAddrInfo(dbgAddr.addr);
this.println(str.pad("", 19) + str.toBin(dbgAddr.addr, 17, 3) + " " + str.toOct(dbgAddr.addr, 8));
if (a.length > 1) {
this.println(str.pad("", 24) + str.toBin(a[1], 13, 3) + " " + str.toOct(a[1], 8));
this.println("+ " + DebuggerPDP11.MODES[a[2]] + "PAR[" + a[3] + "]: " + str.toBin(a[4], 22, 3) + " " + str.toOct(a[4], 8));
this.println("& MMU MASK: " + str.toBin(a[5], 22, 3) + " " + str.toOct(a[5], 8));
this.println("= PHYSICAL: " + str.toBin(a[0], 22, 3) + " " + str.toOct(a[0], 8))
}
return;
}
var len = 0; // 0 is not a default; it triggers the appropriate default below
var fRange = false;
var fJSON = (sCmd == "ds");

View file

@ -349,7 +349,7 @@ var PDP11 = {
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)
MAINT: 0x0100, // 000400 only destination mode references will be relocated
MMU_TRAPS: 0x0200, // 001000 enable MMU traps (11/70 only)
UNUSED: 0x0C00, // 006000
TRAP_MMU: 0x1000, // 010000 trap: MMU (11/70 only)
@ -357,7 +357,11 @@ var PDP11 = {
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
UPDATE: 0xF0FE, // Includes all of: ABORT, TRAP, COMPLETED, MODE, and PAGE bits
SHIFT: {
PAGE: 1,
MODE: 5
}
},
MMR1: { // 177574: general purpose auto-inc/auto-dec register (11/44 and 11/70 only)
REG1_NUM: 0x0007, //
@ -743,15 +747,15 @@ var PDP11 = {
DS: 13
}
},
FUNC: { // NOTE: These function codes are pre-shifted to read/write directly from/to RKCS.FUNC
CRESET: 0b0000, // Controller Reset
WRITE: 0b0010, // Write
READ: 0b0100, // Read
WCHK: 0b0110, // Write Check
SEEK: 0b1000, // Seek
RCHK: 0b1010, // Read Check
DRESET: 0b1100, // Drive Reset
WLOCK: 0b1110 // Write Lock
FUNC: {
CRESET: 0b000, // Controller Reset
WRITE: 0b001, // Write
READ: 0b010, // Read
WCHK: 0b011, // Write Check
SEEK: 0b100, // Seek
RCHK: 0b101, // Read Check
DRESET: 0b110, // Drive Reset
WLOCK: 0b111 // Write Lock
}
},
RL11: { // RL11 Disk Controller

View file

@ -125,6 +125,9 @@ DevicePDP11.prototype.dumpMMU = function(asArgs)
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);
if (cpu.regMMR3 & PDP11.MMR3.UNIBUS_MAP) {
this.dumpRegs("UNIMAP", cpu.regsUniMap, -1, asArgs[0]);
}
}
};
@ -143,9 +146,24 @@ 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]);
var nRegs = 8;
var sDump = "";
var fIndex = false;
var nBytes = 0;
var nWidth = 8;
if (offset < 0) {
nRegs = aRegs.length;
offset = 0;
fIndex = true;
nBytes = 4;
nWidth = 4;
}
for (var i = 0; i < nRegs; i++) {
if (i % nWidth == 0) {
if (sDump) sDump += '\n';
sDump += sName + (fIndex? ('[' + str.toOct(i, 2) + ']') : '') + ':';
}
sDump += ' ' + dbg.toStrBase(aRegs[offset + i], nBytes);
}
dbg.println(sDump + (fBreak? '\n' : ''));
}
@ -292,7 +310,7 @@ DevicePDP11.prototype.writeMMR3 = function(data, addr)
/**
* readUNIMAP(addr)
*
* NOTE: The UNIBUS map is 32 registers spread across 64 words, so we first calculate the word index.
* NOTE: The UNIBUS map ("UNIMAP") is 32 registers spread across 64 words, so we first calculate the word index.
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.UNIMAP)
@ -308,7 +326,7 @@ DevicePDP11.prototype.readUNIMAP = function(addr)
/**
* writeUNIMAP(data, addr)
*
* NOTE: The UNIBUS map is 32 registers spread across 64 words, so we first calculate the word index.
* NOTE: The UNIBUS map ("UNIMAP") is 32 registers spread across 64 words, so we first calculate the word index.
*
* @this {DevicePDP11}
* @param {number} data

View file

@ -887,7 +887,7 @@ RK11.prototype.initDrive = function(drive, iDrive, data)
if (!drive.disk) drive.sDiskPath = ""; // ensure this is initialized to a default that displayDisk() can deal with
drive.status = PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.DRDY | PDP11.RK11.RKDS.RRDY;
drive.status = PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.RRDY;
return fSuccess;
};
@ -899,32 +899,59 @@ RK11.prototype.initDrive = function(drive, iDrive, data)
*/
RK11.prototype.processCommand = function()
{
var fnReadWrite;
var fInterrupt = true;
var fnReadWrite, sFunc = "";
var iDrive = (this.regRKDA & PDP11.RK11.RKDA.DS) >> PDP11.RK11.RKDA.SHIFT.DS;
var drive = this.aDrives[iDrive];
var iCylinder, iHead, iSector, nWords, addr;
this.regRKCS &= ~PDP11.RK11.RKCS.CRDY;
var func = (this.regRKCS & PDP11.RK11.RKCS.FUNC) >> PDP11.RK11.RKCS.SHIFT.FUNC;
switch(this.regRKCS & PDP11.RK11.RKCS.FUNC) {
switch(func) {
case PDP11.RK11.FUNC.CRESET:
this.regRKDS = drive.status;
if (this.messageEnabled()) this.printMessage(this.type + ": CRESET(" + iDrive + ")", true);
this.regRKER = 0;
this.regRKCS = PDP11.RK11.RKCS.CRDY;
this.regRKDA = 0;
break;
case PDP11.RK11.FUNC.SEEK:
iCylinder = (this.regRKDA & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
if (this.messageEnabled()) this.printMessage(this.type + ": SEEK(" + iCylinder + ")", true);
if (iCylinder >= drive.nCylinders) {
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
}
break;
case PDP11.RK11.FUNC.RCHK:
sFunc = "RCHK";
/* falls through */
case PDP11.RK11.FUNC.READ:
if (!sFunc) sFunc = "READ";
fnReadWrite = this.readData;
/* falls through */
case PDP11.RK11.FUNC.WCHK:
if (!sFunc) sFunc = "WCHK";
/* falls through */
case PDP11.RK11.FUNC.WRITE:
if (!fnReadWrite) fnReadWrite = this.writeData;
if (!sFunc) sFunc = "WRITE";
iCylinder = (this.regRKDA & PDP11.RK11.RKDA.CA) >> PDP11.RK11.RKDA.SHIFT.CA;
iHead = (this.regRKDA & PDP11.RK11.RKDA.HS) >> PDP11.RK11.RKDA.SHIFT.HS;
iSector = this.regRKDA & PDP11.RK11.RKDA.SA;
nWords = (0x10000 - this.regRKWC) & 0xffff;
addr = (((this.regRKCS & PDP11.RK11.RKCS.MEX)) << (16 - PDP11.RK11.RKCS.SHIFT.MEX)) | this.regRKBA;
if (this.messageEnabled()) this.printMessage(this.type + ": " + sFunc + "(" + iCylinder + ":" + iHead + ":" + iSector + ") @" + str.toOct(addr) + "-" + str.toOct(addr + ((nWords - 1) << 1)), true);
if (!fnReadWrite) fnReadWrite = this.writeData;
if (iCylinder >= drive.nCylinders) {
this.regRKER |= PDP11.RK11.RKER.DRE | PDP11.RK11.RKER.NXC;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
@ -935,23 +962,24 @@ RK11.prototype.processCommand = function()
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
break;
}
addr = (((this.regRKCS & PDP11.RK11.RKCS.MEX)) << (16 - PDP11.RK11.RKCS.SHIFT.MEX)) | this.regRKBA;
nWords = (0x10000 - this.regRKWC) & 0xffff;
if (DEBUG && (this.messageEnabled(MessagesPDP11.READ) || this.messageEnabled(MessagesPDP11.WRITE))) {
var pos = ((((iCylinder << 1) + iHead) * drive.nSectors) + iSector) * 256;
console.log((fnReadWrite == this.readData? "readData" : "writeData") + "(pos=" + pos + ",addr=" + str.toOct(addr) + ",bytes=" + (nWords * 2) + ")");
}
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, this.endReadWrite.bind(this));
fInterrupt = fnReadWrite.call(this, drive, iCylinder, iHead, iSector, nWords, addr, (func >= PDP11.RK11.FUNC.WCHK), this.endReadWrite.bind(this));
break;
case PDP11.RK11.FUNC.DRESET:
if (this.messageEnabled()) this.printMessage(this.type + ": DRESET(" + iDrive + ")");
break;
default:
if (this.messageEnabled()) this.printMessage(this.type + ": UNSUPPORTED(" + func + ")");
break;
}
/*
* TODO: Determine what's up with the "regRKDA % 9"....
*/
this.regRKDS = drive.status | (iDrive << PDP11.RK11.RKDS.SHIFT.ID) | ((this.regRKDA % 9) & PDP11.RK11.RKDS.SC);
this.regRKDS = drive.status | (drive.disk? PDP11.RK11.RKDS.DRDY : 0) | (iDrive << PDP11.RK11.RKDS.SHIFT.ID) | (this.regRKDA & PDP11.RK11.RKDS.SC);
if (this.regRKER & PDP11.RK11.RKER.DRE) {
if (this.messageEnabled()) this.printMessage(this.type + ": ERROR: " + str.toOct(this.regRKER) + ")");
}
if (fInterrupt) {
this.regRKCS &= ~PDP11.RK11.RKCS.GO;
@ -977,6 +1005,7 @@ RK11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, a
this.regRKBA = addr & 0xffff;
this.regRKCS = (this.regRKCS & ~PDP11.RK11.RKCS.MEX) | ((addr >> (16 - PDP11.RK11.RKCS.SHIFT.MEX)) & PDP11.RK11.RKCS.MEX);
this.regRKWC = (0x10000 - nWords) & 0xffff;
this.regRKDA = (this.regRKDA & ~PDP11.RK11.RKDA.SA) | (iSector & PDP11.RK11.RKDA.SA);
if (err) {
this.regRKER |= err | PDP11.RK11.RKER.DRE;
this.regRKCS |= PDP11.RK11.RKCS.HE | PDP11.RK11.RKCS.ERR;
@ -985,7 +1014,7 @@ RK11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, a
};
/**
* readData(drive, iCylinder, iHead, iSector, nWords, addr, done)
* readData(drive, iCylinder, iHead, iSector, nWords, addr, fCheck, done)
*
* @this {RK11}
* @param {Object} drive
@ -994,10 +1023,11 @@ RK11.prototype.endReadWrite = function(err, iCylinder, iHead, iSector, nWords, a
* @param {number} iSector
* @param {number} nWords
* @param {number} addr
* @param {boolean} fCheck
* @param {function(...)} done
* @return {boolean} true if complete, false if queued
*/
RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, addr, done)
RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, addr, fCheck, done)
{
var err = 0;
var disk = drive.disk;
@ -1018,23 +1048,26 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
}
ibSector = 0;
}
var b0, b1, data;
var b0, b1;
if ((b0 = disk.read(sector, ibSector++)) < 0 || (b1 = disk.read(sector, ibSector++)) < 0) {
err = PDP11.RK11.RKER.NXS;
break;
}
this.bus.setWordDirect(addr, data = b0 | (b1 << 8));
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
if (!sWords) sWords = str.toOct(addr) + ": ";
sWords += str.toOct(data) + ' ';
if (sWords.length >= 64) {
console.log(sWords);
sWords = "";
if (!fCheck) {
var data = b0 | (b1 << 8);
this.bus.setWordDirect(addr, data);
if (DEBUG && this.messageEnabled(MessagesPDP11.READ)) {
if (!sWords) sWords = str.toOct(addr) + ": ";
sWords += str.toOct(data) + ' ';
if (sWords.length >= 64) {
console.log(sWords);
sWords = "";
}
}
if (this.bus.checkFault()) {
err = PDP11.RK11.RKER.NXM;
break;
}
}
if (this.bus.checkFault()) {
err = PDP11.RK11.RKER.NXM;
break;
}
addr += 2;
if (ibSector >= disk.cbSector) {
@ -1055,7 +1088,7 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
};
/**
* writeData(drive, iCylinder, iHead, iSector, nWords, addr, done)
* writeData(drive, iCylinder, iHead, iSector, nWords, addr, fCheck, done)
*
* @this {RK11}
* @param {Object} drive
@ -1064,10 +1097,11 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
* @param {number} iSector
* @param {number} nWords
* @param {number} addr
* @param {boolean} fCheck
* @param {function(...)} done
* @return {boolean} true if complete, false if queued
*/
RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, addr, done)
RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, addr, fCheck, done)
{
var err = 0;
var disk = drive.disk;
@ -1093,9 +1127,21 @@ RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
}
ibSector = 0;
}
if (!disk.write(sector, ibSector++, data & 0xff) || !disk.write(sector, ibSector++, data >> 8)) {
err = PDP11.RK11.RKER.NXS;
break;
if (fCheck) {
var b0, b1;
if ((b0 = disk.read(sector, ibSector++)) < 0 || (b1 = disk.read(sector, ibSector++)) < 0) {
err = PDP11.RK11.RKER.NXS;
break;
}
if (data != (b0 | (b1 << 8))) {
err = PDP11.RK11.RKER.WCE;
break;
}
} else {
if (!disk.write(sector, ibSector++, data & 0xff) || !disk.write(sector, ibSector++, data >> 8)) {
err = PDP11.RK11.RKER.NXS;
break;
}
}
if (ibSector >= disk.cbSector) {
sector = null;

View file

@ -140,15 +140,16 @@ str.parseInt = function(s, base)
};
/**
* toBin(n, cch)
* toBin(n, cch, grouping)
*
* Converts an integer to binary, with the specified number of digits (up to the default of 32).
*
* @param {number|null|undefined} n is a 32-bit value
* @param {number} [cch] is the desired number of binary digits (32 is both the default and the maximum)
* @param {number} [grouping]
* @return {string} the binary representation of n
*/
str.toBin = function(n, cch)
str.toBin = function(n, cch, grouping)
{
var s = "";
if (!cch) {
@ -164,13 +165,16 @@ str.toBin = function(n, cch)
* since JavaScript coerces such operands to zero, but I think there's "value" in seeing those
* values displayed differently.
*/
if (n == null || isNaN(n)) {
while (cch-- > 0) s = '?' + s;
} else {
while (cch-- > 0) {
s = ((n & 0x1)? '1' : '0') + s;
n >>= 1;
var fInvalid = (n == null || isNaN(n));
var group = (grouping = grouping || cch);
while (cch-- > 0) {
if (!group) {
s = "," + s;
group = grouping;
}
s = (fInvalid? '?' : ((n & 0x1)? '1' : '0')) + s;
n >>= 1;
group--;
}
return s;
};