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
|
|
@ -1,24 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<panel id="panel" width="100%" padding="8px">
|
||||
<name>Control Panel</name>
|
||||
<control type="container" class="pcjs-textarea" width="99%" padright="8px">
|
||||
<control type="container" class="pcjs-textarea" width="60%" padbottom="8px" padright="8px">
|
||||
<control type="textarea" binding="print" width="100%" height="252px" pos="relative" style="resize:vertical;"/>
|
||||
</control>
|
||||
<control type="container" class="pcjs-registers" width="100%" padleft="8px">
|
||||
<control type="register" label="PC" binding="PC" width="40px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="container" pos="left">
|
||||
<control type="flag" label="S" binding="SF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
<control type="flag" label="Z" binding="ZF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
<control type="flag" label="C" binding="CF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
</control>
|
||||
<control type="description" padleft="16px" padbottom="8px">Speed:</control>
|
||||
<control type="description" binding="speed" padleft="4px" padbottom="8px">Stopped</control>
|
||||
</control>
|
||||
<control type="container" padbottom="8px">
|
||||
<control type="text" binding="debugInput" width="100px"/>
|
||||
<control type="container" width="100%">
|
||||
<control type="text" binding="debugInput" width="auto"/>
|
||||
<control type="button" binding="debugEnter">Enter</control>
|
||||
<control type="button" binding="clear">Clear</control>
|
||||
<control type="button" binding="run" padleft="32px">Run</control>
|
||||
</control>
|
||||
</control>
|
||||
<control type="container" class="pcjs-registers" width="35%" padleft="8px">
|
||||
<control type="register" label="R0" binding="R0" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="R1" binding="R1" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="R2" binding="R2" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="R3" binding="R3" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="R4" binding="R4" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="R5" binding="R5" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="SP" binding="R6" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="register" label="PC" binding="R7" pos="left" width="60px" padright="8px" padbottom="8px">0000</control>
|
||||
<control type="container" pos="left">
|
||||
<control type="flag" label="N" binding="NF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
<control type="flag" label="Z" binding="ZF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
<control type="flag" label="V" binding="VF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
<control type="flag" label="C" binding="CF" width="8px" padright="4px" padbottom="8px">0</control>
|
||||
</control>
|
||||
</control>
|
||||
<control type="container" padbottom="8px" padleft="8px">
|
||||
<control type="button" binding="run">Run</control>
|
||||
<control type="button" binding="step">Step</control>
|
||||
<control type="button" binding="reset">Reset</control>
|
||||
<control type="button" binding="setSpeed">Fast</control>
|
||||
|
|
|
|||
|
|
@ -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.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
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue