Converted PC8080 to ES6

This commit is contained in:
Jeff Parsons 2016-12-28 10:03:58 -08:00 • committed by Jeff Parsons
commit 0f680839f7
27 changed files with 11054 additions and 10896 deletions

View file

@ -201,8 +201,8 @@ module.exports = function(grunt) {
return "/**\n * @copyright " + filepath.replace(/^\./, "http://pcjs.org") + " (C) Jeff Parsons 2012-2016\n */\n\n" +
src.replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, '')
.replace(/^(import|export)[ \t]+[^\n]*\n/gm, '')
.replace(/^[ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;/gm, '')
.replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, '')
.replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, '')
.replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
.replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
@ -219,8 +219,8 @@ module.exports = function(grunt) {
return "/**\n * @copyright " + filepath.replace(/^\./, "http://pcjs.org") + " (C) Jeff Parsons 2012-2016\n */\n\n" +
src.replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, '')
.replace(/^(import|export)[ \t]+[^\n]*\n/gm, '')
.replace(/^[ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;/gm, '')
.replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, '')
.replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, '')
.replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
.replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
@ -237,8 +237,8 @@ module.exports = function(grunt) {
return "/**\n * @copyright " + filepath.replace(/^\./, "http://pcjs.org") + " (C) Jeff Parsons 2012-2016\n */\n\n" +
src.replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, '')
.replace(/^(import|export)[ \t]+[^\n]*\n/gm, '')
.replace(/^[ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;/gm, '')
.replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, '')
.replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, '')
.replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
.replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
@ -255,8 +255,8 @@ module.exports = function(grunt) {
return "/**\n * @copyright " + filepath.replace(/^\./, "http://pcjs.org") + " (C) Jeff Parsons 2012-2016\n */\n\n" +
src.replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, '')
.replace(/^(import|export)[ \t]+[^\n]*\n/gm, '')
.replace(/^[ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;/gm, '')
.replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, '')
.replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, '')
.replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, '')
.replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')
.replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, '')

View file

@ -92,8 +92,8 @@ gulp.task('mktmp', function() {
.pipe(header('/**\n * @copyright ' + file.path.replace(/.*\/(modules\/.*)/, "http://pcjs.org/$1") + ' (C) Jeff Parsons 2012-2016\n */\n\n'))
.pipe(replace(/(^|\n)[ \t]*(['"])use strict\2;?/g, ""))
.pipe(replace(/^(import|export)[ \t]+[^\n]*\n/gm, ""))
.pipe(replace(/^[ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);/gm, ""))
.pipe(replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;/gm, ""))
.pipe(replace(/^[ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);/gm, ""))
.pipe(replace(/^[ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;/gm, ""))
.pipe(replace(/\/\*\*\s*\*\s*@fileoverview[\s\S]*?\*\/\s*/g, ""))
.pipe(replace(/[ \t]*if\s*\(NODE\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, ""))
.pipe(replace(/[ \t]*if\s*\(typeof\s+module\s*!==\s*(['"])undefined\1\)\s*(\{[^}]*}|[^\n]*)(\n|$)/gm, ""))

View file

@ -514,8 +514,8 @@ HTMLOut.filter = function(req, res, next)
} else {
sData = sData.replace(/^([ \t]*import\s+\S+\s+from\s+(['"]).*?\1;)/gm, "// $1");
sData = sData.replace(/^([ \t]*export\s+default\s+\S+;)/gm, "// $1");
sData = sData.replace(/^([ \t]*var\s+\S+\s+=\s+require\((['"]).*?\1\);)/gm, "// $1");
sData = sData.replace(/^([ \t]*(if\s+\(NODE\)\s*|)module\.exports\s+=\s+\S+;)/gm, "// $1");
sData = sData.replace(/^([ \t]*var\s+\S+\s*=\s*require\((['"]).*?\1\);)/gm, "// $1");
sData = sData.replace(/^([ \t]*(if\s+\(NODE\)\s*|)module\.exports\s*=\s*\S+;)/gm, "// $1");
res.set("Content-Type", "application/javascript");
res.status(200).send(sData);
}

View file

@ -28,16 +28,22 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var Component = require("../../shared/lib/component");
var State = require("../../shared/lib/state");
var Memory8080 = require("./memory");
var Messages8080= require("./messages");
}
var Str = require("../../shared/es6/strlib");
var Usr = require("../../shared/es6/usrlib");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var Memory8080 = require("./memory");
var Messages8080 = require("./messages");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class Bus8080 extends Component {
/**
* Bus8080(cpu, dbg)
*
* The Bus8080 component manages physical memory and I/O address spaces.
@ -57,15 +63,14 @@ if (NODE) {
* 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
* @this {Bus8080}
* @param {Object} parmsBus
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
function Bus8080(parmsBus, cpu, dbg)
{
Component.call(this, "Bus", parmsBus, Bus8080);
constructor(parmsBus, cpu, dbg)
{
super("Bus", parmsBus, Bus8080);
this.cpu = cpu;
this.dbg = dbg;
@ -142,44 +147,35 @@ function Bus8080(parmsBus, cpu, dbg)
this.initMemory();
this.setReady();
}
}
Component.subclass(Bus8080);
Bus8080.ERROR = {
ADD_MEM_INUSE: 1,
ADD_MEM_BADRANGE: 2,
SET_MEM_BADRANGE: 4,
REM_MEM_BADRANGE: 5
};
/**
/**
* initMemory()
*
* Allocate enough (empty) Memory blocks to span the entire physical address space.
*
* @this {Bus8080}
*/
Bus8080.prototype.initMemory = function()
{
initMemory()
{
var block = new Memory8080();
block.copyBreakpoints(this.dbg);
this.aMemBlocks = new Array(this.nBlockTotal);
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
this.aMemBlocks[iBlock] = block;
}
};
}
/**
/**
* reset()
*
* @this {Bus8080}
*/
Bus8080.prototype.reset = function()
{
};
reset()
{
}
/**
/**
* powerUp(data, fRepower)
*
* We don't need a powerDown() handler, because for largely historical reasons, our state is saved by saveMemory(),
@ -196,13 +192,13 @@ Bus8080.prototype.reset = function()
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
Bus8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
if (!fRepower) this.reset();
return true;
};
}
/**
/**
* addMemory(addr, size, type)
*
* Adds new Memory blocks to the specified address range. Any Memory blocks previously
@ -231,8 +227,8 @@ Bus8080.prototype.powerUp = function(data, fRepower)
* @param {number} type is one of the Memory8080.TYPE constants
* @return {boolean} true if successful, false if not
*/
Bus8080.prototype.addMemory = function(addr, size, type)
{
addMemory(addr, size, type)
{
var addrNext = addr;
var sizeLeft = size;
var iBlock = addrNext >>> this.nBlockShift;
@ -279,14 +275,14 @@ Bus8080.prototype.addMemory = function(addr, size, type)
}
if (sizeLeft <= 0) {
this.status(Math.floor(size / 1024) + "Kb " + Memory8080.TYPE.NAMES[type] + " at " + str.toHexWord(addr));
this.status(Math.floor(size / 1024) + "Kb " + Memory8080.TYPE.NAMES[type] + " at " + Str.toHexWord(addr));
return true;
}
return this.reportError(Bus8080.ERROR.ADD_MEM_BADRANGE, addr, size);
};
}
/**
/**
* cleanMemory(addr, size)
*
* @this {Bus8080}
@ -294,8 +290,8 @@ Bus8080.prototype.addMemory = function(addr, size, type)
* @param {number} size
* @return {boolean} true if all blocks were clean, false if dirty; all blocks are cleaned in the process
*/
Bus8080.prototype.cleanMemory = function(addr, size)
{
cleanMemory(addr, size)
{
var fClean = true;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
@ -307,45 +303,9 @@ Bus8080.prototype.cleanMemory = function(addr, size)
iBlock++;
}
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 BusInfo8080 object for the specified address range.
@ -356,8 +316,8 @@ var BusInfo8080;
* @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)
*/
Bus8080.prototype.scanMemory = function(info, addr, size)
{
scanMemory(info, addr, size)
{
if (addr == null) addr = 0;
if (size == null) size = (this.addrTotal - addr) | 0;
if (info == null) info = {cbTotal: 0, cBlocks: 0, aBlocks: []};
@ -371,26 +331,26 @@ Bus8080.prototype.scanMemory = function(info, addr, size)
var block = this.aMemBlocks[iBlock];
info.cbTotal += block.size;
if (block.size) {
info.aBlocks.push(usr.initBitFields(Bus8080.BlockInfo, iBlock, 0, 0, block.type));
info.aBlocks.push(Usr.initBitFields(Bus8080.BlockInfo, iBlock, 0, 0, block.type));
info.cBlocks++
}
iBlock++;
}
return info;
};
}
/**
/**
* getWidth()
*
* @this {Bus8080}
* @return {number}
*/
Bus8080.prototype.getWidth = function()
{
getWidth()
{
return this.nBusWidth;
};
}
/**
/**
* removeMemory(addr, size)
*
* Replaces every block in the specified address range with empty Memory blocks that ignore all reads/writes.
@ -402,8 +362,8 @@ Bus8080.prototype.getWidth = function()
* @param {number} size
* @return {boolean} true if successful, false if not
*/
Bus8080.prototype.removeMemory = function(addr, size)
{
removeMemory(addr, size)
{
if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
var iBlock = addr >>> this.nBlockShift;
while (size > 0) {
@ -417,9 +377,9 @@ Bus8080.prototype.removeMemory = function(addr, size)
return true;
}
return this.reportError(Bus8080.ERROR.REM_MEM_BADRANGE, addr, size);
};
}
/**
/**
* getMemoryBlocks(addr, size)
*
* @this {Bus8080}
@ -427,8 +387,8 @@ Bus8080.prototype.removeMemory = function(addr, size)
* @param {number} size of the request, in bytes
* @return {Array} of Memory blocks
*/
Bus8080.prototype.getMemoryBlocks = function(addr, size)
{
getMemoryBlocks(addr, size)
{
var aBlocks = [];
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
@ -436,9 +396,9 @@ Bus8080.prototype.getMemoryBlocks = function(addr, size)
size -= this.nBlockSize;
}
return aBlocks;
};
}
/**
/**
* setMemoryBlocks(addr, size, aBlocks, type)
*
* If no type is specified, then specified address range uses all the provided blocks as-is;
@ -453,8 +413,8 @@ Bus8080.prototype.getMemoryBlocks = function(addr, size)
* @param {Array} aBlocks as returned by getMemoryBlocks()
* @param {number} [type] is one of the Memory8080.TYPE constants
*/
Bus8080.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
{
setMemoryBlocks(addr, size, aBlocks, type)
{
var i = 0;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
@ -469,21 +429,21 @@ Bus8080.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
this.aMemBlocks[iBlock++] = block;
size -= this.nBlockSize;
}
};
}
/**
/**
* getByte(addr)
*
* @this {Bus8080}
* @param {number} addr is a physical address
* @return {number} byte (8-bit) value at that address
*/
Bus8080.prototype.getByte = function(addr)
{
getByte(addr)
{
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
};
}
/**
/**
* getByteDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getByte() breakpoint detection.
@ -492,29 +452,29 @@ Bus8080.prototype.getByte = function(addr)
* @param {number} addr is a physical address
* @return {number} byte (8-bit) value at that address
*/
Bus8080.prototype.getByteDirect = function(addr)
{
getByteDirect(addr)
{
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByteDirect(addr & this.nBlockLimit, addr);
};
}
/**
/**
* getShort(addr)
*
* @this {Bus8080}
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
Bus8080.prototype.getShort = function(addr)
{
getShort(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
return this.aMemBlocks[iBlock].readShort(off, addr);
}
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
};
}
/**
/**
* getShortDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getShort() breakpoint detection.
@ -523,29 +483,29 @@ Bus8080.prototype.getShort = function(addr)
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
Bus8080.prototype.getShortDirect = function(addr)
{
getShortDirect(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
return this.aMemBlocks[iBlock].readShortDirect(off, addr);
}
return this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
};
}
/**
/**
* setByte(addr, b)
*
* @this {Bus8080}
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
*/
Bus8080.prototype.setByte = function(addr, b)
{
setByte(addr, b)
{
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
};
}
/**
/**
* setByteDirect(addr, b)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
@ -555,20 +515,20 @@ Bus8080.prototype.setByte = function(addr, b)
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
*/
Bus8080.prototype.setByteDirect = function(addr, b)
{
setByteDirect(addr, b)
{
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
};
}
/**
/**
* setShort(addr, w)
*
* @this {Bus8080}
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
*/
Bus8080.prototype.setShort = function(addr, w)
{
setShort(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
@ -577,9 +537,9 @@ Bus8080.prototype.setShort = function(addr, w)
}
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
};
}
/**
/**
* setShortDirect(addr, w)
*
* This is useful for the Debugger and other components that want to bypass breakpoint detection AND read-only
@ -589,8 +549,8 @@ Bus8080.prototype.setShort = function(addr, w)
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
*/
Bus8080.prototype.setShortDirect = function(addr, w)
{
setShortDirect(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (off != this.nBlockLimit) {
@ -599,39 +559,39 @@ Bus8080.prototype.setShortDirect = function(addr, w)
}
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
};
}
/**
/**
* addMemBreak(addr, fWrite)
*
* @this {Bus8080}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
*/
Bus8080.prototype.addMemBreak = function(addr, fWrite)
{
addMemBreak(addr, fWrite)
{
if (DEBUGGER) {
var iBlock = addr >>> this.nBlockShift;
this.aMemBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite);
}
};
}
/**
/**
* removeMemBreak(addr, fWrite)
*
* @this {Bus8080}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
*/
Bus8080.prototype.removeMemBreak = function(addr, fWrite)
{
removeMemBreak(addr, fWrite)
{
if (DEBUGGER) {
var iBlock = addr >>> this.nBlockShift;
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
}
};
}
/**
/**
* saveMemory(fAll)
*
* The only memory blocks we save are those marked as dirty, but most likely all of RAM will have been marked dirty,
@ -660,8 +620,8 @@ Bus8080.prototype.removeMemBreak = function(addr, fWrite)
* @param {boolean} [fAll] (true to save all non-ROM memory blocks, regardless of their dirty flags)
* @return {Array} a
*/
Bus8080.prototype.saveMemory = function(fAll)
{
saveMemory(fAll)
{
var i = 0;
var a = [];
@ -679,9 +639,9 @@ Bus8080.prototype.saveMemory = function(fAll)
}
return a;
};
}
/**
/**
* restoreMemory(a)
*
* This restores the contents of all Memory blocks; called by CPUState.restore().
@ -698,8 +658,8 @@ Bus8080.prototype.saveMemory = function(fAll)
* @param {Array} a
* @return {boolean} true if successful, false if not
*/
Bus8080.prototype.restoreMemory = function(a)
{
restoreMemory(a)
{
var i;
for (i = 0; i < a.length - 1; i += 2) {
var iBlock = a[i];
@ -719,17 +679,17 @@ Bus8080.prototype.restoreMemory = function(a)
}
}
return true;
};
}
/**
/**
* addPortInputBreak(port)
*
* @this {Bus8080}
* @param {number} [port]
* @return {boolean} true if break on port input enabled, false if disabled
*/
Bus8080.prototype.addPortInputBreak = function(port)
{
addPortInputBreak(port)
{
if (port === undefined) {
this.fPortInputBreakAll = !this.fPortInputBreakAll;
return this.fPortInputBreakAll;
@ -739,9 +699,9 @@ Bus8080.prototype.addPortInputBreak = function(port)
}
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.
@ -751,21 +711,21 @@ Bus8080.prototype.addPortInputBreak = function(port)
* @param {number} end port address
* @param {function(number,number)} fn is called with the port and IP values at the time of the input
*/
Bus8080.prototype.addPortInputNotify = function(start, end, fn)
{
addPortInputNotify(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");
Component.warning("Input port " + Str.toHexWord(port) + " already registered");
continue;
}
this.aPortInputNotify[port] = [fn, false];
if (MAXDEBUG) this.log("addPortInputNotify(" + str.toHexWord(port) + ")");
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)
@ -775,17 +735,17 @@ Bus8080.prototype.addPortInputNotify = function(start, end, fn)
* @param {Object} table
* @param {number} [offset] is an optional port offset
*/
Bus8080.prototype.addPortInputTable = function(component, table, offset)
{
addPortInputTable(component, table, offset)
{
if (offset === undefined) offset = 0;
if (table) {
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.
@ -794,12 +754,12 @@ Bus8080.prototype.addPortInputTable = function(component, table, offset)
* @param {number} port
* @param {number} size (1, 2 or 4)
*/
Bus8080.prototype.addPortInputWidth = function(port, size)
{
addPortInputWidth(port, size)
{
this.aPortInputWidth[port] = size;
};
}
/**
/**
* checkPortInputNotify(port, size, addrIP)
*
* @this {Bus8080}
@ -811,8 +771,8 @@ Bus8080.prototype.addPortInputWidth = function(port, size)
* 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.
*/
Bus8080.prototype.checkPortInputNotify = function(port, size, addrIP)
{
checkPortInputNotify(port, size, addrIP)
{
var data = 0, shift = 0;
while (size > 0) {
@ -859,36 +819,17 @@ Bus8080.prototype.checkPortInputNotify = function(port, size, addrIP)
this.assert(!size);
return data;
};
/**
* removePortInputNotify(start, end)
*
* Remove port input-notification handler(s) (to be ENABLED later if needed)
*
* @this {Bus8080}
* @param {number} start address
* @param {number} end address
*
Bus8080.prototype.removePortInputNotify = function(start, end)
{
for (var port = start; port < end; port++) {
if (this.aPortInputNotify[port]) {
delete this.aPortInputNotify[port];
}
}
};
*/
/**
/**
* addPortOutputBreak(port)
*
* @this {Bus8080}
* @param {number} [port]
* @return {boolean} true if break on port output enabled, false if disabled
*/
Bus8080.prototype.addPortOutputBreak = function(port)
{
addPortOutputBreak(port)
{
if (port === undefined) {
this.fPortOutputBreakAll = !this.fPortOutputBreakAll;
return this.fPortOutputBreakAll;
@ -898,9 +839,9 @@ Bus8080.prototype.addPortOutputBreak = function(port)
}
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.
@ -910,21 +851,21 @@ Bus8080.prototype.addPortOutputBreak = function(port)
* @param {number} end port address
* @param {function(number,number)} fn is called with the port and IP values at the time of the output
*/
Bus8080.prototype.addPortOutputNotify = function(start, end, fn)
{
addPortOutputNotify(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");
Component.warning("Output port " + Str.toHexWord(port) + " already registered");
continue;
}
this.aPortOutputNotify[port] = [fn, false];
if (MAXDEBUG) this.log("addPortOutputNotify(" + str.toHexWord(port) + ")");
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)
@ -934,17 +875,17 @@ Bus8080.prototype.addPortOutputNotify = function(start, end, fn)
* @param {Object} table
* @param {number} [offset] is an optional port offset
*/
Bus8080.prototype.addPortOutputTable = function(component, table, offset)
{
addPortOutputTable(component, table, offset)
{
if (offset === undefined) offset = 0;
if (table) {
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.
@ -953,12 +894,12 @@ Bus8080.prototype.addPortOutputTable = function(component, table, offset)
* @param {number} port
* @param {number} size (1, 2 or 4)
*/
Bus8080.prototype.addPortOutputWidth = function(port, size)
{
addPortOutputWidth(port, size)
{
this.aPortOutputWidth[port] = size;
};
}
/**
/**
* checkPortOutputNotify(port, size, data, addrIP)
*
* @this {Bus8080}
@ -967,8 +908,8 @@ Bus8080.prototype.addPortOutputWidth = function(port, size)
* @param {number} data
* @param {number} [addrIP] is the IP value at the time of the output
*/
Bus8080.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
{
checkPortOutputNotify(port, size, data, addrIP)
{
var shift = 0;
while (size > 0) {
@ -1007,9 +948,28 @@ Bus8080.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
size -= sizePort;
}
this.assert(!size);
};
}
/**
/**
* removePortInputNotify(start, end)
*
* Remove port input-notification handler(s) (to be ENABLED later if needed)
*
* @this {Bus8080}
* @param {number} start address
* @param {number} end address
*
removePortInputNotify(start, end)
{
for (var port = start; port < end; port++) {
if (this.aPortInputNotify[port]) {
delete this.aPortInputNotify[port];
}
}
}
*/
/**
* removePortOutputNotify(start, end)
*
* Remove port output-notification handler(s) (to be ENABLED later if needed)
@ -1018,17 +978,17 @@ Bus8080.prototype.checkPortOutputNotify = function(port, size, data, addrIP)
* @param {number} start address
* @param {number} end address
*
Bus8080.prototype.removePortOutputNotify = function(start, end)
{
removePortOutputNotify(start, end)
{
for (var port = start; port < end; port++) {
if (this.aPortOutputNotify[port]) {
delete this.aPortOutputNotify[port];
}
}
};
}
*/
/**
/**
* reportError(op, addr, size, fQuiet)
*
* @this {Bus8080}
@ -1038,9 +998,9 @@ Bus8080.prototype.removePortOutputNotify = function(start, end)
* @param {boolean} [fQuiet] (true if any error should be quietly logged)
* @return {boolean} false
*/
Bus8080.prototype.reportError = function(op, addr, size, fQuiet)
{
var sError = "Memory block error (" + op + ": " + str.toHex(addr) + "," + str.toHex(size) + ")";
reportError(op, addr, size, fQuiet)
{
var sError = "Memory block error (" + op + ": " + Str.toHex(addr) + "," + Str.toHex(size) + ")";
if (fQuiet) {
if (this.dbg) {
this.dbg.message(sError);
@ -1051,6 +1011,50 @@ Bus8080.prototype.reportError = function(op, addr, size, fQuiet)
Component.error(sError);
}
return false;
}
}
Bus8080.ERROR = {
ADD_MEM_INUSE: 1,
ADD_MEM_BADRANGE: 2,
SET_MEM_BADRANGE: 4,
REM_MEM_BADRANGE: 5
};
if (NODE) module.exports = Bus8080;
/*
* 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;
module.exports = Bus8080;

File diff suppressed because it is too large Load diff

View file

@ -28,27 +28,27 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var UserAPI = require("../../shared/lib/userapi");
var ReportAPI = require("../../shared/lib/reportapi");
var Component = require("../../shared/lib/component");
var State = require("../../shared/lib/state");
var PC8080 = require("./defines");
var Bus8080 = require("./bus");
var Messages8080= require("./messages");
}
var Str = require("../../shared/es6/strlib");
var Usr = require("../../shared/es6/usrlib");
var Web = require("../../shared/es6/weblib");
var UserAPI = require("../../shared/es6/userapi");
var ReportAPI = require("../../shared/es6/reportapi");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PC8080 = require("./defines");
var Bus8080 = require("./bus");
var Messages8080 = require("./messages");
/**
* Computer8080(parmsComputer, parmsMachine, fSuspended)
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @constructor
* @extends Component
* @param {Object} parmsComputer
* @param {Object} [parmsMachine]
* @param {boolean} [fSuspended]
* @unrestricted
*/
class Computer8080 extends Component {
/**
* Computer8080(parmsComputer, parmsMachine, fSuspended)
*
* The Computer8080 component has no required (parmsComputer) properties, but it does
* support the following:
@ -102,10 +102,15 @@ if (NODE) {
* Once a component has indicated it's ready, we call its powerUp() notification
* function (if it has one--it's optional). We call the CPU's powerUp() function last,
* so that the CPU is assured that all other components are ready and "powered".
*
* @this {Computer8080}
* @param {Object} parmsComputer
* @param {Object} [parmsMachine]
* @param {boolean} [fSuspended]
*/
function Computer8080(parmsComputer, parmsMachine, fSuspended) {
Component.call(this, "Computer", parmsComputer, Computer8080, Messages8080.COMPUTER);
constructor(parmsComputer, parmsMachine, fSuspended)
{
super("Computer", parmsComputer, Computer8080, Messages8080.COMPUTER);
this.flags.powered = false;
@ -259,7 +264,7 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
this.setReady();
} else {
var cmp = this;
web.getResource(sStatePath, null, true, function(sURL, sResource, nErrorCode) {
Web.getResource(sStatePath, null, true, function(sURL, sResource, nErrorCode) {
cmp.doneLoad(sURL, sResource, nErrorCode);
});
}
@ -270,48 +275,29 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
* Power on the computer, giving every component the opportunity to reset or restore itself.
*/
if (!fSuspended && this.fAutoPower) this.wait(this.powerOn);
}
}
Component.subclass(Computer8080);
Computer8080.STATE_FAILSAFE = "failsafe";
Computer8080.STATE_VALIDATE = "validate";
Computer8080.STATE_TIMESTAMP = "timestamp";
Computer8080.STATE_VERSION = "version";
Computer8080.STATE_HOSTURL = "url";
Computer8080.STATE_BROWSER = "browser";
Computer8080.STATE_USERID = "user";
/*
* The following constants define all the resume options. Negative values (eg, RESUME_REPOWER) are for
* internal use only, and RESUME_DELETE is not documented (it provides a way of deleting ALL saved states
* whenever a resume is declined). As a result, the only "end-user" values are 0, 1 and 2.
*/
Computer8080.RESUME_REPOWER = -1; // resume without changing any state (for internal use only)
Computer8080.RESUME_NONE = 0; // default (no resume)
Computer8080.RESUME_AUTO = 1; // automatically save/restore state
Computer8080.RESUME_PROMPT = 2; // automatically save but conditionally restore (WARNING: if restore is declined, any state is discarded)
Computer8080.RESUME_DELETE = 3; // same as RESUME_PROMPT but discards ALL machines states whenever ANY machine restore is declined (undocumented)
/**
/**
* getMachineID()
*
* @this {Computer8080}
* @return {string}
*/
Computer8080.prototype.getMachineID = function()
{
getMachineID()
{
return this.sMachineID;
};
}
/**
/**
* setMachineParms(parmsMachine)
*
* If no explicit machine parms were provided, then we check for 'parms' in the bundled resources (if any).
*
* @this {Computer8080}
* @param {Object} [parmsMachine]
*/
Computer8080.prototype.setMachineParms = function(parmsMachine)
{
setMachineParms(parmsMachine)
{
if (!parmsMachine) {
var sParms;
if (typeof resources == 'object' && (sParms = resources['parms'])) {
@ -323,9 +309,9 @@ Computer8080.prototype.setMachineParms = function(parmsMachine)
}
}
this.parmsMachine = parmsMachine;
};
}
/**
/**
* getMachineParm(sParm, parmsComponent)
*
* If the machine parameter doesn't exist, we check for a matching component parameter (if parmsComponent is provided),
@ -335,12 +321,13 @@ Computer8080.prototype.setMachineParms = function(parmsMachine)
* 'state' back to the caller (ie, the name of the resource), so that the caller will then attempt to load the 'state'
* resource to obtain the actual state.
*
* @this {Computer8080}
* @param {string} sParm
* @param {Object} [parmsComponent]
* @return {string|undefined}
*/
Computer8080.prototype.getMachineParm = function(sParm, parmsComponent)
{
getMachineParm(sParm, parmsComponent)
{
/*
* When using getURLParm(), the check is allowed be a bit looser, because URL parameters are
* user-supplied, whereas most other parameters are developer-supplied. Granted, a developer
@ -348,7 +335,7 @@ Computer8080.prototype.getMachineParm = function(sParm, parmsComponent)
* but there are limits to my paranoia.
*/
var sParmLC = sParm.toLowerCase();
var value = web.getURLParm(sParm) || web.getURLParm(sParmLC);
var value = Web.getURLParm(sParm) || Web.getURLParm(sParmLC);
if (value === undefined && this.parmsMachine) {
value = this.parmsMachine[sParm];
@ -360,29 +347,31 @@ Computer8080.prototype.getMachineParm = function(sParm, parmsComponent)
value = sParm;
}
return value;
};
}
/**
/**
* saveMachineParms()
*
* @this {Computer8080}
* @return {string|null}
*/
Computer8080.prototype.saveMachineParms = function()
{
saveMachineParms()
{
return this.parmsMachine? JSON.stringify(this.parmsMachine) : null;
};
}
/**
/**
* getUserID()
*
* @this {Computer8080}
* @return {string}
*/
Computer8080.prototype.getUserID = function()
{
getUserID()
{
return this.sUserID || "";
};
}
/**
/**
* doneLoad(sURL, sStateData, nErrorCode)
*
* @this {Computer8080}
@ -390,8 +379,8 @@ Computer8080.prototype.getUserID = function()
* @param {string} sStateData
* @param {number} nErrorCode
*/
Computer8080.prototype.doneLoad = function(sURL, sStateData, nErrorCode)
{
doneLoad(sURL, sStateData, nErrorCode)
{
if (!nErrorCode) {
this.sStateData = sStateData;
this.fStateData = true;
@ -401,12 +390,12 @@ Computer8080.prototype.doneLoad = function(sURL, sStateData, nErrorCode)
} else {
this.sResumePath = null;
this.fServerState = false;
this.notice('Unable to load machine state from server (error ' + nErrorCode + (sStateData? ': ' + str.trim(sStateData) : '') + ')');
this.notice('Unable to load machine state from server (error ' + nErrorCode + (sStateData? ': ' + Str.trim(sStateData) : '') + ')');
}
this.setReady();
};
}
/**
/**
* wait(fn, parms)
*
* wait() waits until every component is ready (including ourselves, the last component we check), then calls the
@ -424,8 +413,8 @@ Computer8080.prototype.doneLoad = function(sURL, sStateData, nErrorCode)
* @param {function(...)} fn
* @param {number|Array} [parms] optional parameters
*/
Computer8080.prototype.wait = function(fn, parms)
{
wait(fn, parms)
{
var computer = this;
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent <= aComponents.length; iComponent++) {
@ -439,9 +428,9 @@ Computer8080.prototype.wait = function(fn, parms)
}
if (DEBUG && this.messageEnabled()) this.printMessage("Computer8080.wait(ready)");
fn.call(this, parms);
};
}
/**
/**
* validateState(stateComputer)
*
* NOTE: We clear() stateValidate only when there's no stateComputer.
@ -450,8 +439,8 @@ Computer8080.prototype.wait = function(fn, parms)
* @param {State|null} [stateComputer]
* @return {boolean} true if state passes validation, false if not
*/
Computer8080.prototype.validateState = function(stateComputer)
{
validateState(stateComputer)
{
var fValid = true;
var stateValidate = new State(this, PC8080.APPVERSION, Computer8080.STATE_VALIDATE);
if (stateValidate.load() && stateValidate.parse()) {
@ -468,9 +457,9 @@ Computer8080.prototype.validateState = function(stateComputer)
}
}
return fValid;
};
}
/**
/**
* powerOn(resume)
*
* Power every component "up", applying any previously available state information.
@ -478,8 +467,8 @@ Computer8080.prototype.validateState = function(stateComputer)
* @this {Computer8080}
* @param {number} [resume] is a valid RESUME value; default is this.resume
*/
Computer8080.prototype.powerOn = function(resume)
{
powerOn(resume)
{
if (resume === undefined) {
resume = this.resume || (this.sStateData? Computer8080.RESUME_AUTO : Computer8080.RESUME_NONE);
}
@ -525,15 +514,15 @@ Computer8080.prototype.powerOn = function(resume)
this.stateFailSafe.unload();
}
this.stateFailSafe.set(Computer8080.STATE_TIMESTAMP, usr.getTimestamp());
this.stateFailSafe.set(Computer8080.STATE_TIMESTAMP, Usr.getTimestamp());
this.stateFailSafe.store();
var fValidate = this.resume && !this.fServerState;
if (resume == Computer8080.RESUME_AUTO || web.confirmUser("Click OK to restore the previous " + PC8080.APPNAME + " machine state, or CANCEL to reset the machine.")) {
if (resume == Computer8080.RESUME_AUTO || Web.confirmUser("Click OK to restore the previous " + PC8080.APPNAME + " machine state, or CANCEL to reset the machine.")) {
fRestore = stateComputer.parse();
if (fRestore) {
var sCode = stateComputer.get(UserAPI.RES.CODE);
var sData = stateComputer.get(UserAPI.RES.DATA);
var sCode = /** @type {string} */ (stateComputer.get(UserAPI.RES.CODE));
var sData = /** @type {string} */ (stateComputer.get(UserAPI.RES.DATA));
if (sCode) {
if (sCode == UserAPI.CODE.OK) {
stateComputer.load(sData);
@ -609,9 +598,9 @@ Computer8080.prototype.powerOn = function(resume)
return;
}
this.donePowerOn(aParms);
};
}
/**
/**
* powerRestore(component, stateComputer, fRepower, fRestore)
*
* @this {Computer8080}
@ -621,8 +610,8 @@ Computer8080.prototype.powerOn = function(resume)
* @param {boolean} fRestore
* @return {boolean} true if restore should continue, false if not
*/
Computer8080.prototype.powerRestore = function(component, stateComputer, fRepower, fRestore)
{
powerRestore(component, stateComputer, fRepower, fRestore)
{
if (!component.flags.powered) {
component.flags.powered = true;
@ -680,7 +669,7 @@ Computer8080.prototype.powerRestore = function(component, stateComputer, fRepowe
if (this.sStatePath && !this.fStateData) {
stateComputer.clear();
this.resume = Computer8080.RESUME_NONE;
web.reloadPage();
Web.reloadPage();
} else {
/*
* In all other cases, we set fRestoreError, which should trigger a call to
@ -712,9 +701,9 @@ Computer8080.prototype.powerRestore = function(component, stateComputer, fRepowe
}
}
return fRestore;
};
}
/**
/**
* donePowerOn(aParms)
*
* This is nothing more than a continuation of powerOn(), giving us the option of calling wait() one more time.
@ -722,8 +711,8 @@ Computer8080.prototype.powerRestore = function(component, stateComputer, fRepowe
* @this {Computer8080}
* @param {Array} aParms containing [stateComputer, resume, fRestore]
*/
Computer8080.prototype.donePowerOn = function(aParms)
{
donePowerOn(aParms)
{
var stateComputer = aParms[0];
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
@ -764,16 +753,16 @@ Computer8080.prototype.donePowerOn = function(aParms)
}
this.nPowerChange = 0;
};
}
/**
/**
* checkPower()
*
* @this {Computer8080}
* @return {boolean} true if the computer is fully powered, false otherwise
*/
Computer8080.prototype.checkPower = function()
{
checkPower()
{
if (this.flags.powered) return true;
var component = null, iComponent;
@ -790,24 +779,24 @@ Computer8080.prototype.checkPower = function()
}
if (iComponent == aComponents.length) component = this;
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);
Web.alertUser(s);
return false;
};
}
/**
/**
* powerReport(stateComputer)
*
* @this {Computer8080}
* @param {State} stateComputer
*/
Computer8080.prototype.powerReport = function(stateComputer)
{
if (web.confirmUser("There may be a problem with your " + PC8080.APPNAME + " machine.\n\nTo help us diagnose it, click OK to send this " + PC8080.APPNAME + " machine state to http://" + SITEHOST + ".")) {
web.sendReport(PC8080.APPNAME, PC8080.APPVERSION, this.url, this.getUserID(), ReportAPI.TYPE.BUG, stateComputer.toString());
powerReport(stateComputer)
{
if (Web.confirmUser("There may be a problem with your " + PC8080.APPNAME + " machine.\n\nTo help us diagnose it, click OK to send this " + PC8080.APPNAME + " machine state to http://" + SITEHOST + ".")) {
Web.sendReport(PC8080.APPNAME, PC8080.APPVERSION, this.url, this.getUserID(), ReportAPI.TYPE.BUG, stateComputer.toString());
}
}
};
/**
/**
* powerOff(fSave, fShutdown)
*
* Power every component "down" and optionally save the machine state.
@ -838,8 +827,8 @@ Computer8080.prototype.powerReport = function(stateComputer)
* @param {boolean} [fShutdown] is true if the machine is being shut down
* @return {string|null} string representing the saved state (or null if error)
*/
Computer8080.prototype.powerOff = function(fSave, fShutdown)
{
powerOff(fSave, fShutdown)
{
var data;
var sState = "none";
@ -855,12 +844,12 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
var stateComputer = new State(this, PC8080.APPVERSION);
var stateValidate = new State(this, PC8080.APPVERSION, Computer8080.STATE_VALIDATE);
var sTimestamp = usr.getTimestamp();
var sTimestamp = Usr.getTimestamp();
stateValidate.set(Computer8080.STATE_TIMESTAMP, sTimestamp);
stateComputer.set(Computer8080.STATE_TIMESTAMP, sTimestamp);
stateComputer.set(Computer8080.STATE_VERSION, APPVERSION);
stateComputer.set(Computer8080.STATE_HOSTURL, web.getHostURL());
stateComputer.set(Computer8080.STATE_BROWSER, web.getUserAgent());
stateComputer.set(Computer8080.STATE_HOSTURL, Web.getHostURL());
stateComputer.set(Computer8080.STATE_BROWSER, Web.getUserAgent());
/*
* Always power the CPU "down" first, just to help insure it doesn't ask other components to do anything
@ -946,9 +935,9 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
this.nPowerChange = 0;
return sState;
};
}
/**
/**
* reset()
*
* Notify all (other) components with a reset() method that the Computer is being reset.
@ -959,8 +948,8 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
*
* @this {Computer8080}
*/
Computer8080.prototype.reset = function()
{
reset()
{
if (this.bus && this.bus.reset) {
/*
* TODO: Why does WebStorm think that this.bus.type is undefined? The base class (Component)
@ -977,9 +966,9 @@ Computer8080.prototype.reset = function()
component.reset();
}
}
};
}
/**
/**
* start(ms, nCycles)
*
* Notify all (other) components with a start() method that the CPU has started.
@ -991,8 +980,8 @@ Computer8080.prototype.reset = function()
* @param {number} ms
* @param {number} nCycles
*/
Computer8080.prototype.start = function(ms, nCycles)
{
start(ms, nCycles)
{
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
@ -1001,9 +990,9 @@ Computer8080.prototype.start = function(ms, nCycles)
component.start(ms, nCycles);
}
}
};
}
/**
/**
* stop(ms, nCycles)
*
* Notify all (other) components with a stop() method that the CPU has stopped.
@ -1015,8 +1004,8 @@ Computer8080.prototype.start = function(ms, nCycles)
* @param {number} ms
* @param {number} nCycles
*/
Computer8080.prototype.stop = function(ms, nCycles)
{
stop(ms, nCycles)
{
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
@ -1025,9 +1014,9 @@ Computer8080.prototype.stop = function(ms, nCycles)
component.stop(ms, nCycles);
}
}
};
}
/**
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {Computer8080}
@ -1037,8 +1026,8 @@ Computer8080.prototype.stop = function(ms, nCycles)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
Computer8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
setBinding(sHTMLType, sBinding, control, sValue)
{
var computer = this;
switch (sBinding) {
@ -1068,7 +1057,7 @@ Computer8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
* and since pcjs.org is no longer running a Node web server, we disable the feature for that
* particular host.
*/
if (str.endsWith(web.getHost(), "pcjs.org")) {
if (Str.endsWith(Web.getHost(), "pcjs.org")) {
if (DEBUG) this.log("Remote user API not available");
/*
* We could also simply hide the control; eg:
@ -1119,28 +1108,28 @@ Computer8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
break;
}
return false;
};
}
/**
/**
* resetUserID()
*/
Computer8080.prototype.resetUserID = function()
{
web.setLocalStorageItem(Computer8080.STATE_USERID, "");
resetUserID()
{
Web.setLocalStorageItem(Computer8080.STATE_USERID, "");
this.sUserID = null;
};
}
/**
/**
* queryUserID(fPrompt)
*
* @param {boolean} [fPrompt]
* @returns {string|null|undefined}
*/
Computer8080.prototype.queryUserID = function(fPrompt)
{
queryUserID(fPrompt)
{
var sUserID = this.sUserID;
if (!sUserID) {
sUserID = web.getLocalStorageItem(Computer8080.STATE_USERID);
sUserID = Web.getLocalStorageItem(Computer8080.STATE_USERID);
if (sUserID !== undefined) {
if (!sUserID && fPrompt) {
/*
@ -1148,7 +1137,7 @@ Computer8080.prototype.queryUserID = function(fPrompt)
* merely a precaution, because ordinarily, setBinding() should have already determined if we are
* running from pcjs.org and disabled any "Save" button.
*/
sUserID = web.promptUser("Saving machine states on the pcjs.org server is currently unsupported.\n\nIf you're running your own server, enter your user ID below.");
sUserID = Web.promptUser("Saving machine states on the pcjs.org server is currently unsupported.\n\nIf you're running your own server, enter your user ID below.");
if (sUserID) {
sUserID = this.verifyUserID(sUserID);
if (!sUserID) this.notice("The user ID is invalid.");
@ -1159,29 +1148,29 @@ Computer8080.prototype.queryUserID = function(fPrompt)
}
}
return sUserID;
};
}
/**
/**
* verifyUserID(sUserID)
*
* @this {Computer8080}
* @param {string} sUserID
* @return {string} validated user ID, or null if error
*/
Computer8080.prototype.verifyUserID = function(sUserID)
{
verifyUserID(sUserID)
{
this.sUserID = null;
var fMessages = DEBUG && this.messageEnabled();
if (fMessages) this.printMessage("verifyUserID(" + sUserID + ")");
var sRequest = web.getHost() + UserAPI.ENDPOINT + '?' + UserAPI.QUERY.REQ + '=' + UserAPI.REQ.VERIFY + '&' + UserAPI.QUERY.USER + '=' + sUserID;
var response = web.getResource(sRequest);
var sRequest = Web.getHost() + UserAPI.ENDPOINT + '?' + UserAPI.QUERY.REQ + '=' + UserAPI.REQ.VERIFY + '&' + UserAPI.QUERY.USER + '=' + sUserID;
var response = Web.getResource(sRequest);
var nErrorCode = response[0];
var sResponse = response[1];
if (!nErrorCode && sResponse) {
try {
response = eval("(" + sResponse + ")");
if (response.code && response.code == UserAPI.CODE.OK) {
web.setLocalStorageItem(Computer8080.STATE_USERID, response.data);
Web.setLocalStorageItem(Computer8080.STATE_USERID, response.data);
if (fMessages) this.printMessage(Computer8080.STATE_USERID + " updated: " + response.data);
this.sUserID = response.data;
} else {
@ -1194,38 +1183,38 @@ Computer8080.prototype.verifyUserID = function(sUserID)
if (fMessages) this.printMessage("invalid response (error " + nErrorCode + ")");
}
return this.sUserID;
};
}
/**
/**
* getServerStatePath()
*
* @this {Computer8080}
* @return {string|null} sStatePath (null if no localStorage or no USERID stored in localStorage)
*/
Computer8080.prototype.getServerStatePath = function()
{
getServerStatePath()
{
var sStatePath = null;
if (this.sUserID) {
if (DEBUG && this.messageEnabled()) {
this.printMessage(Computer8080.STATE_USERID + " for load: " + this.sUserID);
}
sStatePath = web.getHost() + UserAPI.ENDPOINT + '?' + UserAPI.QUERY.REQ + '=' + UserAPI.REQ.LOAD + '&' + UserAPI.QUERY.USER + '=' + this.sUserID + '&' + UserAPI.QUERY.STATE + '=' + State.key(this, PC8080.APPVERSION);
sStatePath = Web.getHost() + UserAPI.ENDPOINT + '?' + UserAPI.QUERY.REQ + '=' + UserAPI.REQ.LOAD + '&' + UserAPI.QUERY.USER + '=' + this.sUserID + '&' + UserAPI.QUERY.STATE + '=' + State.key(this, PC8080.APPVERSION);
} else {
if (DEBUG && this.messageEnabled()) {
this.printMessage(Computer8080.STATE_USERID + " unavailable");
}
}
return sStatePath;
};
}
/**
/**
* saveServerState(sUserID, sState)
*
* @param {string} sUserID
* @param {string|null} sState
*/
Computer8080.prototype.saveServerState = function(sUserID, sState)
{
saveServerState(sUserID, sState)
{
/*
* We must pass fSync == true, because (as I understand it) browsers will blow off any async
* requests when a page is being closed. Since our request is synchronous, storeServerState()
@ -1254,9 +1243,9 @@ Computer8080.prototype.saveServerState = function(sUserID, sState)
this.printMessage("no state to store");
}
}
};
}
/**
/**
* storeServerState(sUserID, sState, fSync)
*
* @this {Computer8080}
@ -1265,8 +1254,8 @@ Computer8080.prototype.saveServerState = function(sUserID, sState)
* @param {boolean} [fSync] is true if we're powering down and should perform a synchronous request (default is async)
* @return {*} server response if fSync is true and a response was received; otherwise null
*/
Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
{
storeServerState(sUserID, sState, fSync)
{
if (DEBUG && this.messageEnabled()) {
this.printMessage(Computer8080.STATE_USERID + " for store: " + sUserID);
}
@ -1279,11 +1268,11 @@ Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
dataPost[UserAPI.QUERY.USER] = sUserID;
dataPost[UserAPI.QUERY.STATE] = State.key(this, PC8080.APPVERSION);
dataPost[UserAPI.QUERY.DATA] = sState;
var sRequest = web.getHost() + UserAPI.ENDPOINT;
var sRequest = Web.getHost() + UserAPI.ENDPOINT;
if (!fSync) {
web.getResource(sRequest, dataPost, true);
Web.getResource(sRequest, dataPost, true);
} else {
var response = web.getResource(sRequest, dataPost);
var response = Web.getResource(sRequest, dataPost);
var sResponse = response[0];
if (response[1]) {
if (sResponse) {
@ -1297,17 +1286,17 @@ Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
return JSON.parse(sResponse);
}
return null;
};
}
/**
/**
* onPower()
*
* This handles UI requests to toggle the computer's power (eg, see the "power" button binding).
*
* @this {Computer8080}
*/
Computer8080.prototype.onPower = function()
{
onPower()
{
if (!this.nPowerChange) {
if (!this.flags.powered) {
this.wait(this.powerOn);
@ -1315,17 +1304,17 @@ Computer8080.prototype.onPower = function()
this.powerOff(false, true);
}
}
};
}
/**
/**
* onReset()
*
* This handles UI requests to reset the computer's state (eg, see the "reset" button binding).
*
* @this {Computer8080}
*/
Computer8080.prototype.onReset = function()
{
onReset()
{
/*
* 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.
@ -1346,7 +1335,7 @@ Computer8080.prototype.onReset = function()
* I used to bypass the prompt if this.resume == Computer8080.RESUME_AUTO, setting fSave to true automatically,
* but that gives the user no means of resetting a resumable machine that contains errors in its resume state.
*/
var fSave = (/* this.resume == Computer8080.RESUME_AUTO || */ web.confirmUser("Click OK to save changes to this " + PC8080.APPNAME + " machine.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
var fSave = (/* this.resume == Computer8080.RESUME_AUTO || */ Web.confirmUser("Click OK to save changes to this " + PC8080.APPNAME + " machine.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
this.powerOff(fSave, true);
/*
* Forcing the page to reload is an expedient option, but ugly. It's preferable to call powerOn()
@ -1361,7 +1350,7 @@ Computer8080.prototype.onReset = function()
* TODO: Make this more graceful, so that we can stop using the reloadPage() sledgehammer.
*/
if (!fSave && this.sStatePath) {
web.reloadPage();
Web.reloadPage();
return;
}
if (!fSave) this.fReload = true;
@ -1371,9 +1360,9 @@ Computer8080.prototype.onReset = function()
this.reset();
if (this.cpu) this.cpu.autoStart();
}
};
}
/**
/**
* getMachineComponent(sType, componentPrev)
*
* @this {Computer8080}
@ -1381,8 +1370,8 @@ Computer8080.prototype.onReset = function()
* @param {Component|null} [componentPrev] of previously returned component, if any
* @return {Component|null}
*/
Computer8080.prototype.getMachineComponent = function(sType, componentPrev)
{
getMachineComponent(sType, componentPrev)
{
var componentLast = componentPrev;
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
@ -1395,9 +1384,9 @@ Computer8080.prototype.getMachineComponent = function(sType, componentPrev)
}
if (!componentLast) Component.log("Machine component type '" + sType + "' not found", "warning");
return null;
};
}
/**
/**
* updateFocus(fScroll)
*
* NOTE: When soft keyboard buttons call us to return focus to the machine (and away from the button),
@ -1407,8 +1396,8 @@ Computer8080.prototype.getMachineComponent = function(sType, componentPrev)
* @this {Computer8080}
* @param {boolean} [fScroll] (true if you really want the control scrolled into view)
*/
Computer8080.prototype.updateFocus = function(fScroll)
{
updateFocus(fScroll)
{
if (this.aVideo.length) {
/*
* This seems to be recommended work-around to prevent the browser from scrolling the focused element
@ -1430,9 +1419,9 @@ Computer8080.prototype.updateFocus = function(fScroll)
window.scrollTo(x, y);
}
}
};
}
/**
/**
* updateStatus(fForce)
*
* If any DOM controls were bound to the CPU, then we need to call its updateStatus() handler; if there are no
@ -1452,8 +1441,8 @@ Computer8080.prototype.updateFocus = function(fScroll)
* @this {Computer8080}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
*/
Computer8080.prototype.updateStatus = function(fForce)
{
updateStatus(fForce)
{
/*
* fForce is generally set to true whenever the CPU is transitioning to/from a running state, in which case
* cpu.updateStatus() will definitely want to hide/show register contents; however, at other times, when the
@ -1465,9 +1454,9 @@ Computer8080.prototype.updateStatus = function(fForce)
*/
if (this.cpu) this.cpu.updateStatus(fForce);
if (this.panel) this.panel.updateStatus(fForce);
};
}
/**
/**
* updateVideo(fForced)
*
* Any high-frequency updates should be performed here (avoid updating DOM elements).
@ -1475,14 +1464,14 @@ Computer8080.prototype.updateStatus = function(fForce)
* @this {Computer8080}
* @param {boolean} [fForced]
*/
Computer8080.prototype.updateVideo = function(fForced)
{
updateVideo(fForced)
{
for (var i = 0; i < this.aVideo.length; i++) {
this.aVideo[i].updateScreen(fForced);
}
};
}
/**
/**
* Computer8080.init()
*
* For every machine represented by an HTML element of class "pc8080-machine", this function
@ -1490,8 +1479,8 @@ Computer8080.prototype.updateVideo = function(fForced)
* Computer constructor from the element's "data-value" attribute, invoking the constructor to
* create a Computer component, and then binding any associated HTML controls to the new component.
*/
Computer8080.init = function()
{
static init()
{
/*
* In non-COMPILED builds, embedMachine() may have set XMLVERSION.
*/
@ -1534,9 +1523,9 @@ Computer8080.init = function()
if (computer.fAutoPower) computer.wait(computer.powerOn);
}
}
};
}
/**
/**
* Computer8080.show()
*
* When exit() is using an "onbeforeunload" handler, this "onpageshow" handler allows us to repower everything,
@ -1545,8 +1534,8 @@ Computer8080.init = function()
* should be very quick, essentially just marking all components as powered again (so that, for example, the Video
* component will start drawing again) and firing the CPU up again.
*/
Computer8080.show = function()
{
static show()
{
var aeComputers = Component.getElementsByClass(document, PC8080.APPCLASS, "computer");
for (var iComputer = 0; iComputer < aeComputers.length; iComputer++) {
var eComputer = aeComputers[iComputer];
@ -1571,9 +1560,9 @@ Computer8080.show = function()
}
}
}
};
}
/**
/**
* Computer8080.exit()
*
* The Computer is currently the only component that uses an "exit" handler, which web.onExit() defines as
@ -1599,8 +1588,8 @@ Computer8080.show = function()
* "unload" instead. But even when the page must be rebuilt from scratch, the combination of browser cache and
* localStorage means the simulation should be restored and become operational almost immediately.
*/
Computer8080.exit = function()
{
static exit()
{
var aeComputers = Component.getElementsByClass(document, PC8080.APPCLASS, "computer");
for (var iComputer = 0; iComputer < aeComputers.length; iComputer++) {
var eComputer = aeComputers[iComputer];
@ -1622,13 +1611,33 @@ Computer8080.exit = function()
}
}
}
};
}
}
Computer8080.STATE_FAILSAFE = "failsafe";
Computer8080.STATE_VALIDATE = "validate";
Computer8080.STATE_TIMESTAMP = "timestamp";
Computer8080.STATE_VERSION = "version";
Computer8080.STATE_HOSTURL = "url";
Computer8080.STATE_BROWSER = "browser";
Computer8080.STATE_USERID = "user";
/*
* The following constants define all the resume options. Negative values (eg, RESUME_REPOWER) are for
* internal use only, and RESUME_DELETE is not documented (it provides a way of deleting ALL saved states
* whenever a resume is declined). As a result, the only "end-user" values are 0, 1 and 2.
*/
Computer8080.RESUME_REPOWER = -1; // resume without changing any state (for internal use only)
Computer8080.RESUME_NONE = 0; // default (no resume)
Computer8080.RESUME_AUTO = 1; // automatically save/restore state
Computer8080.RESUME_PROMPT = 2; // automatically save but conditionally restore (WARNING: if restore is declined, any state is discarded)
Computer8080.RESUME_DELETE = 3; // same as RESUME_PROMPT but discards ALL machines states whenever ANY machine restore is declined (undocumented)
/*
* Initialize every Computer on the page.
*/
web.onInit(Computer8080.init);
web.onShow(Computer8080.show);
web.onExit(Computer8080.exit);
Web.onInit(Computer8080.init);
Web.onShow(Computer8080.show);
Web.onExit(Computer8080.exit);
if (NODE) module.exports = Computer8080;
module.exports = Computer8080;

View file

@ -28,14 +28,20 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var Component = require("../../shared/lib/component");
var Messages8080= require("./messages");
}
var Str = require("../../shared/es6/strlib");
var Usr = require("../../shared/es6/usrlib");
var Component = require("../../shared/es6/component");
var Messages8080 = require("./messages");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class CPU8080 extends Component {
/**
* CPU8080(parmsCPU, nCyclesDefault)
*
* The CPU8080 class supports the following (parmsCPU) properties:
@ -63,14 +69,13 @@ if (NODE) {
*
* It is extended by the CPUState8080 component, where the simulation control logic resides.
*
* @constructor
* @extends Component
* @this {CPU8080}
* @param {Object} parmsCPU
* @param {number} nCyclesDefault
*/
function CPU8080(parmsCPU, nCyclesDefault)
{
Component.call(this, "CPU", parmsCPU, CPU8080, Messages8080.CPU);
constructor(parmsCPU, nCyclesDefault)
{
super("CPU", parmsCPU, CPU8080, Messages8080.CPU);
var nCycles = parmsCPU['cycles'] || nCyclesDefault;
@ -125,30 +130,9 @@ function CPU8080(parmsCPU, nCyclesDefault)
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
this.setReady();
}
}
Component.subclass(CPU8080);
/*
* Constants that control the frequency at which various updates should occur.
*
* These values do NOT control the simulation directly. Instead, they are used by
* calcCycles(), which uses the nCyclesPerSecond passed to the constructor as a starting
* point and computes the following variables:
*
* 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.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
CPU8080.BUTTONS = ["power", "reset"];
/**
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {CPU8080}
@ -157,8 +141,8 @@ CPU8080.BUTTONS = ["power", "reset"];
* @param {CPU8080} cpu
* @param {Debugger8080} dbg
*/
CPU8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.dbg = dbg;
@ -182,18 +166,18 @@ CPU8080.prototype.initBus = function(cmp, bus, cpu, dbg)
}
this.setReady();
};
}
/**
/**
* reset()
*
* @this {CPU8080}
*/
CPU8080.prototype.reset = function()
{
};
reset()
{
}
/**
/**
* save()
*
* This is a placeholder for save support (overridden by the CPUState8080 component).
@ -201,12 +185,12 @@ CPU8080.prototype.reset = function()
* @this {CPU8080}
* @return {Object|null}
*/
CPU8080.prototype.save = function()
{
save()
{
return null;
};
}
/**
/**
* restore(data)
*
* This is a placeholder for restore support (overridden by the CPUState8080 component).
@ -215,12 +199,12 @@ CPU8080.prototype.save = function()
* @param {Object} data
* @return {boolean} true if restore successful, false if not
*/
CPU8080.prototype.restore = function(data)
{
restore(data)
{
return false;
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {CPU8080}
@ -228,8 +212,8 @@ CPU8080.prototype.restore = function(data)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
CPU8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
this.reset();
@ -264,9 +248,9 @@ CPU8080.prototype.powerUp = function(data, fRepower)
*/
this.updateCPU();
return true;
};
}
/**
/**
* powerDown(fSave, fShutdown)
*
* @this {CPU8080}
@ -274,8 +258,8 @@ CPU8080.prototype.powerUp = function(data, fRepower)
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
CPU8080.prototype.powerDown = function(fSave, fShutdown)
{
powerDown(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.
@ -283,16 +267,16 @@ CPU8080.prototype.powerDown = function(fSave, fShutdown)
* this.flags.powered = false;
*/
return fSave? this.save() : true;
};
}
/**
/**
* autoStart()
*
* @this {CPU8080}
* @return {boolean} true if started, false if not
*/
CPU8080.prototype.autoStart = function()
{
autoStart()
{
/*
* Start running automatically on power-up, assuming there's no Debugger and no "Run" button
*/
@ -306,35 +290,35 @@ CPU8080.prototype.autoStart = function()
return true;
}
return false;
};
}
/**
/**
* isPowered()
*
* @this {CPU8080}
* @return {boolean}
*/
CPU8080.prototype.isPowered = function()
{
isPowered()
{
if (!this.flags.powered) {
this.println(this.toString() + " not powered");
return false;
}
return true;
};
}
/**
/**
* isRunning()
*
* @this {CPU8080}
* @return {boolean}
*/
CPU8080.prototype.isRunning = function()
{
isRunning()
{
return this.flags.running;
};
}
/**
/**
* getChecksum()
*
* This will be implemented by the CPUState8080 component.
@ -342,12 +326,12 @@ CPU8080.prototype.isRunning = function()
* @this {CPU8080}
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
*/
CPU8080.prototype.getChecksum = function()
{
getChecksum()
{
return 0;
};
}
/**
/**
* resetChecksum()
*
* If checksum generation is enabled (fChecksum is true), this resets the running 32-bit checksum and the
@ -357,8 +341,8 @@ CPU8080.prototype.getChecksum = function()
* @this {CPU8080}
* @return {boolean} true if checksum generation enabled, false if not
*/
CPU8080.prototype.resetChecksum = function()
{
resetChecksum()
{
if (this.nCyclesChecksumStart === undefined) this.nCyclesChecksumStart = 0;
if (this.nCyclesChecksumInterval === undefined) this.nCyclesChecksumInterval = -1;
if (this.nCyclesChecksumStop === undefined) this.nCyclesChecksumStop = -1;
@ -373,9 +357,9 @@ CPU8080.prototype.resetChecksum = function()
return true;
}
return false;
};
}
/**
/**
* updateChecksum(nCycles)
*
* When checksum generation is enabled (fChecksum is true), runCPU() asks stepCPU() to execute a minimum
@ -386,8 +370,8 @@ CPU8080.prototype.resetChecksum = function()
* @this {CPU8080}
* @param {number} nCycles
*/
CPU8080.prototype.updateChecksum = function(nCycles)
{
updateChecksum(nCycles)
{
if (this.flags.checksum) {
/*
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
@ -409,9 +393,9 @@ CPU8080.prototype.updateChecksum = function(nCycles)
}
if (fDisplay) this.displayChecksum();
}
};
}
/**
/**
* displayChecksum()
*
* When checksum generation is enabled (fChecksum is true), this is called to provide a crude log of all
@ -420,12 +404,12 @@ CPU8080.prototype.updateChecksum = function(nCycles)
*
* @this {CPU8080}
*/
CPU8080.prototype.displayChecksum = function()
{
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.nChecksum));
};
displayChecksum()
{
this.println(this.getCycles() + " cycles: " + "checksum=" + Str.toHex(this.nChecksum));
}
/**
/**
* displayValue(sLabel, nValue, cch)
*
* This is principally for displaying register values, but in reality, it can be used to display any
@ -436,8 +420,8 @@ CPU8080.prototype.displayChecksum = function()
* @param {number} nValue
* @param {number} cch
*/
CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
{
displayValue(sLabel, nValue, cch)
{
if (this.bindings[sLabel]) {
if (nValue === undefined) {
this.setError("Value for " + sLabel + " is invalid");
@ -445,7 +429,7 @@ CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
}
var sVal;
if (!this.flags.running || this.flags.displayLiveRegs) {
sVal = str.toHex(nValue, cch);
sVal = Str.toHex(nValue, cch);
} else {
sVal = "--------".substr(0, cch);
}
@ -457,9 +441,9 @@ CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
*/
if (this.bindings[sLabel].textContent != sVal) this.bindings[sLabel].textContent = sVal;
}
};
}
/**
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {CPU8080}
@ -469,8 +453,8 @@ CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
setBinding(sHTMLType, sBinding, control, sValue)
{
var cpu = this;
var fBound = false;
@ -521,9 +505,9 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
break;
}
return fBound;
};
}
/**
/**
* setBurstCycles(nCycles)
*
* This function is used by the ChipSet component whenever a very low timer count is set,
@ -537,8 +521,8 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {number} nCycles is the target number of cycles to drop the current burst to
* @return {boolean}
*/
CPU8080.prototype.setBurstCycles = function(nCycles)
{
setBurstCycles(nCycles)
{
if (this.flags.running) {
var nDelta = this.nStepCycles - nCycles;
/*
@ -555,24 +539,24 @@ CPU8080.prototype.setBurstCycles = function(nCycles)
return true;
}
return false;
};
}
/**
/**
* addCycles(nCycles, fEndStep)
*
* @this {CPU8080}
* @param {number} nCycles
* @param {boolean} [fEndStep]
*/
CPU8080.prototype.addCycles = function(nCycles, fEndStep)
{
addCycles(nCycles, fEndStep)
{
this.nTotalCycles += nCycles;
if (fEndStep) {
this.nBurstCycles = this.nStepCycles = 0;
}
};
}
/**
/**
* calcCycles(fRecalc)
*
* Calculate the number of cycles to process for each "burst" of CPU activity. The size of a burst
@ -587,8 +571,8 @@ CPU8080.prototype.addCycles = function(nCycles, fEndStep)
* @param {boolean} [fRecalc] is true if the caller wants to recalculate thresholds based on the most recent
* speed calculation (see calcSpeed).
*/
CPU8080.prototype.calcCycles = function(fRecalc)
{
calcCycles(fRecalc)
{
/*
* Calculate "per" yield values.
*/
@ -609,9 +593,9 @@ CPU8080.prototype.calcCycles = function(fRecalc)
this.nCyclesNextYield = this.nCyclesPerYield;
}
this.nCyclesRecalc = 0;
};
}
/**
/**
* getCycles(fScaled)
*
* getCycles() returns the number of cycles executed so far. Note that we can be called after
@ -628,8 +612,8 @@ CPU8080.prototype.calcCycles = function(fRecalc)
* @param {boolean} [fScaled] is true if the caller wants a cycle count relative to a multiplier of 1
* @return {number}
*/
CPU8080.prototype.getCycles = function(fScaled)
{
getCycles(fScaled)
{
var nCycles = this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles;
if (fScaled && this.nCyclesMultiplier > 1 && this.mhz > this.mhzDefault) {
/*
@ -653,9 +637,9 @@ CPU8080.prototype.getCycles = function(fScaled)
nCycles = Math.round(nCycles / this.nCyclesMultiplier);
}
return nCycles;
};
}
/**
/**
* getCyclesPerSecond()
*
* This returns the CPU's "base" speed (ie, the original cycles per second defined for the machine)
@ -663,12 +647,12 @@ CPU8080.prototype.getCycles = function(fScaled)
* @this {CPU8080}
* @return {number}
*/
CPU8080.prototype.getCyclesPerSecond = function()
{
getCyclesPerSecond()
{
return this.nCyclesPerSecond;
};
}
/**
/**
* resetCycles()
*
* Resets speed and cycle information as part of any reset() or restore(); this typically occurs during powerUp().
@ -677,55 +661,55 @@ CPU8080.prototype.getCyclesPerSecond = function()
*
* @this {CPU8080}
*/
CPU8080.prototype.resetCycles = function()
{
resetCycles()
{
this.mhz = 0;
this.nYieldsSinceStatusUpdate = 0;
this.nTotalCycles = this.nRunCycles = this.nBurstCycles = this.nStepCycles = 0;
this.resetChecksum();
this.setSpeed(1);
};
}
/**
/**
* getSpeed()
*
* @this {CPU8080}
* @return {number} the current speed multiplier
*/
CPU8080.prototype.getSpeed = function()
{
getSpeed()
{
return this.nCyclesMultiplier;
};
}
/**
/**
* getSpeedCurrent()
*
* @this {CPU8080}
* @return {string} the current speed, in mhz, as a string formatted to two decimal places
*/
CPU8080.prototype.getSpeedCurrent = function()
{
getSpeedCurrent()
{
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return ((this.flags.running && this.mhz)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
};
}
/**
/**
* getSpeedTarget()
*
* @this {CPU8080}
* @return {string} the target speed, in mhz, as a string formatted to two decimal places
*/
CPU8080.prototype.getSpeedTarget = function()
{
getSpeedTarget()
{
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return this.mhzTarget.toFixed(2) + "Mhz";
};
}
/**
/**
* setSpeed(nMultiplier, fUpdateFocus)
*
* NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point.
@ -739,8 +723,8 @@ CPU8080.prototype.getSpeedTarget = function()
* @param {boolean} [fUpdateFocus] is true to update Computer focus
* @return {boolean} true if successful, false if not
*/
CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
{
setSpeed(nMultiplier, fUpdateFocus)
{
var fSuccess = false;
if (nMultiplier !== undefined) {
/*
@ -764,21 +748,21 @@ CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
}
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
this.msStartRun = usr.getTime();
this.msStartRun = Usr.getTime();
this.msEndThisRun = 0;
this.calcCycles();
return fSuccess;
};
}
/**
/**
* calcSpeed(nCycles, msElapsed)
*
* @this {CPU8080}
* @param {number} nCycles
* @param {number} msElapsed
*/
CPU8080.prototype.calcSpeed = function(nCycles, msElapsed)
{
calcSpeed(nCycles, msElapsed)
{
if (msElapsed) {
this.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
if (msElapsed >= 86400000) {
@ -786,20 +770,20 @@ CPU8080.prototype.calcSpeed = function(nCycles, msElapsed)
this.setSpeed(); // reset all counters once per day so that we never have to worry about overflow
}
}
};
}
/**
/**
* calcStartTime()
*
* @this {CPU8080}
*/
CPU8080.prototype.calcStartTime = function()
{
calcStartTime()
{
if (this.nCyclesRecalc >= this.nCyclesPerSecond) {
this.calcCycles(true);
}
this.nCyclesThisRun = 0;
this.msStartThisRun = usr.getTime();
this.msStartThisRun = Usr.getTime();
/*
* Try to detect situations where the browser may have throttled us, such as when the user switches
@ -842,17 +826,17 @@ CPU8080.prototype.calcStartTime = function()
}
}
}
};
}
/**
/**
* calcRemainingTime()
*
* @this {CPU8080}
* @return {number}
*/
CPU8080.prototype.calcRemainingTime = function()
{
this.msEndThisRun = usr.getTime();
calcRemainingTime()
{
this.msEndThisRun = Usr.getTime();
var msYield = this.msPerYield;
if (this.nCyclesThisRun) {
@ -921,9 +905,9 @@ CPU8080.prototype.calcRemainingTime = function()
this.msEndThisRun += msRemainsThisRun;
return msRemainsThisRun;
};
}
/**
/**
* addTimer(callBack)
*
* Components that want to have timers that periodically fire after some number of milliseconds call
@ -940,14 +924,14 @@ CPU8080.prototype.calcRemainingTime = function()
* @param {function()} callBack
* @return {number} timer index
*/
CPU8080.prototype.addTimer = function(callBack)
{
addTimer(callBack)
{
var iTimer = this.aTimers.length;
this.aTimers.push([-1, callBack]);
return iTimer;
};
}
/**
/**
* setTimer(iTimer, ms, fReset)
*
* Using the timer index from a previous addTimer() call, this sets that timer to fire after the
@ -968,8 +952,8 @@ CPU8080.prototype.addTimer = function(callBack)
* @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, fReset)
{
setTimer(iTimer, ms, fReset)
{
var nCycles = -1;
if (iTimer >= 0 && iTimer < this.aTimers.length) {
if (fReset || this.aTimers[iTimer][0] < 0) {
@ -984,21 +968,21 @@ CPU8080.prototype.setTimer = function(iTimer, ms, fReset)
}
}
return nCycles;
};
}
/**
/**
* getMSCycles(ms)
*
* @this {CPU8080}
* @param {number} ms
* @return {number} number of corresponding cycles
*/
CPU8080.prototype.getMSCycles = function(ms)
{
getMSCycles(ms)
{
return (this.nCyclesPerSecond * this.nCyclesMultiplier) / 1000 * ms;
};
}
/**
/**
* getBurstCycles(nCycles)
*
* Used by runCPU() to get min(nCycles,[timer cycle counts])
@ -1007,8 +991,8 @@ CPU8080.prototype.getMSCycles = function(ms)
* @param {number} nCycles (number of cycles about to execute)
* @return {number} (either nCycles or less if a timer needs to fire)
*/
CPU8080.prototype.getBurstCycles = function(nCycles)
{
getBurstCycles(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
if (timer[0] < 0) continue;
@ -1017,9 +1001,9 @@ CPU8080.prototype.getBurstCycles = function(nCycles)
}
}
return nCycles;
};
}
/**
/**
* updateTimers(nCycles)
*
* Used by runCPU() to reduce all active timer countdown values by the number of cycles just executed;
@ -1029,8 +1013,8 @@ CPU8080.prototype.getBurstCycles = function(nCycles)
* @this {CPU8080}
* @param {number} nCycles (number of cycles actually executed)
*/
CPU8080.prototype.updateTimers = function(nCycles)
{
updateTimers(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
if (timer[0] < 0) continue;
@ -1040,34 +1024,34 @@ CPU8080.prototype.updateTimers = function(nCycles)
timer[1](); // safe to invoke the callback function now
}
}
};
}
/**
/**
* endBurst(fReset)
*
* @this {CPU8080}
* @param {boolean} [fReset]
* @return {number} (number of cycles executed in the most recent burst)
*/
CPU8080.prototype.endBurst = function(fReset)
{
endBurst(fReset)
{
var nCycles = this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
if (fReset) this.nBurstCycles = 0;
return nCycles;
};
}
/**
/**
* runCPU(fUpdateFocus)
*
* @this {CPU8080}
* @param {boolean} [fUpdateFocus] is true to update Computer focus
*/
CPU8080.prototype.runCPU = function(fUpdateFocus)
{
runCPU(fUpdateFocus)
{
if (!this.setBusy(true)) {
this.updateCPU();
if (this.cmp) this.cmp.stop(usr.getTime(), this.getCycles());
if (this.cmp) this.cmp.stop(Usr.getTime(), this.getCycles());
return;
}
@ -1124,24 +1108,24 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
catch (e) {
this.stopCPU();
this.updateCPU();
if (this.cmp) this.cmp.stop(usr.getTime(), this.getCycles());
if (this.cmp) this.cmp.stop(Usr.getTime(), this.getCycles());
this.setBusy(false);
this.setError(e.stack || e.message);
return;
}
setTimeout(this.onRunTimeout, this.calcRemainingTime());
};
}
/**
/**
* startCPU(fUpdateFocus)
*
* WARNING: Other components must use runCPU() to get the CPU running; this is a runCPU() helper function only.
*
* @param {boolean} [fUpdateFocus]
*/
CPU8080.prototype.startCPU = function(fUpdateFocus)
{
startCPU(fUpdateFocus)
{
if (!this.flags.running) {
/*
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
@ -1161,9 +1145,9 @@ CPU8080.prototype.startCPU = function(fUpdateFocus)
if (fUpdateFocus) this.cmp.updateFocus(true);
}
}
};
}
/**
/**
* stepCPU(nMinCycles)
*
* This will be implemented by the CPUState8080 component.
@ -1172,12 +1156,12 @@ CPU8080.prototype.startCPU = function(fUpdateFocus)
* @param {number} nMinCycles (0 implies a single-step, and therefore breakpoints should be ignored)
* @return {number} of cycles executed; 0 indicates that the last instruction was not executed
*/
CPU8080.prototype.stepCPU = function(nMinCycles)
{
stepCPU(nMinCycles)
{
return 0;
};
}
/**
/**
* stopCPU(fComplete)
*
* For use by any component that wants to stop the CPU.
@ -1188,8 +1172,8 @@ CPU8080.prototype.stepCPU = function(nMinCycles)
* @this {CPU8080}
* @param {boolean} [fComplete]
*/
CPU8080.prototype.stopCPU = function(fComplete)
{
stopCPU(fComplete)
{
this.isBusy(true);
this.endBurst();
this.addCycles(this.nRunCycles);
@ -1201,9 +1185,9 @@ CPU8080.prototype.stopCPU = function(fComplete)
if (controlRun) controlRun.textContent = "Run";
}
this.flags.complete = fComplete;
};
}
/**
/**
* updateCPU(fForce)
*
* This used to be performed at the end of every stepCPU(), but runCPU() -- which relies upon
@ -1214,15 +1198,15 @@ CPU8080.prototype.stopCPU = function(fComplete)
* @this {CPU8080}
* @param {boolean} [fForce] (true to force a video update; used by the Debugger)
*/
CPU8080.prototype.updateCPU = function(fForce)
{
updateCPU(fForce)
{
if (this.cmp) {
this.cmp.updateVideo(fForce);
this.cmp.updateStatus(fForce);
}
};
}
/**
/**
* yieldCPU()
*
* Similar to stopCPU() with regard to how it resets various cycle countdown values, but the CPU
@ -1230,8 +1214,8 @@ CPU8080.prototype.updateCPU = function(fForce)
*
* @this {CPU8080}
*/
CPU8080.prototype.yieldCPU = function()
{
yieldCPU()
{
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;
@ -1241,6 +1225,26 @@ CPU8080.prototype.yieldCPU = function()
* for the Video display), so I've added this call to try to keep things looking synchronized.
*/
this.updateCPU();
};
}
}
if (NODE) module.exports = CPU8080;
/*
* Constants that control the frequency at which various updates should occur.
*
* These values do NOT control the simulation directly. Instead, they are used by
* calcCycles(), which uses the nCyclesPerSecond passed to the constructor as a starting
* point and computes the following variables:
*
* 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.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
CPU8080.BUTTONS = ["power", "reset"];
module.exports = CPU8080;

View file

@ -124,4 +124,4 @@ CPUDef8080.PS.RESULT = (CPUDef8080.PS.CF | CPUDef8080.PS.PF | CPUDef8080.PS
*/
CPUDef8080.PS.SET = (CPUDef8080.PS.BIT1);
if (NODE) module.exports = CPUDef8080;
module.exports = CPUDef8080;

View file

@ -28,11 +28,8 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var CPUDef8080 = require("./CPUDef");
var Messages8080= require("./messages");
}
var CPUDef8080 = require("./CPUDef");
var Messages8080 = require("./messages");
/**
* op=0x00 (NOP)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -98,6 +98,5 @@ if (NODE) {
global.BYTEARRAYS = BYTEARRAYS;
global.TYPEDARRAYS = TYPEDARRAYS;
global.PC8080 = PC8080;
module.exports = PC8080;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -91,4 +91,4 @@ Messages8080.CATEGORIES = {
"halt": Messages8080.HALT
};
if (NODE) module.exports = Messages8080;
module.exports = Messages8080;

View file

@ -28,34 +28,35 @@
"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 PC8080 = require("./defines");
var Bus8080 = require("./bus");
var CPUDef8080 = require("./cpudef");
var Memory8080 = require("./memory");
}
var Web = require("../../shared/es6/weblib");
var Component = require("../../shared/es6/component");
var PC8080 = require("./defines");
var Bus8080 = require("./bus");
var CPUDef8080 = require("./cpudef");
var Memory8080 = require("./memory");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class Panel8080 extends Component {
/**
* Panel8080(parmsPanel)
*
* The Panel8080 component has no required (parmsPanel) properties.
*
* @constructor
* @extends Component
* @this {Panel8080}
* @param {Object} parmsPanel
*/
function Panel8080(parmsPanel)
{
Component.call(this, "Panel", parmsPanel, Panel8080);
}
constructor(parmsPanel)
{
super("Panel", parmsPanel, Panel8080);
}
Component.subclass(Panel8080);
/**
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* Most panel layouts don't have bindings of their own, so we pass along all binding requests to the
@ -69,16 +70,16 @@ Component.subclass(Panel8080);
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
Panel8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
setBinding(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 (this.kbd && this.kbd.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);
};
return super.setBinding(sHTMLType, sBinding, control, sValue);
}
/**
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {Panel8080}
@ -87,16 +88,16 @@ Panel8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
Panel8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.kbd = /** @type {Keyboard8080} */ (cmp.getMachineComponent("Keyboard"));
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {Panel8080}
@ -104,13 +105,13 @@ Panel8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
Panel8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
if (!fRepower) Panel8080.init();
return true;
};
}
/**
/**
* powerDown(fSave, fShutdown)
*
* @this {Panel8080}
@ -118,12 +119,12 @@ Panel8080.prototype.powerUp = function(data, fRepower)
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
Panel8080.prototype.powerDown = function(fSave, fShutdown)
{
powerDown(fSave, fShutdown)
{
return true;
};
}
/**
/**
* updateStatus(fForce)
*
* Update function for Panels containing elements with high-frequency display requirements.
@ -136,11 +137,11 @@ Panel8080.prototype.powerDown = function(fSave, fShutdown)
* @this {Panel8080}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
*/
Panel8080.prototype.updateStatus = function(fForce)
{
};
updateStatus(fForce)
{
}
/**
/**
* Panel8080.init()
*
* This function operates on every HTML element of class "panel", extracting the
@ -157,8 +158,8 @@ Panel8080.prototype.updateStatus = function(fForce)
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
* might want to use.
*/
Panel8080.init = function()
{
static init()
{
var fReady = false;
var aePanels = Component.getElementsByClass(document, PC8080.APPCLASS, "panel");
for (var iPanel=0; iPanel < aePanels.length; iPanel++) {
@ -172,11 +173,12 @@ Panel8080.init = function()
Component.bindComponentControls(panel, ePanel, PC8080.APPCLASS);
if (fReady) panel.setReady();
}
};
}
}
/*
* Initialize every Panel module on the page.
*/
web.onInit(Panel8080.init);
Web.onInit(Panel8080.init);
if (NODE) module.exports = Panel8080;
module.exports = Panel8080;

View file

@ -28,18 +28,24 @@
"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 State = require("../../shared/lib/state");
var PC8080 = require("./defines");
var CPUDef8080 = require("./cpudef");
var Memory8080 = require("./memory");
}
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var DumpAPI = require("../../shared/es6/dumpapi");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PC8080 = require("./defines");
var CPUDef8080 = require("./cpudef");
var Memory8080 = require("./memory");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class RAM8080 extends Component {
/**
* RAM8080(parmsRAM)
*
* The RAM8080 component expects the following (parmsRAM) properties:
@ -53,13 +59,12 @@ if (NODE) {
* 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
* @this {RAM8080}
* @param {Object} parmsRAM
*/
function RAM8080(parmsRAM)
{
Component.call(this, "RAM", parmsRAM, RAM8080);
constructor(parmsRAM)
{
super("RAM", parmsRAM, RAM8080);
this.abInit = null;
this.aSymbols = null;
@ -73,7 +78,7 @@ function RAM8080(parmsRAM)
this.fAllocated = false;
this.sFilePath = parmsRAM['file'];
this.sFileName = str.getBaseName(this.sFilePath);
this.sFileName = Str.getBaseName(this.sFilePath);
if (this.sFilePath) {
var sFileURL = this.sFilePath;
@ -83,45 +88,18 @@ function RAM8080(parmsRAM)
* JSON-encoded data, so we load it as-is; ditto for ROM files with a ".hex" extension.
* Otherwise, we ask our server-side converter to return the file in a JSON-compatible format.
*/
var sFileExt = str.getExtension(this.sFileName);
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';
sFileURL = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
}
var ram = this;
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
Web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
ram.doneLoad(sURL, sResponse, nErrorCode);
});
}
}
Component.subclass(RAM8080);
RAM8080.CPM = {
BIOS: {
VECTOR: 0x0000
},
BDOS: {
VECTOR: 0x0005,
FUNC: { // function number (specified in regC)
RESET: 0x00,
CON_READ: 0x01, // output: A = L = ASCII character
CON_WRITE: 0x02, // input: E = ASCII character
AUX_READ: 0x03, // output: A = L = ASCII character
AUX_WRITE: 0x04, // input: E = ASCII character
PRN_WRITE: 0x05, // input: E = ASCII character
MEM_SIZE: 0x06, // output: base address of CCP (Console Command Processor), but which register? (perhaps moot if this was CP/M 1.3 only...)
CON_IO: 0x06, // input: E = ASCII character (or 0xFF to return ASCII character in A)
GET_IOBYTE: 0x07,
SET_IOBYTE: 0x08,
STR_WRITE: 0x09 // input: DE = address of string
}
},
INIT: 0x100
};
RAM8080.CPM.VECTORS = [RAM8080.CPM.BIOS.VECTOR, RAM8080.CPM.BDOS.VECTOR];
/**
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {RAM8080}
@ -130,15 +108,15 @@ RAM8080.CPM.VECTORS = [RAM8080.CPM.BIOS.VECTOR, RAM8080.CPM.BDOS.VECTOR];
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
RAM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.initRAM();
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {RAM8080}
@ -146,8 +124,8 @@ RAM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
RAM8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, 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,
@ -155,9 +133,9 @@ RAM8080.prototype.powerUp = function(data, fRepower)
* logic.
*/
return true;
};
}
/**
/**
* powerDown(fSave, fShutdown)
*
* @this {RAM8080}
@ -165,8 +143,8 @@ RAM8080.prototype.powerUp = function(data, fRepower)
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
RAM8080.prototype.powerDown = function(fSave, fShutdown)
{
powerDown(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
@ -174,9 +152,9 @@ RAM8080.prototype.powerDown = function(fSave, fShutdown)
* save logic.
*/
return true;
};
}
/**
/**
* doneLoad(sURL, sData, nErrorCode)
*
* @this {RAM8080}
@ -184,8 +162,8 @@ RAM8080.prototype.powerDown = function(fSave, fShutdown)
* @param {string} sData
* @param {number} nErrorCode (response from server if anything other than 200)
*/
RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
{
doneLoad(sURL, sData, nErrorCode)
{
if (nErrorCode) {
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
return;
@ -193,7 +171,7 @@ RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
Component.addMachineResource(this.idMachine, sURL, sData);
var resource = web.parseMemoryResource(sURL, sData);
var resource = Web.parseMemoryResource(sURL, sData);
if (resource) {
this.abInit = resource.aBytes;
this.aSymbols = resource.aSymbols;
@ -203,9 +181,9 @@ RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
this.sFilePath = null;
}
this.initRAM();
};
}
/**
/**
* initRAM()
*
* This function is called by both initBus() and doneLoad(), but it cannot copy the initial data into place
@ -214,8 +192,8 @@ RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
*
* @this {RAM8080}
*/
RAM8080.prototype.initRAM = function()
{
initRAM()
{
if (!this.fAllocated && this.sizeRAM) {
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, Memory8080.TYPE.RAM)) {
this.fAllocated = true;
@ -265,29 +243,29 @@ RAM8080.prototype.initRAM = function()
}
this.setReady();
}
};
}
/**
/**
* reset()
*
* @this {RAM8080}
*/
RAM8080.prototype.reset = function()
{
reset()
{
/*
* If you want to zero RAM on reset, then this would be a good place to do it.
*/
};
}
/**
/**
* checkCPMVector(addr)
*
* @this {RAM8080}
* @param {number} addr (of the HLT opcode)
* @return {boolean} true if special processing performed, false if not
*/
RAM8080.prototype.checkCPMVector = function(addr)
{
checkCPMVector(addr)
{
var i = RAM8080.CPM.VECTORS.indexOf(addr);
if (i >= 0) {
var fCPM = false;
@ -311,29 +289,28 @@ RAM8080.prototype.checkCPMVector = function(addr)
CPUDef8080.opRET.call(cpu); // for recognized calls, automatically return
}
else if (dbg) {
this.println("\nCP/M vector " + str.toHexWord(addr));
this.println("\nCP/M vector " + Str.toHexWord(addr));
cpu.setPC(addr); // this is purely for the Debugger's benefit, to show the HLT
dbg.stopCPU();
}
return true;
}
return false;
};
}
/**
/**
* getCPMChar(ch)
*
* @this {RAM8080}
* @param {number} ch
* @return {string}
*/
RAM8080.prototype.getCPMChar = function(ch)
{
getCPMChar(ch)
{
return String.fromCharCode(ch);
};
}
/**
/**
* getCPMString(addr, chEnd)
*
* @this {RAM8080}
@ -341,8 +318,8 @@ RAM8080.prototype.getCPMChar = function(ch)
* @param {string|number} [chEnd] (terminating character, default is 0)
* @return {string}
*/
RAM8080.prototype.getCPMString = function(addr, chEnd)
{
getCPMString(addr, chEnd)
{
var s = "";
var cchMax = 255;
var bEnd = chEnd && chEnd.length && chEnd.charCodeAt(0) || chEnd || 0;
@ -352,24 +329,24 @@ RAM8080.prototype.getCPMString = function(addr, chEnd)
s += String.fromCharCode(b);
}
return s;
};
}
/**
/**
* writeCPMString(s)
*
* @this {RAM8080}
* @param {string} s
*/
RAM8080.prototype.writeCPMString = function(s)
{
writeCPMString(s)
{
s = s.replace(/\r/g, '');
if (this.controlPrint) {
this.controlPrint.value += s;
this.controlPrint.scrollTop = this.controlPrint.scrollHeight;
}
};
}
/**
/**
* RAM8080.init()
*
* This function operates on every HTML element of class "ram", extracting the
@ -377,8 +354,8 @@ RAM8080.prototype.writeCPMString = function(s)
* attribute, invoking the constructor to create a RAM8080 component, and then binding
* any associated HTML controls to the new component.
*/
RAM8080.init = function()
{
static init()
{
var aeRAM = Component.getElementsByClass(document, PC8080.APPCLASS, "ram");
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
var eRAM = aeRAM[iRAM];
@ -386,11 +363,37 @@ RAM8080.init = function()
var ram = new RAM8080(parmsRAM);
Component.bindComponentControls(ram, eRAM, PC8080.APPCLASS);
}
}
}
RAM8080.CPM = {
BIOS: {
VECTOR: 0x0000
},
BDOS: {
VECTOR: 0x0005,
FUNC: { // function number (specified in regC)
RESET: 0x00,
CON_READ: 0x01, // output: A = L = ASCII character
CON_WRITE: 0x02, // input: E = ASCII character
AUX_READ: 0x03, // output: A = L = ASCII character
AUX_WRITE: 0x04, // input: E = ASCII character
PRN_WRITE: 0x05, // input: E = ASCII character
MEM_SIZE: 0x06, // output: base address of CCP (Console Command Processor), but which register? (perhaps moot if this was CP/M 1.3 only...)
CON_IO: 0x06, // input: E = ASCII character (or 0xFF to return ASCII character in A)
GET_IOBYTE: 0x07,
SET_IOBYTE: 0x08,
STR_WRITE: 0x09 // input: DE = address of string
}
},
INIT: 0x100
};
RAM8080.CPM.VECTORS = [RAM8080.CPM.BIOS.VECTOR, RAM8080.CPM.BDOS.VECTOR];
/*
* Initialize all the RAM8080 modules on the page.
*/
web.onInit(RAM8080.init);
Web.onInit(RAM8080.init);
if (NODE) module.exports = RAM8080;
module.exports = RAM8080;

View file

@ -28,16 +28,22 @@
"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 PC8080 = require("./defines");
var Memory8080 = require("./memory");
}
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var DumpAPI = require("../../shared/es6/dumpapi");
var Component = require("../../shared/es6/component");
var PC8080 = require("./defines");
var Memory8080 = require("./memory");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class ROM8080 extends Component {
/**
* ROM8080(parmsROM)
*
* The ROM8080 component expects the following (parmsROM) properties:
@ -53,13 +59,12 @@ if (NODE) {
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
* is the ROM you expected.
*
* @constructor
* @extends Component
* @this {ROM8080}
* @param {Object} parmsROM
*/
function ROM8080(parmsROM)
{
Component.call(this, "ROM", parmsROM, ROM8080);
constructor(parmsROM)
{
super("ROM", parmsROM, ROM8080);
this.abROM = null;
this.addrROM = parmsROM['addr'];
@ -79,7 +84,7 @@ function ROM8080(parmsROM)
this.addrAlias = parmsROM['alias'];
this.sFilePath = parmsROM['file'];
this.sFileName = str.getBaseName(this.sFilePath);
this.sFileName = Str.getBaseName(this.sFilePath);
if (this.sFilePath) {
var sFileURL = this.sFilePath;
@ -89,33 +94,18 @@ function ROM8080(parmsROM)
* 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);
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';
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) {
Web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
rom.doneLoad(sURL, sResponse, nErrorCode);
});
}
}
}
Component.subclass(ROM8080);
/*
* 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 {ROM8080}
@ -124,15 +114,15 @@ Component.subclass(ROM8080);
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
ROM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.copyROM();
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {ROM8080}
@ -140,8 +130,8 @@ ROM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
ROM8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
if (this.aSymbols) {
if (this.dbg) {
this.dbg.addSymbols(this.id, this.addrROM, this.sizeROM, this.aSymbols);
@ -153,9 +143,9 @@ ROM8080.prototype.powerUp = function(data, fRepower)
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
@ -168,12 +158,12 @@ ROM8080.prototype.powerUp = function(data, fRepower)
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
ROM8080.prototype.powerDown = function(fSave, fShutdown)
{
powerDown(fSave, fShutdown)
{
return true;
};
}
/**
/**
* doneLoad(sURL, sROMData, nErrorCode)
*
* @this {ROM8080}
@ -181,8 +171,8 @@ ROM8080.prototype.powerDown = function(fSave, fShutdown)
* @param {string} sROMData
* @param {number} nErrorCode (response from server if anything other than 200)
*/
ROM8080.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
{
doneLoad(sURL, sROMData, nErrorCode)
{
if (nErrorCode) {
this.notice("Unable to load system ROM (error " + nErrorCode + ": " + sURL + ")");
return;
@ -242,13 +232,13 @@ ROM8080.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
var asHexData = sHexData.split(" ");
this.abROM = new Array(asHexData.length);
for (var i = 0; i < asHexData.length; i++) {
this.abROM[i] = str.parseInt(asHexData[i], 16);
this.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
@ -257,8 +247,8 @@ ROM8080.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
*
* @this {ROM8080}
*/
ROM8080.prototype.copyROM = function()
{
copyROM()
{
if (!this.isReady()) {
if (!this.sFilePath) {
this.setReady();
@ -277,7 +267,7 @@ ROM8080.prototype.copyROM = function()
* 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) + ")");
this.setError("ROM size (" + Str.toHexLong(this.abROM.length) + ") does not match specified size (" + Str.toHexLong(this.sizeROM) + ")");
}
else if (this.addROM(this.addrROM)) {
@ -305,19 +295,19 @@ ROM8080.prototype.copyROM = function()
this.setReady();
}
}
};
}
/**
/**
* addROM(addr)
*
* @this {ROM8080}
* @param {number} addr
* @return {boolean}
*/
ROM8080.prototype.addROM = function(addr)
{
addROM(addr)
{
if (this.bus.addMemory(addr, this.sizeROM, Memory8080.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abROM.length) + " bytes)");
if (DEBUG) this.log("addROM(): copying ROM to " + Str.toHexLong(addr) + " (" + Str.toHexLong(this.abROM.length) + " bytes)");
var i;
for (i = 0; i < this.abROM.length; i++) {
this.bus.setByteDirect(addr + i, this.abROM[i]);
@ -328,9 +318,9 @@ ROM8080.prototype.addROM = function(addr)
* 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,
@ -343,13 +333,13 @@ ROM8080.prototype.addROM = function(addr)
* @this {ROM8080}
* @param {number} addr
*/
ROM8080.prototype.cloneROM = function(addr)
{
cloneROM(addr)
{
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
};
}
/**
/**
* ROM8080.init()
*
* This function operates on every HTML element of class "rom", extracting the
@ -357,8 +347,8 @@ ROM8080.prototype.cloneROM = function(addr)
* attribute, invoking the constructor to create a ROM8080 component, and then binding
* any associated HTML controls to the new component.
*/
ROM8080.init = function()
{
static init()
{
var aeROM = Component.getElementsByClass(document, PC8080.APPCLASS, "rom");
for (var iROM = 0; iROM < aeROM.length; iROM++) {
var eROM = aeROM[iROM];
@ -366,11 +356,25 @@ ROM8080.init = function()
var rom = new ROM8080(parmsROM);
Component.bindComponentControls(rom, eROM, PC8080.APPCLASS);
}
};
}
}
/*
* 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().
*/
/*
* Initialize all the ROM8080 modules on the page.
*/
web.onInit(ROM8080.init);
Web.onInit(ROM8080.init);
if (NODE) module.exports = ROM8080;
module.exports = ROM8080;

View file

@ -28,16 +28,22 @@
"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 PC8080 = require("./defines");
var Messages8080= require("./messages");
}
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PC8080 = require("./defines");
var Messages8080 = require("./messages");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class SerialPort8080 extends Component {
/**
* SerialPort8080(parmsSerial)
*
* The SerialPort8080 component has the following component-specific (parmsSerial) properties:
@ -56,11 +62,12 @@ if (NODE) {
* parseInt(), and as an added benefit, we don't need to worry about whether a hex or decimal format
* was used.
*
* @constructor
* @extends Component
* @this {SerialPort8080}
* @param {Object} parmsSerial
*/
function SerialPort8080(parmsSerial) {
constructor(parmsSerial)
{
super("SerialPort", parmsSerial, SerialPort8080, Messages8080.SERIAL);
this.iAdapter = +parmsSerial['adapter'];
@ -113,8 +120,6 @@ function SerialPort8080(parmsSerial) {
this.fAutoStop = false;
this.fNullModem = true;
Component.call(this, "SerialPort", parmsSerial, SerialPort8080, Messages8080.SERIAL);
var sBinding = parmsSerial['binding'];
if (sBinding == "console") {
this.consoleOutput = "";
@ -139,128 +144,9 @@ function SerialPort8080(parmsSerial) {
'receiveData': this.receiveData,
'receiveStatus': this.receiveStatus
};
}
}
/*
* class SerialPort8080
* 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(SerialPort8080);
SerialPort8080.UART8251 = {
/*
* Format of MODE byte written to CONTROL port 0x1
*/
MODE: {
BAUD_FACTOR: 0x03, // 00=SYNC, 01=1x, 10=16x, 11=64x
DATA_BITS: 0x0C, // 00=5, 01=6, 10=7, 11=8
PARITY_ENABLE: 0x10,
EVEN_PARITY: 0x20,
STOP_BITS: 0xC0, // 00=invalid, 01=1, 10=1.5, 11=2
INIT: 0x8E // 16x baud rate, 8 data bits, no parity, 1.5 stop bits
},
/*
* Format of COMMAND byte written to CONTROL port 0x1
*/
COMMAND: {
XMIT_ENABLE: 0x01,
DTR: 0x02, // Data Terminal Ready
RECV_ENABLE: 0x04,
SEND_BREAK: 0x08,
ERROR_RESET: 0x10,
RTS: 0x20, // Request To Send
INTERNAL_RESET: 0x40,
HUNT_MODE: 0x80,
INIT: 0x27 // XMIT_ENABLE | DTR | RECV_ENABLE | RTS
},
/*
* Format of STATUS byte read from CONTROL port 0x1
*/
STATUS: {
XMIT_READY: 0x01,
RECV_FULL: 0x02,
XMIT_EMPTY: 0x04,
PARITY_ERROR: 0x08,
OVERRUN_ERROR: 0x10,
FRAMING_ERROR: 0x20,
BREAK_DETECT: 0x40,
DSR: 0x80, // Data Set Ready
INIT: 0x85 // XMIT_READY | XMIT_EMPTY | DSR
},
/*
* Format of BAUDRATES byte written to port 0x2
*
* Each nibble is an index (0x0-0xF) into a set of internal CPU clock divisors that yield the
* following baud rates:
*
* Index Divisor Baud Rate
* ----- ------- ---------
* 0x0 3456 50
* 0x1 2304 75
* 0x2 1571 110
* 0x3 1285 134.5
* 0x4 1152 150
* 0x5 864 200
* 0x6 576 300
* 0x7 288 600
* 0x8 144 1200
* 0x9 96 1800
* 0xA 86 2000
* 0xB 72 2400
* 0xC 48 3600
* 0xD 36 4800
* 0xE 18 9600 (default)
* 0xF 9 19200
*
* NOTE: This is a VT100-specific port and baud rate table.
*/
BAUDRATES: {
RECV_RATE: 0x0F,
XMIT_RATE: 0xF0,
INIT: 0xEE // default to 9600 (0xE) for both XMIT and RECV
},
BAUDTABLE: [
50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200
]
};
SerialPort8080.UART8251.INIT = [
false,
0,
0,
SerialPort8080.UART8251.STATUS.INIT,
SerialPort8080.UART8251.MODE.INIT,
SerialPort8080.UART8251.COMMAND.INIT,
SerialPort8080.UART8251.BAUDRATES.INIT
];
/*
* Internal name used for the I/O buffer control, if any, that we bind to a SerialPort8080.
*
* Alternatively, if SerialPort8080 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.
*/
SerialPort8080.sIOBuffer = "buffer";
/**
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {SerialPort8080}
@ -270,8 +156,8 @@ SerialPort8080.sIOBuffer = "buffer";
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
SerialPort8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
setBinding(sHTMLType, sBinding, control, sValue)
{
var serial = this;
switch (sBinding) {
@ -381,17 +267,17 @@ SerialPort8080.prototype.setBinding = function(sHTMLType, sBinding, control, sVa
break;
}
return false;
};
}
/**
/**
* echoByte(b)
*
* @this {SerialPort8080}
* @param {number} b
* @return {boolean} true if echo, false if not
*/
SerialPort8080.prototype.echoByte = function(b)
{
echoByte(b)
{
var fEchoed = false;
if (this.controlIOBuffer) {
@ -403,12 +289,12 @@ SerialPort8080.prototype.echoByte = function(b)
if (this.iLogicalCol > 0) this.iLogicalCol--;
}
else {
var s = str.toASCIICode(b);
var s = Str.toASCIICode(b);
var nChars = s.length;
if (b == 0x09) {
var tabSize = this.tabSize || 8;
nChars = tabSize - (this.iLogicalCol % tabSize);
if (this.tabSize) s = str.pad("", nChars);
if (this.tabSize) s = Str.pad("", nChars);
}
else if (b == 0x0D) {
this.iLogicalCol = nChars = 0;
@ -433,9 +319,9 @@ SerialPort8080.prototype.echoByte = function(b)
}
return fEchoed;
};
}
/**
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {SerialPort8080}
@ -444,8 +330,8 @@ SerialPort8080.prototype.echoByte = function(b)
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
@ -465,9 +351,9 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addPortOutputTable(this, SerialPort8080.aPortOutput, this.portBase);
this.setReady();
};
}
/**
/**
* initConnection(fNullModem)
*
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
@ -489,16 +375,16 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* @this {SerialPort8080}
* @param {boolean} [fNullModem] (caller's null-modem setting, to ensure our settings are in agreement)
*/
SerialPort8080.prototype.initConnection = function(fNullModem)
{
initConnection(fNullModem)
{
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]);
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]);
var sTargetID = Str.trim(asParts[1]);
this.connection = Component.getComponentByID(sTargetID);
if (this.connection) {
var exports = this.connection['exports'];
@ -521,9 +407,9 @@ SerialPort8080.prototype.initConnection = function(fNullModem)
this.status("Unable to establish connection: " + sConnection);
}
}
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {SerialPort8080}
@ -531,8 +417,8 @@ SerialPort8080.prototype.initConnection = function(fNullModem)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
SerialPort8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
if (!fRepower) {
/*
@ -550,9 +436,9 @@ SerialPort8080.prototype.powerUp = function(data, fRepower)
}
}
return true;
};
}
/**
/**
* powerDown(fSave, fShutdown)
*
* @this {SerialPort8080}
@ -560,22 +446,22 @@ SerialPort8080.prototype.powerUp = function(data, fRepower)
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
SerialPort8080.prototype.powerDown = function(fSave, fShutdown)
{
powerDown(fSave, fShutdown)
{
return fSave? this.save() : true;
};
}
/**
/**
* reset()
*
* @this {SerialPort8080}
*/
SerialPort8080.prototype.reset = function()
{
reset()
{
this.initState();
};
}
/**
/**
* save()
*
* This implements save support for the SerialPort8080 component.
@ -583,14 +469,14 @@ SerialPort8080.prototype.reset = function()
* @this {SerialPort8080}
* @return {Object}
*/
SerialPort8080.prototype.save = function()
{
save()
{
var state = new State(this);
state.set(0, this.saveRegisters());
return state.data();
};
}
/**
/**
* restore(data)
*
* This implements restore support for the SerialPort8080 component.
@ -599,20 +485,20 @@ SerialPort8080.prototype.save = function()
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
SerialPort8080.prototype.restore = function(data)
{
restore(data)
{
return this.initState(data[0]);
};
}
/**
/**
* initState(data)
*
* @this {SerialPort8080}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
SerialPort8080.prototype.initState = function(data)
{
initState(data)
{
var i = 0;
if (data === undefined) {
data = SerialPort8080.UART8251.INIT;
@ -625,16 +511,16 @@ SerialPort8080.prototype.initState = function(data)
this.bCommand = data[i++];
this.bBaudRates = data[i];
return true;
};
}
/**
/**
* saveRegisters()
*
* @this {SerialPort8080}
* @return {Array}
*/
SerialPort8080.prototype.saveRegisters = function()
{
saveRegisters()
{
var i = 0;
var data = [];
data[i++] = this.fReady;
@ -645,17 +531,17 @@ SerialPort8080.prototype.saveRegisters = function()
data[i++] = this.bCommand;
data[i] = this.bBaudRates;
return data;
};
}
/**
/**
* getBaudTimeout(maskRate)
*
* @this {SerialPort8080}
* @param {number} maskRate (either SerialPort8080.UART8251.BAUDRATES.RECV_RATE or SerialPort8080.UART8251.BAUDRATES.XMIT_RATE)
* @return {number} (number of milliseconds per byte)
*/
SerialPort8080.prototype.getBaudTimeout = function(maskRate)
{
getBaudTimeout(maskRate)
{
var indexRate = (this.bBaudRates & maskRate);
if (!(maskRate & 0xf)) indexRate >>= 4;
var nBaud = SerialPort8080.UART8251.BAUDTABLE[indexRate];
@ -664,19 +550,19 @@ SerialPort8080.prototype.getBaudTimeout = function(maskRate)
nBits += ((((this.bMode & SerialPort8080.UART8251.MODE.STOP_BITS) >> 6) + 1) >> 1);
var nBytesPerSecond = Math.round(nBaud / nBits);
return 1000 / nBytesPerSecond;
};
}
/**
/**
* receiveByte(b)
*
* @this {SerialPort8080}
* @param {number} b
* @return {boolean}
*/
SerialPort8080.prototype.receiveByte = function(b)
{
receiveByte(b)
{
if (MAXDEBUG) this.echoByte(b);
this.printMessage("receiveByte(" + str.toHexByte(b) + "), status=" + str.toHexByte(this.bStatus));
this.printMessage("receiveByte(" + Str.toHexByte(b) + "), status=" + Str.toHexByte(this.bStatus));
if (!this.fAutoStop && !(this.bStatus & SerialPort8080.UART8251.STATUS.RECV_FULL)) {
this.bDataIn = b;
this.bStatus |= SerialPort8080.UART8251.STATUS.RECV_FULL;
@ -684,9 +570,9 @@ SerialPort8080.prototype.receiveByte = function(b)
return true;
}
return false;
};
}
/**
/**
* receiveData(data)
*
* Helper for clocking received data at the expected RECV_RATE.
@ -699,8 +585,8 @@ SerialPort8080.prototype.receiveByte = function(b)
* @param {number|string|undefined} [data]
* @return {boolean} true if received, false if not
*/
SerialPort8080.prototype.receiveData = function(data)
{
receiveData(data)
{
if (data != null) {
if (typeof data != "number") {
this.sDataReceived = data;
@ -717,9 +603,9 @@ SerialPort8080.prototype.receiveData = function(data)
}
}
return true; // for now, return true regardless, since we're buffering everything anyway
};
}
/**
/**
* receiveStatus(pins)
*
* NOTE: Prior to the addition of this interface, the DSR bit was initialized set and remained set for the life
@ -729,24 +615,24 @@ SerialPort8080.prototype.receiveData = function(data)
* @this {SerialPort8080}
* @param {number} pins
*/
SerialPort8080.prototype.receiveStatus = function(pins)
{
receiveStatus(pins)
{
this.bStatus &= ~SerialPort8080.UART8251.STATUS.DSR;
if (pins & RS232.DSR.MASK) this.bStatus |= SerialPort8080.UART8251.STATUS.DSR;
};
}
/**
/**
* transmitByte(b)
*
* @this {SerialPort8080}
* @param {number} b
* @return {boolean} true if transmitted, false if not
*/
SerialPort8080.prototype.transmitByte = function(b)
{
transmitByte(b)
{
var fTransmitted = false;
this.printMessage("transmitByte(" + str.toHexByte(b) + ")");
this.printMessage("transmitByte(" + Str.toHexByte(b) + ")");
if (this.fAutoXOFF) {
if (b == 0x13) { // XOFF
@ -770,9 +656,9 @@ SerialPort8080.prototype.transmitByte = function(b)
}
return fTransmitted;
};
}
/**
/**
* transmitData(sData)
*
* Helper for clocking transmitted data at the expected XMIT_RATE.
@ -784,16 +670,16 @@ SerialPort8080.prototype.transmitByte = function(b)
* @param {string} [sData]
* @return {boolean} true if successful, false if not
*/
SerialPort8080.prototype.transmitData = function(sData)
{
transmitData(sData)
{
this.bStatus |= (SerialPort8080.UART8251.STATUS.XMIT_READY | SerialPort8080.UART8251.STATUS.XMIT_EMPTY);
if (sData) {
return this.sendData? this.sendData.call(this.connection, sData) : false;
}
return true;
};
}
/**
/**
* isTransmitterReady()
*
* Called whenever a ChipSet circuit needs the SerialPort8080 UART's transmitter status.
@ -801,12 +687,12 @@ SerialPort8080.prototype.transmitData = function(sData)
* @this {SerialPort8080}
* @return {boolean} (true if ready, false if not)
*/
SerialPort8080.prototype.isTransmitterReady = function()
{
isTransmitterReady()
{
return !!(this.bStatus & SerialPort8080.UART8251.STATUS.XMIT_READY);
};
}
/**
/**
* inData(port, addrFrom)
*
* @this {SerialPort8080}
@ -814,15 +700,15 @@ SerialPort8080.prototype.isTransmitterReady = function()
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort8080.prototype.inData = function(port, addrFrom)
{
inData(port, addrFrom)
{
var b = this.bDataIn;
this.printMessageIO(port, null, addrFrom, "DATA", b);
this.bStatus &= ~SerialPort8080.UART8251.STATUS.RECV_FULL;
return b;
};
}
/**
/**
* inControl(port, addrFrom)
*
* @this {SerialPort8080}
@ -830,14 +716,14 @@ SerialPort8080.prototype.inData = function(port, addrFrom)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort8080.prototype.inControl = function(port, addrFrom)
{
inControl(port, addrFrom)
{
var b = this.bStatus;
this.printMessageIO(port, null, addrFrom, "STATUS", b);
return b;
};
}
/**
/**
* outData(port, bOut, addrFrom)
*
* @this {SerialPort8080}
@ -845,8 +731,8 @@ SerialPort8080.prototype.inControl = function(port, addrFrom)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort8080.prototype.outData = function(port, bOut, addrFrom)
{
outData(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bDataOut = bOut;
this.bStatus &= ~(SerialPort8080.UART8251.STATUS.XMIT_READY | SerialPort8080.UART8251.STATUS.XMIT_EMPTY);
@ -865,9 +751,9 @@ SerialPort8080.prototype.outData = function(port, bOut, addrFrom)
if (this.cpu) {
this.cpu.setTimer(this.timerTransmitNext, this.getBaudTimeout(SerialPort8080.UART8251.BAUDRATES.XMIT_RATE));
}
};
}
/**
/**
* outControl(port, bOut, addrFrom)
*
* Writes to the CONTROL port (0x1) are either MODE or COMMAND bytes. If the device has just
@ -880,8 +766,8 @@ SerialPort8080.prototype.outData = function(port, bOut, addrFrom)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort8080.prototype.outControl = function(port, bOut, addrFrom)
{
outControl(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "CONTROL");
if (!this.fReady) {
this.bMode = bOut;
@ -909,9 +795,9 @@ SerialPort8080.prototype.outControl = function(port, bOut, addrFrom)
this.fReady = false;
}
}
};
}
/**
/**
* outBaudRates(port, bOut, addrFrom)
*
* @this {SerialPort8080}
@ -919,12 +805,149 @@ SerialPort8080.prototype.outControl = function(port, bOut, addrFrom)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort8080.prototype.outBaudRates = function(port, bOut, addrFrom)
{
outBaudRates(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "BAUDRATES");
this.bBaudRates = bOut;
}
/**
* SerialPort8080.init()
*
* This function operates on every HTML element of class "serial", extracting the
* JSON-encoded parameters for the SerialPort8080 constructor from the element's "data-value"
* attribute, invoking the constructor to create a SerialPort8080 component, and then binding
* any associated HTML controls to the new component.
*/
static init()
{
var aeSerial = Component.getElementsByClass(document, PC8080.APPCLASS, "serial");
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
var eSerial = aeSerial[iSerial];
var parmsSerial = Component.getComponentParms(eSerial);
var serial = new SerialPort8080(parmsSerial);
Component.bindComponentControls(serial, eSerial, PC8080.APPCLASS);
}
}
}
/*
* class SerialPort8080
* 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.
*/
SerialPort8080.UART8251 = {
/*
* Format of MODE byte written to CONTROL port 0x1
*/
MODE: {
BAUD_FACTOR: 0x03, // 00=SYNC, 01=1x, 10=16x, 11=64x
DATA_BITS: 0x0C, // 00=5, 01=6, 10=7, 11=8
PARITY_ENABLE: 0x10,
EVEN_PARITY: 0x20,
STOP_BITS: 0xC0, // 00=invalid, 01=1, 10=1.5, 11=2
INIT: 0x8E // 16x baud rate, 8 data bits, no parity, 1.5 stop bits
},
/*
* Format of COMMAND byte written to CONTROL port 0x1
*/
COMMAND: {
XMIT_ENABLE: 0x01,
DTR: 0x02, // Data Terminal Ready
RECV_ENABLE: 0x04,
SEND_BREAK: 0x08,
ERROR_RESET: 0x10,
RTS: 0x20, // Request To Send
INTERNAL_RESET: 0x40,
HUNT_MODE: 0x80,
INIT: 0x27 // XMIT_ENABLE | DTR | RECV_ENABLE | RTS
},
/*
* Format of STATUS byte read from CONTROL port 0x1
*/
STATUS: {
XMIT_READY: 0x01,
RECV_FULL: 0x02,
XMIT_EMPTY: 0x04,
PARITY_ERROR: 0x08,
OVERRUN_ERROR: 0x10,
FRAMING_ERROR: 0x20,
BREAK_DETECT: 0x40,
DSR: 0x80, // Data Set Ready
INIT: 0x85 // XMIT_READY | XMIT_EMPTY | DSR
},
/*
* Format of BAUDRATES byte written to port 0x2
*
* Each nibble is an index (0x0-0xF) into a set of internal CPU clock divisors that yield the
* following baud rates:
*
* Index Divisor Baud Rate
* ----- ------- ---------
* 0x0 3456 50
* 0x1 2304 75
* 0x2 1571 110
* 0x3 1285 134.5
* 0x4 1152 150
* 0x5 864 200
* 0x6 576 300
* 0x7 288 600
* 0x8 144 1200
* 0x9 96 1800
* 0xA 86 2000
* 0xB 72 2400
* 0xC 48 3600
* 0xD 36 4800
* 0xE 18 9600 (default)
* 0xF 9 19200
*
* NOTE: This is a VT100-specific port and baud rate table.
*/
BAUDRATES: {
RECV_RATE: 0x0F,
XMIT_RATE: 0xF0,
INIT: 0xEE // default to 9600 (0xE) for both XMIT and RECV
},
BAUDTABLE: [
50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200
]
};
SerialPort8080.UART8251.INIT = [
false,
0,
0,
SerialPort8080.UART8251.STATUS.INIT,
SerialPort8080.UART8251.MODE.INIT,
SerialPort8080.UART8251.COMMAND.INIT,
SerialPort8080.UART8251.BAUDRATES.INIT
];
/*
* Internal name used for the I/O buffer control, if any, that we bind to a SerialPort8080.
*
* Alternatively, if SerialPort8080 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.
*/
SerialPort8080.sIOBuffer = "buffer";
/*
* Port input notification table
*/
@ -943,28 +966,9 @@ SerialPort8080.aPortOutput = {
0x2: SerialPort8080.prototype.outBaudRates
};
/**
* SerialPort8080.init()
*
* This function operates on every HTML element of class "serial", extracting the
* JSON-encoded parameters for the SerialPort8080 constructor from the element's "data-value"
* attribute, invoking the constructor to create a SerialPort8080 component, and then binding
* any associated HTML controls to the new component.
*/
SerialPort8080.init = function()
{
var aeSerial = Component.getElementsByClass(document, PC8080.APPCLASS, "serial");
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
var eSerial = aeSerial[iSerial];
var parmsSerial = Component.getComponentParms(eSerial);
var serial = new SerialPort8080(parmsSerial);
Component.bindComponentControls(serial, eSerial, PC8080.APPCLASS);
}
};
/*
* Initialize every SerialPort8080 module on the page.
*/
web.onInit(SerialPort8080.init);
Web.onInit(SerialPort8080.init);
if (NODE) module.exports = SerialPort8080;
module.exports = SerialPort8080;

View file

@ -28,19 +28,25 @@
"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 State = require("../../shared/lib/state");
var PC8080 = require("./defines");
var ChipSet8080 = require("./chipset");
var Memory8080 = require("./memory");
var Messages8080= require("./messages");
}
var Str = require("../../shared/es6/strlib");
var Web = require("../../shared/es6/weblib");
var DumpAPI = require("../../shared/es6/dumpapi");
var Component = require("../../shared/es6/component");
var State = require("../../shared/es6/state");
var PC8080 = require("./defines");
var ChipSet8080 = require("./chipset");
var Memory8080 = require("./memory");
var Messages8080 = require("./messages");
/**
* TODO: The Closure Compiler treats ES6 classes as 'struct' rather than 'dict' by default,
* which would force us to declare all class properties in the constructor, as well as prevent
* us from defining any named properties. So, for now, we mark all our classes as 'unrestricted'.
*
* @unrestricted
*/
class Video8080 extends Component {
/**
* Video8080(parmsVideo, canvas, context, textarea, container)
*
* The Video8080 component can be configured with the following (parmsVideo) properties:
@ -84,21 +90,20 @@ if (NODE) {
* transformation methods (translate(), rotate(), and scale()), while bufferRotate inverts the dimensions
* of the off-screen buffer and then relies on setPixel() to "rotate" the data into the proper location.
*
* @constructor
* @extends Component
* @this {Video8080}
* @param {Object} parmsVideo
* @param {Object} [canvas]
* @param {Object} [context]
* @param {Object} [textarea]
* @param {Object} [container]
*/
function Video8080(parmsVideo, canvas, context, textarea, container)
{
var video = this;
this.fGecko = web.isUserAgent("Gecko/");
var i, sEvent, asWebPrefixes = ['', 'moz', 'ms', 'webkit'];
constructor(parmsVideo, canvas, context, textarea, container)
{
super("Video", parmsVideo, Video8080, Messages8080.VIDEO);
Component.call(this, "Video", parmsVideo, Video8080, Messages8080.VIDEO);
var video = this;
this.fGecko = Web.isUserAgent("Gecko/");
var i, sEvent, asWebPrefixes = ['', 'moz', 'ms', 'webkit'];
this.cxScreen = parmsVideo['screenWidth'];
this.cyScreen = parmsVideo['screenHeight'];
@ -166,7 +171,7 @@ function Video8080(parmsVideo, canvas, context, textarea, container)
* that's apparently been added to Chrome. Sigh.
*/
var fSmoothing = parmsVideo['smoothing'];
var sSmoothing = web.getURLParm('smoothing');
var sSmoothing = Web.getURLParm('smoothing');
if (sSmoothing) fSmoothing = (sSmoothing == "true");
if (fSmoothing != null) {
for (i = 0; i < asWebPrefixes.length; i++) {
@ -236,68 +241,26 @@ function Video8080(parmsVideo, canvas, context, textarea, container)
this.sFontROM = parmsVideo['fontROM'];
if (this.sFontROM) {
var sFileExt = str.getExtension(this.sFontROM);
var sFileExt = Str.getExtension(this.sFontROM);
if (sFileExt != "json") {
this.sFontROM = web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFontROM + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES;
this.sFontROM = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFontROM + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES;
}
web.getResource(this.sFontROM, null, true, function(sURL, sResponse, nErrorCode) {
Web.getResource(this.sFontROM, null, true, function(sURL, sResponse, nErrorCode) {
video.doneLoad(sURL, sResponse, nErrorCode);
});
}
this.ledBindings = {};
}
}
Component.subclass(Video8080);
Video8080.COLORS = {
OVERLAY_TOP: 0,
OVERLAY_BOTTOM: 1,
OVERLAY_TOTAL: 2
};
Video8080.FORMAT = {
UNKNOWN: 0,
SI1978: 1,
VT100: 2
};
Video8080.FORMATS = {
"SI1978": Video8080.FORMAT.SI1978,
"VT100": Video8080.FORMAT.VT100
};
Video8080.VT100 = {
/*
* The following font IDs are nothing more than all the possible LINEATTR values masked with FONTMASK;
* also, note that double-high implies double-wide; the VT100 doesn't support a double-high single-wide font.
*/
FONT: {
NORML: 0x60, // normal font (eg, 10x10)
DWIDE: 0x40, // double-wide, single-high font (eg, 20x10)
DHIGH: 0x20, // technically, this means display only the TOP half of the double-high font (eg, 20x20)
DHIGH_BOT: 0x00 // technically, this means display only the BOTTOM half of the double-high font (eg, 20x20)
},
LINETERM: 0x7F,
LINEATTR: {
ADDRMASK: 0x0F,
ADDRBIAS: 0x10, // 0x10 == ADDRBIAS_LO, 0x00 = ADDRBIAS_HI
FONTMASK: 0x60,
SCROLL: 0x80
},
ADDRBIAS_LO: 0x2000,
ADDRBIAS_HI: 0x4000
};
/**
/**
* initBuffers()
*
* @this {Video8080}
* @return {boolean}
*/
Video8080.prototype.initBuffers = function()
{
initBuffers()
{
/*
* Allocate off-screen buffers now
*/
@ -388,9 +351,9 @@ Video8080.prototype.initBuffers = function()
this.abLineBuffer = new Array(this.nColsBuffer);
}
return true;
};
}
/**
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {Video8080}
@ -399,8 +362,8 @@ Video8080.prototype.initBuffers = function()
* @param {CPUState8080} cpu
* @param {Debugger8080} dbg
*/
Video8080.prototype.initBus = function(cmp, bus, cpu, dbg)
{
initBus(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
@ -433,9 +396,9 @@ Video8080.prototype.initBus = function(cmp, bus, cpu, dbg)
this.nUpdates = 0;
if (!this.sFontROM) this.setReady();
};
}
/**
/**
* doneLoad(sURL, sFontData, nErrorCode)
*
* @this {Video8080}
@ -443,8 +406,8 @@ Video8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* @param {string} sFontData
* @param {number} nErrorCode (response from server if anything other than 200)
*/
Video8080.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
{
doneLoad(sURL, sFontData, nErrorCode)
{
if (nErrorCode) {
this.notice("Unable to load font ROM (error " + nErrorCode + ": " + sURL + ")");
return;
@ -494,16 +457,16 @@ Video8080.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
* TODO: Consider a more general-purpose solution for deciding whether or not the user wants to run in a "headless" mode.
*/
if (this.contextScreen || this.dbg) this.setReady();
};
}
/**
/**
* createFonts()
*
* @this {Video8080}
* @return {boolean}
*/
Video8080.prototype.createFonts = function()
{
createFonts()
{
/*
* We retain abFontData in case we have to rebuild the fonts (eg, when we switch from 80 to 132 columns)
*/
@ -524,9 +487,9 @@ Video8080.prototype.createFonts = function()
return true;
}
return false;
};
}
/**
/**
* createFontVariation(cxCell, cyCell, fUnderline)
*
* This creates a 16x16 character grid for the requested font variation. Variations include:
@ -542,8 +505,8 @@ Video8080.prototype.createFonts = function()
* @param {boolean} [fUnderline] (null for unmodified font, false for reverse video, true for underline)
* @return {Object}
*/
Video8080.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
{
createFontVariation(cxCell, cyCell, fUnderline)
{
/*
* On a VT100, cxCell,cyCell is initially 10,10, but may change to 9,10 for 132-column mode.
*/
@ -604,9 +567,9 @@ Video8080.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
font.context.putImageData(imageChar, (iChar & 0xf) * cxCell, (iChar >> 4) * cyCell);
}
return font;
};
}
/**
/**
* powerUp(data, fRepower)
*
* @this {Video8080}
@ -614,8 +577,8 @@ Video8080.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
Video8080.prototype.powerUp = function(data, fRepower)
{
powerUp(data, fRepower)
{
/*
* Because the VT100 frame buffer can be located anywhere in RAM (above 0x2000), we must defer this
* test code until the powerUp() notification handler is called, when all RAM has (hopefully) been allocated.
@ -685,9 +648,9 @@ Video8080.prototype.powerUp = function(data, fRepower)
this.updateVT100();
}
return true;
};
}
/**
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {Video8080}
@ -697,8 +660,8 @@ Video8080.prototype.powerUp = function(data, fRepower)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
Video8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
setBinding(sHTMLType, sBinding, control, sValue)
{
var video = this;
/*
@ -727,9 +690,9 @@ Video8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
break;
}
return false;
};
}
/**
/**
* updateDimensions(nCols, nRows)
*
* Called from the ChipSet component whenever the screen dimensions have been dynamically altered.
@ -738,8 +701,8 @@ Video8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* @param {number} nCols (should be either 80 or 132; 80 is the default)
* @param {number} nRows (should be either 24 or 14; 24 is the default)
*/
Video8080.prototype.updateDimensions = function(nCols, nRows)
{
updateDimensions(nCols, nRows)
{
this.printMessage("updateDimensions(" + nCols + "," + nRows + ")");
this.nColsBuffer = nCols;
/*
@ -754,9 +717,9 @@ Video8080.prototype.updateDimensions = function(nCols, nRows)
if (this.initBuffers()) {
this.createFonts();
}
};
}
/**
/**
* updateRate(nRate)
*
* Called from the ChipSet component whenever the monitor refresh rate has been dynamically altered.
@ -764,13 +727,13 @@ Video8080.prototype.updateDimensions = function(nCols, nRows)
* @this {Video8080}
* @param {number} nRate (should be either 50 or 60; 60 is the default)
*/
Video8080.prototype.updateRate = function(nRate)
{
updateRate(nRate)
{
this.printMessage("updateRate(" + nRate + ")");
this.rateMonitor = nRate;
};
}
/**
/**
* updateScrollOffset(bScroll)
*
* Called from the ChipSet component whenever the screen scroll offset has been dynamically altered.
@ -778,8 +741,8 @@ Video8080.prototype.updateRate = function(nRate)
* @this {Video8080}
* @param {number} bScroll
*/
Video8080.prototype.updateScrollOffset = function(bScroll)
{
updateScrollOffset(bScroll)
{
this.printMessage("updateScrollOffset(" + bScroll + ")");
if (this.bScrollOffset !== bScroll) {
this.bScrollOffset = bScroll;
@ -804,16 +767,16 @@ Video8080.prototype.updateScrollOffset = function(bScroll)
this.fSkipSingleCellUpdate = true;
}
}
};
}
/**
/**
* doFullScreen()
*
* @this {Video8080}
* @return {boolean} true if request successful, false if not (eg, failed OR not supported)
*/
Video8080.prototype.doFullScreen = function()
{
doFullScreen()
{
var fSuccess = false;
if (this.container) {
if (this.container.doFullScreen) {
@ -873,16 +836,16 @@ Video8080.prototype.doFullScreen = function()
this.setFocus();
}
return fSuccess;
};
}
/**
/**
* notifyFullScreen(fFullScreen)
*
* @this {Video8080}
* @param {boolean|null} fFullScreen (null if there was a full-screen error)
*/
Video8080.prototype.notifyFullScreen = function(fFullScreen)
{
notifyFullScreen(fFullScreen)
{
if (!fFullScreen && this.container) {
if (!this.fGecko) {
this.container.style.width = this.container.style.height = "";
@ -891,30 +854,30 @@ Video8080.prototype.notifyFullScreen = function(fFullScreen)
}
}
this.printMessage("notifyFullScreen(" + fFullScreen + ")");
};
}
/**
/**
* setFocus()
*
* @this {Video8080}
*/
Video8080.prototype.setFocus = function()
{
setFocus()
{
if (this.inputScreen) this.inputScreen.focus();
};
}
/**
/**
* getRefreshTime()
*
* @this {Video8080}
* @return {number} (number of milliseconds per refresh)
*/
Video8080.prototype.getRefreshTime = function()
{
getRefreshTime()
{
return 1000 / Math.max(this.rateRefresh, this.rateInterrupt);
};
}
/**
/**
* initCellCache(nCells)
*
* Initializes the contents of our internal cell cache.
@ -922,24 +885,24 @@ Video8080.prototype.getRefreshTime = function()
* @this {Video8080}
* @param {number} nCells
*/
Video8080.prototype.initCellCache = function(nCells)
{
initCellCache(nCells)
{
this.nCellCache = nCells;
this.fCellCacheValid = false;
if (this.aCellCache === undefined || this.aCellCache.length != this.nCellCache) {
this.aCellCache = new Array(this.nCellCache);
}
};
}
/**
/**
* initColors()
*
* This creates an array of nColors, with additional OVERLAY_TOTAL colors tacked on to the end of the array.
*
* @this {Video8080}
*/
Video8080.prototype.initColors = function()
{
initColors()
{
var rgbBlack = [0x00, 0x00, 0x00, 0xff];
var rgbWhite = [0xff, 0xff, 0xff, 0xff];
this.nColors = (1 << this.nBitsPerPixel);
@ -953,9 +916,9 @@ Video8080.prototype.initColors = function()
this.aRGB[this.nColors + Video8080.COLORS.OVERLAY_TOP] = rgbYellow;
this.aRGB[this.nColors + Video8080.COLORS.OVERLAY_BOTTOM] = rgbGreen;
}
};
}
/**
/**
* setPixel(image, x, y, bPixel)
*
* @this {Video8080}
@ -964,8 +927,8 @@ Video8080.prototype.initColors = function()
* @param {number} y
* @param {number} bPixel (ie, an index into aRGB)
*/
Video8080.prototype.setPixel = function(image, x, y, bPixel)
{
setPixel(image, x, y, bPixel)
{
var index;
if (!this.rotateBuffer) {
index = (x + y * image.width);
@ -986,9 +949,9 @@ Video8080.prototype.setPixel = function(image, x, y, bPixel)
image.data[index+1] = rgb[1];
image.data[index+2] = rgb[2];
image.data[index+3] = rgb[3];
};
}
/**
/**
* updateChar(idFont, col, row, data, context)
*
* Updates a particular character cell (row,col) in the associated window.
@ -1000,8 +963,8 @@ Video8080.prototype.setPixel = function(image, x, y, bPixel)
* @param {number} data
* @param {Object} [context]
*/
Video8080.prototype.updateChar = function(idFont, col, row, data, context)
{
updateChar(idFont, col, row, data, context)
{
var bChar = data & 0x7f;
var font = this.aFonts[idFont][(data & 0x80)? 1 : 0];
if (!font) return;
@ -1053,16 +1016,16 @@ Video8080.prototype.updateChar = function(idFont, col, row, data, context)
yDst += this.yScreenOffset;
this.contextScreen.drawImage(font.canvas, xSrc, ySrc, cxSrc, cySrc, xDst, yDst, cxDst, cyDst);
}
};
}
/**
/**
* updateVT100(fForced)
*
* @this {Video8080}
* @param {boolean} [fForced]
*/
Video8080.prototype.updateVT100 = function(fForced)
{
updateVT100(fForced)
{
var addrNext = this.addrBuffer, fontNext = -1;
var nRows = 0;
@ -1183,9 +1146,9 @@ Video8080.prototype.updateVT100 = function(fForced)
this.cyScreenOffset // cyDst
);
}
};
}
/**
/**
* updateScreen(fForced)
*
* Propagates the video buffer to the cell cache and updates the screen with any changes. Forced updates
@ -1199,8 +1162,8 @@ Video8080.prototype.updateVT100 = function(fForced)
* @this {Video8080}
* @param {boolean} [fForced]
*/
Video8080.prototype.updateScreen = function(fForced)
{
updateScreen(fForced)
{
var fUpdate = true;
if (!fForced) {
@ -1250,31 +1213,31 @@ Video8080.prototype.updateScreen = function(fForced)
} else {
this.updateScreenGraphics(fForced);
}
};
}
/**
/**
* updateScreenText(fForced)
*
* @this {Video8080}
* @param {boolean} [fForced]
*/
Video8080.prototype.updateScreenText = function(fForced)
{
updateScreenText(fForced)
{
switch(this.nFormat) {
case Video8080.FORMAT.VT100:
this.updateVT100(fForced);
break;
}
};
}
/**
/**
* updateScreenGraphics(fForced)
*
* @this {Video8080}
* @param {boolean} [fForced]
*/
Video8080.prototype.updateScreenGraphics = function(fForced)
{
updateScreenGraphics(fForced)
{
var addr = this.addrBuffer;
var addrLimit = addr + this.sizeBuffer;
@ -1352,9 +1315,9 @@ Video8080.prototype.updateScreenGraphics = function(fForced)
*/
this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.canvasBuffer.width, this.canvasBuffer.height, 0, 0, this.cxScreen, this.cyScreen);
}
};
}
/**
/**
* Video8080.init()
*
* This function operates on every HTML element of class "video", extracting the
@ -1362,8 +1325,8 @@ Video8080.prototype.updateScreenGraphics = function(fForced)
* attribute, invoking the constructor to create a Video component, and then binding
* any associated HTML controls to the new component.
*/
Video8080.init = function()
{
static init()
{
var aeVideo = Component.getElementsByClass(document, PC8080.APPCLASS, "video");
for (var iVideo = 0; iVideo < aeVideo.length; iVideo++) {
var eVideo = aeVideo[iVideo];
@ -1396,7 +1359,7 @@ Video8080.init = function()
* browsers don't actually support an 'onresize' handler on anything but the window object.
*/
eCanvas.style.height = "auto";
if (web.getUserAgent().indexOf("MSIE") >= 0) {
if (Web.getUserAgent().indexOf("MSIE") >= 0) {
eVideo.onresize = function(eParent, eChild, cx, cy) {
return function onResizeVideo() {
eChild.style.height = (((eParent.clientWidth * cy) / cx) | 0) + "px";
@ -1410,13 +1373,13 @@ Video8080.init = function()
* until we figure out a better UI. And note that we use our web.onPageEvent() helper function to make
* sure we don't trample any other 'onresize' handler(s) attached to the window object.
*/
var aspect = +(parmsVideo['aspect'] || web.getURLParm('aspect'));
var aspect = +(parmsVideo['aspect'] || Web.getURLParm('aspect'));
/*
* No 'aspect' parameter yields NaN, which is falsey, and anything else must satisfy my arbitrary
* constraints of 0.3 <= aspect <= 3.33, to prevent any useless (or worse, browser-blowing) results.
*/
if (aspect && aspect >= 0.3 && aspect <= 3.33) {
web.onPageEvent('onresize', function(eParent, eChild, aspectRatio) {
Web.onPageEvent('onresize', function(eParent, eChild, aspectRatio) {
return function onResizeWindow() {
/*
* Since aspectRatio is the target width/height, we have:
@ -1472,7 +1435,7 @@ Video8080.init = function()
* which is not the initial keyboard state that the Keyboard component expects, so hopefully turning off
* these "auto" attributes will help.
*/
if (web.isUserAgent("iOS")) {
if (Web.isUserAgent("iOS")) {
eTextArea.setAttribute("autocapitalize", "off");
eTextArea.setAttribute("autocorrect", "off");
}
@ -1490,11 +1453,52 @@ Video8080.init = function()
*/
Component.bindComponentControls(video, eVideo, PC8080.APPCLASS);
}
}
}
Video8080.COLORS = {
OVERLAY_TOP: 0,
OVERLAY_BOTTOM: 1,
OVERLAY_TOTAL: 2
};
Video8080.FORMAT = {
UNKNOWN: 0,
SI1978: 1,
VT100: 2
};
Video8080.FORMATS = {
"SI1978": Video8080.FORMAT.SI1978,
"VT100": Video8080.FORMAT.VT100
};
Video8080.VT100 = {
/*
* The following font IDs are nothing more than all the possible LINEATTR values masked with FONTMASK;
* also, note that double-high implies double-wide; the VT100 doesn't support a double-high single-wide font.
*/
FONT: {
NORML: 0x60, // normal font (eg, 10x10)
DWIDE: 0x40, // double-wide, single-high font (eg, 20x10)
DHIGH: 0x20, // technically, this means display only the TOP half of the double-high font (eg, 20x20)
DHIGH_BOT: 0x00 // technically, this means display only the BOTTOM half of the double-high font (eg, 20x20)
},
LINETERM: 0x7F,
LINEATTR: {
ADDRMASK: 0x0F,
ADDRBIAS: 0x10, // 0x10 == ADDRBIAS_LO, 0x00 = ADDRBIAS_HI
FONTMASK: 0x60,
SCROLL: 0x80
},
ADDRBIAS_LO: 0x2000,
ADDRBIAS_HI: 0x4000
};
/*
* Initialize every Video module on the page.
*/
web.onInit(Video8080.init);
Web.onInit(Video8080.init);
if (NODE) module.exports = Video8080;
module.exports = Video8080;

View file

@ -138,16 +138,9 @@ class Component {
/** @type {Object|null} controlPrint is the HTML control, if any, that we can print to */
this.controlPrint = null;
/** @type {ComputerPDP11|null} */
this.cmp = null;
/** @type {BusPDP11|null} */
this.bus = null;
/** @type {CPUStatePDP11|null} */
this.cpu = null;
/** @type {DebuggerPDP11|null} */
this.dbg = null;
/*
@ -969,6 +962,31 @@ class Component {
}
}
}
/**
* printMessageIO(port, bOut, addrFrom, name, bIn, bitsMessage)
*
* If bitsMessage is not specified, the component's MESSAGE category is used.
* If bitsMessage is true, the message is displayed as long as MESSAGE.PORT is enabled.
*
* @this {Component}
* @param {number} port
* @param {number|null} bOut if an output operation
* @param {number|null} [addrFrom]
* @param {string|null} [name] of the port, if any
* @param {number|null} [bIn] is the input value, if known, on an input operation
* @param {number|boolean} [bitsMessage] is zero or more MESSAGE_* category flag(s)
*/
printMessageIO(port, bOut, addrFrom, name, bIn, bitsMessage) {
if (DEBUGGER && this.dbg) {
if (bitsMessage === true) {
bitsMessage = 0;
} else if (bitsMessage == null) {
bitsMessage = this.bitsMessage;
}
this.dbg.messageIO(this, port, bOut, addrFrom, name, bIn, bitsMessage);
}
}
}
/*

View file

@ -50,6 +50,31 @@ var Component = require("../../shared/es6/component");
*/
var DbgAddr;
/**
* Since the Closure Compiler treats ES6 classes as @struct rather than @dict by default,
* it deters us from defining named properties on our components; eg:
*
* this['exports'] = {...}
*
* results in an error:
*
* Cannot do '[]' access on a struct
*
* So, in order to define 'exports', we must override the @struct assumption by annotating
* the class as @unrestricted (or @dict). Note that this must be done both here and in the
* subclass (eg, SerialPort), because otherwise the Compiler won't allow us to *reference*
* the named property either.
*
* TODO: Consider marking ALL our classes unrestricted, because otherwise it forces us to
* define every single property the class uses in its constructor, which results in a fair
* bit of redundant initialization, since many properties aren't (and don't need to be) fully
* initialized until the appropriate init(), reset(), restore(), etc. function is called.
*
* The upside, however, may be that since the structure of the class is completely defined by
* the constructor, JavaScript engines may be able to optimize and run more efficiently.
*
* @unrestricted
*/
class Debugger extends Component {
/**
* Debugger(parmsDbg)

View file

@ -32,7 +32,7 @@ var Keys = {
/*
* Keys and/or key combinations that generate common ASCII codes.
*
* NOTE: If you're looking for a general-purpose ASCII code table, see str.ASCII in strlib.js;
* NOTE: If you're looking for a general-purpose ASCII code table, see Str.ASCII in strlib.js;
* if something's missing, that's probably the more appropriate table to add it to.
*
* TODO: The Closure Compiler doesn't inline all references to these values, at least those with

View file

@ -144,15 +144,15 @@
"./modules/shared/lib/save.js"
],
"pc8080Files": [
"./modules/shared/lib/defines.js",
"./modules/shared/lib/dumpapi.js",
"./modules/shared/lib/reportapi.js",
"./modules/shared/lib/userapi.js",
"./modules/shared/lib/keys.js",
"./modules/shared/lib/strlib.js",
"./modules/shared/lib/usrlib.js",
"./modules/shared/lib/weblib.js",
"./modules/shared/lib/component.js",
"./modules/shared/es6/defines.js",
"./modules/shared/es6/dumpapi.js",
"./modules/shared/es6/reportapi.js",
"./modules/shared/es6/userapi.js",
"./modules/shared/es6/keys.js",
"./modules/shared/es6/strlib.js",
"./modules/shared/es6/usrlib.js",
"./modules/shared/es6/weblib.js",
"./modules/shared/es6/component.js",
"./modules/pc8080/lib/defines.js",
"./modules/pc8080/lib/cpudef.js",
"./modules/pc8080/lib/messages.js",
@ -168,11 +168,11 @@
"./modules/pc8080/lib/keyboard.js",
"./modules/pc8080/lib/video.js",
"./modules/pc8080/lib/serialport.js",
"./modules/shared/lib/debugger.js",
"./modules/shared/es6/debugger.js",
"./modules/pc8080/lib/debugger.js",
"./modules/pc8080/lib/computer.js",
"./modules/shared/lib/state.js",
"./modules/shared/lib/embed.js"
"./modules/shared/es6/state.js",
"./modules/shared/es6/embed.js"
],
"pdp11Files": [
"./modules/shared/es6/defines.js",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long