Added optimized memory handlers for paged memory
This commit is contained in:
parent
ec16cf78be
commit
d3a8160ad8
3 changed files with 182 additions and 82 deletions
|
|
@ -7670,7 +7670,7 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.println((APPNAME || "PCjs") + " version " + (XMLVERSION || APPVERSION) + " (" + this.cpu.model + (COMPILED? ",RELEASE" : (DEBUG? ",DEBUG" : ",NODEBUG")) + (PREFETCH? ",PREFETCH" : ",NOPREFETCH") + (TYPEDARRAYS? ",TYPEDARRAYS" : (FATARRAYS? ",FATARRAYS" : ",LONGARRAYS")) + (BACKTRACK? ",BACKTRACK" : ",NOBACKTRACK") + ')');
|
this.println((APPNAME || "PCjs") + " version " + (XMLVERSION || APPVERSION) + " (" + this.cpu.model + (COMPILED? ",RELEASE" : (DEBUG? ",DEBUG" : ",NODEBUG")) + (PREFETCH? ",PREFETCH" : ",NOPREFETCH") + (TYPEDARRAYS? ",TYPEDARRAYS" : (BYTEARRAYS? ",BYTEARRAYS" : ",LONGARRAYS")) + (BACKTRACK? ",BACKTRACK" : ",NOBACKTRACK") + ')');
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
this.doExecOptions(asArgs);
|
this.doExecOptions(asArgs);
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,12 @@ var PREFETCH = false;
|
||||||
/**
|
/**
|
||||||
* @define {boolean}
|
* @define {boolean}
|
||||||
*
|
*
|
||||||
* FATARRAYS is a Closure Compiler compile-time option that allocates an Array of numbers for every Memory block,
|
* BYTEARRAYS is a Closure Compiler compile-time option that allocates an Array of numbers for every Memory block,
|
||||||
* where each a number represents ONE byte; very wasteful, but potentially slightly faster.
|
* where each a number represents ONE byte; very wasteful, but potentially slightly faster.
|
||||||
*
|
*
|
||||||
* See the Memory component for details.
|
* See the Memory component for details.
|
||||||
*/
|
*/
|
||||||
var FATARRAYS = false;
|
var BYTEARRAYS = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time-only option, but I've
|
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time-only option, but I've
|
||||||
|
|
@ -147,7 +147,7 @@ if (NODE) {
|
||||||
global.PCJSCLASS = PCJSCLASS;
|
global.PCJSCLASS = PCJSCLASS;
|
||||||
global.DEBUGGER = DEBUGGER;
|
global.DEBUGGER = DEBUGGER;
|
||||||
global.PREFETCH = PREFETCH;
|
global.PREFETCH = PREFETCH;
|
||||||
global.FATARRAYS = FATARRAYS;
|
global.BYTEARRAYS = BYTEARRAYS;
|
||||||
global.TYPEDARRAYS = TYPEDARRAYS;
|
global.TYPEDARRAYS = TYPEDARRAYS;
|
||||||
global.BACKTRACK = BACKTRACK;
|
global.BACKTRACK = BACKTRACK;
|
||||||
global.SYMBOLS = SYMBOLS;
|
global.SYMBOLS = SYMBOLS;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
||||||
* (LONGARRAYS) and shifting/masking bytes/words to/from dwords; in theory, byte accesses would
|
* (LONGARRAYS) and shifting/masking bytes/words to/from dwords; in theory, byte accesses would
|
||||||
* be faster and word accesses somewhat less faster.
|
* be faster and word accesses somewhat less faster.
|
||||||
*
|
*
|
||||||
* However, preliminary testing of that feature (FATARRAYS) did not yield significantly faster
|
* However, preliminary testing of that feature (BYTEARRAYS) did not yield significantly faster
|
||||||
* performance, so it is OFF by default to minimize our memory consumption. Using TYPEDARRAYS
|
* performance, so it is OFF by default to minimize our memory consumption. Using TYPEDARRAYS
|
||||||
* would seem best, but as discussed in defines.js, it's off by default, because it doesn't perform
|
* would seem best, but as discussed in defines.js, it's off by default, because it doesn't perform
|
||||||
* as well as LONGARRAYS; the other advantage of TYPEDARRAYS is that it should theoretically use
|
* as well as LONGARRAYS; the other advantage of TYPEDARRAYS is that it should theoretically use
|
||||||
|
|
@ -110,7 +110,6 @@ function Memory(addr, used, size, type, controller, cpu)
|
||||||
this.controller = null;
|
this.controller = null;
|
||||||
this.cpu = cpu; // if a CPU reference is provided, then this must be an UNPAGED Memory block allocation
|
this.cpu = cpu; // if a CPU reference is provided, then this must be an UNPAGED Memory block allocation
|
||||||
this.fDirty = this.fDirtyEver = false;
|
this.fDirty = this.fDirtyEver = false;
|
||||||
this.setPhysBlock();
|
|
||||||
this.copyBreakpoints(); // initialize the block's Debugger info (eg, breakpoint totals); the caller will reinitialize
|
this.copyBreakpoints(); // initialize the block's Debugger info (eg, breakpoint totals); the caller will reinitialize
|
||||||
|
|
||||||
if (BACKTRACK) {
|
if (BACKTRACK) {
|
||||||
|
|
@ -170,13 +169,13 @@ function Memory(addr, used, size, type, controller, cpu)
|
||||||
this.ab = new Uint8Array(this.buffer, 0, size);
|
this.ab = new Uint8Array(this.buffer, 0, size);
|
||||||
this.aw = new Uint16Array(this.buffer, 0, size >> 1);
|
this.aw = new Uint16Array(this.buffer, 0, size >> 1);
|
||||||
this.adw = new Int32Array(this.buffer, 0, size >> 2);
|
this.adw = new Int32Array(this.buffer, 0, size >> 2);
|
||||||
this.setAccess(littleEndian? Memory.afnLittleEndian : Memory.afnBigEndian);
|
this.setAccess(littleEndian? Memory.afnArrayLE : Memory.afnArrayBE);
|
||||||
} else {
|
} else {
|
||||||
if (FATARRAYS) {
|
if (BYTEARRAYS) {
|
||||||
this.ab = new Array(size);
|
this.ab = new Array(size);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* NOTE: This is the default mode of operation (!TYPEDARRAYS && !FATARRAYS), because it
|
* NOTE: This is the default mode of operation (!TYPEDARRAYS && !BYTEARRAYS), because it
|
||||||
* seems to provide the best performance; and although in theory, that performance might
|
* seems to provide the best performance; and although in theory, that performance might
|
||||||
* come at twice the overhead of TYPEDARRAYS, it's increasingly likely that the JavaScript
|
* come at twice the overhead of TYPEDARRAYS, it's increasingly likely that the JavaScript
|
||||||
* runtime will notice that all we ever store are 32-bit values, and optimize accordingly.
|
* runtime will notice that all we ever store are 32-bit values, and optimize accordingly.
|
||||||
|
|
@ -287,9 +286,9 @@ Memory.prototype = {
|
||||||
this.ab = mem.ab;
|
this.ab = mem.ab;
|
||||||
this.aw = mem.aw;
|
this.aw = mem.aw;
|
||||||
this.adw = mem.adw;
|
this.adw = mem.adw;
|
||||||
this.setAccess(littleEndian? Memory.afnLittleEndian : Memory.afnBigEndian);
|
this.setAccess(littleEndian? Memory.afnArrayLE : Memory.afnArrayBE);
|
||||||
} else {
|
} else {
|
||||||
if (FATARRAYS) {
|
if (BYTEARRAYS) {
|
||||||
this.ab = mem.ab;
|
this.ab = mem.ab;
|
||||||
} else {
|
} else {
|
||||||
this.adw = mem.adw;
|
this.adw = mem.adw;
|
||||||
|
|
@ -315,7 +314,7 @@ Memory.prototype = {
|
||||||
if (this.controller) {
|
if (this.controller) {
|
||||||
adw = null;
|
adw = null;
|
||||||
}
|
}
|
||||||
else if (FATARRAYS) {
|
else if (BYTEARRAYS) {
|
||||||
adw = new Array(this.size >> 2);
|
adw = new Array(this.size >> 2);
|
||||||
var off = 0;
|
var off = 0;
|
||||||
for (i = 0; i < adw.length; i++) {
|
for (i = 0; i < adw.length; i++) {
|
||||||
|
|
@ -367,11 +366,11 @@ Memory.prototype = {
|
||||||
* memory blocks installed at the programmed address, and so we arrived here at a block with
|
* memory blocks installed at the programmed address, and so we arrived here at a block with
|
||||||
* no controller AND no data.
|
* no controller AND no data.
|
||||||
*/
|
*/
|
||||||
// DEBUG: Component.assert(adw != null);
|
Component.assert(adw != null);
|
||||||
|
|
||||||
if (adw && this.size == adw.length << 2) {
|
if (adw && this.size == adw.length << 2) {
|
||||||
var i;
|
var i;
|
||||||
if (FATARRAYS) {
|
if (BYTEARRAYS) {
|
||||||
var off = 0;
|
var off = 0;
|
||||||
for (i = 0; i < adw.length; i++) {
|
for (i = 0; i < adw.length; i++) {
|
||||||
this.ab[off] = adw[i] & 0xff;
|
this.ab[off] = adw[i] & 0xff;
|
||||||
|
|
@ -399,7 +398,7 @@ Memory.prototype = {
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {Array.<function()>} [afn] function table
|
* @param {Array.<function()>} [afn] function table
|
||||||
* @param {boolean} [fDirect]
|
* @param {boolean} [fDirect] (true to update direct access functions as well; default is true)
|
||||||
*/
|
*/
|
||||||
setAccess: function(afn, fDirect) {
|
setAccess: function(afn, fDirect) {
|
||||||
if (!afn) {
|
if (!afn) {
|
||||||
|
|
@ -495,20 +494,32 @@ Memory.prototype = {
|
||||||
* setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE)
|
* setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {Memory|null} [blockPhys]
|
* @param {Memory} blockPhys
|
||||||
* @param {Memory|null} [blockPDE]
|
* @param {Memory} blockPDE
|
||||||
* @param {number} [offPDE]
|
* @param {number} offPDE
|
||||||
* @param {Memory|null} [blockPTE]
|
* @param {Memory} blockPTE
|
||||||
* @param {number} [offPTE]
|
* @param {number} offPTE
|
||||||
*/
|
*/
|
||||||
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
|
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
|
||||||
this.blockPhys = blockPhys;
|
|
||||||
this.blockPDE = blockPDE;
|
this.blockPDE = blockPDE;
|
||||||
this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index)
|
this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index)
|
||||||
this.blockPTE = blockPTE;
|
this.blockPTE = blockPTE;
|
||||||
this.iPTE = offPTE >> 2; // convert offPTE into iPTE (an adw index)
|
this.iPTE = offPTE >> 2; // convert offPTE into iPTE (an adw index)
|
||||||
this.bitPTEDirty = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY) : 0;
|
/*
|
||||||
this.bitPTEAccessed = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED) : 0;
|
* This is an optimization for "normal" pages, installing paged memory handlers that mimic
|
||||||
|
* normal memory but also know how to update page tables. If any of the criteria are not met
|
||||||
|
* for these special handlers, we fall back to the slower default "paged" memory handlers.
|
||||||
|
*/
|
||||||
|
if (TYPEDARRAYS && littleEndian && blockPhys.adw && !blockPhys.controller) {
|
||||||
|
this.ab = blockPhys.ab;
|
||||||
|
this.aw = blockPhys.aw;
|
||||||
|
this.adw = blockPhys.adw;
|
||||||
|
this.setAccess(Memory.afnPagedLE);
|
||||||
|
} else {
|
||||||
|
this.blockPhys = blockPhys;
|
||||||
|
this.bitPTEAccessed = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED) : 0;
|
||||||
|
this.bitPTEDirty = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY) : 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* printAddr(sMessage)
|
* printAddr(sMessage)
|
||||||
|
|
@ -671,7 +682,6 @@ Memory.prototype = {
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeShortDefault: function writeShortDefault(off, w, addr) {
|
writeShortDefault: function writeShortDefault(off, w, addr) {
|
||||||
// DEBUG: Component.assert(!(w & ~0xffff));
|
|
||||||
this.writeByte(off++, w & 0xff, addr++);
|
this.writeByte(off++, w & 0xff, addr++);
|
||||||
this.writeByte(off, w >> 8, addr);
|
this.writeByte(off, w >> 8, addr);
|
||||||
},
|
},
|
||||||
|
|
@ -698,8 +708,7 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readByteMemory: function readByteMemory(off, addr) {
|
readByteMemory: function readByteMemory(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
return this.ab[off];
|
return this.ab[off];
|
||||||
}
|
}
|
||||||
return ((this.adw[off >> 2] >>> ((off & 0x3) << 3)) & 0xff);
|
return ((this.adw[off >> 2] >>> ((off & 0x3) << 3)) & 0xff);
|
||||||
|
|
@ -713,8 +722,7 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readShortMemory: function readShortMemory(off, addr) {
|
readShortMemory: function readShortMemory(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
return this.ab[off] | (this.ab[off + 1] << 8);
|
return this.ab[off] | (this.ab[off + 1] << 8);
|
||||||
}
|
}
|
||||||
var w;
|
var w;
|
||||||
|
|
@ -737,8 +745,7 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readLongMemory: function readLongMemory(off, addr) {
|
readLongMemory: function readLongMemory(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
return this.ab[off] | (this.ab[off + 1] << 8) | (this.ab[off + 2] << 16) | (this.ab[off + 3] << 24);
|
return this.ab[off] | (this.ab[off + 1] << 8) | (this.ab[off + 2] << 16) | (this.ab[off + 3] << 24);
|
||||||
}
|
}
|
||||||
var idw = off >> 2;
|
var idw = off >> 2;
|
||||||
|
|
@ -759,8 +766,7 @@ Memory.prototype = {
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeByteMemory: function writeByteMemory(off, b, addr) {
|
writeByteMemory: function writeByteMemory(off, b, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size && (b & 0xff) == b);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
this.ab[off] = b;
|
this.ab[off] = b;
|
||||||
} else {
|
} else {
|
||||||
var idw = off >> 2;
|
var idw = off >> 2;
|
||||||
|
|
@ -778,8 +784,7 @@ Memory.prototype = {
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeShortMemory: function writeShortMemory(off, w, addr) {
|
writeShortMemory: function writeShortMemory(off, w, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1 && (w & 0xffff) == w);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
this.ab[off] = (w & 0xff);
|
this.ab[off] = (w & 0xff);
|
||||||
this.ab[off + 1] = (w >> 8);
|
this.ab[off + 1] = (w >> 8);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -804,8 +809,7 @@ Memory.prototype = {
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeLongMemory: function writeLongMemory(off, l, addr) {
|
writeLongMemory: function writeLongMemory(off, l, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
if (BYTEARRAYS) {
|
||||||
if (FATARRAYS) {
|
|
||||||
this.ab[off] = (l & 0xff);
|
this.ab[off] = (l & 0xff);
|
||||||
this.ab[off + 1] = (l >> 8) & 0xff;
|
this.ab[off + 1] = (l >> 8) & 0xff;
|
||||||
this.ab[off + 2] = (l >> 16) & 0xff;
|
this.ab[off + 2] = (l >> 16) & 0xff;
|
||||||
|
|
@ -1071,51 +1075,60 @@ Memory.prototype = {
|
||||||
this.getPageBlock(addr, true).writeLong(off, l, addr);
|
this.getPageBlock(addr, true).writeLong(off, l, addr);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readByteBigEndian(off, addr)
|
* readByteBE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readByteBigEndian: function readByteBigEndian(off, addr) {
|
readByteBE: function readByteBE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
return this.ab[off];
|
return this.ab[off];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readByteLittleEndian(off, addr)
|
* readByteLE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readByteLittleEndian: function readByteLittleEndian(off, addr) {
|
readByteLE: function readByteLE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
return this.ab[off];
|
return this.ab[off];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readShortBigEndian(off, addr)
|
* readBytePLE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readShortBigEndian: function readShortBigEndian(off, addr) {
|
readBytePLE: function readBytePLE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1);
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED;
|
||||||
|
return this.ab[off];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* readShortBE(off, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} addr
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readShortBE: function readShortBE(off, addr) {
|
||||||
return this.dv.getUint16(off, true);
|
return this.dv.getUint16(off, true);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readShortLittleEndian(off, addr)
|
* readShortLE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readShortLittleEndian: function readShortLittleEndian(off, addr) {
|
readShortLE: function readShortLE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1);
|
|
||||||
/*
|
/*
|
||||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
||||||
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
|
@ -1123,27 +1136,42 @@ Memory.prototype = {
|
||||||
return (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
|
return (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readLongBigEndian(off, addr)
|
* readShortPLE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readLongBigEndian: function readLongBigEndian(off, addr) {
|
readShortPLE: function readShortPLE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
/*
|
||||||
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
||||||
|
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
*/
|
||||||
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED;
|
||||||
|
return (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* readLongBE(off, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} addr
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readLongBE: function readLongBE(off, addr) {
|
||||||
return this.dv.getInt32(off, true);
|
return this.dv.getInt32(off, true);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* readLongLittleEndian(off, addr)
|
* readLongLE(off, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readLongLittleEndian: function readLongLittleEndian(off, addr) {
|
readLongLE: function readLongLE(off, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
|
||||||
/*
|
/*
|
||||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
||||||
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
|
@ -1151,54 +1179,81 @@ Memory.prototype = {
|
||||||
return (off & 0x3)? (this.ab[off] | (this.ab[off+1] << 8) | (this.ab[off+2] << 16) | (this.ab[off+3] << 24)) : this.adw[off >> 2];
|
return (off & 0x3)? (this.ab[off] | (this.ab[off+1] << 8) | (this.ab[off+2] << 16) | (this.ab[off+3] << 24)) : this.adw[off >> 2];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeByteBigEndian(off, b, addr)
|
* readLongPLE(off, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} addr
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
readLongPLE: function readLongPLE(off, addr) {
|
||||||
|
/*
|
||||||
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned read
|
||||||
|
* vs. always reading the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
*/
|
||||||
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED;
|
||||||
|
return (off & 0x3)? (this.ab[off] | (this.ab[off+1] << 8) | (this.ab[off+2] << 16) | (this.ab[off+3] << 24)) : this.adw[off >> 2];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* writeByteBE(off, b, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} b
|
* @param {number} b
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeByteBigEndian: function writeByteBigEndian(off, b, addr) {
|
writeByteBE: function writeByteBE(off, b, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
this.ab[off] = b;
|
this.ab[off] = b;
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeByteLittleEndian(off, b, addr)
|
* writeByteLE(off, b, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @param {number} b
|
* @param {number} b
|
||||||
*/
|
*/
|
||||||
writeByteLittleEndian: function writeByteLittleEndian(off, b, addr) {
|
writeByteLE: function writeByteLE(off, b, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
this.ab[off] = b;
|
this.ab[off] = b;
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeShortBigEndian(off, w, addr)
|
* writeBytePLE(off, b, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} addr
|
||||||
|
* @param {number} b
|
||||||
|
*/
|
||||||
|
writeBytePLE: function writeBytePLE(off, b, addr) {
|
||||||
|
this.ab[off] = b;
|
||||||
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||||
|
this.fDirty = true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* writeShortBE(off, w, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @param {number} w
|
* @param {number} w
|
||||||
*/
|
*/
|
||||||
writeShortBigEndian: function writeShortBigEndian(off, w, addr) {
|
writeShortBE: function writeShortBE(off, w, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1);
|
|
||||||
this.dv.setUint16(off, w, true);
|
this.dv.setUint16(off, w, true);
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeShortLittleEndian(off, w, addr)
|
* writeShortLE(off, w, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
* @param {number} w
|
* @param {number} w
|
||||||
*/
|
*/
|
||||||
writeShortLittleEndian: function writeShortLittleEndian(off, w, addr) {
|
writeShortLE: function writeShortLE(off, w, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 1);
|
|
||||||
/*
|
/*
|
||||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
||||||
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
|
@ -1212,28 +1267,49 @@ Memory.prototype = {
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeLongBigEndian(off, l, addr)
|
* writeShortPLE(off, w, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} addr
|
||||||
|
* @param {number} w
|
||||||
|
*/
|
||||||
|
writeShortPLE: function writeShortPLE(off, w, addr) {
|
||||||
|
/*
|
||||||
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
||||||
|
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
*/
|
||||||
|
if (off & 0x1) {
|
||||||
|
this.ab[off] = w;
|
||||||
|
this.ab[off+1] = w >> 8;
|
||||||
|
} else {
|
||||||
|
this.aw[off >> 1] = w;
|
||||||
|
}
|
||||||
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||||
|
this.fDirty = true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* writeLongBE(off, l, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} l
|
* @param {number} l
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeLongBigEndian: function writeLongBigEndian(off, l, addr) {
|
writeLongBE: function writeLongBE(off, l, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
|
||||||
this.dv.setInt32(off, l, true);
|
this.dv.setInt32(off, l, true);
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* writeLongLittleEndian(off, l, addr)
|
* writeLongLE(off, l, addr)
|
||||||
*
|
*
|
||||||
* @this {Memory}
|
* @this {Memory}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @param {number} l
|
* @param {number} l
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
writeLongLittleEndian: function writeLongLittleEndian(off, l, addr) {
|
writeLongLE: function writeLongLE(off, l, addr) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size - 3);
|
|
||||||
/*
|
/*
|
||||||
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
||||||
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
|
@ -1248,6 +1324,31 @@ Memory.prototype = {
|
||||||
}
|
}
|
||||||
this.fDirty = true;
|
this.fDirty = true;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* writeLongPLE(off, l, addr)
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} l
|
||||||
|
* @param {number} addr
|
||||||
|
*/
|
||||||
|
writeLongPLE: function writeLongPLE(off, l, addr) {
|
||||||
|
/*
|
||||||
|
* TODO: It remains to be seen if there's any advantage to checking the offset for an aligned write
|
||||||
|
* vs. always writing the bytes separately; it seems a safe bet for longs, but it's less clear for shorts.
|
||||||
|
*/
|
||||||
|
if (off & 0x3) {
|
||||||
|
this.ab[off] = l;
|
||||||
|
this.ab[off+1] = (l >> 8);
|
||||||
|
this.ab[off+2] = (l >> 16);
|
||||||
|
this.ab[off+3] = (l >> 24);
|
||||||
|
} else {
|
||||||
|
this.adw[off >> 2] = l;
|
||||||
|
}
|
||||||
|
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||||
|
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||||
|
this.fDirty = true;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* readBackTrackNone(off)
|
* readBackTrackNone(off)
|
||||||
*
|
*
|
||||||
|
|
@ -1284,7 +1385,6 @@ Memory.prototype = {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
readBackTrackIndex: function readBackTrackIndex(off) {
|
readBackTrackIndex: function readBackTrackIndex(off) {
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
return this.abtIndexes[off];
|
return this.abtIndexes[off];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
@ -1297,7 +1397,6 @@ Memory.prototype = {
|
||||||
*/
|
*/
|
||||||
writeBackTrackIndex: function writeBackTrackIndex(off, bti) {
|
writeBackTrackIndex: function writeBackTrackIndex(off, bti) {
|
||||||
var btiPrev;
|
var btiPrev;
|
||||||
// DEBUG: Component.assert(off >= 0 && off < this.size);
|
|
||||||
btiPrev = this.abtIndexes[off];
|
btiPrev = this.abtIndexes[off];
|
||||||
this.abtIndexes[off] = bti;
|
this.abtIndexes[off] = bti;
|
||||||
return btiPrev;
|
return btiPrev;
|
||||||
|
|
@ -1320,21 +1419,22 @@ Memory.prototype = {
|
||||||
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
|
||||||
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
|
||||||
*
|
*
|
||||||
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault];
|
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault];
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Memory.afnNone = [];
|
Memory.afnNone = [];
|
||||||
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory];
|
Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory];
|
||||||
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked];
|
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked];
|
||||||
|
|
||||||
if (PAGEBLOCKS) {
|
if (PAGEBLOCKS) {
|
||||||
Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
|
Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
|
||||||
Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged];
|
Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TYPEDARRAYS) {
|
if (TYPEDARRAYS) {
|
||||||
Memory.afnBigEndian = [Memory.prototype.readByteBigEndian, Memory.prototype.readShortBigEndian, Memory.prototype.readLongBigEndian, Memory.prototype.writeByteBigEndian, Memory.prototype.writeShortBigEndian, Memory.prototype.writeLongBigEndian];
|
Memory.afnArrayBE = [Memory.prototype.readByteBE, Memory.prototype.readShortBE, Memory.prototype.readLongBE, Memory.prototype.writeByteBE, Memory.prototype.writeShortBE, Memory.prototype.writeLongBE];
|
||||||
Memory.afnLittleEndian = [Memory.prototype.readByteLittleEndian, Memory.prototype.readShortLittleEndian, Memory.prototype.readLongLittleEndian, Memory.prototype.writeByteLittleEndian, Memory.prototype.writeShortLittleEndian, Memory.prototype.writeLongLittleEndian];
|
Memory.afnArrayLE = [Memory.prototype.readByteLE, Memory.prototype.readShortLE, Memory.prototype.readLongLE, Memory.prototype.writeByteLE, Memory.prototype.writeShortLE, Memory.prototype.writeLongLE];
|
||||||
|
Memory.afnPagedLE = [Memory.prototype.readBytePLE, Memory.prototype.readShortPLE, Memory.prototype.readLongPLE, Memory.prototype.writeBytePLE, Memory.prototype.writeShortPLE, Memory.prototype.writeLongPLE];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NODE) module.exports = Memory;
|
if (NODE) module.exports = Memory;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue