Fixed bug in writeDstWord() that was updating flags with negative data (and sadly, none of DEC's basic tests caught it)

This commit is contained in:
Jeff 2016-11-13 11:34:59 -08:00 committed by Jeff Parsons
commit 17816f6fcc
8 changed files with 404 additions and 381 deletions

View file

@ -1398,7 +1398,7 @@ BusPDP11.prototype.fault = function(addr, err, access)
this.dbg.printMessage("memory fault (" + access + ") on " + this.dbg.toStrBase(addr), true, true);
}
if (err) this.cpu.regErr |= err;
this.cpu.trap(PDP11.TRAP.BUS_ERROR, 0, addr);
this.cpu.trap(PDP11.TRAP.BUS, 0, addr);
}
};

View file

@ -831,7 +831,7 @@ PDP11.opBPL = function(opCode)
*/
PDP11.opBPT = function(opCode)
{
this.trap(PDP11.TRAP.BPT, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.BPT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (4 + 1);
};
@ -1105,7 +1105,7 @@ PDP11.opDIV = function(opCode)
*/
PDP11.opEMT = function(opCode)
{
this.trap(PDP11.TRAP.EMT, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.EMT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (22 + 3);
};
@ -1119,7 +1119,7 @@ PDP11.opHALT = function(opCode)
{
if (this.regPSW & PDP11.PSW.CMODE) {
this.regErr |= PDP11.CPUERR.BADHALT;
this.trap(PDP11.TRAP.BUS_ERROR, 0, PDP11.REASON.HALT);
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.HALT);
} else {
if (this.panel) {
this.panel.setData(this.regsGen[0], true);
@ -1178,7 +1178,7 @@ PDP11.opINCB = function(opCode)
*/
PDP11.opIOT = function(opCode)
{
this.trap(PDP11.TRAP.IOT, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.IOT, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (22 + 3);
};
@ -1298,7 +1298,7 @@ PDP11.opMFPT = function(opCode)
/*
* TODO: Review
*/
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.OPCODE);
};
PDP11.MOV_CYCLES = [
@ -1671,7 +1671,7 @@ PDP11.opSPL = function(opCode)
}
if (!(this.regPSW & PDP11.PSW.CMODE)) {
this.regPSW = (this.regPSW & ~(PDP11.PSW.UNUSED | PDP11.PSW.PRI)) | ((opCode & 0x7) << PDP11.PSW.SHIFT.PRI);
this.opFlags |= PDP11.OPFLAG.INTQ_CHK;
this.opFlags |= PDP11.OPFLAG.INTQ_DELAY;
}
this.nStepCycles -= (4 + 1);
};
@ -1720,7 +1720,7 @@ PDP11.opSXT = function(opCode)
*/
PDP11.opTRAP = function(opCode)
{
this.trap(PDP11.TRAP.TRAP, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.TRAP, 0, PDP11.REASON.OPCODE);
this.nStepCycles -= (4 + 1);
};
@ -1830,7 +1830,7 @@ PDP11.opUndefined = function(opCode)
if (DEBUGGER && this.dbg) {
if (this.dbg.undefinedInstruction(opCode)) return;
}
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.RESERVED, 0, PDP11.REASON.OPCODE);
};
/**

View file

@ -590,9 +590,9 @@ CPUStatePDP11.prototype.getOpcode = function()
this.lastAI = 0;
this.lastPC = pc;
/*
* If PC is misaligned, a BUS_ERROR trap will be generated, and because it will generate an
* If PC is unaligned, a BUS trap will be generated, and because it will generate an
* exception, the next line (the equivalent of advancePC(2)) will not be executed, ensuring that
* original misaligned PC will be pushed onto the stack by trap().
* original unaligned PC will be pushed onto the stack by trap().
*/
var op = this.readWord(pc);
this.regsGen[PDP11.REG.PC] = (pc + 2) & 0xffff;
@ -819,10 +819,10 @@ CPUStatePDP11.prototype.checkInterrupts = function()
fInterrupt = true;
}
}
else if (this.opFlags & PDP11.OPFLAG.INTQ_CHK) {
else if (this.opFlags & PDP11.OPFLAG.INTQ_DELAY) {
/*
* We know that INTQ (bit 1) is clear, so since INTQ_CHK (bit 0) is set, incrementing opFlags
* will transform INTQ_CHK into INTQ, without affecting any other (higher) bits.
* We know that INTQ (bit 1) is clear, so since INTQ_DELAY (bit 0) is set, incrementing opFlags
* will transform INTQ_DELAY into INTQ, without affecting any other (higher) bits.
*/
this.opFlags++;
}
@ -899,11 +899,11 @@ CPUStatePDP11.prototype.dispatchInterrupt = function(vector, priority)
CPUStatePDP11.prototype.checkTraps = function()
{
if (this.opFlags & PDP11.OPFLAG.TRAP_MMU) {
this.trap(PDP11.TRAP.MMU, PDP11.OPFLAG.TRAP_MMU, PDP11.REASON.TRAP);
this.trap(PDP11.TRAP.MMU, PDP11.OPFLAG.TRAP_MMU, PDP11.REASON.FAULT);
return true;
}
if (this.opFlags & PDP11.OPFLAG.TRAP_SP) {
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.OPFLAG.TRAP_SP, PDP11.REASON.STACK);
this.trap(PDP11.TRAP.BUS, PDP11.OPFLAG.TRAP_SP, PDP11.REASON.YELLOW);
return true;
}
if (this.opFlags & PDP11.OPFLAG.TRAP_TF) {
@ -1263,6 +1263,7 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
*/
this.regErr |= PDP11.CPUERR.RED;
this.regsGen[6] = 4;
reason = PDP11.REASON.RED;
}
this.lastPC = vector;
@ -1310,22 +1311,22 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
*
* where, after "TRAP 000" has executed, a hardware interrupt will be acknowledged, and instead of
* executing the IOT, we'll execute the HALT and fail the test. We avoid that by relying on the same
* trick that the SPL instruction uses: setting INTQ_CHK instead of INTQ, which effectively delays
* trick that the SPL instruction uses: setting INTQ_DELAY instead of INTQ, which effectively delays
* INTQ detection for one instruction, which is just long enough to allow the diagnostic to pass.
*/
this.opFlags &= ~(flag | PDP11.OPFLAG.TRAP_TF | PDP11.OPFLAG.INTQ);
this.opFlags |= PDP11.OPFLAG.INTQ_CHK;
this.opFlags |= PDP11.OPFLAG.INTQ_DELAY | PDP11.OPFLAG.TRAP;
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()
* These next properties (in conjunction with setting PDP11.OPFLAG.TRAP) are purely an aid for the Debugger;
* see getTrapStatus().
*/
this.opFlags |= PDP11.OPFLAG.TRAP;
this.trapVector = vector;
this.trapReason = reason;
if (reason != PDP11.REASON.INTERRUPT) throw vector;
if (reason >= PDP11.REASON.RED) throw vector;
};
/**
@ -1491,72 +1492,77 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
var errorMask = 0;
switch (pdr & 0x7) {
case 1: // read-only with trap
errorMask = 0x1000; // MMU trap
errorMask = PDP11.MMR0.TRAP_MMU;
/* falls through */
case 2: // read-only
pdr |= 0x80; // Set A bit
if (accessFlags & PDP11.ACCESS.WRITE) {
errorMask = 0x2000; // read-only abort
errorMask = PDP11.MMR0.ABORT_RO;
}
break;
case 4: // read-write with read-write trap
errorMask = 0x1000; // MMU trap
errorMask = PDP11.MMR0.TRAP_MMU;
/* falls through */
case 5: // read-write with write trap
if (accessFlags & PDP11.ACCESS.WRITE) {
errorMask = 0x1000; // MMU trap
errorMask = PDP11.MMR0.TRAP_MMU;
}
/* falls through */
case 6: // read-write: set A & W bits
pdr |= ((accessFlags & PDP11.ACCESS.WRITE) ? 0xc0 : 0x80);
break;
default:
errorMask = 0x8000; // non-resident abort
errorMask = PDP11.MMR0.ABORT_NR;
break;
}
if ((pdr & 0x7f08) !== 0x7f00) { // skip checking most common case (hopefully)
if ((pdr & 0x7f08) != 0x7f00) { // skip checking most common case (hopefully)
if (pdr & 0x8) { // expand downwards
if (pdr & 0x7f00) {
if ((virtualAddress & 0x1fc0) < ((pdr >> 2) & 0x1fc0)) {
errorMask |= 0x4000; // page length error abort
errorMask |= PDP11.MMR0.ABORT_PL;
}
}
} else { // expand upwards
if ((virtualAddress & 0x1fc0) > ((pdr >> 2) & 0x1fc0)) {
errorMask |= 0x4000; // page length error abort
errorMask |= PDP11.MMR0.ABORT_PL;
}
}
}
// aborts and traps: log FIRST trap and MOST RECENT abort
/*
* Aborts and traps: log FIRST trap and MOST RECENT abort
*/
this.mmuPDR[this.mmuMode][page] = pdr;
if ((physicalAddress !== 0x3fff7a) || this.mmuMode) {// MMR0 is 017777572
if (physicalAddress != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.mmuMode) {
this.mmuLastMode = this.mmuMode;
this.mmuLastPage = page;
}
var fTrap = false;
var fAbort = false;
if (errorMask) {
if (errorMask & 0xe000) {
if (errorMask & PDP11.MMR0.ABORT) {
if (this.trapPSW >= 0) errorMask |= 0x80; // Instruction complete
if (!(this.regMMR0 & 0xe000)) {
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
this.regMMR0 |= errorMask | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
}
fTrap = true;
fAbort = true;
}
if (!(this.regMMR0 & 0xf000)) {
//if (physicalAddress < 017772200 || physicalAddress > 017777677) {
if (physicalAddress < 0x3ff480 || physicalAddress > 0x3fffbf) {
this.regMMR0 |= 0x1000; // MMU trap flag
if (this.regMMR0 & 0x0200) {
if (!(this.regMMR0 & (PDP11.MMR0.ABORT | PDP11.MMR0.TRAP_MMU))) {
/*
* TODO: Review the code below, because the address range seems over-inclusive.
*/
if (physicalAddress < ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.SIPDR0) & this.mmuMask) ||
physicalAddress > ((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 (fTrap) { // don't trap until the end, because it throws an exception
this.trap(PDP11.TRAP.MMU, 0, PDP11.REASON.ERROR);
if (fAbort) { // don't abort until the end, because it throws an exception
this.trap(PDP11.TRAP.MMU, 0, PDP11.REASON.ABORT);
}
}
return physicalAddress;
@ -1641,7 +1647,7 @@ CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
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.STACK);
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,
@ -1708,11 +1714,11 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
*
* NOTE: Most instruction code paths never call getAddrByMode() when the mode is zero;
* JMP and JSR instructions are exceptions, but that's OK, because those are documented as
* ILLEGAL instructions which produce a BUS_ERROR trap (as opposed to UNDEFINED instructions
* ILLEGAL instructions which produce a BUS trap (as opposed to UNDEFINED instructions
* that cause a RESERVED trap).
*/
case 0:
this.trap(PDP11.TRAP.BUS_ERROR, 0, PDP11.REASON.ILLEGAL);
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.ILLEGAL);
return 0;
/*
@ -2208,6 +2214,7 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
@ -2273,14 +2280,15 @@ CPUStatePDP11.prototype.writeDstWord = function(opCode, data)
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
if (!mode) {
this.regsGen[reg] = data < 0? this.regsGen[-data-1] : (data & 0xffff);
this.regsGen[reg] = (data = (data < 0? this.regsGen[-data-1] : data));
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_WORD);
this.bus.setWord(addr, data < 0? this.regsGen[-data-1] : data);
this.bus.setWord(addr, (data = (data < 0? this.regsGen[-data-1] : data)));
}
return data;
};

View file

@ -1220,7 +1220,7 @@ if (DEBUGGER) {
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
this.sMessagePrev = sMessage;
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && this.cpu.isRunning()) {
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && this.cpu.isRunning() || this.isBusy(true)) {
this.stopCPU();
sMessage += " (cpu halted)";
}
@ -2984,10 +2984,10 @@ if (DEBUGGER) {
*
* Besides, it's nice for "db" and "dw" to generate the same Bus activity that typical byte and word reads do.
*/
var i, n;
var i = nBytesPerLine;
var data = 0, shift = 0;
for (i = nBytesPerLine; i > 0 && nBytes > 0; i -= n, nBytes -= n) {
n = 1;
while (i > 0 && nBytes > 0) {
var n = 1;
var v = size == 1? this.getByte(dbgAddr, n) : this.getWord(dbgAddr, (n = 2));
data |= (v << (shift << 3));
shift += n;
@ -3001,7 +3001,12 @@ if (DEBUGGER) {
}
data = shift = 0;
}
sChars += (v >= 32 && v < 128? String.fromCharCode(v) : '.');
i -= n; nBytes -= n;
while (n--) {
var c = v & 0xff;
sChars += (c >= 32 && c < 128? String.fromCharCode(c) : '.');
v >>= 8;
}
}
if (sDump) sDump += "\n";
if (fJSON) {

View file

@ -82,7 +82,7 @@ var TYPEDARRAYS = (typeof ArrayBuffer !== 'undefined');
* MEMFAULT forces the Memory interfaces to signal a CPU fault when a word is accessed using an odd (unaligned) address.
*
* Since PDPjs inherited its Bus component from PCx86, it included support for both aligned and unaligned word accesses
* by default. However, the PDP-11 adds a wrinkle: when an odd address is used to access a memory word, a BUS_ERROR trap
* by default. However, the PDP-11 adds a wrinkle: when an odd address is used to access a memory word, a BUS trap
* must be generated. Note that odd IOPAGE word accesses are fine; this only affects the Memory component.
*
* When the MMU is enabled, these checks may also be performed at a higher level, eliminating the need for them at the
@ -197,13 +197,13 @@ var PDP11 = {
* Internal operation state flags
*/
OPFLAG: {
INTQ_CHK: 0x01, // set INTQ on next check (set by SPL and assorted traps)
INTQ_DELAY: 0x01, // set INTQ on next check (set by SPL and traps)
INTQ: 0x02, // call checkInterrupts()
INTQ_MASK: 0x03,
WAIT: 0x04, // WAIT operation in progress
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_SP: 0x20, // set for a deferred BUS_ERROR trap (due to a "yellow" stack overflow condition)
TRAP_SP: 0x20, // set for a deferred BUS trap (due to a "yellow" stack overflow condition)
TRAP_MMU: 0x40,
TRAP_MASK: 0x70,
NO_FLAGS: 0x80, // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
@ -251,7 +251,7 @@ var PDP11 = {
*/
TRAP: {
UNDEFINED: 0x00, // 000 (reserved)
BUS_ERROR: 0x04, // 004 illegal instruction, unaligned address, invalid memory, stack limit, microbreak
BUS: 0x04, // 004 unaligned address, non-existent memory, illegal instruction, etc
RESERVED: 0x08, // 010 reserved instructions
BPT: 0x0C, // 014 BPT: breakpoint trap (trace)
IOT: 0x10, // 020 IOT: input/output trap
@ -262,27 +262,31 @@ var PDP11 = {
MMU: 0xA8 // 250 MMU: aborts and traps
},
/*
* PDP-11 trap reasons (largely for diagnostic purposes only)
* PDP-11 trap reasons; the reason may also be a non-negative address indicating a BUS memory error
* (unaligned address or non-existent memory). Any reason >= RED (which includes BUS memory errors) generate
* immediate (thrown) traps; the rest generate synchronous traps.
*/
REASON: {
UNKNOWN: 0,
TRAP: -1,
HALT: -2,
INTERRUPT: -3,
TRACE: -4,
STACK: -5,
ILLEGAL: -6,
ERROR: -7,
ABORT: -1, // immediate MMU fault
ILLEGAL: -2, // immediate invalid opcode (BUS)
RED: -3, // immediate stack overflow fault (BUS)
YELLOW: -4, // deferred stack overflow fault (BUS)
FAULT: -5, // deferred MMU fault
TRACE: -6, // deferred TF fault (BPT)
HALT: -7, // illegal HALT (BUS)
OPCODE: -8, // opcode-generated trap (eg, BPT, EMT, IOT, TRAP, or RESERVED opcode)
INTERRUPT: -9, // device-generated trap (vector is device-specific)
},
REASONS: [
"UNKNOWN",
"TRAP",
"HALT",
"INTERRUPT",
"TRACE",
"STACK",
"ABORT",
"ILLEGAL",
"ERROR"
"RED",
"YELLOW",
"FAULT",
"TRACE",
"HALT",
"OPCODE",
"INTERRUPT"
],
/*
* Internal memory access flags

View file

@ -690,6 +690,11 @@ SerialPortPDP11.prototype.writeXBUF = function(data, addr)
if (data) {
this.transmitByte(data);
this.xcsr &= ~PDP11.DL11.XCSR.READY;
/*
* NOTE: When debugging issues involving the SerialPort, such as debugging code between a pair of
* transmitted bytes, you can pass 0 instead of getBaudTimeout() to setTimer() to minimize the amount
* of time spent waiting for XCSR.READY to be set again.
*/
this.cpu.setTimer(this.timerTransmitInterrupt, this.getBaudTimeout(this.nBaudTransmit));
}
};