Added resume support for saving/restoring all active timers and IRQs, along with the run/stop state of the CPU
This commit is contained in:
parent
fac5c3c57d
commit
bb24ccaca6
12 changed files with 844 additions and 671 deletions
|
|
@ -117,25 +117,22 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
* These properties will be initialized by initCPU()
|
||||
*/
|
||||
this.flagC = this.flagV = this.flagZ = this.flagN = 0;
|
||||
this.regPSW = 0;
|
||||
this.regsGen = this.regsAlt = this.regsAltStack = [];
|
||||
this.pswMode = 0;
|
||||
this.mapMMR3 = [];
|
||||
this.mmuPDR = this.mmuPAR = this.regsUniMap = this.regsControl = [];
|
||||
this.regMBR = 0;
|
||||
this.opFlags = 0;
|
||||
this.srcMode = this.srcReg = 0;
|
||||
this.dstMode = this.dstReg = this.dstAddr = 0;
|
||||
this.regPSW = this.pswMode = 0;
|
||||
this.pswTrap = 0;
|
||||
this.regsGen = this.regsAlt = this.regsAltStack = [];
|
||||
this.regsPAR = this.regsPDR = this.regsUniMap = this.regsControl = [];
|
||||
this.opFlags = 0;
|
||||
|
||||
/*
|
||||
* These properties will be initialized by initMMU()
|
||||
*/
|
||||
this.regMMR0 = this.regMMR1 = this.regMMR2 = this.regMMR3 = 0;
|
||||
this.regErr = this.regPIR = this.regSLR = 0;
|
||||
this.regErr = this.regMBR = this.regPIR = this.regSLR = 0;
|
||||
this.mmuEnable = this.mmuLastMode = this.mmuLastPage = this.mmuMask = 0;
|
||||
this.addrLast = this.opLast = this.addrInvalid = 0;
|
||||
|
||||
this.mapMMR3 = [4,2,0,1]; // map from mode to MMR3 I/D bit
|
||||
|
||||
/*
|
||||
* Initialize processor operation to match the requested model.
|
||||
*
|
||||
|
|
@ -167,6 +164,7 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
}
|
||||
|
||||
this.nDisableTraps = 0;
|
||||
this.trapVector = this.trapReason = 0;
|
||||
|
||||
/** @type {IRQ|null} */
|
||||
this.irqNext = null; // the head of the active IRQ list, in priority order
|
||||
|
|
@ -174,8 +172,6 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/** @type {Array.<IRQ>} */
|
||||
this.aIRQs = []; // list of all IRQs, active or not (to be used for auto-configuration)
|
||||
|
||||
this.flags.complete = false;
|
||||
|
||||
this.getByte = this.getByteDirect = this.getByteChecked;
|
||||
this.getWord = this.getWordDirect = this.getWordChecked;
|
||||
this.setByte = this.setByteDirect = this.setByteChecked;
|
||||
|
|
@ -187,7 +183,10 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.readWord = this.readWordFromVirtual;
|
||||
this.writeWord = this.writeWordToVirtual;
|
||||
|
||||
this.trapVector = this.trapReason = 0;
|
||||
this.srcMode = this.srcReg = 0;
|
||||
this.dstMode = this.dstReg = this.dstAddr = 0;
|
||||
|
||||
this.flags.complete = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -271,36 +270,37 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.flagZ = f; // PSW Z bit (TODO: Why do we clear instead of set Z, like other flags?)
|
||||
this.flagN = 0x8000; // PSW N bit
|
||||
this.regPSW = 0x000f; // PSW other bits (TODO: What's the point of setting the flag bits here, too?)
|
||||
|
||||
this.regsGen = [ // General R0-R7
|
||||
0, 0, 0, 0, 0, 0, 0, this.addrReset, -1, -2, -3, -4, -5, -6, -7, -8
|
||||
];
|
||||
|
||||
this.regsAlt = [ // Alternate R0-R5
|
||||
0, 0, 0, 0, 0, 0
|
||||
];
|
||||
this.regsAltStack = [ // Alternate R6 stack pointers (KERNEL, SUPER, UNUSED, USER)
|
||||
0, 0, 0, 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)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
|
||||
[f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f], // mode 2 (not used)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
|
||||
];
|
||||
this.mmuPAR = [ // memory management PAR registers by mode
|
||||
this.regsPAR = [ // memory management PAR registers by mode
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // KERNEL (8 KIPAR regs followed by 8 KDPAR regs)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // mode 2 (not used)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
|
||||
];
|
||||
this.regsPDR = [ // 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)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // SUPER (8 SIPDR regs followed by 8 SDPDR regs)
|
||||
[f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f], // mode 2 (not used)
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // USER (8 UIPDR regs followed by 8 UDPDR regs)
|
||||
];
|
||||
this.regsUniMap = [ // 32 UNIBUS map registers
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
this.regsControl = [ // various control registers (177740-177756) we don't really care about
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
|
||||
this.pswMode = 0; // current memory management mode (see PDP11.MODE.KERNEL | SUPER | UNUSED | USER)
|
||||
this.pswTrap = -1;
|
||||
this.regMBR = 0;
|
||||
|
||||
/*
|
||||
|
|
@ -317,8 +317,6 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.srcMode = this.srcReg = 0;
|
||||
this.dstMode = this.dstReg = this.dstAddr = 0;
|
||||
|
||||
this.pswTrap = -1;
|
||||
|
||||
this.initMMU();
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +356,7 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/*
|
||||
* This stores the PC in the lower 16 bits, and any auto-incs or auto-decs from the last opcode in the
|
||||
* upper 16 bits; the lower 16 bits are used to update MMR2, and the upper 16 bits are used to update MMR1.
|
||||
* The upper bits are automatically zeroed at the start of every operation when PC is copied to opLast.
|
||||
* The upper bits are automatically zeroed at the start of every operation when the PC is copied to opLast.
|
||||
*/
|
||||
this.opLast = 0;
|
||||
|
||||
|
|
@ -639,30 +637,33 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.regsGen,
|
||||
this.regsAlt,
|
||||
this.regsAltStack,
|
||||
this.regsPAR,
|
||||
this.regsPDR,
|
||||
this.regsUniMap,
|
||||
this.regsControl,
|
||||
this.regErr,
|
||||
this.regMBR,
|
||||
this.regPIR,
|
||||
this.regSLR,
|
||||
this.pswTrap,
|
||||
this.pswMode,
|
||||
this.opFlags,
|
||||
this.regMMR0,
|
||||
this.regMMR1,
|
||||
this.regMMR2,
|
||||
this.regMMR3,
|
||||
this.mmuEnable,
|
||||
this.mmuLastMode,
|
||||
this.mmuLastPage,
|
||||
this.mmuPDR,
|
||||
this.mmuPAR,
|
||||
this.mmuEnable,
|
||||
this.mmuMask,
|
||||
this.addrLast,
|
||||
this.opLast
|
||||
this.opFlags,
|
||||
this.opLast,
|
||||
this.pswTrap,
|
||||
this.trapReason,
|
||||
this.trapVector
|
||||
]);
|
||||
state.set(1, [this.getPSW()]);
|
||||
state.set(2, [this.nTotalCycles, this.getSpeed()]);
|
||||
state.set(2, [this.nTotalCycles, this.getSpeed(), this.flags.autoStart]);
|
||||
state.set(3, this.saveIRQs());
|
||||
state.set(4, this.saveTimers());
|
||||
return state.data();
|
||||
}
|
||||
|
||||
|
|
@ -675,45 +676,48 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
*/
|
||||
restore(data)
|
||||
{
|
||||
var a;
|
||||
/*
|
||||
* ES6 ALERT: Gotta love these destructuring assignments, which make it easy to perform the inverse
|
||||
* of what save() does when it collects a bunch of object properties into an array.
|
||||
* ES6 ALERT: Gotta love these destructuring assignments, which make it easy to perform the
|
||||
* inverse of what save() does when it collects a bunch of object properties into an array.
|
||||
*/
|
||||
[
|
||||
this.regsGen,
|
||||
this.regsAlt,
|
||||
this.regsAltStack,
|
||||
this.regsPAR,
|
||||
this.regsPDR,
|
||||
this.regsUniMap,
|
||||
this.regsControl,
|
||||
this.regErr,
|
||||
this.regMBR,
|
||||
this.regPIR,
|
||||
this.regSLR,
|
||||
this.pswTrap,
|
||||
this.pswMode,
|
||||
this.opFlags,
|
||||
this.regMMR0,
|
||||
this.regMMR1,
|
||||
this.regMMR2,
|
||||
this.regMMR3,
|
||||
this.mmuEnable,
|
||||
this.mmuLastMode,
|
||||
this.mmuLastPage,
|
||||
this.mmuPDR,
|
||||
this.mmuPAR,
|
||||
this.mmuEnable,
|
||||
this.mmuMask,
|
||||
this.addrLast,
|
||||
this.opLast
|
||||
this.opFlags,
|
||||
this.opLast,
|
||||
this.pswTrap,
|
||||
this.trapReason,
|
||||
this.trapVector
|
||||
] = data[0];
|
||||
|
||||
a = data[1];
|
||||
var a = data[1];
|
||||
this.setPSW(a[0]);
|
||||
|
||||
a = data[2];
|
||||
this.nTotalCycles = a[0];
|
||||
this.setSpeed(a[1]);
|
||||
this.flags.autoStart = a[2];
|
||||
|
||||
this.restoreIRQs(data[3]);
|
||||
this.restoreTimers(data[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -985,8 +989,7 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
*/
|
||||
addIRQ(vector, priority, message)
|
||||
{
|
||||
var irq = {vector: vector, priority: priority, message: message || 0, next: null};
|
||||
irq.name = PDP11.VECTORS[vector];
|
||||
var irq = {vector: vector, priority: priority, message: message || 0, name: PDP11.VECTORS[vector], next: null};
|
||||
this.aIRQs.push(irq);
|
||||
return irq;
|
||||
}
|
||||
|
|
@ -1087,6 +1090,22 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* findIRQ(vector)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} vector
|
||||
* @return {IRQ|null}
|
||||
*/
|
||||
findIRQ(vector)
|
||||
{
|
||||
for (var i = 0; i < this.aIRQs.length; i++) {
|
||||
var irq = this.aIRQs[i];
|
||||
if (irq.vector === vector) return irq;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* checkIRQs(priority)
|
||||
*
|
||||
|
|
@ -1109,6 +1128,41 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
this.irqNext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveIRQs()
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @return {Array.<number>}
|
||||
*/
|
||||
saveIRQs()
|
||||
{
|
||||
var aIRQVectors = [];
|
||||
var irq = this.irqNext;
|
||||
while (irq) {
|
||||
aIRQVectors.push(irq.vector);
|
||||
irq = irq.next;
|
||||
}
|
||||
return aIRQVectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreIRQs(aIRQVectors)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {Array.<number>} aIRQVectors
|
||||
*/
|
||||
restoreIRQs(aIRQVectors)
|
||||
{
|
||||
for (var i = aIRQVectors.length - 1; i >= 0; i--) {
|
||||
var irq = this.findIRQ(aIRQVectors[i]);
|
||||
this.assert(irq != null);
|
||||
if (irq) {
|
||||
irq.next = this.irqNext;
|
||||
this.irqNext = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checkInterrupts()
|
||||
*
|
||||
|
|
@ -1615,8 +1669,8 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
* These next properties (in conjunction with setting PDP11.OPFLAG.TRAP_LAST) are purely an aid for the Debugger;
|
||||
* see getTrapStatus().
|
||||
*/
|
||||
this.trapVector = vector;
|
||||
this.trapReason = reason;
|
||||
this.trapVector = vector;
|
||||
|
||||
if (reason == PDP11.REASON.PANIC) {
|
||||
this.stopCPU();
|
||||
|
|
@ -1757,9 +1811,9 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
var page = addr >> 13;
|
||||
if (page > 7) mode |= 1;
|
||||
if (!(this.regMMR3 & this.mapMMR3[this.pswMode])) page &= 7;
|
||||
var pdr = this.mmuPDR[this.pswMode][page];
|
||||
var pdr = this.regsPDR[this.pswMode][page];
|
||||
var off = addr & 0x1fff;
|
||||
var paf = (this.mmuPAR[this.pswMode][page] << 6);
|
||||
var paf = (this.regsPAR[this.pswMode][page] << 6);
|
||||
addrPhysical = (paf + off) & this.mmuMask;
|
||||
if (addrPhysical >= BusPDP11.UNIBUS_22BIT) addrPhysical = this.mapUnibus(addrPhysical);
|
||||
a.push(addrPhysical); // a[0]
|
||||
|
|
@ -1838,8 +1892,8 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
|
||||
page = addrVirtual >> 13;
|
||||
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;
|
||||
pdr = this.regsPDR[this.pswMode][page];
|
||||
addr = ((this.regsPAR[this.pswMode][page] << 6) + (addrVirtual & 0x1fff)) & this.mmuMask;
|
||||
|
||||
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
|
||||
|
||||
|
|
@ -1928,7 +1982,7 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
/*
|
||||
* Aborts and traps: log FIRST trap and MOST RECENT abort
|
||||
*/
|
||||
this.mmuPDR[this.pswMode][page] = pdr;
|
||||
this.regsPDR[this.pswMode][page] = pdr;
|
||||
if (addr != ((BusPDP11.IOPAGE_22BIT | PDP11.UNIBUS.MMR0) & this.mmuMask) || this.pswMode) {
|
||||
this.mmuLastMode = this.pswMode;
|
||||
this.mmuLastPage = page;
|
||||
|
|
@ -2843,9 +2897,11 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
|
||||
if (!mode) {
|
||||
this.regsGen[reg] = (data = data < 0? this.regsGen[-data-1] : data);
|
||||
//noinspection JSUnresolvedFunction
|
||||
fnFlags.call(this, data);
|
||||
} else {
|
||||
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_WORD);
|
||||
//noinspection JSUnresolvedFunction
|
||||
fnFlags.call(this, (data = data < 0? this.regsGen[-data-1] : data));
|
||||
this.setWord(addr, data);
|
||||
}
|
||||
|
|
@ -2974,7 +3030,7 @@ class CPUStatePDP11 extends CPUPDP11 {
|
|||
|
||||
} while (this.nStepCycles > 0);
|
||||
|
||||
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
|
||||
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === false? -1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue