Converted PC8080 to ES6
This commit is contained in:
parent
d930b31c53
commit
0f680839f7
27 changed files with 11054 additions and 10896 deletions
16
Gruntfile.js
16
Gruntfile.js
|
|
@ -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, '')
|
||||
|
|
|
|||
|
|
@ -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, ""))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,21 @@
|
|||
|
||||
"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 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)
|
||||
*
|
||||
|
|
@ -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)
|
||||
constructor(parmsBus, cpu, dbg)
|
||||
{
|
||||
Component.call(this, "Bus", parmsBus, Bus8080);
|
||||
super("Bus", parmsBus, Bus8080);
|
||||
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
|
@ -144,15 +149,6 @@ function Bus8080(parmsBus, cpu, dbg)
|
|||
this.setReady();
|
||||
}
|
||||
|
||||
Component.subclass(Bus8080);
|
||||
|
||||
Bus8080.ERROR = {
|
||||
ADD_MEM_INUSE: 1,
|
||||
ADD_MEM_BADRANGE: 2,
|
||||
SET_MEM_BADRANGE: 4,
|
||||
REM_MEM_BADRANGE: 5
|
||||
};
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
|
|
@ -160,7 +156,7 @@ Bus8080.ERROR = {
|
|||
*
|
||||
* @this {Bus8080}
|
||||
*/
|
||||
Bus8080.prototype.initMemory = function()
|
||||
initMemory()
|
||||
{
|
||||
var block = new Memory8080();
|
||||
block.copyBreakpoints(this.dbg);
|
||||
|
|
@ -168,16 +164,16 @@ Bus8080.prototype.initMemory = function()
|
|||
for (var iBlock = 0; iBlock < this.nBlockTotal; iBlock++) {
|
||||
this.aMemBlocks[iBlock] = block;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {Bus8080}
|
||||
*/
|
||||
Bus8080.prototype.reset = function()
|
||||
reset()
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
|
|
@ -196,11 +192,11 @@ 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)
|
||||
|
|
@ -231,7 +227,7 @@ 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;
|
||||
|
|
@ -279,12 +275,12 @@ 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)
|
||||
|
|
@ -294,7 +290,7 @@ 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;
|
||||
|
|
@ -307,43 +303,7 @@ 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)
|
||||
|
|
@ -356,7 +316,7 @@ 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;
|
||||
|
|
@ -371,13 +331,13 @@ 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()
|
||||
|
|
@ -385,10 +345,10 @@ Bus8080.prototype.scanMemory = function(info, addr, size)
|
|||
* @this {Bus8080}
|
||||
* @return {number}
|
||||
*/
|
||||
Bus8080.prototype.getWidth = function()
|
||||
getWidth()
|
||||
{
|
||||
return this.nBusWidth;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* removeMemory(addr, size)
|
||||
|
|
@ -402,7 +362,7 @@ 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;
|
||||
|
|
@ -417,7 +377,7 @@ Bus8080.prototype.removeMemory = function(addr, size)
|
|||
return true;
|
||||
}
|
||||
return this.reportError(Bus8080.ERROR.REM_MEM_BADRANGE, addr, size);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getMemoryBlocks(addr, size)
|
||||
|
|
@ -427,7 +387,7 @@ 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;
|
||||
|
|
@ -436,7 +396,7 @@ Bus8080.prototype.getMemoryBlocks = function(addr, size)
|
|||
size -= this.nBlockSize;
|
||||
}
|
||||
return aBlocks;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setMemoryBlocks(addr, size, aBlocks, type)
|
||||
|
|
@ -453,7 +413,7 @@ 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;
|
||||
|
|
@ -469,7 +429,7 @@ Bus8080.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
|||
this.aMemBlocks[iBlock++] = block;
|
||||
size -= this.nBlockSize;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getByte(addr)
|
||||
|
|
@ -478,10 +438,10 @@ Bus8080.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
|||
* @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)
|
||||
|
|
@ -492,10 +452,10 @@ 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)
|
||||
|
|
@ -504,7 +464,7 @@ Bus8080.prototype.getByteDirect = function(addr)
|
|||
* @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;
|
||||
|
|
@ -512,7 +472,7 @@ Bus8080.prototype.getShort = function(addr)
|
|||
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)
|
||||
|
|
@ -523,7 +483,7 @@ 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;
|
||||
|
|
@ -531,7 +491,7 @@ Bus8080.prototype.getShortDirect = function(addr)
|
|||
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)
|
||||
|
|
@ -540,10 +500,10 @@ Bus8080.prototype.getShortDirect = function(addr)
|
|||
* @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)
|
||||
|
|
@ -555,10 +515,10 @@ 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)
|
||||
|
|
@ -567,7 +527,7 @@ Bus8080.prototype.setByteDirect = function(addr, b)
|
|||
* @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;
|
||||
|
|
@ -577,7 +537,7 @@ 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)
|
||||
|
|
@ -589,7 +549,7 @@ 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;
|
||||
|
|
@ -599,7 +559,7 @@ 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)
|
||||
|
|
@ -608,13 +568,13 @@ Bus8080.prototype.setShortDirect = function(addr, w)
|
|||
* @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)
|
||||
|
|
@ -623,13 +583,13 @@ Bus8080.prototype.addMemBreak = function(addr, fWrite)
|
|||
* @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)
|
||||
|
|
@ -660,7 +620,7 @@ 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,7 +639,7 @@ Bus8080.prototype.saveMemory = function(fAll)
|
|||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreMemory(a)
|
||||
|
|
@ -698,7 +658,7 @@ 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) {
|
||||
|
|
@ -719,7 +679,7 @@ Bus8080.prototype.restoreMemory = function(a)
|
|||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addPortInputBreak(port)
|
||||
|
|
@ -728,7 +688,7 @@ Bus8080.prototype.restoreMemory = function(a)
|
|||
* @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;
|
||||
|
|
@ -739,7 +699,7 @@ Bus8080.prototype.addPortInputBreak = function(port)
|
|||
}
|
||||
this.aPortInputNotify[port][1] = !this.aPortInputNotify[port][1];
|
||||
return this.aPortInputNotify[port][1];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addPortInputNotify(start, end, fn)
|
||||
|
|
@ -751,19 +711,19 @@ 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)
|
||||
|
|
@ -775,7 +735,7 @@ 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) {
|
||||
|
|
@ -783,7 +743,7 @@ Bus8080.prototype.addPortInputTable = function(component, table, offset)
|
|||
this.addPortInputNotify(+port + offset, +port + offset, table[port].bind(component));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addPortInputWidth(port, size)
|
||||
|
|
@ -794,10 +754,10 @@ 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)
|
||||
|
|
@ -811,7 +771,7 @@ 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;
|
||||
|
||||
|
|
@ -859,26 +819,7 @@ 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)
|
||||
|
|
@ -887,7 +828,7 @@ Bus8080.prototype.removePortInputNotify = function(start, end)
|
|||
* @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;
|
||||
|
|
@ -898,7 +839,7 @@ Bus8080.prototype.addPortOutputBreak = function(port)
|
|||
}
|
||||
this.aPortOutputNotify[port][1] = !this.aPortOutputNotify[port][1];
|
||||
return this.aPortOutputNotify[port][1];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addPortOutputNotify(start, end, fn)
|
||||
|
|
@ -910,19 +851,19 @@ 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)
|
||||
|
|
@ -934,7 +875,7 @@ 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) {
|
||||
|
|
@ -942,7 +883,7 @@ Bus8080.prototype.addPortOutputTable = function(component, table, offset)
|
|||
this.addPortOutputNotify(+port + offset, +port + offset, table[port].bind(component));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addPortOutputWidth(port, size)
|
||||
|
|
@ -953,10 +894,10 @@ 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)
|
||||
|
|
@ -967,7 +908,7 @@ 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;
|
||||
|
||||
|
|
@ -1007,7 +948,26 @@ 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)
|
||||
|
|
@ -1018,14 +978,14 @@ 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];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -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)
|
||||
reportError(op, addr, size, fQuiet)
|
||||
{
|
||||
var sError = "Memory block error (" + op + ": " + str.toHex(addr) + "," + str.toHex(size) + ")";
|
||||
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
|
|
@ -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 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);
|
||||
});
|
||||
}
|
||||
|
|
@ -272,45 +277,26 @@ function Computer8080(parmsComputer, parmsMachine, fSuspended) {
|
|||
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;
|
||||
|
|
@ -323,7 +309,7 @@ Computer8080.prototype.setMachineParms = function(parmsMachine)
|
|||
}
|
||||
}
|
||||
this.parmsMachine = parmsMachine;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getMachineParm(sParm, parmsComponent)
|
||||
|
|
@ -335,11 +321,12 @@ 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
|
||||
|
|
@ -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,27 +347,29 @@ 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)
|
||||
|
|
@ -390,7 +379,7 @@ 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;
|
||||
|
|
@ -401,10 +390,10 @@ 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)
|
||||
|
|
@ -424,7 +413,7 @@ 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);
|
||||
|
|
@ -439,7 +428,7 @@ Computer8080.prototype.wait = function(fn, parms)
|
|||
}
|
||||
if (DEBUG && this.messageEnabled()) this.printMessage("Computer8080.wait(ready)");
|
||||
fn.call(this, parms);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* validateState(stateComputer)
|
||||
|
|
@ -450,7 +439,7 @@ 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);
|
||||
|
|
@ -468,7 +457,7 @@ Computer8080.prototype.validateState = function(stateComputer)
|
|||
}
|
||||
}
|
||||
return fValid;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerOn(resume)
|
||||
|
|
@ -478,7 +467,7 @@ 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,7 +598,7 @@ Computer8080.prototype.powerOn = function(resume)
|
|||
return;
|
||||
}
|
||||
this.donePowerOn(aParms);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerRestore(component, stateComputer, fRepower, fRestore)
|
||||
|
|
@ -621,7 +610,7 @@ 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) {
|
||||
|
||||
|
|
@ -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,7 +701,7 @@ Computer8080.prototype.powerRestore = function(component, stateComputer, fRepowe
|
|||
}
|
||||
}
|
||||
return fRestore;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* donePowerOn(aParms)
|
||||
|
|
@ -722,7 +711,7 @@ 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);
|
||||
|
|
@ -764,7 +753,7 @@ Computer8080.prototype.donePowerOn = function(aParms)
|
|||
}
|
||||
|
||||
this.nPowerChange = 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* checkPower()
|
||||
|
|
@ -772,7 +761,7 @@ Computer8080.prototype.donePowerOn = function(aParms)
|
|||
* @this {Computer8080}
|
||||
* @return {boolean} true if the computer is fully powered, false otherwise
|
||||
*/
|
||||
Computer8080.prototype.checkPower = function()
|
||||
checkPower()
|
||||
{
|
||||
if (this.flags.powered) return true;
|
||||
|
||||
|
|
@ -790,9 +779,9 @@ 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)
|
||||
|
|
@ -800,12 +789,12 @@ Computer8080.prototype.checkPower = function()
|
|||
* @this {Computer8080}
|
||||
* @param {State} stateComputer
|
||||
*/
|
||||
Computer8080.prototype.powerReport = function(stateComputer)
|
||||
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());
|
||||
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)
|
||||
|
|
@ -838,7 +827,7 @@ 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,7 +935,7 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
|
|||
this.nPowerChange = 0;
|
||||
|
||||
return sState;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
|
|
@ -959,7 +948,7 @@ Computer8080.prototype.powerOff = function(fSave, fShutdown)
|
|||
*
|
||||
* @this {Computer8080}
|
||||
*/
|
||||
Computer8080.prototype.reset = function()
|
||||
reset()
|
||||
{
|
||||
if (this.bus && this.bus.reset) {
|
||||
/*
|
||||
|
|
@ -977,7 +966,7 @@ Computer8080.prototype.reset = function()
|
|||
component.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* start(ms, nCycles)
|
||||
|
|
@ -991,7 +980,7 @@ 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++) {
|
||||
|
|
@ -1001,7 +990,7 @@ Computer8080.prototype.start = function(ms, nCycles)
|
|||
component.start(ms, nCycles);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* stop(ms, nCycles)
|
||||
|
|
@ -1015,7 +1004,7 @@ 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++) {
|
||||
|
|
@ -1025,7 +1014,7 @@ Computer8080.prototype.stop = function(ms, nCycles)
|
|||
component.stop(ms, nCycles);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
|
|
@ -1037,7 +1026,7 @@ 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;
|
||||
|
||||
|
|
@ -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,16 +1108,16 @@ Computer8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* resetUserID()
|
||||
*/
|
||||
Computer8080.prototype.resetUserID = function()
|
||||
resetUserID()
|
||||
{
|
||||
web.setLocalStorageItem(Computer8080.STATE_USERID, "");
|
||||
Web.setLocalStorageItem(Computer8080.STATE_USERID, "");
|
||||
this.sUserID = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* queryUserID(fPrompt)
|
||||
|
|
@ -1136,11 +1125,11 @@ Computer8080.prototype.resetUserID = function()
|
|||
* @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,7 +1148,7 @@ Computer8080.prototype.queryUserID = function(fPrompt)
|
|||
}
|
||||
}
|
||||
return sUserID;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* verifyUserID(sUserID)
|
||||
|
|
@ -1168,20 +1157,20 @@ Computer8080.prototype.queryUserID = function(fPrompt)
|
|||
* @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,7 +1183,7 @@ Computer8080.prototype.verifyUserID = function(sUserID)
|
|||
if (fMessages) this.printMessage("invalid response (error " + nErrorCode + ")");
|
||||
}
|
||||
return this.sUserID;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getServerStatePath()
|
||||
|
|
@ -1202,21 +1191,21 @@ Computer8080.prototype.verifyUserID = function(sUserID)
|
|||
* @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)
|
||||
|
|
@ -1224,7 +1213,7 @@ Computer8080.prototype.getServerStatePath = function()
|
|||
* @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
|
||||
|
|
@ -1254,7 +1243,7 @@ Computer8080.prototype.saveServerState = function(sUserID, sState)
|
|||
this.printMessage("no state to store");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* storeServerState(sUserID, sState, fSync)
|
||||
|
|
@ -1265,7 +1254,7 @@ 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,7 +1286,7 @@ Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
|
|||
return JSON.parse(sResponse);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* onPower()
|
||||
|
|
@ -1306,7 +1295,7 @@ Computer8080.prototype.storeServerState = function(sUserID, sState, fSync)
|
|||
*
|
||||
* @this {Computer8080}
|
||||
*/
|
||||
Computer8080.prototype.onPower = function()
|
||||
onPower()
|
||||
{
|
||||
if (!this.nPowerChange) {
|
||||
if (!this.flags.powered) {
|
||||
|
|
@ -1315,7 +1304,7 @@ Computer8080.prototype.onPower = function()
|
|||
this.powerOff(false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* onReset()
|
||||
|
|
@ -1324,7 +1313,7 @@ Computer8080.prototype.onPower = function()
|
|||
*
|
||||
* @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";
|
||||
|
|
@ -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,7 +1360,7 @@ Computer8080.prototype.onReset = function()
|
|||
this.reset();
|
||||
if (this.cpu) this.cpu.autoStart();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getMachineComponent(sType, componentPrev)
|
||||
|
|
@ -1381,7 +1370,7 @@ 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);
|
||||
|
|
@ -1395,7 +1384,7 @@ Computer8080.prototype.getMachineComponent = function(sType, componentPrev)
|
|||
}
|
||||
if (!componentLast) Component.log("Machine component type '" + sType + "' not found", "warning");
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateFocus(fScroll)
|
||||
|
|
@ -1407,7 +1396,7 @@ 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) {
|
||||
/*
|
||||
|
|
@ -1430,7 +1419,7 @@ Computer8080.prototype.updateFocus = function(fScroll)
|
|||
window.scrollTo(x, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
|
|
@ -1452,7 +1441,7 @@ 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
|
||||
|
|
@ -1465,7 +1454,7 @@ Computer8080.prototype.updateStatus = function(fForce)
|
|||
*/
|
||||
if (this.cpu) this.cpu.updateStatus(fForce);
|
||||
if (this.panel) this.panel.updateStatus(fForce);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateVideo(fForced)
|
||||
|
|
@ -1475,12 +1464,12 @@ 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()
|
||||
|
|
@ -1490,7 +1479,7 @@ 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,7 +1523,7 @@ Computer8080.init = function()
|
|||
if (computer.fAutoPower) computer.wait(computer.powerOn);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computer8080.show()
|
||||
|
|
@ -1545,7 +1534,7 @@ 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++) {
|
||||
|
|
@ -1571,7 +1560,7 @@ Computer8080.show = function()
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computer8080.exit()
|
||||
|
|
@ -1599,7 +1588,7 @@ 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++) {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,13 +28,19 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
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)
|
||||
*
|
||||
|
|
@ -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)
|
||||
constructor(parmsCPU, nCyclesDefault)
|
||||
{
|
||||
Component.call(this, "CPU", parmsCPU, CPU8080, Messages8080.CPU);
|
||||
super("CPU", parmsCPU, CPU8080, Messages8080.CPU);
|
||||
|
||||
var nCycles = parmsCPU['cycles'] || nCyclesDefault;
|
||||
|
||||
|
|
@ -127,27 +132,6 @@ function CPU8080(parmsCPU, nCyclesDefault)
|
|||
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)
|
||||
*
|
||||
|
|
@ -157,7 +141,7 @@ 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;
|
||||
|
|
@ -182,16 +166,16 @@ CPU8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
}
|
||||
|
||||
this.setReady();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {CPU8080}
|
||||
*/
|
||||
CPU8080.prototype.reset = function()
|
||||
reset()
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
|
|
@ -201,10 +185,10 @@ CPU8080.prototype.reset = function()
|
|||
* @this {CPU8080}
|
||||
* @return {Object|null}
|
||||
*/
|
||||
CPU8080.prototype.save = function()
|
||||
save()
|
||||
{
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
|
|
@ -215,10 +199,10 @@ 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)
|
||||
|
|
@ -228,7 +212,7 @@ 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) {
|
||||
|
|
@ -264,7 +248,7 @@ CPU8080.prototype.powerUp = function(data, fRepower)
|
|||
*/
|
||||
this.updateCPU();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
|
|
@ -274,7 +258,7 @@ 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)
|
||||
|
|
@ -283,7 +267,7 @@ CPU8080.prototype.powerDown = function(fSave, fShutdown)
|
|||
* this.flags.powered = false;
|
||||
*/
|
||||
return fSave? this.save() : true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* autoStart()
|
||||
|
|
@ -291,7 +275,7 @@ CPU8080.prototype.powerDown = function(fSave, fShutdown)
|
|||
* @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,7 +290,7 @@ CPU8080.prototype.autoStart = function()
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* isPowered()
|
||||
|
|
@ -314,14 +298,14 @@ CPU8080.prototype.autoStart = function()
|
|||
* @this {CPU8080}
|
||||
* @return {boolean}
|
||||
*/
|
||||
CPU8080.prototype.isPowered = function()
|
||||
isPowered()
|
||||
{
|
||||
if (!this.flags.powered) {
|
||||
this.println(this.toString() + " not powered");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* isRunning()
|
||||
|
|
@ -329,10 +313,10 @@ CPU8080.prototype.isPowered = function()
|
|||
* @this {CPU8080}
|
||||
* @return {boolean}
|
||||
*/
|
||||
CPU8080.prototype.isRunning = function()
|
||||
isRunning()
|
||||
{
|
||||
return this.flags.running;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getChecksum()
|
||||
|
|
@ -342,10 +326,10 @@ 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()
|
||||
|
|
@ -357,7 +341,7 @@ 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;
|
||||
|
|
@ -373,7 +357,7 @@ CPU8080.prototype.resetChecksum = function()
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateChecksum(nCycles)
|
||||
|
|
@ -386,7 +370,7 @@ CPU8080.prototype.resetChecksum = function()
|
|||
* @this {CPU8080}
|
||||
* @param {number} nCycles
|
||||
*/
|
||||
CPU8080.prototype.updateChecksum = function(nCycles)
|
||||
updateChecksum(nCycles)
|
||||
{
|
||||
if (this.flags.checksum) {
|
||||
/*
|
||||
|
|
@ -409,7 +393,7 @@ CPU8080.prototype.updateChecksum = function(nCycles)
|
|||
}
|
||||
if (fDisplay) this.displayChecksum();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* displayChecksum()
|
||||
|
|
@ -420,10 +404,10 @@ CPU8080.prototype.updateChecksum = function(nCycles)
|
|||
*
|
||||
* @this {CPU8080}
|
||||
*/
|
||||
CPU8080.prototype.displayChecksum = function()
|
||||
displayChecksum()
|
||||
{
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.nChecksum));
|
||||
};
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + Str.toHex(this.nChecksum));
|
||||
}
|
||||
|
||||
/**
|
||||
* displayValue(sLabel, nValue, cch)
|
||||
|
|
@ -436,7 +420,7 @@ 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) {
|
||||
|
|
@ -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,7 +441,7 @@ CPU8080.prototype.displayValue = function(sLabel, nValue, cch)
|
|||
*/
|
||||
if (this.bindings[sLabel].textContent != sVal) this.bindings[sLabel].textContent = sVal;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
|
|
@ -469,7 +453,7 @@ 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,7 +505,7 @@ CPU8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
break;
|
||||
}
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBurstCycles(nCycles)
|
||||
|
|
@ -537,7 +521,7 @@ 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,7 +539,7 @@ CPU8080.prototype.setBurstCycles = function(nCycles)
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addCycles(nCycles, fEndStep)
|
||||
|
|
@ -564,13 +548,13 @@ CPU8080.prototype.setBurstCycles = function(nCycles)
|
|||
* @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)
|
||||
|
|
@ -587,7 +571,7 @@ 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,7 +593,7 @@ CPU8080.prototype.calcCycles = function(fRecalc)
|
|||
this.nCyclesNextYield = this.nCyclesPerYield;
|
||||
}
|
||||
this.nCyclesRecalc = 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getCycles(fScaled)
|
||||
|
|
@ -628,7 +612,7 @@ 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,7 +637,7 @@ CPU8080.prototype.getCycles = function(fScaled)
|
|||
nCycles = Math.round(nCycles / this.nCyclesMultiplier);
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getCyclesPerSecond()
|
||||
|
|
@ -663,10 +647,10 @@ CPU8080.prototype.getCycles = function(fScaled)
|
|||
* @this {CPU8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080.prototype.getCyclesPerSecond = function()
|
||||
getCyclesPerSecond()
|
||||
{
|
||||
return this.nCyclesPerSecond;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* resetCycles()
|
||||
|
|
@ -677,14 +661,14 @@ 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()
|
||||
|
|
@ -692,10 +676,10 @@ CPU8080.prototype.resetCycles = function()
|
|||
* @this {CPU8080}
|
||||
* @return {number} the current speed multiplier
|
||||
*/
|
||||
CPU8080.prototype.getSpeed = function()
|
||||
getSpeed()
|
||||
{
|
||||
return this.nCyclesMultiplier;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getSpeedCurrent()
|
||||
|
|
@ -703,13 +687,13 @@ CPU8080.prototype.getSpeed = function()
|
|||
* @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()
|
||||
|
|
@ -717,13 +701,13 @@ CPU8080.prototype.getSpeedCurrent = function()
|
|||
* @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)
|
||||
|
|
@ -739,7 +723,7 @@ 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,11 +748,11 @@ 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)
|
||||
|
|
@ -777,7 +761,7 @@ CPU8080.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
|
|||
* @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;
|
||||
|
|
@ -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,7 +826,7 @@ CPU8080.prototype.calcStartTime = function()
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* calcRemainingTime()
|
||||
|
|
@ -850,9 +834,9 @@ CPU8080.prototype.calcStartTime = function()
|
|||
* @this {CPU8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPU8080.prototype.calcRemainingTime = function()
|
||||
calcRemainingTime()
|
||||
{
|
||||
this.msEndThisRun = usr.getTime();
|
||||
this.msEndThisRun = Usr.getTime();
|
||||
|
||||
var msYield = this.msPerYield;
|
||||
if (this.nCyclesThisRun) {
|
||||
|
|
@ -921,7 +905,7 @@ CPU8080.prototype.calcRemainingTime = function()
|
|||
|
||||
this.msEndThisRun += msRemainsThisRun;
|
||||
return msRemainsThisRun;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addTimer(callBack)
|
||||
|
|
@ -940,12 +924,12 @@ 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)
|
||||
|
|
@ -968,7 +952,7 @@ 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) {
|
||||
|
|
@ -984,7 +968,7 @@ CPU8080.prototype.setTimer = function(iTimer, ms, fReset)
|
|||
}
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getMSCycles(ms)
|
||||
|
|
@ -993,10 +977,10 @@ CPU8080.prototype.setTimer = function(iTimer, ms, fReset)
|
|||
* @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)
|
||||
|
|
@ -1007,7 +991,7 @@ 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];
|
||||
|
|
@ -1017,7 +1001,7 @@ CPU8080.prototype.getBurstCycles = function(nCycles)
|
|||
}
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateTimers(nCycles)
|
||||
|
|
@ -1029,7 +1013,7 @@ 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];
|
||||
|
|
@ -1040,7 +1024,7 @@ CPU8080.prototype.updateTimers = function(nCycles)
|
|||
timer[1](); // safe to invoke the callback function now
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* endBurst(fReset)
|
||||
|
|
@ -1049,13 +1033,13 @@ CPU8080.prototype.updateTimers = function(nCycles)
|
|||
* @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)
|
||||
|
|
@ -1063,11 +1047,11 @@ CPU8080.prototype.endBurst = function(fReset)
|
|||
* @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,14 +1108,14 @@ 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)
|
||||
|
|
@ -1140,7 +1124,7 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
|
|||
*
|
||||
* @param {boolean} [fUpdateFocus]
|
||||
*/
|
||||
CPU8080.prototype.startCPU = function(fUpdateFocus)
|
||||
startCPU(fUpdateFocus)
|
||||
{
|
||||
if (!this.flags.running) {
|
||||
/*
|
||||
|
|
@ -1161,7 +1145,7 @@ CPU8080.prototype.startCPU = function(fUpdateFocus)
|
|||
if (fUpdateFocus) this.cmp.updateFocus(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* stepCPU(nMinCycles)
|
||||
|
|
@ -1172,10 +1156,10 @@ 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)
|
||||
|
|
@ -1188,7 +1172,7 @@ CPU8080.prototype.stepCPU = function(nMinCycles)
|
|||
* @this {CPU8080}
|
||||
* @param {boolean} [fComplete]
|
||||
*/
|
||||
CPU8080.prototype.stopCPU = function(fComplete)
|
||||
stopCPU(fComplete)
|
||||
{
|
||||
this.isBusy(true);
|
||||
this.endBurst();
|
||||
|
|
@ -1201,7 +1185,7 @@ CPU8080.prototype.stopCPU = function(fComplete)
|
|||
if (controlRun) controlRun.textContent = "Run";
|
||||
}
|
||||
this.flags.complete = fComplete;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateCPU(fForce)
|
||||
|
|
@ -1214,13 +1198,13 @@ 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()
|
||||
|
|
@ -1230,7 +1214,7 @@ 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()
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,11 +28,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var CPUDef8080 = require("./CPUDef");
|
||||
var Messages8080 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* op=0x00 (NOP)
|
||||
|
|
|
|||
|
|
@ -28,18 +28,23 @@
|
|||
|
||||
"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 Web = require("../../shared/es6/weblib");
|
||||
var Component = require("../../shared/es6/component");
|
||||
var State = require("../../shared/es6/state");
|
||||
var PC8080 = require("./defines");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
var CPU8080 = require("./cpu");
|
||||
var Messages8080= require("./messages");
|
||||
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 CPUState8080 extends CPU8080 {
|
||||
/**
|
||||
* CPUState8080(parmsCPU)
|
||||
*
|
||||
|
|
@ -54,23 +59,24 @@ if (NODE) {
|
|||
* The CPUState8080 class was initially written to simulate a 8080 microprocessor, although over time
|
||||
* it may evolved to support other microprocessors (eg, the Zilog Z80).
|
||||
*
|
||||
* @constructor
|
||||
* @extends CPU8080
|
||||
* @this {CPUState8080}
|
||||
* @param {Object} parmsCPU
|
||||
*/
|
||||
function CPUState8080(parmsCPU)
|
||||
constructor(parmsCPU)
|
||||
{
|
||||
this.model = +parmsCPU['model'] || CPUDef8080.MODEL_8080;
|
||||
|
||||
var nCyclesDefault = 0;
|
||||
switch(this.model) {
|
||||
var model = +parmsCPU['model'] || CPUDef8080.MODEL_8080;
|
||||
|
||||
switch(model) {
|
||||
case CPUDef8080.MODEL_8080:
|
||||
default:
|
||||
nCyclesDefault = 1000000;
|
||||
break;
|
||||
}
|
||||
|
||||
CPU8080.call(this, parmsCPU, nCyclesDefault);
|
||||
super(parmsCPU, nCyclesDefault);
|
||||
|
||||
this.model = model;
|
||||
|
||||
/*
|
||||
* Initialize processor operation to match the requested model
|
||||
|
|
@ -102,8 +108,6 @@ function CPUState8080(parmsCPU)
|
|||
this.resetRegs();
|
||||
}
|
||||
|
||||
Component.subclass(CPUState8080, CPU8080);
|
||||
|
||||
/**
|
||||
* addHaltCheck(fn)
|
||||
*
|
||||
|
|
@ -112,10 +116,10 @@ Component.subclass(CPUState8080, CPU8080);
|
|||
* @this {CPUState8080}
|
||||
* @param {function(number)} fn
|
||||
*/
|
||||
CPUState8080.prototype.addHaltCheck = function(fn)
|
||||
addHaltCheck(fn)
|
||||
{
|
||||
this.afnHalt.push(fn);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initProcessor()
|
||||
|
|
@ -128,31 +132,31 @@ CPUState8080.prototype.addHaltCheck = function(fn)
|
|||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.initProcessor = function()
|
||||
initProcessor()
|
||||
{
|
||||
this.aOps = CPUDef8080.aOps8080;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.reset = function()
|
||||
reset()
|
||||
{
|
||||
if (this.flags.running) this.stopCPU();
|
||||
this.resetRegs();
|
||||
this.resetCycles();
|
||||
this.clearError(); // clear any fatal error/exception that setError() may have flagged
|
||||
this.parent.reset.call(this);
|
||||
};
|
||||
super.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* resetRegs()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.resetRegs = function()
|
||||
resetRegs()
|
||||
{
|
||||
this.regA = 0;
|
||||
this.regB = 0;
|
||||
|
|
@ -175,7 +179,7 @@ CPUState8080.prototype.resetRegs = function()
|
|||
* that requires us to wait for a hardware interrupt (INTFLAG.INTR) before continuing execution.
|
||||
*/
|
||||
this.intFlags = CPUDef8080.INTFLAG.NONE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setReset(addr)
|
||||
|
|
@ -183,11 +187,11 @@ CPUState8080.prototype.resetRegs = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUState8080.prototype.setReset = function(addr)
|
||||
setReset(addr)
|
||||
{
|
||||
this.addrReset = addr;
|
||||
this.setPC(addr);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getChecksum()
|
||||
|
|
@ -195,12 +199,12 @@ CPUState8080.prototype.setReset = function(addr)
|
|||
* @this {CPUState8080}
|
||||
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
|
||||
*/
|
||||
CPUState8080.prototype.getChecksum = function()
|
||||
getChecksum()
|
||||
{
|
||||
var sum = (this.regA + this.regB + this.regC + this.regD + this.regE + this.regH + this.regL)|0;
|
||||
sum = (sum + this.getSP() + this.getPC() + this.getPS())|0;
|
||||
return sum;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* save()
|
||||
|
|
@ -210,14 +214,14 @@ CPUState8080.prototype.getChecksum = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {Object|null}
|
||||
*/
|
||||
CPUState8080.prototype.save = function()
|
||||
save()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, [this.regA, this.regB, this.regC, this.regD, this.regE, this.regH, this.regL, this.getSP(), this.getPC(), this.getPS()]);
|
||||
state.set(1, [this.intFlags, this.nTotalCycles, this.getSpeed()]);
|
||||
state.set(2, this.bus.saveMemory());
|
||||
return state.data();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
|
|
@ -228,7 +232,7 @@ CPUState8080.prototype.save = function()
|
|||
* @param {Object} data
|
||||
* @return {boolean} true if restore successful, false if not
|
||||
*/
|
||||
CPUState8080.prototype.restore = function(data)
|
||||
restore(data)
|
||||
{
|
||||
var a = data[0];
|
||||
this.regA = a[0];
|
||||
|
|
@ -246,7 +250,7 @@ CPUState8080.prototype.restore = function(data)
|
|||
this.nTotalCycles = a[1];
|
||||
this.setSpeed(a[3]);
|
||||
return this.bus.restoreMemory(data[2]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
|
|
@ -258,7 +262,7 @@ CPUState8080.prototype.restore = function(data)
|
|||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
CPUState8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
setBinding(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
var fBound = false;
|
||||
switch (sBinding) {
|
||||
|
|
@ -286,11 +290,11 @@ CPUState8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
fBound = true;
|
||||
break;
|
||||
default:
|
||||
fBound = this.parent.setBinding.call(this, sHTMLType, sBinding, control);
|
||||
fBound = super.setBinding(sHTMLType, sBinding, control);
|
||||
break;
|
||||
}
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getBC()
|
||||
|
|
@ -298,10 +302,10 @@ CPUState8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getBC = function()
|
||||
getBC()
|
||||
{
|
||||
return (this.regB << 8) | this.regC;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBC(w)
|
||||
|
|
@ -309,11 +313,11 @@ CPUState8080.prototype.getBC = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPUState8080.prototype.setBC = function(w)
|
||||
setBC(w)
|
||||
{
|
||||
this.regB = (w >> 8) & 0xff;
|
||||
this.regC = w & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getDE()
|
||||
|
|
@ -321,10 +325,10 @@ CPUState8080.prototype.setBC = function(w)
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getDE = function()
|
||||
getDE()
|
||||
{
|
||||
return (this.regD << 8) | this.regE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setDE(w)
|
||||
|
|
@ -332,11 +336,11 @@ CPUState8080.prototype.getDE = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPUState8080.prototype.setDE = function(w)
|
||||
setDE(w)
|
||||
{
|
||||
this.regD = (w >> 8) & 0xff;
|
||||
this.regE = w & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getHL()
|
||||
|
|
@ -344,10 +348,10 @@ CPUState8080.prototype.setDE = function(w)
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getHL = function()
|
||||
getHL()
|
||||
{
|
||||
return (this.regH << 8) | this.regL;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setHL(w)
|
||||
|
|
@ -355,11 +359,11 @@ CPUState8080.prototype.getHL = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPUState8080.prototype.setHL = function(w)
|
||||
setHL(w)
|
||||
{
|
||||
this.regH = (w >> 8) & 0xff;
|
||||
this.regL = w & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getSP()
|
||||
|
|
@ -367,10 +371,10 @@ CPUState8080.prototype.setHL = function(w)
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getSP = function()
|
||||
getSP()
|
||||
{
|
||||
return this.regSP;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setSP(off)
|
||||
|
|
@ -378,10 +382,10 @@ CPUState8080.prototype.getSP = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} off
|
||||
*/
|
||||
CPUState8080.prototype.setSP = function(off)
|
||||
setSP(off)
|
||||
{
|
||||
this.regSP = off & 0xffff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPC()
|
||||
|
|
@ -389,10 +393,10 @@ CPUState8080.prototype.setSP = function(off)
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getPC = function()
|
||||
getPC()
|
||||
{
|
||||
return this.regPC;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* offPC()
|
||||
|
|
@ -401,10 +405,10 @@ CPUState8080.prototype.getPC = function()
|
|||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.offPC = function(off)
|
||||
offPC(off)
|
||||
{
|
||||
return (this.regPC + off) & 0xffff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setPC(off)
|
||||
|
|
@ -412,20 +416,20 @@ CPUState8080.prototype.offPC = function(off)
|
|||
* @this {CPUState8080}
|
||||
* @param {number} off
|
||||
*/
|
||||
CPUState8080.prototype.setPC = function(off)
|
||||
setPC(off)
|
||||
{
|
||||
this.regPC = off & 0xffff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearCF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearCF = function()
|
||||
clearCF()
|
||||
{
|
||||
this.resultZeroCarry &= 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getCF()
|
||||
|
|
@ -433,20 +437,20 @@ CPUState8080.prototype.clearCF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or 1 (CPUDef8080.PS.CF)
|
||||
*/
|
||||
CPUState8080.prototype.getCF = function()
|
||||
getCF()
|
||||
{
|
||||
return (this.resultZeroCarry & 0x100)? CPUDef8080.PS.CF : 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setCF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setCF = function()
|
||||
setCF()
|
||||
{
|
||||
this.resultZeroCarry |= 0x100;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateCF(CF)
|
||||
|
|
@ -454,20 +458,20 @@ CPUState8080.prototype.setCF = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} CF (0x000 or 0x100)
|
||||
*/
|
||||
CPUState8080.prototype.updateCF = function(CF)
|
||||
updateCF(CF)
|
||||
{
|
||||
this.resultZeroCarry = (this.resultZeroCarry & 0xff) | CF;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearPF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearPF = function()
|
||||
clearPF()
|
||||
{
|
||||
if (this.getPF()) this.resultParitySign ^= 0x1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPF()
|
||||
|
|
@ -475,30 +479,30 @@ CPUState8080.prototype.clearPF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.PF
|
||||
*/
|
||||
CPUState8080.prototype.getPF = function()
|
||||
getPF()
|
||||
{
|
||||
return (CPUDef8080.PARITY[this.resultParitySign & 0xff])? CPUDef8080.PS.PF : 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setPF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setPF = function()
|
||||
setPF()
|
||||
{
|
||||
if (!this.getPF()) this.resultParitySign ^= 0x1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearAF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearAF = function()
|
||||
clearAF()
|
||||
{
|
||||
this.resultAuxOverflow = (this.resultParitySign & 0x10) | (this.resultAuxOverflow & ~0x10);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getAF()
|
||||
|
|
@ -506,30 +510,30 @@ CPUState8080.prototype.clearAF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.AF
|
||||
*/
|
||||
CPUState8080.prototype.getAF = function()
|
||||
getAF()
|
||||
{
|
||||
return ((this.resultParitySign ^ this.resultAuxOverflow) & 0x10)? CPUDef8080.PS.AF : 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setAF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setAF = function()
|
||||
setAF()
|
||||
{
|
||||
this.resultAuxOverflow = (~this.resultParitySign & 0x10) | (this.resultAuxOverflow & ~0x10);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearZF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearZF = function()
|
||||
clearZF()
|
||||
{
|
||||
this.resultZeroCarry |= 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getZF()
|
||||
|
|
@ -537,30 +541,30 @@ CPUState8080.prototype.clearZF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.ZF
|
||||
*/
|
||||
CPUState8080.prototype.getZF = function()
|
||||
getZF()
|
||||
{
|
||||
return (this.resultZeroCarry & 0xff)? 0 : CPUDef8080.PS.ZF;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setZF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setZF = function()
|
||||
setZF()
|
||||
{
|
||||
this.resultZeroCarry &= ~0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearSF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearSF = function()
|
||||
clearSF()
|
||||
{
|
||||
if (this.getSF()) this.resultParitySign ^= 0xc0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getSF()
|
||||
|
|
@ -568,30 +572,30 @@ CPUState8080.prototype.clearSF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.SF
|
||||
*/
|
||||
CPUState8080.prototype.getSF = function()
|
||||
getSF()
|
||||
{
|
||||
return (this.resultParitySign & 0x80)? CPUDef8080.PS.SF : 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setSF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setSF = function()
|
||||
setSF()
|
||||
{
|
||||
if (!this.getSF()) this.resultParitySign ^= 0xc0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearIF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.clearIF = function()
|
||||
clearIF()
|
||||
{
|
||||
this.regPS &= ~CPUDef8080.PS.IF;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getIF()
|
||||
|
|
@ -599,20 +603,20 @@ CPUState8080.prototype.clearIF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} 0 or CPUDef8080.PS.IF
|
||||
*/
|
||||
CPUState8080.prototype.getIF = function()
|
||||
getIF()
|
||||
{
|
||||
return (this.regPS & CPUDef8080.PS.IF);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setIF()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.setIF = function()
|
||||
setIF()
|
||||
{
|
||||
this.regPS |= CPUDef8080.PS.IF;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPS()
|
||||
|
|
@ -620,10 +624,10 @@ CPUState8080.prototype.setIF = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getPS = function()
|
||||
getPS()
|
||||
{
|
||||
return (this.regPS & ~CPUDef8080.PS.RESULT) | (this.getSF() | this.getZF() | this.getAF() | this.getPF() | this.getCF());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setPS(regPS)
|
||||
|
|
@ -631,7 +635,7 @@ CPUState8080.prototype.getPS = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} regPS
|
||||
*/
|
||||
CPUState8080.prototype.setPS = function(regPS)
|
||||
setPS(regPS)
|
||||
{
|
||||
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
|
||||
if (regPS & CPUDef8080.PS.CF) this.resultZeroCarry |= 0x100;
|
||||
|
|
@ -641,7 +645,7 @@ CPUState8080.prototype.setPS = function(regPS)
|
|||
if (regPS & CPUDef8080.PS.SF) this.resultParitySign ^= 0xc0;
|
||||
this.regPS = (this.regPS & ~(CPUDef8080.PS.RESULT | CPUDef8080.PS.INTERNAL)) | (regPS & CPUDef8080.PS.INTERNAL) | CPUDef8080.PS.SET;
|
||||
Component.assert((regPS & CPUDef8080.PS.RESULT) == (this.getPS() & CPUDef8080.PS.RESULT));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPSW()
|
||||
|
|
@ -649,10 +653,10 @@ CPUState8080.prototype.setPS = function(regPS)
|
|||
* @this {CPUState8080}
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.getPSW = function()
|
||||
getPSW()
|
||||
{
|
||||
return (this.getPS() & CPUDef8080.PS.MASK) | (this.regA << 8);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setPSW(w)
|
||||
|
|
@ -660,11 +664,11 @@ CPUState8080.prototype.getPSW = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} w
|
||||
*/
|
||||
CPUState8080.prototype.setPSW = function(w)
|
||||
setPSW(w)
|
||||
{
|
||||
this.setPS((w & CPUDef8080.PS.MASK) | (this.regPS & ~CPUDef8080.PS.MASK));
|
||||
this.regA = w >> 8;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addByte(src)
|
||||
|
|
@ -673,11 +677,11 @@ CPUState8080.prototype.setPSW = function(w)
|
|||
* @param {number} src
|
||||
* @return {number} regA + src
|
||||
*/
|
||||
CPUState8080.prototype.addByte = function(src)
|
||||
addByte(src)
|
||||
{
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = this.regA + src) & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addByteCarry(src)
|
||||
|
|
@ -686,11 +690,11 @@ CPUState8080.prototype.addByte = function(src)
|
|||
* @param {number} src
|
||||
* @return {number} regA + src + carry
|
||||
*/
|
||||
CPUState8080.prototype.addByteCarry = function(src)
|
||||
addByteCarry(src)
|
||||
{
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = this.regA + src + ((this.resultZeroCarry & 0x100)? 1 : 0)) & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* andByte(src)
|
||||
|
|
@ -702,12 +706,12 @@ CPUState8080.prototype.addByteCarry = function(src)
|
|||
* @param {number} src
|
||||
* @return {number} regA & src
|
||||
*/
|
||||
CPUState8080.prototype.andByte = function(src)
|
||||
andByte(src)
|
||||
{
|
||||
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regA & src;
|
||||
if ((this.regA | src) & 0x8) this.resultAuxOverflow ^= 0x10; // set AF by inverting bit 4 in resultAuxOverflow
|
||||
return this.resultZeroCarry;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* decByte(b)
|
||||
|
|
@ -719,13 +723,13 @@ CPUState8080.prototype.andByte = function(src)
|
|||
* @param {number} b
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.decByte = function(b)
|
||||
decByte(b)
|
||||
{
|
||||
this.resultAuxOverflow = b ^ 0xff;
|
||||
b = this.resultParitySign = (b + 0xff) & 0xff;
|
||||
this.resultZeroCarry = (this.resultZeroCarry & ~0xff) | b;
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* incByte(b)
|
||||
|
|
@ -734,13 +738,13 @@ CPUState8080.prototype.decByte = function(b)
|
|||
* @param {number} b
|
||||
* @return {number}
|
||||
*/
|
||||
CPUState8080.prototype.incByte = function(b)
|
||||
incByte(b)
|
||||
{
|
||||
this.resultAuxOverflow = b;
|
||||
b = this.resultParitySign = (b + 1) & 0xff;
|
||||
this.resultZeroCarry = (this.resultZeroCarry & ~0xff) | b;
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* orByte(src)
|
||||
|
|
@ -749,10 +753,10 @@ CPUState8080.prototype.incByte = function(b)
|
|||
* @param {number} src
|
||||
* @return {number} regA | src
|
||||
*/
|
||||
CPUState8080.prototype.orByte = function(src)
|
||||
orByte(src)
|
||||
{
|
||||
return this.resultParitySign = this.resultZeroCarry = this.resultAuxOverflow = this.regA | src;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* subByte(src)
|
||||
|
|
@ -788,12 +792,12 @@ CPUState8080.prototype.orByte = function(src)
|
|||
* @param {number} src
|
||||
* @return {number} regA - src
|
||||
*/
|
||||
CPUState8080.prototype.subByte = function(src)
|
||||
subByte(src)
|
||||
{
|
||||
src ^= 0xff;
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = (this.regA + src + 1) ^ 0x100) & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* subByteBorrow(src)
|
||||
|
|
@ -809,12 +813,12 @@ CPUState8080.prototype.subByte = function(src)
|
|||
* @param {number} src
|
||||
* @return {number} regA - src - carry
|
||||
*/
|
||||
CPUState8080.prototype.subByteBorrow = function(src)
|
||||
subByteBorrow(src)
|
||||
{
|
||||
src ^= 0xff;
|
||||
this.resultAuxOverflow = this.regA ^ src;
|
||||
return this.resultParitySign = (this.resultZeroCarry = (this.regA + src + ((this.resultZeroCarry & 0x100)? 0 : 1)) ^ 0x100) & 0xff;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* xorByte(src)
|
||||
|
|
@ -823,10 +827,10 @@ CPUState8080.prototype.subByteBorrow = function(src)
|
|||
* @param {number} src
|
||||
* @return {number} regA ^ src
|
||||
*/
|
||||
CPUState8080.prototype.xorByte = function(src)
|
||||
xorByte(src)
|
||||
{
|
||||
return this.resultParitySign = this.resultZeroCarry = this.resultAuxOverflow = this.regA ^ src;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getByte(addr)
|
||||
|
|
@ -835,10 +839,10 @@ CPUState8080.prototype.xorByte = function(src)
|
|||
* @param {number} addr is a linear address
|
||||
* @return {number} byte (8-bit) value at that address
|
||||
*/
|
||||
CPUState8080.prototype.getByte = function(addr)
|
||||
getByte(addr)
|
||||
{
|
||||
return this.bus.getByte(addr);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getWord(addr)
|
||||
|
|
@ -847,10 +851,10 @@ CPUState8080.prototype.getByte = function(addr)
|
|||
* @param {number} addr is a linear address
|
||||
* @return {number} word (16-bit) value at that address
|
||||
*/
|
||||
CPUState8080.prototype.getWord = function(addr)
|
||||
getWord(addr)
|
||||
{
|
||||
return this.bus.getShort(addr);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setByte(addr, b)
|
||||
|
|
@ -859,10 +863,10 @@ CPUState8080.prototype.getWord = function(addr)
|
|||
* @param {number} addr is a linear address
|
||||
* @param {number} b is the byte (8-bit) value to write (which we truncate to 8 bits; required by opSTOSb)
|
||||
*/
|
||||
CPUState8080.prototype.setByte = function(addr, b)
|
||||
setByte(addr, b)
|
||||
{
|
||||
this.bus.setByte(addr, b);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setWord(addr, w)
|
||||
|
|
@ -871,10 +875,10 @@ CPUState8080.prototype.setByte = function(addr, b)
|
|||
* @param {number} addr is a linear address
|
||||
* @param {number} w is the word (16-bit) value to write (which we truncate to 16 bits to be safe)
|
||||
*/
|
||||
CPUState8080.prototype.setWord = function(addr, w)
|
||||
setWord(addr, w)
|
||||
{
|
||||
this.bus.setShort(addr, w);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPCByte()
|
||||
|
|
@ -882,12 +886,12 @@ CPUState8080.prototype.setWord = function(addr, w)
|
|||
* @this {CPUState8080}
|
||||
* @return {number} byte at the current PC; PC advanced by 1
|
||||
*/
|
||||
CPUState8080.prototype.getPCByte = function()
|
||||
getPCByte()
|
||||
{
|
||||
var b = this.getByte(this.regPC);
|
||||
this.setPC(this.regPC + 1);
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getPCWord()
|
||||
|
|
@ -895,12 +899,12 @@ CPUState8080.prototype.getPCByte = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} word at the current PC; PC advanced by 2
|
||||
*/
|
||||
CPUState8080.prototype.getPCWord = function()
|
||||
getPCWord()
|
||||
{
|
||||
var w = this.getWord(this.regPC);
|
||||
this.setPC(this.regPC + 2);
|
||||
return w;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* popWord()
|
||||
|
|
@ -908,12 +912,12 @@ CPUState8080.prototype.getPCWord = function()
|
|||
* @this {CPUState8080}
|
||||
* @return {number} word popped from the current SP; SP increased by 2
|
||||
*/
|
||||
CPUState8080.prototype.popWord = function()
|
||||
popWord()
|
||||
{
|
||||
var w = this.getWord(this.regSP);
|
||||
this.setSP(this.regSP + 2);
|
||||
return w;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* pushWord(w)
|
||||
|
|
@ -921,11 +925,11 @@ CPUState8080.prototype.popWord = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} w is the word (16-bit) value to push at current SP; SP decreased by 2
|
||||
*/
|
||||
CPUState8080.prototype.pushWord = function(w)
|
||||
pushWord(w)
|
||||
{
|
||||
this.setSP(this.regSP - 2);
|
||||
this.setWord(this.regSP, w);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* checkINTR()
|
||||
|
|
@ -933,7 +937,7 @@ CPUState8080.prototype.pushWord = function(w)
|
|||
* @this {CPUState8080}
|
||||
* @return {boolean} true if execution may proceed, false if not
|
||||
*/
|
||||
CPUState8080.prototype.checkINTR = function()
|
||||
checkINTR()
|
||||
{
|
||||
/*
|
||||
* If the Debugger is single-stepping, this.nStepCycles will always be zero, which we take
|
||||
|
|
@ -962,7 +966,7 @@ CPUState8080.prototype.checkINTR = function()
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clearINTR(nLevel)
|
||||
|
|
@ -975,23 +979,22 @@ CPUState8080.prototype.checkINTR = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} nLevel (0-7, or -1 for all)
|
||||
*/
|
||||
CPUState8080.prototype.clearINTR = function(nLevel)
|
||||
clearINTR(nLevel)
|
||||
{
|
||||
var bitsClear = nLevel < 0? 0xff : (1 << nLevel);
|
||||
this.intFlags &= ~bitsClear;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* requestHALT()
|
||||
*
|
||||
* @this {CPUState8080}
|
||||
*/
|
||||
CPUState8080.prototype.requestHALT = function()
|
||||
requestHALT()
|
||||
{
|
||||
this.intFlags |= CPUDef8080.INTFLAG.HALT;
|
||||
this.endBurst();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* requestINTR(nLevel)
|
||||
|
|
@ -1006,13 +1009,13 @@ CPUState8080.prototype.requestHALT = function()
|
|||
* @this {CPUState8080}
|
||||
* @param {number} nLevel (0-7)
|
||||
*/
|
||||
CPUState8080.prototype.requestINTR = function(nLevel)
|
||||
requestINTR(nLevel)
|
||||
{
|
||||
this.intFlags |= (1 << nLevel);
|
||||
if (this.getIF()) {
|
||||
this.endBurst();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateReg(sReg, nValue, cch)
|
||||
|
|
@ -1026,10 +1029,10 @@ CPUState8080.prototype.requestINTR = function(nLevel)
|
|||
* @param {number} nValue
|
||||
* @param {number} [cch] (default is 2 hex digits)
|
||||
*/
|
||||
CPUState8080.prototype.updateReg = function(sReg, nValue, cch)
|
||||
updateReg(sReg, nValue, cch)
|
||||
{
|
||||
this.displayValue(sReg, nValue, cch || 2);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
|
|
@ -1043,7 +1046,7 @@ CPUState8080.prototype.updateReg = function(sReg, nValue, cch)
|
|||
* @this {CPUState8080}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
CPUState8080.prototype.updateStatus = function(fForce)
|
||||
updateStatus(fForce)
|
||||
{
|
||||
if (this.cLiveRegs) {
|
||||
if (fForce || !this.flags.running || this.flags.displayLiveRegs) {
|
||||
|
|
@ -1071,7 +1074,7 @@ CPUState8080.prototype.updateStatus = function(fForce)
|
|||
}
|
||||
var controlSpeed = this.bindings["speed"];
|
||||
if (controlSpeed) controlSpeed.textContent = this.getSpeedCurrent();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* stepCPU(nMinCycles)
|
||||
|
|
@ -1088,7 +1091,7 @@ CPUState8080.prototype.updateStatus = function(fForce)
|
|||
* was hit), -1 indicates a post-execution condition (eg, a read or write breakpoint was hit), and a positive
|
||||
* number indicates successful completion of that many cycles (which should always be >= nMinCycles).
|
||||
*/
|
||||
CPUState8080.prototype.stepCPU = function(nMinCycles)
|
||||
stepCPU(nMinCycles)
|
||||
{
|
||||
/*
|
||||
* The Debugger uses fComplete to determine if the instruction completed (true) or was interrupted
|
||||
|
|
@ -1143,7 +1146,7 @@ CPUState8080.prototype.stepCPU = function(nMinCycles)
|
|||
}
|
||||
|
||||
return (this.flags.complete? this.nBurstCycles - this.nStepCycles : (this.flags.complete === undefined? 0 : -1));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* CPUState8080.init()
|
||||
|
|
@ -1154,7 +1157,7 @@ CPUState8080.prototype.stepCPU = function(nMinCycles)
|
|||
* to create a CPUState8080 component, and then binding any associated HTML controls to the
|
||||
* new component.
|
||||
*/
|
||||
CPUState8080.init = function()
|
||||
static init()
|
||||
{
|
||||
var aeCPUs = Component.getElementsByClass(document, PC8080.APPCLASS, "cpu");
|
||||
for (var iCPU = 0; iCPU < aeCPUs.length; iCPU++) {
|
||||
|
|
@ -1163,11 +1166,12 @@ CPUState8080.init = function()
|
|||
var cpu = new CPUState8080(parmsCPU);
|
||||
Component.bindComponentControls(cpu, eCPU, PC8080.APPCLASS);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize every CPU module on the page
|
||||
*/
|
||||
web.onInit(CPUState8080.init);
|
||||
Web.onInit(CPUState8080.init);
|
||||
|
||||
if (NODE) module.exports = CPUState8080;
|
||||
module.exports = CPUState8080;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
|
@ -91,4 +91,4 @@ Messages8080.CATEGORIES = {
|
|||
"halt": Messages8080.HALT
|
||||
};
|
||||
|
||||
if (NODE) module.exports = Messages8080;
|
||||
module.exports = Messages8080;
|
||||
|
|
|
|||
|
|
@ -28,33 +28,34 @@
|
|||
|
||||
"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 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)
|
||||
constructor(parmsPanel)
|
||||
{
|
||||
Component.call(this, "Panel", parmsPanel, Panel8080);
|
||||
super("Panel", parmsPanel, Panel8080);
|
||||
}
|
||||
|
||||
Component.subclass(Panel8080);
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
|
|
@ -69,14 +70,14 @@ 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)
|
||||
|
|
@ -87,14 +88,14 @@ 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)
|
||||
|
|
@ -104,11 +105,11 @@ 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)
|
||||
|
|
@ -118,10 +119,10 @@ 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)
|
||||
|
|
@ -136,9 +137,9 @@ 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()
|
||||
|
|
@ -157,7 +158,7 @@ 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");
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,17 +28,23 @@
|
|||
|
||||
"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 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)
|
||||
*
|
||||
|
|
@ -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)
|
||||
constructor(parmsRAM)
|
||||
{
|
||||
Component.call(this, "RAM", parmsRAM, RAM8080);
|
||||
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,44 +88,17 @@ 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)
|
||||
*
|
||||
|
|
@ -130,13 +108,13 @@ 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)
|
||||
|
|
@ -146,7 +124,7 @@ 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,
|
||||
|
|
@ -155,7 +133,7 @@ RAM8080.prototype.powerUp = function(data, fRepower)
|
|||
* logic.
|
||||
*/
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
|
|
@ -165,7 +143,7 @@ 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,
|
||||
|
|
@ -174,7 +152,7 @@ RAM8080.prototype.powerDown = function(fSave, fShutdown)
|
|||
* save logic.
|
||||
*/
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sData, nErrorCode)
|
||||
|
|
@ -184,7 +162,7 @@ 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 + ")");
|
||||
|
|
@ -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,7 +181,7 @@ RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
|||
this.sFilePath = null;
|
||||
}
|
||||
this.initRAM();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initRAM()
|
||||
|
|
@ -214,7 +192,7 @@ 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)) {
|
||||
|
|
@ -265,19 +243,19 @@ 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)
|
||||
|
|
@ -286,7 +264,7 @@ RAM8080.prototype.reset = function()
|
|||
* @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) {
|
||||
|
|
@ -311,15 +289,14 @@ 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)
|
||||
|
|
@ -328,10 +305,10 @@ RAM8080.prototype.checkCPMVector = function(addr)
|
|||
* @param {number} ch
|
||||
* @return {string}
|
||||
*/
|
||||
RAM8080.prototype.getCPMChar = function(ch)
|
||||
getCPMChar(ch)
|
||||
{
|
||||
return String.fromCharCode(ch);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getCPMString(addr, chEnd)
|
||||
|
|
@ -341,7 +318,7 @@ 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;
|
||||
|
|
@ -352,7 +329,7 @@ RAM8080.prototype.getCPMString = function(addr, chEnd)
|
|||
s += String.fromCharCode(b);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* writeCPMString(s)
|
||||
|
|
@ -360,14 +337,14 @@ RAM8080.prototype.getCPMString = function(addr, chEnd)
|
|||
* @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()
|
||||
|
|
@ -377,7 +354,7 @@ 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++) {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,15 +28,21 @@
|
|||
|
||||
"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 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)
|
||||
*
|
||||
|
|
@ -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)
|
||||
constructor(parmsROM)
|
||||
{
|
||||
Component.call(this, "ROM", parmsROM, ROM8080);
|
||||
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,32 +94,17 @@ 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)
|
||||
*
|
||||
|
|
@ -124,13 +114,13 @@ 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)
|
||||
|
|
@ -140,7 +130,7 @@ 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) {
|
||||
|
|
@ -153,7 +143,7 @@ ROM8080.prototype.powerUp = function(data, fRepower)
|
|||
delete this.aSymbols;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
|
|
@ -168,10 +158,10 @@ 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)
|
||||
|
|
@ -181,7 +171,7 @@ 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 + ")");
|
||||
|
|
@ -242,11 +232,11 @@ 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()
|
||||
|
|
@ -257,7 +247,7 @@ ROM8080.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
|
|||
*
|
||||
* @this {ROM8080}
|
||||
*/
|
||||
ROM8080.prototype.copyROM = function()
|
||||
copyROM()
|
||||
{
|
||||
if (!this.isReady()) {
|
||||
if (!this.sFilePath) {
|
||||
|
|
@ -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,7 +295,7 @@ ROM8080.prototype.copyROM = function()
|
|||
this.setReady();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* addROM(addr)
|
||||
|
|
@ -314,10 +304,10 @@ ROM8080.prototype.copyROM = function()
|
|||
* @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,7 +318,7 @@ 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)
|
||||
|
|
@ -343,11 +333,11 @@ 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()
|
||||
|
|
@ -357,7 +347,7 @@ 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++) {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,15 +28,21 @@
|
|||
|
||||
"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 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)
|
||||
*
|
||||
|
|
@ -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 = "";
|
||||
|
|
@ -141,125 +146,6 @@ function SerialPort8080(parmsSerial) {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*
|
||||
|
|
@ -270,7 +156,7 @@ 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;
|
||||
|
||||
|
|
@ -381,7 +267,7 @@ SerialPort8080.prototype.setBinding = function(sHTMLType, sBinding, control, sVa
|
|||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* echoByte(b)
|
||||
|
|
@ -390,7 +276,7 @@ SerialPort8080.prototype.setBinding = function(sHTMLType, sBinding, control, sVa
|
|||
* @param {number} b
|
||||
* @return {boolean} true if echo, false if not
|
||||
*/
|
||||
SerialPort8080.prototype.echoByte = function(b)
|
||||
echoByte(b)
|
||||
{
|
||||
var fEchoed = false;
|
||||
|
||||
|
|
@ -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,7 +319,7 @@ SerialPort8080.prototype.echoByte = function(b)
|
|||
}
|
||||
|
||||
return fEchoed;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
|
|
@ -444,7 +330,7 @@ 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;
|
||||
|
|
@ -465,7 +351,7 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
bus.addPortOutputTable(this, SerialPort8080.aPortOutput, this.portBase);
|
||||
|
||||
this.setReady();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initConnection(fNullModem)
|
||||
|
|
@ -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,7 +407,7 @@ SerialPort8080.prototype.initConnection = function(fNullModem)
|
|||
this.status("Unable to establish connection: " + sConnection);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
|
|
@ -531,7 +417,7 @@ 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,7 +436,7 @@ SerialPort8080.prototype.powerUp = function(data, fRepower)
|
|||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
|
|
@ -560,20 +446,20 @@ 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()
|
||||
|
|
@ -583,12 +469,12 @@ 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)
|
||||
|
|
@ -599,10 +485,10 @@ 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)
|
||||
|
|
@ -611,7 +497,7 @@ SerialPort8080.prototype.restore = function(data)
|
|||
* @param {Array} [data]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
SerialPort8080.prototype.initState = function(data)
|
||||
initState(data)
|
||||
{
|
||||
var i = 0;
|
||||
if (data === undefined) {
|
||||
|
|
@ -625,7 +511,7 @@ SerialPort8080.prototype.initState = function(data)
|
|||
this.bCommand = data[i++];
|
||||
this.bBaudRates = data[i];
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* saveRegisters()
|
||||
|
|
@ -633,7 +519,7 @@ SerialPort8080.prototype.initState = function(data)
|
|||
* @this {SerialPort8080}
|
||||
* @return {Array}
|
||||
*/
|
||||
SerialPort8080.prototype.saveRegisters = function()
|
||||
saveRegisters()
|
||||
{
|
||||
var i = 0;
|
||||
var data = [];
|
||||
|
|
@ -645,7 +531,7 @@ SerialPort8080.prototype.saveRegisters = function()
|
|||
data[i++] = this.bCommand;
|
||||
data[i] = this.bBaudRates;
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getBaudTimeout(maskRate)
|
||||
|
|
@ -654,7 +540,7 @@ SerialPort8080.prototype.saveRegisters = function()
|
|||
* @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;
|
||||
|
|
@ -664,7 +550,7 @@ 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)
|
||||
|
|
@ -673,10 +559,10 @@ SerialPort8080.prototype.getBaudTimeout = function(maskRate)
|
|||
* @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,7 +570,7 @@ SerialPort8080.prototype.receiveByte = function(b)
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveData(data)
|
||||
|
|
@ -699,7 +585,7 @@ 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") {
|
||||
|
|
@ -717,7 +603,7 @@ SerialPort8080.prototype.receiveData = function(data)
|
|||
}
|
||||
}
|
||||
return true; // for now, return true regardless, since we're buffering everything anyway
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveStatus(pins)
|
||||
|
|
@ -729,11 +615,11 @@ 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)
|
||||
|
|
@ -742,11 +628,11 @@ SerialPort8080.prototype.receiveStatus = function(pins)
|
|||
* @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,7 +656,7 @@ SerialPort8080.prototype.transmitByte = function(b)
|
|||
}
|
||||
|
||||
return fTransmitted;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* transmitData(sData)
|
||||
|
|
@ -784,14 +670,14 @@ 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()
|
||||
|
|
@ -801,10 +687,10 @@ 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)
|
||||
|
|
@ -814,13 +700,13 @@ 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)
|
||||
|
|
@ -830,12 +716,12 @@ 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)
|
||||
|
|
@ -845,7 +731,7 @@ 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;
|
||||
|
|
@ -865,7 +751,7 @@ 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)
|
||||
|
|
@ -880,7 +766,7 @@ 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) {
|
||||
|
|
@ -909,7 +795,7 @@ SerialPort8080.prototype.outControl = function(port, bOut, addrFrom)
|
|||
this.fReady = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* outBaudRates(port, bOut, addrFrom)
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 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)
|
||||
*
|
||||
|
|
@ -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)
|
||||
constructor(parmsVideo, canvas, context, textarea, container)
|
||||
{
|
||||
var video = this;
|
||||
this.fGecko = web.isUserAgent("Gecko/");
|
||||
var i, sEvent, asWebPrefixes = ['', 'moz', 'ms', 'webkit'];
|
||||
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,11 +241,11 @@ 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);
|
||||
});
|
||||
}
|
||||
|
|
@ -248,55 +253,13 @@ function Video8080(parmsVideo, canvas, context, textarea, container)
|
|||
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,7 +351,7 @@ Video8080.prototype.initBuffers = function()
|
|||
this.abLineBuffer = new Array(this.nColsBuffer);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
|
|
@ -399,7 +362,7 @@ 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;
|
||||
|
|
@ -433,7 +396,7 @@ Video8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
this.nUpdates = 0;
|
||||
|
||||
if (!this.sFontROM) this.setReady();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sFontData, nErrorCode)
|
||||
|
|
@ -443,7 +406,7 @@ 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 + ")");
|
||||
|
|
@ -494,7 +457,7 @@ 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()
|
||||
|
|
@ -502,7 +465,7 @@ Video8080.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
|
|||
* @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,7 +487,7 @@ Video8080.prototype.createFonts = function()
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* createFontVariation(cxCell, cyCell, fUnderline)
|
||||
|
|
@ -542,7 +505,7 @@ 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,7 +567,7 @@ Video8080.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
|
|||
font.context.putImageData(imageChar, (iChar & 0xf) * cxCell, (iChar >> 4) * cyCell);
|
||||
}
|
||||
return font;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
|
|
@ -614,7 +577,7 @@ 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
|
||||
|
|
@ -685,7 +648,7 @@ Video8080.prototype.powerUp = function(data, fRepower)
|
|||
this.updateVT100();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
|
|
@ -697,7 +660,7 @@ 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,7 +690,7 @@ Video8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateDimensions(nCols, nRows)
|
||||
|
|
@ -738,7 +701,7 @@ 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,7 +717,7 @@ Video8080.prototype.updateDimensions = function(nCols, nRows)
|
|||
if (this.initBuffers()) {
|
||||
this.createFonts();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateRate(nRate)
|
||||
|
|
@ -764,11 +727,11 @@ 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)
|
||||
|
|
@ -778,7 +741,7 @@ 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) {
|
||||
|
|
@ -804,7 +767,7 @@ Video8080.prototype.updateScrollOffset = function(bScroll)
|
|||
this.fSkipSingleCellUpdate = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* doFullScreen()
|
||||
|
|
@ -812,7 +775,7 @@ Video8080.prototype.updateScrollOffset = function(bScroll)
|
|||
* @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) {
|
||||
|
|
@ -873,7 +836,7 @@ Video8080.prototype.doFullScreen = function()
|
|||
this.setFocus();
|
||||
}
|
||||
return fSuccess;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* notifyFullScreen(fFullScreen)
|
||||
|
|
@ -881,7 +844,7 @@ Video8080.prototype.doFullScreen = function()
|
|||
* @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) {
|
||||
|
|
@ -891,17 +854,17 @@ Video8080.prototype.notifyFullScreen = function(fFullScreen)
|
|||
}
|
||||
}
|
||||
this.printMessage("notifyFullScreen(" + fFullScreen + ")");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setFocus()
|
||||
*
|
||||
* @this {Video8080}
|
||||
*/
|
||||
Video8080.prototype.setFocus = function()
|
||||
setFocus()
|
||||
{
|
||||
if (this.inputScreen) this.inputScreen.focus();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getRefreshTime()
|
||||
|
|
@ -909,10 +872,10 @@ Video8080.prototype.setFocus = function()
|
|||
* @this {Video8080}
|
||||
* @return {number} (number of milliseconds per refresh)
|
||||
*/
|
||||
Video8080.prototype.getRefreshTime = function()
|
||||
getRefreshTime()
|
||||
{
|
||||
return 1000 / Math.max(this.rateRefresh, this.rateInterrupt);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* initCellCache(nCells)
|
||||
|
|
@ -922,14 +885,14 @@ 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()
|
||||
|
|
@ -938,7 +901,7 @@ Video8080.prototype.initCellCache = function(nCells)
|
|||
*
|
||||
* @this {Video8080}
|
||||
*/
|
||||
Video8080.prototype.initColors = function()
|
||||
initColors()
|
||||
{
|
||||
var rgbBlack = [0x00, 0x00, 0x00, 0xff];
|
||||
var rgbWhite = [0xff, 0xff, 0xff, 0xff];
|
||||
|
|
@ -953,7 +916,7 @@ 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)
|
||||
|
|
@ -964,7 +927,7 @@ 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) {
|
||||
|
|
@ -986,7 +949,7 @@ 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)
|
||||
|
|
@ -1000,7 +963,7 @@ 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];
|
||||
|
|
@ -1053,7 +1016,7 @@ 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)
|
||||
|
|
@ -1061,7 +1024,7 @@ Video8080.prototype.updateChar = function(idFont, col, row, data, context)
|
|||
* @this {Video8080}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video8080.prototype.updateVT100 = function(fForced)
|
||||
updateVT100(fForced)
|
||||
{
|
||||
var addrNext = this.addrBuffer, fontNext = -1;
|
||||
|
||||
|
|
@ -1183,7 +1146,7 @@ Video8080.prototype.updateVT100 = function(fForced)
|
|||
this.cyScreenOffset // cyDst
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateScreen(fForced)
|
||||
|
|
@ -1199,7 +1162,7 @@ Video8080.prototype.updateVT100 = function(fForced)
|
|||
* @this {Video8080}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video8080.prototype.updateScreen = function(fForced)
|
||||
updateScreen(fForced)
|
||||
{
|
||||
var fUpdate = true;
|
||||
|
||||
|
|
@ -1250,7 +1213,7 @@ Video8080.prototype.updateScreen = function(fForced)
|
|||
} else {
|
||||
this.updateScreenGraphics(fForced);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* updateScreenText(fForced)
|
||||
|
|
@ -1258,14 +1221,14 @@ Video8080.prototype.updateScreen = function(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)
|
||||
|
|
@ -1273,7 +1236,7 @@ Video8080.prototype.updateScreenText = function(fForced)
|
|||
* @this {Video8080}
|
||||
* @param {boolean} [fForced]
|
||||
*/
|
||||
Video8080.prototype.updateScreenGraphics = function(fForced)
|
||||
updateScreenGraphics(fForced)
|
||||
{
|
||||
var addr = this.addrBuffer;
|
||||
var addrLimit = addr + this.sizeBuffer;
|
||||
|
|
@ -1352,7 +1315,7 @@ 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()
|
||||
|
|
@ -1362,7 +1325,7 @@ 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++) {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
24
package.json
24
package.json
|
|
@ -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
Loading…
Reference in a new issue