Merge branch 'next-release'
This commit is contained in:
commit
ccac473dec
13 changed files with 847 additions and 672 deletions
|
|
@ -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.
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue