Make memory state platform-independent

This commit is contained in:
Jeff Parsons 2014-10-12 10:09:55 -07:00 committed by jeffpar
commit 04b9473a18
9 changed files with 924 additions and 912 deletions

View file

@ -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.
*

View 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.
*

View file

@ -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;

View file

@ -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