Merge branch 'master' into gh-pages
This commit is contained in:
commit
9f817f79d2
13 changed files with 847 additions and 672 deletions
|
|
@ -256,7 +256,7 @@ MD-11 11/70 CPU EXERCISER
|
|||
-------------------------
|
||||
|
||||
When started in a [PDP-11/70](/devices/pdp11/machine/1170/panel/debugger/cpuexer/) with no disk drives attached,
|
||||
this diagnostic displays the following startup information:
|
||||
this diagnostic displays the following startup information ("EXERCISOR" is DEC's typo, not mine):
|
||||
|
||||
MAINDEC-11-DEQKC-B...PDP 11/70 CPU EXERCISOR
|
||||
OPT.CP=145406
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,12 +1048,14 @@ 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 (!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) + ' ';
|
||||
|
|
@ -1036,6 +1068,7 @@ RK11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
|
|||
err = PDP11.RK11.RKER.NXM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
addr += 2;
|
||||
if (ibSector >= disk.cbSector) {
|
||||
sector = null;
|
||||
|
|
@ -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,10 +1127,22 @@ RK11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
|
|||
}
|
||||
ibSector = 0;
|
||||
}
|
||||
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;
|
||||
if (++iSector >= disk.nSectors) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
var fInvalid = (n == null || isNaN(n));
|
||||
var group = (grouping = grouping || cch);
|
||||
while (cch-- > 0) {
|
||||
s = ((n & 0x1)? '1' : '0') + s;
|
||||
n >>= 1;
|
||||
if (!group) {
|
||||
s = "," + s;
|
||||
group = grouping;
|
||||
}
|
||||
s = (fInvalid? '?' : ((n & 0x1)? '1' : '0')) + s;
|
||||
n >>= 1;
|
||||
group--;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue