Merge branch 'next-release'
This commit is contained in:
commit
7fa19afe3c
410 changed files with 46555 additions and 27752 deletions
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) : ".");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ var usr = require("../../shared/lib/usrlib");
|
|||
* @property {Array.<string>} pcCSSFiles
|
||||
* @property {Array.<string>} pcX86Files
|
||||
* @property {Array.<string>} pc8080Files
|
||||
* @property {Array.<string>} pdp11Files
|
||||
*/
|
||||
var pkg = require("../../../package.json");
|
||||
|
||||
|
|
@ -170,7 +171,8 @@ var aMachineFiles = {
|
|||
'C1P': pkg.c1pCSSFiles.concat(pkg.c1pJSFiles), // will be deprecated when PC6502 becomes available
|
||||
'PC': pkg.pcCSSFiles.concat(pkg.pcX86Files), // deprecated (same as PCx86)
|
||||
'PCx86': pkg.pcCSSFiles.concat(pkg.pcX86Files),
|
||||
'PC8080': pkg.pcCSSFiles.concat(pkg.pc8080Files)
|
||||
'PC8080': pkg.pcCSSFiles.concat(pkg.pc8080Files),
|
||||
'PDP11': pkg.pcCSSFiles.concat(pkg.pdp11Files)
|
||||
};
|
||||
var aMachineFileTypes = {
|
||||
'head': [".css"], // put BOTH ".css" and ".js" here if convertMDMachineLinks() embeds its own scripts
|
||||
|
|
@ -1437,7 +1439,7 @@ HTMLOut.prototype.getMachineXML = function(sToken, sIndent, aParms, sXMLFile, sS
|
|||
* The common denominator in both sets is either "/pc" or "/c1p", which in turn indicates the type of machine
|
||||
* (eg, "PC", "C1P", etc).
|
||||
*/
|
||||
aMatch = sStyleSheet.match(/\/(pc|c1p)([^/]*)/);
|
||||
aMatch = sStyleSheet.match(/\/(pc|c1p|pdp)([^/]*)/);
|
||||
if (aMatch) {
|
||||
var sMachineType = aMatch[1].toUpperCase() + (aMatch[2] != "js"? aMatch[2] : "");
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1168,7 +1168,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
|
|||
* Start looking for Markdown-style machine links now...
|
||||
*/
|
||||
var cMatches = 0;
|
||||
var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P)([^:!|]*)([:!|])(.*?)"\)/gi;
|
||||
var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P|PDP)([^:!|]*)([:!|])(.*?)"\)/gi;
|
||||
|
||||
while ((aMatch = reMachines.exec(sBlock))) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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->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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)";
|
||||
|
||||
|
|
|
|||
|
|
@ -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
31
modules/pdp11/README.md
Normal 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)
|
||||
64
modules/pdp11/bin/oct2hex.js
Normal file
64
modules/pdp11/bin/oct2hex.js
Normal 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
1114
modules/pdp11/lib/bus.js
Normal file
File diff suppressed because it is too large
Load diff
1597
modules/pdp11/lib/computer.js
Normal file
1597
modules/pdp11/lib/computer.js
Normal file
File diff suppressed because it is too large
Load diff
1281
modules/pdp11/lib/cpu.js
Normal file
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
2141
modules/pdp11/lib/cpuops.js
Normal file
File diff suppressed because it is too large
Load diff
1874
modules/pdp11/lib/cpustate.js
Normal file
1874
modules/pdp11/lib/cpustate.js
Normal file
File diff suppressed because it is too large
Load diff
3851
modules/pdp11/lib/debugger.js
Normal file
3851
modules/pdp11/lib/debugger.js
Normal file
File diff suppressed because it is too large
Load diff
528
modules/pdp11/lib/defines.js
Normal file
528
modules/pdp11/lib/defines.js
Normal 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
1499
modules/pdp11/lib/device.js
Normal file
File diff suppressed because it is too large
Load diff
119
modules/pdp11/lib/keyboard.js
Normal file
119
modules/pdp11/lib/keyboard.js
Normal 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
902
modules/pdp11/lib/memory.js
Normal 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;
|
||||
91
modules/pdp11/lib/messages.js
Normal file
91
modules/pdp11/lib/messages.js
Normal 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;
|
||||
48
modules/pdp11/lib/nodebugger.js
Normal file
48
modules/pdp11/lib/nodebugger.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @fileoverview Compile-time definitions for Debugger-less configurations.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* WARNING: DEBUGGER needs to accurately reflect whether or not the Debugger component is (or will be) loaded.
|
||||
* In the compiled case, we rely on the Closure Compiler to override DEBUGGER as appropriate. When it's *false*,
|
||||
* nearly all of debugger.js will be conditionally removed by the compiler, reducing it to little more than a
|
||||
* "type skeleton", which also solves some type-related warnings we would otherwise have if we tried to remove
|
||||
* debugger.js from the compilation process altogether.
|
||||
*
|
||||
* However, when we're in "development mode" and running uncompiled code in debugger-less configurations,
|
||||
* I would still like to skip loading debugger.js altogether. To do that, we must arrange for this additional file,
|
||||
* nodebugger.js, to be loaded immediately after defines.js, *explicitly* overriding the previously defined value
|
||||
* of DEBUGGER with *false*.
|
||||
*/
|
||||
DEBUGGER = false;
|
||||
184
modules/pdp11/lib/panel.js
Normal file
184
modules/pdp11/lib/panel.js
Normal 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
199
modules/pdp11/lib/ram.js
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 RAM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Sep-03
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var MemoryPDP11 = require("./memory");
|
||||
var ROMPDP11 = require("./rom");
|
||||
}
|
||||
|
||||
/**
|
||||
* RAMPDP11(parmsRAM)
|
||||
*
|
||||
* The RAMPDP11 component expects the following (parmsRAM) properties:
|
||||
*
|
||||
* addr: starting physical address of RAM (default is 0)
|
||||
* size: amount of RAM, in bytes (default is 0, which means defer to motherboard switch settings)
|
||||
*
|
||||
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
|
||||
* Computer component calls powerUp().
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsRAM
|
||||
*/
|
||||
function RAMPDP11(parmsRAM)
|
||||
{
|
||||
Component.call(this, "RAM", parmsRAM, RAMPDP11);
|
||||
|
||||
this.addrRAM = parmsRAM['addr'];
|
||||
this.sizeRAM = parmsRAM['size'];
|
||||
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
|
||||
this.fAllocated = false;
|
||||
}
|
||||
|
||||
Component.subclass(RAMPDP11);
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
RAMPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.setReady();
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
/*
|
||||
* The Computer powers up the CPU last, at which point CPUState state is restored,
|
||||
* which includes the Bus state, and since we use the Bus to allocate all our memory,
|
||||
* memory contents are already restored for us, so we don't need the usual restore
|
||||
* logic. We just need to call reset(), to allocate memory for the RAM.
|
||||
*/
|
||||
this.reset();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point CPUState state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
* our memory, memory contents are already saved for us, so we don't need the usual
|
||||
* save logic.
|
||||
*/
|
||||
return (fSave)? this.save() : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
*/
|
||||
RAMPDP11.prototype.reset = function()
|
||||
{
|
||||
if (!this.fAllocated && this.sizeRAM) {
|
||||
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, MemoryPDP11.TYPE.RAM)) {
|
||||
this.fAllocated = true;
|
||||
}
|
||||
}
|
||||
if (!this.fAllocated) {
|
||||
Component.error("No RAM allocated");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the RAMPDP11 component.
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @return {Object}
|
||||
*/
|
||||
RAMPDP11.prototype.save = function()
|
||||
{
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the RAMPDP11 component.
|
||||
*
|
||||
* @this {RAMPDP11}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAMPDP11.prototype.restore = function(data)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* RAMPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "ram", extracting the
|
||||
* JSON-encoded parameters for the RAMPDP11 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a RAMPDP11 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
RAMPDP11.init = function()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(document, PDP11.APPCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
var parmsRAM = Component.getComponentParms(eRAM);
|
||||
var ram = new RAMPDP11(parmsRAM);
|
||||
Component.bindComponentControls(ram, eRAM, PDP11.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize all the RAMPDP11 modules on the page.
|
||||
*/
|
||||
web.onInit(RAMPDP11.init);
|
||||
|
||||
if (NODE) module.exports = RAMPDP11;
|
||||
400
modules/pdp11/lib/rom.js
Normal file
400
modules/pdp11/lib/rom.js
Normal 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;
|
||||
681
modules/pdp11/lib/serialport.js
Normal file
681
modules/pdp11/lib/serialport.js
Normal 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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
705
modules/shared/lib/debugger.js
Normal file
705
modules/shared/lib/debugger.js
Normal 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;
|
||||
|
|
@ -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";
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
247
modules/shared/lib/keys.js
Normal 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;
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue