Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-10-05 17:45:40 -07:00
commit 7fa19afe3c
410 changed files with 46555 additions and 27752 deletions

View file

@ -12,9 +12,10 @@ It is our *private* counterpart to the **node_modules** folder, where all *publi
The project currently includes these emulation modules:
* [PCx86](pcx86/), the emulation module for [x86-based machines](/devices/pcx86/)
* [PC8080](pc8080/), the emulation module for [8080-based machines](/devices/pc8080/)
* [C1Pjs](c1pjs/), the 6502-based emulation module for the [Challenger 1P](/devices/c1p/)
* [C1Pjs](c1pjs/): 6502 emulation module for the [Challenger 1P](/devices/c1p/)
* [PCx86](pcx86/): x86 emulation module for [IBM PC machines](/devices/pcx86/)
* [PC8080](pc8080/): 8080 emulation module for [8080-based machines](/devices/pc8080/)
* [PDPjs](pdp11/): PDP-11 emulation module for [PDP-11 machines](/devices/pdp11/) (eg, PDP-11/45, PDP-11/70)
along with variety of Node support modules, such as:

View file

@ -63,8 +63,8 @@ function C1PCPU(parmsCPU)
Component.call(this, "C1PCPU", parmsCPU);
this.clearRegs();
this.flags.fPowered = false;
this.flags.fRunning = false;
this.flags.powered = false;
this.flags.running = false;
this.fAutoStart = parmsCPU["autoStart"];
/*
@ -488,7 +488,7 @@ Component.subclass(C1PCPU);
*/
C1PCPU.prototype.reset = function(fPowerOn)
{
if (this.flags.fRunning) {
if (this.flags.running) {
this.halt();
}
this.clearRegs();
@ -523,7 +523,7 @@ C1PCPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
this.bindings[sBinding] = control;
control.onclick = function(cpu) {
return function() {
if (!cpu.flags.fRunning) {
if (!cpu.flags.running) {
cpu.run();
} else {
cpu.halt();
@ -587,7 +587,7 @@ C1PCPU.prototype.setBuffer = function(abMemory, start, end)
*/
C1PCPU.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
if (fOn && !this.flags.powered) {
this.cmp = cmp;
/*
* Attach the Debugger, if any, to the CPU, so that the CPU can periodically
@ -615,7 +615,7 @@ C1PCPU.prototype.setPower = function(fOn, cmp)
};
}(video);
}
this.flags.fPowered = true;
this.flags.powered = true;
this.reset(true);
this.update();
}
@ -888,7 +888,7 @@ C1PCPU.prototype.displayStatus = function()
*/
C1PCPU.prototype.isRunning = function()
{
return this.flags.fRunning;
return this.flags.running;
};
/**
@ -1059,7 +1059,7 @@ C1PCPU.prototype.run = function()
if (this.cmp) this.cmp.stop(this.msRunStart, this.nRunCycles);
return;
}
if (!this.flags.fRunning) {
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of run() calls, calculates the maximum number
@ -1068,7 +1068,7 @@ C1PCPU.prototype.run = function()
*/
this.setSpeed();
if (this.cmp) this.cmp.start();
this.flags.fRunning = true;
this.flags.running = true;
if (this.bindings["run"]) this.bindings["run"].innerHTML = "Halt";
this.setFocus();
}
@ -1115,7 +1115,7 @@ C1PCPU.prototype.run = function()
this.nCyclesNextYield += this.nCyclesPerYield;
break;
}
} while (this.flags.fRunning);
} while (this.flags.running);
}
catch (e) {
this.halt();
@ -1276,8 +1276,8 @@ C1PCPU.prototype.halt = function()
this.isBusy(true);
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.flags.running) {
this.flags.running = false;
if (this.bindings["run"]) this.bindings["run"].innerHTML = "Run";
}
};
@ -1311,7 +1311,7 @@ C1PCPU.prototype.update = function()
*/
C1PCPU.prototype.getCycles = function()
{
return (this.flags.fRunning? this.nRunCycles + this.nBurstCycles - this.nStepCycles : 0);
return (this.flags.running? this.nRunCycles + this.nBurstCycles - this.nStepCycles : 0);
};
/**

View file

@ -586,8 +586,8 @@ if (DEBUGGER) {
*/
C1PDebugger.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
this.cpu = cmp.getComponentByType("cpu");
}
};
@ -1247,8 +1247,8 @@ if (DEBUGGER) {
{
var sLine = str.toHex(addr, 4);
var bOpCode = this.getByte(addr++);
var aOpDesc = this.aaOperations[bOpCode];
var b = (bOpCode === undefined? 0 : bOpCode);
var aOpDesc = this.aaOperations[b];
var abOperand = [];
var cb = (aOpDesc[1] === undefined? 0 : aOpDesc[1]);
do {

View file

@ -222,7 +222,7 @@ function C1PDiskController(parmsDC)
{
Component.call(this, "C1PDiskController", parmsDC);
this.flags.fPowered = false;
this.flags.powered = false;
/*
* Our DiskController simulates the combination of an MC6820 PIA and an MC6850 ACIA.
@ -743,8 +743,8 @@ C1PDiskController.prototype.setBuffer = function(abMemory, start, end, cpu)
*/
C1PDiskController.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
}
};

View file

@ -127,7 +127,7 @@ function C1PKeyboard(parmsKbd)
{
Component.call(this, "C1PKeyboard", parmsKbd);
this.flags.fPowered = false;
this.flags.powered = false;
this.nDefaultModel = parmsKbd['model'];
/*
@ -204,7 +204,7 @@ function C1PKeyboard(parmsKbd)
this.msReleaseRepeat = 100; // number of milliseconds before a held key is "forced" up (assuming auto-repeat)
this.msInjectDelay = 300; // number of milliseconds between injected keystrokes
this.aButtonCodeMap = [];
this.aButtonCodeMap = {};
this.aButtonCodeMap['break'] = this.CHARCODE_BREAK;
this.aButtonCodeMap['esc'] = this.CHARCODE_ESC;
this.aButtonCodeMap['ctrl-c'] = this.CHARCODE_CTRLC;
@ -521,8 +521,8 @@ C1PKeyboard.prototype.setModel = function(nModel)
*/
C1PKeyboard.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
this.cmp = cmp;
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
}

View file

@ -48,7 +48,7 @@ function C1PPanel(parmsPanel)
{
Component.call(this, "C1PPanel", parmsPanel);
this.flags.fPowered = false;
this.flags.powered = false;
}
Component.subclass(C1PPanel);
@ -81,8 +81,8 @@ C1PPanel.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
C1PPanel.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
this.cmp = cmp;
this.cpu = cmp.getComponentByType("cpu");
this.kbd = cmp.getComponentByType("keyboard");

View file

@ -118,8 +118,8 @@ C1PROM.prototype.setBuffer = function(abMemory, start, end, cpu)
*/
C1PROM.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
}
};

View file

@ -49,7 +49,7 @@ function C1PSerialPort(parmsSerial)
{
Component.call(this, "C1PSerialPort", parmsSerial);
this.flags.fPowered = false;
this.flags.powered = false;
this.fDemo = parmsSerial['demo'];
this.reset(true);
@ -217,8 +217,8 @@ C1PSerialPort.prototype.setBuffer = function(abMemory, start, end, cpu)
*/
C1PSerialPort.prototype.setPower = function(fOn, cmp)
{
if (fOn && !this.flags.fPowered) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered) {
this.flags.powered = true;
this.cmp = cmp;
this.kbd = cmp.getComponentByType("keyboard");
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");

View file

@ -346,8 +346,8 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
* it ourselves, too. This also means that updateScreen() need check only fPower and not isReady(),
* since we guarantee that the former implies the latter.
*/
if (fOn && !this.flags.fPowered && this.isReady()) {
this.flags.fPowered = true;
if (fOn && !this.flags.powered && this.isReady()) {
this.flags.powered = true;
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
/*
* If we have an associated keyboard, then ensure that the keyboard will be notified whenever
@ -366,8 +366,8 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
}
}
else
if (!fOn && this.flags.fPowered) {
this.flags.fPowered = false;
if (!fOn && this.flags.powered) {
this.flags.powered = false;
/*
* This is where we would add some method of blanking the display, without the disturbing the video
* buffer contents, and blocking all further updates to the display.
@ -481,7 +481,7 @@ C1PVideo.prototype.initScreen = function()
C1PVideo.prototype.updateScreen = function()
{
var offset = 0;
if (this.flags.fPowered) {
if (this.flags.powered) {
while (offset < this.cbScreen) {
var b = this.abMem[this.offVideo + offset];
if (this.abScreen[offset] != b) {

View file

@ -1283,7 +1283,7 @@ DiskDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offData)
}
else {
sLine += str.toHexByte(v);
if (!sASCII) sASCII = "0x" + str.toHex(offData + off) + " ";
if (!sASCII) sASCII = str.toHex(offData + off, 0, true) + " ";
sASCII += (v >= 0x20 && v < 0x7F && v != 0x3C && v != 0x3E? String.fromCharCode(v) : ".");
}
}
@ -1377,7 +1377,7 @@ DiskDump.prototype.trimSector = function(buf, len)
dwPattern = buf.readInt32LE(off);
while ((off -= cbPattern) >= 0) {
var dw = buf.readInt32LE(off);
// if (fDebug) DiskDump.logConsole("0x" + str.toHex(off) + ": comparing 0x" + str.toHex(dw) + " to pattern 0x" + str.toHex(dwPattern));
// if (fDebug) DiskDump.logConsole(str.toHex(off, 0, true) + ": comparing " + str.toHex(dw, 0, true) + " to pattern " + str.toHex(dwPattern, 0, true));
if (dw != dwPattern) break;
cbTrim += cbPattern;
}
@ -2081,7 +2081,7 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
if (!err) {
if (fDebug && cb != buf.length) DiskDump.logConsole(file.FILE_NAME + ": initial size (" + cb + ") does not match actual size (" + buf.length + ")");
buf.copy(obj.bufDisk.buf || obj.bufDisk, off);
if (fDebug) DiskDump.logConsole("0x" + str.toHex(off) + ": 0x" + str.toHex(buf.length) + " bytes written for " + file.FILE_PATH);
if (fDebug) DiskDump.logConsole(str.toHex(off, 0, true) + ": " + str.toHex(buf.length, 0, true) + " bytes written for " + file.FILE_PATH);
if (obj.sManifestFile) file.FILE_MD5 = crypto.createHash('md5').update(buf).digest('hex');
}
if (!--obj.cWritesPending) done(err);
@ -2101,7 +2101,7 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
}
if (bufData) {
bufData.copy(this.bufDisk, offDisk);
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": 0x" + str.toHex(bufData.length) + " bytes IMMEDIATELY written for " + aFiles[iFile].FILE_PATH);
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": " + str.toHex(bufData.length, 0, true) + " bytes IMMEDIATELY written for " + aFiles[iFile].FILE_PATH);
}
offDisk += cbData;
cClusters += ((cbData / cbCluster) | 0);
@ -2117,11 +2117,11 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
for (iFile = 0; iFile < aFiles.length; iFile++) {
var cb = aFiles[iFile].FILE_SIZE;
if (cb < 0) {
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": buildClusters()");
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": buildClusters()");
var cSubClusters = this.buildClusters(aFiles[iFile].FILE_DATA, offDisk, cbCluster, aFiles[iFile].FILE_CLUS, iLevel + 1, done);
cClusters += cSubClusters;
offDisk += cSubClusters * cbCluster;
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": buildClusters() returned, writing " + cSubClusters + " clusters");
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": buildClusters() returned, writing " + cSubClusters + " clusters");
}
}
}
@ -2288,7 +2288,7 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
var nTargetSectors = (this.kbTarget? this.kbTarget * 2 : 0);
var cbTotal = this.calcFileSizes(aFiles);
if (fDebug) DiskDump.logConsole("total calculated size for " + aFiles.length + " files/folders: " + cbTotal + " bytes (0x" + str.toHex(cbTotal) + ")");
if (fDebug) DiskDump.logConsole("total calculated size for " + aFiles.length + " files/folders: " + cbTotal + " bytes (" + str.toHex(cbTotal, 0, true) + ")");
if (cbTotal >= cbMax) {
err = new Error("file(s) too large (" + cbTotal + " bytes total, " + cbMax + " bytes maximum)");
@ -2914,7 +2914,7 @@ DiskDump.prototype.convertOSIDiskToJSON = function()
json += this.dumpLine(-2, "]");
}
else {
DiskDump.logError(new Error("unrecognized OSI disk track at 0x" + str.toHex(offTrack)));
DiskDump.logError(new Error("unrecognized OSI disk track at " + str.toHex(offTrack, 0, true)));
break;
}
}

View file

@ -452,7 +452,7 @@ FileDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offDump, nWidth
} else {
sLine += str.toHexByte(v);
}
if (!sASCII) sASCII = "0x" + str.toHex(off) + " ";
if (!sASCII) sASCII = str.toHex(off, 0, true) + " ";
sASCII += (v >= 0x20 && v < 0x7F && v != 0x3C && v != 0x3E? String.fromCharCode(v) : ".");
}
}

View file

@ -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] : "");
/*

View file

@ -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))) {

View file

@ -59,16 +59,11 @@ if (NODE) {
* 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 {CPUState} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
function Bus(parmsBus, cpu, dbg)
{
@ -80,35 +75,16 @@ function Bus(parmsBus, cpu, dbg)
this.nBusWidth = parmsBus['busWidth'] || 16;
/*
* Compute all Bus 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":
* Compute all Bus6502 memory block parameters, based on the width of the bus. The entire
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
* the bus width. The following table summarizes our simplistic calculations.
*
* Bus Width Block Shift Block Size
* --------- ----------- ----------
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
*
@ -119,7 +95,9 @@ function Bus(parmsBus, cpu, dbg)
*/
this.addrTotal = Math.pow(2, this.nBusWidth);
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
this.nBlockShift = (this.nBusWidth >> 1) + 2;
if (this.nBlockShift < 10) this.nBlockShift = 10;
if (this.nBlockShift > 15) this.nBlockShift = 15;
this.nBlockSize = 1 << this.nBlockShift;
this.nBlockLen = this.nBlockSize >> 2;
this.nBlockLimit = this.nBlockSize - 1;
@ -127,39 +105,6 @@ function Bus(parmsBus, cpu, dbg)
this.nBlockMask = this.nBlockTotal - 1;
this.assert(this.nBlockMask <= Bus.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.
*/
@ -177,38 +122,6 @@ Bus.ERROR = {
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
* }}
*/
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo 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 BusInfo;
/**
* initMemory()
*
@ -365,6 +278,42 @@ Bus.prototype.cleanMemory = function(addr, size)
return fClean;
};
/*
* Data types used by scanMemory()
*/
/**
* @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
* }}
*/
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo 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 BusInfo;
/**
* scanMemory(info, addr, size)
*
@ -741,309 +690,6 @@ Bus.prototype.restoreMemory = function(a)
return true;
};
/**
* addPortInputBreak(port)
*
* @this {Bus}
* @param {number} [port]
* @return {boolean} true if break on port input enabled, false if disabled
*/
Bus.prototype.addPortInputBreak = function(port)
{
if (port === undefined) {
this.fPortInputBreakAll = !this.fPortInputBreakAll;
return this.fPortInputBreakAll;
}
if (this.aPortInputNotify[port] === undefined) {
this.aPortInputNotify[port] = [null, false];
}
this.aPortInputNotify[port][1] = !this.aPortInputNotify[port][1];
return this.aPortInputNotify[port][1];
};
/**
* addPortInputNotify(start, end, fn)
*
* Add a port input-notification handler to the list of such handlers.
*
* @this {Bus}
* @param {number} start port address
* @param {number} end port address
* @param {function(number,number)} fn is called with the port and IP values at the time of the input
*/
Bus.prototype.addPortInputNotify = function(start, end, fn)
{
if (fn !== undefined) {
for (var port = start; port <= end; port++) {
if (this.aPortInputNotify[port] !== undefined) {
Component.warning("Input port " + str.toHexWord(port) + " already registered");
continue;
}
this.aPortInputNotify[port] = [fn, false];
if (MAXDEBUG) this.log("addPortInputNotify(" + str.toHexWord(port) + ")");
}
}
};
/**
* addPortInputTable(component, table, offset)
*
* Add port input-notification handlers from the specified table (a batch version of addPortInputNotify)
*
* @this {Bus}
* @param {Component} component
* @param {Object} table
* @param {number} [offset] is an optional port offset
*/
Bus.prototype.addPortInputTable = function(component, table, offset)
{
if (offset === undefined) offset = 0;
for (var port in table) {
this.addPortInputNotify(+port + offset, +port + offset, table[port].bind(component));
}
};
/**
* addPortInputWidth(port, size)
*
* By default, all input ports are 1 byte wide; ports that are wider must call this function.
*
* @this {Bus}
* @param {number} port
* @param {number} size (1, 2 or 4)
*/
Bus.prototype.addPortInputWidth = function(port, size)
{
this.aPortInputWidth[port] = size;
};
/**
* checkPortInputNotify(port, size, addrIP)
*
* @this {Bus}
* @param {number} port
* @param {number} size (1, 2 or 4)
* @param {number} [addrIP] is the IP value at the time of the input
* @return {number} simulated port data
*
* NOTE: It seems that parts of the ROM BIOS (like the RS-232 probes around F000:E5D7 in the 5150 BIOS)
* assume that ports for non-existent hardware return 0xff rather than 0x00, hence my new default (0xff) below.
*/
Bus.prototype.checkPortInputNotify = function(port, size, addrIP)
{
var data = 0, shift = 0;
while (size > 0) {
var aNotify = this.aPortInputNotify[port];
var sizePort = this.aPortInputWidth[port] || 1;
var maskPort = (sizePort == 1? 0xff : (sizePort == 2? 0xffff : -1));
var dataPort = maskPort;
/*
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
* This assert will warn us if this is a pressing need.
*/
this.assert(size >= sizePort);
if (aNotify !== undefined) {
if (aNotify[0]) {
dataPort = aNotify[0](port, addrIP);
if (dataPort === undefined) {
dataPort = maskPort;
} else {
dataPort &= maskPort;
}
}
if (DEBUGGER && this.dbg && this.fPortInputBreakAll != aNotify[1]) {
this.dbg.checkPortInput(port, size, dataPort);
}
}
else {
if (DEBUGGER && this.dbg) {
this.dbg.messageIO(this, port, null, addrIP);
if (this.fPortInputBreakAll) this.dbg.checkPortInput(port, size, dataPort);
}
}
data |= dataPort << shift;
shift += (sizePort << 3);
port += sizePort;
size -= sizePort;
}
this.assert(!size);
return data;
};
/**
* removePortInputNotify(start, end)
*
* Remove port input-notification handler(s) (to be ENABLED later if needed)
*
* @this {Bus}
* @param {number} start address
* @param {number} end address
*
Bus.prototype.removePortInputNotify = function(start, end)
{
for (var port = start; port < end; port++) {
if (this.aPortInputNotify[port]) {
delete this.aPortInputNotify[port];
}
}
};
*/
/**
* addPortOutputBreak(port)
*
* @this {Bus}
* @param {number} [port]
* @return {boolean} true if break on port output enabled, false if disabled
*/
Bus.prototype.addPortOutputBreak = function(port)
{
if (port === undefined) {
this.fPortOutputBreakAll = !this.fPortOutputBreakAll;
return this.fPortOutputBreakAll;
}
if (this.aPortOutputNotify[port] === undefined) {
this.aPortOutputNotify[port] = [null, false];
}
this.aPortOutputNotify[port][1] = !this.aPortOutputNotify[port][1];
return this.aPortOutputNotify[port][1];
};
/**
* addPortOutputNotify(start, end, fn)
*
* Add a port output-notification handler to the list of such handlers.
*
* @this {Bus}
* @param {number} start port address
* @param {number} end port address
* @param {function(number,number)} fn is called with the port and IP values at the time of the output
*/
Bus.prototype.addPortOutputNotify = function(start, end, fn)
{
if (fn !== undefined) {
for (var port = start; port <= end; port++) {
if (this.aPortOutputNotify[port] !== undefined) {
Component.warning("Output port " + str.toHexWord(port) + " already registered");
continue;
}
this.aPortOutputNotify[port] = [fn, false];
if (MAXDEBUG) this.log("addPortOutputNotify(" + str.toHexWord(port) + ")");
}
}
};
/**
* addPortOutputTable(component, table, offset)
*
* Add port output-notification handlers from the specified table (a batch version of addPortOutputNotify)
*
* @this {Bus}
* @param {Component} component
* @param {Object} table
* @param {number} [offset] is an optional port offset
*/
Bus.prototype.addPortOutputTable = function(component, table, offset)
{
if (offset === undefined) offset = 0;
for (var port in table) {
this.addPortOutputNotify(+port + offset, +port + offset, table[port].bind(component));
}
};
/**
* addPortOutputWidth(port, size)
*
* By default, all output ports are 1 byte wide; ports that are wider must call this function.
*
* @this {Bus}
* @param {number} port
* @param {number} size (1, 2 or 4)
*/
Bus.prototype.addPortOutputWidth = function(port, size)
{
this.aPortOutputWidth[port] = size;
};
/**
* checkPortOutputNotify(port, size, data, addrIP)
*
* @this {Bus}
* @param {number} port
* @param {number} size
* @param {number} data
* @param {number} [addrIP] is the IP value at the time of the output
*/
Bus.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
{
var shift = 0;
while (size > 0) {
var aNotify = this.aPortOutputNotify[port];
var sizePort = this.aPortOutputWidth[port] || 1;
var maskPort = (sizePort == 1? 0xff : (sizePort == 2? 0xffff : -1));
var dataPort = (data >>>= shift) & maskPort;
/*
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
* This assert will warn us if this is a pressing need.
*/
this.assert(size >= sizePort);
if (aNotify !== undefined) {
if (aNotify[0]) {
aNotify[0](port, dataPort, addrIP);
}
if (DEBUGGER && this.dbg && this.fPortOutputBreakAll != aNotify[1]) {
this.dbg.checkPortOutput(port, size, dataPort);
}
}
else {
if (DEBUGGER && this.dbg) {
this.dbg.messageIO(this, port, dataPort, addrIP);
if (this.fPortOutputBreakAll) this.dbg.checkPortOutput(port, size, dataPort);
}
}
shift += (sizePort << 3);
port += sizePort;
size -= sizePort;
}
this.assert(!size);
};
/**
* removePortOutputNotify(start, end)
*
* Remove port output-notification handler(s) (to be ENABLED later if needed)
*
* @this {Bus}
* @param {number} start address
* @param {number} end address
*
Bus.prototype.removePortOutputNotify = function(start, end)
{
for (var port = start; port < end; port++) {
if (this.aPortOutputNotify[port]) {
delete this.aPortOutputNotify[port];
}
}
};
*/
/**
* reportError(op, addr, size, fQuiet)
*

View file

@ -109,7 +109,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
Component.call(this, "Computer", parmsComputer, Computer, Messages.COMPUTER);
this.flags.fPowered = false;
this.flags.powered = false;
this.setMachineParms(parmsMachine);
@ -152,7 +152,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
Component.error("Unable to find CPU component");
return;
}
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
this.dbg = /** @type {Debugger6502} */ (Component.getComponentByType("Debugger", this.id));
/*
* Enumerate all Video components for future updateVideo() calls.
@ -635,9 +635,9 @@ Computer.prototype.powerOn = function(resume)
*/
Computer.prototype.powerRestore = function(component, stateComputer, fRepower, fRestore)
{
if (!component.flags.fPowered) {
if (!component.flags.powered) {
component.flags.fPowered = true;
component.flags.powered = true;
if (component.powerUp) {
@ -740,12 +740,12 @@ Computer.prototype.donePowerOn = function(aParms)
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
if (DEBUG && this.flags.fPowered && this.messageEnabled()) {
if (DEBUG && this.flags.powered && this.messageEnabled()) {
this.printMessage("Computer.donePowerOn(): redundant");
}
this.fInitialized = true;
this.flags.fPowered = true;
this.flags.powered = true;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Shutdown";
@ -786,22 +786,22 @@ Computer.prototype.donePowerOn = function(aParms)
*/
Computer.prototype.checkPower = function()
{
if (this.flags.fPowered) return true;
if (this.flags.powered) return true;
var component = null, iComponent;
var aComponents = Component.getComponents(this.id);
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fReady) break;
if (component !== this && !component.flags.ready) break;
}
if (iComponent == aComponents.length) {
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fPowered) break;
if (component !== this && !component.flags.powered) break;
}
}
if (iComponent == aComponents.length) component = this;
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.fReady? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.ready? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
web.alertUser(s);
return false;
};
@ -883,7 +883,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
data = this.cpu.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(this.cpu.id, data);
if (fShutdown) {
this.cpu.flags.fPowered = false;
this.cpu.flags.powered = false;
if (data === false) sState = null;
}
}
@ -891,13 +891,13 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
if (component.flags.fPowered) {
if (component.flags.powered) {
if (component.powerDown) {
data = component.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(component.id, data);
}
if (fShutdown) {
component.flags.fPowered = false;
component.flags.powered = false;
if (data === false) sState = null;
}
}
@ -950,7 +950,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
}
if (fShutdown) {
this.flags.fPowered = false;
this.flags.powered = false;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Power";
}
@ -1321,7 +1321,7 @@ Computer.prototype.storeServerState = function(sUserID, sState, fSync)
Computer.prototype.onPower = function()
{
if (!this.nPowerChange) {
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.wait(this.powerOn);
} else {
this.powerOff(false, true);
@ -1342,7 +1342,7 @@ Computer.prototype.onReset = function()
* I'm going to start with the presumption that it makes little sense for an "unpowered" computer to be "reset";
* ditto if the power state is currently being changed.
*/
if (!this.flags.fPowered || this.nPowerChange) return;
if (!this.flags.powered || this.nPowerChange) return;
/*
* If this is a "resumable" machine (and it's not using a predefined state), then we overload the reset
@ -1526,7 +1526,7 @@ Computer.init = function()
var computer = new Computer(parmsComputer, parmsMachine, true);
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onInit(" + computer.flags.fPowered + ")");
computer.printMessage("onInit(" + computer.flags.powered + ")");
}
/*
@ -1563,10 +1563,10 @@ Computer.show = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.fPowered + ")");
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.powered + ")");
}
if (computer.fInitialized && !computer.flags.fPowered) {
if (computer.fInitialized && !computer.flags.powered) {
/**
* Repower the computer, notifying every component to continue running as-is.
*/
@ -1612,10 +1612,10 @@ Computer.exit = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onExit(" + computer.flags.fPowered + ")");
computer.printMessage("onExit(" + computer.flags.powered + ")");
}
if (computer.flags.fPowered) {
if (computer.flags.powered) {
/**
* Power off the computer, giving every component an opportunity to save its state,
* but only if 'resume' has been set AND there is no valid resume path (because if a valid resume

View file

@ -98,14 +98,14 @@ function CPU(parmsCPU, nCyclesDefault)
/*
* We add a number of flags to the set initialized by Component
*/
this.flags.fRunning = false;
this.flags.fStarting = false;
this.flags.fAutoStart = parmsCPU['autoStart'];
this.flags.running = false;
this.flags.starting = false;
this.flags.autoStart = parmsCPU['autoStart'];
/*
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both)
*/
this.flags.fDisplayLiveRegs = false;
this.flags.displayLiveRegs = false;
/*
* Get checksum parameters, if any. runCPU() behavior is not affected until fChecksum
@ -116,7 +116,7 @@ function CPU(parmsCPU, nCyclesDefault)
* command ("x"); for example, "x cs int 5000" will set nCyclesChecksumInterval to 5000
* and call resetChecksum().
*/
this.flags.fChecksum = false;
this.flags.checksum = false;
this.aCounts.nChecksum = this.aCounts.nCyclesChecksumNext = 0;
this.aCounts.nCyclesChecksumStart = parmsCPU["csStart"];
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
@ -160,7 +160,7 @@ CPU.BUTTONS = ["power", "reset"];
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPU} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{
@ -189,7 +189,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
var sAutoStart = cmp.getMachineParm('autoStart');
if (sAutoStart != null) {
this.flags.fAutoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
this.flags.autoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
}
this.setReady();
@ -272,7 +272,7 @@ CPU.prototype.powerUp = function(data, fRepower)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = true;
* this.flags.powered = true;
*/
this.updateCPU();
return true;
@ -292,7 +292,7 @@ CPU.prototype.powerDown = function(fSave, fShutdown)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = false;
* this.flags.powered = false;
*/
return fSave? this.save() : true;
};
@ -308,7 +308,7 @@ CPU.prototype.autoStart = function()
/*
* Start running automatically on power-up, assuming there's no Debugger and no "Run" button
*/
if (this.flags.fAutoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/*
* Now we ALSO set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" context,
* a machine without focus is like a day without sunshine.
@ -327,7 +327,7 @@ CPU.prototype.autoStart = function()
*/
CPU.prototype.isPowered = function()
{
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.println(this.toString() + " not powered");
return false;
}
@ -342,7 +342,7 @@ CPU.prototype.isPowered = function()
*/
CPU.prototype.isRunning = function()
{
return this.flags.fRunning;
return this.flags.running;
};
/**
@ -373,8 +373,8 @@ CPU.prototype.resetChecksum = function()
if (this.aCounts.nCyclesChecksumStart === undefined) this.aCounts.nCyclesChecksumStart = 0;
if (this.aCounts.nCyclesChecksumInterval === undefined) this.aCounts.nCyclesChecksumInterval = -1;
if (this.aCounts.nCyclesChecksumStop === undefined) this.aCounts.nCyclesChecksumStop = -1;
this.flags.fChecksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
if (this.flags.fChecksum) {
this.flags.checksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
if (this.flags.checksum) {
this.aCounts.nChecksum = 0;
this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart - this.nTotalCycles;
/*
@ -399,7 +399,7 @@ CPU.prototype.resetChecksum = function()
*/
CPU.prototype.updateChecksum = function(nCycles)
{
if (this.flags.fChecksum) {
if (this.flags.checksum) {
/*
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
*/
@ -455,7 +455,7 @@ CPU.prototype.displayValue = function(sLabel, nValue, cch)
this.stopCPU();
}
var sVal;
if (!this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (!this.flags.running || this.flags.displayLiveRegs) {
sVal = str.toHex(nValue, cch);
} else {
sVal = "--------".substr(0, cch);
@ -501,7 +501,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
this.bindings[sBinding] = control;
control.onclick = function onClickRun() {
if (!cpu.cmp || !cpu.cmp.checkPower()) return;
if (!cpu.flags.fRunning)
if (!cpu.flags.running)
cpu.runCPU(true);
else
cpu.stopCPU(true);
@ -542,7 +542,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
CPU.prototype.setBurstCycles = function(nCycles)
{
if (this.flags.fRunning) {
if (this.flags.running) {
var nDelta = this.nStepCycles - nCycles;
/*
* NOTE: If nDelta is negative, we will actually be increasing nStepCycles and nBurstCycles.
@ -732,7 +732,7 @@ CPU.prototype.getSpeedCurrent = function()
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return ((this.flags.fRunning && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
return ((this.flags.running && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
};
/**
@ -962,7 +962,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
this.calcStartTime();
try {
do {
var nCyclesPerBurst = (this.flags.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
var nCyclesPerBurst = (this.flags.checksum? 1 : this.aCounts.nCyclesPerBurst);
/*
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
@ -999,7 +999,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
break;
}
} while (this.flags.fRunning);
} while (this.flags.running);
}
catch (e) {
this.stopCPU();
@ -1021,7 +1021,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
*/
CPU.prototype.startCPU = function(fUpdateFocus)
{
if (!this.flags.fRunning) {
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
@ -1030,8 +1030,8 @@ CPU.prototype.startCPU = function(fUpdateFocus)
*/
this.setSpeed();
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.flags.fRunning = true;
this.flags.fStarting = true;
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
@ -1074,13 +1074,13 @@ CPU.prototype.stopCPU = function(fComplete)
this.nStepCycles = 0;
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.flags.running) {
this.flags.running = false;
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
}
this.flags.fComplete = fComplete;
this.flags.complete = fComplete;
};
/**

View file

@ -84,7 +84,7 @@ function CPUState(parmsCPU)
* stepCPU() call, but it's good form to do so.
*/
this.resetCycles();
this.flags.fComplete = this.flags.fDebugCheck = false;
this.flags.complete = this.flags.debugCheck = false;
/*
* If there are no live registers to display, then updateStatus() can skip a bit....
@ -406,7 +406,7 @@ CPUState.prototype.initProcessor = function()
*/
CPUState.prototype.reset = function()
{
if (this.flags.fRunning) this.stopCPU();
if (this.flags.running) this.stopCPU();
this.resetRegs();
this.resetCycles();
this.clearError(); // clear any fatal error/exception that setError() may have flagged
@ -1276,7 +1276,7 @@ CPUState.prototype.updateReg = function(sReg, nValue, cch)
CPUState.prototype.updateStatus = function(fForce)
{
if (this.cLiveRegs) {
if (fForce || !this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
this.updateReg("A", this.regA);
this.updateReg("B", this.regB);
this.updateReg("C", this.regC);
@ -1333,12 +1333,12 @@ CPUState.prototype.stepCPU = function(nMinCycles)
* Debugger is single-stepping (even when performing multiple single-steps), fRunning is never set,
* so stopCPU() would have no effect as far as the Debugger is concerned.
*/
this.flags.fComplete = true;
this.flags.complete = true;
/*
* fDebugCheck is true if we need to "check" every instruction with the Debugger.
*/
var fDebugCheck = this.flags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
var fDebugCheck = this.flags.debugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
/*
* nDebugState is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
@ -1348,8 +1348,8 @@ CPUState.prototype.stepCPU = function(nMinCycles)
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have
* officially "started" now.
*/
var nDebugState = (!nMinCycles)? -1 : (this.flags.fStarting? 0 : 1);
this.flags.fStarting = false;
var nDebugState = (!nMinCycles)? -1 : (this.flags.starting? 0 : 1);
this.flags.starting = false;
/*
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
@ -1402,7 +1402,7 @@ CPUState.prototype.stepCPU = function(nMinCycles)
} while (this.nStepCycles > 0);
return (this.flags.fComplete? this.nBurstCycles - this.nStepCycles : (this.flags.fComplete === undefined? 0 : -1));
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
};
/**

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,12 @@
/**
* @define {string}
*/
var APPCLASS = "pc8080"; // this @define is the default application class (formerly APPCLASS) to use
var APPCLASS = "pc6502"; // this @define is the default application class (eg, "pcx86", "c1pjs")
/**
* @define {string}
*/
var APPNAME = "PC6502"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
/**
* @define {boolean}
@ -69,6 +74,26 @@ var BYTEARRAYS = false;
*/
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 (PCX86.DEBUG) ..." instead of "if (DEBUG) ...".
*/
var PC6502 = {
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.DEBUGGER = DEBUGGER;

View file

@ -35,6 +35,7 @@ if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Keys = require("../../shared/lib/keys");
var Messages = require("./messages");
var State = require("./state");
var CPU = require("./cpu");
@ -56,159 +57,6 @@ function Keyboard(parmsKbd)
Component.subclass(Keyboard);
/**
* Alphanumeric and other common (printable) ASCII codes.
*
* TODO: Determine what we can do to get ALL constants like these inlined (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}
*/
Keyboard.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 keys (eg, the LEFT arrow key), yet for some reason, browsers defined
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37). This conflict is
* discussed further in the definition of CLICKCODE below.
*
* @enum {number}
*/
Keyboard.KEYCODE = {
/* 0x08 */ BS: 8,
/* 0x09 */ TAB: 9,
/* 0x0A */ LF: 10,
/* 0x0D */ CR: 13,
/* 0x10 */ SHIFT: 16,
/* 0x11 */ CTRL: 17,
/* 0x12 */ ALT: 18,
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
/* 0x14 */ CAPS_LOCK: 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,
/* 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_INS: 96, // 0
/* 0x60 */ FF_BQUOTE: 96,
/* 0x61 */ NUM_END: 97, // 1
/* 0x62 */ NUM_DOWN: 98, // 2
/* 0x63 */ NUM_PGDN: 99, // 3
/* 0x64 */ NUM_LEFT: 100, // 4
/* 0x65 */ NUM_CENTER: 101, // 5
/* 0x66 */ NUM_RIGHT: 102, // 6
/* 0x67 */ NUM_HOME: 103, // 7
/* 0x68 */ NUM_UP: 104, // 8
/* 0x69 */ NUM_PGUP: 105, // 9
/* 0x6A */ NUM_MUL: 106,
/* 0x6B */ NUM_ADD: 107,
/* 0x6D */ NUM_SUB: 109,
/* 0x6E */ NUM_DEL: 110, // .
/* 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
};
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keyboard.STUPID_KEYCODES = {};
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SEMI] = Keyboard.ASCII[';']; // 186 -> 59
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.EQUALS] = Keyboard.ASCII['=']; // 187 -> 61
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.COMMA] = Keyboard.ASCII[',']; // 188 -> 44
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.DASH] = Keyboard.ASCII['-']; // 189 -> 45
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.PERIOD] = Keyboard.ASCII['.']; // 190 -> 46
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SLASH] = Keyboard.ASCII['/']; // 191 -> 47
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['`']; // 192 -> 96
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.LBRACK] = Keyboard.ASCII['[']; // 219 -> 91
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BSLASH] = Keyboard.ASCII['\\']; // 220 -> 92
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.RBRACK] = Keyboard.ASCII[']']; // 221 -> 93
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.QUOTE] = Keyboard.ASCII["'"]; // 222 -> 39
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.FF_DASH] = Keyboard.ASCII['-'];
/**
* Keyboard.init()
*

View file

@ -237,7 +237,7 @@ Memory.prototype = {
* @this {Memory}
* @param {Memory} mem
* @param {number} [type]
* @param {Debugger} [dbg]
* @param {Debugger6502} [dbg]
*/
clone: function(mem, type, dbg) {
/*
@ -485,7 +485,7 @@ Memory.prototype = {
* copyBreakpoints(dbg, mem)
*
* @this {Memory}
* @param {Debugger} [dbg]
* @param {Debugger6502} [dbg]
* @param {Memory} [mem] (outgoing Memory block to copy breakpoints from, if any)
*/
copyBreakpoints: function(dbg, mem) {
@ -807,18 +807,45 @@ Memory.prototype = {
/*
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 short handlers) are undefined.
*
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault];
Memory.afnNone = [
Memory.prototype.readNone,
Memory.prototype.writeNone,
Memory.prototype.readShortDefault,
Memory.prototype.writeShortDefault
];
*/
Memory.afnNone = [];
Memory.afnNone = [];
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory];
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked];
Memory.afnMemory = [
Memory.prototype.readByteMemory,
Memory.prototype.writeByteMemory,
Memory.prototype.readShortMemory,
Memory.prototype.writeShortMemory
];
Memory.afnChecked = [
Memory.prototype.readByteChecked,
Memory.prototype.writeByteChecked,
Memory.prototype.readShortChecked,
Memory.prototype.writeShortChecked
];
if (TYPEDARRAYS) {
Memory.afnArrayBE = [Memory.prototype.readByteBE, Memory.prototype.readShortBE, Memory.prototype.writeByteBE, Memory.prototype.writeShortBE];
Memory.afnArrayLE = [Memory.prototype.readByteLE, Memory.prototype.readShortLE, Memory.prototype.writeByteLE, Memory.prototype.writeShortLE];
Memory.afnArrayBE = [
Memory.prototype.readByteBE,
Memory.prototype.writeByteBE,
Memory.prototype.readShortBE,
Memory.prototype.writeShortBE
];
Memory.afnArrayLE = [
Memory.prototype.readByteLE,
Memory.prototype.writeByteLE,
Memory.prototype.readShortLE,
Memory.prototype.writeShortLE
];
}
if (NODE) module.exports = Memory;

View file

@ -87,7 +87,7 @@ Panel.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPUState} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -74,7 +74,7 @@ Component.subclass(RAM);
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPUState} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -133,7 +133,7 @@ Component.subclass(ROM);
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPUState} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
ROM.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -223,7 +223,7 @@ Video.COLORS = {
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPUState} cpu
* @param {Debugger} dbg
* @param {Debugger6502} dbg
*/
Video.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -76,35 +76,16 @@ function Bus8080(parmsBus, cpu, dbg)
this.nBusWidth = parmsBus['busWidth'] || 16;
/*
* Compute all Bus8080 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":
* Compute all Bus8080 memory block parameters, based on the width of the bus. The entire
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
* the bus width. The following table summarizes our simplistic calculations.
*
* Bus Width Block Shift Block Size
* --------- ----------- ----------
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
*
@ -115,7 +96,9 @@ function Bus8080(parmsBus, cpu, dbg)
*/
this.addrTotal = Math.pow(2, this.nBusWidth);
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
this.nBlockShift = (this.nBusWidth >> 1) + 2;
if (this.nBlockShift < 10) this.nBlockShift = 10;
if (this.nBlockShift > 15) this.nBlockShift = 15;
this.nBlockSize = 1 << this.nBlockShift;
this.nBlockLen = this.nBlockSize >> 2;
this.nBlockLimit = this.nBlockSize - 1;
@ -173,38 +156,6 @@ Bus8080.ERROR = {
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
* }}
*/
Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* Bus8080Info 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 Bus8080Info;
/**
* initMemory()
*
@ -361,13 +312,49 @@ Bus8080.prototype.cleanMemory = function(addr, size)
return fClean;
};
/*
* Data types used by scanMemory()
*/
/**
* @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
* }}
*/
Bus8080.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo8080 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 BusInfo8080;
/**
* 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)

View file

@ -110,7 +110,7 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
Component.call(this, "Computer", parmsComputer, Computer8080, Messages8080.COMPUTER);
this.flags.fPowered = false;
this.flags.powered = false;
this.setMachineParms(parmsMachine);
@ -189,7 +189,7 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
}
}
this.println(PC8080.APPNAME + " v" + PC8080.APPVERSION + "\n" + COPYRIGHT + "\n" + LICENSE);
this.println(PC8080.APPNAME + " v" + (XMLVERSION || PC8080.APPVERSION) + "\n" + COPYRIGHT + "\n" + LICENSE);
if (DEBUG && this.messageEnabled()) this.printMessage("TYPEDARRAYS: " + TYPEDARRAYS);
@ -626,9 +626,9 @@ Computer8080.prototype.powerOn = function(resume)
*/
Computer8080.prototype.powerRestore = function(component, stateComputer, fRepower, fRestore)
{
if (!component.flags.fPowered) {
if (!component.flags.powered) {
component.flags.fPowered = true;
component.flags.powered = true;
if (component.powerUp) {
@ -731,12 +731,12 @@ Computer8080.prototype.donePowerOn = function(aParms)
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
if (DEBUG && this.flags.fPowered && this.messageEnabled()) {
if (DEBUG && this.flags.powered && this.messageEnabled()) {
this.printMessage("Computer8080.donePowerOn(): redundant");
}
this.fInitialized = true;
this.flags.fPowered = true;
this.flags.powered = true;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Shutdown";
@ -777,22 +777,22 @@ Computer8080.prototype.donePowerOn = function(aParms)
*/
Computer8080.prototype.checkPower = function()
{
if (this.flags.fPowered) return true;
if (this.flags.powered) return true;
var component = null, iComponent;
var aComponents = Component.getComponents(this.id);
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fReady) break;
if (component !== this && !component.flags.ready) break;
}
if (iComponent == aComponents.length) {
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fPowered) break;
if (component !== this && !component.flags.powered) break;
}
}
if (iComponent == aComponents.length) component = this;
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.fReady? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.ready? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
web.alertUser(s);
return false;
};
@ -874,7 +874,7 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
data = this.cpu.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(this.cpu.id, data);
if (fShutdown) {
this.cpu.flags.fPowered = false;
this.cpu.flags.powered = false;
if (data === false) sState = null;
}
}
@ -882,13 +882,13 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
if (component.flags.fPowered) {
if (component.flags.powered) {
if (component.powerDown) {
data = component.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(component.id, data);
}
if (fShutdown) {
component.flags.fPowered = false;
component.flags.powered = false;
if (data === false) sState = null;
}
}
@ -941,7 +941,7 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
}
if (fShutdown) {
this.flags.fPowered = false;
this.flags.powered = false;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Power";
}
@ -1312,7 +1312,7 @@ Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
Computer8080.prototype.onPower = function()
{
if (!this.nPowerChange) {
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.wait(this.powerOn);
} else {
this.powerOff(false, true);
@ -1333,7 +1333,7 @@ Computer8080.prototype.onReset = function()
* I'm going to start with the presumption that it makes little sense for an "unpowered" computer to be "reset";
* ditto if the power state is currently being changed.
*/
if (!this.flags.fPowered || this.nPowerChange) return;
if (!this.flags.powered || this.nPowerChange) return;
/*
* If this is a "resumable" machine (and it's not using a predefined state), then we overload the reset
@ -1521,7 +1521,7 @@ Computer8080.init = function()
var computer = new Computer8080(parmsComputer, parmsMachine, true);
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onInit(" + computer.flags.fPowered + ")");
computer.printMessage("onInit(" + computer.flags.powered + ")");
}
/*
@ -1558,7 +1558,7 @@ Computer8080.show = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.fPowered + ")");
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.powered + ")");
}
/*
@ -1566,7 +1566,7 @@ Computer8080.show = function()
* AFTER the the initial 'onload' event, and at that point in time, fInitialized will not be set yet.
* So, practically speaking, the first show() callback isn't all that useful.
*/
if (computer.fInitialized && !computer.flags.fPowered) {
if (computer.fInitialized && !computer.flags.powered) {
/**
* Repower the computer, notifying every component to continue running as-is.
*/
@ -1612,10 +1612,10 @@ Computer8080.exit = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onExit(" + computer.flags.fPowered + ")");
computer.printMessage("onExit(" + computer.flags.powered + ")");
}
if (computer.flags.fPowered) {
if (computer.flags.powered) {
/**
* Power off the computer, giving every component an opportunity to save its state,
* but only if 'resume' has been set AND there is no valid resume path (because if a valid resume

View file

@ -79,32 +79,31 @@ function CPU8080(parmsCPU, nCyclesDefault)
var nMultiplier = parmsCPU['multiplier'] || 1;
this.counts = {};
this.counts.nCyclesPerSecond = nCycles;
this.nCyclesPerSecond = nCycles;
/*
* nCyclesMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
* the constants (SPEED_SLOW, SPEED_FAST and SPEED_MAX). The UI simply doubles the multiplier
* until we've exceeded the host's speed limit and then starts the multiplier over at 1.
*/
this.counts.nCyclesMultiplier = nMultiplier;
this.counts.mhzDefault = Math.round(this.counts.nCyclesPerSecond / 10000) / 100;
this.nCyclesMultiplier = nMultiplier;
this.mhzDefault = Math.round(this.nCyclesPerSecond / 10000) / 100;
/*
* TODO: Take care of this with an initial setSpeed() call instead?
*/
this.counts.mhzTarget = this.counts.mhzDefault * this.counts.nCyclesMultiplier;
this.mhzTarget = this.mhzDefault * this.nCyclesMultiplier;
/*
* We add a number of flags to the set initialized by Component
*/
this.flags.fRunning = false;
this.flags.fStarting = false;
this.flags.fAutoStart = parmsCPU['autoStart'];
this.flags.running = false;
this.flags.starting = false;
this.flags.autoStart = parmsCPU['autoStart'];
/*
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both)
*/
this.flags.fDisplayLiveRegs = false;
this.flags.displayLiveRegs = false;
/*
* Get checksum parameters, if any. runCPU() behavior is not affected until fChecksum
@ -115,11 +114,11 @@ function CPU8080(parmsCPU, nCyclesDefault)
* command ("x"); for example, "x cs int 5000" will set nCyclesChecksumInterval to 5000
* and call resetChecksum().
*/
this.flags.fChecksum = false;
this.counts.nChecksum = this.counts.nCyclesChecksumNext = 0;
this.counts.nCyclesChecksumStart = parmsCPU["csStart"];
this.counts.nCyclesChecksumInterval = parmsCPU["csInterval"];
this.counts.nCyclesChecksumStop = parmsCPU["csStop"];
this.flags.checksum = false;
this.nChecksum = this.nCyclesChecksumNext = 0;
this.nCyclesChecksumStart = parmsCPU["csStart"];
this.nCyclesChecksumInterval = parmsCPU["csInterval"];
this.nCyclesChecksumStop = parmsCPU["csStop"];
/*
* Array of countdown timers managed by addTimer() and setTimer().
@ -140,12 +139,12 @@ Component.subclass(CPU8080);
* calcCycles(), which uses the nCyclesPerSecond passed to the constructor as a starting
* point and computes the following variables:
*
* this.counts.nCyclesPerYield: (this.counts.nCyclesPerSecond / CPU8080.YIELDS_PER_SECOND)
* this.nCyclesPerYield: (this.nCyclesPerSecond / CPU8080.YIELDS_PER_SECOND)
*
* The above variables are also multiplied by any cycle multiplier in effect, via setSpeed(),
* and then they're used to initialize another set of variables for each runCPU() iteration:
*
* this.counts.nCyclesNextYield: this.counts.nCyclesPerYield
* this.nCyclesNextYield: this.nCyclesPerYield
*/
CPU8080.YIELDS_PER_SECOND = 30; // just a gut feeling for the MINIMUM number of yields per second
CPU8080.YIELDS_PER_STATUS = 15; // every 15 yields (ie, twice per second), perform CPU status updates
@ -182,7 +181,7 @@ CPU8080.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
var sAutoStart = cmp.getMachineParm('autoStart');
if (sAutoStart != null) {
this.flags.fAutoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
this.flags.autoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
}
this.setReady();
@ -200,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}
@ -213,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
@ -264,7 +263,7 @@ CPU8080.prototype.powerUp = function(data, fRepower)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = true;
* this.flags.powered = true;
*/
this.updateCPU();
return true;
@ -284,7 +283,7 @@ CPU8080.prototype.powerDown = function(fSave, fShutdown)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = false;
* this.flags.powered = false;
*/
return fSave? this.save() : true;
};
@ -300,7 +299,7 @@ CPU8080.prototype.autoStart = function()
/*
* Start running automatically on power-up, assuming there's no Debugger and no "Run" button
*/
if (this.flags.fAutoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/*
* Now we ALSO set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" context,
* a machine without focus is like a day without sunshine.
@ -319,7 +318,7 @@ CPU8080.prototype.autoStart = function()
*/
CPU8080.prototype.isPowered = function()
{
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.println(this.toString() + " not powered");
return false;
}
@ -334,13 +333,13 @@ CPU8080.prototype.isPowered = function()
*/
CPU8080.prototype.isRunning = function()
{
return this.flags.fRunning;
return this.flags.running;
};
/**
* 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)
@ -362,16 +361,16 @@ CPU8080.prototype.getChecksum = function()
*/
CPU8080.prototype.resetChecksum = function()
{
if (this.counts.nCyclesChecksumStart === undefined) this.counts.nCyclesChecksumStart = 0;
if (this.counts.nCyclesChecksumInterval === undefined) this.counts.nCyclesChecksumInterval = -1;
if (this.counts.nCyclesChecksumStop === undefined) this.counts.nCyclesChecksumStop = -1;
this.flags.fChecksum = (this.counts.nCyclesChecksumStart >= 0 && this.counts.nCyclesChecksumInterval > 0);
if (this.flags.fChecksum) {
this.counts.nChecksum = 0;
this.counts.nCyclesChecksumNext = this.counts.nCyclesChecksumStart - this.nTotalCycles;
if (this.nCyclesChecksumStart === undefined) this.nCyclesChecksumStart = 0;
if (this.nCyclesChecksumInterval === undefined) this.nCyclesChecksumInterval = -1;
if (this.nCyclesChecksumStop === undefined) this.nCyclesChecksumStop = -1;
this.flags.checksum = (this.nCyclesChecksumStart >= 0 && this.nCyclesChecksumInterval > 0);
if (this.flags.checksum) {
this.nChecksum = 0;
this.nCyclesChecksumNext = this.nCyclesChecksumStart - this.nTotalCycles;
/*
* this.counts.nCyclesChecksumNext = this.counts.nCyclesChecksumStart + this.counts.nCyclesChecksumInterval -
* (this.nTotalCycles % this.counts.nCyclesChecksumInterval);
* this.nCyclesChecksumNext = this.nCyclesChecksumStart + this.nCyclesChecksumInterval -
* (this.nTotalCycles % this.nCyclesChecksumInterval);
*/
return true;
}
@ -391,20 +390,20 @@ CPU8080.prototype.resetChecksum = function()
*/
CPU8080.prototype.updateChecksum = function(nCycles)
{
if (this.flags.fChecksum) {
if (this.flags.checksum) {
/*
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
*/
var fDisplay = false;
this.counts.nChecksum = (this.counts.nChecksum + this.getChecksum())|0;
this.counts.nCyclesChecksumNext -= nCycles;
if (this.counts.nCyclesChecksumNext <= 0) {
this.counts.nCyclesChecksumNext += this.counts.nCyclesChecksumInterval;
this.nChecksum = (this.nChecksum + this.getChecksum())|0;
this.nCyclesChecksumNext -= nCycles;
if (this.nCyclesChecksumNext <= 0) {
this.nCyclesChecksumNext += this.nCyclesChecksumInterval;
fDisplay = true;
}
if (this.counts.nCyclesChecksumStop >= 0) {
if (this.counts.nCyclesChecksumStop <= this.getCycles()) {
this.counts.nCyclesChecksumInterval = this.counts.nCyclesChecksumStop = -1;
if (this.nCyclesChecksumStop >= 0) {
if (this.nCyclesChecksumStop <= this.getCycles()) {
this.nCyclesChecksumInterval = this.nCyclesChecksumStop = -1;
this.resetChecksum();
this.stopCPU();
fDisplay = true;
@ -425,7 +424,7 @@ CPU8080.prototype.updateChecksum = function(nCycles)
*/
CPU8080.prototype.displayChecksum = function()
{
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.counts.nChecksum));
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.nChecksum));
};
/**
@ -447,7 +446,7 @@ CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
this.stopCPU();
}
var sVal;
if (!this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (!this.flags.running || this.flags.displayLiveRegs) {
sVal = str.toHex(nValue, cch);
} else {
sVal = "--------".substr(0, cch);
@ -498,7 +497,7 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* control is visible, then the computer is probably sufficiently visible as well; the problem
* with setting fUpdateFocus to true is that it can jerk the web page around in annoying ways.
*/
if (!cpu.flags.fRunning)
if (!cpu.flags.running)
cpu.runCPU();
else
cpu.stopCPU();
@ -514,7 +513,7 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "setSpeed":
this.bindings[sBinding] = control;
control.onclick = function onClickSetSpeed() {
cpu.setSpeed(cpu.counts.nCyclesMultiplier << 1, true);
cpu.setSpeed(cpu.nCyclesMultiplier << 1, true);
};
control.textContent = this.getSpeedTarget();
fBound = true;
@ -542,7 +541,7 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
CPU8080.prototype.setBurstCycles = function(nCycles)
{
if (this.flags.fRunning) {
if (this.flags.running) {
var nDelta = this.nStepCycles - nCycles;
/*
* NOTE: If nDelta is negative, we will actually be increasing nStepCycles and nBurstCycles.
@ -597,21 +596,21 @@ CPU8080.prototype.calcCycles = function(fRecalc)
*/
var vMultiplier = 1;
if (fRecalc) {
if (this.counts.nCyclesMultiplier > 1 && this.counts.mhz) {
vMultiplier = (this.counts.mhz / this.counts.mhzDefault);
if (this.nCyclesMultiplier > 1 && this.mhz) {
vMultiplier = (this.mhz / this.mhzDefault);
}
}
this.counts.msPerYield = Math.round(1000 / CPU8080.YIELDS_PER_SECOND);
this.counts.nCyclesPerYield = Math.floor(this.counts.nCyclesPerSecond / CPU8080.YIELDS_PER_SECOND * vMultiplier);
this.msPerYield = Math.round(1000 / CPU8080.YIELDS_PER_SECOND);
this.nCyclesPerYield = Math.floor(this.nCyclesPerSecond / CPU8080.YIELDS_PER_SECOND * vMultiplier);
/*
* And initialize "next" yield values to the "per" values.
*/
if (!fRecalc) {
this.counts.nCyclesNextYield = this.counts.nCyclesPerYield;
this.nCyclesNextYield = this.nCyclesPerYield;
}
this.counts.nCyclesRecalc = 0;
this.nCyclesRecalc = 0;
};
/**
@ -634,11 +633,11 @@ CPU8080.prototype.calcCycles = function(fRecalc)
CPU8080.prototype.getCycles = function(fScaled)
{
var nCycles = this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles;
if (fScaled && this.counts.nCyclesMultiplier > 1 && this.counts.mhz > this.counts.mhzDefault) {
if (fScaled && this.nCyclesMultiplier > 1 && this.mhz > this.mhzDefault) {
/*
* We could scale the current cycle count by the current effective speed (this.counts.mhz); eg:
* We could scale the current cycle count by the current effective speed (this.mhz); eg:
*
* nCycles = Math.round(nCycles / (this.counts.mhz / this.counts.mhzDefault));
* nCycles = Math.round(nCycles / (this.mhz / this.mhzDefault));
*
* but that speed will fluctuate somewhat: large fluctuations at first, but increasingly smaller
* fluctuations after each burst of instructions that runCPU() executes.
@ -653,7 +652,7 @@ CPU8080.prototype.getCycles = function(fScaled)
* interface allows any value, as does the CPU "multiplier" parmsCPU property (from the machine's
* XML file).
*/
nCycles = Math.round(nCycles / this.counts.nCyclesMultiplier);
nCycles = Math.round(nCycles / this.nCyclesMultiplier);
}
return nCycles;
};
@ -668,7 +667,7 @@ CPU8080.prototype.getCycles = function(fScaled)
*/
CPU8080.prototype.getCyclesPerSecond = function()
{
return this.counts.nCyclesPerSecond;
return this.nCyclesPerSecond;
};
/**
@ -682,8 +681,8 @@ CPU8080.prototype.getCyclesPerSecond = function()
*/
CPU8080.prototype.resetCycles = function()
{
this.counts.mhz = 0;
this.counts.nYieldsSinceStatusUpdate = 0;
this.mhz = 0;
this.nYieldsSinceStatusUpdate = 0;
this.nTotalCycles = this.nRunCycles = this.nBurstCycles = this.nStepCycles = 0;
this.resetChecksum();
this.setSpeed(1);
@ -697,7 +696,7 @@ CPU8080.prototype.resetCycles = function()
*/
CPU8080.prototype.getSpeed = function()
{
return this.counts.nCyclesMultiplier;
return this.nCyclesMultiplier;
};
/**
@ -711,7 +710,7 @@ CPU8080.prototype.getSpeedCurrent = function()
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return ((this.flags.fRunning && this.counts.mhz)? (this.counts.mhz.toFixed(2) + "Mhz") : "Stopped");
return ((this.flags.running && this.mhz)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
};
/**
@ -725,7 +724,7 @@ CPU8080.prototype.getSpeedTarget = function()
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return this.counts.mhzTarget.toFixed(2) + "Mhz";
return this.mhzTarget.toFixed(2) + "Mhz";
};
/**
@ -749,15 +748,15 @@ CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
/*
* If we haven't reached 80% (0.8) of the current target speed, revert to a multiplier of one (1).
*/
if (this.counts.mhz / this.counts.mhzTarget < 0.8) {
if (this.mhz / this.mhzTarget < 0.8) {
nMultiplier = 1;
} else {
fSuccess = true;
}
this.counts.nCyclesMultiplier = nMultiplier;
var mhz = this.counts.mhzDefault * this.counts.nCyclesMultiplier;
if (this.counts.mhzTarget != mhz) {
this.counts.mhzTarget = mhz;
this.nCyclesMultiplier = nMultiplier;
var mhz = this.mhzDefault * this.nCyclesMultiplier;
if (this.mhzTarget != mhz) {
this.mhzTarget = mhz;
var sSpeed = this.getSpeedTarget();
var controlSpeed = this.bindings["setSpeed"];
if (controlSpeed) controlSpeed.textContent = sSpeed;
@ -767,8 +766,8 @@ CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
}
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
this.counts.msStartRun = usr.getTime();
this.counts.msEndThisRun = 0;
this.msStartRun = usr.getTime();
this.msEndThisRun = 0;
this.calcCycles();
return fSuccess;
};
@ -783,7 +782,7 @@ CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
CPU8080.prototype.calcSpeed = function(nCycles, msElapsed)
{
if (msElapsed) {
this.counts.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
this.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
if (msElapsed >= 86400000) {
this.nTotalCycles = 0;
this.setSpeed(); // reset all counters once per day so that we never have to worry about overflow
@ -798,11 +797,11 @@ CPU8080.prototype.calcSpeed = function(nCycles, msElapsed)
*/
CPU8080.prototype.calcStartTime = function()
{
if (this.counts.nCyclesRecalc >= this.counts.nCyclesPerSecond) {
if (this.nCyclesRecalc >= this.nCyclesPerSecond) {
this.calcCycles(true);
}
this.counts.nCyclesThisRun = 0;
this.counts.msStartThisRun = usr.getTime();
this.nCyclesThisRun = 0;
this.msStartThisRun = usr.getTime();
/*
* Try to detect situations where the browser may have throttled us, such as when the user switches
@ -829,19 +828,19 @@ CPU8080.prototype.calcStartTime = function()
* to hit its target speed, since you would expect any instruction that displays a message to be an
* EXTREMELY slow instruction.
*/
if (this.counts.msEndThisRun) {
var msDelta = this.counts.msStartThisRun - this.counts.msEndThisRun;
if (msDelta > this.counts.msPerYield) {
if (this.msEndThisRun) {
var msDelta = this.msStartThisRun - this.msEndThisRun;
if (msDelta > this.msPerYield) {
if (MAXDEBUG) this.println("large time delay: " + msDelta + "ms");
this.counts.msStartRun += msDelta;
this.msStartRun += msDelta;
/*
* Bumping msStartRun forward should NEVER cause it to exceed msStartThisRun; however, just
* in case, I make absolutely sure it cannot happen, since doing so could result in negative
* speed calculations.
*/
this.assert(this.counts.msStartRun <= this.counts.msStartThisRun);
if (this.counts.msStartRun > this.counts.msStartThisRun) {
this.counts.msStartRun = this.counts.msStartThisRun;
this.assert(this.msStartRun <= this.msStartThisRun);
if (this.msStartRun > this.msStartThisRun) {
this.msStartRun = this.msStartThisRun;
}
}
}
@ -855,47 +854,55 @@ CPU8080.prototype.calcStartTime = function()
*/
CPU8080.prototype.calcRemainingTime = function()
{
this.counts.msEndThisRun = usr.getTime();
this.msEndThisRun = usr.getTime();
var msYield = this.counts.msPerYield;
if (this.counts.nCyclesThisRun) {
var msYield = this.msPerYield;
if (this.nCyclesThisRun) {
/*
* Normally, we would assume we executed a full quota of work over msPerYield, but since the CPU
* now has the option of calling yieldCPU(), that might not be true. If nCyclesThisRun is correct, then
* the ratio of nCyclesThisRun/nCyclesPerYield should represent the percentage of work we performed,
* and so applying that percentage to msPerYield should give us a better estimate of work vs. time.
*/
msYield = Math.round(msYield * this.counts.nCyclesThisRun / this.counts.nCyclesPerYield);
msYield = Math.round(msYield * this.nCyclesThisRun / this.nCyclesPerYield);
}
var msElapsedThisRun = this.counts.msEndThisRun - this.counts.msStartThisRun;
var msElapsedThisRun = this.msEndThisRun - this.msStartThisRun;
var msRemainsThisRun = msYield - msElapsedThisRun;
/*
* We could pass only "this run" results to calcSpeed():
*
* nCycles = this.counts.nCyclesThisRun;
* nCycles = this.nCyclesThisRun;
* msElapsed = msElapsedThisRun;
*
* but it seems preferable to use longer time periods and hopefully get a more accurate speed.
*
* Also, if msRemainsThisRun >= 0 && this.counts.nCyclesMultiplier == 1, we could pass these results instead:
* Also, if msRemainsThisRun >= 0 && this.nCyclesMultiplier == 1, we could pass these results instead:
*
* nCycles = this.counts.nCyclesThisRun;
* msElapsed = this.counts.msPerYield;
* nCycles = this.nCyclesThisRun;
* msElapsed = this.msPerYield;
*
* to insure that we display a smooth, constant N Mhz. But for now, I prefer seeing any fluctuations.
*/
var nCycles = this.nRunCycles;
var msElapsed = this.counts.msEndThisRun - this.counts.msStartRun;
var msElapsed = this.msEndThisRun - this.msStartRun;
if (MAXDEBUG && msRemainsThisRun < 0 && this.counts.nCyclesMultiplier > 1) {
if (MAXDEBUG && msRemainsThisRun < 0 && this.nCyclesMultiplier > 1) {
this.println("warning: updates @" + msElapsedThisRun + "ms (prefer " + Math.round(msYield) + "ms)");
}
this.calcSpeed(nCycles, msElapsed);
if (msRemainsThisRun < 0 || this.counts.mhz < this.counts.mhzTarget) {
if (msRemainsThisRun < 0 || this.mhz < this.mhzTarget) {
/*
* Try "throwing out" the effects of large anomalies, by moving the overall run start time up;
* ordinarily, this should only happen when the someone is using an external Debugger or some other
* tool or feature that is interfering with our overall execution.
*/
if (msRemainsThisRun < -1000) {
this.msStartRun -= msRemainsThisRun;
}
/*
* If the last burst took MORE time than we allotted (ie, it's taking more than 1 second to simulate
* nCyclesPerSecond), all we can do is yield for as little time as possible (ie, 0ms) and hope that the
@ -908,13 +915,13 @@ CPU8080.prototype.calcRemainingTime = function()
* Last but not least, update nCyclesRecalc, so that when runCPU() starts up again and calls calcStartTime(),
* it'll be ready to decide if calcCycles() should be called again.
*/
this.counts.nCyclesRecalc += this.counts.nCyclesThisRun;
this.nCyclesRecalc += this.nCyclesThisRun;
if (DEBUG && this.messageEnabled(Messages8080.LOG) && msRemainsThisRun) {
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.counts.msEndThisRun + "ms");
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.msEndThisRun + "ms");
}
this.counts.msEndThisRun += msRemainsThisRun;
this.msEndThisRun += msRemainsThisRun;
return msRemainsThisRun;
};
@ -943,7 +950,7 @@ CPU8080.prototype.addTimer = function(callBack)
};
/**
* setTimer(iTimer, ms)
* setTimer(iTimer, ms, fReset)
*
* Using the timer index from a previous addTimer() call, this sets that timer to fire after the
* specified number of milliseconds.
@ -960,14 +967,17 @@ CPU8080.prototype.addTimer = function(callBack)
* @this {CPU8080}
* @param {number} iTimer
* @param {number} ms (converted into a cycle countdown internally)
* @param {boolean} [fReset] (true if the timer should be reset even if already armed)
* @return {number} (number of cycles used to arm timer, or -1 if error)
*/
CPU8080.prototype.setTimer = function(iTimer, ms)
CPU8080.prototype.setTimer = function(iTimer, ms, fReset)
{
var nCycles = -1;
if (iTimer >= 0 && iTimer < this.aTimers.length) {
nCycles = this.getMSCycles(ms);
this.aTimers[iTimer][0] = nCycles;
if (fReset || this.aTimers[iTimer][0] < 0) {
nCycles = this.getMSCycles(ms);
this.aTimers[iTimer][0] = nCycles;
}
}
return nCycles;
};
@ -981,7 +991,7 @@ CPU8080.prototype.setTimer = function(iTimer, ms)
*/
CPU8080.prototype.getMSCycles = function(ms)
{
return (this.counts.nCyclesPerSecond * this.counts.nCyclesMultiplier) / 1000 * ms;
return (this.nCyclesPerSecond * this.nCyclesMultiplier) / 1000 * ms;
};
/**
@ -1028,6 +1038,17 @@ CPU8080.prototype.updateTimers = function(nCycles)
}
};
/**
* endBurst()
*
* @this {CPU8080}
*/
CPU8080.prototype.endBurst = function()
{
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
};
/**
* runCPU(fUpdateFocus)
*
@ -1057,7 +1078,7 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
* be as HIGH as nCyclesPerYield, but it may be significantly less. getBurstCycles() will adjust
* nCyclesPerBurst downward if any CPU timers need to fire during the next burst.
*/
var nCyclesPerBurst = this.getBurstCycles(this.flags.fChecksum? 1 : this.counts.nCyclesPerYield);
var nCyclesPerBurst = this.getBurstCycles(this.flags.checksum? 1 : this.nCyclesPerYield);
/*
* Execute the burst.
@ -1077,21 +1098,21 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
*/
this.counts.nCyclesThisRun += nCycles;
this.nCyclesThisRun += nCycles;
this.nRunCycles += nCycles;
this.addCycles(0, true);
this.updateChecksum(nCycles);
this.counts.nCyclesNextYield -= nCycles;
if (this.counts.nCyclesNextYield <= 0) {
this.counts.nCyclesNextYield += this.counts.nCyclesPerYield;
if (++this.counts.nYieldsSinceStatusUpdate >= CPU8080.YIELDS_PER_STATUS) {
this.nCyclesNextYield -= nCycles;
if (this.nCyclesNextYield <= 0) {
this.nCyclesNextYield += this.nCyclesPerYield;
if (++this.nYieldsSinceStatusUpdate >= CPU8080.YIELDS_PER_STATUS) {
if (this.cmp) this.cmp.updateStatus();
this.counts.nYieldsSinceStatusUpdate = 0;
this.nYieldsSinceStatusUpdate = 0;
}
break;
}
} while (this.flags.fRunning);
} while (this.flags.running);
}
catch (e) {
this.stopCPU();
@ -1114,7 +1135,7 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
*/
CPU8080.prototype.startCPU = function(fUpdateFocus)
{
if (!this.flags.fRunning) {
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
@ -1122,9 +1143,9 @@ CPU8080.prototype.startCPU = function(fUpdateFocus)
* threshold counter.
*/
this.setSpeed();
if (this.cmp) this.cmp.start(this.counts.msStartRun, this.getCycles());
this.flags.fRunning = true;
this.flags.fStarting = true;
if (this.cmp) this.cmp.start(this.msStartRun, this.getCycles());
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
@ -1138,7 +1159,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)
@ -1163,17 +1184,16 @@ CPU8080.prototype.stepCPU = function(nMinCycles)
CPU8080.prototype.stopCPU = function(fComplete)
{
this.isBusy(true);
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.flags.running) {
this.flags.running = false;
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
}
this.flags.fComplete = fComplete;
this.flags.complete = fComplete;
};
/**
@ -1205,9 +1225,8 @@ CPU8080.prototype.updateCPU = function(fForce)
*/
CPU8080.prototype.yieldCPU = function()
{
this.counts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0; // this will break us out of stepCPU()
this.endBurst(); // this will break us out of stepCPU()
this.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
// if (DEBUG) this.nSnapCycles = this.nBurstCycles;
/*
* The Debugger calls yieldCPU() after every message() to ensure browser responsiveness, but it looks

View file

@ -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

View file

@ -85,7 +85,7 @@ function CPUState8080(parmsCPU)
* stepCPU() call, but it's good form to do so.
*/
this.resetCycles();
this.flags.fComplete = this.flags.fDebugCheck = false;
this.flags.complete = this.flags.debugCheck = false;
/*
* If there are no live registers to display, then updateStatus() can skip a bit....
@ -143,7 +143,7 @@ CPUState8080.prototype.initProcessor = function()
*/
CPUState8080.prototype.reset = function()
{
if (this.flags.fRunning) this.stopCPU();
if (this.flags.running) this.stopCPU();
this.resetRegs();
this.resetCycles();
this.clearError(); // clear any fatal error/exception that setError() may have flagged
@ -961,8 +961,7 @@ CPUState8080.prototype.checkINTR = function()
* current burst AND that it should not execute any more instructions until checkINTR() indicates
* that a hardware interrupt has been requested.
*/
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
this.endBurst();
return false;
}
return true;
@ -994,8 +993,7 @@ CPUState8080.prototype.clearINTR = function(nLevel)
CPUState8080.prototype.requestHALT = function()
{
this.intFlags |= CPUDef8080.INTFLAG.HALT;
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
this.endBurst();
};
/**
@ -1015,8 +1013,7 @@ CPUState8080.prototype.requestINTR = function(nLevel)
{
this.intFlags |= (1 << nLevel);
if (this.getIF()) {
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
this.endBurst();
}
};
@ -1040,11 +1037,11 @@ CPUState8080.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 {CPUState8080}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
@ -1052,7 +1049,7 @@ CPUState8080.prototype.updateReg = function(sReg, nValue, cch)
CPUState8080.prototype.updateStatus = function(fForce)
{
if (this.cLiveRegs) {
if (fForce || !this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
this.updateReg("A", this.regA);
this.updateReg("B", this.regB);
this.updateReg("C", this.regC);
@ -1088,9 +1085,6 @@ CPUState8080.prototype.updateStatus = function(fForce)
* they're all one continuous stream of instructions that can be stepped or run at will. Moreover,
* stepping vs. running should never change the behavior of the simulation.
*
* 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 {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
@ -1109,12 +1103,12 @@ CPUState8080.prototype.stepCPU = function(nMinCycles)
* Debugger is single-stepping (even when performing multiple single-steps), fRunning is never set,
* so stopCPU() would have no effect as far as the Debugger is concerned.
*/
this.flags.fComplete = true;
this.flags.complete = true;
/*
* fDebugCheck is true if we need to "check" every instruction with the Debugger.
*/
var fDebugCheck = this.flags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
var fDebugCheck = this.flags.debugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
/*
* nDebugState is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
@ -1124,8 +1118,8 @@ CPUState8080.prototype.stepCPU = function(nMinCycles)
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have
* officially "started" now.
*/
var nDebugState = (!nMinCycles)? -1 : (this.flags.fStarting? 0 : 1);
this.flags.fStarting = false;
var nDebugState = (!nMinCycles)? -1 : (this.flags.starting? 0 : 1);
this.flags.starting = false;
/*
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
@ -1151,7 +1145,7 @@ CPUState8080.prototype.stepCPU = function(nMinCycles)
} while (this.nStepCycles > 0);
}
return (this.flags.fComplete? this.nBurstCycles - this.nStepCycles : (this.flags.fComplete === undefined? 0 : -1));
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
};
/**

View file

@ -37,11 +37,12 @@ if (DEBUGGER) {
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Debugger = require("../../shared/lib/debugger");
var Keys = require("../../shared/lib/keys");
var State = require("../../shared/lib/state");
var PC8080 = require("./defines");
var CPUDef8080 = require("./cpudef");
var CPU8080 = require("./cpu");
var Keyboard8080= require("./keyboard");
var Messages8080= require("./messages");
var Memory8080 = require("./memory");
}
@ -60,7 +61,7 @@ if (DEBUGGER) {
* fTemporary:(boolean|undefined),
* sCmd:(string|undefined),
* aCmds:(Array.<string>|undefined)
* }}
* }} DbgAddr8080
*/
var DbgAddr8080;
@ -68,7 +69,7 @@ var DbgAddr8080;
* Debugger8080(parmsDbg)
*
* @constructor
* @extends Component
* @extends Debugger
* @param {Object} parmsDbg
*
* The Debugger8080 component supports the following optional (parmsDbg) properties:
@ -85,21 +86,10 @@ function Debugger8080(parmsDbg)
{
if (DEBUGGER) {
Component.call(this, "Debugger", parmsDbg, Debugger8080);
Debugger.call(this, parmsDbg);
this.style = Debugger8080.STYLE_8080;
/*
* These keep track of instruction activity, but only when tracing or when Debugger8080 checks
* have been enabled (eg, one or more breakpoints have been set).
*
* They are zeroed by the reset() notification handler. cInstructions is advanced by
* stepCPU() and checkInstruction() calls. nCycles is updated by every stepCPU() or stop()
* call and simply represents the number of cycles performed by the last run of instructions.
*/
this.nCycles = 0;
this.cOpcodes = this.cOpcodesStart = 0;
/*
* Most commands that require an address call parseAddr(), which defaults to dbgAddrNextCode
* or dbgAddrNextData when no address has been given. doDump() and doUnassemble(), in turn,
@ -110,18 +100,6 @@ function Debugger8080(parmsDbg)
*/
this.dbgAddrNextCode = this.newAddr();
this.dbgAddrNextData = this.newAddr();
/*
* This maintains command history. New commands are inserted at index 0 of the array.
* When Enter is pressed on an empty input buffer, we default to the command at aPrevCmds[0].
*/
this.iPrevCmd = -1;
this.aPrevCmds = [];
/*
* fAssemble is true when "assemble mode" is active, false when not.
*/
this.fAssemble = false;
this.dbgAddrAssemble = this.newAddr();
/*
@ -138,21 +116,6 @@ function Debugger8080(parmsDbg)
*/
this.aSymbolTable = [];
/*
* aVariables is an object with properties that grows as setVariable() assigns more variables;
* each property corresponds to one variable, where the property name is the variable name (ie,
* a string beginning with a letter or underscore, followed by zero or more additional letters,
* digits, or underscores) and the property value is the variable's numeric value. See doVar()
* and setVariable() for details.
*
* Note that parseValue(), through its reliance on str.parseInt(), assumes a default base of 16
* if no base is explicitly indicated (eg, a trailing decimal period), and if you define variable
* names containing exclusively hex alpha characters (a-f), those variables will take precedence
* over the corresponding hex values. In other words, if you define variables "a" and "b", you
* will no longer be able to simply type "a" or "b" to specify the decimal values 10 or 11.
*/
this.aVariables = {};
/*
* clearBreakpoints() initializes the breakpoints lists: aBreakExec is a list of addresses
* to halt on whenever attempting to execute an instruction at the corresponding address,
@ -184,7 +147,7 @@ function Debugger8080(parmsDbg)
/*
* Initialize Debugger8080 message support
*/
this.afnDumpers = [];
this.afnDumpers = {};
this.messageInit(parmsDbg['messages']);
this.sInitCommands = parmsDbg['commands'];
@ -214,10 +177,10 @@ function Debugger8080(parmsDbg)
if (DEBUGGER) {
Component.subclass(Debugger8080);
Component.subclass(Debugger8080, Debugger);
/*
* NOTE: Every Debugger8080 property from here to the first prototype function definition (initBus()) is a
* NOTE: Every Debugger8080 property from here to the first prototype function definition (initBus()) is
* considered a "class constant"; most of them use our "all-caps" convention (and all of them SHOULD, but
* that wouldn't help us catch any bugs).
*
@ -225,7 +188,7 @@ if (DEBUGGER) {
* really clutters the code. I wish the Closure Compiler had a way to annotate every definition with a given
* section with a single annotation....
*
* Bugs can slip through the cracks without those annotations; for example, I unthinkingly redefined TYPE_SI
* Bugs can slip through the cracks without those annotations; for example, I unthinkingly redefined TYPE_SIZE
* at one point, and if all the definitions had been preceded by an "@const", that mistake would have been
* caught at compile-time.
*/
@ -361,7 +324,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);
@ -719,36 +682,29 @@ if (DEBUGGER) {
* control.focus();
*/
control.onkeydown = function onKeyDownDebugInput(event) {
var sCmds;
if (event.keyCode == Keyboard8080.KEYCODE.CR) {
sCmds = control.value;
var sCmd;
if (event.keyCode == Keys.KEYCODE.CR) {
sCmd = control.value;
control.value = "";
dbg.doCommands(sCmds, true);
dbg.doCommands(sCmd, true);
}
else if (event.keyCode == Keyboard8080.KEYCODE.ESC) {
control.value = sCmds = "";
else if (event.keyCode == Keys.KEYCODE.ESC) {
control.value = sCmd = "";
}
else {
if (event.keyCode == Keyboard8080.KEYCODE.UP) {
if (dbg.iPrevCmd < dbg.aPrevCmds.length - 1) {
sCmds = dbg.aPrevCmds[++dbg.iPrevCmd];
}
if (event.keyCode == Keys.KEYCODE.UP) {
sCmd = dbg.getPrevCommand();
}
else if (event.keyCode == Keyboard8080.KEYCODE.DOWN) {
if (dbg.iPrevCmd > 0) {
sCmds = dbg.aPrevCmds[--dbg.iPrevCmd];
} else {
sCmds = "";
dbg.iPrevCmd = -1;
}
else if (event.keyCode == Keys.KEYCODE.DOWN) {
sCmd = dbg.getNextCommand();
}
if (sCmds != null) {
var cch = sCmds.length;
control.value = sCmds;
if (sCmd != null) {
var cch = sCmd.length;
control.value = sCmd;
control.setSelectionRange(cch, cch);
}
}
if (sCmds != null && event.preventDefault) event.preventDefault();
if (sCmd != null && event.preventDefault) event.preventDefault();
};
return true;
@ -1807,7 +1763,7 @@ if (DEBUGGER) {
* it here, so that if the CPU is reset while running, we can prevent stop()
* from unnecessarily dumping the CPU state.
*/
this.flags.fRunning = false;
this.flags.running = false;
this.clearTempBreakpoint();
if (!fQuiet) this.updateStatus();
};
@ -1866,7 +1822,7 @@ if (DEBUGGER) {
Debugger8080.prototype.start = function(ms, nCycles)
{
if (!this.nStep) this.println("running");
this.flags.fRunning = true;
this.flags.running = true;
this.msStart = ms;
this.nCyclesStart = nCycles;
};
@ -1882,8 +1838,8 @@ if (DEBUGGER) {
*/
Debugger8080.prototype.stop = function(ms, nCycles)
{
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.flags.running) {
this.flags.running = false;
this.nCycles = nCycles - this.nCyclesStart;
if (!this.nStep) {
var sStopped = "stopped";
@ -2461,11 +2417,11 @@ if (DEBUGGER) {
if (sComment) {
sLine = str.pad(sLine, 40) + ';' + sComment;
if (!this.cpu.flags.fChecksum) {
if (!this.cpu.flags.checksum) {
sLine += (nSequence != null? '=' + nSequence.toString() : "");
} else {
var nCycles = this.cpu.getCycles();
sLine += "cycles=" + nCycles.toString() + " cs=" + str.toHex(this.cpu.counts.nChecksum);
sLine += "cycles=" + nCycles.toString() + " cs=" + str.toHex(this.cpu.nChecksum);
}
}
return sLine;
@ -2622,380 +2578,7 @@ if (DEBUGGER) {
return s;
};
Debugger8080.aBinOpPrecedence = {
'||': 0, // logical OR
'&&': 1, // logical AND
'|': 2, // bitwise OR
'^': 3, // bitwise XOR
'&': 4, // bitwise AND
'!=': 5, // inequality
'==': 5, // equality
'>=': 6, // greater than or equal to
'>': 6, // greater than
'<=': 6, // less than or equal to
'<': 6, // less than
'>>>': 7, // unsigned bitwise right shift
'>>': 7, // bitwise right shift
'<<': 7, // bitwise left shift
'-': 8, // subtraction
'+': 8, // addition
'%': 9, // remainder
'/': 9, // division
'*': 9 // multiplication
};
/**
* evalExpression(aVals, aOps, cOps)
*
* In Node, if you set a variable to 0x80000001; ie:
*
* foo=0x80000001|0
*
* and then calculate foo*foo using "(foo*foo).toString(2)", the result is:
*
* '11111111111111111111111111111100000000000000000000000000000000'
*
* which is slightly incorrect because it has overflowed JavaScript's floating-point precision.
*
* 0x80000001 in decimal is -2147483647, so the product is 4611686014132420609, which is 0x3FFFFFFF00000001.
*
* @this {Debugger8080}
* @param {Array.<number>} aVals
* @param {Array.<string>} aOps
* @param {number} [cOps] (default is all)
* @return {boolean} true if successful, false if error
*/
Debugger8080.prototype.evalExpression = function(aVals, aOps, cOps)
{
cOps = cOps || -1;
while (cOps-- && aOps.length) {
var chOp = aOps.pop();
if (aVals.length < 2) return false;
var valNew;
var val2 = aVals.pop();
var val1 = aVals.pop();
switch(chOp) {
case '*':
valNew = val1 * val2;
break;
case '/':
if (!val2) return false;
valNew = val1 / val2;
break;
case '%':
if (!val2) return false;
valNew = val1 % val2;
break;
case '+':
valNew = val1 + val2;
break;
case '-':
valNew = val1 - val2;
break;
case '<<':
valNew = val1 << val2;
break;
case '>>':
valNew = val1 >> val2;
break;
case '>>>':
valNew = val1 >>> val2;
break;
case '<':
valNew = (val1 < val2? 1 : 0);
break;
case '<=':
valNew = (val1 <= val2? 1 : 0);
break;
case '>':
valNew = (val1 > val2? 1 : 0);
break;
case '>=':
valNew = (val1 >= val2? 1 : 0);
break;
case '==':
valNew = (val1 == val2? 1 : 0);
break;
case '!=':
valNew = (val1 != val2? 1 : 0);
break;
case '&':
valNew = val1 & val2;
break;
case '^':
valNew = val1 ^ val2;
break;
case '|':
valNew = val1 | val2;
break;
case '&&':
valNew = (val1 && val2? 1 : 0);
break;
case '||':
valNew = (val1 || val2? 1 : 0);
break;
default:
return false;
}
aVals.push(valNew|0);
}
return true;
};
/**
* parseExpression(sExp, fPrint)
*
* A quick-and-dirty expression parser. It takes an expression like:
*
* EDX+EDX*4+12345678
*
* and builds a value stack in aVals and a "binop" (binary operator) stack in aOps:
*
* aVals aOps
* ----- ----
* EDX +
* EDX *
* 4 +
* ...
*
* We pop 1 "binop" from aOps and 2 values from aVals whenever a "binop" of lower priority than its
* predecessor is encountered, evaluate, and push the result back onto aVals.
*
* Unary operators like '~' and ternary operators like '?:' are not supported; neither are parentheses.
*
* However, parseReference() now makes it possible to write parenthetical-style sub-expressions by using
* {...} (braces), as well as address references by using [...] (brackets).
*
* Why am I using braces instead of parentheses for sub-expressions? Because parseReference() serves
* multiple purposes, the other being reference replacement in message strings passing through replaceRegs(),
* and I didn't want parentheses taking on a new meaning in message strings.
*
* @this {Debugger8080}
* @param {string|undefined} sExp
* @param {boolean} [fPrint] is true to print all resolved values, false for quiet parsing
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
*/
Debugger8080.prototype.parseExpression = function(sExp, fPrint)
{
var value;
if (sExp) {
/*
* First process (and eliminate) any references, aka sub-expressions.
*/
sExp = this.parseReference(sExp);
var i = 0;
var fError = false;
var sExpOrig = sExp;
var aVals = [], aOps = [];
/*
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a regexp split():
* when the regexp uses a capturing pattern, the resulting array will include entries for all the pattern
* matches along with the non-matches. This effectively means that, in the set of expressions that we
* support, all even entries in asValues will contain "values" and all odd entries will contain "operators".
*
* And although I tried to list the supported operators in "precedential" order, bitwise operators must
* be out-of-order so that we don't mistakenly match either '>' or '<' when they're part of '>>' or '<<'.
*/
var regExp = /(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
var asValues = sExp.split(regExp);
while (i < asValues.length) {
var sValue = asValues[i++];
var cchValue = sValue.length;
var s = str.trim(sValue);
if (!s) {
fError = true;
break;
}
var v = this.parseValue(s, null, fPrint === false);
if (v === undefined) {
fError = true;
fPrint = false;
break;
}
aVals.push(v);
if (i == asValues.length) break;
var sOp = asValues[i++], cchOp = sOp.length;
this.assert(Debugger8080.aBinOpPrecedence[sOp] != null);
if (aOps.length && Debugger8080.aBinOpPrecedence[sOp] < Debugger8080.aBinOpPrecedence[aOps[aOps.length-1]]) {
this.evalExpression(aVals, aOps, 1);
}
aOps.push(sOp);
sExp = sExp.substr(cchValue + cchOp);
}
if (!this.evalExpression(aVals, aOps) || aVals.length != 1) {
fError = true;
}
if (!fError) {
value = aVals.pop();
if (fPrint) this.printValue(null, value);
} else {
if (fPrint) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
}
}
return value;
};
/**
* parseReference(s)
*
* Returns the given string with any "{expression}" sequences replaced with the value of the expression,
* and any "[address]" references replaced with the contents of the address. Expressions are parsed BEFORE
* addresses. Owing to this function's simplistic parsing, nested braces/brackets are not supported
* (define intermediate variables if needed).
*
* @this {Debugger8080}
* @param {string} s
* @return {string}
*/
Debugger8080.prototype.parseReference = function(s)
{
var a;
while (a = s.match(/\{(.*?)}/)) {
if (a[1].indexOf('{') >= 0) break; // unsupported nested brace(s)
var value = this.parseExpression(a[1]);
s = s.replace('{' + a[1] + '}', value != null? str.toHex(value) : "undefined");
}
while (a = s.match(/\[(.*?)]/)) {
if (a[1].indexOf('[') >= 0) break; // unsupported nested bracket(s)
var dbgAddr = this.parseAddr(a[1]);
s = s.replace('[' + a[1] + ']', dbgAddr? str.toHex(this.getWord(dbgAddr), 4) : "undefined");
}
return this.parseSysVars(s);
};
/**
* parseSysVars(s)
*
* Returns the given string with any recognized "$var" replaced with its value; eg:
*
* $ops: the number of opcodes executed since the last time it was displayed (or reset)
*
* @this {Debugger8080}
* @param {string} s
* @return {string}
*/
Debugger8080.prototype.parseSysVars = function(s)
{
var a;
while (a = s.match(/\$([a-z]+)/i)) {
var v = null;
switch(a[1].toLowerCase()) {
case "ops":
v = this.cOpcodes - this.cOpcodesStart;
break;
}
if (v == null) break;
s = s.replace(a[0], v.toString());
}
return s;
};
/**
* parseValue(sValue, sName, fQuiet)
*
* @this {Debugger8080}
* @param {string|undefined} sValue
* @param {string|null} [sName] is the name of the value, if any
* @param {boolean} [fQuiet]
* @return {number|undefined} numeric value, or undefined if sValue is either undefined or invalid
*/
Debugger8080.prototype.parseValue = function(sValue, sName, fQuiet)
{
var value;
if (sValue !== undefined) {
var iReg = this.getRegIndex(sValue);
if (iReg >= 0) {
value = this.getRegValue(iReg);
} else {
value = this.getVariable(sValue);
if (value === undefined) value = str.parseInt(sValue);
}
if (value === undefined && !fQuiet) this.println("invalid " + (sName? sName : "value") + ": " + sValue);
} else {
if (!fQuiet) this.println("missing " + (sName || "value"));
}
return value;
};
/**
* printValue(sVar, value)
*
* @this {Debugger8080}
* @param {string|null} sVar
* @param {number|undefined} value
* @return {boolean} true if value defined, false if not
*/
Debugger8080.prototype.printValue = function(sVar, value)
{
var sValue;
var fDefined = false;
if (value !== undefined) {
fDefined = true;
sValue = str.toHexLong(value) + " " + value + ". (" + str.toBinBytes(value) + ")";
}
sVar = (sVar != null? (sVar + ": ") : "");
this.println(sVar + sValue);
return fDefined;
};
/**
* printVariable(sVar)
*
* @this {Debugger8080}
* @param {string} [sVar]
* @return {boolean} true if all value(s) defined, false if not
*/
Debugger8080.prototype.printVariable = function(sVar)
{
if (sVar) {
return this.printValue(sVar, this.aVariables[sVar]);
}
var cVariables = 0;
for (sVar in this.aVariables) {
this.printValue(sVar, this.aVariables[sVar]);
cVariables++;
}
return cVariables > 0;
};
/**
* delVariable(sVar)
*
* @this {Debugger8080}
* @param {string} sVar
*/
Debugger8080.prototype.delVariable = function(sVar)
{
delete this.aVariables[sVar];
};
/**
* getVariable(sVar)
*
* @this {Debugger8080}
* @param {string} sVar
* @return {number|undefined}
*/
Debugger8080.prototype.getVariable = function(sVar)
{
return this.aVariables[sVar];
};
/**
* setVariable(sVar, value)
*
* @this {Debugger8080}
* @param {string} sVar
* @param {number} value
*/
Debugger8080.prototype.setVariable = function(sVar, value)
{
this.aVariables[sVar] = value;
};
/**
/**
* comparePairs(p1, p2)
*
* @this {Debugger8080}
@ -3612,7 +3195,7 @@ if (DEBUGGER) {
this.println("warning: " + str.toHex(vNew) + " exceeds " + size + "-byte value");
}
var vOld = fnGet.call(this, dbgAddr);
this.println("changing " + this.toHexAddr(dbgAddr) + " from 0x" + str.toHex(vOld, cch) + " to 0x" + str.toHex(vNew, cch));
this.println("changing " + this.toHexAddr(dbgAddr) + " from " + str.toHex(vOld, cch, true) + " to " + str.toHex(vNew, cch, true));
fnSet.call(this, dbgAddr, vNew, size);
}
};
@ -3673,7 +3256,7 @@ if (DEBUGGER) {
Debugger8080.prototype.doHalt = function(fQuiet)
{
var sMsg;
if (this.flags.fRunning) {
if (this.flags.running) {
sMsg = "halting";
this.stopCPU();
} else {
@ -3719,8 +3302,8 @@ if (DEBUGGER) {
Debugger8080.prototype.doInfo = function(asArgs)
{
if (DEBUG) {
this.println("msPerYield: " + this.cpu.counts.msPerYield);
this.println("nCyclesPerYield: " + this.cpu.counts.nCyclesPerYield);
this.println("msPerYield: " + this.cpu.msPerYield);
this.println("nCyclesPerYield: " + this.cpu.nCyclesPerYield);
return true;
}
return false;
@ -3971,13 +3554,13 @@ if (DEBUGGER) {
if (asArgs[3] !== undefined) nCycles = +asArgs[3]; // warning: decimal instead of hex conversion
switch (asArgs[2]) {
case "int":
this.cpu.counts.nCyclesChecksumInterval = nCycles;
this.cpu.nCyclesChecksumInterval = nCycles;
break;
case "start":
this.cpu.counts.nCyclesChecksumStart = nCycles;
this.cpu.nCyclesChecksumStart = nCycles;
break;
case "stop":
this.cpu.counts.nCyclesChecksumStop = nCycles;
this.cpu.nCyclesChecksumStop = nCycles;
break;
default:
this.println("unknown cs option");
@ -3986,7 +3569,7 @@ if (DEBUGGER) {
if (nCycles !== undefined) {
this.cpu.resetChecksum();
}
this.println("checksums " + (this.cpu.flags.fChecksum? "enabled" : "disabled"));
this.println("checksums " + (this.cpu.flags.checksum? "enabled" : "disabled"));
return;
case "sp":
@ -4086,81 +3669,74 @@ if (DEBUGGER) {
return;
}
var fValid = false;
var w = this.parseExpression(sValue);
if (w === undefined) return;
if (w !== undefined) {
fValid = true;
var sRegMatch = sReg.toUpperCase();
switch (sRegMatch) {
case "A":
cpu.regA = w & 0xff;
break;
case "B":
cpu.regB = w & 0xff;
break;
case "BC":
cpu.regB = ((w >> 8) & 0xff);
/* falls through */
case "C":
cpu.regC = w & 0xff;
break;
case "D":
cpu.regD = w & 0xff;
break;
case "DE":
cpu.regD = ((w >> 8) & 0xff);
/* falls through */
case "E":
cpu.regE = w & 0xff;
break;
case "H":
cpu.regH = w & 0xff;
break;
case "HL":
cpu.regH = ((w >> 8) & 0xff);
/* falls through */
case "L":
cpu.regL = w & 0xff;
break;
case "SP":
cpu.setSP(w);
break;
case "PC":
cpu.setPC(w);
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
break;
case "PS":
cpu.setPS(w);
break;
case "PSW":
cpu.setPSW(w);
break;
case "CF":
if (w) cpu.setCF(); else cpu.clearCF();
break;
case "PF":
if (w) cpu.setPF(); else cpu.clearPF();
break;
case "AF":
if (w) cpu.setAF(); else cpu.clearAF();
break;
case "ZF":
if (w) cpu.setZF(); else cpu.clearZF();
break;
case "SF":
if (w) cpu.setSF(); else cpu.clearSF();
break;
case "IF":
if (w) cpu.setIF(); else cpu.clearIF();
break;
default:
this.println("unknown register: " + sReg);
return;
}
}
if (!fValid) {
this.println("invalid value: " + sValue);
var sRegMatch = sReg.toUpperCase();
switch (sRegMatch) {
case "A":
cpu.regA = w & 0xff;
break;
case "B":
cpu.regB = w & 0xff;
break;
case "BC":
cpu.regB = ((w >> 8) & 0xff);
/* falls through */
case "C":
cpu.regC = w & 0xff;
break;
case "D":
cpu.regD = w & 0xff;
break;
case "DE":
cpu.regD = ((w >> 8) & 0xff);
/* falls through */
case "E":
cpu.regE = w & 0xff;
break;
case "H":
cpu.regH = w & 0xff;
break;
case "HL":
cpu.regH = ((w >> 8) & 0xff);
/* falls through */
case "L":
cpu.regL = w & 0xff;
break;
case "SP":
cpu.setSP(w);
break;
case "PC":
cpu.setPC(w);
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
break;
case "PS":
cpu.setPS(w);
break;
case "PSW":
cpu.setPSW(w);
break;
case "CF":
if (w) cpu.setCF(); else cpu.clearCF();
break;
case "PF":
if (w) cpu.setPF(); else cpu.clearPF();
break;
case "AF":
if (w) cpu.setAF(); else cpu.clearAF();
break;
case "ZF":
if (w) cpu.setZF(); else cpu.clearZF();
break;
case "SF":
if (w) cpu.setSF(); else cpu.clearSF();
break;
case "IF":
if (w) cpu.setIF(); else cpu.clearIF();
break;
default:
this.println("unknown register: " + sReg);
return;
}
cpu.updateCPU();
@ -4760,7 +4336,7 @@ if (DEBUGGER) {
{
var a = this.parseCommand(sCmds, fSave);
for (var s in a) {
if (!this.doCommand(a[s])) return false;
if (!this.doCommand(a[+s])) return false;
}
return true;
};

View file

@ -35,6 +35,7 @@ if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Keys = require("../../shared/lib/keys");
var PC8080 = require("./defines");
var ChipSet8080 = require("./chipset");
var Messages8080= require("./messages");
@ -70,183 +71,6 @@ function Keyboard8080(parmsKbd)
Component.subclass(Keyboard8080);
/**
* 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}
*/
Keyboard8080.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}
*/
Keyboard8080.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.
*/
Keyboard8080.KEYCODE.NUM_CR = Keyboard8080.KEYCODE.CR + Keyboard8080.KEYCODE.ONRIGHT;
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keyboard8080.STUPID_KEYCODES = {};
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.SEMI] = Keyboard8080.ASCII[';']; // 186 -> 59
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.EQUALS] = Keyboard8080.ASCII['=']; // 187 -> 61
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.COMMA] = Keyboard8080.ASCII[',']; // 188 -> 44
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.DASH] = Keyboard8080.ASCII['-']; // 189 -> 45
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.PERIOD] = Keyboard8080.ASCII['.']; // 190 -> 46
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.SLASH] = Keyboard8080.ASCII['/']; // 191 -> 47
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.BQUOTE] = Keyboard8080.ASCII['`']; // 192 -> 96
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.LBRACK] = Keyboard8080.ASCII['[']; // 219 -> 91
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.BSLASH] = Keyboard8080.ASCII['\\']; // 220 -> 92
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.RBRACK] = Keyboard8080.ASCII[']']; // 221 -> 93
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.QUOTE] = Keyboard8080.ASCII["'"]; // 222 -> 39
Keyboard8080.STUPID_KEYCODES[Keyboard8080.KEYCODE.FF_DASH] = Keyboard8080.ASCII['-'];
Keyboard8080.MINPRESSTIME = 100; // 100ms
/**
@ -256,9 +80,9 @@ Keyboard8080.MINPRESSTIME = 100; // 100ms
* a simple object literal for this and all other object initializations.
*/
Keyboard8080.WASDCODES = {};
Keyboard8080.WASDCODES[Keyboard8080.ASCII.A] = Keyboard8080.KEYCODE.LEFT;
Keyboard8080.WASDCODES[Keyboard8080.ASCII.D] = Keyboard8080.KEYCODE.RIGHT;
Keyboard8080.WASDCODES[Keyboard8080.ASCII.L] = Keyboard8080.KEYCODE.SPACE;
Keyboard8080.WASDCODES[Keys.ASCII.A] = Keys.KEYCODE.LEFT;
Keyboard8080.WASDCODES[Keys.ASCII.D] = Keys.KEYCODE.RIGHT;
Keyboard8080.WASDCODES[Keys.ASCII.L] = Keys.KEYCODE.SPACE;
/*
* Supported configurations
@ -269,12 +93,12 @@ Keyboard8080.SI1978 = {
ALTCODES: Keyboard8080.WASDCODES,
LEDCODES: {},
SOFTCODES: {
'1p': Keyboard8080.KEYCODE.ONE,
'2p': Keyboard8080.KEYCODE.TWO,
'coin': Keyboard8080.KEYCODE.THREE,
'left': Keyboard8080.KEYCODE.LEFT,
'right': Keyboard8080.KEYCODE.RIGHT,
'fire': Keyboard8080.KEYCODE.SPACE
'1p': Keys.KEYCODE.ONE,
'2p': Keys.KEYCODE.TWO,
'coin': Keys.KEYCODE.THREE,
'left': Keys.KEYCODE.LEFT,
'right': Keys.KEYCODE.RIGHT,
'fire': Keys.KEYCODE.SPACE
}
};
@ -284,7 +108,7 @@ Keyboard8080.VT100 = {
ALTCODES: {},
LEDCODES: {},
SOFTCODES: {
'setup': Keyboard8080.KEYCODE.F9
'setup': Keys.KEYCODE.F9
},
/*
* Reading port 0x82 returns a key address from the VT100 keyboard's UART data output.
@ -329,89 +153,89 @@ Keyboard8080.VT100 = {
/*
* Table to map host key codes to VT100 key addresses (ie, unique 7-bit values representing key positions on the VT100)
*/
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.DEL] = 0x03;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.P] = 0x05;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.O] = 0x06;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.Y] = 0x07;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.T] = 0x08;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.W] = 0x09;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.Q] = 0x0A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.RIGHT] = 0x10;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.RBRACK] = 0x14;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.LBRACK] = 0x15;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.I] = 0x16;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.U] = 0x17;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.R] = 0x18;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.E] = 0x19;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.ONE] = 0x1A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.LEFT] = 0x20;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.DOWN] = 0x22;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F6] = 0x23; // aka BREAK
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.PAUSE] = 0x23; // aka BREAK
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.BQUOTE] = 0x24;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.DASH] = 0x25;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NINE] = 0x26;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.SEVEN] = 0x27;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.FOUR] = 0x28;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.THREE] = 0x29;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.ESC] = 0x2A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.UP] = 0x30;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F3] = 0x31; // aka PF3
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F1] = 0x32; // aka PF1
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.BS] = 0x33;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.EQUALS] = 0x34;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.ZERO] = 0x35;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.EIGHT] = 0x36;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.SIX] = 0x37;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.FIVE] = 0x38;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.TWO] = 0x39;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.TAB] = 0x3A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_7] = 0x40;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F4] = 0x41; // aka PF4
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F2] = 0x42; // aka PF2
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_0] = 0x43;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F7] = 0x44; // aka LINE FEED
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.BSLASH] = 0x45;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.L] = 0x46;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.K] = 0x47;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.G] = 0x48;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.F] = 0x49;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.A] = 0x4A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_8] = 0x50;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_CR] = 0x51;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_2] = 0x52;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_1] = 0x53;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.QUOTE] = 0x55;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.SEMI] = 0x56;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.J] = 0x57;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.H] = 0x58;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.D] = 0x59;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.S] = 0x5A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_DEL] = 0x60; // keypad period
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F5] = 0x61; // aka KEYPAD COMMA
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_5] = 0x62;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_4] = 0x63;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.CR] = 0x64; // TODO: Figure out why the Technical Manual lists CR at both 0x04 and 0x64
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.PERIOD] = 0x65;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.COMMA] = 0x66;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.N] = 0x67;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.B] = 0x68;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.X] = 0x69;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F8] = 0x6A; // aka NO SCROLL
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_9] = 0x70;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_3] = 0x71;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_6] = 0x72;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.NUM_SUB] = 0x73; // aka KEYPAD MINUS
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.SLASH] = 0x75;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.M] = 0x76;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII[' ']] = 0x77;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.V] = 0x78;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.C] = 0x79;
Keyboard8080.VT100.KEYMAP[Keyboard8080.ASCII.Z] = 0x7A;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.F9] = 0x7B; // aka SET-UP
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.CTRL] = 0x7C;
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.SHIFT] = 0x7D; // either shift key (doesn't matter)
Keyboard8080.VT100.KEYMAP[Keyboard8080.KEYCODE.CAPSLOCK]= 0x7E;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.DEL] = 0x03;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.P] = 0x05;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.O] = 0x06;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.Y] = 0x07;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.T] = 0x08;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.W] = 0x09;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.Q] = 0x0A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.RIGHT] = 0x10;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.RBRACK] = 0x14;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.LBRACK] = 0x15;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.I] = 0x16;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.U] = 0x17;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.R] = 0x18;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.E] = 0x19;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.ONE] = 0x1A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.LEFT] = 0x20;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.DOWN] = 0x22;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F6] = 0x23; // aka BREAK
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.PAUSE] = 0x23; // aka BREAK
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.BQUOTE] = 0x24;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.DASH] = 0x25;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NINE] = 0x26;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.SEVEN] = 0x27;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.FOUR] = 0x28;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.THREE] = 0x29;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.ESC] = 0x2A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.UP] = 0x30;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F3] = 0x31; // aka PF3
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F1] = 0x32; // aka PF1
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.BS] = 0x33;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.EQUALS] = 0x34;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.ZERO] = 0x35;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.EIGHT] = 0x36;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.SIX] = 0x37;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.FIVE] = 0x38;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.TWO] = 0x39;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.TAB] = 0x3A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_7] = 0x40;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F4] = 0x41; // aka PF4
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F2] = 0x42; // aka PF2
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_0] = 0x43;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F7] = 0x44; // aka LINE FEED
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.BSLASH] = 0x45;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.L] = 0x46;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.K] = 0x47;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.G] = 0x48;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.F] = 0x49;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.A] = 0x4A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_8] = 0x50;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_CR] = 0x51;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_2] = 0x52;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_1] = 0x53;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.QUOTE] = 0x55;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.SEMI] = 0x56;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.J] = 0x57;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.H] = 0x58;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.D] = 0x59;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.S] = 0x5A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_DEL] = 0x60; // keypad period
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F5] = 0x61; // aka KEYPAD COMMA
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_5] = 0x62;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_4] = 0x63;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.CR] = 0x64; // TODO: Figure out why the Technical Manual lists CR at both 0x04 and 0x64
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.PERIOD] = 0x65;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.COMMA] = 0x66;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.N] = 0x67;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.B] = 0x68;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.X] = 0x69;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F8] = 0x6A; // aka NO SCROLL
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_9] = 0x70;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_3] = 0x71;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_6] = 0x72;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.NUM_SUB] = 0x73; // aka KEYPAD MINUS
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.SLASH] = 0x75;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.M] = 0x76;
Keyboard8080.VT100.KEYMAP[Keys.ASCII[' ']] = 0x77;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.V] = 0x78;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.C] = 0x79;
Keyboard8080.VT100.KEYMAP[Keys.ASCII.Z] = 0x7A;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.F9] = 0x7B; // aka SET-UP
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.CTRL] = 0x7C;
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.SHIFT] = 0x7D; // either shift key (doesn't matter)
Keyboard8080.VT100.KEYMAP[Keys.KEYCODE.CAPS_LOCK] = 0x7E;
Keyboard8080.VT100.LEDCODES = {
'l4': Keyboard8080.VT100.STATUS.LED4,

View file

@ -384,11 +384,11 @@ Memory8080.prototype = {
setReadAccess: function(afn, fDirect) {
if (!fDirect || !this.cReadBreakpoints) {
this.readByte = afn[0] || this.readNone;
this.readShort = afn[1] || this.readShortDefault;
this.readShort = afn[2] || this.readShortDefault;
}
if (fDirect || fDirect === undefined) {
this.readByteDirect = afn[0] || this.readNone;
this.readShortDirect = afn[1] || this.readShortDefault;
this.readShortDirect = afn[2] || this.readShortDefault;
}
},
/**
@ -400,11 +400,11 @@ Memory8080.prototype = {
*/
setWriteAccess: function(afn, fDirect) {
if (!fDirect || !this.cWriteBreakpoints) {
this.writeByte = !this.fReadOnly && afn[2] || this.writeNone;
this.writeByte = !this.fReadOnly && afn[1] || this.writeNone;
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
}
if (fDirect || fDirect === undefined) {
this.writeByteDirect = afn[2] || this.writeNone;
this.writeByteDirect = afn[1] || this.writeNone;
this.writeShortDirect = afn[3] || this.writeShortDefault;
}
},
@ -807,18 +807,45 @@ Memory8080.prototype = {
/*
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 short handlers) are undefined.
*
Memory8080.afnNone = [Memory8080.prototype.readNone, Memory8080.prototype.readShortDefault, Memory8080.prototype.writeNone, Memory8080.prototype.writeShortDefault];
Memory8080.afnNone = [
Memory8080.prototype.readNone,
Memory8080.prototype.writeNone,
Memory8080.prototype.readShortDefault,
Memory8080.prototype.writeShortDefault
];
*/
Memory8080.afnNone = [];
Memory8080.afnNone = [];
Memory8080.afnMemory = [Memory8080.prototype.readByteMemory, Memory8080.prototype.readShortMemory, Memory8080.prototype.writeByteMemory, Memory8080.prototype.writeShortMemory];
Memory8080.afnChecked = [Memory8080.prototype.readByteChecked, Memory8080.prototype.readShortChecked, Memory8080.prototype.writeByteChecked, Memory8080.prototype.writeShortChecked];
Memory8080.afnMemory = [
Memory8080.prototype.readByteMemory,
Memory8080.prototype.writeByteMemory,
Memory8080.prototype.readShortMemory,
Memory8080.prototype.writeShortMemory
];
Memory8080.afnChecked = [
Memory8080.prototype.readByteChecked,
Memory8080.prototype.writeByteChecked,
Memory8080.prototype.readShortChecked,
Memory8080.prototype.writeShortChecked
];
if (TYPEDARRAYS) {
Memory8080.afnArrayBE = [Memory8080.prototype.readByteBE, Memory8080.prototype.readShortBE, Memory8080.prototype.writeByteBE, Memory8080.prototype.writeShortBE];
Memory8080.afnArrayLE = [Memory8080.prototype.readByteLE, Memory8080.prototype.readShortLE, Memory8080.prototype.writeByteLE, Memory8080.prototype.writeShortLE];
Memory8080.afnArrayBE = [
Memory8080.prototype.readByteBE,
Memory8080.prototype.writeByteBE,
Memory8080.prototype.readShortBE,
Memory8080.prototype.writeShortBE
];
Memory8080.afnArrayLE = [
Memory8080.prototype.readByteLE,
Memory8080.prototype.writeByteLE,
Memory8080.prototype.readShortLE,
Memory8080.prototype.writeShortLE
];
}
if (NODE) module.exports = Memory8080;

View file

@ -59,7 +59,7 @@ if (NODE) {
* is the ROM you expected.
*
* Finally, while making ROM "writable" may seem a contradiction in terms, I want to be able to load selected
* CP/M binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
* binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
* simplest solution was to add the option to load binary files into memory as "writable" ROMs.
*
* Moreover, if a "writable" ROM is installed at addr 0x100, that triggers our "Fake CP/M" support, providing

View file

@ -910,7 +910,7 @@ Video8080.prototype.setFocus = function()
* getRefreshTime()
*
* @this {Video8080}
* @return {number}
* @return {number} (number of milliseconds per refresh)
*/
Video8080.prototype.getRefreshTime = function()
{
@ -1204,7 +1204,6 @@ Video8080.prototype.updateVT100 = function(fForced)
*/
Video8080.prototype.updateScreen = function(fForced)
{
var fClean;
var fUpdate = true;
if (!fForced) {
@ -1237,7 +1236,7 @@ Video8080.prototype.updateScreen = function(fForced)
* is clean, then there's nothing to do.
*/
if (fUpdate && this.fCellCacheValid && this.sizeBuffer) {
if ((fClean = this.bus.cleanMemory(this.addrBuffer, this.sizeBuffer))) {
if (this.bus.cleanMemory(this.addrBuffer, this.sizeBuffer)) {
fUpdate = false;
}
}

View file

@ -73,18 +73,26 @@ try {
* ]
*
* Every component name comes from the component filename, minus the ".js" extension;
* Create is the constructor returned by require(). The only bit of fudging we do is
* overriding the constructor for component "cpu" with the constructor for "x86cpu",
* because when a "cpu" definition is encountered, it's the "x86cpu" subclass that we
* actually want to create, not the "cpu" superclass.
* Create is the constructor returned by require().
*
* TODO: Update the list of ignored (ie, ignorable) components.
*/
var Component;
var dbg;
var aComponents = [];
var asComponentsIgnore = ["embed","save"];
var asComponentsIgnore = ["embed", "save"];
/*
* A few of the components are subclasses of other classes (eg, "x86cpu" is a subclass
* of "cpu"). In those situations, we "hoist" the subclass constructor into the
* corresponding superclass, because it is the name of the superclass that we rely on during
* machine initialization.
*/
var aSubClasses = {
"pcx86/lib/x86cpu": "pcx86/lib/cpu",
"pcx86/lib/debugger": "shared/lib/debugger"
};
/**
* loadComponents(asFiles)
*
@ -106,9 +114,17 @@ function loadComponents(asFiles)
* we'll want to access later (eg, "component" in getComponentByType()).
*/
var fn = require(lib + "../../../" + sFile);
if (sName == "x86cpu") {
var sSuperClass = null;
for (var s in aSubClasses) {
if (sFile.indexOf(s) >= 0) {
sSuperClass = aSubClasses[s];
break;
}
}
if (sSuperClass) {
for (var j = 0; j < aComponents.length; j++) {
if (aComponents[j].name == "cpu") {
if (aComponents[j].path.indexOf(sSuperClass) >= 0) {
if (fDebug) console.log("updating superclass " + aComponents[j].path + " with subclass " + sFile);
aComponents[j].Create = fn;
sName = null;
break;
@ -121,7 +137,7 @@ function loadComponents(asFiles)
}; // jshint ignore:line
}
if (sName) {
aComponents.push({name: sName, Create: fn, objects: []});
aComponents.push({name: sName, path: sFile, Create: fn, objects: []});
}
if (sName == "defines") {
/*

View file

@ -68,7 +68,7 @@ if (NODE) {
* @extends Component
* @param {Object} parmsBus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
function Bus(parmsBus, cpu, dbg)
{
@ -259,38 +259,6 @@ Bus.ERROR = {
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
* }}
*/
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo 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 BusInfo;
/**
* initMemory()
*
@ -463,6 +431,42 @@ Bus.prototype.cleanMemory = function(addr, size)
return fClean;
};
/*
* Data types used by scanMemory()
*/
/**
* @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
* }}
*/
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo 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 BusInfo;
/**
* scanMemory(info, addr, size)
*

View file

@ -1294,7 +1294,7 @@ ChipSet.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -139,7 +139,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
Component.call(this, "Computer", parmsComputer, Computer, Messages.COMPUTER);
this.flags.fPowered = false;
this.flags.powered = false;
this.setMachineParms(parmsMachine);
@ -182,7 +182,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
Component.error("Unable to find CPU component");
return;
}
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
this.dbg = /** @type {DebuggerX86} */ (Component.getComponentByType("Debugger", this.id));
/*
* Enumerate all Video components for future updateVideo() calls.
@ -218,7 +218,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
}
}
this.println(PCX86.APPNAME + " v" + PCX86.APPVERSION + "\n" + COPYRIGHT + "\n" + LICENSE);
this.println(PCX86.APPNAME + " v" + (XMLVERSION || PCX86.APPVERSION) + "\n" + COPYRIGHT + "\n" + LICENSE);
if (DEBUG && this.messageEnabled()) this.printMessage("PREFETCH: " + PREFETCH + ", TYPEDARRAYS: " + TYPEDARRAYS);
@ -655,9 +655,9 @@ Computer.prototype.powerOn = function(resume)
*/
Computer.prototype.powerRestore = function(component, stateComputer, fRepower, fRestore)
{
if (!component.flags.fPowered) {
if (!component.flags.powered) {
component.flags.fPowered = true;
component.flags.powered = true;
if (component.powerUp) {
@ -760,12 +760,12 @@ Computer.prototype.donePowerOn = function(aParms)
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
if (DEBUG && this.flags.fPowered && this.messageEnabled()) {
if (DEBUG && this.flags.powered && this.messageEnabled()) {
this.printMessage("Computer.donePowerOn(): redundant");
}
this.fInitialized = true;
this.flags.fPowered = true;
this.flags.powered = true;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Shutdown";
@ -806,22 +806,22 @@ Computer.prototype.donePowerOn = function(aParms)
*/
Computer.prototype.checkPower = function()
{
if (this.flags.fPowered) return true;
if (this.flags.powered) return true;
var component = null, iComponent;
var aComponents = Component.getComponents(this.id);
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fReady) break;
if (component !== this && !component.flags.ready) break;
}
if (iComponent == aComponents.length) {
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
if (component !== this && !component.flags.fPowered) break;
if (component !== this && !component.flags.powered) break;
}
}
if (iComponent == aComponents.length) component = this;
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.fReady? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
var s = "The " + component.type + " component (" + component.id + ") is not " + (!component.flags.ready? "ready yet" + (component.fnReady? " (waiting for notification)" : "") : "powered yet") + ".";
web.alertUser(s);
return false;
};
@ -903,7 +903,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
data = this.cpu.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(this.cpu.id, data);
if (fShutdown) {
this.cpu.flags.fPowered = false;
this.cpu.flags.powered = false;
if (data === false) sState = null;
}
}
@ -911,13 +911,13 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
if (component.flags.fPowered) {
if (component.flags.powered) {
if (component.powerDown) {
data = component.powerDown(fSave, fShutdown);
if (typeof data === "object") stateComputer.set(component.id, data);
}
if (fShutdown) {
component.flags.fPowered = false;
component.flags.powered = false;
if (data === false) sState = null;
}
}
@ -970,7 +970,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
}
if (fShutdown) {
this.flags.fPowered = false;
this.flags.powered = false;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Power";
}
@ -1341,7 +1341,7 @@ Computer.prototype.storeServerState = function(sUserID, sState, fSync)
Computer.prototype.onPower = function()
{
if (!this.nPowerChange) {
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.wait(this.powerOn);
} else {
this.powerOff(false, true);
@ -1362,7 +1362,7 @@ Computer.prototype.onReset = function()
* I'm going to start with the presumption that it makes little sense for an "unpowered" computer to be "reset";
* ditto if the power state is currently being changed.
*/
if (!this.flags.fPowered || this.nPowerChange) return;
if (!this.flags.powered || this.nPowerChange) return;
/*
* If this is a "resumable" machine (and it's not using a predefined state), then we overload the reset
@ -1425,7 +1425,9 @@ Computer.prototype.getMachineComponent = function(sType, componentPrev)
}
if (component.type == sType) return component;
}
if (!componentLast) Component.log("Machine component type '" + sType + "' not found", "warning");
if (!componentLast && sType != "FPU") {
Component.log("Machine component type '" + sType + "' not found", "warning");
}
return null;
};
@ -1549,7 +1551,7 @@ Computer.init = function()
var computer = new Computer(parmsComputer, parmsMachine, true);
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onInit(" + computer.flags.fPowered + ")");
computer.printMessage("onInit(" + computer.flags.powered + ")");
}
/*
@ -1586,10 +1588,10 @@ Computer.show = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.fPowered + ")");
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.powered + ")");
}
if (computer.fInitialized && !computer.flags.fPowered) {
if (computer.fInitialized && !computer.flags.powered) {
/**
* Repower the computer, notifying every component to continue running as-is.
*/
@ -1635,10 +1637,10 @@ Computer.exit = function()
if (computer) {
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onExit(" + computer.flags.fPowered + ")");
computer.printMessage("onExit(" + computer.flags.powered + ")");
}
if (computer.flags.fPowered) {
if (computer.flags.powered) {
/**
* Power off the computer, giving every component an opportunity to save its state,
* but only if 'resume' has been set AND there is no valid resume path (because if a valid resume

View file

@ -97,14 +97,14 @@ function CPU(parmsCPU, nCyclesDefault)
/*
* We add a number of flags to the set initialized by Component
*/
this.flags.fRunning = false;
this.flags.fStarting = false;
this.flags.fAutoStart = parmsCPU['autoStart'];
this.flags.running = false;
this.flags.starting = false;
this.flags.autoStart = parmsCPU['autoStart'];
/*
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both)
*/
this.flags.fDisplayLiveRegs = false;
this.flags.displayLiveRegs = false;
/*
* Get checksum parameters, if any. runCPU() behavior is not affected until fChecksum
@ -115,7 +115,7 @@ function CPU(parmsCPU, nCyclesDefault)
* command ("x"); for example, "x cs int 5000" will set nCyclesChecksumInterval to 5000
* and call resetChecksum().
*/
this.flags.fChecksum = false;
this.flags.checksum = false;
this.aCounts.nChecksum = this.aCounts.nCyclesChecksumNext = 0;
this.aCounts.nCyclesChecksumStart = parmsCPU["csStart"];
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
@ -159,7 +159,7 @@ CPU.BUTTONS = ["power", "reset"];
* @param {Computer} cmp
* @param {Bus} bus
* @param {CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{
@ -187,7 +187,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
var sAutoStart = cmp.getMachineParm('autoStart');
if (sAutoStart != null) {
this.flags.fAutoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
this.flags.autoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
}
this.setReady();
@ -271,7 +271,7 @@ CPU.prototype.powerUp = function(data, fRepower)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = true;
* this.flags.powered = true;
*/
this.updateCPU();
return true;
@ -291,7 +291,7 @@ CPU.prototype.powerDown = function(fSave, fShutdown)
* The Computer component (which is responsible for all powerDown and powerUp notifications)
* is now responsible for managing a component's fPowered flag, not us.
*
* this.flags.fPowered = false;
* this.flags.powered = false;
*/
return fSave? this.save() : true;
};
@ -307,7 +307,7 @@ CPU.prototype.autoStart = function()
/*
* Start running automatically on power-up, assuming there's no Debugger and no "Run" button
*/
if (this.flags.fAutoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/*
* Now we ALSO set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" context,
* a machine without focus is like a day without sunshine.
@ -326,7 +326,7 @@ CPU.prototype.autoStart = function()
*/
CPU.prototype.isPowered = function()
{
if (!this.flags.fPowered) {
if (!this.flags.powered) {
this.println(this.toString() + " not powered");
return false;
}
@ -341,7 +341,7 @@ CPU.prototype.isPowered = function()
*/
CPU.prototype.isRunning = function()
{
return this.flags.fRunning;
return this.flags.running;
};
/**
@ -372,8 +372,8 @@ CPU.prototype.resetChecksum = function()
if (this.aCounts.nCyclesChecksumStart === undefined) this.aCounts.nCyclesChecksumStart = 0;
if (this.aCounts.nCyclesChecksumInterval === undefined) this.aCounts.nCyclesChecksumInterval = -1;
if (this.aCounts.nCyclesChecksumStop === undefined) this.aCounts.nCyclesChecksumStop = -1;
this.flags.fChecksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
if (this.flags.fChecksum) {
this.flags.checksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
if (this.flags.checksum) {
this.aCounts.nChecksum = 0;
this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart - this.nTotalCycles;
/*
@ -398,7 +398,7 @@ CPU.prototype.resetChecksum = function()
*/
CPU.prototype.updateChecksum = function(nCycles)
{
if (this.flags.fChecksum) {
if (this.flags.checksum) {
/*
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
*/
@ -454,7 +454,7 @@ CPU.prototype.displayValue = function(sLabel, nValue, cch)
this.stopCPU();
}
var sVal;
if (!this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (!this.flags.running || this.flags.displayLiveRegs) {
sVal = str.toHex(nValue, cch);
} else {
sVal = "--------".substr(0, cch);
@ -500,7 +500,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
this.bindings[sBinding] = control;
control.onclick = function onClickRun() {
if (!cpu.cmp || !cpu.cmp.checkPower()) return;
if (!cpu.flags.fRunning)
if (!cpu.flags.running)
cpu.runCPU(true);
else
cpu.stopCPU(true);
@ -541,7 +541,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
CPU.prototype.setBurstCycles = function(nCycles)
{
if (this.flags.fRunning) {
if (this.flags.running) {
var nDelta = this.nStepCycles - nCycles;
/*
* NOTE: If nDelta is negative, we will actually be increasing nStepCycles and nBurstCycles.
@ -731,7 +731,7 @@ CPU.prototype.getSpeedCurrent = function()
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return ((this.flags.fRunning && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
return ((this.flags.running && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
};
/**
@ -917,6 +917,14 @@ CPU.prototype.calcRemainingTime = function()
this.calcSpeed(nCycles, msElapsed);
if (msRemainsThisRun < 0 || this.aCounts.mhz < this.aCounts.mhzTarget) {
/*
* Try "throwing out" the effects of large anomalies, by moving the overall run start time up;
* ordinarily, this should only happen when the someone is using an external Debugger or some other
* tool or feature that is interfering with our overall execution.
*/
if (msRemainsThisRun < -1000) {
this.aCounts.msStartRun -= msRemainsThisRun;
}
/*
* If the last burst took MORE time than we allotted (ie, it's taking more than 1 second to simulate
* nCyclesPerSecond), all we can do is yield for as little time as possible (ie, 0ms) and hope that the
@ -939,6 +947,17 @@ CPU.prototype.calcRemainingTime = function()
return msRemainsThisRun;
};
/**
* endBurst()
*
* @this {CPU}
*/
CPU.prototype.endBurst = function()
{
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
};
/**
* runCPU(fUpdateFocus)
*
@ -962,7 +981,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
this.calcStartTime();
try {
do {
var nCyclesPerBurst = (this.flags.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
var nCyclesPerBurst = (this.flags.checksum? 1 : this.aCounts.nCyclesPerBurst);
if (this.chipset) {
this.chipset.updateAllTimers();
@ -1018,7 +1037,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
break;
}
} while (this.flags.fRunning);
} while (this.flags.running);
}
catch (e) {
this.stopCPU();
@ -1040,7 +1059,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
*/
CPU.prototype.startCPU = function(fUpdateFocus)
{
if (!this.flags.fRunning) {
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
@ -1049,8 +1068,8 @@ CPU.prototype.startCPU = function(fUpdateFocus)
*/
this.setSpeed();
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.flags.fRunning = true;
this.flags.fStarting = true;
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
@ -1089,17 +1108,16 @@ CPU.prototype.stepCPU = function(nMinCycles)
CPU.prototype.stopCPU = function(fComplete)
{
this.isBusy(true);
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.flags.running) {
this.flags.running = false;
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
}
this.flags.fComplete = fComplete;
this.flags.complete = fComplete;
};
/**
@ -1131,9 +1149,8 @@ CPU.prototype.updateCPU = function(fForce)
*/
CPU.prototype.yieldCPU = function()
{
this.endBurst(); // this will break us out of stepCPU()
this.aCounts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0; // this will break us out of stepCPU()
// if (DEBUG) this.nSnapCycles = this.nBurstCycles;
/*
* The Debugger calls yieldCPU() after every message() to ensure browser responsiveness, but it looks

File diff suppressed because it is too large Load diff

View file

@ -34,12 +34,12 @@
/**
* @define {string}
*/
var APPCLASS = "pcx86"; // this @define is the default application class (eg, "pcx86", "c1pjs")
var APPCLASS = "pcx86"; // this @define is the default application class (eg, "pcx86", "c1pjs")
/**
* @define {string}
*/
var APPNAME = "PCx86"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
var APPNAME = "PCx86"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
/**
* @define {boolean}

View file

@ -811,7 +811,7 @@ var SectorInfo;
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
Disk.prototype.initBus = function(cmp, bus, cpu, dbg) {
this.dbg = dbg;
@ -1166,7 +1166,7 @@ Disk.prototype.doneLoad = function(sURL, sDiskData, nErrorCode)
{
var disk = null;
this.fWriteProtected = false;
var fPrintOnly = (nErrorCode < 0 && this.cmp && !this.cmp.flags.fPowered);
var fPrintOnly = (nErrorCode < 0 && this.cmp && !this.cmp.flags.powered);
if (this.fOnDemand) {
if (!nErrorCode) {
@ -1594,11 +1594,11 @@ Disk.prototype.buildFileTable = function()
* @this {Disk}
* @param {string} sModule
* @param {number} nSegment
* @return {Array}
* @return {Object}
*/
Disk.prototype.getModuleInfo = function(sModule, nSegment)
{
var aSymbols = [];
var aSymbols = {};
if (SYMBOLS && this.aFileTable) {
for (var iFile = 0; iFile < this.aFileTable.length; iFile++) {
var file = this.aFileTable[iFile];

View file

@ -623,7 +623,7 @@ FDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -594,7 +594,7 @@ HDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -31,22 +31,6 @@
"use strict";
/*
* Components that previously used Debugger interrupt definitions by including:
*
* var Debugger = require("./debugger");
*
* and using:
*
* Debugger.INT.FOO
*
* must now instead include:
*
* var Interrupts = require("./interrupts");
*
* and then replace all occurrences of "Debugger.INT.FOO" with "Interrupts.FOO".
*/
var Interrupts = {
VIDEO: 0x10,
DISK: 0x13,
@ -184,7 +168,7 @@ if (DEBUGGER) {
0x4A8: ["SAVE_PTR",4] // POINTER TO EGA PARAMETER CONTROL BLOCK
},
/*
* See Debugger.prototype.replaceRegs() for the rules governing how register contents are replaced in the strings below.
* See DebuggerX86.prototype.replaceRegs() for the rules governing how register contents are replaced in the strings below.
*
* Replacements occur in the following order:
*
@ -1546,7 +1530,7 @@ if (DEBUGGER) {
* INT 2E Execute command (*) (2.0+)
* Entry: DS:SI-&gt;command string-1 followed by a carriage return
* Exit: All registers except CS and IP are destroyed
* Note: This function is not re-enterant and should not be issued from a
* Note: This function is not re-entrant and should not be issued from a
* batch file.
*
* INT 2F Multiplex (3.0+)
@ -1922,7 +1906,7 @@ if (DEBUGGER) {
*
* Stacks descriptor (8 bytes)
* 00 Flag (0=free,1=in use)
* 02 Savearea for SS:SP
* 02 Save area for SS:SP
* 06 Offset of end of stack
*
* Device driver header (12h bytes)

View file

@ -35,6 +35,7 @@ if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Keys = require("../../shared/lib/keys");
var State = require("../../shared/lib/state");
var PCX86 = require("./defines");
var Messages = require("./messages");
@ -153,198 +154,15 @@ Component.subclass(Keyboard);
*/
Keyboard.MODELS = ["US83", "US84", "US101"];
/**
* Alphanumeric and other common (printable) ASCII codes.
*
* TODO: Determine what we can do to get ALL constants like these inlined (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}
*/
Keyboard.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 keys (eg, the LEFT arrow key), yet for some reason, browsers defined
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37). This conflict is
* discussed further in the definition of CLICKCODE below.
*
* @enum {number}
*/
Keyboard.KEYCODE = {
/* 0x08 */ BS: 8,
/* 0x09 */ TAB: 9,
/* 0x0A */ LF: 10,
/* 0x0D */ CR: 13,
/* 0x10 */ SHIFT: 16,
/* 0x11 */ CTRL: 17,
/* 0x12 */ ALT: 18,
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
/* 0x14 */ CAPS_LOCK: 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,
/* 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_INS: 96, // 0
/* 0x60 */ FF_BQUOTE: 96,
/* 0x61 */ NUM_END: 97, // 1
/* 0x62 */ NUM_DOWN: 98, // 2
/* 0x63 */ NUM_PGDN: 99, // 3
/* 0x64 */ NUM_LEFT: 100, // 4
/* 0x65 */ NUM_CENTER: 101, // 5
/* 0x66 */ NUM_RIGHT: 102, // 6
/* 0x67 */ NUM_HOME: 103, // 7
/* 0x68 */ NUM_UP: 104, // 8
/* 0x69 */ NUM_PGUP: 105, // 9
/* 0x6A */ NUM_MUL: 106,
/* 0x6B */ NUM_ADD: 107,
/* 0x6D */ NUM_SUB: 109,
/* 0x6E */ NUM_DEL: 110, // .
/* 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
};
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keyboard.STUPID_KEYCODES = {};
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SEMI] = Keyboard.ASCII[';']; // 186 -> 59
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.EQUALS] = Keyboard.ASCII['=']; // 187 -> 61
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.COMMA] = Keyboard.ASCII[',']; // 188 -> 44
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.DASH] = Keyboard.ASCII['-']; // 189 -> 45
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.PERIOD] = Keyboard.ASCII['.']; // 190 -> 46
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SLASH] = Keyboard.ASCII['/']; // 191 -> 47
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['`']; // 192 -> 96
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.LBRACK] = Keyboard.ASCII['[']; // 219 -> 91
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BSLASH] = Keyboard.ASCII['\\']; // 220 -> 92
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.RBRACK] = Keyboard.ASCII[']']; // 221 -> 93
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.QUOTE] = Keyboard.ASCII["'"]; // 222 -> 39
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.FF_DASH] = Keyboard.ASCII['-'];
/*
* Maps unshifted keyCodes to their shifted counterparts; to be used when a shift-key is down.
* Alphabetic characters are handled in code, since they must also take CAPS_LOCK into consideration.
*/
Keyboard.SHIFTED_KEYCODES = {};
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['1']] = Keyboard.ASCII['!'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['2']] = Keyboard.ASCII['@'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['3']] = Keyboard.ASCII['#'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['4']] = Keyboard.ASCII['$'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['5']] = Keyboard.ASCII['%'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['6']] = Keyboard.ASCII['^'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['7']] = Keyboard.ASCII['&'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['8']] = Keyboard.ASCII['*'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['9']] = Keyboard.ASCII['('];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['0']] = Keyboard.ASCII[')'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.SEMI] = Keyboard.ASCII[':'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.EQUALS] = Keyboard.ASCII['+'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.COMMA] = Keyboard.ASCII['<'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.DASH] = Keyboard.ASCII['_'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.PERIOD] = Keyboard.ASCII['>'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.SLASH] = Keyboard.ASCII['?'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['~'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.LBRACK] = Keyboard.ASCII['{'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.BSLASH] = Keyboard.ASCII['|'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.RBRACK] = Keyboard.ASCII['}'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.QUOTE] = Keyboard.ASCII['"'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.FF_DASH] = Keyboard.ASCII['_'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.FF_EQUALS] = Keyboard.ASCII['+'];
Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.FF_SEMI] = Keyboard.ASCII[':'];
Keyboard.SIMCODE = {
BS: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.ONDOWN,
TAB: Keyboard.KEYCODE.TAB + Keyboard.KEYCODE.ONDOWN,
SHIFT: Keyboard.KEYCODE.SHIFT + Keyboard.KEYCODE.ONDOWN,
RSHIFT: Keyboard.KEYCODE.SHIFT + Keyboard.KEYCODE.ONDOWN + Keyboard.KEYCODE.ONRIGHT,
CTRL: Keyboard.KEYCODE.CTRL + Keyboard.KEYCODE.ONDOWN,
ALT: Keyboard.KEYCODE.ALT + Keyboard.KEYCODE.ONDOWN,
CAPS_LOCK: Keyboard.KEYCODE.CAPS_LOCK + Keyboard.KEYCODE.ONDOWN,
ESC: Keyboard.KEYCODE.ESC + Keyboard.KEYCODE.ONDOWN,
BS: Keys.KEYCODE.BS + Keys.KEYCODE.ONDOWN,
TAB: Keys.KEYCODE.TAB + Keys.KEYCODE.ONDOWN,
SHIFT: Keys.KEYCODE.SHIFT + Keys.KEYCODE.ONDOWN,
RSHIFT: Keys.KEYCODE.SHIFT + Keys.KEYCODE.ONDOWN + Keys.KEYCODE.ONRIGHT,
CTRL: Keys.KEYCODE.CTRL + Keys.KEYCODE.ONDOWN,
ALT: Keys.KEYCODE.ALT + Keys.KEYCODE.ONDOWN,
CAPS_LOCK: Keys.KEYCODE.CAPS_LOCK + Keys.KEYCODE.ONDOWN,
ESC: Keys.KEYCODE.ESC + Keys.KEYCODE.ONDOWN,
/*
* It seems that a recent change to Safari on iOS (first noticed in iOS 9.1) treats SPACE
* differently now, at least with regard to <textarea> controls, and possibly only readonly
@ -355,44 +173,44 @@ Keyboard.SIMCODE = {
* as an ONDOWN key, so that we can call preventDefault() and properly simulate the key at
* the time the key goes down.
*/
SPACE: Keyboard.KEYCODE.SPACE + Keyboard.KEYCODE.ONDOWN,
F1: Keyboard.KEYCODE.F1 + Keyboard.KEYCODE.ONDOWN,
F2: Keyboard.KEYCODE.F2 + Keyboard.KEYCODE.ONDOWN,
F3: Keyboard.KEYCODE.F3 + Keyboard.KEYCODE.ONDOWN,
F4: Keyboard.KEYCODE.F4 + Keyboard.KEYCODE.ONDOWN,
F5: Keyboard.KEYCODE.F5 + Keyboard.KEYCODE.ONDOWN,
F6: Keyboard.KEYCODE.F6 + Keyboard.KEYCODE.ONDOWN,
F7: Keyboard.KEYCODE.F7 + Keyboard.KEYCODE.ONDOWN,
F8: Keyboard.KEYCODE.F8 + Keyboard.KEYCODE.ONDOWN,
F9: Keyboard.KEYCODE.F9 + Keyboard.KEYCODE.ONDOWN,
F10: Keyboard.KEYCODE.F10 + Keyboard.KEYCODE.ONDOWN,
F11: Keyboard.KEYCODE.F11 + Keyboard.KEYCODE.ONDOWN,
F12: Keyboard.KEYCODE.F12 + Keyboard.KEYCODE.ONDOWN,
NUM_LOCK: Keyboard.KEYCODE.NUM_LOCK + Keyboard.KEYCODE.ONDOWN,
SCROLL_LOCK: Keyboard.KEYCODE.SCROLL_LOCK + Keyboard.KEYCODE.ONDOWN,
PRTSC: Keyboard.KEYCODE.PRTSC + Keyboard.KEYCODE.ONDOWN,
HOME: Keyboard.KEYCODE.HOME + Keyboard.KEYCODE.ONDOWN,
UP: Keyboard.KEYCODE.UP + Keyboard.KEYCODE.ONDOWN,
PGUP: Keyboard.KEYCODE.PGUP + Keyboard.KEYCODE.ONDOWN,
NUM_SUB: Keyboard.KEYCODE.NUM_SUB + Keyboard.KEYCODE.ONDOWN,
LEFT: Keyboard.KEYCODE.LEFT + Keyboard.KEYCODE.ONDOWN,
NUM_CENTER: Keyboard.KEYCODE.NUM_CENTER + Keyboard.KEYCODE.ONDOWN,
RIGHT: Keyboard.KEYCODE.RIGHT + Keyboard.KEYCODE.ONDOWN,
NUM_ADD: Keyboard.KEYCODE.NUM_ADD + Keyboard.KEYCODE.ONDOWN,
END: Keyboard.KEYCODE.END + Keyboard.KEYCODE.ONDOWN,
DOWN: Keyboard.KEYCODE.DOWN + Keyboard.KEYCODE.ONDOWN,
PGDN: Keyboard.KEYCODE.PGDN + Keyboard.KEYCODE.ONDOWN,
INS: Keyboard.KEYCODE.INS + Keyboard.KEYCODE.ONDOWN,
DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.ONDOWN,
CMD: Keyboard.KEYCODE.CMD + Keyboard.KEYCODE.ONDOWN,
RCMD: Keyboard.KEYCODE.RCMD + Keyboard.KEYCODE.ONDOWN,
FF_CMD: Keyboard.KEYCODE.FF_CMD + Keyboard.KEYCODE.ONDOWN,
SYSREQ: Keyboard.KEYCODE.ESC + Keyboard.KEYCODE.FAKE,
CTRL_C: Keyboard.ASCII.CTRL_C + Keyboard.KEYCODE.FAKE,
CTRL_BREAK: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.FAKE,
CTRL_ALT_DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.FAKE,
CTRL_ALT_INS: Keyboard.KEYCODE.INS + Keyboard.KEYCODE.FAKE,
CTRL_ALT_ENTER: Keyboard.KEYCODE.CR + Keyboard.KEYCODE.FAKE
SPACE: Keys.KEYCODE.SPACE + Keys.KEYCODE.ONDOWN,
F1: Keys.KEYCODE.F1 + Keys.KEYCODE.ONDOWN,
F2: Keys.KEYCODE.F2 + Keys.KEYCODE.ONDOWN,
F3: Keys.KEYCODE.F3 + Keys.KEYCODE.ONDOWN,
F4: Keys.KEYCODE.F4 + Keys.KEYCODE.ONDOWN,
F5: Keys.KEYCODE.F5 + Keys.KEYCODE.ONDOWN,
F6: Keys.KEYCODE.F6 + Keys.KEYCODE.ONDOWN,
F7: Keys.KEYCODE.F7 + Keys.KEYCODE.ONDOWN,
F8: Keys.KEYCODE.F8 + Keys.KEYCODE.ONDOWN,
F9: Keys.KEYCODE.F9 + Keys.KEYCODE.ONDOWN,
F10: Keys.KEYCODE.F10 + Keys.KEYCODE.ONDOWN,
F11: Keys.KEYCODE.F11 + Keys.KEYCODE.ONDOWN,
F12: Keys.KEYCODE.F12 + Keys.KEYCODE.ONDOWN,
NUM_LOCK: Keys.KEYCODE.NUM_LOCK + Keys.KEYCODE.ONDOWN,
SCROLL_LOCK: Keys.KEYCODE.SCROLL_LOCK + Keys.KEYCODE.ONDOWN,
PRTSC: Keys.KEYCODE.PRTSC + Keys.KEYCODE.ONDOWN,
HOME: Keys.KEYCODE.HOME + Keys.KEYCODE.ONDOWN,
UP: Keys.KEYCODE.UP + Keys.KEYCODE.ONDOWN,
PGUP: Keys.KEYCODE.PGUP + Keys.KEYCODE.ONDOWN,
NUM_SUB: Keys.KEYCODE.NUM_SUB + Keys.KEYCODE.ONDOWN,
LEFT: Keys.KEYCODE.LEFT + Keys.KEYCODE.ONDOWN,
NUM_CENTER: Keys.KEYCODE.NUM_CENTER + Keys.KEYCODE.ONDOWN,
RIGHT: Keys.KEYCODE.RIGHT + Keys.KEYCODE.ONDOWN,
NUM_ADD: Keys.KEYCODE.NUM_ADD + Keys.KEYCODE.ONDOWN,
END: Keys.KEYCODE.END + Keys.KEYCODE.ONDOWN,
DOWN: Keys.KEYCODE.DOWN + Keys.KEYCODE.ONDOWN,
PGDN: Keys.KEYCODE.PGDN + Keys.KEYCODE.ONDOWN,
INS: Keys.KEYCODE.INS + Keys.KEYCODE.ONDOWN,
DEL: Keys.KEYCODE.DEL + Keys.KEYCODE.ONDOWN,
CMD: Keys.KEYCODE.CMD + Keys.KEYCODE.ONDOWN,
RCMD: Keys.KEYCODE.RCMD + Keys.KEYCODE.ONDOWN,
FF_CMD: Keys.KEYCODE.FF_CMD + Keys.KEYCODE.ONDOWN,
SYSREQ: Keys.KEYCODE.ESC + Keys.KEYCODE.FAKE,
CTRL_C: Keys.ASCII.CTRL_C + Keys.KEYCODE.FAKE,
CTRL_BREAK: Keys.KEYCODE.BS + Keys.KEYCODE.FAKE,
CTRL_ALT_DEL: Keys.KEYCODE.DEL + Keys.KEYCODE.FAKE,
CTRL_ALT_INS: Keys.KEYCODE.INS + Keys.KEYCODE.FAKE,
CTRL_ALT_ENTER: Keys.KEYCODE.CR + Keys.KEYCODE.FAKE
};
/*
@ -495,18 +313,6 @@ Keyboard.SCANCODE = {
/* 0xE1 */ EXTEND2: 225
};
/**
* The set of values that a browser may store in the 'location' property of a keyboard event object
* which we also support.
*
* @enum {number}
*/
Keyboard.LOCATION = {
LEFT: 1,
RIGHT: 2,
NUMPAD: 3
};
/**
* These internal "shift key" states are used to indicate BOTH the physical shift-key states (in bitsState)
* and the simulated shift-key states (in bitsStateSim). The LOCK keys are problematic in both cases: the
@ -646,58 +452,58 @@ Keyboard.CLICKCODES = {
*/
Keyboard.SOFTCODES = {
/* 1 */ 'esc': Keyboard.SIMCODE.ESC,
/* 2 */ '1': Keyboard.ASCII['1'],
/* 3 */ '2': Keyboard.ASCII['2'],
/* 4 */ '3': Keyboard.ASCII['3'],
/* 5 */ '4': Keyboard.ASCII['4'],
/* 6 */ '5': Keyboard.ASCII['5'],
/* 7 */ '6': Keyboard.ASCII['6'],
/* 8 */ '7': Keyboard.ASCII['7'],
/* 9 */ '8': Keyboard.ASCII['8'],
/* 10 */ '9': Keyboard.ASCII['9'],
/* 11 */ '0': Keyboard.ASCII['0'],
/* 12 */ '-': Keyboard.ASCII['-'],
/* 13 */ '=': Keyboard.ASCII['='],
/* 2 */ '1': Keys.ASCII['1'],
/* 3 */ '2': Keys.ASCII['2'],
/* 4 */ '3': Keys.ASCII['3'],
/* 5 */ '4': Keys.ASCII['4'],
/* 6 */ '5': Keys.ASCII['5'],
/* 7 */ '6': Keys.ASCII['6'],
/* 8 */ '7': Keys.ASCII['7'],
/* 9 */ '8': Keys.ASCII['8'],
/* 10 */ '9': Keys.ASCII['9'],
/* 11 */ '0': Keys.ASCII['0'],
/* 12 */ '-': Keys.ASCII['-'],
/* 13 */ '=': Keys.ASCII['='],
/* 14 */ 'bs': Keyboard.SIMCODE.BS,
/* 15 */ 'tab': Keyboard.SIMCODE.TAB,
/* 16 */ 'q': Keyboard.ASCII.Q,
/* 17 */ 'w': Keyboard.ASCII.W,
/* 18 */ 'e': Keyboard.ASCII.E,
/* 19 */ 'r': Keyboard.ASCII.R,
/* 20 */ 't': Keyboard.ASCII.T,
/* 21 */ 'y': Keyboard.ASCII.Y,
/* 22 */ 'u': Keyboard.ASCII.U,
/* 23 */ 'i': Keyboard.ASCII.I,
/* 24 */ 'o': Keyboard.ASCII.O,
/* 25 */ 'p': Keyboard.ASCII.P,
/* 26 */ '[': Keyboard.ASCII['['],
/* 27 */ ']': Keyboard.ASCII[']'],
/* 28 */ 'enter': Keyboard.KEYCODE.CR,
/* 16 */ 'q': Keys.ASCII.Q,
/* 17 */ 'w': Keys.ASCII.W,
/* 18 */ 'e': Keys.ASCII.E,
/* 19 */ 'r': Keys.ASCII.R,
/* 20 */ 't': Keys.ASCII.T,
/* 21 */ 'y': Keys.ASCII.Y,
/* 22 */ 'u': Keys.ASCII.U,
/* 23 */ 'i': Keys.ASCII.I,
/* 24 */ 'o': Keys.ASCII.O,
/* 25 */ 'p': Keys.ASCII.P,
/* 26 */ '[': Keys.ASCII['['],
/* 27 */ ']': Keys.ASCII[']'],
/* 28 */ 'enter': Keys.KEYCODE.CR,
/* 29 */ 'ctrl': Keyboard.SIMCODE.CTRL,
/* 30 */ 'a': Keyboard.ASCII.A,
/* 31 */ 's': Keyboard.ASCII.S,
/* 32 */ 'd': Keyboard.ASCII.D,
/* 33 */ 'f': Keyboard.ASCII.F,
/* 34 */ 'g': Keyboard.ASCII.G,
/* 35 */ 'h': Keyboard.ASCII.H,
/* 36 */ 'j': Keyboard.ASCII.J,
/* 37 */ 'k': Keyboard.ASCII.K,
/* 38 */ 'l': Keyboard.ASCII.L,
/* 39 */ ';': Keyboard.ASCII[';'],
/* 40 */ 'quote': Keyboard.ASCII["'"], // formerly "squote"
/* 41 */ '`': Keyboard.ASCII['`'], // formerly "bquote"
/* 30 */ 'a': Keys.ASCII.A,
/* 31 */ 's': Keys.ASCII.S,
/* 32 */ 'd': Keys.ASCII.D,
/* 33 */ 'f': Keys.ASCII.F,
/* 34 */ 'g': Keys.ASCII.G,
/* 35 */ 'h': Keys.ASCII.H,
/* 36 */ 'j': Keys.ASCII.J,
/* 37 */ 'k': Keys.ASCII.K,
/* 38 */ 'l': Keys.ASCII.L,
/* 39 */ ';': Keys.ASCII[';'],
/* 40 */ 'quote': Keys.ASCII["'"], // formerly "squote"
/* 41 */ '`': Keys.ASCII['`'], // formerly "bquote"
/* 42 */ 'shift': Keyboard.SIMCODE.SHIFT, // formerly "lshift"
/* 43 */ '\\': Keyboard.ASCII['\\'], // formerly "bslash"
/* 44 */ 'z': Keyboard.ASCII.Z,
/* 45 */ 'x': Keyboard.ASCII.X,
/* 46 */ 'c': Keyboard.ASCII.C,
/* 47 */ 'v': Keyboard.ASCII.V,
/* 48 */ 'b': Keyboard.ASCII.B,
/* 49 */ 'n': Keyboard.ASCII.N,
/* 50 */ 'm': Keyboard.ASCII.M,
/* 51 */ ',': Keyboard.ASCII[','],
/* 52 */ '.': Keyboard.ASCII['.'],
/* 53 */ '/': Keyboard.ASCII['/'],
/* 43 */ '\\': Keys.ASCII['\\'], // formerly "bslash"
/* 44 */ 'z': Keys.ASCII.Z,
/* 45 */ 'x': Keys.ASCII.X,
/* 46 */ 'c': Keys.ASCII.C,
/* 47 */ 'v': Keys.ASCII.V,
/* 48 */ 'b': Keys.ASCII.B,
/* 49 */ 'n': Keys.ASCII.N,
/* 50 */ 'm': Keys.ASCII.M,
/* 51 */ ',': Keys.ASCII[','],
/* 52 */ '.': Keys.ASCII['.'],
/* 53 */ '/': Keys.ASCII['/'],
/* 54 */ 'right-shift': Keyboard.SIMCODE.RSHIFT, // formerly "rshift"
/* 55 */ 'prtsc': Keyboard.SIMCODE.PRTSC, // unshifted '*'; becomes dedicated 'Print Screen' key on 101-key keyboards
/* 56 */ 'alt': Keyboard.SIMCODE.ALT,
@ -783,137 +589,137 @@ Keyboard.LEDSTATES = {
* @enum {number}
*/
Keyboard.SIMCODES = {};
Keyboard.SIMCODES[Keyboard.SIMCODE.ESC] = Keyboard.SCANCODE.ESC;
Keyboard.SIMCODES[Keyboard.ASCII['1']] = Keyboard.SCANCODE.ONE;
Keyboard.SIMCODES[Keyboard.ASCII['!']] = Keyboard.SCANCODE.ONE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['2']] = Keyboard.SCANCODE.TWO;
Keyboard.SIMCODES[Keyboard.ASCII['@']] = Keyboard.SCANCODE.TWO | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['3']] = Keyboard.SCANCODE.THREE;
Keyboard.SIMCODES[Keyboard.ASCII['#']] = Keyboard.SCANCODE.THREE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['4']] = Keyboard.SCANCODE.FOUR;
Keyboard.SIMCODES[Keyboard.ASCII['$']] = Keyboard.SCANCODE.FOUR | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['5']] = Keyboard.SCANCODE.FIVE;
Keyboard.SIMCODES[Keyboard.ASCII['%']] = Keyboard.SCANCODE.FIVE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['6']] = Keyboard.SCANCODE.SIX;
Keyboard.SIMCODES[Keyboard.ASCII['^']] = Keyboard.SCANCODE.SIX | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['7']] = Keyboard.SCANCODE.SEVEN;
Keyboard.SIMCODES[Keyboard.ASCII['&']] = Keyboard.SCANCODE.SEVEN | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['8']] = Keyboard.SCANCODE.EIGHT;
Keyboard.SIMCODES[Keyboard.ASCII['*']] = Keyboard.SCANCODE.EIGHT | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['9']] = Keyboard.SCANCODE.NINE;
Keyboard.SIMCODES[Keyboard.ASCII['(']] = Keyboard.SCANCODE.NINE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['0']] = Keyboard.SCANCODE.ZERO;
Keyboard.SIMCODES[Keyboard.ASCII[')']] = Keyboard.SCANCODE.ZERO | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['-']] = Keyboard.SCANCODE.DASH;
Keyboard.SIMCODES[Keyboard.ASCII['_']] = Keyboard.SCANCODE.DASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['=']] = Keyboard.SCANCODE.EQUALS;
Keyboard.SIMCODES[Keyboard.ASCII['+']] = Keyboard.SCANCODE.EQUALS | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.BS] = Keyboard.SCANCODE.BS;
Keyboard.SIMCODES[Keyboard.SIMCODE.TAB] = Keyboard.SCANCODE.TAB;
Keyboard.SIMCODES[Keyboard.ASCII.q] = Keyboard.SCANCODE.Q;
Keyboard.SIMCODES[Keyboard.ASCII.Q] = Keyboard.SCANCODE.Q | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.w] = Keyboard.SCANCODE.W;
Keyboard.SIMCODES[Keyboard.ASCII.W] = Keyboard.SCANCODE.W | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.e] = Keyboard.SCANCODE.E;
Keyboard.SIMCODES[Keyboard.ASCII.E] = Keyboard.SCANCODE.E | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.r] = Keyboard.SCANCODE.R;
Keyboard.SIMCODES[Keyboard.ASCII.R] = Keyboard.SCANCODE.R | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.t] = Keyboard.SCANCODE.T;
Keyboard.SIMCODES[Keyboard.ASCII.T] = Keyboard.SCANCODE.T | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.y] = Keyboard.SCANCODE.Y;
Keyboard.SIMCODES[Keyboard.ASCII.Y] = Keyboard.SCANCODE.Y | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.u] = Keyboard.SCANCODE.U;
Keyboard.SIMCODES[Keyboard.ASCII.U] = Keyboard.SCANCODE.U | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.i] = Keyboard.SCANCODE.I;
Keyboard.SIMCODES[Keyboard.ASCII.I] = Keyboard.SCANCODE.I | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.o] = Keyboard.SCANCODE.O;
Keyboard.SIMCODES[Keyboard.ASCII.O] = Keyboard.SCANCODE.O | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.p] = Keyboard.SCANCODE.P;
Keyboard.SIMCODES[Keyboard.ASCII.P] = Keyboard.SCANCODE.P | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['[']] = Keyboard.SCANCODE.LBRACK;
Keyboard.SIMCODES[Keyboard.ASCII['{']] = Keyboard.SCANCODE.LBRACK | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII[']']] = Keyboard.SCANCODE.RBRACK;
Keyboard.SIMCODES[Keyboard.ASCII['}']] = Keyboard.SCANCODE.RBRACK | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.KEYCODE.CR] = Keyboard.SCANCODE.ENTER;
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL] = Keyboard.SCANCODE.CTRL;
Keyboard.SIMCODES[Keyboard.ASCII.a] = Keyboard.SCANCODE.A;
Keyboard.SIMCODES[Keyboard.ASCII.A] = Keyboard.SCANCODE.A | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.s] = Keyboard.SCANCODE.S;
Keyboard.SIMCODES[Keyboard.ASCII.S] = Keyboard.SCANCODE.S | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.d] = Keyboard.SCANCODE.D;
Keyboard.SIMCODES[Keyboard.ASCII.D] = Keyboard.SCANCODE.D | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.f] = Keyboard.SCANCODE.F;
Keyboard.SIMCODES[Keyboard.ASCII.F] = Keyboard.SCANCODE.F | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.g] = Keyboard.SCANCODE.G;
Keyboard.SIMCODES[Keyboard.ASCII.G] = Keyboard.SCANCODE.G | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.h] = Keyboard.SCANCODE.H;
Keyboard.SIMCODES[Keyboard.ASCII.H] = Keyboard.SCANCODE.H | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.j] = Keyboard.SCANCODE.J;
Keyboard.SIMCODES[Keyboard.ASCII.J] = Keyboard.SCANCODE.J | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.k] = Keyboard.SCANCODE.K;
Keyboard.SIMCODES[Keyboard.ASCII.K] = Keyboard.SCANCODE.K | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.l] = Keyboard.SCANCODE.L;
Keyboard.SIMCODES[Keyboard.ASCII.L] = Keyboard.SCANCODE.L | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII[';']] = Keyboard.SCANCODE.SEMI;
Keyboard.SIMCODES[Keyboard.ASCII[':']] = Keyboard.SCANCODE.SEMI | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII["'"]] = Keyboard.SCANCODE.QUOTE;
Keyboard.SIMCODES[Keyboard.ASCII['"']] = Keyboard.SCANCODE.QUOTE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['`']] = Keyboard.SCANCODE.BQUOTE;
Keyboard.SIMCODES[Keyboard.ASCII['~']] = Keyboard.SCANCODE.BQUOTE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.SHIFT] = Keyboard.SCANCODE.SHIFT;
Keyboard.SIMCODES[Keyboard.ASCII['\\']] = Keyboard.SCANCODE.BSLASH;
Keyboard.SIMCODES[Keyboard.ASCII['|']] = Keyboard.SCANCODE.BSLASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.z] = Keyboard.SCANCODE.Z;
Keyboard.SIMCODES[Keyboard.ASCII.Z] = Keyboard.SCANCODE.Z | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.x] = Keyboard.SCANCODE.X;
Keyboard.SIMCODES[Keyboard.ASCII.X] = Keyboard.SCANCODE.X | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.c] = Keyboard.SCANCODE.C;
Keyboard.SIMCODES[Keyboard.ASCII.C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.v] = Keyboard.SCANCODE.V;
Keyboard.SIMCODES[Keyboard.ASCII.V] = Keyboard.SCANCODE.V | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.b] = Keyboard.SCANCODE.B;
Keyboard.SIMCODES[Keyboard.ASCII.B] = Keyboard.SCANCODE.B | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.n] = Keyboard.SCANCODE.N;
Keyboard.SIMCODES[Keyboard.ASCII.N] = Keyboard.SCANCODE.N | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII.m] = Keyboard.SCANCODE.M;
Keyboard.SIMCODES[Keyboard.ASCII.M] = Keyboard.SCANCODE.M | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII[',']] = Keyboard.SCANCODE.COMMA;
Keyboard.SIMCODES[Keyboard.ASCII['<']] = Keyboard.SCANCODE.COMMA | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['.']] = Keyboard.SCANCODE.PERIOD;
Keyboard.SIMCODES[Keyboard.ASCII['>']] = Keyboard.SCANCODE.PERIOD | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.ASCII['/']] = Keyboard.SCANCODE.SLASH;
Keyboard.SIMCODES[Keyboard.ASCII['?']] = Keyboard.SCANCODE.SLASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.RSHIFT] = Keyboard.SCANCODE.RSHIFT;
Keyboard.SIMCODES[Keyboard.SIMCODE.PRTSC] = Keyboard.SCANCODE.PRTSC;
Keyboard.SIMCODES[Keyboard.SIMCODE.ALT] = Keyboard.SCANCODE.ALT;
Keyboard.SIMCODES[Keyboard.SIMCODE.SPACE] = Keyboard.SCANCODE.SPACE;
Keyboard.SIMCODES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.SCANCODE.CAPS_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.F1] = Keyboard.SCANCODE.F1;
Keyboard.SIMCODES[Keyboard.SIMCODE.F2] = Keyboard.SCANCODE.F2;
Keyboard.SIMCODES[Keyboard.SIMCODE.F3] = Keyboard.SCANCODE.F3;
Keyboard.SIMCODES[Keyboard.SIMCODE.F4] = Keyboard.SCANCODE.F4;
Keyboard.SIMCODES[Keyboard.SIMCODE.F5] = Keyboard.SCANCODE.F5;
Keyboard.SIMCODES[Keyboard.SIMCODE.F6] = Keyboard.SCANCODE.F6;
Keyboard.SIMCODES[Keyboard.SIMCODE.F7] = Keyboard.SCANCODE.F7;
Keyboard.SIMCODES[Keyboard.SIMCODE.F8] = Keyboard.SCANCODE.F8;
Keyboard.SIMCODES[Keyboard.SIMCODE.F9] = Keyboard.SCANCODE.F9;
Keyboard.SIMCODES[Keyboard.SIMCODE.F10] = Keyboard.SCANCODE.F10;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_LOCK] = Keyboard.SCANCODE.NUM_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.SCROLL_LOCK] = Keyboard.SCANCODE.SCROLL_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.HOME] = Keyboard.SCANCODE.NUM_HOME;
Keyboard.SIMCODES[Keyboard.SIMCODE.UP] = Keyboard.SCANCODE.NUM_UP;
Keyboard.SIMCODES[Keyboard.SIMCODE.PGUP] = Keyboard.SCANCODE.NUM_PGUP;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_SUB] = Keyboard.SCANCODE.NUM_SUB;
Keyboard.SIMCODES[Keyboard.SIMCODE.LEFT] = Keyboard.SCANCODE.NUM_LEFT;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_CENTER] = Keyboard.SCANCODE.NUM_CENTER;
Keyboard.SIMCODES[Keyboard.SIMCODE.RIGHT] = Keyboard.SCANCODE.NUM_RIGHT;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_ADD] = Keyboard.SCANCODE.NUM_ADD;
Keyboard.SIMCODES[Keyboard.SIMCODE.END] = Keyboard.SCANCODE.NUM_END;
Keyboard.SIMCODES[Keyboard.SIMCODE.DOWN] = Keyboard.SCANCODE.NUM_DOWN;
Keyboard.SIMCODES[Keyboard.SIMCODE.PGDN] = Keyboard.SCANCODE.NUM_PGDN;
Keyboard.SIMCODES[Keyboard.SIMCODE.INS] = Keyboard.SCANCODE.NUM_INS;
Keyboard.SIMCODES[Keyboard.SIMCODE.DEL] = Keyboard.SCANCODE.NUM_DEL;
Keyboard.SIMCODES[Keyboard.SIMCODE.SYSREQ] = Keyboard.SCANCODE.SYSREQ;
Keyboard.SIMCODES[Keyboard.SIMCODE.ESC] = Keyboard.SCANCODE.ESC;
Keyboard.SIMCODES[Keys.ASCII['1']] = Keyboard.SCANCODE.ONE;
Keyboard.SIMCODES[Keys.ASCII['!']] = Keyboard.SCANCODE.ONE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['2']] = Keyboard.SCANCODE.TWO;
Keyboard.SIMCODES[Keys.ASCII['@']] = Keyboard.SCANCODE.TWO | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['3']] = Keyboard.SCANCODE.THREE;
Keyboard.SIMCODES[Keys.ASCII['#']] = Keyboard.SCANCODE.THREE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['4']] = Keyboard.SCANCODE.FOUR;
Keyboard.SIMCODES[Keys.ASCII['$']] = Keyboard.SCANCODE.FOUR | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['5']] = Keyboard.SCANCODE.FIVE;
Keyboard.SIMCODES[Keys.ASCII['%']] = Keyboard.SCANCODE.FIVE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['6']] = Keyboard.SCANCODE.SIX;
Keyboard.SIMCODES[Keys.ASCII['^']] = Keyboard.SCANCODE.SIX | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['7']] = Keyboard.SCANCODE.SEVEN;
Keyboard.SIMCODES[Keys.ASCII['&']] = Keyboard.SCANCODE.SEVEN | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['8']] = Keyboard.SCANCODE.EIGHT;
Keyboard.SIMCODES[Keys.ASCII['*']] = Keyboard.SCANCODE.EIGHT | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['9']] = Keyboard.SCANCODE.NINE;
Keyboard.SIMCODES[Keys.ASCII['(']] = Keyboard.SCANCODE.NINE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['0']] = Keyboard.SCANCODE.ZERO;
Keyboard.SIMCODES[Keys.ASCII[')']] = Keyboard.SCANCODE.ZERO | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['-']] = Keyboard.SCANCODE.DASH;
Keyboard.SIMCODES[Keys.ASCII['_']] = Keyboard.SCANCODE.DASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['=']] = Keyboard.SCANCODE.EQUALS;
Keyboard.SIMCODES[Keys.ASCII['+']] = Keyboard.SCANCODE.EQUALS | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.BS] = Keyboard.SCANCODE.BS;
Keyboard.SIMCODES[Keyboard.SIMCODE.TAB] = Keyboard.SCANCODE.TAB;
Keyboard.SIMCODES[Keys.ASCII.q] = Keyboard.SCANCODE.Q;
Keyboard.SIMCODES[Keys.ASCII.Q] = Keyboard.SCANCODE.Q | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.w] = Keyboard.SCANCODE.W;
Keyboard.SIMCODES[Keys.ASCII.W] = Keyboard.SCANCODE.W | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.e] = Keyboard.SCANCODE.E;
Keyboard.SIMCODES[Keys.ASCII.E] = Keyboard.SCANCODE.E | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.r] = Keyboard.SCANCODE.R;
Keyboard.SIMCODES[Keys.ASCII.R] = Keyboard.SCANCODE.R | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.t] = Keyboard.SCANCODE.T;
Keyboard.SIMCODES[Keys.ASCII.T] = Keyboard.SCANCODE.T | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.y] = Keyboard.SCANCODE.Y;
Keyboard.SIMCODES[Keys.ASCII.Y] = Keyboard.SCANCODE.Y | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.u] = Keyboard.SCANCODE.U;
Keyboard.SIMCODES[Keys.ASCII.U] = Keyboard.SCANCODE.U | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.i] = Keyboard.SCANCODE.I;
Keyboard.SIMCODES[Keys.ASCII.I] = Keyboard.SCANCODE.I | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.o] = Keyboard.SCANCODE.O;
Keyboard.SIMCODES[Keys.ASCII.O] = Keyboard.SCANCODE.O | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.p] = Keyboard.SCANCODE.P;
Keyboard.SIMCODES[Keys.ASCII.P] = Keyboard.SCANCODE.P | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['[']] = Keyboard.SCANCODE.LBRACK;
Keyboard.SIMCODES[Keys.ASCII['{']] = Keyboard.SCANCODE.LBRACK | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII[']']] = Keyboard.SCANCODE.RBRACK;
Keyboard.SIMCODES[Keys.ASCII['}']] = Keyboard.SCANCODE.RBRACK | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.KEYCODE.CR] = Keyboard.SCANCODE.ENTER;
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL] = Keyboard.SCANCODE.CTRL;
Keyboard.SIMCODES[Keys.ASCII.a] = Keyboard.SCANCODE.A;
Keyboard.SIMCODES[Keys.ASCII.A] = Keyboard.SCANCODE.A | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.s] = Keyboard.SCANCODE.S;
Keyboard.SIMCODES[Keys.ASCII.S] = Keyboard.SCANCODE.S | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.d] = Keyboard.SCANCODE.D;
Keyboard.SIMCODES[Keys.ASCII.D] = Keyboard.SCANCODE.D | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.f] = Keyboard.SCANCODE.F;
Keyboard.SIMCODES[Keys.ASCII.F] = Keyboard.SCANCODE.F | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.g] = Keyboard.SCANCODE.G;
Keyboard.SIMCODES[Keys.ASCII.G] = Keyboard.SCANCODE.G | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.h] = Keyboard.SCANCODE.H;
Keyboard.SIMCODES[Keys.ASCII.H] = Keyboard.SCANCODE.H | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.j] = Keyboard.SCANCODE.J;
Keyboard.SIMCODES[Keys.ASCII.J] = Keyboard.SCANCODE.J | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.k] = Keyboard.SCANCODE.K;
Keyboard.SIMCODES[Keys.ASCII.K] = Keyboard.SCANCODE.K | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.l] = Keyboard.SCANCODE.L;
Keyboard.SIMCODES[Keys.ASCII.L] = Keyboard.SCANCODE.L | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII[';']] = Keyboard.SCANCODE.SEMI;
Keyboard.SIMCODES[Keys.ASCII[':']] = Keyboard.SCANCODE.SEMI | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII["'"]] = Keyboard.SCANCODE.QUOTE;
Keyboard.SIMCODES[Keys.ASCII['"']] = Keyboard.SCANCODE.QUOTE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['`']] = Keyboard.SCANCODE.BQUOTE;
Keyboard.SIMCODES[Keys.ASCII['~']] = Keyboard.SCANCODE.BQUOTE | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.SHIFT] = Keyboard.SCANCODE.SHIFT;
Keyboard.SIMCODES[Keys.ASCII['\\']] = Keyboard.SCANCODE.BSLASH;
Keyboard.SIMCODES[Keys.ASCII['|']] = Keyboard.SCANCODE.BSLASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.z] = Keyboard.SCANCODE.Z;
Keyboard.SIMCODES[Keys.ASCII.Z] = Keyboard.SCANCODE.Z | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.x] = Keyboard.SCANCODE.X;
Keyboard.SIMCODES[Keys.ASCII.X] = Keyboard.SCANCODE.X | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.c] = Keyboard.SCANCODE.C;
Keyboard.SIMCODES[Keys.ASCII.C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.v] = Keyboard.SCANCODE.V;
Keyboard.SIMCODES[Keys.ASCII.V] = Keyboard.SCANCODE.V | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.b] = Keyboard.SCANCODE.B;
Keyboard.SIMCODES[Keys.ASCII.B] = Keyboard.SCANCODE.B | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.n] = Keyboard.SCANCODE.N;
Keyboard.SIMCODES[Keys.ASCII.N] = Keyboard.SCANCODE.N | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII.m] = Keyboard.SCANCODE.M;
Keyboard.SIMCODES[Keys.ASCII.M] = Keyboard.SCANCODE.M | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII[',']] = Keyboard.SCANCODE.COMMA;
Keyboard.SIMCODES[Keys.ASCII['<']] = Keyboard.SCANCODE.COMMA | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['.']] = Keyboard.SCANCODE.PERIOD;
Keyboard.SIMCODES[Keys.ASCII['>']] = Keyboard.SCANCODE.PERIOD | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keys.ASCII['/']] = Keyboard.SCANCODE.SLASH;
Keyboard.SIMCODES[Keys.ASCII['?']] = Keyboard.SCANCODE.SLASH | (Keyboard.SCANCODE.SHIFT << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.RSHIFT] = Keyboard.SCANCODE.RSHIFT;
Keyboard.SIMCODES[Keyboard.SIMCODE.PRTSC] = Keyboard.SCANCODE.PRTSC;
Keyboard.SIMCODES[Keyboard.SIMCODE.ALT] = Keyboard.SCANCODE.ALT;
Keyboard.SIMCODES[Keyboard.SIMCODE.SPACE] = Keyboard.SCANCODE.SPACE;
Keyboard.SIMCODES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.SCANCODE.CAPS_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.F1] = Keyboard.SCANCODE.F1;
Keyboard.SIMCODES[Keyboard.SIMCODE.F2] = Keyboard.SCANCODE.F2;
Keyboard.SIMCODES[Keyboard.SIMCODE.F3] = Keyboard.SCANCODE.F3;
Keyboard.SIMCODES[Keyboard.SIMCODE.F4] = Keyboard.SCANCODE.F4;
Keyboard.SIMCODES[Keyboard.SIMCODE.F5] = Keyboard.SCANCODE.F5;
Keyboard.SIMCODES[Keyboard.SIMCODE.F6] = Keyboard.SCANCODE.F6;
Keyboard.SIMCODES[Keyboard.SIMCODE.F7] = Keyboard.SCANCODE.F7;
Keyboard.SIMCODES[Keyboard.SIMCODE.F8] = Keyboard.SCANCODE.F8;
Keyboard.SIMCODES[Keyboard.SIMCODE.F9] = Keyboard.SCANCODE.F9;
Keyboard.SIMCODES[Keyboard.SIMCODE.F10] = Keyboard.SCANCODE.F10;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_LOCK] = Keyboard.SCANCODE.NUM_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.SCROLL_LOCK] = Keyboard.SCANCODE.SCROLL_LOCK;
Keyboard.SIMCODES[Keyboard.SIMCODE.HOME] = Keyboard.SCANCODE.NUM_HOME;
Keyboard.SIMCODES[Keyboard.SIMCODE.UP] = Keyboard.SCANCODE.NUM_UP;
Keyboard.SIMCODES[Keyboard.SIMCODE.PGUP] = Keyboard.SCANCODE.NUM_PGUP;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_SUB] = Keyboard.SCANCODE.NUM_SUB;
Keyboard.SIMCODES[Keyboard.SIMCODE.LEFT] = Keyboard.SCANCODE.NUM_LEFT;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_CENTER] = Keyboard.SCANCODE.NUM_CENTER;
Keyboard.SIMCODES[Keyboard.SIMCODE.RIGHT] = Keyboard.SCANCODE.NUM_RIGHT;
Keyboard.SIMCODES[Keyboard.SIMCODE.NUM_ADD] = Keyboard.SCANCODE.NUM_ADD;
Keyboard.SIMCODES[Keyboard.SIMCODE.END] = Keyboard.SCANCODE.NUM_END;
Keyboard.SIMCODES[Keyboard.SIMCODE.DOWN] = Keyboard.SCANCODE.NUM_DOWN;
Keyboard.SIMCODES[Keyboard.SIMCODE.PGDN] = Keyboard.SCANCODE.NUM_PGDN;
Keyboard.SIMCODES[Keyboard.SIMCODE.INS] = Keyboard.SCANCODE.NUM_INS;
Keyboard.SIMCODES[Keyboard.SIMCODE.DEL] = Keyboard.SCANCODE.NUM_DEL;
Keyboard.SIMCODES[Keyboard.SIMCODE.SYSREQ] = Keyboard.SCANCODE.SYSREQ;
/*
* Entries beyond this point are for keys that existed only on 101-key keyboards (well, except for 'sysreq',
* which also existed on the 84-key keyboard), which ALSO means that these keys essentially did not exist
@ -930,17 +736,17 @@ Keyboard.SIMCODES[Keyboard.SIMCODE.SYSREQ] = Keyboard.SCANCODE.SYSREQ;
* TODO: Add entries for 'num-mul', 'num-div', 'num-enter', the stand-alone arrow keys, etc, AND at the same time,
* make sure that keys with multi-byte sequences (eg, 0xe0 0x1c) work properly.
*/
Keyboard.SIMCODES[Keyboard.SIMCODE.F11] = Keyboard.SCANCODE.F11;
Keyboard.SIMCODES[Keyboard.SIMCODE.F12] = Keyboard.SCANCODE.F12;
Keyboard.SIMCODES[Keyboard.SIMCODE.CMD] = Keyboard.SCANCODE.WIN;
Keyboard.SIMCODES[Keyboard.SIMCODE.RCMD] = Keyboard.SCANCODE.MENU;
Keyboard.SIMCODES[Keyboard.SIMCODE.FF_CMD] = Keyboard.SCANCODE.WIN;
Keyboard.SIMCODES[Keyboard.SIMCODE.F11] = Keyboard.SCANCODE.F11;
Keyboard.SIMCODES[Keyboard.SIMCODE.F12] = Keyboard.SCANCODE.F12;
Keyboard.SIMCODES[Keyboard.SIMCODE.CMD] = Keyboard.SCANCODE.WIN;
Keyboard.SIMCODES[Keyboard.SIMCODE.RCMD] = Keyboard.SCANCODE.MENU;
Keyboard.SIMCODES[Keyboard.SIMCODE.FF_CMD] = Keyboard.SCANCODE.WIN;
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.CTRL << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_BREAK] = Keyboard.SCANCODE.SCROLL_LOCK | (Keyboard.SCANCODE.CTRL << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_DEL] = Keyboard.SCANCODE.NUM_DEL | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_INS] = Keyboard.SCANCODE.NUM_INS | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_ENTER] = Keyboard.SCANCODE.ENTER | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.CTRL << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_BREAK] = Keyboard.SCANCODE.SCROLL_LOCK | (Keyboard.SCANCODE.CTRL << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_DEL] = Keyboard.SCANCODE.NUM_DEL | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_INS] = Keyboard.SCANCODE.NUM_INS | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_ALT_ENTER] = Keyboard.SCANCODE.ENTER | (Keyboard.SCANCODE.CTRL << 8) | (Keyboard.SCANCODE.ALT << 16);
/**
* Commands that can be sent to the Keyboard via the 8042; see sendCmd()
@ -1295,10 +1101,10 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
{
var control;
if (this.cSoftCodes) {
for (var code in Keyboard.SHIFTED_KEYCODES) {
if (simCode == Keyboard.SHIFTED_KEYCODES[code]) {
for (var code in Keys.SHIFTED_KEYCODES) {
if (simCode == Keys.SHIFTED_KEYCODES[code]) {
simCode = +code;
code = Keyboard.STUPID_KEYCODES[code];
code = Keys.STUPID_KEYCODES[code];
if (code) simCode = code;
break;
}
@ -1324,7 +1130,7 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
{
@ -1765,10 +1571,9 @@ Keyboard.prototype.initState = function(data)
*/
Keyboard.prototype.saveState = function()
{
var i = 0;
var data = [];
data[i++] = this.fClock;
data[i] = this.fData;
data[0] = this.fClock;
data[1] = this.fData;
return data;
};
@ -2089,7 +1894,7 @@ Keyboard.prototype.checkActiveKeyShift = function()
*/
Keyboard.prototype.isAlphaKey = function(code)
{
return (code >= Keyboard.ASCII.A && code <= Keyboard.ASCII.Z || code >= Keyboard.ASCII.a && code <= Keyboard.ASCII.z);
return (code >= Keys.ASCII.A && code <= Keys.ASCII.Z || code >= Keys.ASCII.a && code <= Keys.ASCII.z);
};
/**
@ -2101,8 +1906,8 @@ Keyboard.prototype.isAlphaKey = function(code)
*/
Keyboard.prototype.toUpperKey = function(code)
{
if (code >= Keyboard.ASCII.a && code <= Keyboard.ASCII.z) {
code -= (Keyboard.ASCII.a - Keyboard.ASCII.A);
if (code >= Keys.ASCII.a && code <= Keys.ASCII.z) {
code -= (Keys.ASCII.a - Keys.ASCII.A);
}
return code;
};
@ -2149,7 +1954,7 @@ Keyboard.prototype.removeActiveKey = function(simCode, fFlush)
var fRemoved = false;
for (var i = 0; i < this.aKeysActive.length; i++) {
var key = this.aKeysActive[i];
if (key.simCode == simCode || key.simCode == Keyboard.SHIFTED_KEYCODES[simCode]) {
if (key.simCode == simCode || key.simCode == Keys.SHIFTED_KEYCODES[simCode]) {
this.aKeysActive.splice(i, 1);
if (key.timer) clearTimeout(key.timer);
if (key.fDown && !fFlush) this.keySimulate(key.simCode, false);
@ -2225,23 +2030,23 @@ Keyboard.prototype.getSimCode = function(keyCode, fShifted)
var code;
var simCode = keyCode;
if (keyCode >= Keyboard.ASCII.A && keyCode <= Keyboard.ASCII.Z) {
if (keyCode >= Keys.ASCII.A && keyCode <= Keys.ASCII.Z) {
if (!(this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT | Keyboard.STATE.CAPS_LOCK)) == fShifted) {
simCode = keyCode + (Keyboard.ASCII.a - Keyboard.ASCII.A);
simCode = keyCode + (Keys.ASCII.a - Keys.ASCII.A);
}
}
else if (keyCode >= Keyboard.ASCII.a && keyCode <= Keyboard.ASCII.z) {
else if (keyCode >= Keys.ASCII.a && keyCode <= Keys.ASCII.z) {
if (!!(this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT | Keyboard.STATE.CAPS_LOCK)) == fShifted) {
simCode = keyCode - (Keyboard.ASCII.a - Keyboard.ASCII.A);
simCode = keyCode - (Keys.ASCII.a - Keys.ASCII.A);
}
}
else if (!!(this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT)) == fShifted) {
if (code = Keyboard.SHIFTED_KEYCODES[keyCode]) {
if (code = Keys.SHIFTED_KEYCODES[keyCode]) {
simCode = code;
}
}
else {
if (code = Keyboard.STUPID_KEYCODES[keyCode]) {
if (code = Keys.STUPID_KEYCODES[keyCode]) {
simCode = code;
}
}
@ -2292,20 +2097,20 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
*/
var simCode = this.getSimCode(keyCode, true);
if (this.fEscapeDisabled && simCode == Keyboard.ASCII['`']) {
keyCode = simCode = Keyboard.KEYCODE.ESC;
if (this.fEscapeDisabled && simCode == Keys.ASCII['`']) {
keyCode = simCode = Keys.KEYCODE.ESC;
}
if (Keyboard.SIMCODES[keyCode + Keyboard.KEYCODE.ONDOWN]) {
if (Keyboard.SIMCODES[keyCode + Keys.KEYCODE.ONDOWN]) {
simCode += Keyboard.KEYCODE.ONDOWN;
if (event.location == Keyboard.LOCATION.RIGHT) {
simCode += Keyboard.KEYCODE.ONRIGHT;
simCode += Keys.KEYCODE.ONDOWN;
if (event.location == Keys.LOCATION.RIGHT) {
simCode += Keys.KEYCODE.ONRIGHT;
}
if (this.updateShiftState(simCode, false, fDown)) {
if (keyCode == Keyboard.KEYCODE.CAPS_LOCK || keyCode == Keyboard.KEYCODE.NUM_LOCK || keyCode == Keyboard.KEYCODE.SCROLL_LOCK) {
if (keyCode == Keys.KEYCODE.CAPS_LOCK || keyCode == Keys.KEYCODE.NUM_LOCK || keyCode == Keys.KEYCODE.SCROLL_LOCK) {
/*
* FYI, "lock" keys generate a "down" event ONLY when getting locked and an "up" event ONLY
* when getting unlocked--which is a little odd, since the key did go UP and DOWN each time.
@ -2325,7 +2130,7 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
* cases where we don't always get notification of a CMD key's companion key going up (this probably
* overlaps with most if not all situations where we also lose focus).
*/
if (!fDown && (keyCode == Keyboard.KEYCODE.CMD || keyCode == Keyboard.KEYCODE.RCMD)) {
if (!fDown && (keyCode == Keys.KEYCODE.CMD || keyCode == Keys.KEYCODE.RCMD)) {
this.clearActiveKeys();
}
}
@ -2351,7 +2156,7 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
/*
* HACK for simulating CTRL_BREAK using CTRL_DEL (Mac) or CTRL_BS (Windows)
*/
if (keyCode == Keyboard.KEYCODE.BS && (this.bitsState & (Keyboard.STATE.CTRL|Keyboard.STATE.ALT)) == Keyboard.STATE.CTRL) {
if (keyCode == Keys.KEYCODE.BS && (this.bitsState & (Keyboard.STATE.CTRL|Keyboard.STATE.ALT)) == Keyboard.STATE.CTRL) {
simCode = Keyboard.SIMCODE.CTRL_BREAK;
}
@ -2465,7 +2270,7 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
this.updateShiftState(simCode, true, fDown);
var wCode = Keyboard.SIMCODES[simCode] || Keyboard.SIMCODES[simCode + Keyboard.KEYCODE.ONDOWN];
var wCode = Keyboard.SIMCODES[simCode] || Keyboard.SIMCODES[simCode + Keys.KEYCODE.ONDOWN];
if (wCode !== undefined) {
@ -2491,7 +2296,7 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
abScanCodes.push(bCode | (fDown? 0 : Keyboard.SCANCODE.BREAK));
var fAlpha = (simCode >= Keyboard.ASCII.A && simCode <= Keyboard.ASCII.Z || simCode >= Keyboard.ASCII.a && simCode <= Keyboard.ASCII.z);
var fAlpha = (simCode >= Keys.ASCII.A && simCode <= Keys.ASCII.Z || simCode >= Keys.ASCII.a && simCode <= Keys.ASCII.z);
while (wCode >>>= 8) {
var bShift = 0;

View file

@ -288,7 +288,7 @@ Memory.prototype = {
* @this {Memory}
* @param {Memory} mem
* @param {number} [type]
* @param {Debugger} [dbg]
* @param {DebuggerX86} [dbg]
*/
clone: function(mem, type, dbg) {
/*
@ -417,7 +417,19 @@ Memory.prototype = {
/**
* setAccess(afn, fDirect)
*
* If no function table is specified, a default is selected based on the Memory type.
* The afn parameter should be a 6-entry function table containing two byte handlers, two
* short handlers, and two long handlers. See the static afnMemory table for an example.
*
* If no function table is specified, a default is selected based on the Memory type;
* similarly, any undefined entries in the table are filled with default handlers that fall
* back to the byte handlers, and if one or both byte handlers are undefined, they default
* to handlers that simply ignore the access.
*
* fDirect indicates that both the default AND the direct handlers should be updated. Direct
* handlers normally match the default handlers, except when "checked" handlers are installed;
* this allows "checked" handlers to know where to dispatch the call after performing checks.
* Examples of checks are read/write breakpoints, but it's really up to the Debugger to decide
* what the check consists of.
*
* @this {Memory}
* @param {Array.<function()>} [afn] function table
@ -448,13 +460,13 @@ Memory.prototype = {
setReadAccess: function(afn, fDirect) {
if (!fDirect || !this.cReadBreakpoints) {
this.readByte = afn[0] || this.readNone;
this.readShort = afn[1] || this.readShortDefault;
this.readLong = afn[2] || this.readLongDefault;
this.readShort = afn[2] || this.readShortDefault;
this.readLong = afn[4] || this.readLongDefault;
}
if (fDirect || fDirect === undefined) {
this.readByteDirect = afn[0] || this.readNone;
this.readShortDirect = afn[1] || this.readShortDefault;
this.readLongDirect = afn[2] || this.readLongDefault;
this.readShortDirect = afn[2] || this.readShortDefault;
this.readLongDirect = afn[4] || this.readLongDefault;
}
},
/**
@ -466,13 +478,13 @@ Memory.prototype = {
*/
setWriteAccess: function(afn, fDirect) {
if (!fDirect || !this.cWriteBreakpoints) {
this.writeByte = !this.fReadOnly && afn[3] || this.writeNone;
this.writeShort = !this.fReadOnly && afn[4] || this.writeShortDefault;
this.writeByte = !this.fReadOnly && afn[1] || this.writeNone;
this.writeShort = !this.fReadOnly && afn[3] || this.writeShortDefault;
this.writeLong = !this.fReadOnly && afn[5] || this.writeLongDefault;
}
if (fDirect || fDirect === undefined) {
this.writeByteDirect = afn[3] || this.writeNone;
this.writeShortDirect = afn[4] || this.writeShortDefault;
this.writeByteDirect = afn[1] || this.writeNone;
this.writeShortDirect = afn[3] || this.writeShortDefault;
this.writeLongDirect = afn[5] || this.writeLongDefault;
}
},
@ -620,7 +632,7 @@ Memory.prototype = {
* copyBreakpoints(dbg, mem)
*
* @this {Memory}
* @param {Debugger} [dbg]
* @param {DebuggerX86} [dbg]
* @param {Memory} [mem] (outgoing Memory block to copy breakpoints from, if any)
*/
copyBreakpoints: function(dbg, mem) {
@ -1507,25 +1519,85 @@ Memory.prototype = {
};
/*
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
* 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, 2 byte handlers, 2 short handlers, and 2 long handlers) are undefined.
*
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault];
Memory.afnNone = [
Memory.prototype.readNone,
Memory.prototype.writeNone,
Memory.prototype.readShortDefault,
Memory.prototype.writeShortDefault,
Memory.prototype.readLongDefault,
Memory.prototype.writeLongDefault
];
*/
Memory.afnNone = [];
Memory.afnNone = [];
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory];
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked];
Memory.afnMemory = [
Memory.prototype.readByteMemory,
Memory.prototype.writeByteMemory,
Memory.prototype.readShortMemory,
Memory.prototype.writeShortMemory,
Memory.prototype.readLongMemory,
Memory.prototype.writeLongMemory
];
Memory.afnChecked = [
Memory.prototype.readByteChecked,
Memory.prototype.writeByteChecked,
Memory.prototype.readShortChecked,
Memory.prototype.writeShortChecked,
Memory.prototype.readLongChecked,
Memory.prototype.writeLongChecked
];
if (PAGEBLOCKS) {
Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged];
Memory.afnPaged = [
Memory.prototype.readBytePaged,
Memory.prototype.writeBytePaged,
Memory.prototype.readShortPaged,
Memory.prototype.writeShortPaged,
Memory.prototype.readLongPaged,
Memory.prototype.writeLongPaged
];
Memory.afnUnpaged = [
Memory.prototype.readByteUnpaged,
Memory.prototype.writeByteUnpaged,
Memory.prototype.readShortUnpaged,
Memory.prototype.writeShortUnpaged,
Memory.prototype.readLongUnpaged,
Memory.prototype.writeLongUnpaged
];
}
if (TYPEDARRAYS) {
Memory.afnArrayBE = [Memory.prototype.readByteBE, Memory.prototype.readShortBE, Memory.prototype.readLongBE, Memory.prototype.writeByteBE, Memory.prototype.writeShortBE, Memory.prototype.writeLongBE];
Memory.afnArrayLE = [Memory.prototype.readByteLE, Memory.prototype.readShortLE, Memory.prototype.readLongLE, Memory.prototype.writeByteLE, Memory.prototype.writeShortLE, Memory.prototype.writeLongLE];
Memory.afnPagedLE = [Memory.prototype.readBytePLE, Memory.prototype.readShortPLE, Memory.prototype.readLongPLE, Memory.prototype.writeBytePLE, Memory.prototype.writeShortPLE, Memory.prototype.writeLongPLE];
Memory.afnArrayBE = [
Memory.prototype.readByteBE,
Memory.prototype.writeByteBE,
Memory.prototype.readShortBE,
Memory.prototype.writeShortBE,
Memory.prototype.readLongBE,
Memory.prototype.writeLongBE
];
Memory.afnArrayLE = [
Memory.prototype.readByteLE,
Memory.prototype.writeByteLE,
Memory.prototype.readShortLE,
Memory.prototype.writeShortLE,
Memory.prototype.readLongLE,
Memory.prototype.writeLongLE
];
Memory.afnPagedLE = [
Memory.prototype.readBytePLE,
Memory.prototype.writeBytePLE,
Memory.prototype.readShortPLE,
Memory.prototype.writeShortPLE,
Memory.prototype.readLongPLE,
Memory.prototype.writeLongPLE
];
}
if (NODE) module.exports = Memory;

View file

@ -31,22 +31,6 @@
"use strict";
/*
* Components that previously used Debugger messages definitions by including:
*
* var Debugger = require("./debugger");
*
* and using:
*
* Debugger.MESSAGE.FOO
*
* must now instead include:
*
* var Messages = require("./messages");
*
* and then replace all occurrences of "Debugger.MESSAGE.FOO" with "Messages.FOO".
*/
var Messages = {
CPU: 0x00000001,
SEG: 0x00000002,

View file

@ -181,7 +181,7 @@ Mouse.BUTTON = {
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
Mouse.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -341,7 +341,7 @@ Panel.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -230,7 +230,7 @@ ParallelPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
ParallelPort.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -77,7 +77,7 @@ Component.subclass(RAM);
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -162,7 +162,7 @@ ROM.BIOS.RESET_FLAG_WARMBOOT = 0x1234; // value stored at ROM.BIOS.RESET_FLAG t
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
ROM.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -456,7 +456,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -2631,7 +2631,7 @@ Card.prototype.dumpVideoCard = function()
* TODO: Make these options more general-purpose (it currently assumes a conventional VGA planar layout).
*
* @this {Card}
* @param {Array.<string>} asArgs
* @param {Array.<string>} asArgs (all numeric arguments default to base 16 unless otherwise specified)
*/
Card.prototype.dumpVideoBuffer = function(asArgs)
{
@ -2648,12 +2648,12 @@ Card.prototype.dumpVideoBuffer = function(asArgs)
var s = asArgs[i];
if (!i) {
idw = str.parseInt(s);
idw = str.parseInt(s, 16);
continue;
}
var ch = s.charAt(0);
j = str.parseInt(s.substr(1));
j = str.parseInt(s.substr(1), 16);
switch(ch) {
case 'l':
@ -2797,7 +2797,7 @@ Card.prototype.setMemoryAccess = function(nAccess)
}
if (!this.afnAccess) this.afnAccess = new Array(6);
this.afnAccess[0] = fnReadByte;
this.afnAccess[3] = fnWriteByte;
this.afnAccess[1] = fnWriteByte;
this.nAccess = nAccess;
}
};
@ -2899,7 +2899,7 @@ Video.TOUCH = {
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
Video.prototype.initBus = function(cmp, bus, cpu, dbg)
{
@ -5419,7 +5419,7 @@ Video.prototype.updateScreen = function(fForce)
/*
* The Computer component maintains the fPowered setting on our behalf, so we use it.
*/
if (!this.flags.fPowered) return;
if (!this.flags.powered) return;
/*
* If the card's video signal is disabled (eg, during a mode change), then skip the update,

View file

@ -149,7 +149,7 @@ function X86CPU(parmsCPU)
* stepCPU() call, but it's good form to do so.
*/
this.resetCycles();
this.flags.fComplete = this.flags.fDebugCheck = false;
this.flags.complete = this.flags.debugCheck = false;
/*
* If there are no live registers to display, then updateStatus() can skip a bit....
@ -880,7 +880,7 @@ X86CPU.prototype.initProcessor = function()
this.aOps[X86.OPCODE.OS] = X86.opOS; // 0x66
this.aOps[X86.OPCODE.AS] = X86.opAS; // 0x67
for (bOpcode in X86.aOps0F386) {
this.aOps0F[+bOpcode] = X86.aOps0F386[bOpcode];
this.aOps0F[+bOpcode] = X86.aOps0F386[+bOpcode];
}
if (this.stepping >= X86.STEPPING_80386_A0 && this.stepping <= X86.STEPPING_80386_B0) {
this.aOps0F[0xA6] = X86.opXBTS;
@ -898,7 +898,7 @@ X86CPU.prototype.initProcessor = function()
*/
X86CPU.prototype.reset = function()
{
if (this.flags.fRunning) this.stopCPU();
if (this.flags.running) this.stopCPU();
this.resetRegs();
this.resetCycles();
this.clearError(); // clear any fatal error/exception that setError() may have flagged
@ -1540,7 +1540,7 @@ X86CPU.prototype.checkIntNotify = function(nInt)
* For most purposes, just having dbg.messageInt(), and the Debugger's ability to selectively turn categories
* of messages on and off, is good enough.
*/
if (DEBUGGER && this.flags.fDebugCheck) {
if (DEBUGGER && this.flags.debugCheck) {
if (this.messageEnabled(Messages.INT) && this.dbg.messageInt(nInt, this.regLIP) && MAXDEBUG) {
this.addIntReturn(this.regLIP, function(cpu, nCycles) {
return function onIntReturn(nLevel) {
@ -1951,7 +1951,7 @@ X86CPU.prototype.restore = function(data)
* getSeg(sName)
*
* @param {string} sName
* @return {Array}
* @return {X86Seg|Array}
*/
X86CPU.prototype.getSeg = function(sName)
{
@ -2374,8 +2374,11 @@ X86CPU.prototype.setSP = function(off)
* specifies any flags not contained in the new type parameter, then those flags must be immediately
* calculated and written to the appropriate bit(s) in regPS.
*
* The fSubtract parameter is used to indicate a "subtracted" result (eg, CMP, DEC, SUB, SBB); the
* default assumes an "added" result (eg, ADD, ADC, INC).
* The default assumes an "addition" (eg, ADD, ADC, INC), where value = dst + src. The fSubtract
* parameter is used to indicate a "subtraction" (eg, CMP, DEC, SUB, SBB), where value = dst - src;
* We can transform a subtraction into an addition, since it's also true that dst = value + src,
* by swapping swap dst and value -- which is exactly what we do below. This allows all downstream
* flag calculations (eg, getCF(), getOF()) to remain the same.
*
* @this {X86CPU}
* @param {number} dst
@ -2469,12 +2472,11 @@ X86CPU.prototype.getCarry = function()
/**
* getCF()
*
* Notes regarding carry following an I386 addition:
* The following table summarizes bit 31 of the dst (D) and src (S) operands, bit 31 of the
* addition (A), along with the expected carry bit (C):
*
* The following table summarizes bit 31 of dst, src, and result, along with the expected carry:
*
* dst src res carry
* --- --- --- -----
* D S A C
* - - - -
* 0 0 0 0 no
* 0 0 1 0 no (there must have been a carry out of bit 30, but it was "absorbed")
* 0 1 0 1 yes (there must have been a carry out of bit 30, but it was NOT "absorbed")
@ -2488,6 +2490,10 @@ X86CPU.prototype.getCarry = function()
*
* (resultDst ^ ((resultDst ^ resultSrc) & (resultSrc ^ resultArith))) & resultType
*
* NOTE: The above table assumes that the resultDst (D) and resultSrc (S) operands were ADDED to
* produce resultArith (A); if they were SUBTRACTED instead (D - S), then D and A must be swapped
* after the subtraction, so that the above truth table still applies; see setArithResult().
*
* @this {X86CPU}
* @return {number} 0 or X86.PS.CF
*/
@ -2522,8 +2528,8 @@ X86CPU.prototype.getCF = function()
* lowest nibble of v. This number is like a miniature 16-bit parity-table indexed by the low four bits in v.
* The result has the parity of v in bit 1, which is masked and returned.
*
* The x86 parity flag (PF) is based exclusively on the low 8 bits of resultParitySign, and PF must be SET if that byte
* has EVEN parity; the above calculation yields ODD parity, so we use the conditional operator to invert the result.
* The x86 parity flag (PF) is based exclusively on the low 8 bits of resultParitySign, so our calculation is bit
* simpler. Note that PF must be SET if that byte has EVEN parity, and CLEAR if it has ODD parity.
*
* @this {X86CPU}
* @return {number} 0 or X86.PS.PF
@ -2625,8 +2631,8 @@ X86CPU.prototype.getSF = function()
*
* ((resultDst ^ resultArith) & (resultSrc ^ resultArith)) & resultType
*
* which you can verify from the following table of sign bits (where x1 is resultDst ^ resultArith,
* and x2 is resultSrc ^ resultArith):
* which you can verify from the following table of sign bits, where x1 is resultDst ^ resultArith,
* and x2 is resultSrc ^ resultArith:
*
* D S A x1 x2 OF
* - - - -- -- --
@ -2639,6 +2645,10 @@ X86CPU.prototype.getSF = function()
* 1 1 0 1 1 1 (adding two negative values yielded a positive value)
* 1 1 1 0 0 0
*
* NOTE: The above table assumes that the resultDst (D) and resultSrc (S) operands were ADDED to
* produce resultArith (A); if they were SUBTRACTED instead (D - S), then D and A must be swapped
* after the subtraction, so that the above truth table still applies; see setArithResult().
*
* @this {X86CPU}
* @return {number} 0 or X86.PS.OF
*/
@ -4159,7 +4169,7 @@ X86CPU.prototype.updateReg = function(sReg, nValue)
X86CPU.prototype.updateStatus = function(fForce)
{
if (this.cLiveRegs) {
if (fForce || !this.flags.fRunning || this.flags.fDisplayLiveRegs) {
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
this.updateReg("EAX", this.regEAX);
this.updateReg("EBX", this.regEBX);
this.updateReg("ECX", this.regECX);
@ -4231,12 +4241,12 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
* Debugger is single-stepping (even when performing multiple single-steps), fRunning is never set,
* so stopCPU() would have no effect as far as the Debugger is concerned.
*/
this.flags.fComplete = true;
this.flags.complete = true;
/*
* fDebugCheck is true if we need to "check" every instruction with the Debugger.
*/
var fDebugCheck = this.flags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
var fDebugCheck = this.flags.debugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
/*
* nDebugState is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
@ -4246,8 +4256,8 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have
* officially "started" now.
*/
var nDebugState = (!nMinCycles)? -1 : (this.flags.fStarting? 0 : 1);
this.flags.fStarting = false;
var nDebugState = (!nMinCycles)? -1 : (this.flags.starting? 0 : 1);
this.flags.starting = false;
/*
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
@ -4373,7 +4383,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
//
// Make sure that every instruction is assessing a cycle cost, and that the cost is a net positive.
//
if (this.flags.fComplete && this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG_PREFIXES)) {
if (this.flags.complete && this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG_PREFIXES)) {
this.println("cycle miscount: " + (this.nSnapCycles - this.nStepCycles));
this.setIP(this.opLIP - this.segCS.base);
this.stopCPU();
@ -4384,7 +4394,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
} while (this.nStepCycles > 0);
return (this.flags.fComplete? this.nBurstCycles - this.nStepCycles : (this.flags.fComplete === undefined? 0 : -1));
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
};
/**

View file

@ -163,7 +163,7 @@ Component.subclass(X86FPU);
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
* @param {DebuggerX86} dbg
*/
X86FPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{

View file

@ -731,7 +731,7 @@ X86.helpFault = function(nFault, nError, nCycles, fHalt)
{
var fDispatch = false;
if (!this.flags.fComplete) {
if (!this.flags.complete) {
/*
* Prior to each new burst of instructions, stepCPU() sets fComplete to true, and the only (normal) way
* for fComplete to become false is through stopCPU(), which isn't ordinarily called, except by the Debugger.
@ -970,7 +970,7 @@ X86.helpCheckFault = function(nFault, nError, fHalt)
if (this.messageEnabled(bitsMessage) || fHalt) {
var fRunning = this.flags.fRunning;
var fRunning = this.flags.running;
var sMessage = "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode);
if (fHalt && fRunning) sMessage += " (blocked)";

View file

@ -53,7 +53,7 @@ X86.opADDmb = function()
* Notice that we also test fRunning: this allows the Debugger to step over the instruction,
* because its trace ("t") command doesn't "run" the CPU; it merely "steps" the CPU.
*/
if (DEBUG && !this.bModRM && this.flags.fRunning) {
if (DEBUG && !this.bModRM && this.flags.running) {
this.printMessage("suspicious opcode: 0x00 0x00", DEBUGGER || this.bitsMessage);
if (DEBUGGER && this.dbg) this.dbg.stopCPU();
}

31
modules/pdp11/README.md Normal file
View file

@ -0,0 +1,31 @@
---
layout: page
title: PDP-11 Machine Emulation Module (PDPjs)
permalink: /modules/pdp11/
---
PDP-11 Machine Emulation Module (PDPjs)
=========================================
Overview
---
PDPjs is our PDP-11 machine emulation module. The code is being adapted from
the JavaScript [PDP 11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by
[Paul Nankervis](mailto:paulnank@hotmail.com).
PDPjs is currently comprised of the following non-shared components, as listed in
[package.json](../../package.json) (see the *pdp11Files* property):
* [defines.js](/modules/pdp11/lib/defines.js)
* [messages.js](/modules/pdp11/lib/messages.js)
* [panel.js](/modules/pdp11/lib/panel.js)
* [bus.js](/modules/pdp11/lib/bus.js)
* [memory.js](/modules/pdp11/lib/memory.js)
* [cpu.js](/modules/pdp11/lib/cpu.js)
* [cpustate.js](/modules/pdp11/lib/cpustate.js)
* [cpuops.js](/modules/pdp11/lib/cpuops.js)
* [rom.js](/modules/pdp11/lib/rom.js)
* [ram.js](/modules/pdp11/lib/ram.js)
* [keyboard.js](/modules/pdp11/lib/keyboard.js)
* [debugger.js](/modules/pdp11/lib/debugger.js)
* [computer.js](/modules/pdp11/lib/computer.js)

View file

@ -0,0 +1,64 @@
/**
* @fileoverview Converts octal constants to hex.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2016-Sep-04
*
* Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* 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 fs = require("fs");
try {
var sText = fs.readFileSync(process.argv[2], "utf-8");
var asLines = sText.split('\n');
for (var iLine = 0; iLine < asLines.length; iLine++) {
var sLine = asLines[iLine];
var match, re = /(^|[^0-9a-z_-]*)(0[0-7]+)([^0-9a-z_,]*|$)/gi;
var iComment = sLine.indexOf("//");
while (match = re.exec(sLine)) {
if (iComment >= 0 && match.index > iComment) break;
var n = parseInt(match[2], 8);
if (n < 0) {
console.log("found negative octal value (" + match[2] + "), skipping");
continue;
}
var sReplace = match[1] + "0x" + ("0000" + n.toString(16).toUpperCase()).substr(-4) + match[3] + " /*" + match[2] + "*/";
sLine = sLine.substr(0, match.index) + sReplace + sLine.substr(match.index + match[0].length);
re.lastIndex = match.index + sReplace.length;
asLines[iLine] = sLine;
}
}
sText = "";
for (var iLine = 0; iLine < asLines.length; iLine++) {
if (sText) sText += '\n';
sText += asLines[iLine];
}
console.log(sText);
} catch (err) {
console.log("error: " + err.message);
}

1114
modules/pdp11/lib/bus.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1281
modules/pdp11/lib/cpu.js Normal file

File diff suppressed because it is too large Load diff

2141
modules/pdp11/lib/cpuops.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,528 @@
/**
* @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")
/**
* APPNAME is used more for display purposes than anything else now. APPCLASS is what matters in terms
* of folder and file names, CSS styles, etc.
*
* @define {string}
*/
var APPNAME = "PDPjs"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
/**
* 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*.
*
* @define {boolean}
*/
var DEBUGGER = true; // this @define is overridden by the Closure Compiler to remove Debugger-related support
/**
* 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.
*
* @define {boolean}
*/
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
/*
* 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 modes
*/
MODE: {
KERNEL: 0x0,
SUPER: 0x1,
UNUSED: 0x2,
USER: 0x3,
MASK: 0x3
},
/*
* Processor Status flag definitions (stored in regPSW)
*/
PSW: {
CF: 0x0001, // bit 0: Carry Flag
VF: 0x0002, // bit 1: Overflow Flag (aka OF on Intel processors)
ZF: 0x0004, // bit 2: Zero Flag
NF: 0x0008, // bit 3: Negative Flag (aka SF -- Sign Flag -- on Intel processors)
TF: 0x0010, // bit 4: Trap Flag
PRI: 0x00E0, // bits 5-7: Priority
UNUSED: 0x0700, // bits 8-10: unused
REGSET: 0x0800, // bit 11: Register Set
PMODE: 0x3000, // bits 12-13: Prev Mode (see PDP11.MODE)
CMODE: 0xC000, // bits 14-15: Curr Mode (see PDP11.MODE)
SHIFT: {
CF: 0,
VF: 1,
ZF: 2,
NF: 3,
TF: 4,
PRI: 5,
PMODE: 12,
CMODE: 14
}
},
/*
* Internal operation state flags
*/
OPFLAG: {
INTQ_SPL: 0x01, // INTQ triggered by SPL
INTQ: 0x02, // call checkInterruptQueue()
WAIT: 0x04, // WAIT operation in progress
TRAP_TF: 0x10, // aka PDP11.PSW.TF
TRAP_MMU: 0x20,
TRAP_SP: 0x40,
TRAP_MASK: 0x70,
NO_FLAGS: 0x80, // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
PRESERVE: 0x07 // OPFLAG bits to preserve prior to the next instruction
},
/*
* Opcode reg (opcode bits 2-0)
*/
OPREG: {
MASK: 0x07
},
/*
* Opcode modes (opcode bits 5-3)
*/
OPMODE: {
REG: 0x00, // REGISTER (register is operand)
REGD: 0x08, // REGISTER DEFERRED (register is address of operand)
POSTINC: 0x10, // AUTO-INCREMENT (register is address of operand, register incremented)
POSTINCD: 0x18, // AUTO-INCREMENT DEFERRED (register is address of address of operand, register incremented)
PREDEC: 0x20, // AUTO-DECREMENT (register decremented, register is address of operand)
PREDECD: 0x28, // AUTO-DECREMENT DEFERRED (register decremented, register is address of address of operand)
INDEX: 0x30, // INDEX (register + next word is address of operand)
INDEXD: 0x38, // INDEX DEFERRED (register + next word is address of address of operand)
MASK: 0x38,
SHIFT: 3
},
DSTMODE: {
REG: 0x0007,
MODE: 0x0038,
MASK: 0x003F,
SHIFT: 0
},
SRCMODE: {
REG: 0x01C0,
MODE: 0x0E00,
MASK: 0x0FC0,
SHIFT: 6
},
REG: {
SP: 6,
PC: 7,
},
/*
* PDP-11 trap vectors
*/
TRAP: {
UNDEFINED: 0x00, // 000 (reserved)
BUS_ERROR: 0x04, // 004 illegal instructions, bus errors, stack limit, illegal internal address, microbreak
RESERVED: 0x08, // 010 reserved instructions
BREAKPOINT: 0x0C, // 014 BPT, breakpoint trap (trace)
IOT: 0x10, // 020 IOT, input/output trap
POWER_FAIL: 0x14, // 024 power fail
EMULATOR: 0x18, // 030 EMT, emulator trap
TRAP: 0x1C, // 034 TRAP instruction
PIRQ: 0xA0, // 240 PIRQ, program interrupt request
MMU_FAULT: 0xA8 // 250 MMU aborts and traps
},
/*
* PDP-11 trap reasons (for diagnostic purposes only)
*/
REASON: {
BPT: 1,
EMT: 2,
HALT: 3,
IOT: 4,
TRAP: 5,
RESERVED: 6,
ODDMEMADDR: 10,
NOMEMORY: 12,
ODDMMUADDR: 14,
MAPERROR: 16,
PUSHERROR: 18,
NOREGADDR: 20,
STACKMODE1: 22,
STACKERROR: 24,
INTERRUPT: 26,
TRAPMMU: 28,
TRAPSP: 30,
TRAPTF: 32
},
/*
* Internal memory access flags
*/
ACCESS: {
WORD: 0x00,
BYTE: 0x01,
READ: 0x02,
WRITE: 0x04,
UPDATE: 0x06,
VIRT: 0x08, // getVirtualByMode() leaves bit 17 clear if this is set (otherwise the caller would have to clear it again)
ISPACE: 0x00000,
DSPACE: 0x10000 // getVirtualByMode() sets bit 17 in any 16-bit virtual address that refers to D space (as opposed to I space)
},
/*
* Internal flags passed to writeByteByMode(), etc.
*/
WRITE: {
NORMAL: 0x0, // write byte or word normally
SIGNEXT: 0x1 // sign-extend a byte to a word
},
CPUERR: {
RED: 0x0004, // red zone stack limit
YELLOW: 0x0008, // yellow zone stack limit
TIMEOUT: 0x0010, // UNIBUS timeout error
NOMEMORY: 0x0020, // non-existent memory error
ODDADDR: 0x0040, // odd word address error (as in non-even, not strange)
BADHALT: 0x0080 // HALT attempted in USER or SUPER modes
},
MMR0: {
ENABLED: 0x0001, // address relocation enabled
PAGE_NUM: 0x000E, // page number of last fault
PAGE_D: 0x0010, // last fault occurred in D space
PAGE_MODE: 0x0060, // processor mode as of last fault
COMPLETED: 0x0080, // last instruction completed
DSTMODE: 0x0100, // only destination mode references will be relocated (for diagnostic use)
MMU_TRAPS: 0x0200, // enable MMU traps
UNUSED: 0x0C00,
TRAP_MMU: 0x1000, // trap: MMU
ABORT_RO: 0x2000, // abort: read-only
ABORT_PL: 0x4000, // abort: page length
ABORT_NR: 0x8000, // abort: non-resident
ABORT: 0xE000
},
MMR1: { // general purpose auto-inc/auto-dec register
REG1_NUM: 0x0007,
REG1_DELTA: 0x00F8,
REG2_NUM: 0x0700,
REG2_DELTA: 0xF800
},
MMR2: { // virtual program counter register
},
MMR3: { // mapping register
USER_D: 0x0001,
SUPER_D: 0x0002,
KERNEL_D: 0x0004,
MMU_22BIT: 0x0010,
UNIBUS_MAP: 0x0020 // UNIBUS map relocation enabled
},
/*
* Assorted special (UNIBUS) addresses
*
* Within the PDP-11/45's 18-bit address space, of the 0x40000 possible addresses (256Kb), the top 0x2000
* (8Kb) is called the IOPAGE and is reserved for CPU and I/O registers. The IOPAGE spans 0x3E000-0x3FFFF.
*
* Within the PDP-11/70's 22-bit address space, of the 0x400000 possible addresses (4Mb), the top 0x20000
* (256Kb) is mapped to the UNIBUS (not physical memory), and as before, the top 0x2000 (8Kb) of that is
* mapped to the IOPAGE.
*
* To map 18-bit UNIBUS addresses to 22-bit physical addresses, the 11/70 uses a UNIBUS relocation map.
* It consists of 31 double-word registers that each hold a 22-bit base address. When UNIBUS relocation
* is enabled, the top 5 bits of an address select one of the 31 mapping registers, and the bottom 13 bits
* are then added to the contents of the selected mapping register.
*
* ES6 ALERT: By using octal constants, this is the first place I'm dipping my toe into ECMAScript 6 waters.
* If you're loading this raw source code into your browser, then by now (2016) you're almost certainly using
* an ES6-aware browser. Everyone else should be using code compiled by Google's Closure Compiler, which
* we configure to produce code that's backward-compatible with ES5 (for example, octal constants are
* converted to decimal values).
*
* For more details: https://github.com/google/closure-compiler/wiki/ECMAScript6
*/
UNIBUS: { //16-bit 18-bit 22-bit Hex Description
SISDR0: 0o172200, // Supervisor I Space Descriptor Register 0
SISDR1: 0o172202, // Supervisor I Space Descriptor Register 1
SISDR2: 0o172204, // Supervisor I Space Descriptor Register 2
SISDR3: 0o172206, // Supervisor I Space Descriptor Register 3
SISDR4: 0o172210, // Supervisor I Space Descriptor Register 4
SISDR5: 0o172212, // Supervisor I Space Descriptor Register 5
SISDR6: 0o172214, // Supervisor I Space Descriptor Register 6
SISDR7: 0o172216, // Supervisor I Space Descriptor Register 7
SDSDR0: 0o172220, // Supervisor D Space Descriptor Register 0
SDSDR1: 0o172222, // Supervisor D Space Descriptor Register 1
SDSDR2: 0o172224, // Supervisor D Space Descriptor Register 2
SDSDR3: 0o172226, // Supervisor D Space Descriptor Register 3
SDSDR4: 0o172230, // Supervisor D Space Descriptor Register 4
SDSDR5: 0o172232, // Supervisor D Space Descriptor Register 5
SDSDR6: 0o172234, // Supervisor D Space Descriptor Register 6
SDSDR7: 0o172236, // Supervisor D Space Descriptor Register 7
SISAR0: 0o172240, // Supervisor I Space Address Register 0
SISAR1: 0o172242, // Supervisor I Space Address Register 1
SISAR2: 0o172244, // Supervisor I Space Address Register 2
SISAR3: 0o172246, // Supervisor I Space Address Register 3
SISAR4: 0o172250, // Supervisor I Space Address Register 4
SISAR5: 0o172252, // Supervisor I Space Address Register 5
SISAR6: 0o172254, // Supervisor I Space Address Register 6
SISAR7: 0o172256, // Supervisor I Space Address Register 7
SDSAR0: 0o172260, // Supervisor D Space Address Register 0
SDSAR1: 0o172262, // Supervisor D Space Address Register 1
SDSAR2: 0o172264, // Supervisor D Space Address Register 2
SDSAR3: 0o172266, // Supervisor D Space Address Register 3
SDSAR4: 0o172270, // Supervisor D Space Address Register 4
SDSAR5: 0o172272, // Supervisor D Space Address Register 5
SDSAR6: 0o172274, // Supervisor D Space Address Register 6
SDSAR7: 0o172276, // Supervisor D Space Address Register 7
KISDR0: 0o172300, // Kernel I Space Descriptor Register 0
KISDR1: 0o172302, // Kernel I Space Descriptor Register 1
KISDR2: 0o172304, // Kernel I Space Descriptor Register 2
KISDR3: 0o172306, // Kernel I Space Descriptor Register 3
KISDR4: 0o172310, // Kernel I Space Descriptor Register 4
KISDR5: 0o172312, // Kernel I Space Descriptor Register 5
KISDR6: 0o172314, // Kernel I Space Descriptor Register 6
KISDR7: 0o172316, // Kernel I Space Descriptor Register 7
KDSDR0: 0o172320, // Kernel D Space Descriptor Register 0
KDSDR1: 0o172322, // Kernel D Space Descriptor Register 1
KDSDR2: 0o172324, // Kernel D Space Descriptor Register 2
KDSDR3: 0o172326, // Kernel D Space Descriptor Register 3
KDSDR4: 0o172330, // Kernel D Space Descriptor Register 4
KDSDR5: 0o172332, // Kernel D Space Descriptor Register 5
KDSDR6: 0o172334, // Kernel D Space Descriptor Register 6
KDSDR7: 0o172336, // Kernel D Space Descriptor Register 7
KISAR0: 0o172340, // Kernel I Space Address Register 0
KISAR1: 0o172342, // Kernel I Space Address Register 1
KISAR2: 0o172344, // Kernel I Space Address Register 2
KISAR3: 0o172346, // Kernel I Space Address Register 3
KISAR4: 0o172350, // Kernel I Space Address Register 4
KISAR5: 0o172352, // Kernel I Space Address Register 5
KISAR6: 0o172354, // Kernel I Space Address Register 6
KISAR7: 0o172356, // Kernel I Space Address Register 7
KDSAR0: 0o172360, // Kernel D Space Address Register 0
KDSAR1: 0o172362, // Kernel D Space Address Register 1
KDSAR2: 0o172364, // Kernel D Space Address Register 2
KDSAR3: 0o172366, // Kernel D Space Address Register 3
KDSAR4: 0o172370, // Kernel D Space Address Register 4
KDSAR5: 0o172372, // Kernel D Space Address Register 5
KDSAR6: 0o172374, // Kernel D Space Address Register 6
KDSAR7: 0o172376, // Kernel D Space Address Register 7
MMR3: 0o172516, // 772516 17772516
LKS: 0o177546, // KW11-L Clock Status
RCSR: 0o177560, // Console Terminal: Receiver Status Register
RBUF: 0o177562, // Console Terminal: Receiver Data Buffer Register
XCSR: 0o177564, // Console Terminal: Transmitter Status Register
XBUF: 0o177566, // Console Terminal: Transmitter Data Buffer Register
DISPLAY: 0o177570, // Console Switch and Display
MMR0: 0o177572, // 777572 17777572
MMR1: 0o177574, // 777574 17777574
MMR2: 0o177576, // 777576 17777576
UISDR0: 0o177600, // User I Space Descriptor Register 0
UISDR1: 0o177602, // User I Space Descriptor Register 1
UISDR2: 0o177604, // User I Space Descriptor Register 2
UISDR3: 0o177606, // User I Space Descriptor Register 3
UISDR4: 0o177610, // User I Space Descriptor Register 4
UISDR5: 0o177612, // User I Space Descriptor Register 5
UISDR6: 0o177614, // User I Space Descriptor Register 6
UISDR7: 0o177616, // User I Space Descriptor Register 7
UDSDR0: 0o177620, // User D Space Descriptor Register 0
UDSDR1: 0o177622, // User D Space Descriptor Register 1
UDSDR2: 0o177624, // User D Space Descriptor Register 2
UDSDR3: 0o177626, // User D Space Descriptor Register 3
UDSDR4: 0o177630, // User D Space Descriptor Register 4
UDSDR5: 0o177632, // User D Space Descriptor Register 5
UDSDR6: 0o177634, // User D Space Descriptor Register 6
UDSDR7: 0o177636, // User D Space Descriptor Register 7
UISAR0: 0o177640, // User I Space Address Register 0
UISAR1: 0o177642, // User I Space Address Register 1
UISAR2: 0o177644, // User I Space Address Register 2
UISAR3: 0o177646, // User I Space Address Register 3
UISAR4: 0o177650, // User I Space Address Register 4
UISAR5: 0o177652, // User I Space Address Register 5
UISAR6: 0o177654, // User I Space Address Register 6
UISAR7: 0o177656, // User I Space Address Register 7
UDSAR0: 0o177660, // User D Space Address Register 0
UDSAR1: 0o177662, // User D Space Address Register 1
UDSAR2: 0o177664, // User D Space Address Register 2
UDSAR3: 0o177666, // User D Space Address Register 3
UDSAR4: 0o177670, // User D Space Address Register 4
UDSAR5: 0o177672, // User D Space Address Register 5
UDSAR6: 0o177674, // User D Space Address Register 6
UDSAR7: 0o177676, // User D Space Address Register 7
SIZE_LO: 0o177760, // Lower Size Register (last 32-word block) (11/70 only?)
SIZE_HI: 0o177762, // Upper Size Register (always zero) (11/70 only?)
SYSID: 0o177764, // System ID Register (11/70 only?)
CPUERR: 0o177766, // CPU error (11/70 only?)
MB: 0o177770, // Microprogram break (11/70 only?)
PIR: 0o177772, // Program Interrupt Request
SL: 0o177774, // Stack Limit Register
PSW: 0o177776 // 777776 17777776 0x3FFFFE Processor Status Word
},
DL11: { // Serial Line Interface (program compatible with the KL11 for control of console teleprinters)
PRI: 4,
RVEC: 0o60,
XVEC: 0o64,
DELAY: 40,
RCSR: { // 177560
RE: 0x0001, // Reader Enable (W/O) TODO: Determine if we really need to exclude this write-only bit from RMASK
DTR: 0x0002, // Data Terminal Ready (R/W)
RTS: 0x0004, // Request To Send (R/W)
STD: 0x0008, // Secondary Transmitted Data (R/W)
DIE: 0x0020, // Dataset Interrupt Enable (R/W)
RIE: 0x0040, // Receiver Interrupt Enable (R/W)
RD: 0x0080, // Receiver Done (R/O)
SRD: 0x0400, // Secondary Received Data (R/O)
RA: 0x0800, // Receiver Active (R/O)
CD: 0x1000, // Carrier Detect (R/O)
CTS: 0x2000, // Clear To Send (R/O)
RI: 0x4000, // Ring Indicator (R/O)
DSC: 0x8000, // Dataset Status Change (R/O)
RMASK: 0xFFFF, // read mask
WMASK: 0x006F // write mask
},
RBUF: { // 177562
DATA: 0x00ff, // Received Data (R/O)
PARITY: 0x1000, // Received Data Parity (R/O)
FE: 0x2000, // Framing Error (R/O)
OE: 0x4000, // Overrun Error (R/O)
ERROR: 0x8000 // Error (R/O)
},
XCSR: { // 177564
BREAK: 0x0001, // BREAK (R/W)
MAINT: 0x0004, // Maintenance (R/W)
TIE: 0x0040, // Transmitter Interrupt Enable (R/W)
READY: 0x0080, // Transmitter Ready (R/O)
RMASK: 0x00C5,
WMASK: 0x0045,
DELAY: 8
},
XBUF: { // 177566
DATA: 0x00FF, // Transmitted Data (W/O) TODO: Determine why pdp11.js effectively defined this as 0x7F
DELAY: 100
}
},
KW11: { // KW11-L Line Time Clock
PRI: 6,
VEC: 0o100,
DELAY: 0,
LKS: {
IE: 0x0040, // Interrupt Enable
MON: 0x0080 // Monitor
}
}
};
PDP11.ACCESS.READ_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.READ; // formerly READ_MODE (2)
PDP11.ACCESS.READ_BYTE = PDP11.ACCESS.BYTE | PDP11.ACCESS.READ; // formerly READ_MODE (2) | BYTE_MODE (1)
PDP11.ACCESS.WRITE_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.WRITE; // formerly WRITE_MODE (4)
PDP11.ACCESS.WRITE_BYTE = PDP11.ACCESS.BYTE | PDP11.ACCESS.WRITE; // formerly WRITE_MODE (4) | BYTE_MODE (1)
PDP11.ACCESS.UPDATE_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.UPDATE; // formerly MODIFY_WORD (2 | 4)
PDP11.ACCESS.UPDATE_BYTE = PDP11.ACCESS.BYTE | PDP11.ACCESS.UPDATE; // formerly MODIFY_BYTE (1 | 2 | 4)
/*
* PSW arithmetic flags are NOT stored directly into the PSW register; they are maintained across separate
* flag registers.
*/
PDP11.PSW.FLAGS = (PDP11.PSW.NF | PDP11.PSW.ZF | PDP11.PSW.VF | PDP11.PSW.CF);
if (NODE) {
global.APPCLASS = APPCLASS;
global.APPNAME = APPNAME;
global.DEBUGGER = DEBUGGER;
global.BYTEARRAYS = BYTEARRAYS;
global.TYPEDARRAYS = TYPEDARRAYS;
global.PDP11 = PDP11;
module.exports = PDP11;
}

1499
modules/pdp11/lib/device.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,119 @@
/**
* @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 Keys = require("../../shared/lib/keys");
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);
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;

902
modules/pdp11/lib/memory.js Normal file
View file

@ -0,0 +1,902 @@
/**
* @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 PDP11 = require("./defines");
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, controller)
*
* The Bus component allocates Memory 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 Memory 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 Memory objects as the ranges require. Partial Memory blocks could
* also be supported in theory, but in practice, they're not.
*
* Because Memory 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 Memory 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)
* @param {Object} [controller] is an optional memory controller component
*/
function MemoryPDP11(addr, used, size, type, controller)
{
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.controller = null;
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;
}
/*
* When a controller is specified, the controller must provide a buffer, via getControllerBuffer(),
* and memory access functions, via getControllerAccess().
*/
if (controller) {
this.controller = controller;
var a = controller.getControllerBuffer(addr);
this.adw = a[0];
this.offset = a[1];
this.setAccess(controller.getControllerAccess());
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 Memory writeByteDirect() interface), allowing the ROM component to
* initialize its own memory. The CONTROLLER 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 CONTROLLER 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,
CONTROLLER: 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 Memory block.
*
* @this {MemoryPDP11}
* @param {number} addr
*/
init: function(addr) {
this.addr = addr;
},
/**
* clone(mem, type)
*
* Converts the current Memory block (this) into a clone of the given Memory 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 Memory block as an array of 32-bit values; used by Bus.saveMemory(),
* which in turn is called by CPUState.save().
*
* Memory 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 (this.controller) {
adw = null;
}
else 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 Memory block from an array of 32-bit values;
* used by Bus.restoreMemory(), which is called by CPUState.restore(), after all other
* components have been restored and thus all Memory 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) {
if (this.controller) {
return (adw == null);
}
/*
* At this point, it's a consistency error for adw to be null; it's happened once already,
* when there was a restore bug in the Video component that added the frame buffer at the video
* 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)
*
* The afn parameter should be a 4-entry function table containing two byte handlers and
* two word handlers. See the static afnMemory table for an example.
*
* If no function table is specified, a default is selected based on the Memory type;
* similarly, any undefined entries in the table are filled with default handlers that fall
* back to the byte handlers, and if one or both byte handlers are undefined, they default
* to handlers that simply ignore the access.
*
* fDirect indicates that both the default AND the direct handlers should be updated. Direct
* handlers normally match the default handlers, except when "checked" handlers are installed;
* this allows "checked" handlers to know where to dispatch the call after performing checks.
* Examples of checks are read/write breakpoints, but it's really up to the Debugger to decide
* what the check consists of.
*
* @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.readWord = afn[2] || this.readWordDefault;
}
if (fDirect || fDirect === undefined) {
this.readByteDirect = afn[0] || this.readNone;
this.readWordDirect = afn[2] || this.readWordDefault;
}
},
/**
* 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[1] || this.writeNone;
this.writeWord = !this.fReadOnly && afn[3] || this.writeWordDefault;
}
if (fDirect || fDirect === undefined) {
this.writeByteDirect = afn[1] || this.writeNone;
this.writeWordDirect = afn[3] || this.writeWordDefault;
}
},
/**
* resetReadAccess()
*
* @this {MemoryPDP11}
*/
resetReadAccess: function() {
this.readByte = this.readByteDirect;
this.readWord = this.readWordDirect;
},
/**
* resetWriteAccess()
*
* @this {MemoryPDP11}
*/
resetWriteAccess: function() {
this.writeByte = this.fReadOnly? this.writeNone : this.writeByteDirect;
this.writeWord = this.fReadOnly? this.writeWordDefault : this.writeWordDirect;
},
/**
* 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? ('@' + this.dbg.toStrBase(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(), readWordNone() 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
* Memory 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.MEM) /* && !off */) {
this.dbg.printMessage("attempt to read invalid block %" + str.toHex(this.addr), true);
this.dbg.stopCPU();
}
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.MEM) /* && !off */) {
this.dbg.printMessage("attempt to write " + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
this.dbg.stopCPU();
}
},
/**
* readWordDefault(off, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readWordDefault: function readWordDefault(off, addr) {
return this.readByte(off++, addr++) | (this.readByte(off, addr) << 8);
},
/**
* writeWordDefault(off, w, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} w
* @param {number} addr
*/
writeWordDefault: function writeWordDefault(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);
},
/**
* readWordMemory(off, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readWordMemory: function readWordMemory(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;
},
/**
* writeWordMemory(off, w, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} w
* @param {number} addr
*/
writeWordMemory: function writeWordMemory(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);
},
/**
* readWordChecked(off, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readWordChecked: function readWordChecked(off, addr) {
if (DEBUGGER && this.dbg && this.addr != null) {
this.dbg.checkMemoryRead(this.addr + off, 2);
}
return this.readWordDirect(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);
},
/**
* writeWordChecked(off, w, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @param {number} w
*/
writeWordChecked: function writeWordChecked(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.writeWordDirect(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) {
var b = this.ab[off];
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
this.dbg.printMessage("Memory.readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true);
}
return b;
},
/**
* readWordBE(off, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readWordBE: function readWordBE(off, addr) {
return this.dv.getUint16(off, true);
},
/**
* readWordLE(off, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readWordLE: function readWordLE(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.
*/
var w = (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
this.dbg.printMessage("Memory.readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
}
return w;
},
/**
* 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;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
this.dbg.printMessage("Memory.writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true);
}
},
/**
* writeWordBE(off, w, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @param {number} w
*/
writeWordBE: function writeWordBE(off, w, addr) {
this.dv.setUint16(off, w, true);
this.fDirty = true;
},
/**
* writeWordLE(off, w, addr)
*
* @this {MemoryPDP11}
* @param {number} off
* @param {number} addr
* @param {number} w
*/
writeWordLE: function writeWordLE(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.
*/
if (off & 0x1) {
this.ab[off] = w;
this.ab[off+1] = w >> 8;
} else {
this.aw[off >> 1] = w;
}
this.fDirty = true;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
this.dbg.printMessage("Memory.writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true);
}
}
};
/*
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
* uses these defaults when any of the 4 handlers (ie, 2 byte handlers and 2 word handlers) are undefined.
*
MemoryPDP11.afnNone = [
MemoryPDP11.prototype.readNone,
MemoryPDP11.prototype.writeNone,
MemoryPDP11.prototype.readWordDefault,
MemoryPDP11.prototype.writeWordDefault
];
*/
MemoryPDP11.afnNone = [];
MemoryPDP11.afnMemory = [
MemoryPDP11.prototype.readByteMemory,
MemoryPDP11.prototype.writeByteMemory,
MemoryPDP11.prototype.readWordMemory,
MemoryPDP11.prototype.writeWordMemory
];
MemoryPDP11.afnChecked = [
MemoryPDP11.prototype.readByteChecked,
MemoryPDP11.prototype.writeByteChecked,
MemoryPDP11.prototype.readWordChecked,
MemoryPDP11.prototype.writeWordChecked
];
if (TYPEDARRAYS) {
MemoryPDP11.afnArrayBE = [
MemoryPDP11.prototype.readByteBE,
MemoryPDP11.prototype.writeByteBE,
MemoryPDP11.prototype.readWordBE,
MemoryPDP11.prototype.writeWordBE
];
MemoryPDP11.afnArrayLE = [
MemoryPDP11.prototype.readByteLE,
MemoryPDP11.prototype.writeByteLE,
MemoryPDP11.prototype.readWordLE,
MemoryPDP11.prototype.writeWordLE
];
}
if (NODE) module.exports = MemoryPDP11;

View file

@ -0,0 +1,91 @@
/**
* @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,
TRAP: 0x00000010,
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,
"trap": MessagesPDP11.TRAP,
"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;

View 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;

184
modules/pdp11/lib/panel.js Normal file
View file

@ -0,0 +1,184 @@
/**
* @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 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
View 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;

400
modules/pdp11/lib/rom.js Normal file
View file

@ -0,0 +1,400 @@
/**
* @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 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
* 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.
*
* @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);
var i;
if (sROMData.charAt(0) == "[" || sROMData.charAt(0) == "{") {
try {
/*
* The most likely source of any exception will be here: parsing the JSON-encoded ROM.
*/
var a, ib;
var rom = eval("(" + sROMData + ")");
if (a = rom['bytes']) {
this.abROM = a;
}
else if (a = rom['words']) {
/*
* Convert all WORDs into BYTEs, so that subsequent code only has to deal with abROM.
*/
this.abROM = new Array(a.length * 2);
for (i = 0, ib = 0; i < a.length; i++) {
this.abROM[ib++] = a[i] & 0xff;
this.abROM[ib++] = (a[i] >> 8) & 0xff;
this.assert(!(a[i] & ~0xffff));
}
}
else if (a = rom['data']) {
/*
* Convert all DWORDs into BYTEs, so that subsequent code only has to deal with abROM.
*/
this.abROM = new Array(a.length * 4);
for (i = 0, ib = 0; i < a.length; i++) {
this.abROM[ib++] = a[i] & 0xff;
this.abROM[ib++] = (a[i] >> 8) & 0xff;
this.abROM[ib++] = (a[i] >> 16) & 0xff;
this.abROM[ib++] = (a[i] >> 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 (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++) {
if (DEBUGGER && this.dbg) this.dbg.disableMessages();
this.bus.setByteDirect(addr + i, this.abROM[i]);
if (DEBUGGER && this.dbg) this.dbg.enableMessages();
}
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;

View file

@ -0,0 +1,681 @@
/**
* @fileoverview Implements the PDP11 SerialPort component.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2016-Oct-04
*
* 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 MessagesPDP11 = require("./messages");
}
/**
* SerialPortPDP11(parmsSerial)
*
* The SerialPort component has the following component-specific (parmsSerial) properties:
*
* adapter: adapter number; 0 if not defined
*
* binding: name of a control (based on its "binding" attribute) to bind to this port's I/O
*
* tabSize: set to a non-zero number to convert tabs to spaces (applies only to output to
* the above binding); default is 0 (no conversion)
*
* In the future, we may support 'port' and 'irq' properties that allow the machine to define a
* non-standard serial port configuration, instead of only our pre-defined 'adapter' configurations.
*
* NOTE: Since the XSL file defines 'adapter' as a number, not a string, there's no need to use
* parseInt(), and as an added benefit, we don't need to worry about whether a hex or decimal format
* was used.
*
* @constructor
* @extends Component
* @param {Object} parmsSerial
*/
function SerialPortPDP11(parmsSerial) {
this.iAdapter = parmsSerial['adapter'];
/**
* consoleOutput becomes a string that records serial port output if the 'binding' property is set to the
* reserved name "console". Nothing is written to the console, however, until a linefeed (0x0A) is output
* or the string length reaches a threshold (currently, 1024 characters).
*
* @type {string|null}
*/
this.consoleOutput = null;
/**
* controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()).
*
* Example: CTTY COM2
*
* The CTTY DOS command redirects all CON I/O to the specified serial port (eg, COM2), which it assumes is
* connected to a serial terminal, and therefore anything it *transmits* via COM2 will be displayed by the
* terminal. It further assumes that anything typed on such a terminal is NOT displayed, so as DOS *receives*
* serial input, DOS *transmits* the appropriate characters back to the terminal via COM2.
*
* As a result, controlIOBuffer only needs to be updated by the transmitByte() function.
*
* @type {Object}
*/
this.controlIOBuffer = null;
/*
* If controlIOBuffer is being used AND 'tabSize' is set, then we make an attempt to monitor the characters
* being echoed via transmitByte(), maintain a logical column position, and convert any tabs into the appropriate
* number of spaces.
*
* charBOL, if nonzero, is a character to automatically output at the beginning of every line. This probably
* isn't generally useful; I use it internally to preformat serial output.
*/
this.tabSize = parmsSerial['tabSize'];
this.charBOL = parmsSerial['charBOL'];
this.iLogicalCol = 0;
Component.call(this, "SerialPort", parmsSerial, SerialPortPDP11, MessagesPDP11.SERIAL);
var sBinding = parmsSerial['binding'];
if (sBinding == "console") {
this.consoleOutput = "";
} else {
/*
* NOTE: If sBinding is not the name of a valid Control Panel DOM element, this call does nothing.
*/
Component.bindExternalControl(this, sBinding, SerialPortPDP11.sIOBuffer);
}
/*
* No connection until initConnection() is called.
*/
this.sDataReceived = "";
this.connection = this.sendData = null;
/*
* Export all functions required by initConnection(); currently, this is the bare minimum (no flow control yet).
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
};
}
/*
* class SerialPortPDP11
* property {number} iAdapter
* property {number} portBase
* property {number} nIRQ
* property {Object} controlIOBuffer is a DOM element bound to the port (for rudimentary output; see transmitByte())
*
* NOTE: This class declaration started as a way of informing the code inspector of the controlIOBuffer property,
* which remained undefined until a setBinding() call set it later, but I've since decided that explicitly
* initializing such properties in the constructor is a better way to go -- even though it's more code -- because
* JavaScript compilers are supposed to be happier when the underlying object structures aren't constantly changing.
*
* Besides, I'm not sure I want to get into documenting every property this way, for this or any/every other class,
* let alone getting into which ones should be considered private or protected, because PCjs isn't really a library
* for third-party apps.
*/
Component.subclass(SerialPortPDP11);
/*
* Internal name used for the I/O buffer control, if any, that we bind to the SerialPort.
*
* Alternatively, if SerialPort wants to use another component's control (eg, the Panel's
* "print" control), it can specify the name of that control with the 'binding' property.
*
* For that binding to succeed, we also need to know the target component; for now, that's
* been hard-coded to "Panel", in part because that's one of the few components we can rely
* upon initializing before we do, but it would be a simple matter to include a component type
* or ID as part of the 'binding' property as well, if we need more flexibility later.
*/
SerialPortPDP11.sIOBuffer = "buffer";
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {SerialPortPDP11}
* @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, "buffer")
* @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
*/
SerialPortPDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
var serial = this;
switch (sBinding) {
case SerialPortPDP11.sIOBuffer:
this.bindings[sBinding] = this.controlIOBuffer = control;
/*
* By establishing an onkeypress handler here, we make it possible for DOS commands like
* "CTTY COM1" to more or less work (use "CTTY CON" to restore control to the DOS console).
*/
control.onkeydown = function onKeyDown(event) {
/*
* This is required in addition to onkeypress, because it's the only way to prevent
* BACKSPACE (keyCode 8) from being interpreted by the browser as a "Back" operation;
* moreover, not all browsers generate an onkeypress notification for BACKSPACE.
*
* A related problem exists for Ctrl-key combinations in most Windows-based browsers
* (eg, IE, Edge, Chrome for Windows, etc), because keys like Ctrl-C and Ctrl-S have
* special meanings (eg, Copy, Save). To the extent the browser will allow it, we
* attempt to disable that default behavior when this control receives an onkeydown
* event for one of those keys (probably the only event the browser generates for them).
*/
event = event || window.event;
var keyCode = event.keyCode;
if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) {
if (event.preventDefault) event.preventDefault();
if (keyCode > 0x40) keyCode -= 0x40;
serial.receiveData(keyCode);
}
return true;
};
control.onkeypress = function onKeyPress(event) {
/*
* Browser-independent keyCode extraction; refer to onKeyPress() and the other key event
* handlers in keyboard.js.
*/
event = event || window.event;
var keyCode = event.which || event.keyCode;
serial.receiveData(keyCode);
/*
* Since we're going to remove the "readonly" attribute from the <textarea> control
* (so that the soft keyboard activates on iOS), instead of calling preventDefault() for
* selected keys (eg, the SPACE key, whose default behavior is to scroll the page), we must
* now call it for *all* keys, so that the keyCode isn't added to the control immediately,
* on top of whatever the machine is echoing back, resulting in double characters.
*/
if (event.preventDefault) event.preventDefault();
return true;
};
/*
* Now that we've added an onkeypress handler that calls preventDefault() for ALL keys, the control
* itself no longer needs the "readonly" attribute; we primarily need to remove it for iOS browsers,
* so that the soft keyboard will activate, but it shouldn't hurt to remove the attribute for all browsers.
*/
control.removeAttribute("readonly");
return true;
default:
break;
}
return false;
};
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {SerialPortPDP11}
* @param {ComputerPDP11} cmp
* @param {BusPDP11} bus
* @param {CPUStatePDP11} cpu
* @param {DebuggerPDP11} dbg
*/
SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
var serial = this;
this.timerReceiveData = this.cpu.addTimer(function() {
if (!(serial.rcsr & PDP11.DL11.RCSR.RD)) {
if (serial.abReceive.length) {
serial.rbuf = serial.abReceive.shift();
serial.rcsr |= PDP11.DL11.RCSR.RD;
if (serial.rcsr & PDP11.DL11.RCSR.RIE) {
serial.cpu.interrupt(PDP11.DL11.DELAY, PDP11.DL11.PRI, PDP11.DL11.RVEC);
}
}
}
});
this.doneTransmitInterrupt = function() {
serial.xcsr |= PDP11.DL11.XCSR.READY;
return !!(serial.xcsr & PDP11.DL11.XCSR.TIE);
};
bus.addIOTable(this, SerialPortPDP11.UNIBUS_IOTABLE);
this.setReady();
};
/**
* initConnection()
*
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
* and "{sourcePort}" matches our idComponent, then look for a component with id "{targetMachine}.{targetPort}".
*
* If the target component is found, then verify that it has exported functions with the following names:
*
* receiveData(data): called when we have data to transmit; aliased internally to sendData(data)
*
* For now, we're not going to worry about communication in the other direction, because when the target component
* performs its own initConnection(), it will find our receiveData() function, at which point communication in both
* directions should be established.
*
* For added robustness, if the target machine initializes much more slowly than we do, and our connection attempt
* fails, that's OK, because when it finally initializes, its initConnection() will call our initConnection();
* if we've already initialized, no harm done.
*
* @this {SerialPortPDP11}
*/
SerialPortPDP11.prototype.initConnection = function()
{
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
if (sConnection) {
var asParts = sConnection.split('->');
if (asParts.length == 2) {
var sSourceID = str.trim(asParts[0]);
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
var sTargetID = str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
}
}
}
/*
* Changed from notice() to status() because sometimes a connection fails simply because one of us is a laggard.
*/
this.status("Unable to establish connection: " + sConnection);
}
}
};
/**
* powerUp(data, fRepower)
*
* @this {SerialPortPDP11}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
SerialPortPDP11.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
/*
* This is as late as we can currently wait to make our first inter-machine connection attempt;
* even so, the target machine's initialization process may still be ongoing, so any connection
* may be not fully resolved until the target machine performs its own initConnection(), which will
* in turn invoke our initConnection() again.
*/
this.initConnection();
if (!data || !this.restore) {
this.reset();
} else {
if (!this.restore(data)) return false;
}
}
return true;
};
/**
* powerDown(fSave, fShutdown)
*
* @this {SerialPortPDP11}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
SerialPortPDP11.prototype.powerDown = function(fSave, fShutdown)
{
return fSave? this.save() : true;
};
/**
* reset()
*
* @this {SerialPortPDP11}
*/
SerialPortPDP11.prototype.reset = function()
{
this.initState();
};
/**
* save()
*
* This implements save support for the SerialPort component.
*
* @this {SerialPortPDP11}
* @return {Object}
*/
SerialPortPDP11.prototype.save = function()
{
var state = new State(this);
state.set(0, this.saveRegisters());
return state.data();
};
/**
* restore(data)
*
* This implements restore support for the SerialPort component.
*
* @this {SerialPortPDP11}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
SerialPortPDP11.prototype.restore = function(data)
{
return this.initState(data[0]);
};
/**
* initState(data)
*
* @this {SerialPortPDP11}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
SerialPortPDP11.prototype.initState = function(data)
{
this.rbuf = 0;
this.rcsr = 0;
this.xcsr = PDP11.DL11.XCSR.READY;
this.abReceive = [];
return true;
};
/**
* saveRegisters()
*
* @this {SerialPortPDP11}
* @return {Array}
*/
SerialPortPDP11.prototype.saveRegisters = function()
{
return [];
};
/**
* receiveData(data)
*
* This replaces the old sendRBR() function, which expected an Array of bytes. We still support that,
* but in order to support connections with other SerialPort components (ie, the PC8080 SerialPort), we
* have added support for numbers and strings as well.
*
* @this {SerialPortPDP11}
* @param {number|string|Array} data
* @return {boolean} true if received, false if not
*/
SerialPortPDP11.prototype.receiveData = function(data)
{
if (typeof data == "number") {
this.abReceive.push(data);
}
else if (typeof data == "string") {
for (var i = 0; i < data.length; i++) {
this.abReceive.push(data.charCodeAt(i));
}
}
else {
this.abReceive = this.abReceive.concat(data);
}
this.cpu.setTimer(this.timerReceiveData, 50);
return true; // for now, return true regardless, since we're buffering everything anyway
};
SerialPortPDP11.prototype.advanceRBUF = function()
{
};
/**
* transmitByte(b)
*
* @this {SerialPortPDP11}
* @param {number} b
* @return {boolean} true if transmitted, false if not
*/
SerialPortPDP11.prototype.transmitByte = function(b)
{
var fTransmitted = false;
this.printMessage("transmitByte(" + str.toHexByte(b) + ")");
if (this.sendData) {
if (this.sendData.call(this.connection, b)) {
fTransmitted = true;
}
}
if (this.controlIOBuffer) {
if (b == 0x0D) {
this.iLogicalCol = 0;
}
else if (b == 0x08) {
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
/*
* TODO: Back up the correct number of columns if the character erased was a tab.
*/
if (this.iLogicalCol > 0) this.iLogicalCol--;
}
else {
var s = str.toASCIICode(b); // formerly: String.fromCharCode(b);
var nChars = s.length; // formerly: (b >= 0x20? 1 : 0);
if (b < 0x20 && nChars == 1) nChars = 0;
if (b == 0x09) {
var tabSize = this.tabSize || 8;
nChars = tabSize - (this.iLogicalCol % tabSize);
if (this.tabSize) s = str.pad("", nChars);
}
if (this.charBOL && !this.iLogicalCol && nChars) s = String.fromCharCode(this.charBOL) + s;
this.controlIOBuffer.value += s;
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
this.iLogicalCol += nChars;
}
fTransmitted = true;
}
else if (this.consoleOutput != null) {
if (b == 0x0A || this.consoleOutput.length >= 1024) {
this.println(this.consoleOutput);
this.consoleOutput = "";
}
if (b != 0x0A) {
this.consoleOutput += String.fromCharCode(b);
}
fTransmitted = true;
}
return fTransmitted;
};
/**
* readRCSR(addr)
*
* @this {SerialPortPDP11}
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
* @return {number}
*/
SerialPortPDP11.prototype.readRCSR = function(addr)
{
return this.rcsr & PDP11.DL11.RCSR.RMASK;
};
/**
* writeRCSR(data, addr)
*
* @this {SerialPortPDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
*/
SerialPortPDP11.prototype.writeRCSR = function(data, addr)
{
this.rcsr = (this.rcsr & ~PDP11.DL11.RCSR.WMASK) | (data & PDP11.DL11.RCSR.WMASK);
};
/**
* readRBUF(addr)
*
* @this {SerialPortPDP11}
* @param {number} addr (eg, PDP11.UNIBUS.RBUF or 177562)
* @return {number}
*/
SerialPortPDP11.prototype.readRBUF = function(addr)
{
this.rcsr &= ~PDP11.DL11.RCSR.RD;
if (this.abReceive.length > 0) {
this.cpu.setTimer(this.timerReceiveData, 50);
}
return this.rbuf;
};
/**
* writeRBUF(data, addr)
*
* @this {SerialPortPDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.RBUF or 177562)
*/
SerialPortPDP11.prototype.writeRBUF = function(data, addr)
{
};
/**
* readXCSR(addr)
*
* @this {SerialPortPDP11}
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
* @return {number}
*/
SerialPortPDP11.prototype.readXCSR = function(addr)
{
return this.xcsr;
};
/**
* writeXCSR(data, addr)
*
* @this {SerialPortPDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
*/
SerialPortPDP11.prototype.writeXCSR = function(data, addr)
{
/*
* If the device is READY, and IE is transitioning on, then request an interrupt.
*/
if ((this.xcsr & (PDP11.DL11.XCSR.READY | PDP11.DL11.XCSR.TIE)) == PDP11.DL11.XCSR.READY && (data & PDP11.DL11.XCSR.TIE)) {
this.cpu.interrupt(PDP11.DL11.XCSR.DELAY, PDP11.DL11.PRI, PDP11.DL11.XVEC, this.doneTransmitInterrupt);
}
this.xcsr = (this.xcsr & ~PDP11.DL11.XCSR.WMASK) | (data & PDP11.DL11.XCSR.WMASK);
};
/**
* readXBUF(addr)
*
* @this {SerialPortPDP11}
* @param {number} addr (eg, PDP11.UNIBUS.XBUF or 177566)
* @return {number}
*/
SerialPortPDP11.prototype.readXBUF = function(addr)
{
return 0;
};
/**
* writeXBUF(data, addr)
*
* @this {SerialPortPDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.XBUF or 177566)
*/
SerialPortPDP11.prototype.writeXBUF = function(data, addr)
{
data &= PDP11.DL11.XBUF.DATA;
if (data) {
this.transmitByte(data);
this.xcsr &= ~PDP11.DL11.XCSR.READY;
this.cpu.interrupt(PDP11.DL11.XBUF.DELAY, PDP11.DL11.PRI, PDP11.DL11.XVEC, this.doneTransmitInterrupt);
}
};
/*
* ES6 ALERT: As you can see below, I've finally started using computed property names.
*/
SerialPortPDP11.UNIBUS_IOTABLE = {
[PDP11.UNIBUS.RCSR]: /* 177560 */ [null, null, SerialPortPDP11.prototype.readRCSR, SerialPortPDP11.prototype.writeRCSR, "RCSR"],
[PDP11.UNIBUS.RBUF]: /* 177562 */ [null, null, SerialPortPDP11.prototype.readRBUF, SerialPortPDP11.prototype.writeRBUF, "RBUF"],
[PDP11.UNIBUS.XCSR]: /* 177564 */ [null, null, SerialPortPDP11.prototype.readXCSR, SerialPortPDP11.prototype.writeXCSR, "XCSR"],
[PDP11.UNIBUS.XBUF]: /* 177566 */ [null, null, SerialPortPDP11.prototype.readXBUF, SerialPortPDP11.prototype.writeXBUF, "XBUF"]
};
/**
* SerialPortPDP11.init()
*
* This function operates on every HTML element of class "serial", extracting the
* JSON-encoded parameters for the SerialPort constructor from the element's "data-value"
* attribute, invoking the constructor to create a SerialPort component, and then binding
* any associated HTML controls to the new component.
*/
SerialPortPDP11.init = function()
{
var aeSerial = Component.getElementsByClass(document, PDP11.APPCLASS, "serial");
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
var eSerial = aeSerial[iSerial];
var parmsSerial = Component.getComponentParms(eSerial);
var serial = new SerialPortPDP11(parmsSerial);
Component.bindComponentControls(serial, eSerial, PDP11.APPCLASS);
}
};
/*
* Initialize every SerialPort module on the page.
*/
web.onInit(SerialPortPDP11.init);
if (NODE) module.exports = SerialPortPDP11;

View file

@ -1,5 +1,5 @@
/**
* @fileoverview The Component class used by C1Pjs and PCx86.
* @fileoverview The Component class used by all PCjs components.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2012-May-14
@ -30,8 +30,8 @@
*/
/*
* All the C1Pjs and PCjs components now use JSDoc types, primarily so that Google's Closure Compiler
* will compile everything with ZERO warnings. For more information about the JSDoc types supported by
* All the PCjs components now use JSDoc types, primarily so that Google's Closure Compiler will
* compile everything with ZERO warnings. For more information about the JSDoc types supported by
* the Closure Compiler:
*
* https://developers.google.com/closure/compiler/docs/js-for-compiler#types
@ -110,11 +110,11 @@ function Component(type, parms, constructor, bitsMessage)
* subclasses to do the same, to reduce the property clutter we have to wade through while debugging.
*/
this.flags = {
fReady: false,
fBusy: false,
fBusyCancel: false,
fPowered: false,
fError: false
ready: false,
busy: false,
busyCancel: false,
powered: false,
error: false
};
this.fnReady = null;
@ -769,8 +769,8 @@ Component.prototype = {
* TODO: Add a task to the build process that "asserts" there are no instances of "assertion failure" in RELEASE builds.
*
* @this {Component}
* @param {boolean} f is the expression we are asserting to be true
* @param {string} [s] is description of the assertion on failure
* @param {boolean|number} f is the expression asserted to be true
* @param {string} [s] is a description of the assertion to be displayed or logged on failure
*/
assert: function(f, s) {
if (DEBUG) {
@ -806,7 +806,7 @@ Component.prototype = {
}
},
/**
* println(s, type)
* println(s, type, id)
*
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
*
@ -856,7 +856,7 @@ Component.prototype = {
* @param {string} s describes a fatal error condition
*/
setError: function(s) {
this.flags.fError = true;
this.flags.error = true;
this.notice(s); // TODO: Any cases where we should still prefix this string with "Fatal error: "?
},
/**
@ -867,7 +867,7 @@ Component.prototype = {
* @this {Component}
*/
clearError: function() {
this.flags.fError = false;
this.flags.error = false;
},
/**
* isError()
@ -878,7 +878,7 @@ Component.prototype = {
* @return {boolean} true if a fatal error condition exists, false if not
*/
isError: function() {
if (this.flags.fError) {
if (this.flags.error) {
this.println(this.toString() + " error");
return true;
}
@ -899,14 +899,14 @@ Component.prototype = {
*/
isReady: function(fnReady) {
if (fnReady) {
if (this.flags.fReady) {
if (this.flags.ready) {
fnReady();
} else {
if (MAXDEBUG) this.log("NOT ready");
this.fnReady = fnReady;
}
}
return this.flags.fReady;
return this.flags.ready;
},
/**
* setReady(fReady)
@ -917,9 +917,9 @@ Component.prototype = {
* @param {boolean} [fReady] is assumed to indicate "ready" unless EXPLICITLY set to false
*/
setReady: function(fReady) {
if (!this.flags.fError) {
this.flags.fReady = (fReady !== false);
if (this.flags.fReady) {
if (!this.flags.error) {
this.flags.ready = (fReady !== false);
if (this.flags.ready) {
if (MAXDEBUG /* || this.name */) this.log("ready");
var fnReady = this.fnReady;
this.fnReady = null;
@ -937,38 +937,36 @@ Component.prototype = {
* @return {boolean} true if "busy", false if not
*/
isBusy: function(fCancel) {
if (this.flags.fBusy) {
if (this.flags.busy) {
if (fCancel) {
this.flags.fBusyCancel = true;
this.flags.busyCancel = true;
} else if (fCancel === undefined) {
this.println(this.toString() + " busy");
}
}
return this.flags.fBusy;
return this.flags.busy;
},
/**
* setBusy(fBusy)
*
* Update the current busy state; if an fCancel request is pending, it will be honored now.
* Update the current busy state; if a busyCancel request is pending, it will be honored now.
*
* @this {Component}
* @param {boolean} fBusy
* @return {boolean}
*/
setBusy: function(fBusy) {
if (this.flags.fBusyCancel) {
if (this.flags.fBusy) {
this.flags.fBusy = false;
}
this.flags.fBusyCancel = false;
if (this.flags.busyCancel) {
this.flags.busy = false;
this.flags.busyCancel = false;
return false;
}
if (this.flags.fError) {
if (this.flags.error) {
this.println(this.toString() + " error");
return false;
}
this.flags.fBusy = fBusy;
return this.flags.fBusy;
this.flags.busy = fBusy;
return this.flags.busy;
},
/**
* powerUp(fSave)
@ -979,7 +977,7 @@ Component.prototype = {
* @return {boolean} true if successful, false if failure
*/
powerUp: function(data, fRepower) {
this.flags.fPowered = true;
this.flags.powered = true;
return true;
},
/**
@ -991,7 +989,7 @@ Component.prototype = {
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
powerDown: function(fSave, fShutdown) {
if (fShutdown) this.flags.fPowered = false;
if (fShutdown) this.flags.powered = false;
return true;
},
/**
@ -1059,4 +1057,48 @@ Component.prototype = {
}
};
/*
* The following polyfills provide ES5 functionality that's missing in older browsers (eg, IE8),
* allowing PCjs apps to run without slamming into exceptions; however, due to the lack of HTML5 canvas
* support in those browsers, all you're likely to see are "soft" errors (eg, "Missing <canvas> support").
*
* Perhaps we can implement a text-only faux video display for a fun retro-browser experience someday.
*
* TODO: Come up with a better place to put these polyfills. We will likely have more if we decide to
* make the leap from ES5 to ES6 features.
*/
/*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
*/
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(obj, start) {
for (var i = (start || 0), j = this.length; i < j; i++) {
if (this[i] === obj) { return i; }
}
return -1;
}
}
/*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
*/
if (!Function.prototype.bind) {
Function.prototype.bind = function(obj) {
if (typeof this != "function") {
// Closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind: non-callable object");
}
var args = Array.prototype.slice.call(arguments, 1);
var fToBind = this;
var fnNOP = /** @constructor */ (function() {});
var fnBound = function() {
return fToBind.apply(this instanceof fnNOP && obj? this : obj, args.concat(Array.prototype.slice.call(arguments)));
};
fnNOP.prototype = this.prototype;
fnBound.prototype = new fnNOP();
return fnBound;
};
}
if (NODE) module.exports = Component;

View file

@ -0,0 +1,705 @@
/**
* @fileoverview Common PCjs Debugger support.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2012-Jun-21
*
* Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* 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 (DEBUGGER) {
if (NODE) {
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
}
}
/**
* Debugger Address Object
*
* This is the basic structure; other debuggers may extend it.
*
* addr address
* fTemporary true if this is a temporary breakpoint address
* sCmd set for breakpoint addresses if there's an associated command string
* aCmds preprocessed commands (from sCmd)
*
* @typedef {{
* addr:(number|undefined),
* fTemporary:(boolean|undefined),
* sCmd:(string|undefined),
* aCmds:(Array.<string>|undefined)
* }} DbgAddr
*/
var DbgAddr;
/**
* Debugger(parmsDbg)
*
* The Debugger component supports the following optional (parmsDbg) properties:
*
* base: the base to use for most numeric input/output (default is 16)
*
* The Debugger component is a shared component containing a subset of functionality used by
* the other CPU-specific Debuggers (eg, DebuggerX86). Over time, the goal is to factor out as
* much common debugging support as possible from those components into this one.
*
* @constructor
* @extends Component
* @param {Object} parmsDbg
*/
function Debugger(parmsDbg)
{
if (DEBUGGER) {
Component.call(this, "Debugger", parmsDbg, Debugger);
/*
* Default base used to display all values; modified with the "s base" command.
*/
this.nBase = parmsDbg['base'] || 16;
this.fParens = false;
/*
* These keep track of instruction activity, but only when tracing or when Debugger checks
* have been enabled (eg, one or more breakpoints have been set).
*
* They are zeroed by the reset() notification handler. cInstructions is advanced by
* stepCPU() and checkInstruction() calls. nCycles is updated by every stepCPU() or stop()
* call and simply represents the number of cycles performed by the last run of instructions.
*/
this.nCycles = 0;
this.cOpcodes = this.cOpcodesStart = 0;
/*
* fAssemble is true when "assemble mode" is active, false when not.
*/
this.fAssemble = false;
/*
* This maintains command history. New commands are inserted at index 0 of the array.
* When Enter is pressed on an empty input buffer, we default to the command at aPrevCmds[0].
*/
this.iPrevCmd = -1;
this.aPrevCmds = [];
/*
* aVariables is an object with properties that grow as setVariable() assigns more variables;
* each property corresponds to one variable, where the property name is the variable name (ie,
* a string beginning with a letter or underscore, followed by zero or more additional letters,
* digits, or underscores) and the property value is the variable's numeric value. See doVar()
* and setVariable() for details.
*
* Note that parseValue() parses variables before numbers, so any variable that looks like a
* unprefixed hex value (eg, "a5" as opposed to "0xa5") will trump the numeric value. Unprefixed
* hex values are a convenience of parseValue(), which always calls str.parseInt() with a default
* base of 16; however, that default be overridden with a variety of explicit prefixes or suffixes
* (eg, a leading "0o" to indicate octal, a trailing period to indicate decimal, etc.)
*
* See str.parseInt() for more details about supported numbers.
*/
this.aVariables = {};
} // endif DEBUGGER
}
if (DEBUGGER) {
Component.subclass(Debugger);
Debugger.aBinOpPrecedence = {
'||': 0, // logical OR
'&&': 1, // logical AND
'|': 2, // bitwise OR
'^': 3, // bitwise XOR
'&': 4, // bitwise AND
'!=': 5, // inequality
'==': 5, // equality
'>=': 6, // greater than or equal to
'>': 6, // greater than
'<=': 6, // less than or equal to
'<': 6, // less than
'>>>': 7, // unsigned bitwise right shift
'>>': 7, // bitwise right shift
'<<': 7, // bitwise left shift
'-': 8, // subtraction
'+': 8, // addition
'%': 9, // remainder
'/': 9, // division
'*': 9 // multiplication
};
/**
* getRegIndex(sReg, off)
*
* NOTE: This must be implemented by the individual debuggers.
*
* @this {Debugger}
* @param {string} sReg
* @param {number} [off] optional offset into sReg
* @return {number} register index, or -1 if not found
*/
Debugger.prototype.getRegIndex = function(sReg, off)
{
return -1;
};
/**
* getRegValue(iReg)
*
* NOTE: This must be implemented by the individual debuggers.
*
* @this {Debugger}
* @param {number} iReg
* @return {number|undefined}
*/
Debugger.prototype.getRegValue = function(iReg)
{
return undefined;
};
/**
* parseAddrReference(s, sAddr)
*
* Returns the given string with the given address reference replaced with the contents of that address.
*
* NOTE: This must be implemented by the individual debuggers.
*
* @this {Debugger}
* @param {string} s
* @param {string} sAddr
* @return {string}
*/
Debugger.prototype.parseAddrReference = function(s, sAddr)
{
return s.replace('[' + sAddr + ']', "unimplemented");
};
/**
* getNextCommand()
*
* @this {Debugger}
* @return {string}
*/
Debugger.prototype.getNextCommand = function()
{
var sCmd;
if (this.iPrevCmd > 0) {
sCmd = this.aPrevCmds[--this.iPrevCmd];
} else {
sCmd = "";
this.iPrevCmd = -1;
}
return sCmd;
};
/**
* getPrevCommand()
*
* @this {Debugger}
* @return {string|null}
*/
Debugger.prototype.getPrevCommand = function()
{
var sCmd = null;
if (this.iPrevCmd < this.aPrevCmds.length - 1) {
sCmd = this.aPrevCmds[++this.iPrevCmd];
}
return sCmd;
};
/**
* parseCommand(sCmd, fSave, chSep)
*
* @this {Debugger}
* @param {string|undefined} sCmd
* @param {boolean} [fSave] is true to save the command, false if not
* @param {string} [chSep] is the command separator character (default is ';')
* @return {Array.<string>}
*/
Debugger.prototype.parseCommand = function(sCmd, fSave, chSep)
{
if (fSave) {
if (!sCmd) {
if (this.fAssemble) {
sCmd = "end";
} else {
sCmd = this.aPrevCmds[this.iPrevCmd+1];
}
} else {
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
this.iPrevCmd = 0;
}
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
this.aPrevCmds.splice(0, 0, sCmd);
this.iPrevCmd = 0;
}
this.iPrevCmd--;
}
}
var a = [];
if (sCmd) {
/*
* With the introduction of breakpoint commands (ie, quoted command sequences
* associated with a breakpoint), we can no longer perform simplistic splitting.
*
* a = sCmd.split(chSep || ';');
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
*
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
*
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
* DOUBLE double-quotes with single quotes.
*/
sCmd = sCmd.replace(/""/g, "'");
var iPrev = 0;
var chQuote = null;
chSep = chSep || ';';
/*
* NOTE: Processing charAt() up to and INCLUDING length is not a typo; we're taking
* advantage of the fact that charAt() with an invalid index returns an empty string,
* allowing us to use the same substring() call to capture the final portion of sCmd.
*
* In a sense, it allows us to pretend that the string ends with a zero terminator.
*/
for (var i = 0; i <= sCmd.length; i++) {
var ch = sCmd.charAt(i);
if (ch == '"' || ch == "'") {
if (!chQuote) {
chQuote = ch;
} else if (ch == chQuote) {
chQuote = null;
}
}
else if (ch == chSep && !chQuote || !ch) {
/*
* Recall that substring() accepts starting (inclusive) and ending (exclusive)
* indexes, whereas substr() accepts a starting index and a length. We need the former.
*/
a.push(str.trim(sCmd.substring(iPrev, i)));
iPrev = i + 1;
}
}
}
return a;
};
/**
* evalExpression(aVals, aOps, cOps)
*
* In Node, if you set a variable to 0x80000001; ie:
*
* foo=0x80000001|0
*
* and then calculate foo*foo using "(foo*foo).toString(2)", the result is:
*
* '11111111111111111111111111111100000000000000000000000000000000'
*
* which is slightly incorrect because it has overflowed JavaScript's floating-point precision.
*
* 0x80000001 in decimal is -2147483647, so the product is 4611686014132420609, which is 0x3FFFFFFF00000001.
*
* @this {Debugger}
* @param {Array.<number>} aVals
* @param {Array.<string>} aOps
* @param {number} [cOps] (default is all)
* @return {boolean} true if successful, false if error
*/
Debugger.prototype.evalExpression = function(aVals, aOps, cOps)
{
cOps = cOps || -1;
while (cOps-- && aOps.length) {
var chOp = aOps.pop();
if (aVals.length < 2) return false;
var valNew;
var val2 = aVals.pop();
var val1 = aVals.pop();
switch(chOp) {
case '*':
valNew = val1 * val2;
break;
case '/':
if (!val2) return false;
valNew = val1 / val2;
break;
case '%':
if (!val2) return false;
valNew = val1 % val2;
break;
case '+':
valNew = val1 + val2;
break;
case '-':
valNew = val1 - val2;
break;
case '<<':
valNew = val1 << val2;
break;
case '>>':
valNew = val1 >> val2;
break;
case '>>>':
valNew = val1 >>> val2;
break;
case '<':
valNew = (val1 < val2? 1 : 0);
break;
case '<=':
valNew = (val1 <= val2? 1 : 0);
break;
case '>':
valNew = (val1 > val2? 1 : 0);
break;
case '>=':
valNew = (val1 >= val2? 1 : 0);
break;
case '==':
valNew = (val1 == val2? 1 : 0);
break;
case '!=':
valNew = (val1 != val2? 1 : 0);
break;
case '&':
valNew = val1 & val2;
break;
case '^':
valNew = val1 ^ val2;
break;
case '|':
valNew = val1 | val2;
break;
case '&&':
valNew = (val1 && val2? 1 : 0);
break;
case '||':
valNew = (val1 || val2? 1 : 0);
break;
default:
return false;
}
aVals.push(valNew|0);
}
return true;
};
/**
* parseExpression(sExp, fPrint)
*
* A quick-and-dirty expression parser. It takes an expression like:
*
* EDX+EDX*4+12345678
*
* and builds a value stack in aVals and a "binop" (binary operator) stack in aOps:
*
* aVals aOps
* ----- ----
* EDX +
* EDX *
* 4 +
* ...
*
* We pop 1 "binop" from aOps and 2 values from aVals whenever a "binop" of lower priority than its
* predecessor is encountered, evaluate, and push the result back onto aVals.
*
* Unary operators like '~' and ternary operators like '?:' are not supported; neither are parentheses.
*
* However, parseReference() now makes it possible to write parenthetical-style sub-expressions by using
* {...} (braces), as well as address references by using [...] (brackets).
*
* Why am I using braces instead of parentheses for sub-expressions? Because parseReference() serves
* multiple purposes, the other being reference replacement in message strings passing through replaceRegs(),
* and I didn't want parentheses taking on a new meaning in message strings.
*
* However, a Debugger can override this choice by setting fParens to true, if there's no conflict in its
* replaceRegs() implementation.
*
* @this {Debugger}
* @param {string|undefined} sExp
* @param {boolean} [fPrint] is true to print all resolved values, false for quiet parsing
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
*/
Debugger.prototype.parseExpression = function(sExp, fPrint)
{
var value;
if (sExp) {
/*
* First process (and eliminate) any references, aka sub-expressions.
*/
sExp = this.parseReference(sExp);
var i = 0;
var fError = false;
var sExpOrig = sExp;
var aVals = [], aOps = [];
/*
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a regexp split():
* when the regexp uses a capturing pattern, the resulting array will include entries for all the pattern
* matches along with the non-matches. This effectively means that, in the set of expressions that we
* support, all even entries in asValues will contain "values" and all odd entries will contain "operators".
*
* And although I tried to list the supported operators in "precedential" order, bitwise operators must
* be out-of-order so that we don't mistakenly match either '>' or '<' when they're part of '>>' or '<<'.
*/
var regExp = /(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
var asValues = sExp.split(regExp);
while (i < asValues.length) {
var sValue = asValues[i++];
var cchValue = sValue.length;
var s = str.trim(sValue);
if (!s) {
fError = true;
break;
}
var v = this.parseValue(s, null, fPrint === false);
if (v === undefined) {
fError = true;
fPrint = false;
break;
}
aVals.push(v);
if (i == asValues.length) break;
var sOp = asValues[i++], cchOp = sOp.length;
this.assert(Debugger.aBinOpPrecedence[sOp] != null);
if (aOps.length && Debugger.aBinOpPrecedence[sOp] < Debugger.aBinOpPrecedence[aOps[aOps.length-1]]) {
this.evalExpression(aVals, aOps, 1);
}
aOps.push(sOp);
sExp = sExp.substr(cchValue + cchOp);
}
if (!this.evalExpression(aVals, aOps) || aVals.length != 1) {
fError = true;
}
if (!fError) {
value = aVals.pop();
if (fPrint) this.printValue(null, value);
} else {
if (fPrint) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
}
}
return value;
};
/**
* parseReference(s)
*
* Returns the given string with any "{expression}" sequences replaced with the value of the expression,
* and any "[address]" references replaced with the contents of the address. Expressions are parsed BEFORE
* addresses. Owing to this function's simplistic parsing, nested braces/brackets are not supported
* (define intermediate variables if needed).
*
* @this {Debugger}
* @param {string} s
* @return {string}
*/
Debugger.prototype.parseReference = function(s)
{
var a;
var chOpen = this.fParens? '(' : '{';
var chClose = this.fParens? ')' : '}';
var reSubExp = new RegExp(this.fParens? "\\((.*?)\\)" : "\\{(.*?)\\}");
while (a = s.match(reSubExp)) {
if (a[1].indexOf(chOpen) >= 0) break; // unsupported nested brace(s)
var value = this.parseExpression(a[1]);
s = s.replace(chOpen + a[1] + chClose, value != null? this.toStrBase(value) : "undefined");
}
while (a = s.match(/\[(.*?)]/)) {
if (a[1].indexOf('[') >= 0) break; // unsupported nested bracket(s)
s = this.parseAddrReference(s, a[1]);
}
return this.parseSysVars(s);
};
/**
* parseSysVars(s)
*
* Returns the given string with any recognized "$var" replaced with its value; eg:
*
* $ops: the number of opcodes executed since the last time it was displayed (or reset)
*
* @this {Debugger}
* @param {string} s
* @return {string}
*/
Debugger.prototype.parseSysVars = function(s)
{
var a;
while (a = s.match(/\$([a-z]+)/i)) {
var v = null;
switch(a[1].toLowerCase()) {
case "ops":
v = this.cOpcodes - this.cOpcodesStart;
break;
}
if (v == null) break;
s = s.replace(a[0], v.toString());
}
return s;
};
/**
* parseValue(sValue, sName, fQuiet)
*
* @this {Debugger}
* @param {string|undefined} sValue
* @param {string|null} [sName] is the name of the value, if any
* @param {boolean} [fQuiet]
* @return {number|undefined} numeric value, or undefined if sValue is either undefined or invalid
*/
Debugger.prototype.parseValue = function(sValue, sName, fQuiet)
{
var value;
if (sValue != null) {
var iReg = this.getRegIndex(sValue);
if (iReg >= 0) {
value = this.getRegValue(iReg);
} else {
value = this.getVariable(sValue);
if (value == null) value = str.parseInt(sValue, this.nBase);
}
if (value == null && !fQuiet) this.println("invalid " + (sName? sName : "value") + ": " + sValue);
} else {
if (!fQuiet) this.println("missing " + (sName || "value"));
}
return value;
};
/**
* printValue(sVar, value)
*
* @this {Debugger}
* @param {string|null} sVar
* @param {number|undefined} value
* @return {boolean} true if value defined, false if not
*/
Debugger.prototype.printValue = function(sVar, value)
{
var sValue;
var fDefined = false;
if (value !== undefined) {
fDefined = true;
sValue = str.toHex(value, 0, true) + " " + value + ". " + str.toOct(value, 0, true) + " " + str.toBinBytes(value, 0, true);
if (value >= 0x20 && value < 0x7F) {
sValue += " '" + String.fromCharCode(value) + "'";
}
}
sVar = (sVar != null? (sVar + ": ") : "");
this.println(sVar + sValue);
return fDefined;
};
/**
* printVariable(sVar)
*
* @this {Debugger}
* @param {string} [sVar]
* @return {boolean} true if all value(s) defined, false if not
*/
Debugger.prototype.printVariable = function(sVar)
{
if (sVar) {
return this.printValue(sVar, this.aVariables[sVar]);
}
var cVariables = 0;
for (sVar in this.aVariables) {
this.printValue(sVar, this.aVariables[sVar]);
cVariables++;
}
return cVariables > 0;
};
/**
* delVariable(sVar)
*
* @this {Debugger}
* @param {string} sVar
*/
Debugger.prototype.delVariable = function(sVar)
{
delete this.aVariables[sVar];
};
/**
* getVariable(sVar)
*
* @this {Debugger}
* @param {string} sVar
* @return {number|undefined}
*/
Debugger.prototype.getVariable = function(sVar)
{
return this.aVariables[sVar];
};
/**
* setVariable(sVar, value)
*
* @this {Debugger}
* @param {string} sVar
* @param {number} value
*/
Debugger.prototype.setVariable = function(sVar, value)
{
this.aVariables[sVar] = value;
};
/**
* toStrBase(n, nBytes, fStripLeadingZeros)
*
* Use this instead of str.toHex() or str.toOct() to convert bytes/words to the Debugger's default base.
*
* @this {Debugger}
* @param {number|null|undefined} n
* @param {number} [nBytes] is the number of bytes to display, which we translate into a number of characters
* @param {boolean} [fStripLeadingZeros]
* @return {string}
*/
Debugger.prototype.toStrBase = function(n, nBytes, fStripLeadingZeros)
{
var s;
switch(this.nBase) {
case 8:
s = str.toOct(n, nBytes * 3);
break;
case 10:
s = n.toString();
break;
case 16:
default:
s = str.toHex(n, nBytes * 2);
break;
}
if (fStripLeadingZeros && s.charAt(0) == '0') {
s = s.replace(/^0+([0-9A-F]+)$/i, "$1");
}
return s;
};
} // endif DEBUGGER
if (NODE) module.exports = Debugger;

View file

@ -35,13 +35,9 @@
* @define {string}
*/
var APPVERSION = "1.x.x"; // this @define is overridden by the Closure Compiler with the version in package.json
var XMLVERSION = null; // this is set in non-COMPILED builds by embedMachine() if a version number was found in the machine XML
var COPYRIGHT = "Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>";
var LICENSE = "License: GPL version 3 or later <http://gnu.org/licenses/gpl.html>";
var CSSCLASS = "pcjs";
/**

View file

@ -35,8 +35,8 @@
if (NODE) {
var Component = require("./component");
var str = require("./strlib");
var web = require("./weblib");
var str = require("./strlib");
var web = require("./weblib");
}
/*
@ -55,7 +55,7 @@ var fAsync = true;
var cAsyncMachines = 0;
/**
* loadXML(sFile, idMachine, sAppClass, sParms, fResolve, display, done)
* loadXML(sFile, idMachine, sAppName, sAppClass, sParms, fResolve, display, done)
*
* This is the preferred way to load all XML and XSL files. It uses getResource()
* to load them as strings, which parseXML() can massage before parsing/transforming them.
@ -80,13 +80,14 @@ var cAsyncMachines = 0;
*
* @param {string} sXMLFile
* @param {string|null|undefined} idMachine
* @param {string|null|undefined} sAppName
* @param {string|null|undefined} sAppClass
* @param {string|null|undefined} sParms
* @param {boolean} fResolve is true to resolve any "ref" attributes
* @param {function(string)} display
* @param {function(string,Object)} done (string contains the unparsed XML string data, and Object contains a parsed XML object)
*/
function loadXML(sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done)
function loadXML(sXMLFile, idMachine, sAppName, sAppClass, sParms, fResolve, display, done)
{
var doneLoadXML = function(sURLName, sXML, nErrorCode) {
if (nErrorCode) {
@ -94,14 +95,14 @@ function loadXML(sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done
done(sXML, null);
return;
}
parseXML(sXML, sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done);
parseXML(sXML, sXMLFile, idMachine, sAppName, sAppClass, sParms, fResolve, display, done);
};
display("Loading " + sXMLFile + "...");
web.getResource(sXMLFile, null, fAsync, doneLoadXML);
}
/**
* parseXML(sXML, sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done)
* parseXML(sXML, sXMLFile, idMachine, sAppName, sAppClass, sParms, fResolve, display, done)
*
* Generates an XML document from an XML string. This function also provides a work-around for XSLT's
* lack of support for the document() function (at least on some browsers), by replacing every reference
@ -110,13 +111,14 @@ function loadXML(sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done
* @param {string} sXML
* @param {string|null} sXMLFile
* @param {string|null|undefined} idMachine
* @param {string|null|undefined} sAppName
* @param {string|null|undefined} sAppClass
* @param {string|null|undefined} sParms
* @param {boolean} fResolve is true to resolve any "ref" attributes; default is false
* @param {function(string)} display
* @param {function(string,Object)} done (string contains the unparsed XML string data, and Object contains a parsed XML object)
*/
function parseXML(sXML, sXMLFile, idMachine, sAppClass, sParms, fResolve, display, done)
function parseXML(sXML, sXMLFile, idMachine, sAppName, sAppClass, sParms, fResolve, display, done)
{
var buildXML = function(sXML, sError) {
if (sError) {
@ -166,6 +168,7 @@ function parseXML(sXML, sXMLFile, idMachine, sAppClass, sParms, fResolve, displa
* I'm trying to switch to a shared components.xsl (at least for all PC-class machines),
* but in the interim, that means hacking the XSL file on the fly to reflect the actual class.
*/
sXML = sXML.replace(/(<xsl:variable name="APPNAME">).*?(<\/xsl:variable>)/, "$1" + sAppName + "$2");
sXML = sXML.replace(/(<xsl:variable name="APPCLASS">).*?(<\/xsl:variable>)/, "$1" + sAppClass + "$2");
/*
@ -327,11 +330,12 @@ function resolveXML(sXML, display, done)
}
/**
* embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
* embedMachine(sAppName, sAppClass, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
*
* This allows to you embed a machine on a web page, by transforming the machine XML into HTML.
*
* @param {string} sName is the app name (eg, "PCjs")
* @param {string} sAppName is the app name (eg, "PCx86")
* @param {string} sAppClass is the app class (eg, "pcx86"); also known as the machine class
* @param {string} sVersion is the app version (eg, "1.15.7")
* @param {string} idMachine
* @param {string} sXMLFile
@ -339,7 +343,7 @@ function resolveXML(sXML, display, done)
* @param {string} [sParms]
* @return {boolean} true if successful, false if error
*/
function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
function embedMachine(sAppName, sAppClass, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
{
var eMachine, eWarning, fSuccess = true;
@ -400,7 +404,6 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
head.appendChild(style);
}
var sAppClass = sName.toLowerCase(); // eg, "pcx86" or "c1pjs"
if (!sXSLFile) {
/*
* Now that PCjs is an open-source project, we can make the following test more flexible,
@ -516,13 +519,13 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
displayError("unable to transform XML: unsupported browser");
}
};
loadXML(sXSLFile, null, sAppClass, null, false, displayMessage, transformXML);
loadXML(sXSLFile, null, sAppName, sAppClass, null, false, displayMessage, transformXML);
};
if (sXMLFile.charAt(0) != '<') {
loadXML(sXMLFile, idMachine, sAppClass, sParms, true, displayMessage, processXML);
loadXML(sXMLFile, idMachine, sAppName, sAppClass, sParms, true, displayMessage, processXML);
} else {
parseXML(sXMLFile, null, idMachine, sAppClass, sParms, false, displayMessage, processXML);
parseXML(sXMLFile, null, idMachine, sAppName, sAppClass, sParms, false, displayMessage, processXML);
}
} else {
displayError("missing machine element: " + idMachine);
@ -544,7 +547,7 @@ function embedMachine(sName, sVersion, idMachine, sXMLFile, sXSLFile, sParms)
function embedC1P(idMachine, sXMLFile, sXSLFile)
{
if (fAsync) web.enablePageEvents(false);
return embedMachine("C1Pjs", APPVERSION, idMachine, sXMLFile, sXSLFile);
return embedMachine("C1Pjs", "c1pjs", APPVERSION, idMachine, sXMLFile, sXSLFile);
}
/**
@ -559,7 +562,7 @@ function embedC1P(idMachine, sXMLFile, sXSLFile)
function embedPCx86(idMachine, sXMLFile, sXSLFile, sParms)
{
if (fAsync) web.enablePageEvents(false);
return embedMachine("PCx86", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
return embedMachine("PCx86", "pcx86", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
}
/**
@ -574,12 +577,26 @@ function embedPCx86(idMachine, sXMLFile, sXSLFile, sParms)
function embedPC8080(idMachine, sXMLFile, sXSLFile, sParms)
{
if (fAsync) web.enablePageEvents(false);
return embedMachine("PC8080", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
return embedMachine("PC8080", "pc8080", 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.
* 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("PDPjs", "pdp11", APPVERSION, idMachine, sXMLFile, sXSLFile, sParms);
}
/**
* Prevent the Closure Compiler from renaming functions we want to export, by adding them as global properties.
*/
if (APPNAME == "C1Pjs") {
window['embedC1P'] = embedC1P;
@ -591,6 +608,9 @@ if (APPNAME == "PCx86") {
if (APPNAME == "PC8080") {
window['embedPC8080'] = embedPC8080;
}
if (APPNAME == "PDPjs") {
window['embedPDP11'] = embedPDP11;
}
window['enableEvents'] = web.enablePageEvents;
window['sendEvent'] = web.sendPageEvent;

247
modules/shared/lib/keys.js Normal file
View file

@ -0,0 +1,247 @@
/**
* @fileoverview Defines browser keyboard constants.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2012-Jun-20
*
* Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* 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 Keys = {
/*
* Alphanumeric and other common (printable) ASCII codes.
*
* TODO: Determine what we can do to get ALL constants like these inlined (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.
*/
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 keys (eg, the LEFT arrow key), yet for some reason, browsers defined
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37).
*/
KEYCODE: {
/* 0x08 */ BS: 8,
/* 0x09 */ TAB: 9,
/* 0x0A */ LF: 10, // TODO: Determine if any key actually generates this
/* 0x0D */ CR: 13,
/* 0x10 */ SHIFT: 16,
/* 0x11 */ CTRL: 17,
/* 0x12 */ ALT: 18,
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
/* 0x14 */ CAPS_LOCK: 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
},
/*
* The set of values that a browser may store in the 'location' property of a keyboard event object
* which we also support.
*/
LOCATION: {
LEFT: 1,
RIGHT: 2,
NUMPAD: 3
}
};
/*
* Check the event object's 'location' property for a non-zero value for the following ONRIGHT keys.
*/
Keys.KEYCODE.NUM_CR = Keys.KEYCODE.CR + Keys.KEYCODE.ONRIGHT;
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keys.STUPID_KEYCODES = {};
Keys.STUPID_KEYCODES[Keys.KEYCODE.SEMI] = Keys.ASCII[';']; // 186 -> 59
Keys.STUPID_KEYCODES[Keys.KEYCODE.EQUALS] = Keys.ASCII['=']; // 187 -> 61
Keys.STUPID_KEYCODES[Keys.KEYCODE.COMMA] = Keys.ASCII[',']; // 188 -> 44
Keys.STUPID_KEYCODES[Keys.KEYCODE.DASH] = Keys.ASCII['-']; // 189 -> 45
Keys.STUPID_KEYCODES[Keys.KEYCODE.PERIOD] = Keys.ASCII['.']; // 190 -> 46
Keys.STUPID_KEYCODES[Keys.KEYCODE.SLASH] = Keys.ASCII['/']; // 191 -> 47
Keys.STUPID_KEYCODES[Keys.KEYCODE.BQUOTE] = Keys.ASCII['`']; // 192 -> 96
Keys.STUPID_KEYCODES[Keys.KEYCODE.LBRACK] = Keys.ASCII['[']; // 219 -> 91
Keys.STUPID_KEYCODES[Keys.KEYCODE.BSLASH] = Keys.ASCII['\\']; // 220 -> 92
Keys.STUPID_KEYCODES[Keys.KEYCODE.RBRACK] = Keys.ASCII[']']; // 221 -> 93
Keys.STUPID_KEYCODES[Keys.KEYCODE.QUOTE] = Keys.ASCII["'"]; // 222 -> 39
Keys.STUPID_KEYCODES[Keys.KEYCODE.FF_DASH] = Keys.ASCII['-'];
/*
* Maps unshifted keyCodes to their shifted counterparts; to be used when a shift-key is down.
* Alphabetic characters are handled in code, since they must also take CAPS_LOCK into consideration.
*/
Keys.SHIFTED_KEYCODES = {};
Keys.SHIFTED_KEYCODES[Keys.ASCII['1']] = Keys.ASCII['!'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['2']] = Keys.ASCII['@'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['3']] = Keys.ASCII['#'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['4']] = Keys.ASCII['$'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['5']] = Keys.ASCII['%'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['6']] = Keys.ASCII['^'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['7']] = Keys.ASCII['&'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['8']] = Keys.ASCII['*'];
Keys.SHIFTED_KEYCODES[Keys.ASCII['9']] = Keys.ASCII['('];
Keys.SHIFTED_KEYCODES[Keys.ASCII['0']] = Keys.ASCII[')'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.SEMI] = Keys.ASCII[':'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.EQUALS] = Keys.ASCII['+'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.COMMA] = Keys.ASCII['<'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.DASH] = Keys.ASCII['_'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.PERIOD] = Keys.ASCII['>'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.SLASH] = Keys.ASCII['?'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.BQUOTE] = Keys.ASCII['~'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.LBRACK] = Keys.ASCII['{'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.BSLASH] = Keys.ASCII['|'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.RBRACK] = Keys.ASCII['}'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.QUOTE] = Keys.ASCII['"'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.FF_DASH] = Keys.ASCII['_'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.FF_EQUALS] = Keys.ASCII['+'];
Keys.SHIFTED_KEYCODES[Keys.KEYCODE.FF_SEMI] = Keys.ASCII[':'];
if (NODE) module.exports = Keys;

View file

@ -136,12 +136,13 @@ net.propagateParms = function(sURL, req)
/**
* encodeURL(sURL, req, fDebug)
*
* Used to encodes any URLs presented on the current page, using this 3-step (um, 4-step) process:
* Used to encodes any URLs presented on the current page, using this, um, simple 5-step process:
*
* 1) Replace any backslashes with slashes, in case the URL was derived from a file system path
* 2) Remap links that begin with "archive/" to the corresponding URL at "http://archive.pcjs.org/"
* 3) Transform any "htmlspecialchars" into the corresponding entities, using encodeURI()
* 4) Massage the result with net.propagateParms(), so that any special parameters are passed along
* 3) Use decodeURI() to eliminate escape sequences (like "%20") so that encodeURI() won't re-encode the "%"
* 4) Use encodeURI() to transform all "htmlspecialchars" and reserved characters into the appropriate sequences
* 5) Massage the result with net.propagateParms(), so that any special parameters are passed along
*
* @param {string} sURL
* @param {Object} req is the web server's (ie, Express) request object, if any
@ -158,7 +159,12 @@ net.encodeURL = function(sURL, req, fDebug)
sURL = "http://archive.pcjs.org" + sURL.replace("/archive/", "/");
}
}
return net.propagateParms(encodeURI(sURL), req);
/*
* If the incoming URL already contains URI-style escape sequences (eg, "%20" instead of spaces),
* calling decodeURI() first will eliminate them, preventing encodeURI() from converting leading
* "%" into "%25" and corrupting sequences like "%20" by turning them into "%2520".
*/
return net.propagateParms(encodeURI(decodeURI(sURL)), req);
}
return sURL;
};

View file

@ -38,56 +38,101 @@ var str = {};
*
* The built-in parseInt() function has the annoying feature of returning a partial value (ie,
* up to the point where it encounters an invalid character); eg, parseInt("foo", 16) returns 0xf.
* So use this function to validate the entire string.
*
* So it's best to use our own str.parseInt() function, which will in turn use this function to
* validate the entire string.
*
* @param {string} s is the string representation of some number
* @param {number} [base] is the radix of the number represented above (only 2, 10 and 16 are supported)
* @param {number} [base] is the radix to use (default is 10); only 2, 8, 10 and 16 are supported
* @return {boolean} true if valid, false if invalid (or the specified base isn't supported)
*/
str.isValidInt = function(s, base)
{
if (!base || base == 10) return s.match(/^[0-9]+$/) !== null;
if (base == 16) return s.match(/^[0-9a-f]+$/i) !== null;
if (base == 2) return s.match(/^[01]+$/i) !== null;
if (base == 8) return s.match(/^[0-7]+$/) !== null;
if (base == 2) return s.match(/^[01]+$/) !== null;
return false;
};
/**
* parseInt(s, base)
*
* This is a wrapper around the built-in parseInt() function, which recognizes certain prefixes (eg,
* '$' or "0x" for hex) and suffixes (eg, 'h' for hex, or '.' for decimal), and then calls isValidInt()
* to ensure we don't convert strings that contain partial values (see isValidInt() for details).
* This is a wrapper around the built-in parseInt() function. Our wrapper recognizes certain prefixes
* ('$' or "0x" for hex, '#' or "0o" for octal) and suffixes ('.' for decimal, 'h' for hex, 'y' for
* binary), and then calls isValidInt() to ensure we don't convert strings that contain partial values;
* see isValidInt() for details.
*
* We don't support multiple prefix/suffix combinations, nor do we support the "0b" prefix (or "b" suffix)
* for binary, because 1) it's not commonly used, and 2) it conflicts with valid hex sequences.
* The use of multiple prefix/suffix combinations is undefined (although for the record, we process
* prefixes first). We do NOT support the "0b" prefix to indicate binary UNLESS one or more commas are
* also present (because "0b" is also a valid hex sequence), and we do NOT support a single leading zero
* to indicate octal (because such a number could also be decimal or hex). Any number of commas are
* allowed; we remove them all before calling the built-in parseInt().
*
* To summarize our non-standard alternatives: a 'y' suffix indicates binary, a '#' prefix indicates
* octal, a '$' prefix indicates hex, and a "0b" prefix indicates binary IF at least one comma is present.
* Commas are useful for grouping binary digits, but if you don't want to use them, then you must use a
* 'y' suffix for binary numbers.
*
* @param {string} s is the string representation of some number
* @param {number} [base] is the default radix to use (default is 16); can be overridden by prefixes/suffixes
* @param {number} [base] is the radix to use (default is 10); can be overridden by prefixes/suffixes
* @return {number|undefined} corresponding value, or undefined if invalid
*/
str.parseInt = function(s, base)
{
var value;
if (s) {
if (!base) base = 16;
if (s.charAt(0) == '$') {
if (!base) base = 10;
var chPrefix = s.charAt(0);
var fCommas = (s.indexOf(',') > 0);
if (fCommas) s = s.replace(/,/g, '');
if (chPrefix == '#') {
base = 8;
chPrefix = null;
}
else if (chPrefix == '$') {
base = 16;
chPrefix = null;
}
if (chPrefix == null) {
s = s.substr(1);
} else if (s.substr(0, 2) == "0x") {
base = 16;
s = s.substr(2);
} else {
var chSuffix = s.charAt(s.length-1).toLowerCase();
if (chSuffix == 'h') {
base = 16;
chSuffix = null;
}
else {
if (chPrefix == '0') {
chPrefix = s.charAt(1);
if (chPrefix == 'b' && fCommas) {
base = 2;
chPrefix = null;
}
if (chPrefix == 'o') {
base = 8;
chPrefix = null;
}
else if (chPrefix == 'x') {
base = 16;
chPrefix = null;
}
}
else if (chSuffix == '.') {
base = 10;
chSuffix = null;
if (chPrefix == null) {
s = s.substr(2);
}
else {
var chSuffix = s.charAt(s.length-1).toLowerCase();
if (chSuffix == 'y') {
base = 2;
chSuffix = null;
}
else if (chSuffix == '.') {
base = 10;
chSuffix = null;
}
else if (chSuffix == 'h') {
base = 16;
chSuffix = null;
}
if (chSuffix == null) s = s.substr(0, s.length-1);
}
if (chSuffix == null) s = s.substr(0, s.length-1);
}
var v;
if (str.isValidInt(s, base) && !isNaN(v = parseInt(s, base))) {
@ -109,7 +154,7 @@ str.parseInt = function(s, base)
str.toBin = function(n, cch)
{
var s = "";
if (cch === undefined) {
if (!cch) {
cch = 32;
} else {
if (cch > 32) cch = 32;
@ -134,30 +179,74 @@ str.toBin = function(n, cch)
};
/**
* toBinBytes(n, cb)
* toBinBytes(n, cb, fPrefix)
*
* Converts an integer to binary, with the specified number of bytes (up to the default of 4).
*
* @param {number|null|undefined} n is a 32-bit value
* @param {number} [cb] is the desired number of binary bytes (4 is both the default and the maximum)
* @param {boolean} [fPrefix]
* @return {string} the binary representation of n
*/
str.toBinBytes = function(n, cb)
str.toBinBytes = function(n, cb, fPrefix)
{
var s = "";
if (!cb || cb > 4) cb = 4;
for (var i = 0; i < cb; i++) {
if (s) s = ',' + s;
s = str.toBin(n & 0xff, 8) + 'b' + s;
s = str.toBin(n & 0xff, 8) + s;
n >>= 8;
}
return s;
return (fPrefix? "0b" : "") + s;
};
/**
* toHex(n, cch)
* toOct(n, cch, fPrefix)
*
* Converts an integer to hex, with the specified number of digits (up to the default of 8).
* Converts an integer to octal, with the specified number of digits (default of 6; max of 11)
*
* You might be tempted to use the built-in n.toString(8) instead, but it doesn't zero-pad and it
* doesn't properly convert negative values. Moreover, if n is undefined, n.toString() will throw
* an exception, whereas this function will return '?' characters.
*
* @param {number|null|undefined} n is a 32-bit value
* @param {number} [cch] is the desired number of octal digits (0 or undefined for default of either 6 or 11)
* @param {boolean} [fPrefix]
* @return {string} the octal representation of n
*/
str.toOct = function(n, cch, fPrefix)
{
var s = "";
if (cch) {
if (cch > 11) cch = 11;
} else {
cch = (n & ~0xffff)? 11 : 6;
}
/*
* An initial "falsey" check for null takes care of both null and undefined;
* we can't rely entirely on isNaN(), because isNaN(null) returns false, oddly enough.
*
* Alternatively, we could mask and shift n regardless of whether it's null/undefined/NaN,
* since JavaScript coerces such operands to zero, but I think there's "value" in seeing those
* values displayed differently.
*/
if (n == null || isNaN(n)) {
while (cch-- > 0) s = '?' + s;
} else {
while (cch-- > 0) {
var d = (n & 7) + 0x30;
s = String.fromCharCode(d) + s;
n >>= 3;
}
}
return (fPrefix? "0o" : "") + s;
};
/**
* toHex(n, cch, fPrefix)
*
* Converts an integer to hex, with the specified number of digits (default of 4 or 8, max of 8).
*
* You might be tempted to use the built-in n.toString(16) instead, but it doesn't zero-pad and it
* doesn't properly convert negative values; for example, if n is -2147483647, then n.toString(16)
@ -172,16 +261,18 @@ str.toBinBytes = function(n, cb)
* s = s.substr(0, cch).toUpperCase();
*
* @param {number|null|undefined} n is a 32-bit value
* @param {number} [cch] is the desired number of hex digits (8 is both the default and the maximum)
* @param {number} [cch] is the desired number of hex digits (0 or undefined for default of either 4 or 8)
* @param {boolean} [fPrefix]
* @return {string} the hex representation of n
*/
str.toHex = function(n, cch)
str.toHex = function(n, cch, fPrefix)
{
var s = "";
if (cch === undefined) {
cch = 8;
} else {
if (cch) {
if (cch > 8) cch = 8;
} else {
cch = (n & ~0xffff)? 8 : 4;
}
/*
* An initial "falsey" check for null takes care of both null and undefined;
@ -201,46 +292,46 @@ str.toHex = function(n, cch)
n >>= 4;
}
}
return s;
return (fPrefix? "0x" : "") + s;
};
/**
* toHexByte(b)
*
* Alias for "0x" + str.toHex(b, 2)
* Alias for str.toHex(b, 2, true)
*
* @param {number|null|undefined} b is a byte value
* @return {string} the hex representation of b
*/
str.toHexByte = function(b)
{
return "0x" + str.toHex(b, 2);
return str.toHex(b, 2, true);
};
/**
* toHexWord(w)
*
* Alias for "0x" + str.toHex(w, 4)
* Alias for str.toHex(w, 4, true)
*
* @param {number|null|undefined} w is a word (16-bit) value
* @return {string} the hex representation of w
*/
str.toHexWord = function(w)
{
return "0x" + str.toHex(w, 4);
return str.toHex(w, 4, true);
};
/**
* toHexLong(l)
*
* Alias for "0x" + toHex(l)
* Alias for str.toHex(l, 8, true)
*
* @param {number|null|undefined} l is a dword (32-bit) value
* @return {string} the hex representation of w
*/
str.toHexLong = function(l)
{
return "0x" + str.toHex(l);
return str.toHex(l, 8, true);
};
/**

View file

@ -57,7 +57,7 @@
<p class="common-copyright">
<!-- When using AWS, the pcjs.org domain must redirect to www.pcjs.org, so let's avoid unnecessary redirects in any absolute URLs -->
<span class="common-copyright"><a href="http://www.pcjs.org/">pcjs.org</a> website © 2012-<!-- pcjs:year --> by <a href="http://twitter.com/jeffpar">@jeffpar</a></span><br/>
<span class="common-copyright">PCjs and C1Pjs released under <a href="http://gnu.org/licenses/gpl.html">GPL version 3 or later</a></span>
<span class="common-copyright"><a href="http://github.com/jeffpar/pcjs">PCjs Project</a> released under <a href="http://gnu.org/licenses/gpl.html">GPL version 3 or later</a></span>
</p>
</div>
</div>

View file

@ -52,7 +52,7 @@
<p class="common-reference"></p>
<p class="common-copyright">
<span class="common-copyright"><a href="http://www.pcjs.org/">pcjs.org</a> website © 2012-2016 by <a href="http://twitter.com/jeffpar">@jeffpar</a></span><br/>
<span class="common-copyright">PCjs and C1Pjs released under <a href="http://gnu.org/licenses/gpl.html">GPL version 3 or later</a></span>
<span class="common-copyright"><a href="http://github.com/jeffpar/pcjs">PCjs Project</a> released under <a href="http://gnu.org/licenses/gpl.html">GPL version 3 or later</a></span>
</p>
</div>
</xsl:template>

View file

@ -37,10 +37,10 @@
line-height: 19px;
vertical-align: middle;
float: left;
font-family: "Lucida Console", monospace;
font-family: Monaco, "Lucida Console", monospace;
}
.pcjs-controls textarea {
font-family: Monaco, monospace;
font-family: Monaco, "Lucida Console", monospace;
font-size: x-small;
}
.pcjs-fieldset {
@ -49,14 +49,14 @@
padding: 0;
}
.pcjs-flag {
font-family: "Lucida Console", monospace;
font-family: Monaco, "Lucida Console", monospace;
font-size: small;
text-align: center;
line-height: 19px;
vertical-align: middle;
}
.pcjs-register {
font-family: "Lucida Console", monospace;
font-family: Monaco, "Lucida Console", monospace;
font-size: small;
text-align: center;
line-height: 19px;

View file

@ -580,6 +580,12 @@
<xsl:otherwise>null</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="resetAddr">
<xsl:choose>
<xsl:when test="@resetAddr"><xsl:value-of select="@resetAddr"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="csStart">
<xsl:choose>
<xsl:when test="@csstart"><xsl:value-of select="@csstart"/></xsl:when>
@ -604,7 +610,7 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'cpu'"/>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',stepping:'<xsl:value-of select="$stepping"/>',fpu:<xsl:value-of select="$fpu"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',stepping:'<xsl:value-of select="$stepping"/>',fpu:<xsl:value-of select="$fpu"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,resetAddr:<xsl:value-of select="$resetAddr"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -698,6 +704,27 @@
</xsl:call-template>
</xsl:template>
<xsl:template match="device[@ref]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="componentFile"><xsl:value-of select="$rootDir"/><xsl:value-of select="@ref"/></xsl:variable>
<xsl:apply-templates select="document($componentFile)/device"><xsl:with-param name="machine" select="$machine"/></xsl:apply-templates>
</xsl:template>
<xsl:template match="device[not(@ref)]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="name">
<xsl:choose>
<xsl:when test="@name"><xsl:value-of select="@name"/></xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">device</xsl:with-param>
<xsl:with-param name="parms">,name:'<xsl:value-of select="$name"/>'</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="keyboard[@ref]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="componentFile"><xsl:value-of select="$rootDir"/><xsl:value-of select="@ref"/></xsl:variable>
@ -1162,6 +1189,12 @@
<xsl:template match="debugger[not(@ref)]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="base">
<xsl:choose>
<xsl:when test="@base"><xsl:value-of select="@base"/></xsl:when>
<xsl:otherwise>16</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="commands">
<xsl:choose>
<xsl:when test="@commands"><xsl:value-of select="@commands"/></xsl:when>
@ -1177,7 +1210,7 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">debugger</xsl:with-param>
<xsl:with-param name="parms">,commands:'<xsl:value-of select="$commands"/>',messages:'<xsl:value-of select="$messages"/>'</xsl:with-param>
<xsl:with-param name="parms">,base:<xsl:value-of select="$base"/>,commands:'<xsl:value-of select="$commands"/>',messages:'<xsl:value-of select="$messages"/>'</xsl:with-param>
</xsl:call-template>
</xsl:template>

View file

@ -232,7 +232,7 @@ TextOut.prototype.massageLines = function()
var cBytes = 0, c;
var sBytes = match[1], sASCIILine = "";
for (var i = 0; i < sBytes.length; i += 2) {
c = str.parseInt(sBytes.substr(i, 2));
c = str.parseInt(sBytes.substr(i, 2), 16);
if (c != 0x0D && c != 0x0A && (c < 0x20 || c >= 0x7F)) {
c = 0x2E;
fASCII = false;
@ -357,7 +357,7 @@ TextOut.prototype.labelTargets = function()
} else {
sTarget = sTarget.substr(sShort.length);
}
target = str.parseInt(sTarget);
target = str.parseInt(sTarget, 16);
if (target == undefined) continue;
if (aTargets.indexOf(target) < 0) {
aTargets.push(target);
@ -376,7 +376,7 @@ TextOut.prototype.labelTargets = function()
for (i = 0; i < this.asLines.length; i++) {
as = this.getLineParts(i);
if (!as) continue;
addr = str.parseInt(as[4]);
addr = str.parseInt(as[4], 16);
if (addr == undefined) continue;
j = aTargets.indexOf(addr);
if (j >= 0) {
@ -398,7 +398,7 @@ TextOut.prototype.labelTargets = function()
as = this.getLineParts(i);
if (!as) continue;
if (as[3].charAt(0) == chPrefix) {
addr = str.parseInt(as[3].substr(1));
addr = str.parseInt(as[3].substr(1), 16);
if (aTargets.indexOf(addr) >= 0) {
/*
* Instead of putting back the original target address, let's just convert the line to a "db"