Converted the rest of the PCjs machines to ES6

This commit is contained in:
Jeff Parsons 2017-01-31 11:44:32 -08:00 committed by Jeff Parsons
commit 323a42be37
301 changed files with 60754 additions and 52273 deletions

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

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

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

View file

@ -29,88 +29,15 @@
"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 PCX86 = require("./defines");
var Messages = require("./messages");
var ChipSet = require("./chipset");
}
/**
* ParallelPort(parmsParallel)
*
* The ParallelPort component has the following component-specific (parmsParallel) properties:
*
* adapter: 1 (port 0x3BC), 2 (port 0x378), or 3 (port 0x278); 0 if not defined
*
* binding: name of a control (based on its "binding" attribute) to bind to this port's I/O
*
* In the future, we may support 'port' and 'irq' properties that allow the machine to define a
* non-standard parallel port configuration, instead of only our pre-defined 'adapter' configurations.
*
* NOTE: Since the XSL file defines 'adapter' as a number, not a string, there's no need to use
* parseInt(), and as an added benefit, we don't need to worry about whether a hex or decimal format
* was used.
*
* DOS typically names the Primary adapter "LPT1" and the Secondary adapter "LPT2", but I prefer
* to stick to adapter numbers, since not all operating systems follow those naming conventions.
*
* @constructor
* @extends Component
* @param {Object} parmsParallel
*/
function ParallelPort(parmsParallel) {
this.iAdapter = parmsParallel['adapter'];
switch (this.iAdapter) {
case 1:
this.portBase = 0x3BC;
this.nIRQ = ChipSet.IRQ.LPT1;
break;
case 2:
this.portBase = 0x378;
this.nIRQ = ChipSet.IRQ.LPT1;
break;
case 3:
this.portBase = 0x278;
this.nIRQ = ChipSet.IRQ.LPT2;
break;
default:
Component.warning("Unrecognized parallel adapter #" + this.iAdapter);
return;
}
/**
* consoleOutput becomes a string that records parallel port output if the 'binding' property is set to the
* reserved name "console". Nothing is written to the console, however, until a linefeed (0x0A) is output
* or the string length reaches a threshold (currently, 1024 characters).
*
* @type {string|null}
*/
this.consoleOutput = null;
/**
* controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()).
*
* @type {Object}
*/
this.controlIOBuffer = null;
Component.call(this, "ParallelPort", parmsParallel, ParallelPort, Messages.PARALLEL);
var sBinding = parmsParallel['binding'];
if (sBinding == "console") {
this.consoleOutput = "";
} else {
/*
* NOTE: If sBinding is not the name of a valid Control Panel DOM element, this call does nothing.
*/
Component.bindExternalControl(this, sBinding, ParallelPort.sIOBuffer);
}
}
/*
* class ParallelPort
* property {number} iAdapter
@ -128,7 +55,387 @@ function ParallelPort(parmsParallel) {
* for third-party apps.
*/
Component.subclass(ParallelPort);
/**
* 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 ParallelPort extends Component {
/**
* ParallelPort(parmsParallel)
*
* The ParallelPort component has the following component-specific (parmsParallel) properties:
*
* adapter: 1 (port 0x3BC), 2 (port 0x378), or 3 (port 0x278); 0 if not defined
*
* binding: name of a control (based on its "binding" attribute) to bind to this port's I/O
*
* In the future, we may support 'port' and 'irq' properties that allow the machine to define a
* non-standard parallel port configuration, instead of only our pre-defined 'adapter' configurations.
*
* NOTE: Since the XSL file defines 'adapter' as a number, not a string, there's no need to use
* parseInt(), and as an added benefit, we don't need to worry about whether a hex or decimal format
* was used.
*
* DOS typically names the Primary adapter "LPT1" and the Secondary adapter "LPT2", but I prefer
* to stick to adapter numbers, since not all operating systems follow those naming conventions.
*
* @this {ParallelPort}
* @param {Object} parmsParallel
*/
constructor(parmsParallel)
{
super("ParallelPort", parmsParallel, Messages.PARALLEL);
this.iAdapter = parmsParallel['adapter'];
switch (this.iAdapter) {
case 1:
this.portBase = 0x3BC;
this.nIRQ = ChipSet.IRQ.LPT1;
break;
case 2:
this.portBase = 0x378;
this.nIRQ = ChipSet.IRQ.LPT1;
break;
case 3:
this.portBase = 0x278;
this.nIRQ = ChipSet.IRQ.LPT2;
break;
default:
Component.warning("Unrecognized parallel adapter #" + this.iAdapter);
return;
}
/**
* consoleOutput becomes a string that records parallel port output if the 'binding' property is set to the
* reserved name "console". Nothing is written to the console, however, until a linefeed (0x0A) is output
* or the string length reaches a threshold (currently, 1024 characters).
*
* @type {string|null}
*/
this.consoleOutput = null;
/**
* controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()).
*
* @type {Object}
*/
this.controlIOBuffer = null;
var sBinding = parmsParallel['binding'];
if (sBinding == "console") {
this.consoleOutput = "";
} else {
/*
* NOTE: If sBinding is not the name of a valid Control Panel DOM element, this call does nothing.
*/
Component.bindExternalControl(this, sBinding, ParallelPort.sIOBuffer);
}
}
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {ParallelPort}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "buffer")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
setBinding(sHTMLType, sBinding, control, sValue)
{
switch (sBinding) {
case ParallelPort.sIOBuffer:
this.bindings[sBinding] = this.controlIOBuffer = control;
return true;
default:
break;
}
return false;
}
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {ParallelPort}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
initBus(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.chipset = cmp.getMachineComponent("ChipSet");
bus.addPortInputTable(this, ParallelPort.aPortInput, this.portBase);
bus.addPortOutputTable(this, ParallelPort.aPortOutput, this.portBase);
this.setReady();
}
/**
* powerUp(data, fRepower)
*
* @this {ParallelPort}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
powerUp(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
this.reset();
} else {
if (!this.restore(data)) return false;
}
}
return true;
}
/**
* powerDown(fSave, fShutdown)
*
* @this {ParallelPort}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
powerDown(fSave, fShutdown)
{
return fSave? this.save() : true;
}
/**
* reset()
*
* @this {ParallelPort}
*/
reset()
{
this.initState();
}
/**
* save()
*
* This implements save support for the ParallelPort component.
*
* @this {ParallelPort}
* @return {Object}
*/
save()
{
var state = new State(this);
state.set(0, this.saveRegisters());
return state.data();
}
/**
* restore(data)
*
* This implements restore support for the ParallelPort component.
*
* @this {ParallelPort}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
return this.initState(data[0]);
}
/**
* initState(data)
*
* @this {ParallelPort}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
initState(data)
{
var i = 0;
if (data === undefined) {
data = [0, 0, 0];
}
this.bData = data[i++];
this.bStatus = data[i++];
this.bControl = data[i];
return true;
}
/**
* saveRegisters()
*
* @this {ParallelPort}
* @return {Array}
*/
saveRegisters()
{
var i = 0;
var data = [];
data[i++] = this.bData;
data[i++] = this.bStatus;
data[i] = this.bControl;
return data;
}
/**
* inData(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BC, 0x378, or 0x278)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
inData(port, addrFrom)
{
var b = this.bData;
this.printMessageIO(port, null, addrFrom, "DATA", b);
return b;
}
/**
* inStatus(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BD, 0x379, or 0x279)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
inStatus(port, addrFrom)
{
var b = this.bStatus;
this.printMessageIO(port, null, addrFrom, "STAT", b);
return b;
}
/**
* inControl(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BE, 0x37A, or 0x27A)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
inControl(port, addrFrom)
{
var b = this.bControl;
this.printMessageIO(port, null, addrFrom, "CTRL", b);
return b;
}
/**
* outData(port, bOut, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BC, 0x378, or 0x278)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
outData(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bData = bOut;
this.bStatus |= ParallelPort.STATUS.NOTREADY;
if (this.transmitByte(bOut)) {
this.bStatus &= ~ParallelPort.STATUS.NOTREADY;
}
this.updateIRR();
}
/**
* outControl(port, bOut, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BE, 0x37A, or 0x27A)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
outControl(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "CTRL");
this.bControl = bOut;
this.updateIRR();
}
/**
* updateIRR()
*
* @this {ParallelPort}
*/
updateIRR()
{
if (this.chipset && this.nIRQ) {
if ((this.bControl & ParallelPort.CONTROL.IRQ_ENABLE) && !(this.bStatus & ParallelPort.STATUS.NOTREADY)) {
this.chipset.setIRR(this.nIRQ);
} else {
this.chipset.clearIRR(this.nIRQ);
}
}
}
/**
* transmitByte(b)
*
* @this {ParallelPort}
* @param {number} b
* @return {boolean} true if transmitted, false if not
*/
transmitByte(b)
{
var fTransmitted = false;
this.printMessage("transmitByte(" + Str.toHexByte(b) + ")");
if (this.controlIOBuffer) {
if (b == 0x08) {
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
}
else {
this.controlIOBuffer.value += String.fromCharCode(b);
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
}
fTransmitted = true;
}
if (this.consoleOutput != null) {
if (b == 0x0A || this.consoleOutput.length >= 1024) {
this.println(this.consoleOutput);
this.consoleOutput = "";
}
if (b != 0x0A) {
this.consoleOutput += String.fromCharCode(b);
}
fTransmitted = true;
}
return fTransmitted;
}
/**
* ParallelPort.init()
*
* This function operates on every HTML element of class "parallel", extracting the
* JSON-encoded parameters for the ParallelPort constructor from the element's "data-value"
* attribute, invoking the constructor to create a ParallelPort component, and then binding
* any associated HTML controls to the new component.
*/
static init()
{
var aeParallel = Component.getElementsByClass(document, PCX86.APPCLASS, "parallel");
for (var iParallel = 0; iParallel < aeParallel.length; iParallel++) {
var eParallel = aeParallel[iParallel];
var parmsParallel = Component.getComponentParms(eParallel);
var parallel = new ParallelPort(parmsParallel);
Component.bindComponentControls(parallel, eParallel, PCX86.APPCLASS);
}
}
}
/*
* Internal name used for the I/O buffer control, if any, that we bind to the ParallelPort.
@ -197,288 +504,6 @@ ParallelPort.CONTROL = { // (read/write)
IRQ_ENABLE: 0x10 // set to enable interrupts
};
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
* @this {ParallelPort}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "buffer")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @param {string} [sValue] optional data value
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
ParallelPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
{
switch (sBinding) {
case ParallelPort.sIOBuffer:
this.bindings[sBinding] = this.controlIOBuffer = control;
return true;
default:
break;
}
return false;
};
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {ParallelPort}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
ParallelPort.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.chipset = cmp.getMachineComponent("ChipSet");
bus.addPortInputTable(this, ParallelPort.aPortInput, this.portBase);
bus.addPortOutputTable(this, ParallelPort.aPortOutput, this.portBase);
this.setReady();
};
/**
* powerUp(data, fRepower)
*
* @this {ParallelPort}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
ParallelPort.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
this.reset();
} else {
if (!this.restore(data)) return false;
}
}
return true;
};
/**
* powerDown(fSave, fShutdown)
*
* @this {ParallelPort}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
ParallelPort.prototype.powerDown = function(fSave, fShutdown)
{
return fSave? this.save() : true;
};
/**
* reset()
*
* @this {ParallelPort}
*/
ParallelPort.prototype.reset = function()
{
this.initState();
};
/**
* save()
*
* This implements save support for the ParallelPort component.
*
* @this {ParallelPort}
* @return {Object}
*/
ParallelPort.prototype.save = function()
{
var state = new State(this);
state.set(0, this.saveRegisters());
return state.data();
};
/**
* restore(data)
*
* This implements restore support for the ParallelPort component.
*
* @this {ParallelPort}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
ParallelPort.prototype.restore = function(data)
{
return this.initState(data[0]);
};
/**
* initState(data)
*
* @this {ParallelPort}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
ParallelPort.prototype.initState = function(data)
{
var i = 0;
if (data === undefined) {
data = [0, 0, 0];
}
this.bData = data[i++];
this.bStatus = data[i++];
this.bControl = data[i];
return true;
};
/**
* saveRegisters()
*
* @this {ParallelPort}
* @return {Array}
*/
ParallelPort.prototype.saveRegisters = function()
{
var i = 0;
var data = [];
data[i++] = this.bData;
data[i++] = this.bStatus;
data[i] = this.bControl;
return data;
};
/**
* inData(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BC, 0x378, or 0x278)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
ParallelPort.prototype.inData = function(port, addrFrom)
{
var b = this.bData;
this.printMessageIO(port, null, addrFrom, "DATA", b);
return b;
};
/**
* inStatus(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BD, 0x379, or 0x279)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
ParallelPort.prototype.inStatus = function(port, addrFrom)
{
var b = this.bStatus;
this.printMessageIO(port, null, addrFrom, "STAT", b);
return b;
};
/**
* inControl(port, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BE, 0x37A, or 0x27A)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
ParallelPort.prototype.inControl = function(port, addrFrom)
{
var b = this.bControl;
this.printMessageIO(port, null, addrFrom, "CTRL", b);
return b;
};
/**
* outData(port, bOut, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BC, 0x378, or 0x278)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
ParallelPort.prototype.outData = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bData = bOut;
this.bStatus |= ParallelPort.STATUS.NOTREADY;
if (this.transmitByte(bOut)) {
this.bStatus &= ~ParallelPort.STATUS.NOTREADY;
}
this.updateIRR();
};
/**
* outControl(port, bOut, addrFrom)
*
* @this {ParallelPort}
* @param {number} port (0x3BE, 0x37A, or 0x27A)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
ParallelPort.prototype.outControl = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "CTRL");
this.bControl = bOut;
this.updateIRR();
};
/**
* updateIRR()
*
* @this {ParallelPort}
*/
ParallelPort.prototype.updateIRR = function()
{
if (this.chipset && this.nIRQ) {
if ((this.bControl & ParallelPort.CONTROL.IRQ_ENABLE) && !(this.bStatus & ParallelPort.STATUS.NOTREADY)) {
this.chipset.setIRR(this.nIRQ);
} else {
this.chipset.clearIRR(this.nIRQ);
}
}
};
/**
* transmitByte(b)
*
* @this {ParallelPort}
* @param {number} b
* @return {boolean} true if transmitted, false if not
*/
ParallelPort.prototype.transmitByte = function(b)
{
var fTransmitted = false;
this.printMessage("transmitByte(" + str.toHexByte(b) + ")");
if (this.controlIOBuffer) {
if (b == 0x08) {
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
}
else {
this.controlIOBuffer.value += String.fromCharCode(b);
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
}
fTransmitted = true;
}
if (this.consoleOutput != null) {
if (b == 0x0A || this.consoleOutput.length >= 1024) {
this.println(this.consoleOutput);
this.consoleOutput = "";
}
if (b != 0x0A) {
this.consoleOutput += String.fromCharCode(b);
}
fTransmitted = true;
}
return fTransmitted;
};
/*
* Port input notification table
*/
@ -496,28 +521,9 @@ ParallelPort.aPortOutput = {
0x2: ParallelPort.prototype.outControl
};
/**
* ParallelPort.init()
*
* This function operates on every HTML element of class "parallel", extracting the
* JSON-encoded parameters for the ParallelPort constructor from the element's "data-value"
* attribute, invoking the constructor to create a ParallelPort component, and then binding
* any associated HTML controls to the new component.
*/
ParallelPort.init = function()
{
var aeParallel = Component.getElementsByClass(document, PCX86.APPCLASS, "parallel");
for (var iParallel = 0; iParallel < aeParallel.length; iParallel++) {
var eParallel = aeParallel[iParallel];
var parmsParallel = Component.getComponentParms(eParallel);
var parallel = new ParallelPort(parmsParallel);
Component.bindComponentControls(parallel, eParallel, PCX86.APPCLASS);
}
};
/*
* Initialize every ParallelPort module on the page.
*/
web.onInit(ParallelPort.init);
Web.onInit(ParallelPort.init);
if (NODE) module.exports = ParallelPort;

View file

@ -29,290 +29,474 @@
"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 PCX86 = require("./defines");
var Memory = require("./memory");
var ROM = require("./rom");
}
/**
* RAM(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 RAM 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)
* test: true (default) means don't interfere with any BIOS memory tests, false means "fake a warm boot"
*
* 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 RAM(parmsRAM)
{
Component.call(this, "RAM", parmsRAM, RAM);
class RAM extends Component {
/**
* RAM(parmsRAM)
*
* The RAM 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)
* test: true (default) means don't interfere with any BIOS memory tests, false means "fake a warm boot"
*
* 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 {RAM}
* @param {Object} parmsRAM
*/
constructor(parmsRAM)
{
super("RAM", parmsRAM);
this.addrRAM = parmsRAM['addr'];
this.sizeRAM = parmsRAM['size'];
this.fTestRAM = parmsRAM['test'];
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
this.fAllocated = false;
}
this.addrRAM = parmsRAM['addr'];
this.sizeRAM = parmsRAM['size'];
this.fTestRAM = parmsRAM['test'];
this.fInstalled = (!!this.sizeRAM); // 0 is the default value for 'size' when none is specified
this.fAllocated = false;
}
Component.subclass(RAM);
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {RAM}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
initBus(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.chipset = cmp.getMachineComponent("ChipSet");
this.setReady();
}
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {RAM}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.chipset = cmp.getMachineComponent("ChipSet");
this.setReady();
};
/**
* powerUp(data, fRepower)
*
* @this {RAM}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
powerUp(data, fRepower)
{
if (!fRepower) {
/*
* The Computer powers up the CPU last, at which point X86CPU state is restored,
* which includes the Bus state, and since we use the Bus to allocate all our memory,
* memory contents are already restored for us, so we don't need the usual restore
* logic. We just need to call reset(), to allocate memory for the RAM.
*
* The only exception is when there's a custom Memory controller (eg, CompaqController).
*/
this.reset();
if (data && this.controller) {
if (!this.restore(data)) return false;
}
}
return true;
}
/**
* powerUp(data, fRepower)
*
* @this {RAM}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
RAM.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
/**
* powerDown(fSave, fShutdown)
*
* @this {RAM}
* @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 up the CPU last, at which point X86CPU state is restored,
* which includes the Bus state, and since we use the Bus to allocate all our memory,
* memory contents are already restored for us, so we don't need the usual restore
* logic. We just need to call reset(), to allocate memory for the RAM.
* The Computer powers down the CPU first, at which point X86CPU 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.
*
* The only exception is when there's a custom Memory controller (eg, CompaqController).
*/
this.reset();
if (data && this.controller) {
if (!this.restore(data)) return false;
}
return (fSave && this.controller)? this.save() : true;
}
return true;
};
/**
* powerDown(fSave, fShutdown)
*
* @this {RAM}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
RAM.prototype.powerDown = function(fSave, fShutdown)
{
/*
* The Computer powers down the CPU first, at which point X86CPU 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.
/**
* reset()
*
* The only exception is when there's a custom Memory controller (eg, CompaqController).
* NOTE: When we were initialized, we were given an amount of INSTALLED memory (see sizeRAM above).
* The ChipSet component, on the other hand, tells us how much SPECIFIED memory there is -- which,
* like a real PC, may not match the amount of installed memory (due to either user error or perhaps
* an attempt to prevent some portion of the installed memory from being used).
*
* However, since we're a virtual machine, we can defer allocation of RAM until we're able to query the
* ChipSet component, and then allocate an amount of memory that matches the SPECIFIED memory, making
* it easy to reconfigure the machine on the fly and prevent mismatches.
*
* But, we do that ONLY for the RAM instance configured with an addrRAM of 0x0000, and ONLY if that RAM
* object was not given a specific size (see fInstalled). If there are other RAM objects in the system,
* they must necessarily specify a non-conflicting, non-zero start address, in which case their sizeRAM
* value will never be affected by the ChipSet settings.
*
* @this {RAM}
*/
return (fSave && this.controller)? this.save() : true;
};
/**
* reset()
*
* NOTE: When we were initialized, we were given an amount of INSTALLED memory (see sizeRAM above).
* The ChipSet component, on the other hand, tells us how much SPECIFIED memory there is -- which,
* like a real PC, may not match the amount of installed memory (due to either user error or perhaps
* an attempt to prevent some portion of the installed memory from being used).
*
* However, since we're a virtual machine, we can defer allocation of RAM until we're able to query the
* ChipSet component, and then allocate an amount of memory that matches the SPECIFIED memory, making
* it easy to reconfigure the machine on the fly and prevent mismatches.
*
* But, we do that ONLY for the RAM instance configured with an addrRAM of 0x0000, and ONLY if that RAM
* object was not given a specific size (see fInstalled). If there are other RAM objects in the system,
* they must necessarily specify a non-conflicting, non-zero start address, in which case their sizeRAM
* value will never be affected by the ChipSet settings.
*
* @this {RAM}
*/
RAM.prototype.reset = function()
{
if (!this.addrRAM && !this.fInstalled && this.chipset) {
var baseRAM = this.chipset.getDIPMemorySize() * 1024;
if (this.sizeRAM && baseRAM != this.sizeRAM) {
this.bus.removeMemory(this.addrRAM, this.sizeRAM);
this.fAllocated = false;
reset()
{
if (!this.addrRAM && !this.fInstalled && this.chipset) {
var baseRAM = this.chipset.getDIPMemorySize() * 1024;
if (this.sizeRAM && baseRAM != this.sizeRAM) {
this.bus.removeMemory(this.addrRAM, this.sizeRAM);
this.fAllocated = false;
}
this.sizeRAM = baseRAM;
}
this.sizeRAM = baseRAM;
}
if (!this.fAllocated && this.sizeRAM) {
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, Memory.TYPE.RAM)) {
this.fAllocated = true;
if (!this.fAllocated && this.sizeRAM) {
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, Memory.TYPE.RAM)) {
this.fAllocated = true;
/*
* NOTE: I'm specifying MAXDEBUG for status() messages because I'm not yet sure I want these
* messages buried in the app, since they're seen only when a Control Panel is active. Another
* and perhaps better alternative is to add "comment" attributes to the XML configuration file
* for these components, which the Computer component will display as it "powers up" components.
*/
if (MAXDEBUG && this.fInstalled) this.status("specified size overrides SW1");
/*
* NOTE: I'm specifying MAXDEBUG for status() messages because I'm not yet sure I want these
* messages buried in the app, since they're seen only when a Control Panel is active. Another
* and perhaps better alternative is to add "comment" attributes to the XML configuration file
* for these components, which the Computer component will display as it "powers up" components.
*/
if (MAXDEBUG && this.fInstalled) this.status("specified size overrides SW1");
/*
* Memory with an ID of "ramCPQ" is reserved for built-in memory located just below the 16Mb
* boundary on COMPAQ DeskPro 386 machines.
*
* Technically, that memory is part of the first 1Mb of memory that also provides up to 640Kb
* of conventional memory (ie, memory below 1Mb).
*
* However, PCx86 doesn't support individual memory allocations that (a) are discontiguous
* or (b) dynamically change location. Components must simulate those features by performing
* a separate allocation for each starting address, and removing/adding memory allocations
* whenever their starting address changes.
*
* Therefore, a DeskPro 386's first 1Mb of physical memory is allocated by PCx86 in two pieces,
* and the second piece must have an ID of "ramCPQ", triggering the additional allocation of
* COMPAQ-specific memory-mapped registers.
*
* See CompaqController for more details.
*/
if (DESKPRO386) {
if (this.idComponent == "ramCPQ") {
this.controller = new CompaqController(this);
this.bus.addMemory(CompaqController.ADDR, 4, Memory.TYPE.CTRL, this.controller);
/*
* Memory with an ID of "ramCPQ" is reserved for built-in memory located just below the 16Mb
* boundary on COMPAQ DeskPro 386 machines.
*
* Technically, that memory is part of the first 1Mb of memory that also provides up to 640Kb
* of conventional memory (ie, memory below 1Mb).
*
* However, PCx86 doesn't support individual memory allocations that (a) are discontiguous
* or (b) dynamically change location. Components must simulate those features by performing
* a separate allocation for each starting address, and removing/adding memory allocations
* whenever their starting address changes.
*
* Therefore, a DeskPro 386's first 1Mb of physical memory is allocated by PCx86 in two pieces,
* and the second piece must have an ID of "ramCPQ", triggering the additional allocation of
* COMPAQ-specific memory-mapped registers.
*
* See CompaqController for more details.
*/
if (DESKPRO386) {
if (this.idComponent == "ramCPQ") {
this.controller = new CompaqController(this);
this.bus.addMemory(CompaqController.ADDR, 4, Memory.TYPE.CTRL, this.controller);
}
}
}
}
}
if (this.fAllocated) {
if (!this.fTestRAM) {
if (this.fAllocated) {
if (!this.fTestRAM) {
/*
* HACK: Set the word at 40:72 in the ROM BIOS Data Area (RBDA) to 0x1234 to bypass the ROM BIOS
* memory storage tests. See rom.js for all RBDA definitions.
*/
if (MAXDEBUG) this.status("ROM BIOS memory test has been disabled");
this.bus.setShortDirect(ROM.BIOS.RESET_FLAG, ROM.BIOS.RESET_FLAG_WARMBOOT);
}
/*
* HACK: Set the word at 40:72 in the ROM BIOS Data Area (RBDA) to 0x1234 to bypass the ROM BIOS
* memory storage tests. See rom.js for all RBDA definitions.
* Don't add the "ramCPQ" memory to the CMOS total, because addCMOSMemory() will add it to the extended
* memory total, which will just confuse the COMPAQ BIOS.
*/
if (MAXDEBUG) this.status("ROM BIOS memory test has been disabled");
this.bus.setShortDirect(ROM.BIOS.RESET_FLAG, ROM.BIOS.RESET_FLAG_WARMBOOT);
if (!DESKPRO386 || this.idComponent != "ramCPQ") {
if (this.chipset) this.chipset.addCMOSMemory(this.addrRAM, this.sizeRAM);
}
} else {
Component.error("No RAM allocated");
}
/*
* Don't add the "ramCPQ" memory to the CMOS total, because addCMOSMemory() will add it to the extended
* memory total, which will just confuse the COMPAQ BIOS.
*/
if (!DESKPRO386 || this.idComponent != "ramCPQ") {
if (this.chipset) this.chipset.addCMOSMemory(this.addrRAM, this.sizeRAM);
}
} else {
Component.error("No RAM allocated");
}
};
/**
* save()
*
* This implements save support for the RAM component.
*
* @this {RAM}
* @return {Object}
*/
RAM.prototype.save = function()
{
var state = new State(this);
if (this.controller) state.set(0, this.controller.save());
return state.data();
};
/**
* restore(data)
*
* This implements restore support for the RAM component.
*
* @this {RAM}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
RAM.prototype.restore = function(data)
{
if (this.controller) return this.controller.restore(data[0]);
return true;
};
/**
* RAM.init()
*
* This function operates on every HTML element of class "ram", extracting the
* JSON-encoded parameters for the RAM constructor from the element's "data-value"
* attribute, invoking the constructor to create a RAM component, and then binding
* any associated HTML controls to the new component.
*/
RAM.init = function()
{
var aeRAM = Component.getElementsByClass(document, PCX86.APPCLASS, "ram");
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
var eRAM = aeRAM[iRAM];
var parmsRAM = Component.getComponentParms(eRAM);
var ram = new RAM(parmsRAM);
Component.bindComponentControls(ram, eRAM, PCX86.APPCLASS);
}
};
/**
* CompaqController(ram)
*
* DeskPro 386 machines came with a minimum of 1Mb of RAM, which could be configured (via jumpers)
* for 256Kb, 512Kb or 640Kb of conventional memory, starting at address 0x00000000, with the
* remainder (768Kb, 512Kb, or 384Kb) accessible only at an address just below 0x01000000. In PCx86,
* this second chunk of RAM must be separately allocated, with an ID of "ramCPQ".
*
* The typical configuration was 640Kb of conventional memory, leaving 384Kb accessible at 0x00FA0000.
* Presumably, the other configurations (256Kb and 512Kb) would leave 768Kb and 512Kb accessible at
* 0x00F40000 and 0x00F80000, respectively.
*
* The DeskPro 386 also contained two memory-mapped registers at 0x80C00000. The first is a write-only
* mapping register that provides the ability to map the 128Kb at 0x00FE0000 to 0x000E0000, replacing
* any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb; internally,
* this register corresponds to wMappings.
*
* The second register is a read-only diagnostics register that indicates jumper configuration and
* parity errors; internally, this register corresponds to wSettings.
*
* To emulate the memory-mapped registers at 0x80C00000, the RAM component allocates a block at that
* address using this custom controller once it sees an allocation for "ramCPQ".
*
* Later, when the addressability of "ramCPQ" memory is altered, we record the blocks in all the
* memory slots spanning 0x000E0000-0x000FFFFF, and then update those slots with the blocks from
* 0x00FE0000-0x00FFFFFF. Note that only the top 128Kb of "ramCPQ" addressability is affected; the
* rest of that memory, ranging anywhere from 256Kb to 640Kb, remains addressable at its original
* location. COMPAQ's CEMM and VDISK utilities were generally the only software able to access that
* remaining memory (what COMPAQ refers to as "Compaq Built-in Memory").
*
* @constructor
* @param {RAM} ram
*/
function CompaqController(ram)
{
this.ram = ram;
this.wMappings = CompaqController.MAPPINGS.DEFAULT;
/*
* TODO: wSettings needs to reflect the actual amount of configured memory....
/**
* save()
*
* This implements save support for the RAM component.
*
* @this {RAM}
* @return {Object}
*/
this.wSettings = CompaqController.SETTINGS.DEFAULT;
this.wRAMSetup = CompaqController.RAMSETUP.DEFAULT;
this.aBlocksDst = null;
save()
{
var state = new State(this);
if (this.controller) state.set(0, this.controller.save());
return state.data();
}
/**
* restore(data)
*
* This implements restore support for the RAM component.
*
* @this {RAM}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
if (this.controller) return this.controller.restore(data[0]);
return true;
}
/**
* RAM.init()
*
* This function operates on every HTML element of class "ram", extracting the
* JSON-encoded parameters for the RAM constructor from the element's "data-value"
* attribute, invoking the constructor to create a RAM component, and then binding
* any associated HTML controls to the new component.
*/
static init()
{
var aeRAM = Component.getElementsByClass(document, PCX86.APPCLASS, "ram");
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
var eRAM = aeRAM[iRAM];
var parmsRAM = Component.getComponentParms(eRAM);
var ram = new RAM(parmsRAM);
Component.bindComponentControls(ram, eRAM, PCX86.APPCLASS);
}
}
}
/**
* 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 CompaqController {
/**
* CompaqController(ram)
*
* DeskPro 386 machines came with a minimum of 1Mb of RAM, which could be configured (via jumpers)
* for 256Kb, 512Kb or 640Kb of conventional memory, starting at address 0x00000000, with the
* remainder (768Kb, 512Kb, or 384Kb) accessible only at an address just below 0x01000000. In PCx86,
* this second chunk of RAM must be separately allocated, with an ID of "ramCPQ".
*
* The typical configuration was 640Kb of conventional memory, leaving 384Kb accessible at 0x00FA0000.
* Presumably, the other configurations (256Kb and 512Kb) would leave 768Kb and 512Kb accessible at
* 0x00F40000 and 0x00F80000, respectively.
*
* The DeskPro 386 also contained two memory-mapped registers at 0x80C00000. The first is a write-only
* mapping register that provides the ability to map the 128Kb at 0x00FE0000 to 0x000E0000, replacing
* any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb; internally,
* this register corresponds to wMappings.
*
* The second register is a read-only diagnostics register that indicates jumper configuration and
* parity errors; internally, this register corresponds to wSettings.
*
* To emulate the memory-mapped registers at 0x80C00000, the RAM component allocates a block at that
* address using this custom controller once it sees an allocation for "ramCPQ".
*
* Later, when the addressability of "ramCPQ" memory is altered, we record the blocks in all the
* memory slots spanning 0x000E0000-0x000FFFFF, and then update those slots with the blocks from
* 0x00FE0000-0x00FFFFFF. Note that only the top 128Kb of "ramCPQ" addressability is affected; the
* rest of that memory, ranging anywhere from 256Kb to 640Kb, remains addressable at its original
* location. COMPAQ's CEMM and VDISK utilities were generally the only software able to access that
* remaining memory (what COMPAQ refers to as "Compaq Built-in Memory").
*
* @this {CompaqController}
* @param {RAM} ram
*/
constructor(ram)
{
this.ram = ram;
this.wMappings = CompaqController.MAPPINGS.DEFAULT;
/*
* TODO: wSettings needs to reflect the actual amount of configured memory....
*/
this.wSettings = CompaqController.SETTINGS.DEFAULT;
this.wRAMSetup = CompaqController.RAMSETUP.DEFAULT;
this.aBlocksDst = null;
}
/**
* save()
*
* This implements save support for the CompaqController component.
*
* @this {CompaqController}
* @return {Array}
*/
save()
{
return [this.wMappings, this.wRAMSetup];
}
/**
* restore(data)
*
* This implements restore support for the CompaqController component.
*
* @this {CompaqController}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
restore(data)
{
this.setByte(0, data[0] & 0xff);
this.setByte(2, data[1] & 0xff);
return true;
}
/**
* getByte(off)
*
* @this {CompaqController}
* @param {number} off
* @return {number}
*/
getByte(off)
{
/*
* Offsets 0-3 correspond to reads from 0x80C00000-0x80C00003; anything outside that range
* returns our standard non-responsive value of 0xff.
*/
var b = 0xff;
if (off < 0x02) {
b = (off & 0x1)? (this.wSettings >> 8) : (this.wSettings & 0xff);
}
else if (off < 0x4) {
b = (off & 0x1)? (this.wRAMSetup >> 8) : (this.wRAMSetup & 0xff);
}
return b;
}
/**
* setByte(off, b)
*
* @this {CompaqController}
* @param {number} off (relative to 0x80C00000)
* @param {number} b
*/
setByte(off, b)
{
if (!off) {
/*
* This is a write to 0x80C00000
*/
if (b != (this.wMappings & 0xff)) {
var bus = this.ram.bus;
if (!(b & CompaqController.MAPPINGS.UNMAPPED)) {
if (!this.aBlocksDst) {
this.aBlocksDst = bus.getMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE);
}
/*
* You might think that the next three lines could ALSO be moved to the preceding IF,
* but it's possible for the write-protection feature to be enabled/disabled separately
* from the mapping feature. We could avoid executing this code as well by checking the
* current read-write state, but this is an infrequent operation, so there's no point.
*/
var aBlocks = bus.getMemoryBlocks(CompaqController.MAP_SRC, CompaqController.MAP_SIZE);
var type = (b & CompaqController.MAPPINGS.READWRITE)? Memory.TYPE.RAM : Memory.TYPE.ROM;
bus.setMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE, aBlocks, type);
}
else {
if (this.aBlocksDst) {
bus.setMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE, this.aBlocksDst);
this.aBlocksDst = null;
}
}
this.wMappings = (this.wMappings & ~0xff) | b;
}
}
else if (off == 0x2) {
/*
* This is a write to 0x80C00002
*/
this.wRAMSetup = (this.wRAMSetup & ~0xff) | b;
}
}
/**
* getMemoryBuffer(addr)
*
* @this {CompaqController}
* @param {number} addr
* @return {Array} containing the buffer (and an offset within that buffer)
*/
getMemoryBuffer(addr)
{
return CompaqController.BUFFER;
}
/**
* getMemoryAccess()
*
* @this {CompaqController}
* @return {Array.<function()>}
*/
getMemoryAccess()
{
return CompaqController.ACCESS;
}
/**
* readByte(off, addr)
*
* NOTE: Even though we asked bus.addMemory() for only 4 bytes, corresponding to the 4 memory-mapped register
* locations we must manage, we're at the mercy of the Bus component's physical block allocation granularity,
* which, on 80386-based machines, is fixed at 4K (the same as the 80386 page size, to simplify emulation of paging).
*
* So we must allow for requests outside that 4-byte range.
*
* @this {Memory}
* @param {number} off (relative to 0x80C00000)
* @param {number} [addr]
* @return {number}
*/
static readByte(off, addr)
{
var b = this.controller.getByte(off);
if (DEBUG) {
this.controller.ram.printMessage("CompaqController.readByte(" + Str.toHexWord(off) + ") returned " + Str.toHexByte(b), 0, true);
}
return b;
}
/**
* writeByte(off, b, addr)
*
* NOTE: Even though we asked bus.addMemory() for only 4 bytes, corresponding to the 4 memory-mapped register
* locations we must manage, we're at the mercy of the Bus component's physical memory allocation granularity,
* which, on 80386-based machines, is fixed at 4K (the same as the 80386 page size, to simplify emulation of paging).
*
* So we must allow for requests outside that 4-byte range.
*
* @this {Memory}
* @param {number} off (relative to 0x80C00000)
* @param {number} b
* @param {number} [addr]
*/
static writeByte(off, b, addr)
{
this.controller.setByte(off, b);
/*
* All bits in 0x80C00001 and 0x80C00003 are reserved, so we can simply ignore those writes.
*/
if (DEBUG) {
this.controller.ram.printMessage("CompaqController.writeByte(" + Str.toHexWord(off) + "," + Str.toHexByte(b) + ")", 0, true);
}
}
}
CompaqController.ADDR = 0x80C00000|0;
@ -408,181 +592,12 @@ CompaqController.RAMSETUP = {
DEFAULT: 0x0002 // our default settings (ie, 2Mb, cache disabled)
};
/**
* readByte(off, addr)
*
* NOTE: Even though we asked bus.addMemory() for only 4 bytes, corresponding to the 4 memory-mapped register
* locations we must manage, we're at the mercy of the Bus component's physical block allocation granularity,
* which, on 80386-based machines, is fixed at 4K (the same as the 80386 page size, to simplify emulation of paging).
*
* So we must allow for requests outside that 4-byte range.
*
* @this {Memory}
* @param {number} off (relative to 0x80C00000)
* @param {number} [addr]
* @return {number}
*/
CompaqController.readByte = function readCompaqControllerByte(off, addr)
{
var b = this.controller.getByte(off);
if (DEBUG) {
this.controller.ram.printMessage("CompaqController.readByte(" + str.toHexWord(off) + ") returned " + str.toHexByte(b), 0, true);
}
return b;
};
/**
* writeByte(off, b, addr)
*
* NOTE: Even though we asked bus.addMemory() for only 4 bytes, corresponding to the 4 memory-mapped register
* locations we must manage, we're at the mercy of the Bus component's physical memory allocation granularity,
* which, on 80386-based machines, is fixed at 4K (the same as the 80386 page size, to simplify emulation of paging).
*
* So we must allow for requests outside that 4-byte range.
*
* @this {Memory}
* @param {number} off (relative to 0x80C00000)
* @param {number} b
* @param {number} [addr]
*/
CompaqController.writeByte = function writeCompaqControllerByte(off, b, addr)
{
this.controller.setByte(off, b);
/*
* All bits in 0x80C00001 and 0x80C00003 are reserved, so we can simply ignore those writes.
*/
if (DEBUG) {
this.controller.ram.printMessage("CompaqController.writeByte(" + str.toHexWord(off) + "," + str.toHexByte(b) + ")", 0, true);
}
};
CompaqController.BUFFER = [null, 0];
CompaqController.ACCESS = [CompaqController.readByte, null, null, CompaqController.writeByte, null, null];
/**
* save()
*
* This implements save support for the CompaqController component.
*
* @this {CompaqController}
* @return {Array}
*/
CompaqController.prototype.save = function()
{
return [this.wMappings, this.wRAMSetup];
};
/**
* restore(data)
*
* This implements restore support for the CompaqController component.
*
* @this {CompaqController}
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
CompaqController.prototype.restore = function(data)
{
this.setByte(0, data[0] & 0xff);
this.setByte(2, data[1] & 0xff);
return true;
};
/**
* getByte(off)
*
* @this {CompaqController}
* @param {number} off
* @return {number}
*/
CompaqController.prototype.getByte = function(off)
{
/*
* Offsets 0-3 correspond to reads from 0x80C00000-0x80C00003; anything outside that range
* returns our standard non-responsive value of 0xff.
*/
var b = 0xff;
if (off < 0x02) {
b = (off & 0x1)? (this.wSettings >> 8) : (this.wSettings & 0xff);
}
else if (off < 0x4) {
b = (off & 0x1)? (this.wRAMSetup >> 8) : (this.wRAMSetup & 0xff);
}
return b;
};
/**
* setByte(off, b)
*
* @this {CompaqController}
* @param {number} off (relative to 0x80C00000)
* @param {number} b
*/
CompaqController.prototype.setByte = function(off, b)
{
if (!off) {
/*
* This is a write to 0x80C00000
*/
if (b != (this.wMappings & 0xff)) {
var bus = this.ram.bus;
if (!(b & CompaqController.MAPPINGS.UNMAPPED)) {
if (!this.aBlocksDst) {
this.aBlocksDst = bus.getMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE);
}
/*
* You might think that the next three lines could ALSO be moved to the preceding IF,
* but it's possible for the write-protection feature to be enabled/disabled separately
* from the mapping feature. We could avoid executing this code as well by checking the
* current read-write state, but this is an infrequent operation, so there's no point.
*/
var aBlocks = bus.getMemoryBlocks(CompaqController.MAP_SRC, CompaqController.MAP_SIZE);
var type = (b & CompaqController.MAPPINGS.READWRITE)? Memory.TYPE.RAM : Memory.TYPE.ROM;
bus.setMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE, aBlocks, type);
}
else {
if (this.aBlocksDst) {
bus.setMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE, this.aBlocksDst);
this.aBlocksDst = null;
}
}
this.wMappings = (this.wMappings & ~0xff) | b;
}
}
else if (off == 0x2) {
/*
* This is a write to 0x80C00002
*/
this.wRAMSetup = (this.wRAMSetup & ~0xff) | b;
}
};
/**
* getMemoryBuffer(addr)
*
* @this {CompaqController}
* @param {number} addr
* @return {Array} containing the buffer (and an offset within that buffer)
*/
CompaqController.prototype.getMemoryBuffer = function(addr)
{
return CompaqController.BUFFER;
};
/**
* getMemoryAccess()
*
* @this {CompaqController}
* @return {Array.<function()>}
*/
CompaqController.prototype.getMemoryAccess = function()
{
return CompaqController.ACCESS;
};
/*
* Initialize all the RAM modules on the page.
*/
web.onInit(RAM.init);
Web.onInit(RAM.init);
if (NODE) module.exports = RAM;

View file

@ -29,99 +29,375 @@
"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 PCX86 = require("./defines");
var Memory = require("./memory");
}
/**
* ROM(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 ROM 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
* notify: ID of a component to notify once the ROM is in place (optional)
*
* 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 ROM(parmsROM)
{
Component.call(this, "ROM", parmsROM, ROM);
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 ROM extends Component {
/**
* ROM(parmsROM)
*
* [0xf0000,0xffff0000,0xffff8000]
* The ROM 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
* notify: ID of a component to notify once the ROM is in place (optional)
*
* 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 {ROM}
* @param {Object} parmsROM
*/
this.addrAlias = parmsROM['alias'];
this.sFilePath = parmsROM['file'];
constructor(parmsROM)
{
super("ROM", parmsROM);
/*
* The 'notify' property can now (as of v1.18.2) contain an array of parameters that the notified
* component (typically Video) may use as it sees fit. For example, the Video component is generally
* interested in knowing the offsets of specific font tables within the ROM, which used to be hard-coded
* when all we supported were a few specific IBM video cards, but that's no longer feasible as we move
* beyond the original handful of IBM cards.
*
* It's up to the notified component to decide how to interpret the parameters it receives, if any.
*/
this.idNotify = parmsROM['notify'];
this.aNotifyParms = null;
if (this.idNotify) {
var i = this.idNotify.indexOf('[');
if (i > 0) {
try {
this.aNotifyParms = eval(this.idNotify.substr(i));
} catch (e) {}
this.idNotify = this.idNotify.substr(0, i);
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:
*
* [0xf0000,0xffff0000,0xffff8000]
*
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
* aliased locations listed under a separate property.
*
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
*/
this.addrAlias = parmsROM['alias'];
this.sFilePath = parmsROM['file'];
/*
* The 'notify' property can now (as of v1.18.2) contain an array of parameters that the notified
* component (typically Video) may use as it sees fit. For example, the Video component is generally
* interested in knowing the offsets of specific font tables within the ROM, which used to be hard-coded
* when all we supported were a few specific IBM video cards, but that's no longer feasible as we move
* beyond the original handful of IBM cards.
*
* It's up to the notified component to decide how to interpret the parameters it receives, if any.
*/
this.idNotify = parmsROM['notify'];
this.aNotifyParms = null;
if (this.idNotify) {
var i = this.idNotify.indexOf('[');
if (i > 0) {
try {
this.aNotifyParms = eval(this.idNotify.substr(i));
} catch (e) {}
this.idNotify = this.idNotify.substr(0, i);
}
}
if (this.sFilePath) {
var sFileURL = this.sFilePath;
var sFileName = Str.getBaseName(sFileURL);
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(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);
});
}
}
if (this.sFilePath) {
var sFileURL = this.sFilePath;
var sFileName = str.getBaseName(sFileURL);
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(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';
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {ROM}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
initBus(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.copyROM();
}
/**
* powerUp(data, fRepower)
*
* @this {ROM}
* @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, 0, this.addrROM >>> 4, 0, 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 {ROM}
* @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 {ROM}
* @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 {ROM}
*/
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]);
}
/*
* If there's a component we should notify, notify it now, and give it the internal byte array, so that
* it doesn't have to ask the CPU for the data. Currently, the only component that uses this notification
* option is the Video component, and only when the associated ROM contains font data that it needs.
*/
if (this.idNotify) {
var component = Component.getComponentByID(this.idNotify, this.id);
if (component) {
component.onROMLoad(this.abROM, this.aNotifyParms);
} else {
this.notice("Unable to find component: " + this.idNotify);
}
}
/*
* 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 {ROM}
* @param {number} addr
* @return {boolean}
*/
addROM(addr)
{
if (this.bus.addMemory(addr, this.sizeROM, Memory.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + Str.toHexLong(addr) + " (" + Str.toHexLong(this.abROM.length) + " bytes)");
var bto = null;
for (var off = 0; off < this.abROM.length; off++) {
this.bus.setByteDirect(addr + off, this.abROM[off]);
if (BACKTRACK) {
bto = this.bus.addBackTrackObject(this, bto, off);
this.bus.writeBackTrackObject(addr + off, bto, off);
}
}
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 {ROM}
* @param {number} addr
*/
cloneROM(addr)
{
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
}
/**
* ROM.init()
*
* This function operates on every HTML element of class "rom", extracting the
* JSON-encoded parameters for the ROM constructor from the element's "data-value"
* attribute, invoking the constructor to create a ROM component, and then binding
* any associated HTML controls to the new component.
*/
static init()
{
var aeROM = Component.getElementsByClass(document, PCX86.APPCLASS, "rom");
for (var iROM = 0; iROM < aeROM.length; iROM++) {
var eROM = aeROM[iROM];
var parmsROM = Component.getComponentParms(eROM);
var rom = new ROM(parmsROM);
Component.bindComponentControls(rom, eROM, PCX86.APPCLASS);
}
var rom = this;
web.getResource(sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
rom.doneLoad(sURL, sResponse, nErrorCode);
});
}
}
Component.subclass(ROM);
/*
* ROM BIOS Data Area (RBDA) definitions, in physical address form, using the same ALL-CAPS names
* found in the original IBM PC ROM BIOS listing. TODO: Fill in remaining RBDA holes.
@ -152,279 +428,9 @@ ROM.BIOS.RESET_FLAG_WARMBOOT = 0x1234; // value stored at ROM.BIOS.RESET_FLAG t
* via bus.addMemory().
*/
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {ROM}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {DebuggerX86} dbg
*/
ROM.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.copyROM();
};
/**
* powerUp(data, fRepower)
*
* @this {ROM}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
ROM.prototype.powerUp = function(data, fRepower)
{
if (this.aSymbols) {
if (this.dbg) {
this.dbg.addSymbols(this.id, 0, this.addrROM >>> 4, 0, 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 {ROM}
* @param {boolean} [fSave]
* @param {boolean} [fShutdown]
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
*/
ROM.prototype.powerDown = function(fSave, fShutdown)
{
return true;
};
/**
* doneLoad(sURL, sROMData, nErrorCode)
*
* @this {ROM}
* @param {string} sURL
* @param {string} sROMData
* @param {number} nErrorCode (response from server if anything other than 200)
*/
ROM.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 {ROM}
*/
ROM.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]);
}
/*
* If there's a component we should notify, notify it now, and give it the internal byte array, so that
* it doesn't have to ask the CPU for the data. Currently, the only component that uses this notification
* option is the Video component, and only when the associated ROM contains font data that it needs.
*/
if (this.idNotify) {
var component = Component.getComponentByID(this.idNotify, this.id);
if (component) {
component.onROMLoad(this.abROM, this.aNotifyParms);
} else {
this.notice("Unable to find component: " + this.idNotify);
}
}
/*
* 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 {ROM}
* @param {number} addr
* @return {boolean}
*/
ROM.prototype.addROM = function(addr)
{
if (this.bus.addMemory(addr, this.sizeROM, Memory.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abROM.length) + " bytes)");
var bto = null;
for (var off = 0; off < this.abROM.length; off++) {
this.bus.setByteDirect(addr + off, this.abROM[off]);
if (BACKTRACK) {
bto = this.bus.addBackTrackObject(this, bto, off);
this.bus.writeBackTrackObject(addr + off, bto, off);
}
}
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 {ROM}
* @param {number} addr
*/
ROM.prototype.cloneROM = function(addr)
{
var aBlocks = this.bus.getMemoryBlocks(this.addrROM, this.sizeROM);
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
};
/**
* ROM.init()
*
* This function operates on every HTML element of class "rom", extracting the
* JSON-encoded parameters for the ROM constructor from the element's "data-value"
* attribute, invoking the constructor to create a ROM component, and then binding
* any associated HTML controls to the new component.
*/
ROM.init = function()
{
var aeROM = Component.getElementsByClass(document, PCX86.APPCLASS, "rom");
for (var iROM = 0; iROM < aeROM.length; iROM++) {
var eROM = aeROM[iROM];
var parmsROM = Component.getComponentParms(eROM);
var rom = new ROM(parmsROM);
Component.bindComponentControls(rom, eROM, PCX86.APPCLASS);
}
};
/*
* Initialize all the ROM modules on the page.
*/
web.onInit(ROM.init);
Web.onInit(ROM.init);
if (NODE) module.exports = ROM;

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

View file

@ -29,7 +29,6 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var Messages = require("./messages");
var X86 = require("./x86");
}

View file

@ -29,7 +29,7 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var Str = require("../../shared/es6/strlib");
var Messages = require("./messages");
var X86 = require("./x86");
}
@ -968,7 +968,7 @@ X86.helpCheckFault = function(nFault, nError, fHalt)
if (this.messageEnabled(bitsMessage) || fHalt) {
var fRunning = this.flags.running;
var sMessage = "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode);
var sMessage = "Fault " + Str.toHexByte(nFault) + (nError != null? " (" + Str.toHexWord(nError) + ")" : "") + " on opcode " + Str.toHexByte(bOpcode);
if (fHalt && fRunning) sMessage += " (blocked)";
if (DEBUGGER && this.dbg) {

View file

@ -29,7 +29,7 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var Str = require("../../shared/es6/strlib");
var X86 = require("./x86");
}
@ -196,7 +196,7 @@ X86.modRegByte16 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegByte16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegByte16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -404,7 +404,7 @@ X86.modMemByte16 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemByte16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemByte16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -529,7 +529,7 @@ X86.modMemByte16 = function(fn)
if (BACKTRACK) this.backTrack.btiBH = this.backTrack.btiEALo;
break;
default:
this.assert(false, "modMemByte16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemByte16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
};
@ -668,7 +668,7 @@ X86.modGrpByte16 = function(afnGrp, fnSrc) {
break;
default:
dst = 0;
this.assert(false, "modGrpByte16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpByte16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -907,7 +907,7 @@ X86.modRegShort16 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegShort16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegShort16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -1128,7 +1128,7 @@ X86.modMemShort16 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemShort16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemShort16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -1282,7 +1282,7 @@ X86.modMemShort16 = function(fn)
}
break;
default:
this.assert(false, "modMemShort16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemShort16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
};
@ -1421,7 +1421,7 @@ X86.modGrpShort16 = function(afnGrp, fnSrc) {
break;
default:
dst = 0;
this.assert(false, "modGrpShort16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpShort16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -1660,7 +1660,7 @@ X86.modRegLong16 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegLong16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegLong16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -1881,7 +1881,7 @@ X86.modMemLong16 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemLong16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemLong16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -2035,7 +2035,7 @@ X86.modMemLong16 = function(fn)
}
break;
default:
this.assert(false, "modMemLong16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemLong16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
};
@ -2173,7 +2173,7 @@ X86.modGrpLong16 = function(afnGrp, fnSrc) {
dst = this.regEDI;
break;
default:
this.assert(false, "modGrpLong16(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpLong16(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -2372,7 +2372,7 @@ X86.modRegByte32 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegByte32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegByte32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -2580,7 +2580,7 @@ X86.modMemByte32 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemByte32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemByte32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -2799,7 +2799,7 @@ X86.modGrpByte32 = function(afnGrp, fnSrc) {
break;
default:
dst = 0;
this.assert(false, "modGrpByte32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpByte32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -2972,7 +2972,7 @@ X86.modRegShort32 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegShort32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegShort32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -3193,7 +3193,7 @@ X86.modMemShort32 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemShort32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemShort32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -3441,7 +3441,7 @@ X86.modGrpShort32 = function(afnGrp, fnSrc) {
break;
default:
dst = 0;
this.assert(false, "modGrpShort32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpShort32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -3614,7 +3614,7 @@ X86.modRegLong32 = function(fn)
break;
default:
src = 0;
this.assert(false, "modRegLong32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modRegLong32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -3835,7 +3835,7 @@ X86.modMemLong32 = function(fn)
break;
default:
dst = 0;
this.assert(false, "modMemLong32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modMemLong32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}
@ -4083,7 +4083,7 @@ X86.modGrpLong32 = function(afnGrp, fnSrc) {
break;
default:
dst = 0;
this.assert(false, "modGrpLong32(): unrecognized modrm byte " + str.toHexByte(bModRM));
this.assert(false, "modGrpLong32(): unrecognized modrm byte " + Str.toHexByte(bModRM));
break;
}

View file

@ -29,7 +29,7 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var Str = require("../../shared/es6/strlib");
var Messages = require("./messages");
var X86 = require("./x86");
}
@ -3564,7 +3564,7 @@ X86.opINTn = function()
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to V86-mode.
*/
if (I386 && (this.regPS & X86.PS.VM) && this.nIOPL < 3) {
if (DEBUG && this.messageEnabled()) this.printMessage("INT " + str.toHexByte(nInt) + " in v86-mode (IOPL < 3)", true, true);
if (DEBUG && this.messageEnabled()) this.printMessage("INT " + Str.toHexByte(nInt) + " in v86-mode (IOPL < 3)", true, true);
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
@ -4415,7 +4415,7 @@ X86.opInvalid = function()
X86.opUndefined = function()
{
this.setIP(this.opLIP - this.segCS.base);
this.setError("Undefined opcode " + str.toHexByte(this.getByte(this.regLIP)) + " at " + str.toHexLong(this.regLIP));
this.setError("Undefined opcode " + Str.toHexByte(this.getByte(this.regLIP)) + " at " + Str.toHexLong(this.regLIP));
this.stopCPU();
};

File diff suppressed because it is too large Load diff