Merge branch 'next-release'
This commit is contained in:
commit
7b989b6306
57 changed files with 3539 additions and 559 deletions
|
|
@ -1398,7 +1398,7 @@ BusPDP11.prototype.fault = function(addr, err, access)
|
|||
this.dbg.printMessage("memory fault (" + access + ") on address " + this.dbg.toStrBase(addr), true, true);
|
||||
}
|
||||
if (err) this.cpu.regErr |= err;
|
||||
this.cpu.trap(PDP11.TRAP.BUS_ERROR, addr);
|
||||
this.cpu.trap(PDP11.TRAP.BUS_ERROR, 0, addr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -959,9 +959,12 @@ CPUPDP11.prototype.setTimer = function(iTimer, ms, fReset)
|
|||
* We must now confront the following problem: if the CPU is currently executing a burst of cycles,
|
||||
* the number of cycles it has executed in that burst so far must NOT be charged against the cycle
|
||||
* timeout we're about to set. The simplest way to resolve that is to immediately call endBurst()
|
||||
* and bias the above cycle timeout by the number of cycles that the burst executed.
|
||||
* and bias the cycle timeout by the number of cycles that the burst executed.
|
||||
*/
|
||||
this.aTimers[iTimer][0] = nCycles + this.endBurst();
|
||||
if (this.flags.running) {
|
||||
nCycles += this.endBurst();
|
||||
}
|
||||
this.aTimers[iTimer][0] = nCycles;
|
||||
}
|
||||
}
|
||||
return nCycles;
|
||||
|
|
@ -992,6 +995,7 @@ CPUPDP11.prototype.getBurstCycles = function(nCycles)
|
|||
{
|
||||
for (var i = this.aTimers.length - 1; i >= 0; i--) {
|
||||
var timer = this.aTimers[i];
|
||||
this.assert(!isNaN(timer[0]));
|
||||
if (timer[0] < 0) continue;
|
||||
if (nCycles > timer[0]) {
|
||||
nCycles = timer[0];
|
||||
|
|
@ -1014,6 +1018,7 @@ CPUPDP11.prototype.updateTimers = function(nCycles)
|
|||
{
|
||||
for (var i = this.aTimers.length - 1; i >= 0; i--) {
|
||||
var timer = this.aTimers[i];
|
||||
this.assert(!isNaN(timer[0]));
|
||||
if (timer[0] < 0) continue;
|
||||
timer[0] -= nCycles;
|
||||
if (timer[0] <= 0) {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ PDP11.fnASR = function(src, dst)
|
|||
*/
|
||||
PDP11.fnASRB = function(src, dst)
|
||||
{
|
||||
var result = (dst & 0x800) | (dst >> 1) | (dst << 8);
|
||||
var result = (dst & 0x80) | (dst >> 1) | (dst << 8);
|
||||
this.updateShiftFlags(result << 8);
|
||||
return result & 0xff;
|
||||
};
|
||||
|
|
@ -827,7 +827,7 @@ PDP11.opBPL = function(opCode)
|
|||
*/
|
||||
PDP11.opBPT = function(opCode)
|
||||
{
|
||||
this.trap(PDP11.TRAP.BPT, PDP11.REASON.BPT);
|
||||
this.trap(PDP11.TRAP.BPT, 0, PDP11.REASON.BPT);
|
||||
this.nStepCycles -= (4 + 1);
|
||||
};
|
||||
|
||||
|
|
@ -1101,7 +1101,7 @@ PDP11.opDIV = function(opCode)
|
|||
*/
|
||||
PDP11.opEMT = function(opCode)
|
||||
{
|
||||
this.trap(PDP11.TRAP.EMT, PDP11.REASON.EMT);
|
||||
this.trap(PDP11.TRAP.EMT, 0, PDP11.REASON.EMT);
|
||||
this.nStepCycles -= (22 + 3);
|
||||
};
|
||||
|
||||
|
|
@ -1115,8 +1115,11 @@ PDP11.opHALT = function(opCode)
|
|||
{
|
||||
if (this.regPSW & PDP11.PSW.CMODE) {
|
||||
this.regErr |= PDP11.CPUERR.BADHALT;
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.HALT);
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, 0, PDP11.REASON.HALT);
|
||||
} else {
|
||||
if (this.panel) {
|
||||
this.panel.setData(this.regsGen[0], true);
|
||||
}
|
||||
if (!this.dbg) {
|
||||
/*
|
||||
* This will leave the PC exactly where it's supposed to be: at the address of the HALT + 2.
|
||||
|
|
@ -1171,7 +1174,7 @@ PDP11.opINCB = function(opCode)
|
|||
*/
|
||||
PDP11.opIOT = function(opCode)
|
||||
{
|
||||
this.trap(PDP11.TRAP.IOT, PDP11.REASON.IOT);
|
||||
this.trap(PDP11.TRAP.IOT, 0, PDP11.REASON.IOT);
|
||||
this.nStepCycles -= (22 + 3);
|
||||
};
|
||||
|
||||
|
|
@ -1291,7 +1294,7 @@ PDP11.opMFPT = function(opCode)
|
|||
/*
|
||||
* TODO: Review
|
||||
*/
|
||||
this.trap(PDP11.TRAP.RESERVED, PDP11.REASON.RESERVED);
|
||||
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.RESERVED);
|
||||
};
|
||||
|
||||
PDP11.MOV_CYCLES = [
|
||||
|
|
@ -1713,7 +1716,7 @@ PDP11.opSXT = function(opCode)
|
|||
*/
|
||||
PDP11.opTRAP = function(opCode)
|
||||
{
|
||||
this.trap(PDP11.TRAP.TRAP, PDP11.REASON.TRAP);
|
||||
this.trap(PDP11.TRAP.TRAP, 0, PDP11.REASON.TRAP);
|
||||
this.nStepCycles -= (4 + 1);
|
||||
};
|
||||
|
||||
|
|
@ -1823,7 +1826,7 @@ PDP11.opUndefined = function(opCode)
|
|||
if (DEBUGGER && this.dbg) {
|
||||
if (this.dbg.undefinedInstruction(opCode)) return;
|
||||
}
|
||||
this.trap(PDP11.TRAP.RESERVED, PDP11.REASON.RESERVED);
|
||||
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.RESERVED);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ CPUStatePDP11.prototype.dispatchInterrupt = function(vector, priority)
|
|||
this.advancePC(2);
|
||||
this.opFlags &= ~PDP11.OPFLAG.WAIT;
|
||||
}
|
||||
this.trap(vector, PDP11.REASON.INTERRUPT);
|
||||
this.trap(vector, 0, PDP11.REASON.INTERRUPT);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1083,6 +1083,13 @@ CPUStatePDP11.prototype.updateMulFlags = function(result)
|
|||
/*
|
||||
* NOTE: Technically, the MUL instruction doesn't need to worry about NO_FLAGS, because that instruction
|
||||
* doesn't write to the bus, and therefore can't modify the PSW directly. But it doesn't hurt to be consistent.
|
||||
*
|
||||
* TODO: Conduct a review of all opcode handlers, because one possible alternative to all this NO_FLAGS nonsense
|
||||
* would be to pass the appropriate flag update function to the writeDstByte()/writeDstWord() functions, and
|
||||
* have them update the flags BEFORE the write occurs, thus allowing any subsequent write to the PSW to be honored.
|
||||
*
|
||||
* That already happens automatically with the updateDstByte()/updateDstWord() functions, since generally the
|
||||
* specified modify function also updates the flags BEFORE the write occurs.
|
||||
*/
|
||||
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
|
||||
this.flagN = result >> 16;
|
||||
|
|
@ -1139,7 +1146,7 @@ CPUStatePDP11.prototype.panic = function(reason)
|
|||
};
|
||||
|
||||
/**
|
||||
* trap(vector, reason)
|
||||
* trap(vector, flag, reason)
|
||||
*
|
||||
* trap() handles all the trap/abort functions. It reads the trap vector from kernel
|
||||
* D space, changes mode to reflect the new PSW and PC, and then pushes the old PSW and
|
||||
|
|
@ -1147,9 +1154,10 @@ CPUStatePDP11.prototype.panic = function(reason)
|
|||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} vector
|
||||
* @param {number} flag
|
||||
* @param {number} [reason] (for diagnostic purposes only)
|
||||
*/
|
||||
CPUStatePDP11.prototype.trap = function(vector, reason)
|
||||
CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
||||
{
|
||||
if (DEBUG && this.dbg) {
|
||||
if (this.messageEnabled(MessagesPDP11.TRAP)) {
|
||||
|
|
@ -1194,8 +1202,15 @@ CPUStatePDP11.prototype.trap = function(vector, reason)
|
|||
this.pushWord(this.regsGen[7]);
|
||||
this.setPC(newPC);
|
||||
|
||||
this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK; // lose interest in traps after an abort
|
||||
this.trapPSW = -1; // reset flag that we have a trap within a trap
|
||||
/*
|
||||
* Since DEC's "TRAP TEST" triggers a RESERVED (instruction) trap with the stack deliberately
|
||||
* set too low, and expects the stack overflow trap to be "sprung" immediately afterward, we only
|
||||
* want to "lose interest" in TRAP flags that were set on entry.
|
||||
*
|
||||
* this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK; // lose interest in traps after an abort
|
||||
*/
|
||||
this.opFlags &= ~flag;
|
||||
this.trapPSW = -1; // reset flag that we have a trap within a trap
|
||||
|
||||
/*
|
||||
* These next properties are purely an aid for the Debugger; see getTrapStatus()
|
||||
|
|
@ -1435,7 +1450,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
}
|
||||
}
|
||||
if (fTrap) { // don't trap until the end, because it throws an exception
|
||||
this.trap(PDP11.TRAP.MMU, PDP11.REASON.MAPERROR);
|
||||
this.trap(PDP11.TRAP.MMU, 0, PDP11.REASON.MAPERROR);
|
||||
}
|
||||
}
|
||||
return physicalAddress;
|
||||
|
|
@ -1494,18 +1509,35 @@ CPUStatePDP11.prototype.pushWord = function(data)
|
|||
if (!(this.regMMR0 & 0xe000)) {
|
||||
this.regMMR1 = (this.regMMR1 << 8) | 0xf6;
|
||||
}
|
||||
this.checkStackLimit(0, virtualAddress);
|
||||
this.writeWord(virtualAddress, data);
|
||||
};
|
||||
|
||||
if ((!this.mmuMode) && virtualAddress <= this.regSL && virtualAddress > 4) {
|
||||
if (virtualAddress <= this.regSL - 32) {
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.PUSHERROR);
|
||||
} else {
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= 4;
|
||||
/**
|
||||
* checkStackLimit(mode, addr)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode (ie, the addressing mode; 0 if push)
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
||||
{
|
||||
if (!this.mmuMode && !(this.opFlags & PDP11.OPFLAG.TRAP_SP)) {
|
||||
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_ERROR, 0, PDP11.REASON.STACKMODE1);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.writeWord(virtualAddress, data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1532,6 +1564,15 @@ CPUStatePDP11.prototype.pushWord = function(data)
|
|||
* incremented and decremented so that the OS can reset and restart an instruction
|
||||
* if a page fault occurs.
|
||||
*
|
||||
* Stack Overflow Traps
|
||||
* --------------------
|
||||
* 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.
|
||||
*
|
||||
* On the PDP-11/70, the stack limit register (177774) allows a variable boundary for the
|
||||
* kernel stack.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
|
|
@ -1544,8 +1585,8 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
var addrDSpace = (accessFlags & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
|
||||
|
||||
/*
|
||||
* Modes that need to auto-increment or auto-decrement will break, in order to perform the
|
||||
* update; others will return an address immediately.
|
||||
* Modes that need to auto-increment or auto-decrement will break, in order to perform
|
||||
* the update; others will return an address immediately.
|
||||
*/
|
||||
switch (mode) {
|
||||
/*
|
||||
|
|
@ -1556,26 +1597,18 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
* "cause an 'illegal' instruction" condition", which presumably means a BUS_ERROR trap.
|
||||
*/
|
||||
case 0:
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.NOREGADDR);
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, 0, PDP11.REASON.NOREGADDR);
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Mode 1: (R)
|
||||
*/
|
||||
case 1:
|
||||
if (reg === 6 && (!this.mmuMode) && (accessFlags & PDP11.ACCESS.WRITE) &&
|
||||
(this.regsGen[6] <= this.regSL || this.regsGen[6] >= 0xfffe)) {
|
||||
if (this.regsGen[6] <= this.regSL - 32 || this.regsGen[6] >= 0xfffe) {
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.STACKMODE1);
|
||||
} else {
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
if (reg == 6 && (accessFlags & PDP11.ACCESS.WRITE)) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
this.nStepCycles -= (2 + 1);
|
||||
return (reg === 7? this.regsGen[reg] : (this.regsGen[reg] | addrDSpace));
|
||||
return (reg == 7? this.regsGen[reg] : (this.regsGen[reg] | addrDSpace));
|
||||
|
||||
/*
|
||||
* Mode 2: (R)+
|
||||
|
|
@ -1583,7 +1616,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 2:
|
||||
stepSize = 2;
|
||||
virtualAddress = this.regsGen[reg];
|
||||
if (reg !== 7) {
|
||||
if (reg != 7) {
|
||||
virtualAddress |= addrDSpace;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
stepSize = 1;
|
||||
|
|
@ -1598,7 +1631,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 3:
|
||||
stepSize = 2;
|
||||
virtualAddress = this.regsGen[reg];
|
||||
if (reg !== 7) virtualAddress |= addrDSpace;
|
||||
if (reg != 7) virtualAddress |= addrDSpace;
|
||||
virtualAddress = this.readWord(virtualAddress);
|
||||
virtualAddress |= addrDSpace;
|
||||
this.nStepCycles -= (5 + 2);
|
||||
|
|
@ -1611,7 +1644,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
stepSize = -2;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) stepSize = -1;
|
||||
virtualAddress = (this.regsGen[reg] + stepSize) & 0xffff;
|
||||
if (reg !== 7) virtualAddress |= addrDSpace;
|
||||
if (reg != 7) virtualAddress |= addrDSpace;
|
||||
this.nStepCycles -= (3 + 1);
|
||||
break;
|
||||
|
||||
|
|
@ -1621,7 +1654,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 5:
|
||||
stepSize = -2;
|
||||
virtualAddress = (this.regsGen[reg] - 2) & 0xffff;
|
||||
if (reg !== 7) virtualAddress |= addrDSpace;
|
||||
if (reg != 7) virtualAddress |= addrDSpace;
|
||||
virtualAddress = this.readWord(virtualAddress) | addrDSpace;
|
||||
this.nStepCycles -= (6 + 2);
|
||||
break;
|
||||
|
|
@ -1652,15 +1685,12 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
this.regMMR1 = (this.regMMR1 << 8) | ((stepSize << 3) & 0xf8) | reg;
|
||||
}
|
||||
|
||||
if (reg == 6 && (!this.mmuMode) && (accessFlags & PDP11.ACCESS.WRITE) && stepSize <= 0 && (this.regsGen[6] <= this.regSL || this.regsGen[6] >= 0xfffe)) {
|
||||
if (this.regsGen[6] <= this.regSL - 32) {
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.STACKERROR);
|
||||
} else {
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
/*
|
||||
* 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 && stepSize < 0) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
return virtualAddress;
|
||||
};
|
||||
|
|
@ -2016,7 +2046,7 @@ CPUStatePDP11.prototype.updateDstByte = function(opCode, src, fnOp)
|
|||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
|
||||
if (!mode) {
|
||||
this.regsGen[reg] = (this.regsGen[reg] & 0xff00) | fnOp.call(this, src, this.regsGen[reg]);
|
||||
this.regsGen[reg] = (this.regsGen[reg] & 0xff00) | fnOp.call(this, src, this.regsGen[reg] & 0xff);
|
||||
} else {
|
||||
var addr = this.dstAddr = this.getAddr(mode, reg, PDP11.ACCESS.UPDATE_BYTE);
|
||||
this.writeByteToPhysical(addr, fnOp.call(this, src, this.readByteFromPhysical(addr)));
|
||||
|
|
@ -2191,23 +2221,23 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
|||
/*
|
||||
* Check for any pending traps.
|
||||
*
|
||||
* I've moved this TRAP_MASK check BEFORE we decode the next instruction instead
|
||||
* of immediately AFTER, because the last instruction may have thrown an exception,
|
||||
* kicking us out before we reach the bottom of this loop.
|
||||
* I've moved this TRAP_MASK check BEFORE we decode the next instruction instead of immediately AFTER,
|
||||
* just in case the last instruction threw an exception that kicked us out before we reached the bottom
|
||||
* of the stepCPU() loop.
|
||||
*
|
||||
* Note: I've swapped the order (priority) of the TF and SP traps, based on the PDP-11/20 Handbook.
|
||||
* TODO: Determine if 1) the 11/20 Handbook was wrong, or 2) the 11/70 really has different priorities.
|
||||
*/
|
||||
if (this.opFlags & PDP11.OPFLAG.TRAP_MASK) {
|
||||
if (this.opFlags & PDP11.OPFLAG.TRAP_MMU) {
|
||||
this.trap(PDP11.TRAP.MMU, PDP11.REASON.TRAPMMU); // MMU trap has priority
|
||||
} else {
|
||||
if (this.opFlags & PDP11.OPFLAG.TRAP_SP) {
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.TRAPSP); // then SP trap
|
||||
} else {
|
||||
if (this.opFlags & PDP11.OPFLAG.TRAP_TF) {
|
||||
this.trap(PDP11.TRAP.BPT, PDP11.REASON.TRAPTF); // and finally a TF trap
|
||||
}
|
||||
}
|
||||
this.trap(PDP11.TRAP.MMU, PDP11.OPFLAG.TRAP_MMU, PDP11.REASON.TRAPMMU);
|
||||
}
|
||||
else if (this.opFlags & PDP11.OPFLAG.TRAP_TF) {
|
||||
this.trap(PDP11.TRAP.BPT, PDP11.OPFLAG.TRAP_TF, PDP11.REASON.TRAPTF);
|
||||
}
|
||||
else if (this.opFlags & PDP11.OPFLAG.TRAP_SP) {
|
||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.OPFLAG.TRAP_SP, PDP11.REASON.TRAPSP);
|
||||
}
|
||||
this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK;
|
||||
}
|
||||
/*
|
||||
* If we're in the INTQ or WAIT state, see if any interrupts can kick us out of that state.
|
||||
|
|
|
|||
|
|
@ -1400,7 +1400,7 @@ if (DEBUGGER) {
|
|||
var trapStatus = this.cpu.getTrapStatus();
|
||||
if (trapStatus) {
|
||||
var trapReason = trapStatus >> 8;
|
||||
var sReason = trapReason? (" (" + this.toStrBase(trapReason) + ")") : "";
|
||||
var sReason = trapReason? (" (" + trapReason + ")") : "";
|
||||
this.println("trapped to " + this.toStrBase(trapStatus & 0xff, 1) + sReason);
|
||||
}
|
||||
|
||||
|
|
@ -1724,8 +1724,11 @@ if (DEBUGGER) {
|
|||
*/
|
||||
DebuggerPDP11.prototype.undefinedInstruction = function(opCode)
|
||||
{
|
||||
this.printMessage("undefined opcode " + this.toStrBase(opCode), true, true);
|
||||
return this.stopInstruction(); // allow the caller to step over it if they really want a trap generated
|
||||
if (this.messageEnabled(MessagesPDP11.CPU)) {
|
||||
this.printMessage("undefined opcode " + this.toStrBase(opCode), true, true);
|
||||
return this.stopInstruction(); // allow the caller to step over it if they really want a trap generated
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2946,8 +2949,13 @@ if (DEBUGGER) {
|
|||
if (!dbgAddr) return;
|
||||
|
||||
var len = 0; // 0 is not a default; it triggers the appropriate default below
|
||||
var fRange = false;
|
||||
var fJSON = (sCmd == "ds");
|
||||
|
||||
if (sLen) {
|
||||
fRange = true;
|
||||
if (sLen.charAt(0) == 'l') {
|
||||
fRange = false;
|
||||
sLen = sLen.substr(1) || sBytes;
|
||||
}
|
||||
len = this.parseValue(sLen) >>> 0; // negative lengths not allowed
|
||||
|
|
@ -2959,8 +2967,9 @@ if (DEBUGGER) {
|
|||
* since this is primarily a word-oriented machine.
|
||||
*/
|
||||
var size = (sCmd == "dd"? 4 : (sCmd == "db"? 1 : 2));
|
||||
var nBytes = (size * len) || 128;
|
||||
var nLines = ((nBytes + 15) >> 4) || 1;
|
||||
var nBytes = (fRange? (len - dbgAddr.addr) : (size * len)) || 128;
|
||||
var nBytesPerLine = fJSON? 16 : this.nBase;
|
||||
var nLines = (((nBytes + nBytesPerLine - 1) / nBytesPerLine)|0) || 1;
|
||||
|
||||
var sDump = "";
|
||||
while (nLines-- && nBytes > 0) {
|
||||
|
|
@ -2977,20 +2986,29 @@ if (DEBUGGER) {
|
|||
*/
|
||||
var i, n;
|
||||
var data = 0, shift = 0;
|
||||
for (i = this.nBase; i > 0 && nBytes > 0; i -= n, nBytes -= n) {
|
||||
for (i = nBytesPerLine; i > 0 && nBytes > 0; i -= n, nBytes -= n) {
|
||||
n = 1;
|
||||
var v = size == 1? this.getByte(dbgAddr, n) : this.getWord(dbgAddr, (n = 2));
|
||||
data |= (v << (shift << 3));
|
||||
shift += n;
|
||||
if (shift == size) {
|
||||
sData += this.toStrBase(data, size);
|
||||
sData += (size == 1? (i == 9? '-' : ' ') : " ");
|
||||
if (fJSON) {
|
||||
if (sData) sData += ",";
|
||||
sData += "0x"+ str.toHex(data, size * 2);
|
||||
} else {
|
||||
sData += this.toStrBase(data, size);
|
||||
sData += (size == 1? (i == 9? '-' : ' ') : " ");
|
||||
}
|
||||
data = shift = 0;
|
||||
}
|
||||
sChars += (v >= 32 && v < 128? String.fromCharCode(v) : '.');
|
||||
}
|
||||
if (sDump) sDump += '\n';
|
||||
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
|
||||
if (sDump) sDump += "\n";
|
||||
if (fJSON) {
|
||||
sDump += sData + ",";
|
||||
} else {
|
||||
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (sDump) this.println(sDump);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ var PDP11 = {
|
|||
TRAP: 0x08, // set if last operation was a trap (see trapLast for the vector, and trapReason for the reason)
|
||||
TRAP_TF: 0x10, // aka PDP11.PSW.TF
|
||||
TRAP_MMU: 0x20,
|
||||
TRAP_SP: 0x40,
|
||||
TRAP_SP: 0x40, // set for a deferred BUS_ERROR trap (due to a "yellow" stack overflow condition)
|
||||
TRAP_MASK: 0x70,
|
||||
NO_FLAGS: 0x80, // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
|
||||
PRESERVE: 0x07 // OPFLAG bits to preserve prior to the next instruction
|
||||
|
|
@ -264,24 +264,24 @@ var PDP11 = {
|
|||
* PDP-11 trap reasons (for diagnostic purposes only)
|
||||
*/
|
||||
REASON: {
|
||||
BPT: 1,
|
||||
EMT: 2,
|
||||
HALT: 3,
|
||||
IOT: 4,
|
||||
TRAP: 5,
|
||||
RESERVED: 6,
|
||||
ODDMEMADDR: 10,
|
||||
NOMEMORY: 12,
|
||||
ODDMMUADDR: 14,
|
||||
MAPERROR: 16,
|
||||
PUSHERROR: 18,
|
||||
NOREGADDR: 20,
|
||||
STACKMODE1: 22,
|
||||
STACKERROR: 24,
|
||||
INTERRUPT: 26,
|
||||
TRAPMMU: 28,
|
||||
TRAPSP: 30,
|
||||
TRAPTF: 32
|
||||
BPT: -1,
|
||||
EMT: -2,
|
||||
HALT: -3,
|
||||
IOT: -4,
|
||||
TRAP: -5,
|
||||
RESERVED: -6,
|
||||
TRAPMMU: -7,
|
||||
TRAPSP: -8,
|
||||
TRAPTF: -9,
|
||||
ODDMEMADDR: -10,
|
||||
NOMEMORY: -11,
|
||||
ODDMMUADDR: -12,
|
||||
MAPERROR: -13,
|
||||
PUSHERROR: -14,
|
||||
NOREGADDR: -15,
|
||||
STACKMODE1: -16,
|
||||
STACKERROR: -17,
|
||||
INTERRUPT: -18
|
||||
},
|
||||
/*
|
||||
* Internal memory access flags
|
||||
|
|
|
|||
|
|
@ -1048,7 +1048,16 @@ DevicePDP11.prototype.readPSW = function(addr)
|
|||
*/
|
||||
DevicePDP11.prototype.writePSW = function(data, addr)
|
||||
{
|
||||
var maskDisallowed = PDP11.PSW.UNUSED | PDP11.PSW.TF;
|
||||
/*
|
||||
* pdp11.js disallowed PSW.TF in addition to PSW.UNUSED, but DEC's "TRAP TEST" expects the
|
||||
* following instruction to trap:
|
||||
*
|
||||
* 004174: 052767 000020 173574 BIS #20,177776
|
||||
*
|
||||
* Since that test was written for the PDP-11/20, it's possible that newer machines
|
||||
* have a different behavior, but for now, we assume that all machines allow setting PSW.TF.
|
||||
*/
|
||||
var maskDisallowed = PDP11.PSW.UNUSED;
|
||||
this.cpu.setPSW((data & ~maskDisallowed) | (this.cpu.getPSW() & maskDisallowed));
|
||||
this.cpu.opFlags |= PDP11.OPFLAG.NO_FLAGS;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ var MessagesPDP11 = {
|
|||
DEVICE: 0x00000200,
|
||||
KEYBOARD: 0x00010000,
|
||||
KEYS: 0x00020000,
|
||||
DISK: 0x00200000,
|
||||
PAPER: 0x00100000,
|
||||
DISK: 0x00400000,
|
||||
SERIAL: 0x00800000,
|
||||
SPEAKER: 0x02000000,
|
||||
COMPUTER: 0x04000000,
|
||||
|
|
@ -76,6 +77,7 @@ MessagesPDP11.CATEGORIES = {
|
|||
"device": MessagesPDP11.DEVICE,
|
||||
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
|
||||
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"paper": MessagesPDP11.PAPER,
|
||||
"disk": MessagesPDP11.DISK,
|
||||
"serial": MessagesPDP11.SERIAL,
|
||||
"speaker": MessagesPDP11.SPEAKER,
|
||||
|
|
|
|||
|
|
@ -33,13 +33,14 @@
|
|||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -254,13 +255,13 @@ RAMPDP11.prototype.reset = function()
|
|||
/**
|
||||
* loadImage(aBytes, addrLoad, addrExec, addrInit, fReset)
|
||||
*
|
||||
* If the array contains an image in the "Absolute Format," load it as specified by
|
||||
* the format; otherwise, load it as-is using the address(es) supplied.
|
||||
* If the array contains a PAPER tape image in the "Absolute Format," load it as specified
|
||||
* by the format; otherwise, load it as-is using the address(es) supplied.
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {Array|Uint8Array} aBytes
|
||||
* @param {number|null} [addrLoad]
|
||||
* @param {number|null} [addrExec]
|
||||
* @param {number|null} [addrExec] (this CAN override any starting address INSIDE the image)
|
||||
* @param {number|null} [addrInit]
|
||||
* @param {boolean} [fReset]
|
||||
* @return {boolean} (true if loaded, false if not)
|
||||
|
|
@ -337,9 +338,11 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
if (addr & 0x1) {
|
||||
this.cpu.stopCPU();
|
||||
} else {
|
||||
this.cpu.setReset(addr, fReset);
|
||||
if (addrExec == null) addrExec = addr;
|
||||
}
|
||||
if (addrExec != null) this.printMessage("starting address: " + str.toHexWord(addrExec), MessagesPDP11.PAPER);
|
||||
} else {
|
||||
this.printMessage("loading " + str.toHexWord(cbData) + " bytes at " + str.toHexWord(addr) + "-" + str.toHexWord(addr + cbData - 1), MessagesPDP11.PAPER);
|
||||
while (cbData--) {
|
||||
this.cpu.setByteDirect(addr++, aBytes[offData++] & 0xff);
|
||||
}
|
||||
|
|
@ -353,10 +356,20 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
for (var i = 0; i < aBytes.length; i++) {
|
||||
this.cpu.setByteDirect(addrLoad + i, aBytes[i]);
|
||||
}
|
||||
if (addrExec != null) this.cpu.setReset(addrExec, fReset);
|
||||
fLoaded = true;
|
||||
}
|
||||
}
|
||||
if (fLoaded) {
|
||||
/*
|
||||
* Set the start address to whatever the caller provided, or failing that, whatever start
|
||||
* address was specified inside the image.
|
||||
*
|
||||
* For example, the diagnostic "MAINDEC-11-D0AA-PB" doesn't include a start address inside the
|
||||
* image, but we know that the directions for that diagnostic say to "Start and Restart at 200",
|
||||
* so we have manually inserted an "exec":128 in the JSON containing the image.
|
||||
*/
|
||||
if (addrExec != null) this.cpu.setReset(addrExec, fReset);
|
||||
}
|
||||
return fLoaded;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue