Restored support for custom memory controllers in the PDP11 emulator (to be used for UNIBUS I/O)
This commit is contained in:
parent
e865857c4b
commit
614fc11aca
11 changed files with 568 additions and 431 deletions
|
|
@ -59,7 +59,7 @@ if (NODE) {
|
|||
* is the ROM you expected.
|
||||
*
|
||||
* Finally, while making ROM "writable" may seem a contradiction in terms, I want to be able to load selected
|
||||
* CP/M binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
|
||||
* binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
|
||||
* simplest solution was to add the option to load binary files into memory as "writable" ROMs.
|
||||
*
|
||||
* Moreover, if a "writable" ROM is installed at addr 0x100, that triggers our "Fake CP/M" support, providing
|
||||
|
|
|
|||
|
|
@ -106,7 +106,9 @@ function BusPDP11(parmsBus, cpu, dbg)
|
|||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
|
||||
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
|
||||
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
|
||||
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
|
||||
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
|
||||
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
|
||||
*
|
||||
|
|
@ -117,7 +119,9 @@ function BusPDP11(parmsBus, cpu, dbg)
|
|||
*/
|
||||
this.addrTotal = Math.pow(2, this.nBusWidth);
|
||||
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
|
||||
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
|
||||
this.nBlockShift = (this.nBusWidth >> 1) + 2;
|
||||
if (this.nBlockShift < 10) this.nBlockShift = 10;
|
||||
if (this.nBlockShift > 15) this.nBlockShift = 15;
|
||||
this.nBlockSize = 1 << this.nBlockShift;
|
||||
this.nBlockLen = this.nBlockSize >> 2;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
|
|
@ -168,9 +172,17 @@ function BusPDP11(parmsBus, cpu, dbg)
|
|||
|
||||
Component.subclass(BusPDP11);
|
||||
|
||||
BusPDP11.IOBASE_VIRT = 0xE000; /*000160000*/
|
||||
BusPDP11.IOBASE_18BIT = 0x3E000; /*000760000*/
|
||||
BusPDP11.IOBASE_UNIBUS = 0x3C0000; /*017000000*/
|
||||
BusPDP11.IOBASE_22BIT = 0x3FE000; /*017760000*/
|
||||
BusPDP11.MAX_MEMORY = BusPDP11.IOBASE_UNIBUS - 16384; // Maximum memory address (need less memory for BSD 2.9 boot)
|
||||
BusPDP11.MAX_ADDRESS = 0x400000; /*020000000*/ // Register addresses are above 22 bit addressing
|
||||
|
||||
BusPDP11.ERROR = {
|
||||
ADD_MEM_INUSE: 1,
|
||||
ADD_MEM_BADRANGE: 2,
|
||||
SET_MEM_NOCTRL: 3,
|
||||
SET_MEM_BADRANGE: 4,
|
||||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
|
@ -222,6 +234,89 @@ BusPDP11.prototype.initMemory = function()
|
|||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
this.aMemBlocks[iBlock] = block;
|
||||
}
|
||||
this.afnCtrl = new Array(4);
|
||||
this.afnCtrl[0] = this.readCtrlByte;
|
||||
this.afnCtrl[1] = this.readCtrlWord;
|
||||
this.afnCtrl[2] = this.writeCtrlByte;
|
||||
this.afnCtrl[3] = this.writeCtrlWord;
|
||||
this.addMemory(BusPDP11.IOBASE_22BIT, 8192, MemoryPDP11.TYPE.CTRL, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* getMemoryBuffer(addr)
|
||||
*
|
||||
* Our Bus component also acts as custom memory controller, so it must also provide this function.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @return {Array} containing the buffer (and the offset within that buffer that corresponds to the requested block)
|
||||
*/
|
||||
BusPDP11.prototype.getMemoryBuffer = function(addr)
|
||||
{
|
||||
return [null, 0];
|
||||
};
|
||||
|
||||
/**
|
||||
* getMemoryAccess()
|
||||
*
|
||||
* Our Bus component also acts as custom memory controller, so it must also provide this function.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @return {Array.<function()>}
|
||||
*/
|
||||
BusPDP11.prototype.getMemoryAccess = function()
|
||||
{
|
||||
return this.afnCtrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* readCtrlByte(off, addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} [addr]
|
||||
* @return {number}
|
||||
*/
|
||||
BusPDP11.prototype.readCtrlByte = function(off, addr)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* readCtrlWord(off, addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} [addr]
|
||||
* @return {number}
|
||||
*/
|
||||
BusPDP11.prototype.readCtrlWord = function(off, addr)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeCtrlByte(off, b, addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} b (which should already be pre-masked to 8 bits)
|
||||
* @param {number} [addr]
|
||||
*/
|
||||
BusPDP11.prototype.writeCtrlByte = function(off, b, addr)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* writeCtrlWord(off, w, addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} w (which should already be pre-masked to 16 bits)
|
||||
* @param {number} [addr]
|
||||
*/
|
||||
BusPDP11.prototype.writeCtrlWord = function(off, w, addr)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -257,7 +352,7 @@ BusPDP11.prototype.powerUp = function(data, fRepower)
|
|||
};
|
||||
|
||||
/**
|
||||
* addMemory(addr, size, type)
|
||||
* addMemory(addr, size, type, controller)
|
||||
*
|
||||
* Adds new Memory blocks to the specified address range. Any Memory blocks previously
|
||||
* added to that range must first be removed via removeMemory(); otherwise, you'll get
|
||||
|
|
@ -283,9 +378,10 @@ BusPDP11.prototype.powerUp = function(data, fRepower)
|
|||
* @param {number} addr is the starting physical address of the request
|
||||
* @param {number} size of the request, in bytes
|
||||
* @param {number} type is one of the MemoryPDP11.TYPE constants
|
||||
* @param {Object} [controller] is an optional memory controller component
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
BusPDP11.prototype.addMemory = function(addr, size, type)
|
||||
BusPDP11.prototype.addMemory = function(addr, size, type, controller)
|
||||
{
|
||||
var addrNext = addr;
|
||||
var sizeLeft = size;
|
||||
|
|
@ -299,7 +395,7 @@ BusPDP11.prototype.addMemory = function(addr, size, type)
|
|||
if (sizeBlock > sizeLeft) sizeBlock = sizeLeft;
|
||||
|
||||
if (block && block.size) {
|
||||
if (block.type == type) {
|
||||
if (block.type == type && block.controller == controller) {
|
||||
/*
|
||||
* Where there is already a similar block with a non-zero size, we allow the allocation only if:
|
||||
*
|
||||
|
|
@ -324,7 +420,7 @@ BusPDP11.prototype.addMemory = function(addr, size, type)
|
|||
return this.reportError(BusPDP11.ERROR.ADD_MEM_INUSE, addrNext, sizeLeft);
|
||||
}
|
||||
|
||||
var blockNew = new MemoryPDP11(addrNext, sizeBlock, this.nBlockSize, type);
|
||||
var blockNew = new MemoryPDP11(addrNext, sizeBlock, this.nBlockSize, type, controller);
|
||||
blockNew.copyBreakpoints(this.dbg, block);
|
||||
this.aMemBlocks[iBlock++] = blockNew;
|
||||
|
||||
|
|
@ -456,6 +552,38 @@ BusPDP11.prototype.getMemoryBlocks = function(addr, size)
|
|||
return aBlocks;
|
||||
};
|
||||
|
||||
/**
|
||||
* setMemoryAccess(addr, size, afn, fQuiet)
|
||||
*
|
||||
* Updates the access functions in every block of the specified address range. Since the only components
|
||||
* that should be dynamically modifying the memory access functions are those that use addMemory() with a custom
|
||||
* memory controller, we require that the block(s) being updated do in fact have a controller.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @param {Array.<function()>} [afn]
|
||||
* @param {boolean} [fQuiet] (true if any error should be quietly logged)
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
BusPDP11.prototype.setMemoryAccess = function(addr, size, afn, fQuiet)
|
||||
{
|
||||
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
if (!block.controller) {
|
||||
return this.reportError(BusPDP11.ERROR.SET_MEM_NOCTRL, addr, size, fQuiet);
|
||||
}
|
||||
block.setAccess(afn, true);
|
||||
size -= this.nBlockSize;
|
||||
iBlock++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return this.reportError(BusPDP11.ERROR.SET_MEM_BADRANGE, addr, size);
|
||||
};
|
||||
|
||||
/**
|
||||
* setMemoryBlocks(addr, size, aBlocks, type)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -433,12 +433,12 @@ CPUPDP11.prototype.displayChecksum = function()
|
|||
* displayValue(sLabel, nValue, cch)
|
||||
*
|
||||
* This is principally for displaying register values, but in reality, it can be used to display any
|
||||
* numeric (hex) value bound to the given label.
|
||||
* numeric value bound to the given label.
|
||||
*
|
||||
* @this {CPUPDP11}
|
||||
* @param {string} sLabel
|
||||
* @param {number} nValue
|
||||
* @param {number} cch
|
||||
* @param {number} [cch]
|
||||
*/
|
||||
CPUPDP11.prototype.displayValue = function(sLabel, nValue, cch)
|
||||
{
|
||||
|
|
@ -448,10 +448,11 @@ CPUPDP11.prototype.displayValue = function(sLabel, nValue, cch)
|
|||
this.stopCPU();
|
||||
}
|
||||
var sVal;
|
||||
var nBase = this.dbg && this.dbg.nBase || 8;
|
||||
if (!this.flags.running || this.flags.displayLiveRegs) {
|
||||
sVal = str.toHex(nValue, cch);
|
||||
sVal = nBase == 8? str.toOct(nValue, cch) : str.toHex(nValue, cch);
|
||||
} else {
|
||||
sVal = "--------".substr(0, cch);
|
||||
sVal = "--------".substr(0, cch || 4);
|
||||
}
|
||||
/*
|
||||
* TODO: Determine if this test actually avoids any redrawing when a register hasn't changed, and/or if
|
||||
|
|
|
|||
|
|
@ -128,47 +128,16 @@ CPUStatePDP11.prototype.reset = function()
|
|||
CPUStatePDP11.prototype.initRegs = function()
|
||||
{
|
||||
/*
|
||||
* Instead of having separate flagC, flagZ and flagN variables, I would prefer to have only one: flagsCZN.
|
||||
* The C and N flags don't conflict; they are always bits 16 and 15 of the last 16-bit arithmetic result (or
|
||||
* bits 8 and 7 of the last 8-bit arithmetic result). The Z flag is a "bit" more complicated, because it's a
|
||||
* representation of bits 15-0 (or 7-0), which overlap the N flag bit. That overlap is fine after any given
|
||||
* arithmetic operation, because of the four possible N and Z combinations:
|
||||
*
|
||||
* N Z
|
||||
* - -
|
||||
* 0 0 Positive non-zero number
|
||||
* 0 1 Zero
|
||||
* 1 0 Negative non-zero number
|
||||
* 1 1 INVALID
|
||||
*
|
||||
* the fourth combination is an impossibility (the world of floating point numbers, with their NaNs, positive
|
||||
* and negative zeros, infinities, etc, is another story, which doesn't concern us here).
|
||||
*
|
||||
* The problem is that some intervening NON-arithmetic instruction (eg, SEN or SEZ) could be executed that sets
|
||||
* either N or Z independently, resulting in BOTH flags being set.
|
||||
*
|
||||
* One way to support that combination would be to define Z as the result of all non-sign bits. However,
|
||||
* that means that, after any arithmetic operation, we would have to propagate the sign bit of the result to
|
||||
* one or more other bits in flagsCZN; eg:
|
||||
*
|
||||
* flagsCZN = result | ((result & 0x8000) >> 1)
|
||||
* or:
|
||||
* flagsCZN = result | ((result & 0xffff)? 1 : 0)
|
||||
*
|
||||
* It's worth noting that all that extra work is actually required for only ONE 16-bit value: -32768 or 0x8000
|
||||
* (and ONE 8-bit value: -128 or 0x80), because all other negative numbers already have 1 or more lower bits set.
|
||||
*
|
||||
* A simpler approach would be to leave flagN independent, and only combine flagC and flagZ (into flagCZ).
|
||||
* Alternatively, we could combine flagC and flagN and leave flagZ independent; the choice is arbitrary. -JP
|
||||
* TODO: Verify the initial state of all PDP-11 flags (are they documented?)
|
||||
*/
|
||||
this.flagC = 0x10000; // PSW C bit
|
||||
this.flagV = 0x8000; // PSW V bit
|
||||
this.flagZ = 0xffff; // ~ PSW Z bit
|
||||
this.flagZ = 0xffff; // ~ PSW Z bit (TODO: Why is Z clear instead of set like all other flags?)
|
||||
this.flagN = 0x8000; // PSW N bit
|
||||
|
||||
this.PSW = 0xf; // PSW other bits
|
||||
this.regsGen = [ // General R0 - R7
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
0, 0, 0, 0, 0, 0, 0, this.resetAddr
|
||||
];
|
||||
this.regsAlt = [ // Alternate R0 - R5
|
||||
0, 0, 0, 0, 0, 0
|
||||
|
|
@ -212,7 +181,6 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
this.controlReg = [ // various control registers we don't really care about
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
this.debugPC = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -222,7 +190,6 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.resetRegs = function()
|
||||
{
|
||||
this.setPC(this.resetAddr);
|
||||
this.stackLimit = 0xff;
|
||||
this.CPU_Error = 0;
|
||||
this.interruptQueue = [];
|
||||
|
|
@ -292,12 +259,19 @@ CPUStatePDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sVal
|
|||
{
|
||||
var fBound = false;
|
||||
switch (sBinding) {
|
||||
case "PC":
|
||||
case "PSW":
|
||||
case "R0":
|
||||
case "R1":
|
||||
case "R2":
|
||||
case "R3":
|
||||
case "R4":
|
||||
case "R5":
|
||||
case "R6":
|
||||
case "R7":
|
||||
case "NF":
|
||||
case "ZF":
|
||||
case "VF":
|
||||
case "CF":
|
||||
case "PSW":
|
||||
this.bindings[sBinding] = control;
|
||||
this.cLiveRegs++;
|
||||
fBound = true;
|
||||
|
|
@ -309,6 +283,34 @@ CPUStatePDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sVal
|
|||
return fBound;
|
||||
};
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* This provides periodic Control Panel updates (a few times per second; see YIELDS_PER_STATUS).
|
||||
* this is where we take care of any DOM updates (eg, register values) while the CPU is running.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
CPUStatePDP11.prototype.updateStatus = function(fForce)
|
||||
{
|
||||
if (this.cLiveRegs) {
|
||||
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
|
||||
for (var i = 0; i < this.regsGen.length; i++) {
|
||||
this.displayValue('R'+i, this.regsGen[i]);
|
||||
}
|
||||
var regPSW = this.getPSW();
|
||||
this.displayValue("PSW", regPSW);
|
||||
this.displayValue("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
|
||||
this.displayValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
|
||||
this.displayValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
|
||||
this.displayValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
|
||||
}
|
||||
}
|
||||
var controlSpeed = this.bindings["speed"];
|
||||
if (controlSpeed) controlSpeed.textContent = this.getSpeedCurrent();
|
||||
};
|
||||
|
||||
/**
|
||||
* clearCF()
|
||||
*
|
||||
|
|
@ -316,7 +318,7 @@ CPUStatePDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sVal
|
|||
*/
|
||||
CPUStatePDP11.prototype.clearCF = function()
|
||||
{
|
||||
// this.resultZeroCarry &= 0xff;
|
||||
this.flagC = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -337,7 +339,7 @@ CPUStatePDP11.prototype.getCF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.setCF = function()
|
||||
{
|
||||
// this.resultZeroCarry |= 0x100;
|
||||
this.flagC = 0x10000;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -347,7 +349,7 @@ CPUStatePDP11.prototype.setCF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.clearVF = function()
|
||||
{
|
||||
// this.resultZeroCarry |= 0xff;
|
||||
this.flagV = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -368,7 +370,7 @@ CPUStatePDP11.prototype.getVF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.setVF = function()
|
||||
{
|
||||
// this.resultZeroCarry &= ~0xff;
|
||||
this.flagV = 0x8000;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -378,7 +380,7 @@ CPUStatePDP11.prototype.setVF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.clearZF = function()
|
||||
{
|
||||
// this.resultZeroCarry |= 0xff;
|
||||
this.flagZ = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -399,7 +401,7 @@ CPUStatePDP11.prototype.getZF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.setZF = function()
|
||||
{
|
||||
// this.resultZeroCarry &= ~0xff;
|
||||
this.flagZ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -409,7 +411,7 @@ CPUStatePDP11.prototype.setZF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.clearNF = function()
|
||||
{
|
||||
// if (this.getNF()) this.resultParitySign ^= 0xc0;
|
||||
this.flagN = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -420,7 +422,7 @@ CPUStatePDP11.prototype.clearNF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.getNF = function()
|
||||
{
|
||||
return (this.flagN >> (15 - PDP11.PSW.NF_SHIFT)) & PDP11.PSW.NF;
|
||||
return (this.flagN & 0x8000)? PDP11.PSW.NF : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -430,7 +432,7 @@ CPUStatePDP11.prototype.getNF = function()
|
|||
*/
|
||||
CPUStatePDP11.prototype.setNF = function()
|
||||
{
|
||||
// if (!this.getNF()) this.resultParitySign ^= 0xc0;
|
||||
this.flagN = 0x8000;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -527,48 +529,6 @@ CPUStatePDP11.prototype.requestHALT = function()
|
|||
this.endBurst();
|
||||
};
|
||||
|
||||
/**
|
||||
* updateReg(sReg, nValue, cch)
|
||||
*
|
||||
* This function helps updateStatus() by massaging the register names and values according to
|
||||
* CPU type before passing the call to displayValue(); in the "old days", updateStatus() called
|
||||
* displayValue() directly (although then it was called displayReg()).
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {string} sReg
|
||||
* @param {number} nValue
|
||||
* @param {number} [cch] (default is 4 hex digits)
|
||||
*/
|
||||
CPUStatePDP11.prototype.updateReg = function(sReg, nValue, cch)
|
||||
{
|
||||
this.displayValue(sReg, nValue, cch || 4);
|
||||
};
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* This provides periodic Control Panel updates (eg, a few times per second; see YIELDS_PER_STATUS).
|
||||
* this is where we take care of any DOM updates (eg, register values) while the CPU is running.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
CPUStatePDP11.prototype.updateStatus = function(fForce)
|
||||
{
|
||||
if (this.cLiveRegs) {
|
||||
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
|
||||
var regPSW = this.getPSW();
|
||||
this.updateReg("PSW", regPSW, 4);
|
||||
this.updateReg("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
|
||||
this.updateReg("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
|
||||
this.updateReg("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
|
||||
this.updateReg("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
|
||||
}
|
||||
}
|
||||
var controlSpeed = this.bindings["speed"];
|
||||
if (controlSpeed) controlSpeed.textContent = this.getSpeedCurrent();
|
||||
};
|
||||
|
||||
/**
|
||||
* interrupt(delay, priority, vector, callback)
|
||||
*
|
||||
|
|
@ -931,7 +891,7 @@ CPUStatePDP11.prototype.readWordByAddr = function(physicalAddress)
|
|||
return this.bus.access_iopage(physicalAddress, -1, 0);
|
||||
} else {
|
||||
if (physicalAddress >= 0) {
|
||||
return this.memory[physicalAddress >> 1];
|
||||
return this.bus.getShort(physicalAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -952,13 +912,14 @@ CPUStatePDP11.prototype.writeWordByAddr = function(physicalAddress, data)
|
|||
if (physicalAddress >= PDP11.MAX_ADDRESS) {
|
||||
return (this.regsGen[physicalAddress - PDP11.MAX_ADDRESS] = data);
|
||||
} else {
|
||||
if (physicalAddress >= PDP11.IOBASE_UNIBUS) {
|
||||
return this.bus.access_iopage(physicalAddress, data, 0);
|
||||
} else {
|
||||
// if (physicalAddress >= PDP11.IOBASE_UNIBUS) {
|
||||
// return this.bus.access_iopage(physicalAddress, data, 0);
|
||||
// } else {
|
||||
if (physicalAddress >= 0) {
|
||||
return (this.memory[physicalAddress >> 1] = data);
|
||||
this.bus.setShort(physicalAddress, data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
return physicalAddress;
|
||||
};
|
||||
|
|
@ -980,11 +941,7 @@ CPUStatePDP11.prototype.readByteByAddr = function(physicalAddress)
|
|||
return this.bus.access_iopage(physicalAddress, -1, 1);
|
||||
} else {
|
||||
if (physicalAddress >= 0) {
|
||||
result = this.memory[physicalAddress >> 1];
|
||||
if (physicalAddress & 1) {
|
||||
result = result >> 8;
|
||||
}
|
||||
return (result & 0xff);
|
||||
return this.bus.getByte(physicalAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1009,11 +966,8 @@ CPUStatePDP11.prototype.writeByteByAddr = function(physicalAddress, data)
|
|||
return this.bus.access_iopage(physicalAddress, data, 1);
|
||||
} else {
|
||||
if (physicalAddress >= 0) {
|
||||
if (physicalAddress & 1) {
|
||||
return (this.memory[physicalAddress >> 1] = (data << 8) | (this.memory[physicalAddress >> 1] & 0xff));
|
||||
} else {
|
||||
return (this.memory[physicalAddress >> 1] = (this.memory[physicalAddress >> 1] & 0xff00) | data);
|
||||
}
|
||||
this.bus.setByte(physicalAddress, data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1404,11 +1358,6 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
|||
}
|
||||
}
|
||||
}
|
||||
//if (this.regsGen[7] === this.debugPC) {
|
||||
//LOG_PRINT();
|
||||
//
|
||||
//}
|
||||
// Initialize this.memory before getting an instruction
|
||||
if (!(this.MMR0 & 0xe000)) {
|
||||
this.MMR1 = 0;
|
||||
this.MMR2 = this.regsGen[7];
|
||||
|
|
@ -2332,19 +2281,17 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
|||
break;
|
||||
case 0xA0: /*0000240*/ // CLR CC 00024M Part 1 without N
|
||||
case 0xA8: /*0000250*/ // CLR CC 00025M Part 2 with N
|
||||
//LOG_INSTRUCTION(instruction, 10, "CLR CC");
|
||||
if (instruction & 1) this.flagC = 0; // CLC
|
||||
if (instruction & 2) this.flagV = 0; // CLV
|
||||
if (instruction & 4) this.flagZ = 1; // CLZ
|
||||
if (instruction & 8) this.flagN = 0; // CLN
|
||||
if (instruction & 1) this.clearCF(); // CLC
|
||||
if (instruction & 2) this.clearVF(); // CLV
|
||||
if (instruction & 4) this.clearZF(); // CLZ
|
||||
if (instruction & 8) this.clearNF(); // CLN
|
||||
break;
|
||||
case 0xB0: /*0000260*/ // SET CC 00026M Part 1 without N
|
||||
case 0xB8: /*0000270*/ // SET CC 00026M Part 2 with N
|
||||
//LOG_INSTRUCTION(instruction, 10, "SET CC");
|
||||
if (instruction & 1) this.flagC = 0x10000; // SEC
|
||||
if (instruction & 2) this.flagV = 0x8000; // SEV
|
||||
if (instruction & 4) this.flagZ = 0; // SEZ
|
||||
if (instruction & 8) this.flagN = 0x8000; // SEN
|
||||
if (instruction & 1) this.setCF(); // SEC
|
||||
if (instruction & 2) this.setVF(); // SEV
|
||||
if (instruction & 4) this.setZF(); // SEZ
|
||||
if (instruction & 8) this.setNF(); // SEN
|
||||
break;
|
||||
default: // Misc instructions (decode ALL remaining bits) xxxxxx
|
||||
switch (instruction) {
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@ var PDP11 = {
|
|||
PRI_SHIFT: 5,
|
||||
UNUSED: 0x0700, // bits 8-10: unused
|
||||
REGSET: 0x0800, // bit 11: Register Set
|
||||
PMODE: 0x3000, // bits 12-13: Previous Mode (see PDP11.MODE)
|
||||
PMODE: 0x3000, // bits 12-13: Prev Mode (see PDP11.MODE)
|
||||
PMODE_SHIFT:12,
|
||||
CMODE: 0xC000, // bits 14-15: Current Mode (see PDP11.MODE)
|
||||
CMODE: 0xC000, // bits 14-15: Curr Mode (see PDP11.MODE)
|
||||
CMODE_SHIFT:14
|
||||
},
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
})() : false);
|
||||
|
||||
/**
|
||||
* MemoryPDP11(addr, used, size, type)
|
||||
* MemoryPDP11(addr, used, size, type, controller)
|
||||
*
|
||||
* The Bus component allocates MemoryPDP11 objects so that each has a memory buffer with a
|
||||
* block-granular starting address and an address range equal to bus.nBlockSize; however,
|
||||
|
|
@ -94,8 +94,9 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
* @param {number} [used] portion of block in bytes (0 for none); must be a multiple of 4
|
||||
* @param {number} [size] of block's buffer in bytes (0 for none); must be a multiple of 4
|
||||
* @param {number} [type] is one of the MemoryPDP11.TYPE constants (default is MemoryPDP11.TYPE.NONE)
|
||||
* @param {Object} [controller] is an optional memory controller component
|
||||
*/
|
||||
function MemoryPDP11(addr, used, size, type)
|
||||
function MemoryPDP11(addr, used, size, type, controller)
|
||||
{
|
||||
var i;
|
||||
this.id = (MemoryPDP11.idBlock += 2);
|
||||
|
|
@ -106,6 +107,7 @@ function MemoryPDP11(addr, used, size, type)
|
|||
this.size = size || 0;
|
||||
this.type = type || MemoryPDP11.TYPE.NONE;
|
||||
this.fReadOnly = (type == MemoryPDP11.TYPE.ROM);
|
||||
this.controller = null;
|
||||
this.copyBreakpoints(); // initialize the block's Debugger info; the caller will reinitialize
|
||||
|
||||
/*
|
||||
|
|
@ -129,6 +131,19 @@ function MemoryPDP11(addr, used, size, type)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a controller is specified, the controller must provide a buffer,
|
||||
* via getMemoryBuffer(), and memory access functions, via getMemoryAccess().
|
||||
*/
|
||||
if (controller) {
|
||||
this.controller = controller;
|
||||
var a = controller.getMemoryBuffer(addr);
|
||||
this.adw = a[0];
|
||||
this.offset = a[1];
|
||||
this.setAccess(controller.getMemoryAccess());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the normal case: allocate a buffer that provides 8 bits of data per address;
|
||||
* no controller is required because our default memory access functions (see afnMemory)
|
||||
|
|
@ -285,7 +300,10 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
save: function() {
|
||||
var adw, i;
|
||||
if (BYTEARRAYS) {
|
||||
if (this.controller) {
|
||||
adw = null;
|
||||
}
|
||||
else if (BYTEARRAYS) {
|
||||
adw = new Array(this.size >> 2);
|
||||
var off = 0;
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
|
|
@ -327,6 +345,9 @@ MemoryPDP11.prototype = {
|
|||
* @return {boolean} true if successful, false if block size mismatch
|
||||
*/
|
||||
restore: function(adw) {
|
||||
if (this.controller) {
|
||||
return (adw == null);
|
||||
}
|
||||
/*
|
||||
* At this point, it's a consistency error for adw to be null; it's happened once already,
|
||||
* when there was a restore bug in the Video component that added the frame buffer at the video
|
||||
|
|
@ -809,18 +830,45 @@ MemoryPDP11.prototype = {
|
|||
|
||||
/*
|
||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
||||
* uses these defaults when any of the 4 handlers (ie, 2 read handlers and 2 write handlers) are undefined.
|
||||
*
|
||||
MemoryPDP11.afnNone = [MemoryPDP11.prototype.readNone, MemoryPDP11.prototype.readShortDefault, MemoryPDP11.prototype.writeNone, MemoryPDP11.prototype.writeShortDefault];
|
||||
MemoryPDP11.afnNone = [
|
||||
MemoryPDP11.prototype.readNone,
|
||||
MemoryPDP11.prototype.readShortDefault,
|
||||
MemoryPDP11.prototype.writeNone,
|
||||
MemoryPDP11.prototype.writeShortDefault
|
||||
];
|
||||
*/
|
||||
MemoryPDP11.afnNone = [];
|
||||
|
||||
MemoryPDP11.afnNone = [];
|
||||
MemoryPDP11.afnMemory = [MemoryPDP11.prototype.readByteMemory, MemoryPDP11.prototype.readShortMemory, MemoryPDP11.prototype.writeByteMemory, MemoryPDP11.prototype.writeShortMemory];
|
||||
MemoryPDP11.afnChecked = [MemoryPDP11.prototype.readByteChecked, MemoryPDP11.prototype.readShortChecked, MemoryPDP11.prototype.writeByteChecked, MemoryPDP11.prototype.writeShortChecked];
|
||||
MemoryPDP11.afnMemory = [
|
||||
MemoryPDP11.prototype.readByteMemory,
|
||||
MemoryPDP11.prototype.readShortMemory,
|
||||
MemoryPDP11.prototype.writeByteMemory,
|
||||
MemoryPDP11.prototype.writeShortMemory
|
||||
];
|
||||
|
||||
MemoryPDP11.afnChecked = [
|
||||
MemoryPDP11.prototype.readByteChecked,
|
||||
MemoryPDP11.prototype.readShortChecked,
|
||||
MemoryPDP11.prototype.writeByteChecked,
|
||||
MemoryPDP11.prototype.writeShortChecked
|
||||
];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
MemoryPDP11.afnArrayBE = [MemoryPDP11.prototype.readByteBE, MemoryPDP11.prototype.readShortBE, MemoryPDP11.prototype.writeByteBE, MemoryPDP11.prototype.writeShortBE];
|
||||
MemoryPDP11.afnArrayLE = [MemoryPDP11.prototype.readByteLE, MemoryPDP11.prototype.readShortLE, MemoryPDP11.prototype.writeByteLE, MemoryPDP11.prototype.writeShortLE];
|
||||
MemoryPDP11.afnArrayBE = [
|
||||
MemoryPDP11.prototype.readByteBE,
|
||||
MemoryPDP11.prototype.readShortBE,
|
||||
MemoryPDP11.prototype.writeByteBE,
|
||||
MemoryPDP11.prototype.writeShortBE
|
||||
];
|
||||
|
||||
MemoryPDP11.afnArrayLE = [
|
||||
MemoryPDP11.prototype.readByteLE,
|
||||
MemoryPDP11.prototype.readShortLE,
|
||||
MemoryPDP11.prototype.writeByteLE,
|
||||
MemoryPDP11.prototype.writeShortLE
|
||||
];
|
||||
}
|
||||
|
||||
if (NODE) module.exports = MemoryPDP11;
|
||||
|
|
|
|||
|
|
@ -60,12 +60,9 @@ if (NODE) {
|
|||
* is the ROM you expected.
|
||||
*
|
||||
* Finally, while making ROM "writable" may seem a contradiction in terms, I want to be able to load selected
|
||||
* CP/M binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
|
||||
* binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
|
||||
* simplest solution was to add the option to load binary files into memory as "writable" ROMs.
|
||||
*
|
||||
* Moreover, if a "writable" ROM is installed at addr 0x100, that triggers our "Fake CP/M" support, providing
|
||||
* a quick-and-dirty means of loading simple CP/M test binaries. See addROM() for details.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsROM
|
||||
|
|
|
|||
Loading…
Reference in a new issue