Make memory state platform-independent
This commit is contained in:
parent
53578f128d
commit
04b9473a18
9 changed files with 924 additions and 912 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -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];
|
||||
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.
|
||||
*
|
||||
* 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) {
|
||||
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]
|
||||
|
|
@ -695,7 +697,10 @@ 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue