Merge branch 'next-release'
This commit is contained in:
commit
6b204b1747
18 changed files with 964 additions and 652 deletions
|
|
@ -211,7 +211,7 @@ BusPDP11.IOHANDLER = {
|
|||
*
|
||||
* Those fallbacks may not always be appropriate; for example, byte writes to some device registers
|
||||
* must be zero-extended to update the entire word. For those cases, the fallback's "preliminary" read
|
||||
* is issued with a zero address so that the handler can distinguish a normal read from one of these
|
||||
* is issued with a fPreWrite flag so that the handler can distinguish a normal read from one of these
|
||||
* preliminary reads (aka read-before-write), and return an appropriate value for the update (eg, zero).
|
||||
*
|
||||
* If none of these fallback behaviors are appropriate, the device has a simple recourse: register
|
||||
|
|
|
|||
|
|
@ -1243,15 +1243,15 @@ PDP11.JSR_CYCLES = [
|
|||
PDP11.opJSR = function(opCode)
|
||||
{
|
||||
/*
|
||||
* Since JMP and JSR opcodes have their own unique timings for the various dst modes, we must snapshot
|
||||
* nStepCycles before decoding the mode, and then use that to update nStepCycles.
|
||||
* Since JMP and JSR opcodes have their own unique timings for the various dst modes, we must
|
||||
* snapshot nStepCycles before decoding the mode, and then use that to update nStepCycles.
|
||||
*/
|
||||
this.nSnapCycles = this.nStepCycles;
|
||||
/*
|
||||
* TODO: Determine whether or not the SRCMODE operand (regsGen[reg]) should be snapped BEFORE or AFTER we
|
||||
* decode the DSTMODE operand. Doing it AFTER seems a bit risky.
|
||||
*/
|
||||
var addr = this.readDstAddr(opCode);
|
||||
/*
|
||||
* As per the WARNING in readSrcWord(), reading the SRC register AFTER decoding the DST operand
|
||||
* is entirely appropriate.
|
||||
*/
|
||||
var reg = (opCode >> PDP11.SRCMODE.SHIFT) & PDP11.OPREG.MASK;
|
||||
this.pushWord(this.regsGen[reg]);
|
||||
this.regsGen[reg] = this.getPC();
|
||||
|
|
@ -1883,7 +1883,11 @@ PDP11.opWAIT = function(opCode)
|
|||
PDP11.opXOR = function(opCode)
|
||||
{
|
||||
var reg = (opCode >> PDP11.SRCMODE.SHIFT) & PDP11.OPREG.MASK;
|
||||
this.updateDstWord(opCode, this.regsGen[reg], PDP11.fnXOR);
|
||||
/*
|
||||
* As per the WARNING in readSrcWord(), we must supply a register number rather than a register value,
|
||||
* which will then be evaluated by updateDstWord() after both the SRC and DST operands have been decoded.
|
||||
*/
|
||||
this.updateDstWord(opCode, -reg-1 /* this.regsGen[reg] */, PDP11.fnXOR);
|
||||
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -125,8 +125,10 @@ CPUStatePDP11.prototype.initProcessor = function()
|
|||
{
|
||||
if (this.model == PDP11.MODEL_1120) {
|
||||
this.decode = PDP11.op1120.bind(this);
|
||||
this.checkStackLimit = this.checkStackLimit1120;
|
||||
} else {
|
||||
this.decode = PDP11.op1145.bind(this);
|
||||
this.checkStackLimit = this.checkStackLimit1145;
|
||||
}
|
||||
|
||||
this.initRegs();
|
||||
|
|
@ -247,7 +249,7 @@ CPUStatePDP11.prototype.resetMMU = function()
|
|||
this.regMMR1 = 0; // 177574
|
||||
this.regMMR2 = 0; // 177576
|
||||
this.regMMR3 = 0; // 172516
|
||||
this.regErr = 0; // 177766
|
||||
this.regErr = 0; // 177766 TODO: Do we ever need to clear this, because it appears to accumulate errors...
|
||||
this.regPIR = 0; // 177772
|
||||
this.regSL = 0xff; // 177774
|
||||
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
|
||||
|
|
@ -1304,9 +1306,11 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
if (this.trapPSW < 0) {
|
||||
this.trapPSW = this.getPSW();
|
||||
} else if (!this.mmuMode) {
|
||||
/*
|
||||
* Double-fault (nested trap) condition detected.
|
||||
*/
|
||||
reason = PDP11.REASON.RED; // double-fault (nested trap) forces a RED condition
|
||||
}
|
||||
|
||||
var fRed = false;
|
||||
if (reason == PDP11.REASON.RED) {
|
||||
vector = 4;
|
||||
/*
|
||||
* The next two lines used to be deferred until after the setPSW() below, but
|
||||
|
|
@ -1314,7 +1318,7 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
*/
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
reason = PDP11.REASON.RED;
|
||||
fRed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1343,14 +1347,15 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
*/
|
||||
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.trapPSW >> 2) & PDP11.PSW.PMODE));
|
||||
|
||||
this.pushWord(this.trapPSW);
|
||||
this.pushWord(this.regsGen[7]);
|
||||
this.pushWord(this.trapPSW, fRed);
|
||||
this.pushWord(this.regsGen[7], fRed);
|
||||
this.setPC(newPC);
|
||||
|
||||
/*
|
||||
* DEC's "TRAP TEST" triggers a RESERVED trap with an invalid opcode and the stack deliberately
|
||||
* set too low, and expects the stack overflow trap to be "sprung" immediately afterward, so we
|
||||
* only want to "lose interest" in the TRAP flag(s) that were set on entry, not ALL of them.
|
||||
* DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB) triggers a RESERVED trap with an invalid opcode and the
|
||||
* stack deliberately set too low, and expects the stack overflow trap to be "sprung" immediately
|
||||
* afterward, so we only want to "lose interest" in the TRAP flag(s) that were set on entry, not ALL
|
||||
* of them.
|
||||
*
|
||||
* this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK; // lose interest in traps after an abort
|
||||
*
|
||||
|
|
@ -1488,12 +1493,12 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
|
|||
};
|
||||
|
||||
/**
|
||||
* mapVirtualToPhysical(virtualAddress, accessFlags)
|
||||
* mapVirtualToPhysical(virtualAddress, 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 (accessFlags). If there is no
|
||||
* (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.
|
||||
|
|
@ -1539,19 +1544,19 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
|
|||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} virtualAddress
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFlags)
|
||||
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
|
||||
{
|
||||
var page, pdr, physicalAddress;
|
||||
|
||||
this.assert(!(virtualAddress & ~0x1ffff) && accessFlags);
|
||||
this.assert(!(virtualAddress & ~0x1ffff) && access);
|
||||
|
||||
/*
|
||||
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but *not* the ENABLED bit.
|
||||
*/
|
||||
if (!(accessFlags & this.mmuEnable)) {
|
||||
if (!(access & this.mmuEnable)) {
|
||||
physicalAddress = virtualAddress & 0xffff;
|
||||
if (physicalAddress >= BusPDP11.IOPAGE_16BIT) {
|
||||
physicalAddress |= this.addrIOPage;
|
||||
|
|
@ -1572,13 +1577,24 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
* As an aside it turns out that it is the memory management unit that does odd address and
|
||||
* non-existent memory trapping: who knew? :-) I thought these would have been handled at access time.
|
||||
*
|
||||
* I haven't imported the non-existent memory checks he performed (yet); I would like to see the problems
|
||||
* in action first.
|
||||
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers an ODDADDR error using this
|
||||
* instruction:
|
||||
*
|
||||
* As for the ODDADDR error that's supposed to generate a BUS error rather than an MMU error, this happens
|
||||
* in TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076456).
|
||||
* 076356: 005037 140001 CLR @#140001
|
||||
*
|
||||
* and it expects that instruction to generate a BUS error rather than an MMU error, so we deal with that
|
||||
* next. However, the test also claims to check for an NEXM (non-existent memory) error, using this
|
||||
* instruction:
|
||||
*
|
||||
* 076170: 005037 140100 CLR @#140100
|
||||
*
|
||||
* but that error never materializes, so I've NOT included any NEXM checks yet. I assume that any such
|
||||
* checks would be limited to whatever's recorded in the Memory Size registers (177760), because actually
|
||||
* checking every physical address at this stage -- even in a real MMU -- seems prohibitively expensive.
|
||||
*
|
||||
* TODO: Investigate why the test's NEXM error doesn't occur.
|
||||
*/
|
||||
if ((physicalAddress & 0x1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
if ((physicalAddress & 0x1) && !(access & PDP11.ACCESS.BYTE)) {
|
||||
this.regErr |= PDP11.CPUERR.ODDADDR;
|
||||
this.trap(PDP11.TRAP.BUS, 0, physicalAddress);
|
||||
}
|
||||
|
|
@ -1592,7 +1608,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
|
||||
case PDP11.PDR.ACF.RO: // 0x2: read-only, abort on write attempt
|
||||
pdr |= PDP11.PDR.ACCESSED;
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
if (access & PDP11.ACCESS.WRITE) {
|
||||
newMMR0 = PDP11.MMR0.ABORT_RO;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1602,13 +1618,13 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
/* falls through */
|
||||
|
||||
case PDP11.PDR.ACF.RW2: // 0x5: read/write, memory management trap upon completion of a write (11/70 only)
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
if (access & PDP11.ACCESS.WRITE) {
|
||||
newMMR0 = PDP11.MMR0.TRAP_MMU;
|
||||
}
|
||||
/* falls through */
|
||||
|
||||
case PDP11.PDR.ACF.RW: // 0x6: read/write, no system trap/abort action
|
||||
pdr |= ((accessFlags & PDP11.ACCESS.WRITE) ? (PDP11.PDR.ACCESSED | PDP11.PDR.MODIFIED) : PDP11.PDR.ACCESSED);
|
||||
pdr |= ((access & PDP11.ACCESS.WRITE) ? (PDP11.PDR.ACCESSED | PDP11.PDR.MODIFIED) : PDP11.PDR.ACCESSED);
|
||||
break;
|
||||
|
||||
default: // 0x0 (non-resident, abort all accesses) or 0x3 or 0x7 (unused, abort all accesses)
|
||||
|
|
@ -1717,61 +1733,23 @@ CPUStatePDP11.prototype.popWord = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* pushWord(data)
|
||||
* pushWord(data, fRed)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} data
|
||||
* @param {boolean} [fRed]
|
||||
*/
|
||||
CPUStatePDP11.prototype.pushWord = function(data)
|
||||
CPUStatePDP11.prototype.pushWord = function(data, fRed)
|
||||
{
|
||||
var virtualAddress = (this.regsGen[6] - 2) & 0xffff;
|
||||
this.regsGen[6] = virtualAddress; // BSD needs SP updated before any fault :-(
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | (0x00f6 << 16);
|
||||
this.checkStackLimit(0, virtualAddress);
|
||||
if (!fRed) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, virtualAddress);
|
||||
this.writeWord(virtualAddress, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit(mode, addr)
|
||||
*
|
||||
* The special "mode 0" case used by pushWord() ignores addr <= 4, because pushWord() is used by trap(),
|
||||
* and if the trap() was generated by a RED error (below) or generated a RED error itself, then we need to
|
||||
* avoid triggering another (nested) RED error.
|
||||
*
|
||||
* TODO: pushWord() is not used exclusively by trap(); it's also used by a few instructions, like opJSR(),
|
||||
* so we might need to do some additional factoring of this function's logic.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode (ie, the addressing mode, or 0 if pushWord() is checking)
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
||||
{
|
||||
if (!this.mmuMode) {
|
||||
/*
|
||||
* NOTE: I've removed the tests below for addr >= 0xFFFE, which were ported from the original code,
|
||||
* because while it's definitely a bad physical stack address, I'm not sure it rises to the level of
|
||||
* a trap, and this code is already expensive enough as it is.
|
||||
*/
|
||||
if (addr <= this.regSL && (mode || addr > 4) /* || mode && addr >= 0xFFFE */) {
|
||||
if (this.model >= PDP11.MODEL_1145 && (addr <= this.regSL - 32 /* || mode == 1 && addr >= 0xFFFE */)) {
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.RED);
|
||||
} else {
|
||||
/*
|
||||
* On older machines (eg, the PDP-11/20), the instruction is always allowed to complete,
|
||||
* so the trap must always be issued in this fashion.
|
||||
*/
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getAddrByMode(mode, reg, accessFlags)
|
||||
* getAddrByMode(mode, reg, access)
|
||||
*
|
||||
* getAddrByMode() maps a six bit operand to a 17 bit I/D virtual address space.
|
||||
*
|
||||
|
|
@ -1798,7 +1776,14 @@ CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
|||
* --------------------
|
||||
* On the PDP-11/20, stack overflow traps occur when an address below 400 is referenced
|
||||
* by SP in either mode 4 (auto-decrement) or 5 (auto-decrement deferred). The instruction
|
||||
* is allowed to complete before the trap is issued.
|
||||
* is allowed to complete before the trap is issued. NOTE: This information comes
|
||||
* directly from the PDP-11/20 Handbook (1971), but the 11/20 diagnostics apparently only
|
||||
* test mode 4, not mode 5, because when I later removed stack limit checks for mode 5 on
|
||||
* the 11/70, none of the 11/20 tests complained.
|
||||
*
|
||||
* TODO: Find some independent confirmation as to whether ANY PDP-11 models check for
|
||||
* stack overflow on mode 5 (auto-decrement deferred); if they do, then further tweaks to
|
||||
* checkStackLimit functions may be required.
|
||||
*
|
||||
* On the PDP-11/70, the stack limit register (177774) allows a variable boundary for the
|
||||
* kernel stack.
|
||||
|
|
@ -1806,13 +1791,13 @@ CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
|||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
var virtualAddress, step;
|
||||
var addrDSpace = (accessFlags & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
|
||||
var addrDSpace = (access & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
|
||||
|
||||
/*
|
||||
* Modes that need to auto-increment or auto-decrement will break, in order to perform
|
||||
|
|
@ -1835,9 +1820,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
* Mode 1: (R)
|
||||
*/
|
||||
case 1:
|
||||
if (reg == 6 && (accessFlags & PDP11.ACCESS.WRITE)) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
if (reg == 6) this.checkStackLimit(access, 0, this.regsGen[6]);
|
||||
this.nStepCycles -= (2 + 1);
|
||||
return (reg == 7? this.regsGen[reg] : (this.regsGen[reg] | addrDSpace));
|
||||
|
||||
|
|
@ -1847,11 +1830,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 2:
|
||||
step = 2;
|
||||
virtualAddress = this.regsGen[reg];
|
||||
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
|
||||
if (reg != 7) {
|
||||
virtualAddress |= addrDSpace;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
step = 1;
|
||||
}
|
||||
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = 1;
|
||||
}
|
||||
this.nStepCycles -= (2 + 1);
|
||||
break;
|
||||
|
|
@ -1873,8 +1855,9 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
*/
|
||||
case 4:
|
||||
step = -2;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) step = -1;
|
||||
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = -1;
|
||||
virtualAddress = (this.regsGen[reg] + step) & 0xffff;
|
||||
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
|
||||
if (reg != 7) virtualAddress |= addrDSpace;
|
||||
this.nStepCycles -= (3 + 1);
|
||||
break;
|
||||
|
|
@ -1895,9 +1878,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
*/
|
||||
case 6:
|
||||
virtualAddress = this.readWord(this.advancePC(2));
|
||||
virtualAddress = ((virtualAddress + this.regsGen[reg]) & 0xffff) | addrDSpace;
|
||||
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
|
||||
if (reg == 6) this.checkStackLimit(access, 0, virtualAddress);
|
||||
this.nStepCycles -= (4 + 2);
|
||||
return virtualAddress;
|
||||
return virtualAddress | addrDSpace;
|
||||
|
||||
/*
|
||||
* Mode 7: @d(R)
|
||||
|
|
@ -1905,24 +1889,73 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 7:
|
||||
virtualAddress = this.readWord(this.advancePC(2));
|
||||
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
|
||||
virtualAddress = this.readWord(virtualAddress | this.addrDSpace) | addrDSpace;
|
||||
virtualAddress = this.readWord(virtualAddress | this.addrDSpace);
|
||||
this.nStepCycles -= (7 + 3);
|
||||
return virtualAddress;
|
||||
return virtualAddress | addrDSpace;
|
||||
}
|
||||
|
||||
this.regsGen[reg] = (this.regsGen[reg] + step) & 0xffff;
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | ((((step << 3) & 0xf8) | reg) << 16);
|
||||
|
||||
/*
|
||||
* NOTE: We had to eliminate the test for "(accessFlags & PDP11.ACCESS.WRITE)", because DEC's
|
||||
* "TRAP TEST" expects "TST -(SP)" to trap when SP is 150.
|
||||
*/
|
||||
if (reg == 6 && step < 0) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
return virtualAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit1120(access, step, addr)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} access
|
||||
* @param {number} step
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit1120 = function(access, step, addr)
|
||||
{
|
||||
/*
|
||||
* NOTE: DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB) expects "TST -(SP)" to trap when SP is 150,
|
||||
* so we ignore the access parameter. Also, strangely, it does NOT expect this instruction
|
||||
* to trap:
|
||||
*
|
||||
* R0=006302 R1=000000 R2=000000 R3=000000 R4=000000 R5=000776
|
||||
* SP=000000 PC=006346 PS=000344 IR=000000 SL=000377 T0 N0 Z1 V0 C0
|
||||
* 006346: 112667 171426 MOVB (SP)+,000000
|
||||
*
|
||||
* so if the step parameter is positive, we let it go.
|
||||
*/
|
||||
if (!this.mmuMode && 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.
|
||||
*/
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit1145(access, step, addr)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} access
|
||||
* @param {number} step
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit1145 = function(access, step, addr)
|
||||
{
|
||||
if (!this.mmuMode) {
|
||||
/*
|
||||
* NOTE: The 11/70 CPU Instruction Exerciser does NOT expect reads to trigger a stack overflow,
|
||||
* so we check the access parameter.
|
||||
*/
|
||||
if ((access & PDP11.ACCESS.WRITE) && addr <= this.regSL) {
|
||||
if (addr <= this.regSL - 32) {
|
||||
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.RED);
|
||||
} else {
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getByteDirect(addr)
|
||||
*
|
||||
|
|
@ -2008,35 +2041,35 @@ CPUStatePDP11.prototype.setWordDirect = function(addr, data)
|
|||
};
|
||||
|
||||
/**
|
||||
* getPhysicalAddrByMode(mode, reg, accessFlags)
|
||||
* getPhysicalAddrByMode(mode, reg, access)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through getAddr().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getPhysicalAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getPhysicalAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
return this.getAddrByMode(mode, reg, accessFlags);
|
||||
return this.getAddrByMode(mode, reg, access);
|
||||
};
|
||||
|
||||
/**
|
||||
* getVirtualAddrByMode(mode, reg, accessFlags)
|
||||
* getVirtualAddrByMode(mode, reg, access)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through getAddr().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
return this.mapVirtualToPhysical(this.getAddrByMode(mode, reg, accessFlags), accessFlags);
|
||||
return this.mapVirtualToPhysical(this.getAddrByMode(mode, reg, access), access);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2096,14 +2129,14 @@ CPUStatePDP11.prototype.writeWordToVirtual = function(virtualAddress, data)
|
|||
};
|
||||
|
||||
/**
|
||||
* readWordFromPrevSpace(opCode, accessFlags)
|
||||
* readWordFromPrevSpace(opCode, access)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {number} accessFlags (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} access (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, accessFlags)
|
||||
CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, access)
|
||||
{
|
||||
var data;
|
||||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
|
|
@ -2116,25 +2149,25 @@ CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, accessFlags)
|
|||
}
|
||||
} else {
|
||||
var addr = this.getAddrByMode(mode, reg, PDP11.ACCESS.READ_WORD);
|
||||
if (!(accessFlags & PDP11.ACCESS.DSPACE)) {
|
||||
if (!(access & PDP11.ACCESS.DSPACE)) {
|
||||
if ((this.regPSW & 0xf000) !== 0xf000) addr &= 0xffff;
|
||||
}
|
||||
this.mmuMode = (this.regPSW >> 12) & 3;
|
||||
data = this.readWord(addr | (accessFlags & this.addrDSpace));
|
||||
data = this.readWord(addr | (access & this.addrDSpace));
|
||||
this.mmuMode = (this.regPSW >> 14) & 3;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeWordToPrevSpace(opCode, accessFlags, data)
|
||||
* writeWordToPrevSpace(opCode, access, data)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {number} accessFlags (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} access (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} data
|
||||
*/
|
||||
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, data)
|
||||
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, access, data)
|
||||
{
|
||||
this.lastOp = (this.lastOp & 0xffff) | (0x0016 << 16);
|
||||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
|
|
@ -2147,7 +2180,7 @@ CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, dat
|
|||
}
|
||||
} else {
|
||||
var addr = this.getAddrByMode(mode, reg, PDP11.ACCESS.WRITE_WORD);
|
||||
if (!(accessFlags & PDP11.ACCESS.DSPACE)) addr &= 0xffff;
|
||||
if (!(access & PDP11.ACCESS.DSPACE)) addr &= 0xffff;
|
||||
/*
|
||||
* TODO: Consider replacing the following code with writeWord(), by adding optional mmuMode
|
||||
* parameters for each of the discrete mapVirtualToPhysical() and bus.setWord() operations, because
|
||||
|
|
@ -2155,7 +2188,7 @@ CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, dat
|
|||
* setMemoryAccess() handlers.
|
||||
*/
|
||||
this.mmuMode = (this.regPSW >> 12) & 3;
|
||||
addr = this.mapVirtualToPhysical(addr | (accessFlags & PDP11.ACCESS.DSPACE), PDP11.ACCESS.WRITE);
|
||||
addr = this.mapVirtualToPhysical(addr | (access & PDP11.ACCESS.DSPACE), PDP11.ACCESS.WRITE);
|
||||
this.mmuMode = (this.regPSW >> 14) & 3;
|
||||
this.bus.setWord(addr, data);
|
||||
}
|
||||
|
|
@ -2195,7 +2228,7 @@ CPUStatePDP11.prototype.readSrcByte = function(opCode)
|
|||
* been decoded and any pre-decrement or post-increment operations affecting the SRC register have
|
||||
* been completed.
|
||||
*
|
||||
* Here's an example from DEC's "TRAP TEST":
|
||||
* Here's an example from DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB):
|
||||
*
|
||||
* 007200: 012700 006340 MOV #6340,R0
|
||||
* 007204: 010020 MOV R0,(R0)+
|
||||
|
|
|
|||
|
|
@ -69,41 +69,6 @@ function DevicePDP11(parmsDevice)
|
|||
|
||||
Component.subclass(DevicePDP11);
|
||||
|
||||
DevicePDP11.M9312 = [
|
||||
0x101F, /*0010037*/ 0x01C0, /*0000700*/ 0x105F, /*0010137*/ 0x01C2, /*0000702*/ 0x111F, /*0010437*/ 0x01C4, /*0000704*/ 0x0A1F, /*0005037*/ 0x01C6, /*0000706*/
|
||||
0x55DF, /*0052737*/ 0x8000, /*0100000*/ 0xFFFE, /*0177776*/ 0x35DF, /*0032737*/ 0x4000, /*0040000*/ 0xFFFE, /*0177776*/ 0x0302, /*0001402*/ 0x0A9F, /*0005237*/
|
||||
0x01C6, /*0000706*/ 0x0A1F, /*0005037*/ 0xFFFE, /*0177776*/ 0x0101, /*0000401*/ 0x0000, /*0000000*/ 0x0A06, /*0005006*/ 0x8104, /*0100404*/ 0x8503, /*0102403*/
|
||||
0x8202, /*0101002*/ 0x0501, /*0002401*/ 0x8301, /*0101401*/ 0x0000, /*0000000*/ 0x0AC6, /*0005306*/ 0x8003, /*0100003*/ 0x0302, /*0001402*/ 0x0401, /*0002001*/
|
||||
0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x0C06, /*0006006*/ 0x8402, /*0102002*/ 0x8601, /*0103001*/ 0x0201, /*0001001*/ 0x0000, /*0000000*/ 0x15C6, /*0012706*/
|
||||
0xAAAA, /*0125252*/ 0x1180, /*0010600*/ 0x1001, /*0010001*/ 0x1042, /*0010102*/ 0x1083, /*0010203*/ 0x10C4, /*0010304*/ 0x1105, /*0010405*/ 0xE141, /*0160501*/
|
||||
0x0501, /*0002401*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0C42, /*0006102*/ 0x8601, /*0103001*/ 0x0501, /*0002401*/ 0x0000, /*0000000*/ 0x6083, /*0060203*/
|
||||
0x0A83, /*0005203*/ 0x0A43, /*0005103*/ 0x60C1, /*0060301*/ 0x8701, /*0103401*/ 0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x0C04, /*0006004*/ 0x5103, /*0050403*/
|
||||
0x6143, /*0060503*/ 0x0A83, /*0005203*/ 0x8702, /*0103402*/ 0x0AC1, /*0005301*/ 0x0501, /*0002401*/ 0x0000, /*0000000*/ 0x0A40, /*0005100*/ 0x8301, /*0101401*/
|
||||
0x0000, /*0000000*/ 0x4001, /*0040001*/ 0x6041, /*0060101*/ 0x0601, /*0003001*/ 0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x00C1, /*0000301*/ 0x2057, /*0020127*/
|
||||
0x5455, /*0052125*/ 0x0204, /*0001004*/ 0x3105, /*0030405*/ 0x0602, /*0003002*/ 0x0A45, /*0005105*/ 0x0201, /*0001001*/ 0x0000, /*0000000*/ 0x95C0, /*0112700*/
|
||||
0xFF01, /*0177401*/ 0x8001, /*0100001*/ 0x0000, /*0000000*/ 0x7E02, /*0077002*/ 0x0A01, /*0005001*/ 0x0A81, /*0005201*/ 0x7E02, /*0077002*/ 0x0BC0, /*0005700*/
|
||||
0x0202, /*0001002*/ 0x0BC1, /*0005701*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x15C6, /*0012706*/ 0x01FE, /*0000776*/ 0x09F7, /*0004767*/ 0x0002, /*0000002*/
|
||||
0x0000, /*0000000*/ 0x25CE, /*0022716*/ 0x00D0, /*0000320*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x15CE, /*0012716*/ 0x00E2, /*0000342*/ 0x0087, /*0000207*/
|
||||
0x0000, /*0000000*/ 0x0A26, /*0005046*/ 0x15E6, /*0012746*/ 0x00EC, /*0000354*/ 0x0002, /*0000002*/ 0x0000, /*0000000*/ 0x005F, /*0000137*/ 0x00F2, /*0000362*/
|
||||
0x0080, /*0000200*/ 0x15C5, /*0012705*/ 0xE000, /*0160000*/ 0x0A1F, /*0005037*/ 0x0006, /*0000006*/ 0x15DF, /*0012737*/ 0x0100, /*0000400*/ 0x0004, /*0000004*/
|
||||
0x15C6, /*0012706*/ 0x01FE, /*0000776*/ 0x0BE5, /*0005745*/ 0x15DF, /*0012737*/ 0x01CC, /*0000714*/ 0x004C, /*0000114*/ 0x0A1F, /*0005037*/ 0x004E, /*0000116*/
|
||||
0x15C3, /*0012703*/ 0xFFE6, /*0177746*/ 0x15CB, /*0012713*/ 0x000C, /*0000014*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x1080, /*0010200*/ 0x1008, /*0010010*/
|
||||
0x0BD0, /*0005720*/ 0x2005, /*0020005*/ 0x83FC, /*0101774*/ 0x1080, /*0010200*/ 0x1201, /*0011001*/ 0x2001, /*0020001*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/
|
||||
0x0A50, /*0005120*/ 0x2005, /*0020005*/ 0x83F9, /*0101771*/ 0x1801, /*0014001*/ 0x0A41, /*0005101*/ 0x2001, /*0020001*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/
|
||||
0x2002, /*0020002*/ 0x02F9, /*0001371*/ 0x0A0E, /*0005016*/ 0x15C4, /*0012704*/ 0xAAAA, /*0125252*/ 0x15CB, /*0012713*/ 0x0018, /*0000030*/ 0x15C0, /*0012700*/
|
||||
0x0800, /*0004000*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x0A44, /*0005104*/ 0x1108, /*0010410*/ 0x0A48, /*0005110*/ 0x0A48, /*0005110*/ 0x2204, /*0021004*/
|
||||
0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0C1F, /*0006037*/ 0xFFEA, /*0177752*/ 0x8702, /*0103402*/ 0x0000, /*0000000*/ 0x0131, /*0000461*/ 0x8A4E, /*0105116*/
|
||||
0x02F2, /*0001362*/ 0x0102, /*0000402*/ 0x0077, /*0000167*/ 0xFE88, /*0177210*/ 0x0BD0, /*0005720*/ 0x7E93, /*0077223*/ 0x15CB, /*0012713*/ 0x0024, /*0000044*/
|
||||
0x15C0, /*0012700*/ 0x0C00, /*0006000*/ 0x8A76, /*0105166*/ 0x0001, /*0000001*/ 0x02E4, /*0001344*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x1080, /*0010200*/
|
||||
0x1008, /*0010010*/ 0x0BD0, /*0005720*/ 0x2005, /*0020005*/ 0x83FC, /*0101774*/ 0x15C1, /*0012701*/ 0x0003, /*0000003*/ 0x0A0E, /*0005016*/ 0x0BDF, /*0005737*/
|
||||
0x01C6, /*0000706*/ 0x0210, /*0001020*/ 0x15CE, /*0012716*/ 0x0018, /*0000030*/ 0x1080, /*0010200*/ 0x0A48, /*0005110*/ 0x0A48, /*0005110*/ 0x2008, /*0020010*/
|
||||
0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0BD0, /*0005720*/ 0x0C1F, /*0006037*/ 0xFFEA, /*0177752*/ 0x8702, /*0103402*/ 0x0000, /*0000000*/ 0x0108, /*0000410*/
|
||||
0x2005, /*0020005*/ 0x83F3, /*0101763*/ 0x138B, /*0011613*/ 0x0A0E, /*0005016*/ 0x7E51, /*0077121*/ 0x0104, /*0000404*/ 0x0000, /*0000000*/ 0x0102, /*0000402*/
|
||||
0x15CB, /*0012713*/ 0x000C, /*0000014*/ 0x17C0, /*0013700*/ 0x01C0, /*0000700*/ 0x17C1, /*0013701*/ 0x01C2, /*0000702*/ 0x17C4, /*0013704*/ 0x01C4, /*0000704*/
|
||||
0x0074, /*0000164*/ 0x0002, /*0000002*/ 0x17C4, /*0013704*/ 0xFF78, /*0177570*/ 0x45C4, /*0042704*/ 0xFE00, /*0177000*/ 0x55C4, /*0052704*/ 0xF600, /*0173000*/
|
||||
0x97C0, /*0113700*/ 0xFF79, /*0177571*/ 0x0C80, /*0006200*/ 0x00A1, /*0000241*/ 0x004C, /*0000114*/ 0x0000, /*0000000*/ 0x4230, /*0041060*/ 0x2A2D /*0025055*/
|
||||
];
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ var MessagesPDP11 = {
|
|||
KEYS: 0x00020000,
|
||||
PAPER: 0x00100000,
|
||||
DISK: 0x00400000,
|
||||
SERIAL: 0x00800000,
|
||||
RL11: 0x00800000,
|
||||
SERIAL: 0x01000000,
|
||||
SPEAKER: 0x02000000,
|
||||
COMPUTER: 0x04000000,
|
||||
LOG: 0x10000000,
|
||||
|
|
@ -83,6 +84,7 @@ MessagesPDP11.CATEGORIES = {
|
|||
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"paper": MessagesPDP11.PAPER,
|
||||
"disk": MessagesPDP11.DISK,
|
||||
"rl11": MessagesPDP11.RL11,
|
||||
"serial": MessagesPDP11.SERIAL,
|
||||
"speaker": MessagesPDP11.SPEAKER,
|
||||
"computer": MessagesPDP11.COMPUTER,
|
||||
|
|
|
|||
|
|
@ -308,11 +308,11 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
}
|
||||
var offBlock = off;
|
||||
if (w != 0x0001) {
|
||||
if (MAXDEBUG) this.println("invalid signature (" + str.toHexWord(w) + ") at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid signature (" + str.toHexWord(w) + ") at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
if (off + 6 >= aBytes.length) {
|
||||
if (MAXDEBUG) this.println("invalid block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
off += 2;
|
||||
|
|
@ -326,12 +326,12 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
len--;
|
||||
}
|
||||
if (len != 0 || off >= aBytes.length) {
|
||||
if (MAXDEBUG) this.println("insufficient data for block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("insufficient data for block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
checksum += aBytes[off++] & 0xff;
|
||||
if (checksum & 0xff) {
|
||||
if (MAXDEBUG) this.println("invalid checksum (" + str.toHexByte(checksum) + ") for block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid checksum (" + str.toHexByte(checksum) + ") for block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
if (!cbData) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ if (NODE) {
|
|||
*/
|
||||
function RL11(parms)
|
||||
{
|
||||
Component.call(this, "RL11", parms, RL11, MessagesPDP11.DISK);
|
||||
Component.call(this, "RL11", parms, RL11, MessagesPDP11.RL11);
|
||||
|
||||
/*
|
||||
* We record any 'autoMount' object now, but we no longer parse it until initBus(),
|
||||
|
|
@ -963,6 +963,8 @@ RL11.prototype.processCommand = function()
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* endReadWrite(err, iCylinder, iHead, iSector, nWords, addr)
|
||||
*
|
||||
|
|
@ -1027,7 +1029,7 @@ RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
|
|||
err = PDP11.RL11.ERRC.HNF;
|
||||
break;
|
||||
}
|
||||
var data = this.bus.setWord(addr, b0 | (b1 << 8));
|
||||
var data = this.bus.setWordDirect(addr, b0 | (b1 << 8));
|
||||
if (this.bus.checkFault()) {
|
||||
err = PDP11.RL11.ERRC.NXM;
|
||||
break;
|
||||
|
|
@ -1075,7 +1077,7 @@ RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
|
|||
}
|
||||
|
||||
while (nWords--) {
|
||||
var data = this.bus.getWord(addr);
|
||||
var data = this.bus.getWordDirect(addr);
|
||||
if (this.bus.checkFault()) {
|
||||
err = PDP11.RL11.ERRC.NXM;
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in a new issue