Make memory state platform-independent
This commit is contained in:
parent
53578f128d
commit
04b9473a18
9 changed files with 924 additions and 912 deletions
|
|
@ -147,7 +147,6 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
return;
|
||||
}
|
||||
this.dbg = Component.getComponentByType("Debugger", this.id);
|
||||
this.panel = Component.getComponentByType("Panel", this.id);
|
||||
|
||||
/*
|
||||
* Initialize the Bus component
|
||||
|
|
@ -155,25 +154,33 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
this.bus = new Bus({'id': this.idMachine + '.bus', 'buswidth': this.nBusWidth}, this.cpu, this.dbg);
|
||||
|
||||
/*
|
||||
* Iterate through all the other components and call their initBus() handler, if any
|
||||
* Iterate through all the components and connect them to the Control Panel, if any
|
||||
*/
|
||||
var iComponent, component;
|
||||
var aComponents = Component.getComponents(this.id);
|
||||
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
|
||||
var component = aComponents[iComponent];
|
||||
/*
|
||||
* I can think of many "cleaner" ways for the Control Panel component to pass its
|
||||
* notice(), println(), etc, overrides on to all the other components, but it's just
|
||||
* too darn convenient to slam those overrides into the components directly.
|
||||
*
|
||||
* Adding more initBus() parameters was another option, but that function is already
|
||||
* looking a bit unwieldy, and Control Panel functionality is a little far afield
|
||||
* from Bus initialization.
|
||||
*/
|
||||
if (this.panel && this.panel.controlPrint) {
|
||||
this.panel = Component.getComponentByType("Panel", this.id);
|
||||
|
||||
if (this.panel && this.panel.controlPrint) {
|
||||
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
|
||||
component = aComponents[iComponent];
|
||||
/*
|
||||
* I can think of many "cleaner" ways for the Control Panel component to pass its
|
||||
* notice(), println(), etc, overrides on to all the other components, but it's just
|
||||
* too darn convenient to slam those overrides into the components directly.
|
||||
*/
|
||||
component.notice = this.panel.notice;
|
||||
component.println = this.panel.println;
|
||||
component.controlPrint = this.panel.controlPrint;
|
||||
}
|
||||
}
|
||||
|
||||
this.println("PREFETCH: " + PREFETCH + ", TYPEDARRAYS: " + TYPEDARRAYS);
|
||||
|
||||
/*
|
||||
* Iterate through all the components again and call their initBus() handler, if any
|
||||
*/
|
||||
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
|
||||
component = aComponents[iComponent];
|
||||
if (component.initBus) component.initBus(this, this.bus, this.cpu, this.dbg);
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +195,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
}
|
||||
|
||||
/*
|
||||
* 'state' is a new property that allows a state file to be specified independent of the 'resume' feature;
|
||||
* The Computer 'state' property allows a state file to be specified independent of the 'resume' feature;
|
||||
* previously, you could only use 'resume' to load a state file -- which we still support, but loading a state
|
||||
* file that way prevents the machine's state from being saved, since we always resume from the 'resume' file.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ var FATARRAYS = false;
|
|||
|
||||
/**
|
||||
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time * option, but since I've
|
||||
* added memory access functions for typed arrays (see Memory.afnTArray), I can turn the support on dynamically now.
|
||||
* added memory access functions for typed arrays (see Memory.afnTypedArray), I can turn the support on dynamically now.
|
||||
* Originally, I didn't see much of a speed increase over the original (non-typed) implementation, but that will probably
|
||||
* change over time.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -55,8 +55,12 @@ if (typeof module !== 'undefined') {
|
|||
}
|
||||
/**
|
||||
* @class DataView
|
||||
* @property {function(number,boolean):number} getUint8
|
||||
* @property {function(number,number,boolean)} setUint8
|
||||
* @property {function(number,boolean):number} getUint16
|
||||
* @property {function(number,number,boolean)} setUint16
|
||||
* @property {function(number,boolean):number} getInt32
|
||||
* @property {function(number,number,boolean)} setInt32
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -130,13 +134,16 @@ function Memory(addr, size, fReadOnly, controller) {
|
|||
*/
|
||||
if (TYPEDARRAYS) {
|
||||
this.buffer = new window.ArrayBuffer(size);
|
||||
this.ab = new window.Uint8Array(this.buffer, 0, size);
|
||||
/**
|
||||
* @type {DataView}
|
||||
*/
|
||||
this.dv = new window.DataView(this.buffer, 0, size);
|
||||
this.adw = new window.Int32Array(this.buffer, 0, size >> 2);
|
||||
this.setAccess(Memory.afnTArray);
|
||||
/*
|
||||
* We could also use dv.getUint8() and dv.setUint8(), but using ab[] to get/set bytes
|
||||
* in this.buffer is more convenient and presents no "endianness" issues.
|
||||
*/
|
||||
this.ab = new window.Uint8Array(this.buffer, 0, size);
|
||||
this.setAccess(Memory.afnTypedArray);
|
||||
} else {
|
||||
if (FATARRAYS) {
|
||||
this.ab = new Array(size);
|
||||
|
|
@ -172,47 +179,47 @@ Memory.prototype = {
|
|||
writeNone: function(off, v) {
|
||||
},
|
||||
/**
|
||||
* readByteTArray(off)
|
||||
* readByteTypedArray(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
readByteTArray: function(off) {
|
||||
readByteTypedArray: function(off) {
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
return this.ab[off];
|
||||
},
|
||||
/**
|
||||
* readWordTArray(off)
|
||||
* readWordTypedArray(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
readWordTArray: function(off) {
|
||||
readWordTypedArray: function(off) {
|
||||
Component.assert(off >= 0 && off < this.cb - 1);
|
||||
return this.dv.getUint16(off, true);
|
||||
},
|
||||
/**
|
||||
* writeByteTArray(off, b)
|
||||
* writeByteTypedArray(off, b)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} b
|
||||
*/
|
||||
writeByteTArray: function(off, b) {
|
||||
writeByteTypedArray: function(off, b) {
|
||||
Component.assert(off >= 0 && off < this.cb && (b & 0xff) == b);
|
||||
this.ab[off] = b;
|
||||
this.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeWordTArray(off, w)
|
||||
* writeWordTypedArray(off, w)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
*/
|
||||
writeWordTArray: function(off, w) {
|
||||
writeWordTypedArray: function(off, w) {
|
||||
Component.assert(off >= 0 && off < this.cb - 1 && (w & 0xffff) == w);
|
||||
this.dv.setUint16(off, w, true);
|
||||
this.fDirty = true;
|
||||
|
|
@ -229,7 +236,7 @@ Memory.prototype = {
|
|||
if (FATARRAYS) {
|
||||
return this.ab[off];
|
||||
}
|
||||
return ((this.adw[off >> 2] >> ((off & 0x3) << 3)) & 0xff);
|
||||
return ((this.adw[off >> 2] >>> ((off & 0x3) << 3)) & 0xff);
|
||||
},
|
||||
/**
|
||||
* readWordMemory(off)
|
||||
|
|
@ -246,7 +253,7 @@ Memory.prototype = {
|
|||
var w;
|
||||
var idw = off >> 2;
|
||||
var nShift = (off & 0x3) << 3;
|
||||
var dw = (this.adw[idw] >> nShift);
|
||||
var dw = (this.adw[idw] >>> nShift);
|
||||
if (nShift < 24) {
|
||||
w = dw & 0xffff;
|
||||
} else {
|
||||
|
|
@ -317,9 +324,6 @@ Memory.prototype = {
|
|||
*/
|
||||
readWordVerify: function(off) {
|
||||
if (DEBUGGER) {
|
||||
/*
|
||||
* Shut up, JSHint -- I don't need to make the second call if the first returned true.
|
||||
*/
|
||||
this.dbg.checkMemoryRead(this.addr + off) || this.dbg.checkMemoryRead(this.addr + off + 1); // jshint ignore:line
|
||||
}
|
||||
return this.readWordDirect(off);
|
||||
|
|
@ -344,9 +348,6 @@ Memory.prototype = {
|
|||
*/
|
||||
writeWordVerify: function(off, w) {
|
||||
if (DEBUGGER) {
|
||||
/*
|
||||
* Shut up, JSHint -- I don't need to make the second call if the first returned true.
|
||||
*/
|
||||
this.dbg.checkMemoryWrite(this.addr + off) || this.dbg.checkMemoryWrite(this.addr + off + 1); // jshint ignore:line
|
||||
}
|
||||
this.writeWordDirect(off, w);
|
||||
|
|
@ -378,19 +379,18 @@ Memory.prototype = {
|
|||
}
|
||||
else if (TYPEDARRAYS) {
|
||||
/*
|
||||
* While it might seem that we could get away with returning "this.adw", the fact that
|
||||
* it's a Int32Array rather than a normal Array causes problems with the way JSON.stringify()
|
||||
* and JSON.parse() interpret these buffers in State.store() and State.parse(): basically, the
|
||||
* buffers are deserialized as Objects rather than Arrays, so they lack a "length" property,
|
||||
* and then we get confused.
|
||||
* It might be tempting to just return a copy of Int32Array(this.buffer, 0, this.cb >> 2),
|
||||
* but we can't be sure of the "endianness" of an Int32Array -- which would be OK if the array
|
||||
* was always saved/restored on the same machine, but there's no guarantee of that, either.
|
||||
* So we use getInt32() and require little-endian values.
|
||||
*
|
||||
* Rather than trying to solve that problem on the deserialization side, we solve it here by
|
||||
* ensuring the caller always gets an Array (which also ensures consistency in our serialization
|
||||
* format).
|
||||
* Moreover, an Int32Array isn't treated by JSON.stringify() and JSON.parse() exactly like
|
||||
* a normal array; it's serialized as an Object rather than an Array, so it lacks a "length"
|
||||
* property and causes problems for State.store() and State.parse().
|
||||
*/
|
||||
adw = new Array(this.cb >> 2);
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
adw[i] = this.adw[i];
|
||||
adw[i] = this.dv.getInt32(i << 2, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -426,8 +426,8 @@ Memory.prototype = {
|
|||
off += 4;
|
||||
}
|
||||
} else if (TYPEDARRAYS) {
|
||||
for (i = 0; i < this.adw.length; i++) {
|
||||
this.adw[i] = adw[i];
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
this.dv.setInt32(i << 2, adw[i], true);
|
||||
}
|
||||
} else {
|
||||
this.adw = adw;
|
||||
|
|
@ -570,7 +570,7 @@ Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readWordMe
|
|||
Memory.afnVerify = [Memory.prototype.readByteVerify, Memory.prototype.readWordVerify, Memory.prototype.writeByteVerify, Memory.prototype.writeWordVerify];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
Memory.afnTArray = [Memory.prototype.readByteTArray, Memory.prototype.readWordTArray, Memory.prototype.writeByteTArray, Memory.prototype.writeWordTArray];
|
||||
Memory.afnTypedArray = [Memory.prototype.readByteTypedArray, Memory.prototype.readWordTypedArray, Memory.prototype.writeByteTypedArray, Memory.prototype.writeWordTypedArray];
|
||||
}
|
||||
|
||||
if (typeof APP_PCJS !== 'undefined') APP_PCJS.Memory = Memory;
|
||||
|
|
|
|||
|
|
@ -281,7 +281,11 @@ Component.assert = function(f, s)
|
|||
/**
|
||||
* Component.println(s, type, id)
|
||||
*
|
||||
* For non-diagnostic output, which some components override in order to make their output visible in their own way.
|
||||
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
|
||||
*
|
||||
* Components that inherit from this class should use the instance method, this.println(), rather than Component.println(),
|
||||
* because if a Control Panel is loaded, it will override only the instance method, not the class method (overriding the class
|
||||
* method would improperly affect any other machines loaded on the same page).
|
||||
*
|
||||
* @param {string} [s] is the message text
|
||||
* @param {string} [type] is the message type
|
||||
|
|
@ -297,9 +301,7 @@ Component.println = function(s, type, id)
|
|||
/**
|
||||
* Component.notice(s, fPrintOnly, id)
|
||||
*
|
||||
* notice() is like println() but implies a need for user notification, which means calling log() isn't good enough,
|
||||
* so we alert() as well; however, if Component.println() is overridden, Component.notice will be replaced with the same
|
||||
* override, on the assumption that the override is taking care of all user notifications.
|
||||
* notice() is like println() but implies a need for user notification, so we alert() as well.
|
||||
*
|
||||
* @param {string} s is the message text
|
||||
* @param {boolean} [fPrintOnly]
|
||||
|
|
@ -694,9 +696,12 @@ Component.prototype = {
|
|||
},
|
||||
/**
|
||||
* println(s, type)
|
||||
*
|
||||
* For non-diagnostic output, which some components override in order to make their output visible in their own way.
|
||||
*
|
||||
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
|
||||
*
|
||||
* Components using this.println() should wait until after their constructor has run to display any messages, because
|
||||
* if a Control Panel has been loaded, its override will not take effect until its own constructor has run.
|
||||
*
|
||||
* @this {Component}
|
||||
* @param {string} [s] is the message text
|
||||
* @param {string} [type] is the message type
|
||||
|
|
@ -719,9 +724,9 @@ Component.prototype = {
|
|||
/**
|
||||
* notice(s, fPrintOnly)
|
||||
*
|
||||
* notice() is like println() but implies a need for user notification, which means calling log() isn't good enough,
|
||||
* so we alert() as well; however, if Component.println() is overridden, Component.notice will be replaced with the
|
||||
* same override, on the assumption that the override is taking care of alerting the user.
|
||||
* notice() is like println() but implies a need for user notification, so we alert() as well; however, if this.println()
|
||||
* is overridden, this.notice will be replaced with a similar override, on the assumption that the override is taking care
|
||||
* of alerting the user.
|
||||
*
|
||||
* @this {Component}
|
||||
* @param {string} s is the message text
|
||||
|
|
|
|||
Loading…
Reference in a new issue