Groundwork for a PDP-11 emulator (starting with a gutted copy of PC8080)
This commit is contained in:
parent
a08fcab276
commit
4b778b281d
80 changed files with 24230 additions and 427 deletions
|
|
@ -57,6 +57,7 @@ var usr = require("../../shared/lib/usrlib");
|
|||
* @property {Array.<string>} pcCSSFiles
|
||||
* @property {Array.<string>} pcX86Files
|
||||
* @property {Array.<string>} pc8080Files
|
||||
* @property {Array.<string>} pdp11Files
|
||||
*/
|
||||
var pkg = require("../../../package.json");
|
||||
|
||||
|
|
@ -170,7 +171,8 @@ var aMachineFiles = {
|
|||
'C1P': pkg.c1pCSSFiles.concat(pkg.c1pJSFiles), // will be deprecated when PC6502 becomes available
|
||||
'PC': pkg.pcCSSFiles.concat(pkg.pcX86Files), // deprecated (same as PCx86)
|
||||
'PCx86': pkg.pcCSSFiles.concat(pkg.pcX86Files),
|
||||
'PC8080': pkg.pcCSSFiles.concat(pkg.pc8080Files)
|
||||
'PC8080': pkg.pcCSSFiles.concat(pkg.pc8080Files),
|
||||
'PDP11': pkg.pcCSSFiles.concat(pkg.pdp11Files)
|
||||
};
|
||||
var aMachineFileTypes = {
|
||||
'head': [".css"], // put BOTH ".css" and ".js" here if convertMDMachineLinks() embeds its own scripts
|
||||
|
|
@ -1437,7 +1439,7 @@ HTMLOut.prototype.getMachineXML = function(sToken, sIndent, aParms, sXMLFile, sS
|
|||
* The common denominator in both sets is either "/pc" or "/c1p", which in turn indicates the type of machine
|
||||
* (eg, "PC", "C1P", etc).
|
||||
*/
|
||||
aMatch = sStyleSheet.match(/\/(pc|c1p)([^/]*)/);
|
||||
aMatch = sStyleSheet.match(/\/(pc|c1p|pdp)([^/]*)/);
|
||||
if (aMatch) {
|
||||
var sMachineType = aMatch[1].toUpperCase() + (aMatch[2] != "js"? aMatch[2] : "");
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1168,7 +1168,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
|
|||
* Start looking for Markdown-style machine links now...
|
||||
*/
|
||||
var cMatches = 0;
|
||||
var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P)([^:!|]*)([:!|])(.*?)"\)/gi;
|
||||
var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P|PDP)([^:!|]*)([:!|])(.*?)"\)/gi;
|
||||
|
||||
while ((aMatch = reMachines.exec(sBlock))) {
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ if (NODE) {
|
|||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsBus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
function Bus8080(parmsBus, cpu, dbg)
|
||||
|
|
@ -191,7 +191,7 @@ var BlockInfo;
|
|||
Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* Bus8080Info object definition (returned by scanMemory())
|
||||
* BusInfo8080 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
|
|
@ -203,7 +203,7 @@ Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
|||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var Bus8080Info;
|
||||
var BusInfo8080;
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
|
|
@ -364,10 +364,10 @@ Bus8080.prototype.cleanMemory = function(addr, size)
|
|||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
* Returns a Bus8080Info object for the specified address range.
|
||||
* Returns a BusInfo8080 object for the specified address range.
|
||||
*
|
||||
* @this {Bus8080}
|
||||
* @param {Object} [info] previous Bus8080Info, if any
|
||||
* @param {Object} [info] previous BusInfo8080, if any
|
||||
* @param {number} [addr] starting address of range (0 if none provided)
|
||||
* @param {number} [size] size of range, in bytes (up to end of address space if none provided)
|
||||
* @return {Object} updated info (or new info if no previous info provided)
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ ChipSet8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue
|
|||
* @this {ChipSet8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
ChipSet8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
|
|||
* where we know getComponentByType() will only return an CPUState object or null), wrap the expression
|
||||
* in parentheses. I never knew this until I stumbled across it in "Closure: The Definitive Guide".
|
||||
*/
|
||||
this.cpu = /** @type {CPU8080State} */ (Component.getComponentByType("CPU", this.id));
|
||||
this.cpu = /** @type {CPUState8080} */ (Component.getComponentByType("CPU", this.id));
|
||||
if (!this.cpu) {
|
||||
Component.error("Unable to find CPU component");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ if (NODE) {
|
|||
*
|
||||
* The CPU8080 class supports the following (parmsCPU) properties:
|
||||
*
|
||||
* cycles: the machine's base cycles per second; the CPU8080State constructor
|
||||
* cycles: the machine's base cycles per second; the CPUState8080 constructor
|
||||
* will provide us with a default (based on the CPU model) to use as a fallback.
|
||||
*
|
||||
* multiplier: base cycle multiplier; default is 1.
|
||||
|
|
@ -64,7 +64,7 @@ if (NODE) {
|
|||
* This component is primarily responsible for interfacing the CPU with the outside
|
||||
* world (eg, Panel and Debugger components), and managing overall CPU operation.
|
||||
*
|
||||
* It is extended by the CPU8080State component, where the simulation control logic resides.
|
||||
* It is extended by the CPUState8080 component, where the simulation control logic resides.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
|
|
@ -199,7 +199,7 @@ CPU8080.prototype.reset = function()
|
|||
/**
|
||||
* save()
|
||||
*
|
||||
* This is a placeholder for save support (overridden by the CPUState component).
|
||||
* This is a placeholder for save support (overridden by the CPUState8080 component).
|
||||
*
|
||||
* @this {CPU8080}
|
||||
* @return {Object|null}
|
||||
|
|
@ -212,7 +212,7 @@ CPU8080.prototype.save = function()
|
|||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This is a placeholder for restore support (overridden by the CPUState component).
|
||||
* This is a placeholder for restore support (overridden by the CPUState8080 component).
|
||||
*
|
||||
* @this {CPU8080}
|
||||
* @param {Object} data
|
||||
|
|
@ -339,7 +339,7 @@ CPU8080.prototype.isRunning = function()
|
|||
/**
|
||||
* getChecksum()
|
||||
*
|
||||
* This will be implemented by the CPUState component.
|
||||
* This will be implemented by the CPUState8080 component.
|
||||
*
|
||||
* @this {CPU8080}
|
||||
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
|
||||
|
|
@ -1145,7 +1145,7 @@ CPU8080.prototype.startCPU = function(fUpdateFocus)
|
|||
/**
|
||||
* stepCPU(nMinCycles)
|
||||
*
|
||||
* This will be implemented by the CPUState component.
|
||||
* This will be implemented by the CPUState8080 component.
|
||||
*
|
||||
* @this {CPU8080}
|
||||
* @param {number} nMinCycles (0 implies a single-step, and therefore breakpoints should be ignored)
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@ var CPUDef8080 = {
|
|||
* Interrupt-related flags (stored in intFlags)
|
||||
*/
|
||||
INTFLAG: {
|
||||
NONE: 0x0000,
|
||||
INTR: 0x00ff, // mask for 8 bits, representing interrupt levels 0-7
|
||||
HALT: 0x0100 // halt requested; see opHLT()
|
||||
NONE: 0x0000,
|
||||
INTR: 0x00ff, // mask for 8 bits, representing interrupt levels 0-7
|
||||
HALT: 0x0100 // halt requested; see opHLT()
|
||||
},
|
||||
/*
|
||||
* Opcode definitions
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -44,9 +44,9 @@ if (NODE) {
|
|||
}
|
||||
|
||||
/**
|
||||
* CPU8080State(parmsCPU)
|
||||
* CPUState8080(parmsCPU)
|
||||
*
|
||||
* The CPU8080State class uses the following (parmsCPU) properties:
|
||||
* The CPUState8080 class uses the following (parmsCPU) properties:
|
||||
*
|
||||
* model: a number (eg, 8080) that should match one of the CPUDef8080.MODEL_* values
|
||||
*
|
||||
|
|
@ -54,14 +54,14 @@ if (NODE) {
|
|||
* constructor, along with a default speed (cycles per second) based on the specified (or default)
|
||||
* CPU model number.
|
||||
*
|
||||
* The CPU8080State class was initially written to simulate a 8080 microprocessor, although over time
|
||||
* The CPUState8080 class was initially written to simulate a 8080 microprocessor, although over time
|
||||
* it may evolved to support other microprocessors (eg, the Zilog Z80).
|
||||
*
|
||||
* @constructor
|
||||
* @extends CPU8080
|
||||
* @param {Object} parmsCPU
|
||||
*/
|
||||
function CPU8080State(parmsCPU)
|
||||
function CPUState8080(parmsCPU)
|
||||
{
|
||||
this.model = +parmsCPU['model'] || CPUDef8080.MODEL_8080;
|
||||
|
||||
|
|
@ -105,17 +105,17 @@ function CPU8080State(parmsCPU)
|
|||
this.resetRegs();
|
||||
}
|
||||
|
||||
Component.subclass(CPU8080State, CPU8080);
|
||||
Component.subclass(CPUState8080, CPU8080);
|
||||
|
||||
/**
|
||||
* addHaltCheck(fn)
|
||||
*
|
||||
* Records a function that will be called during HLT opcode processing.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {function(number)} fn
|
||||
*/
|
||||
CPU8080State.prototype.addHaltCheck = function(fn)
|
||||
CPUState8080.prototype.addHaltCheck = function(fn)
|
||||
{
|
||||
this.afnHalt.push(fn);
|
||||
};
|
||||
|
|
@ -129,9 +129,9 @@ CPU8080State.prototype.addHaltCheck = function(fn)
|
|||
* a Closure Compiler optimization is defeated when generating the function array at run-time instead of at
|
||||
* compile-time.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.initProcessor = function()
|
||||
CPUState8080.prototype.initProcessor = function()
|
||||
{
|
||||
this.aOps = CPUDef8080.aOps8080;
|
||||
};
|
||||
|
|
@ -139,9 +139,9 @@ CPU8080State.prototype.initProcessor = function()
|
|||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.reset = function()
|
||||
CPUState8080.prototype.reset = function()
|
||||
{
|
||||
if (this.flags.fRunning) this.stopCPU();
|
||||
this.resetRegs();
|
||||
|
|
@ -153,9 +153,9 @@ CPU8080State.prototype.reset = function()
|
|||
/**
|
||||
* resetRegs()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.resetRegs = function()
|
||||
CPUState8080.prototype.resetRegs = function()
|
||||
{
|
||||
this.regA = 0;
|
||||
this.regB = 0;
|
||||
|
|
@ -183,10 +183,10 @@ CPU8080State.prototype.resetRegs = function()
|
|||
/**
|
||||
* setReset(addr)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPU8080State.prototype.setReset = function(addr)
|
||||
CPUState8080.prototype.setReset = function(addr)
|
||||
{
|
||||
this.addrReset = addr;
|
||||
this.setPC(addr);
|
||||
|
|
@ -195,10 +195,10 @@ CPU8080State.prototype.setReset = function(addr)
|
|||
/**
|
||||
* getChecksum()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
|
||||
*/
|
||||
CPU8080State.prototype.getChecksum = function()
|
||||
CPUState8080.prototype.getChecksum = function()
|
||||
{
|
||||
var sum = (this.regA + this.regB + this.regC + this.regD + this.regE + this.regH + this.regL)|0;
|
||||
sum = (sum + this.getSP() + this.getPC() + this.getPS())|0;
|
||||
|
|
@ -208,12 +208,12 @@ CPU8080State.prototype.getChecksum = function()
|
|||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the CPU8080State component.
|
||||
* This implements save support for the CPUState8080 component.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {Object|null}
|
||||
*/
|
||||
CPU8080State.prototype.save = function()
|
||||
CPUState8080.prototype.save = function()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, [this.regA, this.regB, this.regC, this.regD, this.regE, this.regH, this.regL, this.getSP(), this.getPC(), this.getPS()]);
|
||||
|
|
@ -225,13 +225,13 @@ CPU8080State.prototype.save = function()
|
|||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the CPU8080State component.
|
||||
* This implements restore support for the CPUState8080 component.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if restore successful, false if not
|
||||
*/
|
||||
CPU8080State.prototype.restore = function(data)
|
||||
CPUState8080.prototype.restore = function(data)
|
||||
{
|
||||
var a = data[0];
|
||||
this.regA = a[0];
|
||||
|
|
@ -254,14 +254,14 @@ CPU8080State.prototype.restore = function(data)
|
|||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "AX")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
CPU8080State.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
CPUState8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
var fBound = false;
|
||||
switch (sBinding) {
|
||||
|
|
@ -298,10 +298,10 @@ CPU8080State.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
/**
|
||||
* getBC()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getBC = function()
|
||||
CPUState8080.prototype.getBC = function()
|
||||
{
|
||||
return (this.regB << 8) | this.regC;
|
||||
};
|
||||
|
|
@ -309,10 +309,10 @@ CPU8080State.prototype.getBC = function()
|
|||
/**
|
||||
* setBC(w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPU8080State.prototype.setBC = function(w)
|
||||
CPUState8080.prototype.setBC = function(w)
|
||||
{
|
||||
this.regB = (w >> 8) & 0xff;
|
||||
this.regC = w & 0xff;
|
||||
|
|
@ -321,10 +321,10 @@ CPU8080State.prototype.setBC = function(w)
|
|||
/**
|
||||
* getDE()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getDE = function()
|
||||
CPUState8080.prototype.getDE = function()
|
||||
{
|
||||
return (this.regD << 8) | this.regE;
|
||||
};
|
||||
|
|
@ -332,10 +332,10 @@ CPU8080State.prototype.getDE = function()
|
|||
/**
|
||||
* setDE(w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPU8080State.prototype.setDE = function(w)
|
||||
CPUState8080.prototype.setDE = function(w)
|
||||
{
|
||||
this.regD = (w >> 8) & 0xff;
|
||||
this.regE = w & 0xff;
|
||||
|
|
@ -344,10 +344,10 @@ CPU8080State.prototype.setDE = function(w)
|
|||
/**
|
||||
* getHL()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getHL = function()
|
||||
CPUState8080.prototype.getHL = function()
|
||||
{
|
||||
return (this.regH << 8) | this.regL;
|
||||
};
|
||||
|
|
@ -355,10 +355,10 @@ CPU8080State.prototype.getHL = function()
|
|||
/**
|
||||
* setHL(w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPU8080State.prototype.setHL = function(w)
|
||||
CPUState8080.prototype.setHL = function(w)
|
||||
{
|
||||
this.regH = (w >> 8) & 0xff;
|
||||
this.regL = w & 0xff;
|
||||
|
|
@ -367,10 +367,10 @@ CPU8080State.prototype.setHL = function(w)
|
|||
/**
|
||||
* getSP()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getSP = function()
|
||||
CPUState8080.prototype.getSP = function()
|
||||
{
|
||||
return this.regSP;
|
||||
};
|
||||
|
|
@ -378,10 +378,10 @@ CPU8080State.prototype.getSP = function()
|
|||
/**
|
||||
* setSP(off)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} off
|
||||
*/
|
||||
CPU8080State.prototype.setSP = function(off)
|
||||
CPUState8080.prototype.setSP = function(off)
|
||||
{
|
||||
this.regSP = off & 0xffff;
|
||||
};
|
||||
|
|
@ -389,10 +389,10 @@ CPU8080State.prototype.setSP = function(off)
|
|||
/**
|
||||
* getPC()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getPC = function()
|
||||
CPUState8080.prototype.getPC = function()
|
||||
{
|
||||
return this.regPC;
|
||||
};
|
||||
|
|
@ -400,11 +400,11 @@ CPU8080State.prototype.getPC = function()
|
|||
/**
|
||||
* offPC()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.offPC = function(off)
|
||||
CPUState8080.prototype.offPC = function(off)
|
||||
{
|
||||
return (this.regPC + off) & 0xffff;
|
||||
};
|
||||
|
|
@ -412,10 +412,10 @@ CPU8080State.prototype.offPC = function(off)
|
|||
/**
|
||||
* setPC(off)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} off
|
||||
*/
|
||||
CPU8080State.prototype.setPC = function(off)
|
||||
CPUState8080.prototype.setPC = function(off)
|
||||
{
|
||||
this.regPC = off & 0xffff;
|
||||
};
|
||||
|
|
@ -423,9 +423,9 @@ CPU8080State.prototype.setPC = function(off)
|
|||
/**
|
||||
* clearCF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearCF = function()
|
||||
CPUState8080.prototype.clearCF = function()
|
||||
{
|
||||
this.resultZeroCarry &= 0xff;
|
||||
};
|
||||
|
|
@ -433,10 +433,10 @@ CPU8080State.prototype.clearCF = function()
|
|||
/**
|
||||
* getCF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or 1 (CPUDef8080.PS.CF)
|
||||
*/
|
||||
CPU8080State.prototype.getCF = function()
|
||||
CPUState8080.prototype.getCF = function()
|
||||
{
|
||||
return (this.resultZeroCarry & 0x100)? CPUDef8080.PS.CF : 0;
|
||||
};
|
||||
|
|
@ -444,9 +444,9 @@ CPU8080State.prototype.getCF = function()
|
|||
/**
|
||||
* setCF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setCF = function()
|
||||
CPUState8080.prototype.setCF = function()
|
||||
{
|
||||
this.resultZeroCarry |= 0x100;
|
||||
};
|
||||
|
|
@ -454,10 +454,10 @@ CPU8080State.prototype.setCF = function()
|
|||
/**
|
||||
* updateCF(CF)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} CF (0x000 or 0x100)
|
||||
*/
|
||||
CPU8080State.prototype.updateCF = function(CF)
|
||||
CPUState8080.prototype.updateCF = function(CF)
|
||||
{
|
||||
this.resultZeroCarry = (this.resultZeroCarry & 0xff) | CF;
|
||||
};
|
||||
|
|
@ -465,9 +465,9 @@ CPU8080State.prototype.updateCF = function(CF)
|
|||
/**
|
||||
* clearPF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearPF = function()
|
||||
CPUState8080.prototype.clearPF = function()
|
||||
{
|
||||
if (this.getPF()) this.resultParitySign ^= 0x1;
|
||||
};
|
||||
|
|
@ -475,10 +475,10 @@ CPU8080State.prototype.clearPF = function()
|
|||
/**
|
||||
* getPF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.PF
|
||||
*/
|
||||
CPU8080State.prototype.getPF = function()
|
||||
CPUState8080.prototype.getPF = function()
|
||||
{
|
||||
return (CPUDef8080.PARITY[this.resultParitySign & 0xff])? CPUDef8080.PS.PF : 0;
|
||||
};
|
||||
|
|
@ -486,9 +486,9 @@ CPU8080State.prototype.getPF = function()
|
|||
/**
|
||||
* setPF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setPF = function()
|
||||
CPUState8080.prototype.setPF = function()
|
||||
{
|
||||
if (!this.getPF()) this.resultParitySign ^= 0x1;
|
||||
};
|
||||
|
|
@ -496,9 +496,9 @@ CPU8080State.prototype.setPF = function()
|
|||
/**
|
||||
* clearAF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearAF = function()
|
||||
CPUState8080.prototype.clearAF = function()
|
||||
{
|
||||
this.resultAuxOverflow = (this.resultParitySign & 0x10) | (this.resultAuxOverflow & ~0x10);
|
||||
};
|
||||
|
|
@ -506,10 +506,10 @@ CPU8080State.prototype.clearAF = function()
|
|||
/**
|
||||
* getAF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.AF
|
||||
*/
|
||||
CPU8080State.prototype.getAF = function()
|
||||
CPUState8080.prototype.getAF = function()
|
||||
{
|
||||
return ((this.resultParitySign ^ this.resultAuxOverflow) & 0x10)? CPUDef8080.PS.AF : 0;
|
||||
};
|
||||
|
|
@ -517,9 +517,9 @@ CPU8080State.prototype.getAF = function()
|
|||
/**
|
||||
* setAF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setAF = function()
|
||||
CPUState8080.prototype.setAF = function()
|
||||
{
|
||||
this.resultAuxOverflow = (~this.resultParitySign & 0x10) | (this.resultAuxOverflow & ~0x10);
|
||||
};
|
||||
|
|
@ -527,9 +527,9 @@ CPU8080State.prototype.setAF = function()
|
|||
/**
|
||||
* clearZF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearZF = function()
|
||||
CPUState8080.prototype.clearZF = function()
|
||||
{
|
||||
this.resultZeroCarry |= 0xff;
|
||||
};
|
||||
|
|
@ -537,10 +537,10 @@ CPU8080State.prototype.clearZF = function()
|
|||
/**
|
||||
* getZF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.ZF
|
||||
*/
|
||||
CPU8080State.prototype.getZF = function()
|
||||
CPUState8080.prototype.getZF = function()
|
||||
{
|
||||
return (this.resultZeroCarry & 0xff)? 0 : CPUDef8080.PS.ZF;
|
||||
};
|
||||
|
|
@ -548,9 +548,9 @@ CPU8080State.prototype.getZF = function()
|
|||
/**
|
||||
* setZF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setZF = function()
|
||||
CPUState8080.prototype.setZF = function()
|
||||
{
|
||||
this.resultZeroCarry &= ~0xff;
|
||||
};
|
||||
|
|
@ -558,9 +558,9 @@ CPU8080State.prototype.setZF = function()
|
|||
/**
|
||||
* clearSF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearSF = function()
|
||||
CPUState8080.prototype.clearSF = function()
|
||||
{
|
||||
if (this.getSF()) this.resultParitySign ^= 0xc0;
|
||||
};
|
||||
|
|
@ -568,10 +568,10 @@ CPU8080State.prototype.clearSF = function()
|
|||
/**
|
||||
* getSF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.SF
|
||||
*/
|
||||
CPU8080State.prototype.getSF = function()
|
||||
CPUState8080.prototype.getSF = function()
|
||||
{
|
||||
return (this.resultParitySign & 0x80)? CPUDef8080.PS.SF : 0;
|
||||
};
|
||||
|
|
@ -579,9 +579,9 @@ CPU8080State.prototype.getSF = function()
|
|||
/**
|
||||
* setSF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setSF = function()
|
||||
CPUState8080.prototype.setSF = function()
|
||||
{
|
||||
if (!this.getSF()) this.resultParitySign ^= 0xc0;
|
||||
};
|
||||
|
|
@ -589,9 +589,9 @@ CPU8080State.prototype.setSF = function()
|
|||
/**
|
||||
* clearIF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.clearIF = function()
|
||||
CPUState8080.prototype.clearIF = function()
|
||||
{
|
||||
this.regPS &= ~CPUDef8080.PS.IF;
|
||||
};
|
||||
|
|
@ -599,10 +599,10 @@ CPU8080State.prototype.clearIF = function()
|
|||
/**
|
||||
* getIF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.IF
|
||||
*/
|
||||
CPU8080State.prototype.getIF = function()
|
||||
CPUState8080.prototype.getIF = function()
|
||||
{
|
||||
return (this.regPS & CPUDef8080.PS.IF);
|
||||
};
|
||||
|
|
@ -610,9 +610,9 @@ CPU8080State.prototype.getIF = function()
|
|||
/**
|
||||
* setIF()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.setIF = function()
|
||||
CPUState8080.prototype.setIF = function()
|
||||
{
|
||||
this.regPS |= CPUDef8080.PS.IF;
|
||||
};
|
||||
|
|
@ -620,10 +620,10 @@ CPU8080State.prototype.setIF = function()
|
|||
/**
|
||||
* getPS()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getPS = function()
|
||||
CPUState8080.prototype.getPS = function()
|
||||
{
|
||||
return (this.regPS & ~CPUDef8080.PS.RESULT) | (this.getSF() | this.getZF() | this.getAF() | this.getPF() | this.getCF());
|
||||
};
|
||||
|
|
@ -631,10 +631,10 @@ CPU8080State.prototype.getPS = function()
|
|||
/**
|
||||
* setPS(regPS)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} regPS
|
||||
*/
|
||||
CPU8080State.prototype.setPS = function(regPS)
|
||||
CPUState8080.prototype.setPS = function(regPS)
|
||||
{
|
||||
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
|
||||
if (regPS & CPUDef8080.PS.CF) this.resultZeroCarry |= 0x100;
|
||||
|
|
@ -649,10 +649,10 @@ CPU8080State.prototype.setPS = function(regPS)
|
|||
/**
|
||||
* getPSW()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.getPSW = function()
|
||||
CPUState8080.prototype.getPSW = function()
|
||||
{
|
||||
return (this.getPS() & CPUDef8080.PS.MASK) | (this.regA << 8);
|
||||
};
|
||||
|
|
@ -660,10 +660,10 @@ CPU8080State.prototype.getPSW = function()
|
|||
/**
|
||||
* setPSW(w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPU8080State.prototype.setPSW = function(w)
|
||||
CPUState8080.prototype.setPSW = function(w)
|
||||
{
|
||||
this.setPS((w & CPUDef8080.PS.MASK) | (this.regPS & ~CPUDef8080.PS.MASK));
|
||||
this.regA = w >> 8;
|
||||
|
|
@ -672,11 +672,11 @@ CPU8080State.prototype.setPSW = function(w)
|
|||
/**
|
||||
* addByte(src)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA + src
|
||||
*/
|
||||
CPU8080State.prototype.addByte = function(src)
|
||||
CPUState8080.prototype.addByte = function(src)
|
||||
{
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = this.regA + src) & 0xff;
|
||||
|
|
@ -685,11 +685,11 @@ CPU8080State.prototype.addByte = function(src)
|
|||
/**
|
||||
* addByteCarry(src)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA + src + carry
|
||||
*/
|
||||
CPU8080State.prototype.addByteCarry = function(src)
|
||||
CPUState8080.prototype.addByteCarry = function(src)
|
||||
{
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = this.regA + src + ((this.resultZeroCarry & 0x100)? 1 : 0)) & 0xff;
|
||||
|
|
@ -701,11 +701,11 @@ CPU8080State.prototype.addByteCarry = function(src)
|
|||
* Ordinarily, one would expect the Auxiliary Carry flag (AF) to be clear after this operation,
|
||||
* but apparently the 8080 will set AF if bit 3 in either operand is set.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA & src
|
||||
*/
|
||||
CPU8080State.prototype.andByte = function(src)
|
||||
CPUState8080.prototype.andByte = function(src)
|
||||
{
|
||||
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regA & src;
|
||||
if ((this.regA | src) & 0x8) this.resultAuxOverflow ^= 0x10; // set AF by inverting bit 4 in resultAuxOverflow
|
||||
|
|
@ -718,11 +718,11 @@ CPU8080State.prototype.andByte = function(src)
|
|||
* We perform this operation using 8-bit two's complement arithmetic, by negating and then adding
|
||||
* the implied src of 1. This appears to mimic how the 8080 manages the Auxiliary Carry flag (AF).
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} b
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.decByte = function(b)
|
||||
CPUState8080.prototype.decByte = function(b)
|
||||
{
|
||||
this.resultAuxOverflow = b ^ 0xff;
|
||||
b = this.resultParitySign = (b + 0xff) & 0xff;
|
||||
|
|
@ -733,11 +733,11 @@ CPU8080State.prototype.decByte = function(b)
|
|||
/**
|
||||
* incByte(b)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} b
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080State.prototype.incByte = function(b)
|
||||
CPUState8080.prototype.incByte = function(b)
|
||||
{
|
||||
this.resultAuxOverflow = b;
|
||||
b = this.resultParitySign = (b + 1) & 0xff;
|
||||
|
|
@ -748,11 +748,11 @@ CPU8080State.prototype.incByte = function(b)
|
|||
/**
|
||||
* orByte(src)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA | src
|
||||
*/
|
||||
CPU8080State.prototype.orByte = function(src)
|
||||
CPUState8080.prototype.orByte = function(src)
|
||||
{
|
||||
return this.resultParitySign = this.resultZeroCarry = this.resultAuxOverflow = this.regA | src;
|
||||
};
|
||||
|
|
@ -787,11 +787,11 @@ CPU8080State.prototype.orByte = function(src)
|
|||
* ---------
|
||||
* 1 0101 0110 (0x56)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA - src
|
||||
*/
|
||||
CPU8080State.prototype.subByte = function(src)
|
||||
CPUState8080.prototype.subByte = function(src)
|
||||
{
|
||||
src ^= 0xff;
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
|
|
@ -808,11 +808,11 @@ CPU8080State.prototype.subByte = function(src)
|
|||
* This mimics the behavior of subByte() when the Carry flag (CF) is clear, and hopefully also mimics how the
|
||||
* 8080 manages the Auxiliary Carry flag (AF) when the Carry flag (CF) is set.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA - src - carry
|
||||
*/
|
||||
CPU8080State.prototype.subByteBorrow = function(src)
|
||||
CPUState8080.prototype.subByteBorrow = function(src)
|
||||
{
|
||||
src ^= 0xff;
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
|
|
@ -822,11 +822,11 @@ CPU8080State.prototype.subByteBorrow = function(src)
|
|||
/**
|
||||
* xorByte(src)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} src
|
||||
* @return {number} regA ^ src
|
||||
*/
|
||||
CPU8080State.prototype.xorByte = function(src)
|
||||
CPUState8080.prototype.xorByte = function(src)
|
||||
{
|
||||
return this.resultParitySign = this.resultZeroCarry = this.resultAuxOverflow = this.regA ^ src;
|
||||
};
|
||||
|
|
@ -834,11 +834,11 @@ CPU8080State.prototype.xorByte = function(src)
|
|||
/**
|
||||
* getByte(addr)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} addr is a linear address
|
||||
* @return {number} byte (8-bit) value at that address
|
||||
*/
|
||||
CPU8080State.prototype.getByte = function(addr)
|
||||
CPUState8080.prototype.getByte = function(addr)
|
||||
{
|
||||
return this.bus.getByte(addr);
|
||||
};
|
||||
|
|
@ -846,11 +846,11 @@ CPU8080State.prototype.getByte = function(addr)
|
|||
/**
|
||||
* getWord(addr)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} addr is a linear address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
CPU8080State.prototype.getWord = function(addr)
|
||||
CPUState8080.prototype.getWord = function(addr)
|
||||
{
|
||||
return this.bus.getShort(addr);
|
||||
};
|
||||
|
|
@ -858,11 +858,11 @@ CPU8080State.prototype.getWord = function(addr)
|
|||
/**
|
||||
* setByte(addr, b)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} addr is a linear address
|
||||
* @param {number} b is the byte (8-bit) value to write (which we truncate to 8 bits; required by opSTOSb)
|
||||
*/
|
||||
CPU8080State.prototype.setByte = function(addr, b)
|
||||
CPUState8080.prototype.setByte = function(addr, b)
|
||||
{
|
||||
this.bus.setByte(addr, b);
|
||||
};
|
||||
|
|
@ -870,11 +870,11 @@ CPU8080State.prototype.setByte = function(addr, b)
|
|||
/**
|
||||
* setWord(addr, w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} addr is a linear address
|
||||
* @param {number} w is the word (16-bit) value to write (which we truncate to 16 bits to be safe)
|
||||
*/
|
||||
CPU8080State.prototype.setWord = function(addr, w)
|
||||
CPUState8080.prototype.setWord = function(addr, w)
|
||||
{
|
||||
this.bus.setShort(addr, w);
|
||||
};
|
||||
|
|
@ -882,10 +882,10 @@ CPU8080State.prototype.setWord = function(addr, w)
|
|||
/**
|
||||
* getPCByte()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} byte at the current PC; PC advanced by 1
|
||||
*/
|
||||
CPU8080State.prototype.getPCByte = function()
|
||||
CPUState8080.prototype.getPCByte = function()
|
||||
{
|
||||
var b = this.getByte(this.regPC);
|
||||
this.setPC(this.regPC + 1);
|
||||
|
|
@ -895,10 +895,10 @@ CPU8080State.prototype.getPCByte = function()
|
|||
/**
|
||||
* getPCWord()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} word at the current PC; PC advanced by 2
|
||||
*/
|
||||
CPU8080State.prototype.getPCWord = function()
|
||||
CPUState8080.prototype.getPCWord = function()
|
||||
{
|
||||
var w = this.getWord(this.regPC);
|
||||
this.setPC(this.regPC + 2);
|
||||
|
|
@ -908,10 +908,10 @@ CPU8080State.prototype.getPCWord = function()
|
|||
/**
|
||||
* popWord()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {number} word popped from the current SP; SP increased by 2
|
||||
*/
|
||||
CPU8080State.prototype.popWord = function()
|
||||
CPUState8080.prototype.popWord = function()
|
||||
{
|
||||
var w = this.getWord(this.regSP);
|
||||
this.setSP(this.regSP + 2);
|
||||
|
|
@ -921,10 +921,10 @@ CPU8080State.prototype.popWord = function()
|
|||
/**
|
||||
* pushWord(w)
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} w is the word (16-bit) value to push at current SP; SP decreased by 2
|
||||
*/
|
||||
CPU8080State.prototype.pushWord = function(w)
|
||||
CPUState8080.prototype.pushWord = function(w)
|
||||
{
|
||||
this.setSP(this.regSP - 2);
|
||||
this.setWord(this.regSP, w);
|
||||
|
|
@ -933,10 +933,10 @@ CPU8080State.prototype.pushWord = function(w)
|
|||
/**
|
||||
* checkINTR()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @return {boolean} true if execution may proceed, false if not
|
||||
*/
|
||||
CPU8080State.prototype.checkINTR = function()
|
||||
CPUState8080.prototype.checkINTR = function()
|
||||
{
|
||||
/*
|
||||
* If the Debugger is single-stepping, this.nStepCycles will always be zero, which we take
|
||||
|
|
@ -976,10 +976,10 @@ CPU8080State.prototype.checkINTR = function()
|
|||
* nLevel can either be a valid interrupt level (0-7), or -1 to clear all pending interrupts
|
||||
* (eg, in the event of a system-wide reset).
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} nLevel (0-7, or -1 for all)
|
||||
*/
|
||||
CPU8080State.prototype.clearINTR = function(nLevel)
|
||||
CPUState8080.prototype.clearINTR = function(nLevel)
|
||||
{
|
||||
var bitsClear = nLevel < 0? 0xff : (1 << nLevel);
|
||||
this.intFlags &= ~bitsClear;
|
||||
|
|
@ -989,9 +989,9 @@ CPU8080State.prototype.clearINTR = function(nLevel)
|
|||
/**
|
||||
* requestHALT()
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPU8080State.prototype.requestHALT = function()
|
||||
CPUState8080.prototype.requestHALT = function()
|
||||
{
|
||||
this.intFlags |= CPUDef8080.INTFLAG.HALT;
|
||||
this.nBurstCycles -= this.nStepCycles;
|
||||
|
|
@ -1008,10 +1008,10 @@ CPU8080State.prototype.requestHALT = function()
|
|||
* by setting nStepCycles to zero. But before we do, we subtract nStepCycles from nBurstCycles,
|
||||
* so that the calculation of how many cycles were actually executed on this burst is correct.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} nLevel (0-7)
|
||||
*/
|
||||
CPU8080State.prototype.requestINTR = function(nLevel)
|
||||
CPUState8080.prototype.requestINTR = function(nLevel)
|
||||
{
|
||||
this.intFlags |= (1 << nLevel);
|
||||
if (this.getIF()) {
|
||||
|
|
@ -1027,12 +1027,12 @@ CPU8080State.prototype.requestINTR = function(nLevel)
|
|||
* CPU type before passing the call to displayValue(); in the "old days", updateStatus() called
|
||||
* displayValue() directly (although then it was called displayReg()).
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {string} sReg
|
||||
* @param {number} nValue
|
||||
* @param {number} [cch] (default is 2 hex digits)
|
||||
*/
|
||||
CPU8080State.prototype.updateReg = function(sReg, nValue, cch)
|
||||
CPUState8080.prototype.updateReg = function(sReg, nValue, cch)
|
||||
{
|
||||
this.displayValue(sReg, nValue, cch || 2);
|
||||
};
|
||||
|
|
@ -1040,16 +1040,16 @@ CPU8080State.prototype.updateReg = function(sReg, nValue, cch)
|
|||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* This provides periodic Control Panel updates (eg, a few times per second; see STATUS_UPDATES_PER_SECOND).
|
||||
* 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.
|
||||
*
|
||||
* Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates, since updateVideo()
|
||||
* can be called up to 60 times per second (see VIDEO_UPDATES_PER_SECOND).
|
||||
* Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates,
|
||||
* since updateVideo() can be called up to 60 times per second.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
CPU8080State.prototype.updateStatus = function(fForce)
|
||||
CPUState8080.prototype.updateStatus = function(fForce)
|
||||
{
|
||||
if (this.cLiveRegs) {
|
||||
if (fForce || !this.flags.fRunning || this.flags.fDisplayLiveRegs) {
|
||||
|
|
@ -1091,13 +1091,13 @@ CPU8080State.prototype.updateStatus = function(fForce)
|
|||
* As a result, the Debugger's complete independence means you can run other 8086/8088 debuggers
|
||||
* (eg, DEBUG) inside the simulation without interference; you can even "debug" them with the Debugger.
|
||||
*
|
||||
* @this {CPU8080State}
|
||||
* @this {CPUState8080}
|
||||
* @param {number} nMinCycles (0 implies a single-step, and therefore breakpoints should be ignored)
|
||||
* @return {number} of cycles executed; 0 indicates a pre-execution condition (ie, an execution breakpoint
|
||||
* was hit), -1 indicates a post-execution condition (eg, a read or write breakpoint was hit), and a positive
|
||||
* number indicates successful completion of that many cycles (which should always be >= nMinCycles).
|
||||
*/
|
||||
CPU8080State.prototype.stepCPU = function(nMinCycles)
|
||||
CPUState8080.prototype.stepCPU = function(nMinCycles)
|
||||
{
|
||||
/*
|
||||
* The Debugger uses fComplete to determine if the instruction completed (true) or was interrupted
|
||||
|
|
@ -1155,21 +1155,21 @@ CPU8080State.prototype.stepCPU = function(nMinCycles)
|
|||
};
|
||||
|
||||
/**
|
||||
* CPU8080State.init()
|
||||
* CPUState8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "cpu", extracting the
|
||||
* JSON-encoded parameters for the CPU8080State constructor from the element's "data-value"
|
||||
* JSON-encoded parameters for the CPUState8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor (which in turn invokes the CPU constructor)
|
||||
* to create a CPU8080State component, and then binding any associated HTML controls to the
|
||||
* to create a CPUState8080 component, and then binding any associated HTML controls to the
|
||||
* new component.
|
||||
*/
|
||||
CPU8080State.init = function()
|
||||
CPUState8080.init = function()
|
||||
{
|
||||
var aeCPUs = Component.getElementsByClass(document, PC8080.APPCLASS, "cpu");
|
||||
for (var iCPU = 0; iCPU < aeCPUs.length; iCPU++) {
|
||||
var eCPU = aeCPUs[iCPU];
|
||||
var parmsCPU = Component.getComponentParms(eCPU);
|
||||
var cpu = new CPU8080State(parmsCPU);
|
||||
var cpu = new CPUState8080(parmsCPU);
|
||||
Component.bindComponentControls(cpu, eCPU, PC8080.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
|
@ -1177,6 +1177,6 @@ CPU8080State.init = function()
|
|||
/*
|
||||
* Initialize every CPU module on the page
|
||||
*/
|
||||
web.onInit(CPU8080State.init);
|
||||
web.onInit(CPUState8080.init);
|
||||
|
||||
if (NODE) module.exports = CPU8080State;
|
||||
if (NODE) module.exports = CPUState8080;
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ if (DEBUGGER) {
|
|||
/*
|
||||
* TYPE_IREG values, based on the REG_* constants.
|
||||
*
|
||||
* NOte that TYPE_M isn't really a register, just an alternative form of TYPE_HL | TYPE_MEM.
|
||||
* Note that TYPE_M isn't really a register, just an alternative form of TYPE_HL | TYPE_MEM.
|
||||
*/
|
||||
Debugger8080.TYPE_A = (Debugger8080.REG_A << 8 | Debugger8080.TYPE_REG | Debugger8080.TYPE_BYTE);
|
||||
Debugger8080.TYPE_B = (Debugger8080.REG_B << 8 | Debugger8080.TYPE_REG | Debugger8080.TYPE_BYTE);
|
||||
|
|
@ -673,7 +673,7 @@ if (DEBUGGER) {
|
|||
* @this {Debugger8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
Debugger8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ Keyboard8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
* @this {Keyboard8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
Keyboard8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ Panel8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @this {Panel8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
Panel8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Component.subclass(RAM8080);
|
|||
* @this {RAM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
RAM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ ROM8080.CPM.VECTORS = [ROM8080.CPM.BIOS.VECTOR, ROM8080.CPM.BDOS.VECTOR];
|
|||
* @this {ROM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
ROM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ SerialPort8080.prototype.echoByte = function(b)
|
|||
* @this {SerialPort8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ Video8080.prototype.initBuffers = function()
|
|||
* @this {Video8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPU8080State} cpu
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
Video8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
|
|
|
|||
767
modules/pdp11/lib/bus.js
Normal file
767
modules/pdp11/lib/bus.js
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Bus component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* BusPDP11(cpu, dbg)
|
||||
*
|
||||
* The BusPDP11 component manages physical memory and I/O address spaces.
|
||||
*
|
||||
* The BusPDP11 component has no UI elements, so it does not require an init() handler,
|
||||
* but it still inherits from the Component class and must be allocated like any
|
||||
* other device component. It's currently allocated by the Computer's init() handler,
|
||||
* which then calls the initBus() method of all the other components.
|
||||
*
|
||||
* For memory beyond the simple needs of the ROM and RAM components (ie, memory-mapped
|
||||
* devices), the address space must still be allocated through the BusPDP11 component via
|
||||
* addMemory(). If the component needs something more than simple read/write storage,
|
||||
* it must provide a custom controller.
|
||||
*
|
||||
* All port (I/O) operations are defined by external handlers; they register with us,
|
||||
* and we manage those registrations and provide support for I/O breakpoints, but the
|
||||
* only default I/O behavior we provide is ignoring writes to any unregistered output
|
||||
* ports and returning 0xff from any unregistered input ports.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsBus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
function BusPDP11(parmsBus, cpu, dbg)
|
||||
{
|
||||
Component.call(this, "Bus", parmsBus, BusPDP11);
|
||||
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
this.nBusWidth = parmsBus['busWidth'] || 16;
|
||||
|
||||
/*
|
||||
* Compute all BusPDP11 memory block parameters, based on the width of the bus.
|
||||
*
|
||||
* Regarding blockTotal, we want to avoid using block overflow expressions like:
|
||||
*
|
||||
* iBlock < this.nBlockTotal? iBlock : 0
|
||||
*
|
||||
* As long as we know that blockTotal is a power of two (eg, 256 or 0x100, in the case of
|
||||
* nBusWidth == 20 and blockSize == 4096), we can define blockMask as (blockTotal - 1) and
|
||||
* rewrite the previous expression as:
|
||||
*
|
||||
* iBlock & this.nBlockMask
|
||||
*
|
||||
* Bus Property Old hard-coded values (when nBusWidth was always 20)
|
||||
* ------------ ----------------------------------------------------
|
||||
* this.nBusLimit 0xfffff
|
||||
* this.nBusMask [same as busLimit]
|
||||
* this.nBlockSize 4096
|
||||
* this.nBlockLen (this.nBlockSize >> 2)
|
||||
* this.nBlockShift 12
|
||||
* this.nBlockLimit 0xfff
|
||||
* this.nBlockTotal ((this.nBusLimit + this.nBlockSize) / this.nBlockSize) | 0
|
||||
* this.nBlockMask (this.nBlockTotal - 1) [ie, 0xff]
|
||||
*
|
||||
* Note that we choose a nBlockShift value (and thus a physical memory block size) based on "buswidth":
|
||||
*
|
||||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
|
||||
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
|
||||
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
|
||||
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
|
||||
*
|
||||
* The coarser block granularities (ie, 16Kb and 32Kb) may cause problems for certain RAM and/or ROM
|
||||
* allocations that are contiguous but are allocated out of order, or that have different controller
|
||||
* requirements. Your choices, for the moment, are either to ensure the allocations are performed in
|
||||
* order, or to choose smaller nBlockShift values (at the expense of a generating a larger block array).
|
||||
*/
|
||||
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.nBlockSize = 1 << this.nBlockShift;
|
||||
this.nBlockLen = this.nBlockSize >> 2;
|
||||
this.nBlockLimit = this.nBlockSize - 1;
|
||||
this.nBlockTotal = (this.addrTotal / this.nBlockSize) | 0;
|
||||
this.nBlockMask = this.nBlockTotal - 1;
|
||||
this.assert(this.nBlockMask <= BusPDP11.BlockInfo.num.mask);
|
||||
|
||||
/*
|
||||
* Lists of I/O notification functions: aPortInputNotify and aPortOutputNotify are arrays, indexed by
|
||||
* port, of sub-arrays which contain:
|
||||
*
|
||||
* [0]: registered function to call for every I/O access
|
||||
*
|
||||
* The registered function is called with the port address, and if the access was triggered by the CPU,
|
||||
* the instruction pointer (IP) at the point of access.
|
||||
*
|
||||
* WARNING: Unlike the (old) read and write memory notification functions, these support only one
|
||||
* pair of input/output functions per port. A more sophisticated architecture could support a list
|
||||
* of chained functions across multiple components, but I doubt that will be necessary here.
|
||||
*
|
||||
* UPDATE: The Debugger now piggy-backs on these arrays to indicate ports for which it wants notification
|
||||
* of I/O. In those cases, the registered component/function elements may or may not be set, but the
|
||||
* following additional element will be set:
|
||||
*
|
||||
* [1]: true to break on I/O, false to ignore I/O
|
||||
*
|
||||
* The false case is important if fPortInputBreakAll and/or fPortOutputBreakAll is set, because it allows the
|
||||
* Debugger to selectively ignore specific ports.
|
||||
*/
|
||||
this.aPortInputNotify = [];
|
||||
this.aPortOutputNotify = [];
|
||||
this.fPortInputBreakAll = this.fPortOutputBreakAll = false;
|
||||
|
||||
/*
|
||||
* By default, all I/O ports are 1 byte wide; ports that are wider must add themselves to one or both of
|
||||
* these lists, using addPortInputWidth() and/or addPortOutputWidth().
|
||||
*/
|
||||
this.aPortInputWidth = [];
|
||||
this.aPortOutputWidth = [];
|
||||
|
||||
/*
|
||||
* Allocate empty Memory blocks to span the entire physical address space.
|
||||
*/
|
||||
this.initMemory();
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
Component.subclass(BusPDP11);
|
||||
|
||||
BusPDP11.ERROR = {
|
||||
ADD_MEM_INUSE: 1,
|
||||
ADD_MEM_BADRANGE: 2,
|
||||
SET_MEM_BADRANGE: 4,
|
||||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number}
|
||||
*/
|
||||
var BlockInfo;
|
||||
|
||||
/**
|
||||
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
|
||||
*
|
||||
* @typedef {{
|
||||
* num: BitField,
|
||||
* count: BitField,
|
||||
* btmod: BitField,
|
||||
* type: BitField
|
||||
* }}
|
||||
*/
|
||||
BusPDP11.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
|
||||
|
||||
/**
|
||||
* BusInfoPDP11 object definition (returned by scanMemory())
|
||||
*
|
||||
* cbTotal: total bytes allocated
|
||||
* cBlocks: total Memory blocks allocated
|
||||
* aBlocks: array of allocated Memory block numbers
|
||||
*
|
||||
* @typedef {{
|
||||
* cbTotal: number,
|
||||
* cBlocks: number,
|
||||
* aBlocks: Array.<BlockInfo>
|
||||
* }}
|
||||
*/
|
||||
var BusInfoPDP11;
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
* Allocate enough (empty) Memory blocks to span the entire physical address space.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
*/
|
||||
BusPDP11.prototype.initMemory = function()
|
||||
{
|
||||
var block = new MemoryPDP11();
|
||||
block.copyBreakpoints(this.dbg);
|
||||
this.aMemBlocks = new Array(this.nBlockTotal);
|
||||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
this.aMemBlocks[iBlock] = block;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
*/
|
||||
BusPDP11.prototype.reset = function()
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* We don't need a powerDown() handler, because for largely historical reasons, our state is saved by saveMemory(),
|
||||
* which called by the CPU.
|
||||
*
|
||||
* However, we do need a powerUp() handler, because on resumable machines, the Computer's onReset() function calls
|
||||
* everyone's powerUp() handler rather than their reset() handler.
|
||||
*
|
||||
* TODO: Perhaps Computer should be smarter: if there's no powerUp() handler, then fallback to the reset() handler.
|
||||
* In that case, however, we'd either need to remove the powerUp() stub in Component, or detect the existence of the stub.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {Object|null} data (always null because we supply no powerDown() handler)
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
BusPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) this.reset();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* addMemory(addr, size, type)
|
||||
*
|
||||
* 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
|
||||
* an allocation conflict error. This helps prevent address calculation errors, redundant
|
||||
* allocations, etc.
|
||||
*
|
||||
* We've relaxed some of the original requirements (ie, that addresses must start at a
|
||||
* block-granular address, or that sizes must be equal to exactly one or more blocks),
|
||||
* because machines with large block sizes can make it impossible to load certain ROMs at
|
||||
* their required addresses. Every allocation still allocates a whole number of blocks.
|
||||
*
|
||||
* Even so, BusPDP11 memory management does NOT provide a general-purpose heap. Most memory
|
||||
* allocations occur during machine initialization and never change. In particular, there
|
||||
* is NO support for removing partial-block allocations.
|
||||
*
|
||||
* Each Memory block keeps track of a start address (addr) and length (used), indicating
|
||||
* the used space within the block; any free space that precedes or follows that used space
|
||||
* can be allocated later, by simply extending the beginning or ending of the previously used
|
||||
* space. However, any holes that might have existed between the original allocation and an
|
||||
* extension are subsumed by the extension.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @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
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
BusPDP11.prototype.addMemory = function(addr, size, type)
|
||||
{
|
||||
var addrNext = addr;
|
||||
var sizeLeft = size;
|
||||
var iBlock = addrNext >>> this.nBlockShift;
|
||||
|
||||
while (sizeLeft > 0 && iBlock < this.aMemBlocks.length) {
|
||||
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
var addrBlock = iBlock * this.nBlockSize;
|
||||
var sizeBlock = this.nBlockSize - (addrNext - addrBlock);
|
||||
if (sizeBlock > sizeLeft) sizeBlock = sizeLeft;
|
||||
|
||||
if (block && block.size) {
|
||||
if (block.type == type) {
|
||||
/*
|
||||
* Where there is already a similar block with a non-zero size, we allow the allocation only if:
|
||||
*
|
||||
* 1) addrNext + sizeLeft <= block.addr (the request precedes the used portion of the current block), or
|
||||
* 2) addrNext >= block.addr + block.used (the request follows the used portion of the current block)
|
||||
*/
|
||||
if (addrNext + sizeLeft <= block.addr) {
|
||||
block.used += (block.addr - addrNext);
|
||||
block.addr = addrNext;
|
||||
return true;
|
||||
}
|
||||
if (addrNext >= block.addr + block.used) {
|
||||
var sizeAvail = block.size - (addrNext - addrBlock);
|
||||
if (sizeAvail > sizeLeft) sizeAvail = sizeLeft;
|
||||
block.used = addrNext - block.addr + sizeAvail;
|
||||
addrNext = addrBlock + this.nBlockSize;
|
||||
sizeLeft -= sizeAvail;
|
||||
iBlock++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return this.reportError(BusPDP11.ERROR.ADD_MEM_INUSE, addrNext, sizeLeft);
|
||||
}
|
||||
|
||||
var blockNew = new MemoryPDP11(addrNext, sizeBlock, this.nBlockSize, type);
|
||||
blockNew.copyBreakpoints(this.dbg, block);
|
||||
this.aMemBlocks[iBlock++] = blockNew;
|
||||
|
||||
addrNext = addrBlock + this.nBlockSize;
|
||||
sizeLeft -= sizeBlock;
|
||||
}
|
||||
|
||||
if (sizeLeft <= 0) {
|
||||
this.status(Math.floor(size / 1024) + "Kb " + MemoryPDP11.TYPE.NAMES[type] + " at " + str.toHexWord(addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.reportError(BusPDP11.ERROR.ADD_MEM_BADRANGE, addr, size);
|
||||
};
|
||||
|
||||
/**
|
||||
* cleanMemory(addr, size)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @return {boolean} true if all blocks were clean, false if dirty; all blocks are cleaned in the process
|
||||
*/
|
||||
BusPDP11.prototype.cleanMemory = function(addr, size)
|
||||
{
|
||||
var fClean = true;
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
if (this.aMemBlocks[iBlock].fDirty) {
|
||||
this.aMemBlocks[iBlock].fDirty = fClean = false;
|
||||
this.aMemBlocks[iBlock].fDirtyEver = true;
|
||||
}
|
||||
size -= this.nBlockSize;
|
||||
iBlock++;
|
||||
}
|
||||
return fClean;
|
||||
};
|
||||
|
||||
/**
|
||||
* scanMemory(info, addr, size)
|
||||
*
|
||||
* Returns a BusInfoPDP11 object for the specified address range.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {Object} [info] previous BusInfoPDP11, if any
|
||||
* @param {number} [addr] starting address of range (0 if none provided)
|
||||
* @param {number} [size] size of range, in bytes (up to end of address space if none provided)
|
||||
* @return {Object} updated info (or new info if no previous info provided)
|
||||
*/
|
||||
BusPDP11.prototype.scanMemory = function(info, addr, size)
|
||||
{
|
||||
if (addr == null) addr = 0;
|
||||
if (size == null) size = (this.addrTotal - addr) | 0;
|
||||
if (info == null) info = {cbTotal: 0, cBlocks: 0, aBlocks: []};
|
||||
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
var iBlockMax = ((addr + size - 1) >>> this.nBlockShift);
|
||||
|
||||
info.cbTotal = 0;
|
||||
info.cBlocks = 0;
|
||||
while (iBlock <= iBlockMax) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
info.cbTotal += block.size;
|
||||
if (block.size) {
|
||||
info.aBlocks.push(usr.initBitFields(BusPDP11.BlockInfo, iBlock, 0, 0, block.type));
|
||||
info.cBlocks++
|
||||
}
|
||||
iBlock++;
|
||||
}
|
||||
return info;
|
||||
};
|
||||
|
||||
/**
|
||||
* getWidth()
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @return {number}
|
||||
*/
|
||||
BusPDP11.prototype.getWidth = function()
|
||||
{
|
||||
return this.nBusWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* removeMemory(addr, size)
|
||||
*
|
||||
* Replaces every block in the specified address range with empty Memory blocks that ignore all reads/writes.
|
||||
*
|
||||
* TODO: Update the removeMemory() interface to reflect the relaxed requirements of the addMemory() interface.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
BusPDP11.prototype.removeMemory = function(addr, size)
|
||||
{
|
||||
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0) {
|
||||
var blockOld = this.aMemBlocks[iBlock];
|
||||
var blockNew = new MemoryPDP11(addr);
|
||||
blockNew.copyBreakpoints(this.dbg, blockOld);
|
||||
this.aMemBlocks[iBlock++] = blockNew;
|
||||
addr = iBlock * this.nBlockSize;
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return this.reportError(BusPDP11.ERROR.REM_MEM_BADRANGE, addr, size);
|
||||
};
|
||||
|
||||
/**
|
||||
* getMemoryBlocks(addr, size)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is the starting physical address
|
||||
* @param {number} size of the request, in bytes
|
||||
* @return {Array} of Memory blocks
|
||||
*/
|
||||
BusPDP11.prototype.getMemoryBlocks = function(addr, size)
|
||||
{
|
||||
var aBlocks = [];
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
aBlocks.push(this.aMemBlocks[iBlock++]);
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
return aBlocks;
|
||||
};
|
||||
|
||||
/**
|
||||
* setMemoryBlocks(addr, size, aBlocks, type)
|
||||
*
|
||||
* If no type is specified, then specified address range uses all the provided blocks as-is;
|
||||
* this form of setMemoryBlocks() is used for complete physical aliases.
|
||||
*
|
||||
* Otherwise, new blocks are allocated with the specified type; the underlying memory from the
|
||||
* provided blocks is still used, but the new blocks may have different access to that memory.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is the starting physical address
|
||||
* @param {number} size of the request, in bytes
|
||||
* @param {Array} aBlocks as returned by getMemoryBlocks()
|
||||
* @param {number} [type] is one of the MemoryPDP11.TYPE constants
|
||||
*/
|
||||
BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
||||
{
|
||||
var i = 0;
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
var block = aBlocks[i++];
|
||||
this.assert(block);
|
||||
if (!block) break;
|
||||
if (type !== undefined) {
|
||||
var blockNew = new MemoryPDP11(addr);
|
||||
blockNew.clone(block, type, this.dbg);
|
||||
block = blockNew;
|
||||
}
|
||||
this.aMemBlocks[iBlock++] = block;
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getByte(addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} byte (8-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getByte = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
|
||||
};
|
||||
|
||||
/**
|
||||
* getByteDirect(addr)
|
||||
*
|
||||
* This is useful for the Debugger and other components that want to bypass getByte() breakpoint detection.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} byte (8-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getByteDirect = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByteDirect(addr & this.nBlockLimit, addr);
|
||||
};
|
||||
|
||||
/**
|
||||
* getShort(addr)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getShort = function(addr)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off != this.nBlockLimit) {
|
||||
return this.aMemBlocks[iBlock].readShort(off, addr);
|
||||
}
|
||||
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
|
||||
};
|
||||
|
||||
/**
|
||||
* getShortDirect(addr)
|
||||
*
|
||||
* This is useful for the Debugger and other components that want to bypass getShort() breakpoint detection.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
BusPDP11.prototype.getShortDirect = function(addr)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off != this.nBlockLimit) {
|
||||
return this.aMemBlocks[iBlock].readShortDirect(off, addr);
|
||||
}
|
||||
return this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
|
||||
};
|
||||
|
||||
/**
|
||||
* setByte(addr, b)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setByte = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
|
||||
};
|
||||
|
||||
/**
|
||||
* setByteDirect(addr, b)
|
||||
*
|
||||
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
|
||||
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setByteDirect = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
|
||||
};
|
||||
|
||||
/**
|
||||
* setShort(addr, w)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setShort = function(addr, w)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off != this.nBlockLimit) {
|
||||
this.aMemBlocks[iBlock].writeShort(off, w & 0xffff, addr);
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
|
||||
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* setShortDirect(addr, w)
|
||||
*
|
||||
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
|
||||
* memory protection (for example, this is an interface the ROM component could use to initialize ROM contents).
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr is a physical address
|
||||
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
|
||||
*/
|
||||
BusPDP11.prototype.setShortDirect = function(addr, w)
|
||||
{
|
||||
var off = addr & this.nBlockLimit;
|
||||
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
|
||||
if (off != this.nBlockLimit) {
|
||||
this.aMemBlocks[iBlock].writeShortDirect(off, w & 0xffff, addr);
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
|
||||
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* addMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
BusPDP11.prototype.addMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aMemBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* removeMemBreak(addr, fWrite)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
|
||||
*/
|
||||
BusPDP11.prototype.removeMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >>> this.nBlockShift;
|
||||
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* saveMemory(fAll)
|
||||
*
|
||||
* The only memory blocks we save are those marked as dirty, but most likely all of RAM will have been marked dirty,
|
||||
* and even if our dirty-memory flags were as smart as our dirty-sector flags (ie, were set only when a write changed
|
||||
* what was already there), it's unlikely that would reduce the number of RAM blocks we must save/restore. At least
|
||||
* all the ROM blocks should be clean (except in the unlikely event that the Debugger was used to modify them).
|
||||
*
|
||||
* All dirty blocks will be stored in a single array, as pairs of block numbers and data arrays, like so:
|
||||
*
|
||||
* [iBlock0, [dw0, dw1, ...], iBlock1, [dw0, dw1, ...], ...]
|
||||
*
|
||||
* In a normal 4Kb block, there will be 1K DWORD values in the data array. Remember that each DWORD is a signed 32-bit
|
||||
* integer (because they are formed using bit-wise operator rather than floating-point math operators), so don't be
|
||||
* surprised to see negative numbers in the data.
|
||||
*
|
||||
* The above example assumes "uncompressed" data arrays. If we choose to use "compressed" data arrays, the data arrays
|
||||
* will look like:
|
||||
*
|
||||
* [count0, dw0, count1, dw1, ...]
|
||||
*
|
||||
* where each count indicates how many times the following DWORD value occurs. A data array length less than 1K indicates
|
||||
* that it's compressed, since we'll only store them in compressed form if they actually shrank, and we'll use State
|
||||
* helper methods compress() and decompress() to create and expand the compressed data arrays.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {boolean} [fAll] (true to save all non-ROM memory blocks, regardless of their dirty flags)
|
||||
* @return {Array} a
|
||||
*/
|
||||
BusPDP11.prototype.saveMemory = function(fAll)
|
||||
{
|
||||
var i = 0;
|
||||
var a = [];
|
||||
|
||||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
/*
|
||||
* We have to check both fDirty and fDirtyEver, because we may have called cleanMemory() on some of
|
||||
* the memory blocks (eg, video memory), and while cleanMemory() will clear a dirty block's fDirty flag,
|
||||
* it also sets the dirty block's fDirtyEver flag, which is left set for the lifetime of the machine.
|
||||
*/
|
||||
if (fAll && block.type != MemoryPDP11.TYPE.ROM || block.fDirty || block.fDirtyEver) {
|
||||
a[i++] = iBlock;
|
||||
a[i++] = State.compress(block.save());
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* restoreMemory(a)
|
||||
*
|
||||
* This restores the contents of all Memory blocks; called by CPUState.restore().
|
||||
*
|
||||
* In theory, we ONLY have to save/restore block contents. Other block attributes,
|
||||
* like the type, the memory controller (if any), and the active memory access functions,
|
||||
* should already be restored, since every component (re)allocates all the memory blocks
|
||||
* it was using when it's restored. And since the CPU is guaranteed to be the last
|
||||
* component to be restored, all those blocks (and their attributes) should be in place now.
|
||||
*
|
||||
* See saveMemory() for more information on how the memory block contents are saved.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {Array} a
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
BusPDP11.prototype.restoreMemory = function(a)
|
||||
{
|
||||
var i;
|
||||
for (i = 0; i < a.length - 1; i += 2) {
|
||||
var iBlock = a[i];
|
||||
var adw = a[i+1];
|
||||
if (adw && adw.length < this.nBlockLen) {
|
||||
adw = State.decompress(adw, this.nBlockLen);
|
||||
}
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
if (!block || !block.restore(adw)) {
|
||||
/*
|
||||
* Either the block to restore hasn't been allocated, indicating a change in the machine
|
||||
* configuration since it was last saved (the most likely explanation) or there's some internal
|
||||
* inconsistency (eg, the block size is wrong).
|
||||
*/
|
||||
Component.error("Unable to restore memory block " + iBlock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* reportError(op, addr, size, fQuiet)
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} op
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @param {boolean} [fQuiet] (true if any error should be quietly logged)
|
||||
* @return {boolean} false
|
||||
*/
|
||||
BusPDP11.prototype.reportError = function(op, addr, size, fQuiet)
|
||||
{
|
||||
var sError = "Memory block error (" + op + ": " + str.toHex(addr) + "," + str.toHex(size) + ")";
|
||||
if (fQuiet) {
|
||||
if (this.dbg) {
|
||||
this.dbg.message(sError);
|
||||
} else {
|
||||
this.log(sError);
|
||||
}
|
||||
} else {
|
||||
Component.error(sError);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (NODE) module.exports = BusPDP11;
|
||||
1595
modules/pdp11/lib/computer.js
Normal file
1595
modules/pdp11/lib/computer.js
Normal file
File diff suppressed because it is too large
Load diff
1228
modules/pdp11/lib/cpu.js
Normal file
1228
modules/pdp11/lib/cpu.js
Normal file
File diff suppressed because it is too large
Load diff
115
modules/pdp11/lib/cpudef.js
Normal file
115
modules/pdp11/lib/cpudef.js
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* @fileoverview Defines PDP11 CPU constants.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var CPUDefPDP11 = {
|
||||
/*
|
||||
* CPU model numbers (supported)
|
||||
*/
|
||||
MODEL_1145: 1145,
|
||||
MODEL_1170: 1170,
|
||||
|
||||
/*
|
||||
* This constant is used to mark points in the code where the physical address being returned
|
||||
* is invalid and should not be used.
|
||||
*
|
||||
* In a 32-bit CPU, -1 (ie, 0xffffffff) could actually be a valid address, so consider changing
|
||||
* ADDR_INVALID to NaN or null (which is also why all ADDR_INVALID tests should use strict equality
|
||||
* operators).
|
||||
*
|
||||
* The main reason I'm NOT using NaN or null now is my concern that, by mixing non-numbers
|
||||
* (specifically, values outside the range of signed 32-bit integers), performance may suffer.
|
||||
*
|
||||
* WARNING: Like many of the properties defined here, ADDR_INVALID is a common constant, which the
|
||||
* Closure Compiler will happily inline (with or without @const annotations; in fact, I've yet to
|
||||
* see a @const annotation EVER improve automatic inlining). However, if you don't make ABSOLUTELY
|
||||
* certain that this file is included BEFORE the first reference to any of these properties, that
|
||||
* automatic inlining will no longer occur.
|
||||
*/
|
||||
ADDR_INVALID: -1,
|
||||
|
||||
/*
|
||||
* Processor Status flag definitions (stored in regPS)
|
||||
*/
|
||||
PS: {
|
||||
CF: 0x0001, // bit 0: Carry Flag
|
||||
BIT1: 0x0002, // bit 1: reserved, always set
|
||||
PF: 0x0004, // bit 2: Parity Flag
|
||||
BIT3: 0x0008, // bit 3: reserved, always clear
|
||||
AF: 0x0010, // bit 4: Auxiliary Carry Flag
|
||||
BIT5: 0x0020, // bit 5: reserved, always clear
|
||||
ZF: 0x0040, // bit 6: Zero Flag
|
||||
SF: 0x0080, // bit 7: Sign Flag
|
||||
ALL: 0x00D5, // all "arithmetic" flags (CF, PF, AF, ZF, SF)
|
||||
MASK: 0x00FF, //
|
||||
IF: 0x0200 // bit 9: Interrupt Flag (set if interrupts enabled; Intel calls this the INTE bit)
|
||||
},
|
||||
/*
|
||||
* Interrupt-related flags (stored in intFlags)
|
||||
*/
|
||||
INTFLAG: {
|
||||
NONE: 0x0000,
|
||||
INTR: 0x00ff, // mask for 8 bits, representing interrupt levels 0-7
|
||||
HALT: 0x0100 // halt requested; see opHLT()
|
||||
},
|
||||
/*
|
||||
* Opcode definitions
|
||||
*/
|
||||
OPCODE: {
|
||||
HLT: 0x76, // Halt
|
||||
ACI: 0xCE, // Add with Carry Immediate (affects PS.ALL)
|
||||
CALL: 0xCD, // Call
|
||||
RST0: 0xC7
|
||||
// to be continued....
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* These are the internal PS bits (outside of PS.MASK) that getPS() and setPS() can get and set,
|
||||
* but which cannot be seen with any of the documented instructions.
|
||||
*/
|
||||
CPUDefPDP11.PS.INTERNAL = (CPUDefPDP11.PS.IF);
|
||||
|
||||
/*
|
||||
* PS "arithmetic" flags are NOT stored in regPS; they are maintained across separate result registers,
|
||||
* hence the RESULT designation.
|
||||
*/
|
||||
CPUDefPDP11.PS.RESULT = (CPUDefPDP11.PS.CF | CPUDefPDP11.PS.PF | CPUDefPDP11.PS.AF | CPUDefPDP11.PS.ZF | CPUDefPDP11.PS.SF);
|
||||
|
||||
/*
|
||||
* These are the "always set" PS bits for the PDP11.
|
||||
*/
|
||||
CPUDefPDP11.PS.SET = (CPUDefPDP11.PS.BIT1);
|
||||
|
||||
if (NODE) module.exports = CPUDefPDP11;
|
||||
124
modules/pdp11/lib/cpuops.js
Normal file
124
modules/pdp11/lib/cpuops.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* @fileoverview Implements PDP11 opcode handlers.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var CPUDefPDP11 = require("./CPUDef");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* op=0x00 (NOP)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
*/
|
||||
CPUDefPDP11.opNOP = function()
|
||||
{
|
||||
this.nStepCycles -= 4;
|
||||
};
|
||||
|
||||
/*
|
||||
* This 256-entry array of opcode functions is at the heart of the CPU engine: stepCPU(n).
|
||||
*
|
||||
* It might be worth trying a switch() statement instead, to see how the performance compares,
|
||||
* but I suspect that would vary quite a bit across JavaScript engines; for now, I'm putting my
|
||||
* money on array lookup.
|
||||
*/
|
||||
CPUDefPDP11.aOpsPDP1170 = [
|
||||
/* 0x00-0x03 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x04-0x07 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x08-0x0B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x0C-0x0F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x10-0x13 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x14-0x17 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x18-0x1B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x1C-0x1F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x20-0x23 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x24-0x27 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x28-0x2B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x2C-0x2F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x30-0x33 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x34-0x37 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x38-0x3B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x3C-0x3F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x40-0x43 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x44-0x47 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x48-0x4B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x4C-0x4F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x50-0x53 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x54-0x57 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x58-0x5B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x5C-0x5F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x60-0x63 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x64-0x67 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x68-0x6B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x6C-0x6F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x70-0x73 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x74-0x77 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x78-0x7B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x7C-0x7F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x80-0x83 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x84-0x87 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x88-0x8B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x8C-0x8F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x90-0x93 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x94-0x97 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x98-0x9B */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0x9C-0x9F */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xA0-0xA3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xA4-0xA7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xA8-0xAB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xAC-0xAF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xB0-0xB3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xB4-0xB7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xB8-0xBB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xBC-0xBF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xC0-0xC3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xC4-0xC7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xC8-0xCB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xCC-0xCF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xD0-0xD3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xD4-0xD7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xD8-0xDB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xDC-0xDF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xE0-0xE3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xE4-0xE7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xE8-0xEB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xEC-0xEF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xF0-0xF3 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xF4-0xF7 */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xF8-0xFB */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP,
|
||||
/* 0xFC-0xFF */ CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP, CPUDefPDP11.opNOP
|
||||
];
|
||||
1053
modules/pdp11/lib/cpustate.js
Normal file
1053
modules/pdp11/lib/cpustate.js
Normal file
File diff suppressed because it is too large
Load diff
4219
modules/pdp11/lib/debugger.js
Normal file
4219
modules/pdp11/lib/debugger.js
Normal file
File diff suppressed because it is too large
Load diff
108
modules/pdp11/lib/defines.js
Normal file
108
modules/pdp11/lib/defines.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* @fileoverview PDP11-specific compile-time definitions.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var APPCLASS = "pdp11"; // this @define is the default application class (eg, "pcx86", "c1pjs")
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var APPNAME = "PDP11"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*
|
||||
* WARNING: DEBUGGER needs to accurately reflect whether or not the Debugger component is (or will be) loaded.
|
||||
* In the compiled case, we rely on the Closure Compiler to override DEBUGGER as appropriate. When it's *false*,
|
||||
* nearly all of debugger.js will be conditionally removed by the compiler, reducing it to little more than a
|
||||
* "type skeleton", which also solves some type-related warnings we would otherwise have if we tried to remove
|
||||
* debugger.js from the compilation process altogether.
|
||||
*
|
||||
* However, when we're in "development mode" and running uncompiled code in debugger-less configurations,
|
||||
* I would like to skip loading debugger.js altogether. When doing that, we must ALSO arrange for an additional file
|
||||
* (nodebugger.js) to be loaded immediately after this file, which *explicitly* overrides DEBUGGER with *false*.
|
||||
*/
|
||||
var DEBUGGER = true; // this @define is overridden by the Closure Compiler to remove Debugger-related support
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*
|
||||
* BYTEARRAYS is a Closure Compiler compile-time option that allocates an Array of numbers for every Memory block,
|
||||
* where each a number represents ONE byte; very wasteful, but potentially slightly faster.
|
||||
*
|
||||
* See the Memory component for details.
|
||||
*/
|
||||
var BYTEARRAYS = false;
|
||||
|
||||
/**
|
||||
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time-only option, but I've
|
||||
* added Memory access functions for typed arrays (see MemoryPDP11.afnTypedArray), so support can be enabled dynamically now.
|
||||
*
|
||||
* See the Memory component for details.
|
||||
*/
|
||||
var TYPEDARRAYS = (typeof ArrayBuffer !== 'undefined');
|
||||
|
||||
/*
|
||||
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
|
||||
* which all machine components should start using; eg: "if (PDP11.DEBUG) ..." instead of "if (DEBUG) ...".
|
||||
*/
|
||||
var PDP11 = {
|
||||
APPCLASS: APPCLASS,
|
||||
APPNAME: APPNAME,
|
||||
APPVERSION: APPVERSION, // shared
|
||||
BYTEARRAYS: BYTEARRAYS,
|
||||
COMPILED: COMPILED, // shared
|
||||
CSSCLASS: CSSCLASS, // shared
|
||||
DEBUG: DEBUG, // shared
|
||||
DEBUGGER: DEBUGGER,
|
||||
MAXDEBUG: MAXDEBUG, // shared
|
||||
PRIVATE: PRIVATE, // shared
|
||||
TYPEDARRAYS: TYPEDARRAYS,
|
||||
SITEHOST: SITEHOST, // shared
|
||||
XMLVERSION: XMLVERSION // shared
|
||||
};
|
||||
|
||||
if (NODE) {
|
||||
global.APPCLASS = APPCLASS;
|
||||
global.APPNAME = APPNAME;
|
||||
global.DEBUGGER = DEBUGGER;
|
||||
global.BYTEARRAYS = BYTEARRAYS;
|
||||
global.TYPEDARRAYS = TYPEDARRAYS;
|
||||
global.PDP11 = PDP11;
|
||||
|
||||
module.exports = PDP11;
|
||||
}
|
||||
295
modules/pdp11/lib/keyboard.js
Normal file
295
modules/pdp11/lib/keyboard.js
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Keyboard component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP11 = require("./defines");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyboardPDP11(parmsKbd)
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsKbd
|
||||
*/
|
||||
function KeyboardPDP11(parmsKbd)
|
||||
{
|
||||
Component.call(this, "Keyboard", parmsKbd, KeyboardPDP11, MessagesPDP11.KEYBOARD);
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
Component.subclass(KeyboardPDP11);
|
||||
|
||||
/**
|
||||
* Alphanumeric and other common (printable) ASCII codes.
|
||||
*
|
||||
* TODO: Determine what we can do to get ALL constants like these inlined by the Closure Compiler
|
||||
* (enum doesn't seem to get the job done); the problem seems to be limited to property references
|
||||
* that use quotes, which is why I've 'unquoted' as many of them as possible.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
KeyboardPDP11.ASCII = {
|
||||
CTRL_A: 1, CTRL_C: 3, CTRL_Z: 26,
|
||||
' ': 32, '!': 33, '"': 34, '#': 35, '$': 36, '%': 37, '&': 38, "'": 39,
|
||||
'(': 40, ')': 41, '*': 42, '+': 43, ',': 44, '-': 45, '.': 46, '/': 47,
|
||||
'0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55,
|
||||
'8': 56, '9': 57, ':': 58, ';': 59, '<': 60, '=': 61, '>': 62, '?': 63,
|
||||
'@': 64, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71,
|
||||
H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79,
|
||||
P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87,
|
||||
X: 88, Y: 89, Z: 90, '[': 91, '\\':92, ']': 93, '^': 94, '_': 95,
|
||||
'`': 96, a: 97, b: 98, c: 99, d: 100, e: 101, f: 102, g: 103,
|
||||
h: 104, i: 105, j: 106, k: 107, l: 108, m: 109, n: 110, o: 111,
|
||||
p: 112, q: 113, r: 114, s: 115, t: 116, u: 117, v: 118, w: 119,
|
||||
x: 120, y: 121, z: 122, '{':123, '|':124, '}':125, '~':126
|
||||
};
|
||||
|
||||
/**
|
||||
* Browser keyCodes we must pay particular attention to. For the most part, these are non-alphanumeric
|
||||
* or function keys, some which may require special treatment (eg, preventDefault() if returning false on
|
||||
* the initial keyDown event is insufficient).
|
||||
*
|
||||
* keyCodes for most common ASCII keys can simply use the appropriate ASCII code above.
|
||||
*
|
||||
* Most of these represent non-ASCII characters (eg, the LEFT arrow key), yet for some reason, browsers
|
||||
* defined them using ASCII codes (eg, the LEFT arrow key uses 37, which is the ASCII code for '%').
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
KeyboardPDP11.KEYCODE = {
|
||||
/* 0x08 */ BS: 8,
|
||||
/* 0x09 */ TAB: 9,
|
||||
/* 0x0A */ LF: 10, // TODO: Determine if any key actually generates this (I suspect there is none)
|
||||
/* 0x0D */ CR: 13,
|
||||
/* 0x10 */ SHIFT: 16,
|
||||
/* 0x11 */ CTRL: 17,
|
||||
/* 0x12 */ ALT: 18,
|
||||
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
|
||||
/* 0x14 */ CAPSLOCK: 20,
|
||||
/* 0x1B */ ESC: 27,
|
||||
/* 0x20 */ SPACE: 32,
|
||||
/* 0x21 */ PGUP: 33,
|
||||
/* 0x22 */ PGDN: 34,
|
||||
/* 0x23 */ END: 35,
|
||||
/* 0x24 */ HOME: 36,
|
||||
/* 0x25 */ LEFT: 37,
|
||||
/* 0x26 */ UP: 38,
|
||||
/* 0x27 */ RIGHT: 39,
|
||||
/* 0x27 */ FF_QUOTE: 39,
|
||||
/* 0x28 */ DOWN: 40,
|
||||
/* 0x2C */ FF_COMMA: 44,
|
||||
/* 0x2C */ PRTSC: 44,
|
||||
/* 0x2D */ INS: 45,
|
||||
/* 0x2E */ DEL: 46,
|
||||
/* 0x2E */ FF_PERIOD: 46,
|
||||
/* 0x2F */ FF_SLASH: 47,
|
||||
/* 0x30 */ ZERO: 48,
|
||||
/* 0x31 */ ONE: 49,
|
||||
/* 0x32 */ TWO: 50,
|
||||
/* 0x33 */ THREE: 51,
|
||||
/* 0x34 */ FOUR: 52,
|
||||
/* 0x35 */ FIVE: 53,
|
||||
/* 0x36 */ SIX: 54,
|
||||
/* 0x37 */ SEVEN: 55,
|
||||
/* 0x38 */ EIGHT: 56,
|
||||
/* 0x39 */ NINE: 57,
|
||||
/* 0x3B */ FF_SEMI: 59,
|
||||
/* 0x3D */ FF_EQUALS: 61,
|
||||
/* 0x5B */ CMD: 91, // aka WIN
|
||||
/* 0x5B */ FF_LBRACK: 91,
|
||||
/* 0x5C */ FF_BSLASH: 92,
|
||||
/* 0x5D */ RCMD: 93, // aka MENU
|
||||
/* 0x5D */ FF_RBRACK: 93,
|
||||
/* 0x60 */ NUM_0: 96,
|
||||
/* 0x60 */ NUM_INS: 96,
|
||||
/* 0x60 */ FF_BQUOTE: 96,
|
||||
/* 0x61 */ NUM_1: 97,
|
||||
/* 0x61 */ NUM_END: 97,
|
||||
/* 0x62 */ NUM_2: 98,
|
||||
/* 0x62 */ NUM_DOWN: 98,
|
||||
/* 0x63 */ NUM_3: 99,
|
||||
/* 0x63 */ NUM_PGDN: 99,
|
||||
/* 0x64 */ NUM_4: 100,
|
||||
/* 0x64 */ NUM_LEFT: 100,
|
||||
/* 0x65 */ NUM_5: 101,
|
||||
/* 0x65 */ NUM_CENTER: 101,
|
||||
/* 0x66 */ NUM_6: 102,
|
||||
/* 0x66 */ NUM_RIGHT: 102,
|
||||
/* 0x67 */ NUM_7: 103,
|
||||
/* 0x67 */ NUM_HOME: 103,
|
||||
/* 0x68 */ NUM_8: 104,
|
||||
/* 0x68 */ NUM_UP: 104,
|
||||
/* 0x69 */ NUM_9: 105,
|
||||
/* 0x69 */ NUM_PGUP: 105,
|
||||
/* 0x6A */ NUM_MUL: 106,
|
||||
/* 0x6B */ NUM_ADD: 107,
|
||||
/* 0x6D */ NUM_SUB: 109,
|
||||
/* 0x6E */ NUM_DEL: 110, // aka PERIOD
|
||||
/* 0x6F */ NUM_DIV: 111,
|
||||
/* 0x70 */ F1: 112,
|
||||
/* 0x71 */ F2: 113,
|
||||
/* 0x72 */ F3: 114,
|
||||
/* 0x73 */ F4: 115,
|
||||
/* 0x74 */ F5: 116,
|
||||
/* 0x75 */ F6: 117,
|
||||
/* 0x76 */ F7: 118,
|
||||
/* 0x77 */ F8: 119,
|
||||
/* 0x78 */ F9: 120,
|
||||
/* 0x79 */ F10: 121,
|
||||
/* 0x7A */ F11: 122,
|
||||
/* 0x7B */ F12: 123,
|
||||
/* 0x90 */ NUM_LOCK: 144,
|
||||
/* 0x91 */ SCROLL_LOCK: 145,
|
||||
/* 0xAD */ FF_DASH: 173,
|
||||
/* 0xBA */ SEMI: 186, // Firefox: 59
|
||||
/* 0xBB */ EQUALS: 187, // Firefox: 61
|
||||
/* 0xBC */ COMMA: 188, // Firefox: 44
|
||||
/* 0xBD */ DASH: 189, // Firefox: 173
|
||||
/* 0xBE */ PERIOD: 190, // Firefox: 46
|
||||
/* 0xBF */ SLASH: 191, // Firefox: 47
|
||||
/* 0xC0 */ BQUOTE: 192, // Firefox: 96
|
||||
/* 0xDB */ LBRACK: 219, // Firefox: 91
|
||||
/* 0xDC */ BSLASH: 220, // Firefox: 92
|
||||
/* 0xDD */ RBRACK: 221, // Firefox: 93
|
||||
/* 0xDE */ QUOTE: 222, // Firefox: 39
|
||||
/* 0xE0 */ FF_CMD: 224, // Firefox only (used for both CMD and RCMD)
|
||||
//
|
||||
// The following biases use what I'll call Decimal Coded Binary or DCB (the opposite of BCD),
|
||||
// where the thousands digit is used to store the sum of "binary" digits 1 and/or 2 and/or 4.
|
||||
//
|
||||
// Technically, that makes it DCO (Decimal Coded Octal), but then again, BCD should have really
|
||||
// been called HCD (Hexadecimal Coded Decimal), so if "they" can take liberties, so can I.
|
||||
//
|
||||
// ONDOWN is a bias we add to browser keyCodes that we want to handle on "down" rather than on "press".
|
||||
//
|
||||
ONDOWN: 1000,
|
||||
//
|
||||
// ONRIGHT is a bias we add to browser keyCodes that need to check for a "right" location (default is "left")
|
||||
//
|
||||
ONRIGHT: 2000,
|
||||
//
|
||||
// FAKE is a bias we add to signal these are fake keyCodes corresponding to internal keystroke combinations.
|
||||
// The actual values are for internal use only and merely need to be unique and used consistently.
|
||||
//
|
||||
FAKE: 4000
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the event object's 'location' property for a non-zero value for the following ONRIGHT keys.
|
||||
*/
|
||||
KeyboardPDP11.KEYCODE.NUM_CR = KeyboardPDP11.KEYCODE.CR + KeyboardPDP11.KEYCODE.ONRIGHT;
|
||||
|
||||
/*
|
||||
* Maps "stupid" keyCodes to their "non-stupid" counterparts
|
||||
*/
|
||||
KeyboardPDP11.STUPID_KEYCODES = {};
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.SEMI] = KeyboardPDP11.ASCII[';']; // 186 -> 59
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.EQUALS] = KeyboardPDP11.ASCII['=']; // 187 -> 61
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.COMMA] = KeyboardPDP11.ASCII[',']; // 188 -> 44
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.DASH] = KeyboardPDP11.ASCII['-']; // 189 -> 45
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.PERIOD] = KeyboardPDP11.ASCII['.']; // 190 -> 46
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.SLASH] = KeyboardPDP11.ASCII['/']; // 191 -> 47
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.BQUOTE] = KeyboardPDP11.ASCII['`']; // 192 -> 96
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.LBRACK] = KeyboardPDP11.ASCII['[']; // 219 -> 91
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.BSLASH] = KeyboardPDP11.ASCII['\\']; // 220 -> 92
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.RBRACK] = KeyboardPDP11.ASCII[']']; // 221 -> 93
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.QUOTE] = KeyboardPDP11.ASCII["'"]; // 222 -> 39
|
||||
KeyboardPDP11.STUPID_KEYCODES[KeyboardPDP11.KEYCODE.FF_DASH] = KeyboardPDP11.ASCII['-'];
|
||||
|
||||
KeyboardPDP11.MINPRESSTIME = 100; // 100ms
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* @this {KeyboardPDP11}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "esc")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
KeyboardPDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {KeyboardPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
KeyboardPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg; // NOTE: The "dbg" property must be set for the message functions to work
|
||||
this.chipset = null; // /** @type {ChipSetPDP11} */ (cmp.getMachineComponent("ChipSet"));
|
||||
};
|
||||
|
||||
/**
|
||||
* KeyboardPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "keyboard", extracting the
|
||||
* JSON-encoded parameters for the Keyboard constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a Keyboard component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
KeyboardPDP11.init = function()
|
||||
{
|
||||
var aeKbd = Component.getElementsByClass(document, PDP11.APPCLASS, "keyboard");
|
||||
for (var iKbd = 0; iKbd < aeKbd.length; iKbd++) {
|
||||
var eKbd = aeKbd[iKbd];
|
||||
var parmsKbd = Component.getComponentParms(eKbd);
|
||||
var kbd = new KeyboardPDP11(parmsKbd);
|
||||
Component.bindComponentControls(kbd, eKbd, PDP11.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize every Keyboard module on the page.
|
||||
*/
|
||||
web.onInit(KeyboardPDP11.init);
|
||||
|
||||
if (NODE) module.exports = KeyboardPDP11;
|
||||
826
modules/pdp11/lib/memory.js
Normal file
826
modules/pdp11/lib/memory.js
Normal file
|
|
@ -0,0 +1,826 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Memory component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var CPUDefPDP11 = require("./cpudef");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* @class DataView
|
||||
* @property {function(number,boolean):number} getUint8
|
||||
* @property {function(number,number,boolean)} setUint8
|
||||
* @property {function(number,boolean):number} getUint16
|
||||
* @property {function(number,number,boolean)} setUint16
|
||||
* @property {function(number,boolean):number} getInt32
|
||||
* @property {function(number,number,boolean)} setInt32
|
||||
*/
|
||||
|
||||
var littleEndian = (TYPEDARRAYS? (function() {
|
||||
var buffer = new ArrayBuffer(2);
|
||||
new DataView(buffer).setUint16(0, 256, true);
|
||||
return new Uint16Array(buffer)[0] === 256;
|
||||
})() : false);
|
||||
|
||||
/**
|
||||
* MemoryPDP11(addr, used, size, type)
|
||||
*
|
||||
* 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,
|
||||
* the size of any given MemoryPDP11 object's underlying buffer can be either zero or bus.nBlockSize;
|
||||
* memory read/write functions for empty (buffer-less) blocks are mapped to readNone/writeNone.
|
||||
*
|
||||
* The Bus allocates empty blocks for the entire address space during initialization, so that
|
||||
* any reads/writes to undefined addresses will have no effect. Later, the ROM and RAM
|
||||
* components will ask the Bus to allocate memory for specific ranges, and the Bus will allocate
|
||||
* as many new blockSize MemoryPDP11 objects as the ranges require. Partial MemoryPDP11 blocks could
|
||||
* also be supported in theory, but in practice, they're not.
|
||||
*
|
||||
* Because MemoryPDP11 blocks now allow us to have a "sparse" address space, we could choose to
|
||||
* take the memory hit of allocating 4K arrays per block, where each element stores only one byte,
|
||||
* instead of the more frugal but slightly slower approach of allocating arrays of 32-bit dwords
|
||||
* (LONGARRAYS) and shifting/masking bytes/words to/from dwords; in theory, byte accesses would
|
||||
* be faster and word accesses somewhat less faster.
|
||||
*
|
||||
* However, preliminary testing of that feature (BYTEARRAYS) did not yield significantly faster
|
||||
* performance, so it is OFF by default to minimize our memory consumption. Using TYPEDARRAYS
|
||||
* would seem best, but as discussed in defines.js, it's off by default, because it doesn't perform
|
||||
* as well as LONGARRAYS; the other advantage of TYPEDARRAYS is that it should theoretically use
|
||||
* about 1/2 the memory of LONGARRAYS (32-bit elements vs 64-bit numbers), but I value speed over
|
||||
* size at this point. Also, not all JavaScript implementations support TYPEDARRAYS (IE9 is probably
|
||||
* the only real outlier: it lacks typed arrays but otherwise has all the necessary HTML5 support).
|
||||
*
|
||||
* WARNING: Since MemoryPDP11 blocks are low-level objects that have no UI requirements, they
|
||||
* do not inherit from the Component class, so if you want to use any Component class methods,
|
||||
* such as Component.assert(), use the corresponding Debugger methods instead (assuming a debugger
|
||||
* is available).
|
||||
*
|
||||
* @constructor
|
||||
* @param {number|null} [addr] of lowest used address in block
|
||||
* @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)
|
||||
*/
|
||||
function MemoryPDP11(addr, used, size, type)
|
||||
{
|
||||
var i;
|
||||
this.id = (MemoryPDP11.idBlock += 2);
|
||||
this.adw = null;
|
||||
this.offset = 0;
|
||||
this.addr = addr;
|
||||
this.used = used;
|
||||
this.size = size || 0;
|
||||
this.type = type || MemoryPDP11.TYPE.NONE;
|
||||
this.fReadOnly = (type == MemoryPDP11.TYPE.ROM);
|
||||
this.copyBreakpoints(); // initialize the block's Debugger info; the caller will reinitialize
|
||||
|
||||
/*
|
||||
* TODO: Study the impact of dirty block tracking. The original purposes were to allow saveMemory()
|
||||
* to save only dirty blocks, and to enable the Video component to quickly detect changes to the video buffer.
|
||||
* But the benefit to saveMemory() is minimal, and the Video component has other options; for example, it now
|
||||
* uses a custom memory controller for all EGA/VGA video modes, which performs its own dirty block tracking,
|
||||
* and that could easily be extended to the older MDA/CGA video modes, which still use conventional memory blocks.
|
||||
* Alternatively, we could restrict the use of dirty block tracking to certain memory types (eg, VIDEO memory).
|
||||
*
|
||||
* However, a quick test with dirty block tracking disabled didn't yield a noticeable improvement in performance,
|
||||
* so I think the overhead of our block-based architecture is swamping the impact of these micro-updates.
|
||||
*/
|
||||
this.fDirty = this.fDirtyEver = false;
|
||||
|
||||
/*
|
||||
* For empty memory blocks, all we need to do is ensure all access functions are mapped to "none" handlers.
|
||||
*/
|
||||
if (!size) {
|
||||
this.setAccess();
|
||||
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)
|
||||
* know how to deal with this simple 1-1 mapping of addresses to bytes and words.
|
||||
*
|
||||
* TODO: Consider initializing the memory array to random (or pseudo-random) values in DEBUG
|
||||
* mode; pseudo-random might be best, to help make any bugs reproducible.
|
||||
*/
|
||||
if (TYPEDARRAYS) {
|
||||
this.buffer = new ArrayBuffer(size);
|
||||
this.dv = new DataView(this.buffer, 0, size);
|
||||
/*
|
||||
* If littleEndian is true, we can use ab[], aw[] and adw[] directly; well, we can use them
|
||||
* whenever the offset is a multiple of 1, 2 or 4, respectively. Otherwise, we must fallback to
|
||||
* dv.getUint8()/dv.setUint8(), dv.getUint16()/dv.setUint16() and dv.getInt32()/dv.setInt32().
|
||||
*/
|
||||
this.ab = new Uint8Array(this.buffer, 0, size);
|
||||
this.aw = new Uint16Array(this.buffer, 0, size >> 1);
|
||||
this.adw = new Int32Array(this.buffer, 0, size >> 2);
|
||||
this.setAccess(littleEndian? MemoryPDP11.afnArrayLE : MemoryPDP11.afnArrayBE);
|
||||
} else {
|
||||
if (BYTEARRAYS) {
|
||||
this.ab = new Array(size);
|
||||
} else {
|
||||
/*
|
||||
* NOTE: This is the default mode of operation (!TYPEDARRAYS && !BYTEARRAYS), because it
|
||||
* seems to provide the best performance; and although in theory, that performance might
|
||||
* come at twice the overhead of TYPEDARRAYS, it's increasingly likely that the JavaScript
|
||||
* runtime will notice that all we ever store are 32-bit values, and optimize accordingly.
|
||||
*/
|
||||
this.adw = new Array(size >> 2);
|
||||
for (i = 0; i < this.adw.length; i++) this.adw[i] = 0;
|
||||
}
|
||||
this.setAccess(MemoryPDP11.afnMemory);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic memory types
|
||||
*
|
||||
* RAM is the most conventional memory type, providing full read/write capability to x86-compatible (ie,
|
||||
* 'little endian") storage. ROM is equally conventional, except that the fReadOnly property is set,
|
||||
* disabling writes. VIDEO is treated exactly like RAM, unless a controller is provided. Both RAM and
|
||||
* VIDEO memory are always considered writable, and even ROM can be written using the Bus setByteDirect()
|
||||
* interface (which in turn uses the MemoryPDP11 writeByteDirect() interface), allowing the ROM component to
|
||||
* initialize its own memory. The CTRL type is used to identify memory-mapped devices that do not need
|
||||
* any default storage and always provide their own controller.
|
||||
*
|
||||
* Unallocated regions of the address space contain a special memory block of type NONE that contains
|
||||
* no storage. Mapping every addressible location to a memory block allows all accesses to be routed in
|
||||
* exactly the same manner, without resorting to any range or processor checks.
|
||||
*
|
||||
* These types are not mutually exclusive. For example, VIDEO memory could be allocated as RAM, with or
|
||||
* without a custom controller (the original Monochrome and CGA video cards used read/write storage that
|
||||
* was indistinguishable from RAM), and CTRL memory could be allocated as an empty block of any type, with
|
||||
* a custom controller. A few types are required for certain features (eg, ROM is required if you want
|
||||
* read-only memory), but the larger purpose of these types is to help document the caller's intent and to
|
||||
* provide the Control Panel with the ability to highlight memory regions accordingly.
|
||||
*/
|
||||
MemoryPDP11.TYPE = {
|
||||
NONE: 0,
|
||||
RAM: 1,
|
||||
ROM: 2,
|
||||
VIDEO: 3,
|
||||
CTRL: 4,
|
||||
COLORS: ["black", "blue", "green", "cyan"],
|
||||
NAMES: ["NONE", "RAM", "ROM", "VID", "H/W"]
|
||||
};
|
||||
|
||||
/*
|
||||
* Last used block ID (used for debugging only)
|
||||
*/
|
||||
MemoryPDP11.idBlock = 0;
|
||||
|
||||
/**
|
||||
* adjustEndian(dw)
|
||||
*
|
||||
* @param {number} dw
|
||||
* @return {number}
|
||||
*/
|
||||
MemoryPDP11.adjustEndian = function(dw) {
|
||||
if (TYPEDARRAYS && !littleEndian) {
|
||||
dw = (dw << 24) | ((dw << 8) & 0x00ff0000) | ((dw >> 8) & 0x0000ff00) | (dw >>> 24);
|
||||
}
|
||||
return dw;
|
||||
};
|
||||
|
||||
MemoryPDP11.prototype = {
|
||||
constructor: MemoryPDP11,
|
||||
parent: null,
|
||||
/**
|
||||
* init(addr)
|
||||
*
|
||||
* Quick reinitializer when reusing a MemoryPDP11 block.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} addr
|
||||
*/
|
||||
init: function(addr) {
|
||||
this.addr = addr;
|
||||
},
|
||||
/**
|
||||
* clone(mem, type)
|
||||
*
|
||||
* Converts the current MemoryPDP11 block (this) into a clone of the given MemoryPDP11 block (mem),
|
||||
* and optionally overrides the current block's type with the specified type.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {MemoryPDP11} mem
|
||||
* @param {number} [type]
|
||||
* @param {DebuggerPDP11} [dbg]
|
||||
*/
|
||||
clone: function(mem, type, dbg) {
|
||||
/*
|
||||
* Original memory block IDs are even; cloned memory block IDs are odd;
|
||||
* the original ID of the current block is lost, but that's OK, since it was presumably
|
||||
* produced merely to become a clone.
|
||||
*/
|
||||
this.id = mem.id | 0x1;
|
||||
this.used = mem.used;
|
||||
this.size = mem.size;
|
||||
if (type) {
|
||||
this.type = type;
|
||||
this.fReadOnly = (type == MemoryPDP11.TYPE.ROM);
|
||||
}
|
||||
if (TYPEDARRAYS) {
|
||||
this.buffer = mem.buffer;
|
||||
this.dv = mem.dv;
|
||||
this.ab = mem.ab;
|
||||
this.aw = mem.aw;
|
||||
this.adw = mem.adw;
|
||||
this.setAccess(littleEndian? MemoryPDP11.afnArrayLE : MemoryPDP11.afnArrayBE);
|
||||
} else {
|
||||
if (BYTEARRAYS) {
|
||||
this.ab = mem.ab;
|
||||
} else {
|
||||
this.adw = mem.adw;
|
||||
}
|
||||
this.setAccess(MemoryPDP11.afnMemory);
|
||||
}
|
||||
this.copyBreakpoints(dbg, mem);
|
||||
},
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This gets the contents of a MemoryPDP11 block as an array of 32-bit values; used by BusPDP11.saveMemory(),
|
||||
* which in turn is called by CPUStatePDP11.save().
|
||||
*
|
||||
* MemoryPDP11 blocks with custom memory controllers do NOT save their contents; that's the responsibility
|
||||
* of the controller component.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @return {Array|Int32Array|null}
|
||||
*/
|
||||
save: function() {
|
||||
var adw, i;
|
||||
if (BYTEARRAYS) {
|
||||
adw = new Array(this.size >> 2);
|
||||
var off = 0;
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
adw[i] = this.ab[off] | (this.ab[off + 1] << 8) | (this.ab[off + 2] << 16) | (this.ab[off + 3] << 24);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
else if (TYPEDARRAYS) {
|
||||
/*
|
||||
* It might be tempting to just return a copy of Int32Array(this.buffer, 0, this.size >> 2),
|
||||
* but we can't be sure of the "endianness" of an Int32Array -- which would be OK if the array
|
||||
* was always saved/restored on the same machine, but there's no guarantee of that, either.
|
||||
* So we use getInt32() and require little-endian values.
|
||||
*
|
||||
* Moreover, an Int32Array isn't treated by JSON.stringify() and JSON.parse() exactly like
|
||||
* a normal array; it's serialized as an Object rather than an Array, so it lacks a "length"
|
||||
* property and causes problems for State.store() and State.parse().
|
||||
*/
|
||||
adw = new Array(this.size >> 2);
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
adw[i] = this.dv.getInt32(i << 2, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
adw = this.adw;
|
||||
}
|
||||
return adw;
|
||||
},
|
||||
/**
|
||||
* restore(adw)
|
||||
*
|
||||
* This restores the contents of a MemoryPDP11 block from an array of 32-bit values;
|
||||
* used by BusPDP11.restoreMemory(), which is called by CPUStatePDP11.restore(), after all other
|
||||
* components have been restored and thus all MemoryPDP11 blocks have been allocated
|
||||
* by their respective components.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {Array|null} adw
|
||||
* @return {boolean} true if successful, false if block size mismatch
|
||||
*/
|
||||
restore: function(adw) {
|
||||
/*
|
||||
* 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
|
||||
* card's "spec'ed" address instead of the programmed address, so there were no controller-owned
|
||||
* memory blocks installed at the programmed address, and so we arrived here at a block with
|
||||
* no controller AND no data.
|
||||
*/
|
||||
Component.assert(adw != null);
|
||||
|
||||
if (adw && this.size == adw.length << 2) {
|
||||
var i;
|
||||
if (BYTEARRAYS) {
|
||||
var off = 0;
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
this.ab[off] = adw[i] & 0xff;
|
||||
this.ab[off + 1] = (adw[i] >> 8) & 0xff;
|
||||
this.ab[off + 2] = (adw[i] >> 16) & 0xff;
|
||||
this.ab[off + 3] = (adw[i] >> 24) & 0xff;
|
||||
off += 4;
|
||||
}
|
||||
} else if (TYPEDARRAYS) {
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
this.dv.setInt32(i << 2, adw[i], true);
|
||||
}
|
||||
} else {
|
||||
this.adw = adw;
|
||||
}
|
||||
this.fDirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* setAccess(afn, fDirect)
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the MemoryPDP11 type.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {Array.<function()>} [afn] function table
|
||||
* @param {boolean} [fDirect] (true to update direct access functions as well; default is true)
|
||||
*/
|
||||
setAccess: function(afn, fDirect) {
|
||||
if (!afn) {
|
||||
Component.assert(this.type == MemoryPDP11.TYPE.NONE);
|
||||
afn = MemoryPDP11.afnNone;
|
||||
}
|
||||
this.setReadAccess(afn, fDirect);
|
||||
this.setWriteAccess(afn, fDirect);
|
||||
},
|
||||
/**
|
||||
* setReadAccess(afn, fDirect)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {Array.<function()>} afn
|
||||
* @param {boolean} [fDirect]
|
||||
*/
|
||||
setReadAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cReadBreakpoints) {
|
||||
this.readByte = afn[0] || this.readNone;
|
||||
this.readShort = afn[1] || this.readShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.readByteDirect = afn[0] || this.readNone;
|
||||
this.readShortDirect = afn[1] || this.readShortDefault;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* setWriteAccess(afn, fDirect)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {Array.<function()>} afn
|
||||
* @param {boolean} [fDirect]
|
||||
*/
|
||||
setWriteAccess: function(afn, fDirect) {
|
||||
if (!fDirect || !this.cWriteBreakpoints) {
|
||||
this.writeByte = !this.fReadOnly && afn[2] || this.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
|
||||
}
|
||||
if (fDirect || fDirect === undefined) {
|
||||
this.writeByteDirect = afn[2] || this.writeNone;
|
||||
this.writeShortDirect = afn[3] || this.writeShortDefault;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* resetReadAccess()
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
*/
|
||||
resetReadAccess: function() {
|
||||
this.readByte = this.readByteDirect;
|
||||
this.readShort = this.readShortDirect;
|
||||
},
|
||||
/**
|
||||
* resetWriteAccess()
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
*/
|
||||
resetWriteAccess: function() {
|
||||
this.writeByte = this.fReadOnly? this.writeNone : this.writeByteDirect;
|
||||
this.writeShort = this.fReadOnly? this.writeShortDefault : this.writeShortDirect;
|
||||
},
|
||||
/**
|
||||
* printAddr(sMessage)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {string} sMessage
|
||||
*/
|
||||
printAddr: function(sMessage) {
|
||||
if (DEBUG && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.printMessage(sMessage + ' ' + (this.addr != null? ('%' + str.toHex(this.addr)) : '#' + this.id), true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* addBreakpoint(off, fWrite)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {boolean} fWrite
|
||||
*/
|
||||
addBreakpoint: function(off, fWrite) {
|
||||
if (!fWrite) {
|
||||
if (this.cReadBreakpoints++ === 0) {
|
||||
this.setReadAccess(MemoryPDP11.afnChecked, false);
|
||||
}
|
||||
if (DEBUG) this.printAddr("read breakpoint added to memory block");
|
||||
}
|
||||
else {
|
||||
if (this.cWriteBreakpoints++ === 0) {
|
||||
this.setWriteAccess(MemoryPDP11.afnChecked, false);
|
||||
}
|
||||
if (DEBUG) this.printAddr("write breakpoint added to memory block");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* removeBreakpoint(off, fWrite)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {boolean} fWrite
|
||||
*/
|
||||
removeBreakpoint: function(off, fWrite) {
|
||||
if (!fWrite) {
|
||||
if (--this.cReadBreakpoints === 0) {
|
||||
this.resetReadAccess();
|
||||
if (DEBUG) this.printAddr("all read breakpoints removed from memory block");
|
||||
}
|
||||
Component.assert(this.cReadBreakpoints >= 0);
|
||||
}
|
||||
else {
|
||||
if (--this.cWriteBreakpoints === 0) {
|
||||
this.resetWriteAccess();
|
||||
if (DEBUG) this.printAddr("all write breakpoints removed from memory block");
|
||||
}
|
||||
Component.assert(this.cWriteBreakpoints >= 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* copyBreakpoints(dbg, mem)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {DebuggerPDP11} [dbg]
|
||||
* @param {MemoryPDP11} [mem] (outgoing MemoryPDP11 block to copy breakpoints from, if any)
|
||||
*/
|
||||
copyBreakpoints: function(dbg, mem) {
|
||||
this.dbg = dbg;
|
||||
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
|
||||
if (mem) {
|
||||
if ((this.cReadBreakpoints = mem.cReadBreakpoints)) {
|
||||
this.setReadAccess(MemoryPDP11.afnChecked, false);
|
||||
}
|
||||
if ((this.cWriteBreakpoints = mem.cWriteBreakpoints)) {
|
||||
this.setWriteAccess(MemoryPDP11.afnChecked, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* readNone(off)
|
||||
*
|
||||
* Previously, this always returned 0x00, but the initial memory probe by the COMPAQ DeskPro 386 ROM BIOS
|
||||
* writes 0x0000 to the first word of every 64Kb block in the nearly 16Mb address space it supports, and
|
||||
* if it reads back 0x0000, it will initially think that LOTS of RAM exists, only to be disappointed later
|
||||
* when it performs a more exhaustive memory test, generating unwanted error messages in the process.
|
||||
*
|
||||
* TODO: Determine if we should have separate readByteNone(), readShortNone() and readLongNone() functions
|
||||
* to return 0xff, 0xffff and 0xffffffff|0, respectively. This seems sufficient for now, as it seems unlikely
|
||||
* that a system would require nonexistent memory locations to return ALL bits set.
|
||||
*
|
||||
* Also, I'm reluctant to address that potential issue by simply returning -1, because to date, the above
|
||||
* MemoryPDP11 interfaces have always returned values that are properly masked to 8, 16 or 32 bits, respectively.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readNone: function readNone(off, addr) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.CPU | MessagesPDP11.MEM) /* && !off */) {
|
||||
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr), true);
|
||||
}
|
||||
return 0xff;
|
||||
},
|
||||
/**
|
||||
* writeNone(off, v, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} v (could be either a byte or word value, since we use the same handler for both kinds of accesses)
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeNone: function writeNone(off, v, addr) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.CPU | MessagesPDP11.MEM) /* && !off */) {
|
||||
this.dbg.message("attempt to write " + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* readShortDefault(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readShortDefault: function readShortDefault(off, addr) {
|
||||
return this.readByte(off++, addr++) | (this.readByte(off, addr) << 8);
|
||||
},
|
||||
/**
|
||||
* writeShortDefault(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeShortDefault: function writeShortDefault(off, w, addr) {
|
||||
this.writeByte(off++, w & 0xff, addr++);
|
||||
this.writeByte(off, w >> 8, addr);
|
||||
},
|
||||
/**
|
||||
* readByteMemory(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readByteMemory: function readByteMemory(off, addr) {
|
||||
if (BYTEARRAYS) {
|
||||
return this.ab[off];
|
||||
}
|
||||
return ((this.adw[off >> 2] >>> ((off & 0x3) << 3)) & 0xff);
|
||||
},
|
||||
/**
|
||||
* readShortMemory(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readShortMemory: function readShortMemory(off, addr) {
|
||||
if (BYTEARRAYS) {
|
||||
return this.ab[off] | (this.ab[off + 1] << 8);
|
||||
}
|
||||
var w;
|
||||
var idw = off >> 2;
|
||||
var nShift = (off & 0x3) << 3;
|
||||
var dw = (this.adw[idw] >> nShift);
|
||||
if (nShift < 24) {
|
||||
w = dw & 0xffff;
|
||||
} else {
|
||||
w = (dw & 0xff) | ((this.adw[idw + 1] & 0xff) << 8);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
/**
|
||||
* writeByteMemory(off, b, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} b
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeByteMemory: function writeByteMemory(off, b, addr) {
|
||||
if (BYTEARRAYS) {
|
||||
this.ab[off] = b;
|
||||
} else {
|
||||
var idw = off >> 2;
|
||||
var nShift = (off & 0x3) << 3;
|
||||
this.adw[idw] = (this.adw[idw] & ~(0xff << nShift)) | (b << nShift);
|
||||
}
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeShortMemory(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeShortMemory: function writeShortMemory(off, w, addr) {
|
||||
if (BYTEARRAYS) {
|
||||
this.ab[off] = (w & 0xff);
|
||||
this.ab[off + 1] = (w >> 8);
|
||||
} else {
|
||||
var idw = off >> 2;
|
||||
var nShift = (off & 0x3) << 3;
|
||||
if (nShift < 24) {
|
||||
this.adw[idw] = (this.adw[idw] & ~(0xffff << nShift)) | (w << nShift);
|
||||
} else {
|
||||
this.adw[idw] = (this.adw[idw] & 0x00ffffff) | (w << 24);
|
||||
idw++;
|
||||
this.adw[idw] = (this.adw[idw] & (0xffffff00|0)) | (w >> 8);
|
||||
}
|
||||
}
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* readByteChecked(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readByteChecked: function readByteChecked(off, addr) {
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryRead(this.addr + off);
|
||||
}
|
||||
return this.readByteDirect(off, addr);
|
||||
},
|
||||
/**
|
||||
* readShortChecked(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readShortChecked: function readShortChecked(off, addr) {
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryRead(this.addr + off, 2);
|
||||
}
|
||||
return this.readShortDirect(off, addr);
|
||||
},
|
||||
/**
|
||||
* writeByteChecked(off, b, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} b
|
||||
*/
|
||||
writeByteChecked: function writeByteChecked(off, b, addr) {
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryWrite(this.addr + off);
|
||||
}
|
||||
if (this.fReadOnly) this.writeNone(off, b, addr); else this.writeByteDirect(off, b, addr);
|
||||
},
|
||||
/**
|
||||
* writeShortChecked(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
*/
|
||||
writeShortChecked: function writeShortChecked(off, w, addr) {
|
||||
if (DEBUGGER && this.dbg && this.addr != null) {
|
||||
this.dbg.checkMemoryWrite(this.addr + off, 2)
|
||||
}
|
||||
if (this.fReadOnly) this.writeNone(off, w, addr); else this.writeShortDirect(off, w, addr);
|
||||
},
|
||||
/**
|
||||
* readByteBE(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readByteBE: function readByteBE(off, addr) {
|
||||
return this.ab[off];
|
||||
},
|
||||
/**
|
||||
* readByteLE(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readByteLE: function readByteLE(off, addr) {
|
||||
return this.ab[off];
|
||||
},
|
||||
/**
|
||||
* readShortBE(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readShortBE: function readShortBE(off, addr) {
|
||||
return this.dv.getUint16(off, true);
|
||||
},
|
||||
/**
|
||||
* readShortLE(off, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @return {number}
|
||||
*/
|
||||
readShortLE: function readShortLE(off, addr) {
|
||||
/*
|
||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
||||
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||
*/
|
||||
return (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
|
||||
},
|
||||
/**
|
||||
* writeByteBE(off, b, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} b
|
||||
* @param {number} addr
|
||||
*/
|
||||
writeByteBE: function writeByteBE(off, b, addr) {
|
||||
this.ab[off] = b;
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeByteLE(off, b, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} b
|
||||
*/
|
||||
writeByteLE: function writeByteLE(off, b, addr) {
|
||||
this.ab[off] = b;
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeShortBE(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
*/
|
||||
writeShortBE: function writeShortBE(off, w, addr) {
|
||||
this.dv.setUint16(off, w, true);
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeShortLE(off, w, addr)
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
* @param {number} addr
|
||||
* @param {number} w
|
||||
*/
|
||||
writeShortLE: function writeShortLE(off, w, addr) {
|
||||
/*
|
||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
||||
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||
*/
|
||||
if (off & 0x1) {
|
||||
this.ab[off] = w;
|
||||
this.ab[off+1] = w >> 8;
|
||||
} else {
|
||||
this.aw[off >> 1] = w;
|
||||
}
|
||||
this.fDirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
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];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if (NODE) module.exports = MemoryPDP11;
|
||||
89
modules/pdp11/lib/messages.js
Normal file
89
modules/pdp11/lib/messages.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* @fileoverview Defines PDP11 message categories.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var MessagesPDP11 = {
|
||||
CPU: 0x00000001,
|
||||
BUS: 0x00000040,
|
||||
MEM: 0x00000080,
|
||||
KEYBOARD: 0x00010000,
|
||||
KEYS: 0x00020000,
|
||||
DISK: 0x00200000,
|
||||
SERIAL: 0x00800000,
|
||||
SPEAKER: 0x02000000,
|
||||
COMPUTER: 0x04000000,
|
||||
LOG: 0x10000000,
|
||||
WARN: 0x20000000,
|
||||
BUFFER: 0x40000000,
|
||||
HALT: 0x80000000|0
|
||||
};
|
||||
|
||||
/*
|
||||
* Message categories supported by the messageEnabled() function and other assorted message
|
||||
* functions. Each category has a corresponding bit value that can be combined (ie, OR'ed) as
|
||||
* needed. The Debugger's message command ("m") is used to turn message categories on and off,
|
||||
* like so:
|
||||
*
|
||||
* m port on
|
||||
* m port off
|
||||
* ...
|
||||
*
|
||||
* NOTE: The order of these categories can be rearranged, alphabetized, etc, as desired; just be
|
||||
* aware that changing the bit values could break saved Debugger states (not a huge concern, just
|
||||
* something to be aware of).
|
||||
*/
|
||||
MessagesPDP11.CATEGORIES = {
|
||||
"cpu": MessagesPDP11.CPU,
|
||||
"bus": MessagesPDP11.BUS,
|
||||
"mem": MessagesPDP11.MEM,
|
||||
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
|
||||
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"disk": MessagesPDP11.DISK,
|
||||
"serial": MessagesPDP11.SERIAL,
|
||||
"speaker": MessagesPDP11.SPEAKER,
|
||||
"computer": MessagesPDP11.COMPUTER,
|
||||
"log": MessagesPDP11.LOG,
|
||||
"warn": MessagesPDP11.WARN,
|
||||
/*
|
||||
* Now we turn to message actions rather than message types; for example, setting "halt"
|
||||
* on or off doesn't enable "halt" messages, but rather halts the CPU on any message above.
|
||||
*
|
||||
* Similarly, "m buffer on" turns on message buffering, deferring the display of all messages
|
||||
* until "m buffer off" is issued.
|
||||
*/
|
||||
"buffer": MessagesPDP11.BUFFER,
|
||||
"halt": MessagesPDP11.HALT
|
||||
};
|
||||
|
||||
if (NODE) module.exports = MessagesPDP11;
|
||||
48
modules/pdp11/lib/nodebugger.js
Normal file
48
modules/pdp11/lib/nodebugger.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @fileoverview Compile-time definitions for Debugger-less configurations.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* WARNING: DEBUGGER needs to accurately reflect whether or not the Debugger component is (or will be) loaded.
|
||||
* In the compiled case, we rely on the Closure Compiler to override DEBUGGER as appropriate. When it's *false*,
|
||||
* nearly all of debugger.js will be conditionally removed by the compiler, reducing it to little more than a
|
||||
* "type skeleton", which also solves some type-related warnings we would otherwise have if we tried to remove
|
||||
* debugger.js from the compilation process altogether.
|
||||
*
|
||||
* However, when we're in "development mode" and running uncompiled code in debugger-less configurations,
|
||||
* I would still like to skip loading debugger.js altogether. To do that, we must arrange for this additional file,
|
||||
* nodebugger.js, to be loaded immediately after defines.js, *explicitly* overriding the previously defined value
|
||||
* of DEBUGGER with *false*.
|
||||
*/
|
||||
DEBUGGER = false;
|
||||
185
modules/pdp11/lib/panel.js
Normal file
185
modules/pdp11/lib/panel.js
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 Panel component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP11 = require("./defines");
|
||||
var BusPDP11 = require("./bus");
|
||||
var CPUDefPDP11 = require("./cpudef");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* PanelPDP11(parmsPanel)
|
||||
*
|
||||
* The PanelPDP11 component has no required (parmsPanel) properties.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsPanel
|
||||
*/
|
||||
function PanelPDP11(parmsPanel)
|
||||
{
|
||||
Component.call(this, "Panel", parmsPanel, PanelPDP11);
|
||||
}
|
||||
|
||||
Component.subclass(PanelPDP11);
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* Most panel layouts don't have bindings of their own, so we pass along all binding requests to the
|
||||
* Computer, CPU, Keyboard and Debugger components first. The order shouldn't matter, since any component
|
||||
* that doesn't recognize the specified binding should simply ignore it.
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "reset")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
PanelPDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
if (this.cmp && this.cmp.setBinding(sHTMLType, sBinding, control, sValue)) return true;
|
||||
if (this.cpu && this.cpu.setBinding(sHTMLType, sBinding, control, sValue)) return true;
|
||||
if (DEBUGGER && this.dbg && this.dbg.setBinding(sHTMLType, sBinding, control, sValue)) return true;
|
||||
return this.parent.setBinding.call(this, sHTMLType, sBinding, control, sValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
PanelPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
PanelPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) PanelPDP11.init();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
PanelPDP11.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* Update function for Panels containing elements with high-frequency display requirements.
|
||||
*
|
||||
* For older (and slower) DOM-based display elements, those are sill being managed by the CPUState component,
|
||||
* so it has its own updateStatus() handler.
|
||||
*
|
||||
* The Computer's updateStatus() handler is currently responsible for calling both our handler and the CPU's handler.
|
||||
*
|
||||
* @this {PanelPDP11}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
PanelPDP11.prototype.updateStatus = function(fForce)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* PanelPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "panel", extracting the
|
||||
* JSON-encoded parameters for the PanelPDP11 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a PanelPDP11 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*
|
||||
* NOTE: Unlike most other component init() functions, this one is designed to be
|
||||
* called multiple times: once at load time, so that we can bind our print()
|
||||
* function to the panel's output control ASAP, and again when the Computer component
|
||||
* is verifying that all components are ready and invoking their powerUp() functions.
|
||||
*
|
||||
* Our powerUp() method gives us a second opportunity to notify any components that
|
||||
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
|
||||
* might want to use.
|
||||
*/
|
||||
PanelPDP11.init = function()
|
||||
{
|
||||
var fReady = false;
|
||||
var aePanels = Component.getElementsByClass(document, PDP11.APPCLASS, "panel");
|
||||
for (var iPanel=0; iPanel < aePanels.length; iPanel++) {
|
||||
var ePanel = aePanels[iPanel];
|
||||
var parmsPanel = Component.getComponentParms(ePanel);
|
||||
var panel = Component.getComponentByID(parmsPanel['id']);
|
||||
if (!panel) {
|
||||
fReady = true;
|
||||
panel = new PanelPDP11(parmsPanel);
|
||||
}
|
||||
Component.bindComponentControls(panel, ePanel, PDP11.APPCLASS);
|
||||
if (fReady) panel.setReady();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize every Panel module on the page.
|
||||
*/
|
||||
web.onInit(PanelPDP11.init);
|
||||
|
||||
if (NODE) module.exports = PanelPDP11;
|
||||
199
modules/pdp11/lib/ram.js
Normal file
199
modules/pdp11/lib/ram.js
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 RAM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var ROMPDP11 = require("./rom");
|
||||
}
|
||||
|
||||
/**
|
||||
* RAMPDP11(parmsRAM)
|
||||
*
|
||||
* The RAMPDP11 component expects the following (parmsRAM) properties:
|
||||
*
|
||||
* addr: starting physical address of RAM (default is 0)
|
||||
* size: amount of RAM, in bytes (default is 0, which means defer to motherboard switch settings)
|
||||
*
|
||||
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
|
||||
* Computer component calls powerUp().
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsRAM
|
||||
*/
|
||||
function RAMPDP11(parmsRAM)
|
||||
{
|
||||
Component.call(this, "RAM", parmsRAM, RAMPDP11);
|
||||
|
||||
this.addrRAM = parmsRAM['addr'];
|
||||
this.sizeRAM = parmsRAM['size'];
|
||||
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
|
||||
this.fAllocated = false;
|
||||
}
|
||||
|
||||
Component.subclass(RAMPDP11);
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
RAMPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.setReady();
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
/*
|
||||
* The Computer powers up the CPU last, at which point CPUState state is restored,
|
||||
* which includes the Bus state, and since we use the Bus to allocate all our memory,
|
||||
* memory contents are already restored for us, so we don't need the usual restore
|
||||
* logic. We just need to call reset(), to allocate memory for the RAM.
|
||||
*/
|
||||
this.reset();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point CPUState state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
* our memory, memory contents are already saved for us, so we don't need the usual
|
||||
* save logic.
|
||||
*/
|
||||
return (fSave)? this.save() : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
*/
|
||||
RAMPDP11.prototype.reset = function()
|
||||
{
|
||||
if (!this.fAllocated && this.sizeRAM) {
|
||||
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, MemoryPDP11.TYPE.RAM)) {
|
||||
this.fAllocated = true;
|
||||
}
|
||||
}
|
||||
if (!this.fAllocated) {
|
||||
Component.error("No RAM allocated");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the RAMPDP11 component.
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @return {Object}
|
||||
*/
|
||||
RAMPDP11.prototype.save = function()
|
||||
{
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the RAMPDP11 component.
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.restore = function(data)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* RAMPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "ram", extracting the
|
||||
* JSON-encoded parameters for the RAMPDP11 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a RAMPDP11 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
RAMPDP11.init = function()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(document, PDP11.APPCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
var parmsRAM = Component.getComponentParms(eRAM);
|
||||
var ram = new RAMPDP11(parmsRAM);
|
||||
Component.bindComponentControls(ram, eRAM, PDP11.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize all the RAMPDP11 modules on the page.
|
||||
*/
|
||||
web.onInit(RAMPDP11.init);
|
||||
|
||||
if (NODE) module.exports = RAMPDP11;
|
||||
391
modules/pdp11/lib/rom.js
Normal file
391
modules/pdp11/lib/rom.js
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 ROM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PDP11 = require("./defines");
|
||||
var CPUDefPDP11 = require("./cpudef");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* ROMPDP11(parmsROM)
|
||||
*
|
||||
* The ROMPDP11 component expects the following (parmsROM) properties:
|
||||
*
|
||||
* addr: physical address of ROM
|
||||
* size: amount of ROM, in bytes
|
||||
* alias: physical alias address (null if none)
|
||||
* file: name of ROM data file
|
||||
* writable: true to make ROM writable (default is false)
|
||||
*
|
||||
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND the
|
||||
* ROM data file has finished loading (see doneLoad()).
|
||||
*
|
||||
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
function ROMPDP11(parmsROM)
|
||||
{
|
||||
Component.call(this, "ROM", parmsROM, ROMPDP11);
|
||||
|
||||
this.abROM = null;
|
||||
this.addrROM = parmsROM['addr'];
|
||||
this.sizeROM = parmsROM['size'];
|
||||
this.fWritable = parmsROM['writable'];
|
||||
|
||||
/*
|
||||
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
|
||||
* physical addresses; eg:
|
||||
*
|
||||
* [0xf0000,0xffff0000,0xffff8000]
|
||||
*
|
||||
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
|
||||
* aliased locations listed under a separate property.
|
||||
*
|
||||
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
|
||||
*/
|
||||
this.addrAlias = parmsROM['alias'];
|
||||
|
||||
this.sFilePath = parmsROM['file'];
|
||||
this.sFileName = str.getBaseName(this.sFilePath);
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected ROM file has a ".json" extension, then we assume it's pre-converted
|
||||
* JSON-encoded ROM data, so we load it as-is; ditto for ROM files with a ".hex" extension.
|
||||
* Otherwise, we ask our server-side ROM converter to return the file in a JSON-compatible format.
|
||||
*/
|
||||
var sFileExt = str.getExtension(this.sFileName);
|
||||
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
|
||||
sFileURL = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var rom = this;
|
||||
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
rom.doneLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component.subclass(ROMPDP11);
|
||||
|
||||
/*
|
||||
* NOTE: There's currently no need for this component to have a reset() function, since
|
||||
* once the ROM data is loaded, it can't be changed, so there's nothing to reinitialize.
|
||||
*
|
||||
* OK, well, I take that back, because the Debugger, if installed, has the ability to modify
|
||||
* ROM contents, so in that case, having a reset() function that restores the original ROM data
|
||||
* might be useful; then again, it might not, depending on what you're trying to debug.
|
||||
*
|
||||
* If we do add reset(), then we'll want to change copyROM() to hang onto the original
|
||||
* ROM data; currently, we release it after copying it into the read-only memory allocated
|
||||
* via bus.addMemory().
|
||||
*/
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
ROMPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.copyROM();
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
ROMPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.id, this.addrROM, this.sizeROM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
|
||||
*/
|
||||
delete this.aSymbols;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* Since we have nothing to do on powerDown(), and no state to return, we could simply omit
|
||||
* this function. But it doesn't hurt anything, and maybe we'll use our state to save something
|
||||
* useful down the road, like user-defined symbols (ie, symbols that the Debugger may have
|
||||
* created, above and beyond those symbols we automatically loaded, if any, along with the ROM).
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
ROMPDP11.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sROMData, nErrorCode)
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {string} sURL
|
||||
* @param {string} sROMData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
ROMPDP11.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load system ROM (error " + nErrorCode + ": " + sURL + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Component.addMachineResource(this.idMachine, sURL, sROMData);
|
||||
|
||||
if (sROMData.charAt(0) == "[" || sROMData.charAt(0) == "{") {
|
||||
try {
|
||||
/*
|
||||
* The most likely source of any exception will be here: parsing the JSON-encoded ROM data.
|
||||
*/
|
||||
var rom = eval("(" + sROMData + ")");
|
||||
var ab = rom['bytes'];
|
||||
var adw = rom['data'];
|
||||
|
||||
if (ab) {
|
||||
this.abROM = ab;
|
||||
}
|
||||
else if (adw) {
|
||||
/*
|
||||
* Convert all the DWORDs into BYTEs, so that subsequent code only has to deal with abROM.
|
||||
*/
|
||||
this.abROM = new Array(adw.length * 4);
|
||||
for (var idw = 0, ib = 0; idw < adw.length; idw++) {
|
||||
this.abROM[ib++] = adw[idw] & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 8) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 16) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.abROM = rom;
|
||||
}
|
||||
|
||||
this.aSymbols = rom['symbols'];
|
||||
|
||||
if (!this.abROM.length) {
|
||||
Component.error("Empty ROM: " + sURL);
|
||||
return;
|
||||
}
|
||||
else if (this.abROM.length == 1) {
|
||||
Component.error(this.abROM[0]);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.notice("ROM data error: " + e.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Parse the ROM data manually; we assume it's in "simplified" hex form (a series of hex byte-values
|
||||
* separated by whitespace).
|
||||
*/
|
||||
var sHexData = sROMData.replace(/\n/gm, " ").replace(/ +$/, "");
|
||||
var asHexData = sHexData.split(" ");
|
||||
this.abROM = new Array(asHexData.length);
|
||||
for (var i = 0; i < asHexData.length; i++) {
|
||||
this.abROM[i] = str.parseInt(asHexData[i], 16);
|
||||
}
|
||||
}
|
||||
this.copyROM();
|
||||
};
|
||||
|
||||
/**
|
||||
* copyROM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the the ROM data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the abROM data. When both
|
||||
* those criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
*/
|
||||
ROMPDP11.prototype.copyROM = function()
|
||||
{
|
||||
if (!this.isReady()) {
|
||||
if (!this.sFilePath) {
|
||||
this.setReady();
|
||||
}
|
||||
else if (this.abROM && this.bus) {
|
||||
/*
|
||||
* If no explicit size was specified, then use whatever the actual size is.
|
||||
*/
|
||||
if (!this.sizeROM) {
|
||||
this.sizeROM = this.abROM.length;
|
||||
}
|
||||
if (this.abROM.length != this.sizeROM) {
|
||||
/*
|
||||
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from
|
||||
* marking the component ready. TODO: Revisit this decision. On the one hand, it sounds like a
|
||||
* good idea to stop the machine in its tracks whenever a setError() occurs, but there may also be
|
||||
* times when we'd like to forge ahead anyway.
|
||||
*/
|
||||
this.setError("ROM size (" + str.toHexLong(this.abROM.length) + ") does not match specified size (" + str.toHexLong(this.sizeROM) + ")");
|
||||
}
|
||||
else if (this.addROM(this.addrROM)) {
|
||||
|
||||
var aliases = [];
|
||||
if (typeof this.addrAlias == "number") {
|
||||
aliases.push(this.addrAlias);
|
||||
} else if (this.addrAlias != null && this.addrAlias.length) {
|
||||
aliases = this.addrAlias;
|
||||
}
|
||||
for (var i = 0; i < aliases.length; i++) {
|
||||
this.cloneROM(aliases[i]);
|
||||
}
|
||||
/*
|
||||
* We used to hang onto the original ROM data so that we could restore any bytes the CPU overwrote,
|
||||
* using memory write-notification handlers, but with the introduction of read-only memory blocks, that's
|
||||
* no longer necessary.
|
||||
*
|
||||
* TODO: Consider an option to retain the ROM data, and give the user some way of restoring ROMs.
|
||||
* That may be useful for "resumable" machines that save/restore all dirty block of memory, regardless
|
||||
* whether they're ROM or RAM. However, the only way to modify a machine's ROM is with the Debugger,
|
||||
* and Debugger users should know better.
|
||||
*/
|
||||
delete this.abROM;
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addROM(addr)
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {number} addr
|
||||
* @return {boolean}
|
||||
*/
|
||||
ROMPDP11.prototype.addROM = function(addr)
|
||||
{
|
||||
if (this.bus.addMemory(addr, this.sizeROM, this.fWritable? MemoryPDP11.TYPE.RAM : MemoryPDP11.TYPE.ROM)) {
|
||||
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abROM.length) + " bytes)");
|
||||
var i;
|
||||
for (i = 0; i < this.abROM.length; i++) {
|
||||
this.bus.setByteDirect(addr + i, this.abROM[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* We don't need to report an error here, because addMemory() already takes care of that.
|
||||
*/
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* cloneROM(addr)
|
||||
*
|
||||
* For ROMs with one or more alias addresses, we used to call addROM() for each address. However,
|
||||
* that obviously wasted memory, since each alias was an independent copy, and if you used the
|
||||
* Debugger to edit the ROM in one location, the changes would not appear in the other location(s).
|
||||
*
|
||||
* Now that the Bus component provides low-level getMemoryBlocks() and setMemoryBlocks() methods
|
||||
* to manually get and set the blocks of any memory range, it is now possible to create true aliases.
|
||||
*
|
||||
* @this {ROMPDP11}
|
||||
* @param {number} addr
|
||||
*/
|
||||
ROMPDP11.prototype.cloneROM = function(addr)
|
||||
{
|
||||
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
|
||||
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
|
||||
};
|
||||
|
||||
/**
|
||||
* ROMPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "rom", extracting the
|
||||
* JSON-encoded parameters for the ROMPDP11 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a ROMPDP11 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
ROMPDP11.init = function()
|
||||
{
|
||||
var aeROM = Component.getElementsByClass(document, PDP11.APPCLASS, "rom");
|
||||
for (var iROM = 0; iROM < aeROM.length; iROM++) {
|
||||
var eROM = aeROM[iROM];
|
||||
var parmsROM = Component.getComponentParms(eROM);
|
||||
var rom = new ROMPDP11(parmsROM);
|
||||
Component.bindComponentControls(rom, eROM, PDP11.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize all the ROMPDP11 modules on the page.
|
||||
*/
|
||||
web.onInit(ROMPDP11.init);
|
||||
|
||||
if (NODE) module.exports = ROMPDP11;
|
||||
|
|
@ -577,6 +577,21 @@ function embedPC8080(idMachine, sXMLFile, sXSLFile, sParms)
|
|||
return embedMachine("PC8080", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
|
||||
}
|
||||
|
||||
/**
|
||||
* embedPDP11(idMachine, sXMLFile, sXSLFile, sParms)
|
||||
*
|
||||
* @param {string} idMachine
|
||||
* @param {string} sXMLFile
|
||||
* @param {string} sXSLFile
|
||||
* @param {string} [sParms]
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
function embedPDP11(idMachine, sXMLFile, sXSLFile, sParms)
|
||||
{
|
||||
if (fAsync) web.enablePageEvents(false);
|
||||
return embedMachine("PDP11", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the Closure Compiler from renaming functions we want to export,
|
||||
* by adding them as (named) properties of a global object.
|
||||
|
|
@ -591,6 +606,9 @@ if (APPNAME == "PCx86") {
|
|||
if (APPNAME == "PC8080") {
|
||||
window['embedPC8080'] = embedPC8080;
|
||||
}
|
||||
if (APPNAME == "PDP11") {
|
||||
window['embedPDP11'] = embedPDP11;
|
||||
}
|
||||
|
||||
window['enableEvents'] = web.enablePageEvents;
|
||||
window['sendEvent'] = web.sendPageEvent;
|
||||
|
|
|
|||
Loading…
Reference in a new issue