Converted PC8080 to ES6
This commit is contained in:
parent
d930b31c53
commit
0f680839f7
27 changed files with 11054 additions and 10896 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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");
|
||||
}
|
||||
var CPUDef8080 = require("./CPUDef");
|
||||
var Messages8080 = require("./messages");
|
||||
|
||||
/**
|
||||
* op=0x00 (NOP)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -98,6 +98,5 @@ if (NODE) {
|
|||
global.BYTEARRAYS = BYTEARRAYS;
|
||||
global.TYPEDARRAYS = TYPEDARRAYS;
|
||||
global.PC8080 = PC8080;
|
||||
|
||||
module.exports = 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,155 +28,157 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var usr = require("../../shared/lib/usrlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PC8080 = require("./defines");
|
||||
var Bus8080 = require("./bus");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
var Memory8080 = require("./memory");
|
||||
}
|
||||
var Web = require("../../shared/es6/weblib");
|
||||
var Component = require("../../shared/es6/component");
|
||||
var PC8080 = require("./defines");
|
||||
var Bus8080 = require("./bus");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
var Memory8080 = require("./memory");
|
||||
|
||||
/**
|
||||
* Panel8080(parmsPanel)
|
||||
* 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'.
|
||||
*
|
||||
* The Panel8080 component has no required (parmsPanel) properties.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsPanel
|
||||
* @unrestricted
|
||||
*/
|
||||
function Panel8080(parmsPanel)
|
||||
{
|
||||
Component.call(this, "Panel", parmsPanel, Panel8080);
|
||||
}
|
||||
|
||||
Component.subclass(Panel8080);
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* Most panel layouts don't have bindings of their own, so we pass along all binding requests to the
|
||||
* Computer, CPU, Keyboard and Debugger components first. The order shouldn't matter, since any component
|
||||
* that doesn't recognize the specified binding should simply ignore it.
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "reset")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
Panel8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
if (this.cmp && this.cmp.setBinding(sHTMLType, sBinding, control, sValue)) return true;
|
||||
if (this.cpu && this.cpu.setBinding(sHTMLType, sBinding, control, sValue)) return true;
|
||||
if (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);
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
Panel8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.kbd = /** @type {Keyboard8080} */ (cmp.getMachineComponent("Keyboard"));
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
Panel8080.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) Panel8080.init();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
Panel8080.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* Update function for Panels containing elements with high-frequency display requirements.
|
||||
*
|
||||
* For older (and slower) DOM-based display elements, those are sill being managed by the CPUState component,
|
||||
* so it has its own updateStatus() handler.
|
||||
*
|
||||
* The Computer's updateStatus() handler is currently responsible for calling both our handler and the CPU's handler.
|
||||
*
|
||||
* @this {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)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* Panel8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "panel", extracting the
|
||||
* JSON-encoded parameters for the Panel8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a Panel8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*
|
||||
* NOTE: Unlike most other component init() functions, this one is designed to be
|
||||
* called multiple times: once at load time, so that we can bind our print()
|
||||
* function to the panel's output control ASAP, and again when the Computer component
|
||||
* is verifying that all components are ready and invoking their powerUp() functions.
|
||||
*
|
||||
* Our powerUp() method gives us a second opportunity to notify any components that
|
||||
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
|
||||
* might want to use.
|
||||
*/
|
||||
Panel8080.init = function()
|
||||
{
|
||||
var fReady = false;
|
||||
var aePanels = Component.getElementsByClass(document, PC8080.APPCLASS, "panel");
|
||||
for (var iPanel=0; iPanel < aePanels.length; iPanel++) {
|
||||
var ePanel = aePanels[iPanel];
|
||||
var parmsPanel = Component.getComponentParms(ePanel);
|
||||
var panel = Component.getComponentByID(parmsPanel['id']);
|
||||
if (!panel) {
|
||||
fReady = true;
|
||||
panel = new Panel8080(parmsPanel);
|
||||
}
|
||||
Component.bindComponentControls(panel, ePanel, PC8080.APPCLASS);
|
||||
if (fReady) panel.setReady();
|
||||
class Panel8080 extends Component {
|
||||
/**
|
||||
* Panel8080(parmsPanel)
|
||||
*
|
||||
* The Panel8080 component has no required (parmsPanel) properties.
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {Object} parmsPanel
|
||||
*/
|
||||
constructor(parmsPanel)
|
||||
{
|
||||
super("Panel", parmsPanel, Panel8080);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* Most panel layouts don't have bindings of their own, so we pass along all binding requests to the
|
||||
* Computer, CPU, Keyboard and Debugger components first. The order shouldn't matter, since any component
|
||||
* that doesn't recognize the specified binding should simply ignore it.
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "reset")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
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 super.setBinding(sHTMLType, sBinding, control, sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.kbd = /** @type {Keyboard8080} */ (cmp.getMachineComponent("Keyboard"));
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (!fRepower) Panel8080.init();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* updateStatus(fForce)
|
||||
*
|
||||
* Update function for Panels containing elements with high-frequency display requirements.
|
||||
*
|
||||
* For older (and slower) DOM-based display elements, those are sill being managed by the CPUState component,
|
||||
* so it has its own updateStatus() handler.
|
||||
*
|
||||
* The Computer's updateStatus() handler is currently responsible for calling both our handler and the CPU's handler.
|
||||
*
|
||||
* @this {Panel8080}
|
||||
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
|
||||
*/
|
||||
updateStatus(fForce)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Panel8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "panel", extracting the
|
||||
* JSON-encoded parameters for the Panel8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a Panel8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*
|
||||
* NOTE: Unlike most other component init() functions, this one is designed to be
|
||||
* called multiple times: once at load time, so that we can bind our print()
|
||||
* function to the panel's output control ASAP, and again when the Computer component
|
||||
* is verifying that all components are ready and invoking their powerUp() functions.
|
||||
*
|
||||
* Our powerUp() method gives us a second opportunity to notify any components that
|
||||
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
|
||||
* might want to use.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var fReady = false;
|
||||
var aePanels = Component.getElementsByClass(document, PC8080.APPCLASS, "panel");
|
||||
for (var iPanel=0; iPanel < aePanels.length; iPanel++) {
|
||||
var ePanel = aePanels[iPanel];
|
||||
var parmsPanel = Component.getComponentParms(ePanel);
|
||||
var panel = Component.getComponentByID(parmsPanel['id']);
|
||||
if (!panel) {
|
||||
fReady = true;
|
||||
panel = new Panel8080(parmsPanel);
|
||||
}
|
||||
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,74 +28,344 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PC8080 = require("./defines");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
var Memory8080 = require("./memory");
|
||||
}
|
||||
var Str = require("../../shared/es6/strlib");
|
||||
var Web = require("../../shared/es6/weblib");
|
||||
var DumpAPI = require("../../shared/es6/dumpapi");
|
||||
var Component = require("../../shared/es6/component");
|
||||
var State = require("../../shared/es6/state");
|
||||
var PC8080 = require("./defines");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
var Memory8080 = require("./memory");
|
||||
|
||||
/**
|
||||
* RAM8080(parmsRAM)
|
||||
* 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'.
|
||||
*
|
||||
* The RAM8080 component expects the following (parmsRAM) properties:
|
||||
*
|
||||
* addr: starting physical address of RAM (default is 0)
|
||||
* size: amount of RAM, in bytes (default is 0, which means defer to motherboard switch settings)
|
||||
* file: name of optional data file to load into RAM (default is "")
|
||||
* load: optional file load address (overrides any load address specified in the data file; default is null)
|
||||
* exec: optional file exec address (overrides any exec address specified in the data file; default is null)
|
||||
*
|
||||
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
|
||||
* Computer component calls powerUp().
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsRAM
|
||||
* @unrestricted
|
||||
*/
|
||||
function RAM8080(parmsRAM)
|
||||
{
|
||||
Component.call(this, "RAM", parmsRAM, RAM8080);
|
||||
class RAM8080 extends Component {
|
||||
/**
|
||||
* RAM8080(parmsRAM)
|
||||
*
|
||||
* The RAM8080 component expects the following (parmsRAM) properties:
|
||||
*
|
||||
* addr: starting physical address of RAM (default is 0)
|
||||
* size: amount of RAM, in bytes (default is 0, which means defer to motherboard switch settings)
|
||||
* file: name of optional data file to load into RAM (default is "")
|
||||
* load: optional file load address (overrides any load address specified in the data file; default is null)
|
||||
* exec: optional file exec address (overrides any exec address specified in the data file; default is null)
|
||||
*
|
||||
* NOTE: We make a note of the specified size, but no memory is initially allocated for the RAM until the
|
||||
* Computer component calls powerUp().
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {Object} parmsRAM
|
||||
*/
|
||||
constructor(parmsRAM)
|
||||
{
|
||||
super("RAM", parmsRAM, RAM8080);
|
||||
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
this.abInit = null;
|
||||
this.aSymbols = null;
|
||||
|
||||
this.addrRAM = parmsRAM['addr'];
|
||||
this.sizeRAM = parmsRAM['size'];
|
||||
this.addrLoad = parmsRAM['load'];
|
||||
this.addrExec = parmsRAM['exec'];
|
||||
this.addrRAM = parmsRAM['addr'];
|
||||
this.sizeRAM = parmsRAM['size'];
|
||||
this.addrLoad = parmsRAM['load'];
|
||||
this.addrExec = parmsRAM['exec'];
|
||||
|
||||
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
|
||||
this.fAllocated = false;
|
||||
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
|
||||
this.fAllocated = false;
|
||||
|
||||
this.sFilePath = parmsRAM['file'];
|
||||
this.sFileName = str.getBaseName(this.sFilePath);
|
||||
this.sFilePath = parmsRAM['file'];
|
||||
this.sFileName = Str.getBaseName(this.sFilePath);
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected data file has a ".json" extension, then we assume it's pre-converted
|
||||
* 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);
|
||||
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';
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected data file has a ".json" extension, then we assume it's pre-converted
|
||||
* 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);
|
||||
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
|
||||
sFileURL = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var ram = this;
|
||||
Web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
ram.doneLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.initRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
/*
|
||||
* The Computer powers up the CPU last, at which point CPUState state is restored,
|
||||
* which includes the Bus state, and since we use the Bus to allocate all our memory,
|
||||
* memory contents are already restored for us, so we don't need the usual restore
|
||||
* logic.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point CPUState state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
* our memory, memory contents are already saved for us, so we don't need the usual
|
||||
* save logic.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
doneLoad(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Component.addMachineResource(this.idMachine, sURL, sData);
|
||||
|
||||
var resource = Web.parseMemoryResource(sURL, sData);
|
||||
if (resource) {
|
||||
this.abInit = resource.aBytes;
|
||||
this.aSymbols = resource.aSymbols;
|
||||
if (this.addrLoad == null) this.addrLoad = resource.addrLoad;
|
||||
if (this.addrExec == null) this.addrExec = resource.addrExec;
|
||||
} else {
|
||||
this.sFilePath = null;
|
||||
}
|
||||
this.initRAM();
|
||||
}
|
||||
|
||||
/**
|
||||
* initRAM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {RAM8080}
|
||||
*/
|
||||
initRAM()
|
||||
{
|
||||
if (!this.fAllocated && this.sizeRAM) {
|
||||
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, Memory8080.TYPE.RAM)) {
|
||||
this.fAllocated = true;
|
||||
}
|
||||
}
|
||||
if (!this.isReady()) {
|
||||
if (!this.fAllocated) {
|
||||
Component.error("No RAM allocated");
|
||||
}
|
||||
else if (this.sFilePath) {
|
||||
/*
|
||||
* Too early...
|
||||
*/
|
||||
if (!this.abInit || !this.bus) return;
|
||||
|
||||
var addr = this.addrRAM;
|
||||
if (this.addrLoad !== null) addr = this.addrLoad;
|
||||
for (var i = 0; i < this.abInit.length; i++) {
|
||||
this.bus.setByteDirect(addr + i, this.abInit[i]);
|
||||
}
|
||||
|
||||
if (this.addrExec !== null) {
|
||||
/*
|
||||
* Here's where we enable our "Fake CP/M" support, triggered by the user loading a "writable" ROM image
|
||||
* at offset 0x100. Fake CP/M support works by installing HLT opcodes at well-known CP/M addresses
|
||||
* (namely, 0x0000, which is the CP/M reset vector, and 0x0005, which is the CP/M system call vector) and
|
||||
* then telling the CPU to call us whenever a HLT occurs, so we can check PC for one of these addresses.
|
||||
*/
|
||||
if (this.addrExec == RAM8080.CPM.INIT) {
|
||||
for (i = 0; i < RAM8080.CPM.VECTORS.length; i++) {
|
||||
this.bus.setByteDirect(RAM8080.CPM.VECTORS[i], CPUDef8080.OPCODE.HLT);
|
||||
}
|
||||
|
||||
this.cpu.addHaltCheck(function(rom) {
|
||||
return function(addr) {
|
||||
return rom.checkCPMVector(addr)
|
||||
};
|
||||
}(this));
|
||||
}
|
||||
this.cpu.setReset(this.addrExec);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Consider an option to retain this data and give the user a way of restoring the initial contents.
|
||||
*/
|
||||
delete this.abInit;
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {RAM8080}
|
||||
*/
|
||||
reset()
|
||||
{
|
||||
/*
|
||||
* If you want to zero RAM on reset, then this would be a good place to do it.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* checkCPMVector(addr)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} addr (of the HLT opcode)
|
||||
* @return {boolean} true if special processing performed, false if not
|
||||
*/
|
||||
checkCPMVector(addr)
|
||||
{
|
||||
var i = RAM8080.CPM.VECTORS.indexOf(addr);
|
||||
if (i >= 0) {
|
||||
var fCPM = false;
|
||||
var cpu = this.cpu;
|
||||
var dbg = this.dbg;
|
||||
if (addr == RAM8080.CPM.BDOS.VECTOR) {
|
||||
fCPM = true;
|
||||
switch(cpu.regC) {
|
||||
case RAM8080.CPM.BDOS.FUNC.CON_WRITE:
|
||||
this.writeCPMString(this.getCPMChar(cpu.regE));
|
||||
break;
|
||||
case RAM8080.CPM.BDOS.FUNC.STR_WRITE:
|
||||
this.writeCPMString(this.getCPMString(cpu.getDE(), '$'));
|
||||
break;
|
||||
default:
|
||||
fCPM = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fCPM) {
|
||||
CPUDef8080.opRET.call(cpu); // for recognized calls, automatically return
|
||||
}
|
||||
else if (dbg) {
|
||||
this.println("\nCP/M vector " + Str.toHexWord(addr));
|
||||
cpu.setPC(addr); // this is purely for the Debugger's benefit, to show the HLT
|
||||
dbg.stopCPU();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCPMChar(ch)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} ch
|
||||
* @return {string}
|
||||
*/
|
||||
getCPMChar(ch)
|
||||
{
|
||||
return String.fromCharCode(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* getCPMString(addr, chEnd)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} addr (of a string)
|
||||
* @param {string|number} [chEnd] (terminating character, default is 0)
|
||||
* @return {string}
|
||||
*/
|
||||
getCPMString(addr, chEnd)
|
||||
{
|
||||
var s = "";
|
||||
var cchMax = 255;
|
||||
var bEnd = chEnd && chEnd.length && chEnd.charCodeAt(0) || chEnd || 0;
|
||||
while (cchMax--) {
|
||||
var b = this.cpu.getByte(addr++);
|
||||
if (b == bEnd) break;
|
||||
s += String.fromCharCode(b);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* writeCPMString(s)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {string} s
|
||||
*/
|
||||
writeCPMString(s)
|
||||
{
|
||||
s = s.replace(/\r/g, '');
|
||||
if (this.controlPrint) {
|
||||
this.controlPrint.value += s;
|
||||
this.controlPrint.scrollTop = this.controlPrint.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RAM8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "ram", extracting the
|
||||
* JSON-encoded parameters for the RAM8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a RAM8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(document, PC8080.APPCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
var parmsRAM = Component.getComponentParms(eRAM);
|
||||
var ram = new RAM8080(parmsRAM);
|
||||
Component.bindComponentControls(ram, eRAM, PC8080.APPCLASS);
|
||||
}
|
||||
var ram = this;
|
||||
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
ram.doneLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component.subclass(RAM8080);
|
||||
|
||||
RAM8080.CPM = {
|
||||
BIOS: {
|
||||
VECTOR: 0x0000
|
||||
|
|
@ -121,276 +391,9 @@ RAM8080.CPM = {
|
|||
|
||||
RAM8080.CPM.VECTORS = [RAM8080.CPM.BIOS.VECTOR, RAM8080.CPM.BDOS.VECTOR];
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
RAM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.initRAM();
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAM8080.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
/*
|
||||
* The Computer powers up the CPU last, at which point CPUState state is restored,
|
||||
* which includes the Bus state, and since we use the Bus to allocate all our memory,
|
||||
* memory contents are already restored for us, so we don't need the usual restore
|
||||
* logic.
|
||||
*/
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
RAM8080.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point CPUState state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
* our memory, memory contents are already saved for us, so we don't need the usual
|
||||
* save logic.
|
||||
*/
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sData, nErrorCode)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {string} sURL
|
||||
* @param {string} sData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
RAM8080.prototype.doneLoad = function(sURL, sData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Component.addMachineResource(this.idMachine, sURL, sData);
|
||||
|
||||
var resource = web.parseMemoryResource(sURL, sData);
|
||||
if (resource) {
|
||||
this.abInit = resource.aBytes;
|
||||
this.aSymbols = resource.aSymbols;
|
||||
if (this.addrLoad == null) this.addrLoad = resource.addrLoad;
|
||||
if (this.addrExec == null) this.addrExec = resource.addrExec;
|
||||
} else {
|
||||
this.sFilePath = null;
|
||||
}
|
||||
this.initRAM();
|
||||
};
|
||||
|
||||
/**
|
||||
* initRAM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the initial data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the data. When both those
|
||||
* criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {RAM8080}
|
||||
*/
|
||||
RAM8080.prototype.initRAM = function()
|
||||
{
|
||||
if (!this.fAllocated && this.sizeRAM) {
|
||||
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, Memory8080.TYPE.RAM)) {
|
||||
this.fAllocated = true;
|
||||
}
|
||||
}
|
||||
if (!this.isReady()) {
|
||||
if (!this.fAllocated) {
|
||||
Component.error("No RAM allocated");
|
||||
}
|
||||
else if (this.sFilePath) {
|
||||
/*
|
||||
* Too early...
|
||||
*/
|
||||
if (!this.abInit || !this.bus) return;
|
||||
|
||||
var addr = this.addrRAM;
|
||||
if (this.addrLoad !== null) addr = this.addrLoad;
|
||||
for (var i = 0; i < this.abInit.length; i++) {
|
||||
this.bus.setByteDirect(addr + i, this.abInit[i]);
|
||||
}
|
||||
|
||||
if (this.addrExec !== null) {
|
||||
/*
|
||||
* Here's where we enable our "Fake CP/M" support, triggered by the user loading a "writable" ROM image
|
||||
* at offset 0x100. Fake CP/M support works by installing HLT opcodes at well-known CP/M addresses
|
||||
* (namely, 0x0000, which is the CP/M reset vector, and 0x0005, which is the CP/M system call vector) and
|
||||
* then telling the CPU to call us whenever a HLT occurs, so we can check PC for one of these addresses.
|
||||
*/
|
||||
if (this.addrExec == RAM8080.CPM.INIT) {
|
||||
for (i = 0; i < RAM8080.CPM.VECTORS.length; i++) {
|
||||
this.bus.setByteDirect(RAM8080.CPM.VECTORS[i], CPUDef8080.OPCODE.HLT);
|
||||
}
|
||||
|
||||
this.cpu.addHaltCheck(function(rom) {
|
||||
return function(addr) {
|
||||
return rom.checkCPMVector(addr)
|
||||
};
|
||||
}(this));
|
||||
}
|
||||
this.cpu.setReset(this.addrExec);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Consider an option to retain this data and give the user a way of restoring the initial contents.
|
||||
*/
|
||||
delete this.abInit;
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {RAM8080}
|
||||
*/
|
||||
RAM8080.prototype.reset = function()
|
||||
{
|
||||
/*
|
||||
* If you want to zero RAM on reset, then this would be a good place to do it.
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* checkCPMVector(addr)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} addr (of the HLT opcode)
|
||||
* @return {boolean} true if special processing performed, false if not
|
||||
*/
|
||||
RAM8080.prototype.checkCPMVector = function(addr)
|
||||
{
|
||||
var i = RAM8080.CPM.VECTORS.indexOf(addr);
|
||||
if (i >= 0) {
|
||||
var fCPM = false;
|
||||
var cpu = this.cpu;
|
||||
var dbg = this.dbg;
|
||||
if (addr == RAM8080.CPM.BDOS.VECTOR) {
|
||||
fCPM = true;
|
||||
switch(cpu.regC) {
|
||||
case RAM8080.CPM.BDOS.FUNC.CON_WRITE:
|
||||
this.writeCPMString(this.getCPMChar(cpu.regE));
|
||||
break;
|
||||
case RAM8080.CPM.BDOS.FUNC.STR_WRITE:
|
||||
this.writeCPMString(this.getCPMString(cpu.getDE(), '$'));
|
||||
break;
|
||||
default:
|
||||
fCPM = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fCPM) {
|
||||
CPUDef8080.opRET.call(cpu); // for recognized calls, automatically return
|
||||
}
|
||||
else if (dbg) {
|
||||
this.println("\nCP/M vector " + str.toHexWord(addr));
|
||||
cpu.setPC(addr); // this is purely for the Debugger's benefit, to show the HLT
|
||||
dbg.stopCPU();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* getCPMChar(ch)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} ch
|
||||
* @return {string}
|
||||
*/
|
||||
RAM8080.prototype.getCPMChar = function(ch)
|
||||
{
|
||||
return String.fromCharCode(ch);
|
||||
};
|
||||
|
||||
/**
|
||||
* getCPMString(addr, chEnd)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {number} addr (of a string)
|
||||
* @param {string|number} [chEnd] (terminating character, default is 0)
|
||||
* @return {string}
|
||||
*/
|
||||
RAM8080.prototype.getCPMString = function(addr, chEnd)
|
||||
{
|
||||
var s = "";
|
||||
var cchMax = 255;
|
||||
var bEnd = chEnd && chEnd.length && chEnd.charCodeAt(0) || chEnd || 0;
|
||||
while (cchMax--) {
|
||||
var b = this.cpu.getByte(addr++);
|
||||
if (b == bEnd) break;
|
||||
s += String.fromCharCode(b);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeCPMString(s)
|
||||
*
|
||||
* @this {RAM8080}
|
||||
* @param {string} s
|
||||
*/
|
||||
RAM8080.prototype.writeCPMString = function(s)
|
||||
{
|
||||
s = s.replace(/\r/g, '');
|
||||
if (this.controlPrint) {
|
||||
this.controlPrint.value += s;
|
||||
this.controlPrint.scrollTop = this.controlPrint.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RAM8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "ram", extracting the
|
||||
* JSON-encoded parameters for the RAM8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a RAM8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
RAM8080.init = function()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(document, PC8080.APPCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
var parmsRAM = Component.getComponentParms(eRAM);
|
||||
var ram = new RAM8080(parmsRAM);
|
||||
Component.bindComponentControls(ram, eRAM, PC8080.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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,80 +28,337 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var DumpAPI = require("../../shared/lib/dumpapi");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var PC8080 = require("./defines");
|
||||
var Memory8080 = require("./memory");
|
||||
}
|
||||
var Str = require("../../shared/es6/strlib");
|
||||
var Web = require("../../shared/es6/weblib");
|
||||
var DumpAPI = require("../../shared/es6/dumpapi");
|
||||
var Component = require("../../shared/es6/component");
|
||||
var PC8080 = require("./defines");
|
||||
var Memory8080 = require("./memory");
|
||||
|
||||
/**
|
||||
* ROM8080(parmsROM)
|
||||
* 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'.
|
||||
*
|
||||
* The ROM8080 component expects the following (parmsROM) properties:
|
||||
*
|
||||
* addr: physical address of ROM
|
||||
* size: amount of ROM, in bytes
|
||||
* alias: physical alias address (null if none)
|
||||
* file: name of ROM data file
|
||||
*
|
||||
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND the
|
||||
* ROM data file has finished loading (see doneLoad()).
|
||||
*
|
||||
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
|
||||
* is the ROM you expected.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsROM
|
||||
* @unrestricted
|
||||
*/
|
||||
function ROM8080(parmsROM)
|
||||
{
|
||||
Component.call(this, "ROM", parmsROM, ROM8080);
|
||||
|
||||
this.abROM = null;
|
||||
this.addrROM = parmsROM['addr'];
|
||||
this.sizeROM = parmsROM['size'];
|
||||
|
||||
/*
|
||||
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
|
||||
* physical addresses; eg:
|
||||
class ROM8080 extends Component {
|
||||
/**
|
||||
* ROM8080(parmsROM)
|
||||
*
|
||||
* [0xf0000,0xffff0000,0xffff8000]
|
||||
* The ROM8080 component expects the following (parmsROM) properties:
|
||||
*
|
||||
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
|
||||
* aliased locations listed under a separate property.
|
||||
* addr: physical address of ROM
|
||||
* size: amount of ROM, in bytes
|
||||
* alias: physical alias address (null if none)
|
||||
* file: name of ROM data file
|
||||
*
|
||||
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
|
||||
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND the
|
||||
* ROM data file has finished loading (see doneLoad()).
|
||||
*
|
||||
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
|
||||
* is the ROM you expected.
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {Object} parmsROM
|
||||
*/
|
||||
this.addrAlias = parmsROM['alias'];
|
||||
constructor(parmsROM)
|
||||
{
|
||||
super("ROM", parmsROM, ROM8080);
|
||||
|
||||
this.sFilePath = parmsROM['file'];
|
||||
this.sFileName = str.getBaseName(this.sFilePath);
|
||||
this.abROM = null;
|
||||
this.addrROM = parmsROM['addr'];
|
||||
this.sizeROM = parmsROM['size'];
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected ROM file has a ".json" extension, then we assume it's pre-converted
|
||||
* JSON-encoded ROM data, so we load it as-is; ditto for ROM files with a ".hex" extension.
|
||||
* Otherwise, we ask our server-side ROM converter to return the file in a JSON-compatible format.
|
||||
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
|
||||
* physical addresses; eg:
|
||||
*
|
||||
* [0xf0000,0xffff0000,0xffff8000]
|
||||
*
|
||||
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
|
||||
* aliased locations listed under a separate property.
|
||||
*
|
||||
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
|
||||
*/
|
||||
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';
|
||||
this.addrAlias = parmsROM['alias'];
|
||||
|
||||
this.sFilePath = parmsROM['file'];
|
||||
this.sFileName = Str.getBaseName(this.sFilePath);
|
||||
|
||||
if (this.sFilePath) {
|
||||
var sFileURL = this.sFilePath;
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
/*
|
||||
* If the selected ROM file has a ".json" extension, then we assume it's pre-converted
|
||||
* JSON-encoded ROM data, so we load it as-is; ditto for ROM files with a ".hex" extension.
|
||||
* Otherwise, we ask our server-side ROM converter to return the file in a JSON-compatible format.
|
||||
*/
|
||||
var sFileExt = Str.getExtension(this.sFileName);
|
||||
if (sFileExt != DumpAPI.FORMAT.JSON && sFileExt != DumpAPI.FORMAT.HEX) {
|
||||
sFileURL = Web.getHost() + DumpAPI.ENDPOINT + '?' + DumpAPI.QUERY.FILE + '=' + this.sFilePath + '&' + DumpAPI.QUERY.FORMAT + '=' + DumpAPI.FORMAT.BYTES + '&' + DumpAPI.QUERY.DECIMAL + '=true';
|
||||
}
|
||||
var rom = this;
|
||||
Web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
|
||||
rom.doneLoad(sURL, sResponse, nErrorCode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.copyROM();
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp(data, fRepower)
|
||||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.id, this.addrROM, this.sizeROM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
|
||||
*/
|
||||
delete this.aSymbols;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* Since we have nothing to do on powerDown(), and no state to return, we could simply omit
|
||||
* this function. But it doesn't hurt anything, and maybe we'll use our state to save something
|
||||
* useful down the road, like user-defined symbols (ie, symbols that the Debugger may have
|
||||
* created, above and beyond those symbols we automatically loaded, if any, along with the ROM).
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sROMData, nErrorCode)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {string} sURL
|
||||
* @param {string} sROMData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
doneLoad(sURL, sROMData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load system ROM (error " + nErrorCode + ": " + sURL + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Component.addMachineResource(this.idMachine, sURL, sROMData);
|
||||
|
||||
if (sROMData.charAt(0) == "[" || sROMData.charAt(0) == "{") {
|
||||
try {
|
||||
/*
|
||||
* The most likely source of any exception will be here: parsing the JSON-encoded ROM data.
|
||||
*/
|
||||
var rom = eval("(" + sROMData + ")");
|
||||
var ab = rom['bytes'];
|
||||
var adw = rom['data'];
|
||||
|
||||
if (ab) {
|
||||
this.abROM = ab;
|
||||
}
|
||||
else if (adw) {
|
||||
/*
|
||||
* Convert all the DWORDs into BYTEs, so that subsequent code only has to deal with abROM.
|
||||
*/
|
||||
this.abROM = new Array(adw.length * 4);
|
||||
for (var idw = 0, ib = 0; idw < adw.length; idw++) {
|
||||
this.abROM[ib++] = adw[idw] & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 8) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 16) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.abROM = rom;
|
||||
}
|
||||
|
||||
this.aSymbols = rom['symbols'];
|
||||
|
||||
if (!this.abROM.length) {
|
||||
Component.error("Empty ROM: " + sURL);
|
||||
return;
|
||||
}
|
||||
else if (this.abROM.length == 1) {
|
||||
Component.error(this.abROM[0]);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.notice("ROM data error: " + e.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Parse the ROM data manually; we assume it's in "simplified" hex form (a series of hex byte-values
|
||||
* separated by whitespace).
|
||||
*/
|
||||
var sHexData = sROMData.replace(/\n/gm, " ").replace(/ +$/, "");
|
||||
var asHexData = sHexData.split(" ");
|
||||
this.abROM = new Array(asHexData.length);
|
||||
for (var i = 0; i < asHexData.length; i++) {
|
||||
this.abROM[i] = Str.parseInt(asHexData[i], 16);
|
||||
}
|
||||
}
|
||||
this.copyROM();
|
||||
}
|
||||
|
||||
/**
|
||||
* copyROM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the the ROM data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the abROM data. When both
|
||||
* those criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {ROM8080}
|
||||
*/
|
||||
copyROM()
|
||||
{
|
||||
if (!this.isReady()) {
|
||||
if (!this.sFilePath) {
|
||||
this.setReady();
|
||||
}
|
||||
else if (this.abROM && this.bus) {
|
||||
/*
|
||||
* If no explicit size was specified, then use whatever the actual size is.
|
||||
*/
|
||||
if (!this.sizeROM) {
|
||||
this.sizeROM = this.abROM.length;
|
||||
}
|
||||
if (this.abROM.length != this.sizeROM) {
|
||||
/*
|
||||
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from
|
||||
* marking the component ready. TODO: Revisit this decision. On the one hand, it sounds like a
|
||||
* good idea to stop the machine in its tracks whenever a setError() occurs, but there may also be
|
||||
* times when we'd like to forge ahead anyway.
|
||||
*/
|
||||
this.setError("ROM size (" + Str.toHexLong(this.abROM.length) + ") does not match specified size (" + Str.toHexLong(this.sizeROM) + ")");
|
||||
}
|
||||
else if (this.addROM(this.addrROM)) {
|
||||
|
||||
var aliases = [];
|
||||
if (typeof this.addrAlias == "number") {
|
||||
aliases.push(this.addrAlias);
|
||||
} else if (this.addrAlias != null && this.addrAlias.length) {
|
||||
aliases = this.addrAlias;
|
||||
}
|
||||
for (var i = 0; i < aliases.length; i++) {
|
||||
this.cloneROM(aliases[i]);
|
||||
}
|
||||
/*
|
||||
* We used to hang onto the original ROM data so that we could restore any bytes the CPU overwrote,
|
||||
* using memory write-notification handlers, but with the introduction of read-only memory blocks, that's
|
||||
* no longer necessary.
|
||||
*
|
||||
* TODO: Consider an option to retain the ROM data, and give the user some way of restoring ROMs.
|
||||
* That may be useful for "resumable" machines that save/restore all dirty block of memory, regardless
|
||||
* whether they're ROM or RAM. However, the only way to modify a machine's ROM is with the Debugger,
|
||||
* and Debugger users should know better.
|
||||
*/
|
||||
delete this.abROM;
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addROM(addr)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {number} addr
|
||||
* @return {boolean}
|
||||
*/
|
||||
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)");
|
||||
var i;
|
||||
for (i = 0; i < this.abROM.length; i++) {
|
||||
this.bus.setByteDirect(addr + i, this.abROM[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* We don't need to report an error here, because addMemory() already takes care of that.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* cloneROM(addr)
|
||||
*
|
||||
* For ROMs with one or more alias addresses, we used to call addROM() for each address. However,
|
||||
* that obviously wasted memory, since each alias was an independent copy, and if you used the
|
||||
* Debugger to edit the ROM in one location, the changes would not appear in the other location(s).
|
||||
*
|
||||
* Now that the Bus component provides low-level getMemoryBlocks() and setMemoryBlocks() methods
|
||||
* to manually get and set the blocks of any memory range, it is now possible to create true aliases.
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {number} addr
|
||||
*/
|
||||
cloneROM(addr)
|
||||
{
|
||||
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
|
||||
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* ROM8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "rom", extracting the
|
||||
* JSON-encoded parameters for the ROM8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a ROM8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
static init()
|
||||
{
|
||||
var aeROM = Component.getElementsByClass(document, PC8080.APPCLASS, "rom");
|
||||
for (var iROM = 0; iROM < aeROM.length; iROM++) {
|
||||
var eROM = aeROM[iROM];
|
||||
var parmsROM = Component.getComponentParms(eROM);
|
||||
var rom = new ROM8080(parmsROM);
|
||||
Component.bindComponentControls(rom, eROM, PC8080.APPCLASS);
|
||||
}
|
||||
var rom = this;
|
||||
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.
|
||||
|
|
@ -115,262 +372,9 @@ Component.subclass(ROM8080);
|
|||
* via bus.addMemory().
|
||||
*/
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {Computer8080} cmp
|
||||
* @param {Bus8080} bus
|
||||
* @param {CPUState8080} cpu
|
||||
* @param {Debugger8080} dbg
|
||||
*/
|
||||
ROM8080.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.copyROM();
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
ROM8080.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (this.aSymbols) {
|
||||
if (this.dbg) {
|
||||
this.dbg.addSymbols(this.id, this.addrROM, this.sizeROM, this.aSymbols);
|
||||
}
|
||||
/*
|
||||
* Our only role in the handling of symbols is to hand them off to the Debugger at our
|
||||
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
|
||||
*/
|
||||
delete this.aSymbols;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* Since we have nothing to do on powerDown(), and no state to return, we could simply omit
|
||||
* this function. But it doesn't hurt anything, and maybe we'll use our state to save something
|
||||
* useful down the road, like user-defined symbols (ie, symbols that the Debugger may have
|
||||
* created, above and beyond those symbols we automatically loaded, if any, along with the ROM).
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
ROM8080.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* doneLoad(sURL, sROMData, nErrorCode)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {string} sURL
|
||||
* @param {string} sROMData
|
||||
* @param {number} nErrorCode (response from server if anything other than 200)
|
||||
*/
|
||||
ROM8080.prototype.doneLoad = function(sURL, sROMData, nErrorCode)
|
||||
{
|
||||
if (nErrorCode) {
|
||||
this.notice("Unable to load system ROM (error " + nErrorCode + ": " + sURL + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Component.addMachineResource(this.idMachine, sURL, sROMData);
|
||||
|
||||
if (sROMData.charAt(0) == "[" || sROMData.charAt(0) == "{") {
|
||||
try {
|
||||
/*
|
||||
* The most likely source of any exception will be here: parsing the JSON-encoded ROM data.
|
||||
*/
|
||||
var rom = eval("(" + sROMData + ")");
|
||||
var ab = rom['bytes'];
|
||||
var adw = rom['data'];
|
||||
|
||||
if (ab) {
|
||||
this.abROM = ab;
|
||||
}
|
||||
else if (adw) {
|
||||
/*
|
||||
* Convert all the DWORDs into BYTEs, so that subsequent code only has to deal with abROM.
|
||||
*/
|
||||
this.abROM = new Array(adw.length * 4);
|
||||
for (var idw = 0, ib = 0; idw < adw.length; idw++) {
|
||||
this.abROM[ib++] = adw[idw] & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 8) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 16) & 0xff;
|
||||
this.abROM[ib++] = (adw[idw] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.abROM = rom;
|
||||
}
|
||||
|
||||
this.aSymbols = rom['symbols'];
|
||||
|
||||
if (!this.abROM.length) {
|
||||
Component.error("Empty ROM: " + sURL);
|
||||
return;
|
||||
}
|
||||
else if (this.abROM.length == 1) {
|
||||
Component.error(this.abROM[0]);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.notice("ROM data error: " + e.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Parse the ROM data manually; we assume it's in "simplified" hex form (a series of hex byte-values
|
||||
* separated by whitespace).
|
||||
*/
|
||||
var sHexData = sROMData.replace(/\n/gm, " ").replace(/ +$/, "");
|
||||
var asHexData = sHexData.split(" ");
|
||||
this.abROM = new Array(asHexData.length);
|
||||
for (var i = 0; i < asHexData.length; i++) {
|
||||
this.abROM[i] = str.parseInt(asHexData[i], 16);
|
||||
}
|
||||
}
|
||||
this.copyROM();
|
||||
};
|
||||
|
||||
/**
|
||||
* copyROM()
|
||||
*
|
||||
* This function is called by both initBus() and doneLoad(), but it cannot copy the the ROM data into place
|
||||
* until after initBus() has received the Bus component AND doneLoad() has received the abROM data. When both
|
||||
* those criteria are satisfied, the component becomes "ready".
|
||||
*
|
||||
* @this {ROM8080}
|
||||
*/
|
||||
ROM8080.prototype.copyROM = function()
|
||||
{
|
||||
if (!this.isReady()) {
|
||||
if (!this.sFilePath) {
|
||||
this.setReady();
|
||||
}
|
||||
else if (this.abROM && this.bus) {
|
||||
/*
|
||||
* If no explicit size was specified, then use whatever the actual size is.
|
||||
*/
|
||||
if (!this.sizeROM) {
|
||||
this.sizeROM = this.abROM.length;
|
||||
}
|
||||
if (this.abROM.length != this.sizeROM) {
|
||||
/*
|
||||
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from
|
||||
* marking the component ready. TODO: Revisit this decision. On the one hand, it sounds like a
|
||||
* good idea to stop the machine in its tracks whenever a setError() occurs, but there may also be
|
||||
* times when we'd like to forge ahead anyway.
|
||||
*/
|
||||
this.setError("ROM size (" + str.toHexLong(this.abROM.length) + ") does not match specified size (" + str.toHexLong(this.sizeROM) + ")");
|
||||
}
|
||||
else if (this.addROM(this.addrROM)) {
|
||||
|
||||
var aliases = [];
|
||||
if (typeof this.addrAlias == "number") {
|
||||
aliases.push(this.addrAlias);
|
||||
} else if (this.addrAlias != null && this.addrAlias.length) {
|
||||
aliases = this.addrAlias;
|
||||
}
|
||||
for (var i = 0; i < aliases.length; i++) {
|
||||
this.cloneROM(aliases[i]);
|
||||
}
|
||||
/*
|
||||
* We used to hang onto the original ROM data so that we could restore any bytes the CPU overwrote,
|
||||
* using memory write-notification handlers, but with the introduction of read-only memory blocks, that's
|
||||
* no longer necessary.
|
||||
*
|
||||
* TODO: Consider an option to retain the ROM data, and give the user some way of restoring ROMs.
|
||||
* That may be useful for "resumable" machines that save/restore all dirty block of memory, regardless
|
||||
* whether they're ROM or RAM. However, the only way to modify a machine's ROM is with the Debugger,
|
||||
* and Debugger users should know better.
|
||||
*/
|
||||
delete this.abROM;
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* addROM(addr)
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {number} addr
|
||||
* @return {boolean}
|
||||
*/
|
||||
ROM8080.prototype.addROM = function(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)");
|
||||
var i;
|
||||
for (i = 0; i < this.abROM.length; i++) {
|
||||
this.bus.setByteDirect(addr + i, this.abROM[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* We don't need to report an error here, because addMemory() already takes care of that.
|
||||
*/
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* cloneROM(addr)
|
||||
*
|
||||
* For ROMs with one or more alias addresses, we used to call addROM() for each address. However,
|
||||
* that obviously wasted memory, since each alias was an independent copy, and if you used the
|
||||
* Debugger to edit the ROM in one location, the changes would not appear in the other location(s).
|
||||
*
|
||||
* Now that the Bus component provides low-level getMemoryBlocks() and setMemoryBlocks() methods
|
||||
* to manually get and set the blocks of any memory range, it is now possible to create true aliases.
|
||||
*
|
||||
* @this {ROM8080}
|
||||
* @param {number} addr
|
||||
*/
|
||||
ROM8080.prototype.cloneROM = function(addr)
|
||||
{
|
||||
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
|
||||
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
|
||||
};
|
||||
|
||||
/**
|
||||
* ROM8080.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "rom", extracting the
|
||||
* JSON-encoded parameters for the ROM8080 constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a ROM8080 component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
ROM8080.init = function()
|
||||
{
|
||||
var aeROM = Component.getElementsByClass(document, PC8080.APPCLASS, "rom");
|
||||
for (var iROM = 0; iROM < aeROM.length; iROM++) {
|
||||
var eROM = aeROM[iROM];
|
||||
var parmsROM = Component.getComponentParms(eROM);
|
||||
var rom = new ROM8080(parmsROM);
|
||||
Component.bindComponentControls(rom, eROM, PC8080.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize all the ROM8080 modules on the page.
|
||||
*/
|
||||
web.onInit(ROM8080.init);
|
||||
Web.onInit(ROM8080.init);
|
||||
|
||||
if (NODE) module.exports = ROM8080;
|
||||
module.exports = ROM8080;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue