Cleaned up OPFLAG handling
This commit is contained in:
parent
b3ba03d0a5
commit
98d3700fa4
8 changed files with 381 additions and 361 deletions
|
|
@ -1092,7 +1092,9 @@ CPUPDP11.prototype.runCPU = function()
|
|||
}
|
||||
|
||||
/*
|
||||
* nCycles is how many cycles stepCPU() actually ran (nBurstCycles less any remaining nStepCycles).
|
||||
* nCycles is how many cycles stepCPU() actually ran (nBurstCycles less any remaining nStepCycles);
|
||||
* that calculation matches the return value from stepCPU(), but since it may have thrown an exception,
|
||||
* we can't rely on it.
|
||||
*/
|
||||
var nCycles = this.nBurstCycles - this.nStepCycles;
|
||||
|
||||
|
|
|
|||
|
|
@ -1527,7 +1527,7 @@ PDP11.opSPL = function(opCode)
|
|||
this.assert(opCode & 0x08);
|
||||
if (!(this.regPSW & PDP11.PSW.CMODE)) {
|
||||
this.regPSW = (this.regPSW & ~(PDP11.PSW.UNUSED | PDP11.PSW.PRI)) | ((opCode & 0x7) << PDP11.PSW.SHIFT.PRI);
|
||||
this.priorityReview = 1;
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ_SPL;
|
||||
}
|
||||
this.nStepCycles -= 1;
|
||||
};
|
||||
|
|
@ -1618,13 +1618,21 @@ PDP11.opWAIT = function(opCode)
|
|||
* However, the PCjs approach requires the CPU to continue running. One simple solution to this dilemma:
|
||||
*
|
||||
* 1) opWAIT() sets a new opFlags bit (OPFLAG.WAIT)
|
||||
* 2) When stepCPU() sees OPFLAG.WAIT, it checks for interrupts; if none, it rewinds the PC back to the WAIT
|
||||
* 2) Rewind PC back to WAIT
|
||||
* 3) Whenever stepCPU() detects OPFLAG.WAIT, call checkInterruptQueue()
|
||||
* 4) If checkInterruptQueue() detects an interrupt, advance PC past WAIT before dispatching it
|
||||
*
|
||||
* Technically, the PC is already exactly where it's supposed to be, so why are we wasting time with steps
|
||||
* 2 and 4? It's largely for the Debugger's sake, so that as long as execution is "blocked" by a WAIT, that's
|
||||
* what you'll see in the Debugger. I could make those steps conditioned on the presence of the Debugger,
|
||||
* but I feel it's better to keep all code paths the same.
|
||||
*
|
||||
* NOTE: It's almost always a bad idea to add more checks to the inner stepCPU() loop, because every additional
|
||||
* check can have a measurable (negative) impact on performance. Which is why it's important to use opFlags bits
|
||||
* whenever possible, since we can test for multiple (up to 32) exceptional conditions with a single check.
|
||||
*/
|
||||
this.opFlags |= PDP11.OPFLAG.WAIT;
|
||||
this.advancePC(-2);
|
||||
this.nStepCycles -= 1;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -194,8 +194,8 @@ CPUStatePDP11.prototype.resetRegs = function()
|
|||
/**
|
||||
* @type {Array.<InterruptEvent>}
|
||||
*/
|
||||
this.interruptQueue = []; // List of interrupts pending
|
||||
this.priorityReview = 2; // flag to mark if we need to check priority change
|
||||
this.interruptQueue = [];
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
this.initMemoryAccess();
|
||||
};
|
||||
|
||||
|
|
@ -511,10 +511,21 @@ CPUStatePDP11.prototype.getPC = function()
|
|||
CPUStatePDP11.prototype.getPCWord = function()
|
||||
{
|
||||
var data = this.readWordFromVirtual(this.regsGen[7]);
|
||||
this.regsGen[7] = (this.regsGen[7] + 2) & 0xffff;
|
||||
this.advancePC(2);
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* advancePC(off)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} off
|
||||
*/
|
||||
CPUStatePDP11.prototype.advancePC = function(off)
|
||||
{
|
||||
this.regsGen[7] = (this.regsGen[7] + off) & 0xffff;
|
||||
};
|
||||
|
||||
/**
|
||||
* setPC()
|
||||
*
|
||||
|
|
@ -601,7 +612,7 @@ CPUStatePDP11.prototype.interrupt = function(delay, priority, vector, callback)
|
|||
"callback": callback
|
||||
});
|
||||
}
|
||||
this.priorityReview = 2;
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -611,16 +622,14 @@ CPUStatePDP11.prototype.interrupt = function(delay, priority, vector, callback)
|
|||
*/
|
||||
CPUStatePDP11.prototype.checkInterruptQueue = function()
|
||||
{
|
||||
if (this.priorityReview == 1) {
|
||||
this.priorityReview = 2; // SPL delay
|
||||
} else {
|
||||
this.priorityReview = 0;
|
||||
if (this.opFlags & PDP11.OPFLAG.INTQ) {
|
||||
this.opFlags &= ~PDP11.OPFLAG.INTQ;
|
||||
var interruptEvent = null;
|
||||
var savePSW = this.regPIR & 0xe0;
|
||||
for (var i = this.interruptQueue.length; --i >= 0;) {
|
||||
if (this.interruptQueue[i].delay > 0) {
|
||||
this.interruptQueue[i].delay--;
|
||||
this.priorityReview = 2;
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
break; // Decrement only one delay 'difference' per cycle
|
||||
}
|
||||
//if (typeof this.interruptQueue[i].callback !== "undefined") {
|
||||
|
|
@ -639,7 +648,10 @@ CPUStatePDP11.prototype.checkInterruptQueue = function()
|
|||
}
|
||||
}
|
||||
if (savePSW > (this.regPSW & 0xe0)) {
|
||||
this.opFlags &= ~PDP11.OPFLAG.WAIT;
|
||||
if (this.opFlags & PDP11.OPFLAG.WAIT) {
|
||||
this.advancePC(2);
|
||||
this.opFlags &= ~PDP11.OPFLAG.WAIT;
|
||||
}
|
||||
if (!interruptEvent) {
|
||||
this.trap(PDP11.TRAP.PIRQ, PDP11.REASON.INTERRUPT);
|
||||
} else {
|
||||
|
|
@ -647,6 +659,13 @@ CPUStatePDP11.prototype.checkInterruptQueue = function()
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (this.opFlags & PDP11.OPFLAG.INTQ_SPL) {
|
||||
/*
|
||||
* We know that INTQ (bit 1) is clear, so since INTQ_SPL (bit 0) is set, incrementing opFlags
|
||||
* will transform INTQ_SPL into INTQ, without affecting any other (higher) bits.
|
||||
*/
|
||||
this.opFlags++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -708,9 +727,9 @@ CPUStatePDP11.prototype.setPSW = function(newPSW)
|
|||
this.regsGen[6] = this.regsAltStack[this.mmuMode];
|
||||
}
|
||||
/*
|
||||
* Trigger check of priority levels
|
||||
* Trigger a call to checkInterruptQueue()
|
||||
*/
|
||||
this.priorityReview = 2;
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
this.regPSW = newPSW;
|
||||
};
|
||||
|
||||
|
|
@ -911,7 +930,7 @@ CPUStatePDP11.prototype.trap = function(vector, reason)
|
|||
|
||||
this.pushWord(this.trapPSW);
|
||||
this.pushWord(this.regsGen[7]);
|
||||
this.regsGen[7] = newPC;
|
||||
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
|
||||
|
|
@ -1704,42 +1723,26 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
|||
this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK;
|
||||
}
|
||||
/*
|
||||
* If we're in WAIT state, see if any interrupts are ready to kick us out of that state.
|
||||
* If we're in the INTQ or WAIT state, see if any interrupts can kick us out of that state.
|
||||
*
|
||||
* By also requiring nMinCycles to be non-zero before checking the interrupt queue, we avoid
|
||||
* interrupting the natural flow of instructions whenever the Debugger is stepping through code.
|
||||
*/
|
||||
if (this.opFlags & PDP11.OPFLAG.WAIT) {
|
||||
/*
|
||||
* If checkInterruptQueue() found an interrupt, it will have dispatched it AND cleared
|
||||
* the WAIT flag; it won't return, so we're done.
|
||||
*/
|
||||
if ((this.opFlags & (PDP11.OPFLAG.INTQ_SPL | PDP11.OPFLAG.INTQ | PDP11.OPFLAG.WAIT)) /*&& nMinCycles*/) {
|
||||
this.checkInterruptQueue();
|
||||
/*
|
||||
* Since checkInterruptQueue() returned, we need to rewind the PC to the WAIT instruction;
|
||||
* we're going to play it safe and turn off the WAIT flag, because assuming the WAIT instruction
|
||||
* is still there, it will automatically re-enable it. TODO: Assert that the WAIT is still there.
|
||||
*/
|
||||
this.opFlags &= ~PDP11.OPFLAG.WAIT;
|
||||
this.regsGen[7] = (this.regsGen[7] - 2) & 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* By requiring nMinCycles to be non-zero before checking the interrupt queue, we avoid
|
||||
* interrupting the natural flow of instructions when the Debugger is stepping through code.
|
||||
*/
|
||||
if (nMinCycles && this.priorityReview) {
|
||||
this.checkInterruptQueue();
|
||||
}
|
||||
|
||||
if (!(this.regMMR0 & PDP11.MMR0.ABORT)) {
|
||||
this.regMMR1 = 0;
|
||||
this.regMMR2 = this.regsGen[7];
|
||||
}
|
||||
|
||||
/*
|
||||
* Snapshot the TF bit in opFlags, while simultaneously clearing all other opFlags (except WAIT);
|
||||
* Snapshot the TF bit in opFlags, while clearing all other opFlags (except those in PRESERVE);
|
||||
* we'll check the TRAP_TF bit in opFlags when we come back around for another opcode.
|
||||
*/
|
||||
this.opFlags = (this.opFlags & PDP11.OPFLAG.WAIT) | (this.regPSW & PDP11.PSW.TF);
|
||||
this.opFlags = (this.opFlags & PDP11.OPFLAG.PRESERVE) | (this.regPSW & PDP11.PSW.TF);
|
||||
|
||||
this.decode(this.getPCWord());
|
||||
|
||||
|
|
|
|||
|
|
@ -1269,8 +1269,10 @@ if (DEBUGGER) {
|
|||
* For our typically tiny bursts (usually single instructions), mimic what runCPU() does.
|
||||
*/
|
||||
try {
|
||||
nCycles = this.cpu.getBurstCycles(nCycles);
|
||||
var nCyclesStep = this.cpu.stepCPU(nCycles);
|
||||
if (nCyclesStep > 0) {
|
||||
this.cpu.updateTimers(nCycles);
|
||||
this.nCycles += nCyclesStep;
|
||||
this.cpu.addCycles(nCyclesStep, true);
|
||||
this.cpu.updateChecksum(nCyclesStep);
|
||||
|
|
@ -3460,7 +3462,8 @@ if (DEBUGGER) {
|
|||
*
|
||||
* The "tc" command interprets the count as a number of cycles rather than instructions,
|
||||
* allowing you to quickly execute large chunks of instructions with a single command; it
|
||||
* doesn't display anything until the the chunk has finished.
|
||||
* doesn't display anything until the the chunk has finished. "tc 1" is also a useful
|
||||
* command in that it doesn't inhibit interrupts like "t" or "tr" does.
|
||||
*
|
||||
* However, generally a more useful command is "bn", which allows you to break after some
|
||||
* number of instructions have been executed (as opposed to some number of cycles).
|
||||
|
|
|
|||
|
|
@ -161,12 +161,15 @@ var PDP11 = {
|
|||
* Internal operation state flags
|
||||
*/
|
||||
OPFLAG: {
|
||||
WAIT: 0x01, // WAIT operation in progress
|
||||
INTQ_SPL: 0x01, // INTQ triggered by SPL
|
||||
INTQ: 0x02, // call checkInterruptQueue()
|
||||
WAIT: 0x04, // WAIT operation in progress
|
||||
TRAP_TF: 0x10, // aka PDP11.PSW.TF
|
||||
TRAP_MMU: 0x20,
|
||||
TRAP_SP: 0x40,
|
||||
TRAP_MASK: 0x70,
|
||||
NO_FLAGS: 0x80 // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
|
||||
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
|
||||
},
|
||||
/*
|
||||
* Opcode reg (opcode bits 2-0)
|
||||
|
|
|
|||
|
|
@ -994,7 +994,7 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
|
|||
} while (idx >>= 1);
|
||||
}
|
||||
cpu.regPIR = result;
|
||||
cpu.priorityReview = 2;
|
||||
cpu.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
}
|
||||
break;
|
||||
case 0x3FFFF6: /*017777766*/ // CPU error
|
||||
|
|
|
|||
Loading…
Reference in a new issue