Eliminated block size constraints, added support for bit fields

This commit is contained in:
Jeff Parsons 2015-04-27 18:26:54 -07:00 committed by jeffpar
commit bf8148611d
6 changed files with 341 additions and 212 deletions

View file

@ -33,11 +33,11 @@ ROMs for their earliest 80386-based systems.
cd 1988-01-28
filedump --file=109592-001.hex --merge=109591-001.hex --output=1988-01-28.json
For a more human-readable dump, use this command:
For a more human-readable dump, use the `--comments` option:
filedump --file=109592-001.hex --merge=109591-001.hex --comments
filedump --file=109592-001.hex --merge=109591-001.hex --output=1988-01-28.dump --comments
And for those who prefer a binary file, the FileDump API can be used to recreate binary data from JSON data:
And for those who want a binary file, the FileDump API can be used to recreate binary data from JSON data:
> [http://www.pcjs.org/api/v1/dump?file=http://www.pcjs.org/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.json&format=rom](http://www.pcjs.org/api/v1/dump?file=http://www.pcjs.org/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.json&format=rom)
@ -48,6 +48,8 @@ from the two 16Kb BIN files provided by [Al Kossow](http://www.vintage-computer.
filedump --file=private/109592-005.U11.bin --merge=private/109591-005.U13.bin --output=private/1989-04-14.rom --format=rom
filedump --file=private/1989-04-14.rom --output=1989-04-14.json
Reading the ROMs
---
The *.hex* files for the 1988-01-28 DeskPro ROM were produced by running [eeprom_read](http://github.com/phooky/PROM/blob/master/tools/eeprom_read/eeprom_read.pde)
on a [chipKIT Uno32](http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,892,893&Prod=CHIPKIT-UNO32) Arduino-compatible
prototyping board, and capturing the serial port output on my MacBook Pro -- as outlined in

View file

@ -34,29 +34,13 @@
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var Component = require("../../shared/lib/component");
var Memory = require("./memory");
var Messages = require("./messages");
var State = require("./state");
}
/**
* BackTrack objects have the following properties:
*
* obj: a reference to the source object (eg, ROM object, Sector object)
* off: the offset within the source object that this object refers to
* slot: the slot (+1) in abtObjects which this object currently occupies
* refs: the number of memory references, as recorded by writeBackTrack()
*
* @typedef {{
* obj: Object,
* off: number,
* slot: number,
* refs: number
* }}
*/
var BackTrack;
/**
* Bus(cpu, dbg)
*
@ -96,32 +80,32 @@ function Bus(parmsBus, cpu, dbg)
this.nBusWidth = parmsBus['buswidth'] || 20;
/*
* Compute all the Bus memory block addressing values that we rely on, based on the width of the bus.
* Compute all Bus memory block parameters, based on the width of the bus.
*
* Regarding this.blockTotal, we want to avoid address-overflow-detection expressions like:
* Regarding blockTotal, we want to avoid using block overflow expressions like:
*
* iBlock < this.blockTotal? iBlock : 0
*
* and as long as we know that this.blockTotal is a power-of-two (eg, 256 or 0x100, in the case
* of nBusWidth == 20), we can define this.blockMask as (this.blockTotal - 1) and rewrite the previous
* expression as:
* As long as we know that blockTotal is a power of two (eg, 256 or 0x100, in the case of
* nBusWidth == 20 and blockSize == 4096), we can define blockMask as (blockTotal - 1) and
* rewrite the previous expression as:
*
* iBlock & this.blockMask
*
* While we *could* say that we mask addresses with this.busMask to simulate "A20 wrap", the simple
* fact is it relieves us from bounds-checking every aMemBlocks index. Address wrapping at the 1Mb
* boundary (ie, the A20 address line) is something we'll have to deal with more carefully on the 80286.
* Similarly, we mask addresses with busMask to enforce "A20 wrap" on 20-bit busses.
* For larger busses, A20 wrap can be simulated by either clearing bit 20 of busMask or by
* changing all the block entries for the 2nd megabyte to match those in the 1st megabyte.
*
* New property Old property Old hard-coded values (when nBusWidth was always 20)
* ------------ ------------ ----------------------------------------------------
* this.busLimit Bus.ADDR.LIMIT 0xfffff
* this.busMask N/A N/A
* this.blockSize Bus.BLOCK.SIZE 4096
* this.blockLen Bus.BLOCK.LEN (this.blockSize >> 2)
* this.blockShift Bus.BLOCK.SHIFT 12
* this.blockLimit Bus.BLOCK.LIMIT 0xfff
* this.blockTotal Bus.BLOCK.TOTAL ((this.busLimit + this.blockSize) / this.blockSize) | 0
* this.blockMask Bus.BLOCK.MASK (this.blockTotal - 1) (ie, 0xff)
* Bus Property Old hard-coded values (when nBusWidth was always 20)
* ------------ ----------------------------------------------------
* this.busLimit 0xfffff
* this.busMask N/A
* this.blockSize 4096
* this.blockLen (this.blockSize >> 2)
* this.blockShift 12
* this.blockLimit 0xfff
* this.blockTotal ((this.busLimit + this.blockSize) / this.blockSize) | 0
* this.blockMask (this.blockTotal - 1) (ie, 0xff)
*
* Note that we choose a blockShift value (and thus a physical memory block size) based on "buswidth":
*
@ -136,18 +120,17 @@ function Bus(parmsBus, cpu, dbg)
* requirements. Your choices, for the moment, are either to ensure the allocations are performed in
* order, or to choose smaller blockShift values (at the expense of a generating a larger block array).
*
* Be aware that this is strictly a physical memory implementation detail, which should have no bearing
* on segment or page granularity of any future virtual memory implementation.
* However, if PAGEBLOCKS is set, then for a bus width of 32 bits, the block size is fixed at 4Kb.
*/
this.addrTotal = Math.pow(2, this.nBusWidth);
this.busLimit = this.busMask = (this.addrTotal - 1) | 0;
this.blockShift = (this.nBusWidth <= 20? 12 : (this.nBusWidth <= 24? 14 : 15));
this.blockShift = (PAGEBLOCKS && this.nBusWidth == 32 || this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15);
this.blockSize = 1 << this.blockShift;
this.blockLen = this.blockSize >> 2;
this.blockLimit = this.blockSize - 1;
this.blockTotal = (this.addrTotal / this.blockSize) | 0;
this.blockMask = this.blockTotal - 1;
this.assert(this.blockMask <= Bus.BLOCK.NUM_MASK);
this.assert(this.blockMask <= Bus.BlockInfo.num.mask);
/*
* Lists of I/O notification functions: aPortInputNotify and aPortOutputNotify are arrays, indexed by
@ -194,6 +177,23 @@ function Bus(parmsBus, cpu, dbg)
Component.subclass(Bus);
if (BACKTRACK) {
/**
* BackTrack object definition
*
* obj: reference to the source object (eg, ROM object, Sector object)
* off: the offset within the source object that this object refers to
* slot: the slot (+1) in abtObjects which this object currently occupies
* refs: the number of memory references, as recorded by writeBackTrack()
*
* @typedef {{
* obj: Object,
* off: number,
* slot: number,
* refs: number
* }}
*/
var BackTrack;
/*
* BackTrack indexes are 31-bit values, where bits 0-8 store an object offset (0-511) and bits 16-30 store
* an object number (1-32767). Object number 0 is reserved for dynamic data (ie, data created independent
@ -245,20 +245,37 @@ if (BACKTRACK) {
};
}
/*
* scanMemory() records block numbers in bits 0-16, a BackTrack "mod" bit in bit 17, and a block type at bit 28;
* the bits reserved for a count are not used.
/**
* @typedef {number}
*/
Bus.BLOCK = {
NUM_SHIFT: 0,
NUM_MASK: 0x1ffff,
BTMOD_SHIFT: 17,
BTMOD_MASK: 0x1,
COUNT_SHIFT: 18,
COUNT_MASK: 0x03ff,
TYPE_SHIFT: 28,
TYPE_MASK: 0x7
};
var BlockInfo;
/**
* This defines the BlockInfo bit fields used by scanMemory() when it creates the aBlocks array.
*
* @typedef {{
* num: BitField,
* count: BitField,
* btmod: BitField,
* type: BitField
* }}
*/
Bus.BlockInfo = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
/**
* BusInfo object definition (returned by scanMemory())
*
* cbTotal: total bytes allocated
* cBlocks: total Memory blocks allocated
* aBlocks: array of allocated Memory block numbers
*
* @typedef {{
* cbTotal: number,
* cBlocks: number,
* aBlocks: Array.<BlockInfo>
* }}
*/
var BusInfo;
/**
* initMemory()
@ -269,12 +286,10 @@ Bus.BLOCK = {
*/
Bus.prototype.initMemory = function()
{
var block = new Memory();
this.aMemBlocks = new Array(this.blockTotal);
for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) {
var addr = iBlock * this.blockSize;
var block = this.aMemBlocks[iBlock] = new Memory(addr);
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
this.aMemBlocks[iBlock] = block;
}
this.cpu.initMemory(this.aMemBlocks, this.blockShift, this.blockLimit, this.blockMask);
this.cpu.setAddressMask(this.busMask);
@ -352,6 +367,7 @@ Bus.prototype.addMemory = function(addr, size, type, controller)
var block = this.aMemBlocks[iBlock];
var addrBlock = iBlock * this.blockSize;
var sizeBlock = size > this.blockSize? this.blockSize : size;
if (block && block.size) {
if (block.type == type && block.controller == controller) {
/*
@ -378,7 +394,7 @@ Bus.prototype.addMemory = function(addr, size, type, controller)
return this.reportError(1, addr, size);
}
block = this.aMemBlocks[iBlock++] = new Memory(addr, sizeBlock, this.blockSize, type, controller);
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
if (DEBUGGER && this.dbg) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
size -= sizeBlock;
addr = addrBlock + this.blockSize;
}
@ -412,49 +428,38 @@ Bus.prototype.cleanMemory = function(addr, size)
};
/**
* scanMemory(stats, addr, size)
* scanMemory(info, addr, size)
*
* Returns a Stats object for the specified address range with the following properties:
*
* cbTotal: total bytes allocated
* cBlocks: total Memory blocks allocated
* aBlocks: array of allocated Memory block numbers
*
* aBlocks is preallocated to its maximum size, so don't rely on its length; at any given moment,
* only the first cBlocks entries will be valid.
* Returns a BusInfo object for the specified address range.
*
* @this {Bus}
* @param {Object} [stats] previous stats, if any
* @param {Object} [info] previous BusInfo, if any
* @param {number} [addr] starting address of range (0 if none provided)
* @param {number} [size] size of range, in bytes (up to end of address space if none provided)
* @return {Object} updated stats (or new stats if no previous stats provided)
* @return {Object} updated info (or new info if no previous info provided)
*/
Bus.prototype.scanMemory = function(stats, addr, size)
Bus.prototype.scanMemory = function(info, addr, size)
{
if (addr == null) addr = 0;
if (size == null) size = (this.addrTotal - addr) | 0;
if (stats == null) stats = {cbTotal: 0, cBlocks: 0, aBlocks: new Array(this.blockTotal)};
if (info == null) info = {cbTotal: 0, cBlocks: 0, aBlocks: []};
var iBlock = addr >>> this.blockShift;
var iBlockMax = ((addr + size - 1) >>> this.blockShift);
stats.cbTotal = 0;
stats.cBlocks = 0;
info.cbTotal = 0;
info.cBlocks = 0;
while (iBlock <= iBlockMax) {
var block = this.aMemBlocks[iBlock];
stats.cbTotal += block.size;
info.cbTotal += block.size;
if (block.size) {
var nBlock = iBlock;
nBlock |= (block.type << Bus.BLOCK.TYPE_SHIFT);
if (BACKTRACK) {
var fMod = block.modBackTrack(false);
if (fMod) nBlock |= (1 << Bus.BLOCK.BTMOD_SHIFT);
}
stats.aBlocks[stats.cBlocks++] = nBlock;
var btmod = (BACKTRACK && block.modBackTrack(false)? 1 : 0);
info.aBlocks.push(usr.initBitFields(Bus.BlockInfo, iBlock, 0, btmod, block.type));
info.cBlocks++
}
iBlock++;
}
return stats;
return info;
};
/**
@ -574,7 +579,7 @@ Bus.prototype.removeMemory = function(addr, size)
while (size > 0) {
addr = iBlock * this.blockSize;
var block = this.aMemBlocks[iBlock++] = new Memory(addr);
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
if (DEBUGGER && this.dbg) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
size -= this.blockSize;
}
return true;
@ -626,7 +631,7 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
if (!block) break;
if (type !== undefined) {
var blockNew = new Memory(addr);
if (DEBUGGER) blockNew.setDebugInfo(this.cpu, this.dbg, this.blockSize);
if (DEBUGGER && this.dbg) blockNew.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
blockNew.clone(block, type);
block = blockNew;
}

View file

@ -82,16 +82,16 @@ var TYPEDARRAYS = (typeof ArrayBuffer !== 'undefined');
/**
* @define {boolean}
*
* Enables backtracking (disabled in compiled versions). Backtracking is a mechanism that allows us to tag
* every byte of incoming data and follow the flow of that data.
* BACKTRACK enables backtracking (disabled in compiled versions). Backtracking is a mechanism that allows
* us to tag every byte of incoming data and follow the flow of that data.
*/
var BACKTRACK = !COMPILED;
/**
* @define {boolean}
*
* Enables instruction sampling (a work-in-progress). This was used briefly as an internal debugging aid, to
* periodically record LIP values in a fixed-length sampling buffer, halting execution once the sampling buffer
* SAMPLER enables instruction sampling (a work-in-progress). This was used briefly as an internal debugging aid,
* to periodically record LIP values in a fixed-length sampling buffer, halting execution once the sampling buffer
* was full, and then compare those sampled LIP values to corresponding LIP values on subsequent runs, to look
* for deviations. In theory, every run is supposed to be absolutely identical, even if you interrupt execution
* with the Debugger or enable/disable different sets of messages, but in practice, that's hard to guarantee.
@ -101,9 +101,9 @@ var SAMPLER = false;
/**
* @define {boolean}
*
* Enables support for known 8086 bugs. It's turned off by default, because 1) it adds overhead, and 2) it's
* hard to imagine any software actually being dependent on any of the bugs covered by this (eg, the failure to
* properly restart string instructions with multiple prefixes, or the failure to inhibit hardware interrupts
* BUGS_8086 enables support for known 8086 bugs. It's turned off by default, because 1) it adds overhead, and
* 2) it's hard to imagine any software actually being dependent on any of the bugs covered by this (eg, the failure
* to properly restart string instructions with multiple prefixes, or the failure to inhibit hardware interrupts
* following SS segment loads).
*/
var BUGS_8086 = false;
@ -111,19 +111,33 @@ var BUGS_8086 = false;
/**
* @define {boolean}
*
* Enables 80386 support. My preference continues to be one "binary" that supports all implemented CPUs, but
* I386 enables 80386 support. My preference continues to be one "binary" that supports all implemented CPUs, but
* I'm providing this to enable a slimmed-down binary, at least until 80386 support is actually finished; at the
* moment, there's just a lot of scaffolding that bloats the compiled version without adding real functionality.
* moment, there's just a lot of scaffolding that bloats the compiled version without adding any real functionality.
*/
var I386 = true;
/**
* @define {boolean}
*
* Enables Compaq DeskPro 386 support.
* COMPAQ386 enables Compaq DeskPro 386 support.
*/
var COMPAQ386 = true;
/**
* @define {boolean}
*
* PAGEBLOCKS enables 80386 paging support with assistance from the Bus component. This affects how the Bus component
* defines physical memory parameters for a 32-bit bus. With the 8086 and 80286 processors, the Bus component was free
* to choose any block size for physical memory allocations that made sense for the bus width (eg, 4Kb blocks for a
* 20-bit bus, or 16Kb blocks for 24-bit bus).
*
* However, for the 80386 processor, it makes more sense to choose a block size that matches the page size (ie, 4Kb),
* because then we have the option of altering the address-to-memory mapping for any block to match whatever page table
* mapping is in effect for that address, if any, without requiring another layer of address translation.
*/
var PAGEBLOCKS = I386;
if (typeof module !== 'undefined') {
global.PCJSCLASS = PCJSCLASS;
global.DEBUGGER = DEBUGGER;

View file

@ -104,7 +104,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
* is available).
*
* @constructor
* @param {number} addr of lowest used address in block
* @param {number} [addr] of lowest used address in block
* @param {number} [used] portion of block in bytes (0 for none); must be a multiple of 4
* @param {number} [size] of block's buffer in bytes (0 for none); must be a multiple of 4
* @param {number} [type] is one of the Memory.TYPE constants (default is Memory.TYPE.NONE)
@ -432,19 +432,21 @@ Memory.prototype = {
this.writeLong = this.fReadOnly? this.writeNone : this.writeLongDirect;
},
/**
* setDebugInfo(cpu, dbg, size)
* setDebugInfo(cpu, dbg, addr, size)
*
* @this {Memory}
* @param {X86CPU|Component} cpu
* @param {Debugger|Component} dbg
* @param {number} addr of block
* @param {number} size of block
*/
setDebugInfo: function(cpu, dbg, size) {
setDebugInfo: function(cpu, dbg, addr, size) {
if (DEBUGGER) {
this.cpu = cpu;
this.dbg = dbg;
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
if (this.dbg) this.dbg.redoBreakpoints(this.addr, size);
Component.assert(this.dbg);
this.dbg.redoBreakpoints(addr, size);
}
},
/**
@ -455,7 +457,7 @@ Memory.prototype = {
* @param {boolean} fWrite
*/
addBreakpoint: function(off, fWrite) {
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
if (!fWrite) {
if (this.cReadBreakpoints++ === 0) {
this.setReadAccess(Memory.afnChecked);
@ -478,7 +480,7 @@ Memory.prototype = {
* @param {boolean} fWrite
*/
removeBreakpoint: function(off, fWrite) {
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
if (!fWrite) {
if (--this.cReadBreakpoints === 0) {
this.resetReadAccess();
@ -515,7 +517,7 @@ Memory.prototype = {
* @return {number}
*/
readNone: function readNone(off) {
if (DEBUGGER && this.dbg.messageEnabled(Messages.MEM) /* && !off */) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.MEM) /* && !off */) {
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr) + " from " + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()));
}
return 0xff;
@ -529,7 +531,7 @@ Memory.prototype = {
*/
writeNone: function writeNone(off, v)
{
if (DEBUGGER && this.dbg.messageEnabled(Messages.MEM) /* && !off */) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Messages.MEM) /* && !off */) {
this.dbg.message("attempt to write " + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
}
},
@ -736,7 +738,7 @@ Memory.prototype = {
*/
readByteChecked: function readByteChecked(off)
{
if (DEBUGGER) this.dbg.checkMemoryRead(this.addr + off);
if (DEBUGGER && this.dbg) this.dbg.checkMemoryRead(this.addr + off);
return this.readByteDirect(off);
},
/**
@ -748,7 +750,7 @@ Memory.prototype = {
*/
readShortChecked: function readShortChecked(off)
{
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
this.dbg.checkMemoryRead(this.addr + off) ||
this.dbg.checkMemoryRead(this.addr + off + 1);
}
@ -763,7 +765,7 @@ Memory.prototype = {
*/
readLongChecked: function readLongChecked(off)
{
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
this.dbg.checkMemoryRead(this.addr + off) ||
this.dbg.checkMemoryRead(this.addr + off + 1) ||
this.dbg.checkMemoryRead(this.addr + off + 2) ||
@ -780,7 +782,7 @@ Memory.prototype = {
*/
writeByteChecked: function writeByteChecked(off, b)
{
if (DEBUGGER) this.dbg.checkMemoryWrite(this.addr + off);
if (DEBUGGER && this.dbg) this.dbg.checkMemoryWrite(this.addr + off);
this.writeByteDirect(off, b);
},
/**
@ -792,7 +794,7 @@ Memory.prototype = {
*/
writeShortChecked: function writeShortChecked(off, w)
{
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
this.dbg.checkMemoryWrite(this.addr + off) ||
this.dbg.checkMemoryWrite(this.addr + off + 1);
}
@ -807,7 +809,7 @@ Memory.prototype = {
*/
writeLongChecked: function writeLongChecked(off, l)
{
if (DEBUGGER) {
if (DEBUGGER && this.dbg) {
this.dbg.checkMemoryWrite(this.addr + off) ||
this.dbg.checkMemoryWrite(this.addr + off + 1) ||
this.dbg.checkMemoryWrite(this.addr + off + 2) ||

View file

@ -34,6 +34,7 @@
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
@ -59,7 +60,7 @@ function Panel(parmsPanel)
this.fMouseDown = false;
this.xMouse = this.yMouse = -1;
if (BACKTRACK) {
this.stats = null;
this.busInfo = null;
this.fBackTrack = false;
}
}
@ -477,19 +478,17 @@ Panel.prototype.updateMouse = function(event, fDown)
*/
Panel.prototype.findAddress = function(x, y)
{
if (x < Panel.LIVEMEM.CX && this.stats && this.stats.aRects) {
if (x < Panel.LIVEMEM.CX && this.busInfo && this.busInfo.aRects) {
var i, rect;
for (i = 0; i < this.stats.aRects.length; i++) {
rect = this.stats.aRects[i];
for (i = 0; i < this.busInfo.aRects.length; i++) {
rect = this.busInfo.aRects[i];
if (rect.contains(x, y)) {
x -= rect.x;
y -= rect.y;
var nRegion = this.stats.aRegions[i];
var iBlock = this.stats.aBlocks[nRegion & Bus.BLOCK.NUM_MASK] & Bus.BLOCK.NUM_MASK;
var cBlocks = (nRegion >> Bus.BLOCK.COUNT_SHIFT) & Bus.BLOCK.COUNT_MASK;
var type = (nRegion >> Bus.BLOCK.TYPE_SHIFT) & Bus.BLOCK.TYPE_MASK;
var region = this.busInfo.aRegions[i];
var iBlock = usr.getBitField(Bus.BlockInfo.num, this.busInfo.aBlocks[region.iBlock]);
var addr = iBlock * this.bus.blockSize;
var addrLimit = (iBlock + cBlocks) * this.bus.blockSize - 1;
var addrLimit = (iBlock + region.cBlocks) * this.bus.blockSize - 1;
/*
* If you want memory to be arranged "vertically" instead of "horizontally", do this:
@ -502,7 +501,7 @@ Panel.prototype.findAddress = function(x, y)
addr |= 0;
if (addr > addrLimit) addr = addrLimit;
if (MAXDEBUG) this.log("Panel.findAddress(" + x + "," + y + ") found type " + Memory.TYPE.NAMES[type] + ", address %" + str.toHex(addr));
if (MAXDEBUG) this.log("Panel.findAddress(" + x + "," + y + ") found type " + Memory.TYPE.NAMES[region.type] + ", address %" + str.toHex(addr));
return addr;
}
}
@ -525,19 +524,19 @@ Panel.prototype.updateAnimation = function()
if (this.fBackTrack) {
if (DEBUG) this.log("begin scanMemory()");
this.stats = this.bus.scanMemory(this.stats);
this.busInfo = this.bus.scanMemory(this.busInfo);
/*
* Calculate the pixel-to-memory-address ratio
*/
this.ratioMemoryToPixels = (this.stats.cBlocks * this.bus.blockSize) / (Panel.LIVEMEM.CX * Panel.LIVEMEM.CY);
this.ratioMemoryToPixels = (this.busInfo.cBlocks * this.bus.blockSize) / (Panel.LIVEMEM.CX * Panel.LIVEMEM.CY);
/*
* Update the Stats object with region information (cRegions and aRegions); return true if region
* Update the BusInfo object with region information (cRegions and aRegions); return true if region
* information has changed since the last call.
*/
if (this.findRegions()) {
/*
* For each region, I choose a slice of the LiveMem canvas and record the corresponding rectangle
* within an aRects array (parallel to the aRegions array) in the Stats object.
* within an aRects array (parallel to the aRegions array) in the BusInfo object.
*
* I don't need a sophisticated Treemap algorithm, because at this level, the data is not hierarchical.
* subDivide() makes a simple horizontal or vertical slicing decision based on the ratio of region blocks
@ -545,33 +544,34 @@ Panel.prototype.updateAnimation = function()
*/
var i, rect;
var rectAvail = new Rectangle(0, 0, this.canvasLiveMem.width, this.canvasLiveMem.height);
this.stats.aRects = [];
var cBlocksRemaining = this.stats.cBlocks;
for (i = 0; i < this.stats.cRegions; i++) {
var cBlocksRegion = (this.stats.aRegions[i] >> Bus.BLOCK.COUNT_SHIFT) & Bus.BLOCK.COUNT_MASK;
this.stats.aRects.push(rect = rectAvail.subDivide(cBlocksRegion, cBlocksRemaining, !i));
this.busInfo.aRects = [];
var cBlocksRemaining = this.busInfo.cBlocks;
for (i = 0; i < this.busInfo.cRegions; i++) {
var cBlocksRegion = this.busInfo.aRegions[i].cBlocks;
this.busInfo.aRects.push(rect = rectAvail.subDivide(cBlocksRegion, cBlocksRemaining, !i));
if (MAXDEBUG) this.log("region " + i + " rectangle: (" + rect.x + "," + rect.y + " " + rect.cx + "," + rect.cy + ")");
cBlocksRemaining -= cBlocksRegion;
}
/*
* Assert that not only did all the specified regions account for all the specified blocks, but also that
* the series of subDivide() calls exhausted the original rectangle to one of either zero width or zero height.
*/
this.assert(!cBlocksRemaining && (!rectAvail.cx || !rectAvail.cy));
/*
* Now draw all the rectangles produced by the series of subDivide() calls.
*/
for (i = 0; i < this.stats.aRects.length; i++) {
var nRegion = this.stats.aRegions[i];
var type = (nRegion >> Bus.BLOCK.TYPE_SHIFT) & Bus.BLOCK.TYPE_MASK;
var cBlocks = (nRegion >> Bus.BLOCK.COUNT_SHIFT) & Bus.BLOCK.COUNT_MASK;
rect = this.stats.aRects[i];
rect.drawWith(this.contextLiveMem, Memory.TYPE.COLORS[type]);
for (i = 0; i < this.busInfo.aRects.length; i++) {
var region = this.busInfo.aRegions[i];
rect = this.busInfo.aRects[i];
rect.drawWith(this.contextLiveMem, Memory.TYPE.COLORS[region.type]);
this.centerPen(rect);
this.centerText(Memory.TYPE.NAMES[type] + " (" + (((cBlocks * this.bus.blockSize) / 1024) | 0) + "Kb)");
this.centerText(Memory.TYPE.NAMES[region.type] + " (" + (((region.cBlocks * this.bus.blockSize) / 1024) | 0) + "Kb)");
}
}
if (DEBUG) this.log("end scanMemory(): total bytes: " + this.stats.cbTotal + ", total blocks: " + this.stats.cBlocks + ", total regions: " + this.stats.cRegions);
if (DEBUG) this.log("end scanMemory(): total bytes: " + this.busInfo.cbTotal + ", total blocks: " + this.busInfo.cBlocks + ", total regions: " + this.busInfo.cRegions);
} else {
this.drawText("This space intentionally left blank");
}
@ -599,10 +599,10 @@ Panel.prototype.updateStatus = function()
/**
* findRegions()
*
* This takes the Stats object produced by scanMemory() and adds the following:
* This takes the BusInfo object produced by scanMemory() and adds the following:
*
* cRegions: number of contiguous memory regions
* aRegions: array of aBlocks indexes (bits 0-14) combined with block counts (bits 16-27) and block types (bits 28-31)
* aRegions: array of aBlocks [index, count, type] objects
*
* It calls addRegion() for each discrete region (set of contiguous blocks with the same type) that it finds.
*
@ -612,30 +612,49 @@ Panel.prototype.updateStatus = function()
Panel.prototype.findRegions = function()
{
var checksum = 0;
this.stats.cRegions = 0;
if (!this.stats.aRegions) this.stats.aRegions = [];
this.busInfo.cRegions = 0;
if (!this.busInfo.aRegions) this.busInfo.aRegions = [];
var typeRegion = -1, iBlockRegion = 0, addrRegion = 0, nBlockPrev = -1;
for (var iBlock = 0; iBlock < this.stats.cBlocks; iBlock++) {
var nBlock = this.stats.aBlocks[iBlock];
var type = nBlock >>> Bus.BLOCK.TYPE_SHIFT;
var nBlockCurr = (nBlock & Bus.BLOCK.NUM_MASK);
if (type != typeRegion || nBlockCurr != nBlockPrev + 1) {
for (var iBlock = 0; iBlock < this.busInfo.cBlocks; iBlock++) {
var blockInfo = this.busInfo.aBlocks[iBlock];
var typeBlock = usr.getBitField(Bus.BlockInfo.type, blockInfo);
var nBlockCurr = usr.getBitField(Bus.BlockInfo.num, blockInfo);
if (typeBlock != typeRegion || nBlockCurr != nBlockPrev + 1) {
var cBlocks = iBlock - iBlockRegion;
if (cBlocks) {
checksum += this.addRegion(addrRegion, iBlockRegion, cBlocks, typeRegion);
}
typeRegion = type;
typeRegion = typeBlock;
iBlockRegion = iBlock;
addrRegion = (nBlock & Bus.BLOCK.NUM_MASK) << this.bus.blockShift;
addrRegion = nBlockCurr << this.bus.blockShift;
}
nBlockPrev = nBlockCurr;
}
checksum += this.addRegion(addrRegion, iBlockRegion, iBlock - iBlockRegion, typeRegion);
var fChanged = (this.stats.checksumRegions != checksum);
this.stats.checksumRegions = checksum;
var fChanged = (this.busInfo.checksumRegions != checksum);
this.busInfo.checksumRegions = checksum;
return fChanged;
};
/**
* Region object definition
*
* iBlock: starting block number
* cBlocks: number of blocks spanned by region
* type: type of all blocks in the region (see Memory.TYPE.*)
*
* @typedef {{
* iBlock: number,
* cBlocks: number,
* type: number
* }}
*/
var Region;
/**
* addRegion(addr, iBlock, cBlocks, type)
*
@ -644,13 +663,13 @@ Panel.prototype.findRegions = function()
* @param {number} iBlock
* @param {number} cBlocks
* @param {number} type
* @return {number} region data added
* @return {number} bitfield containing the above values (used for checksum)
*/
Panel.prototype.addRegion = function(addr, iBlock, cBlocks, type)
{
if (DEBUG) this.log("region " + this.stats.cRegions + " (addr " + str.toHexLong(addr) + ", type " + Memory.TYPE.NAMES[type] + ") contains " + cBlocks + " blocks");
this.assert(iBlock <= Bus.BLOCK.NUM_MASK && cBlocks <= Bus.BLOCK.COUNT_MASK && type <= Bus.BLOCK.TYPE_MASK);
return this.stats.aRegions[this.stats.cRegions++] = (iBlock | (cBlocks << Bus.BLOCK.COUNT_SHIFT) | (type << Bus.BLOCK.TYPE_SHIFT));
if (DEBUG) this.log("region " + this.busInfo.cRegions + " (addr " + str.toHexLong(addr) + ", type " + Memory.TYPE.NAMES[type] + ") contains " + cBlocks + " blocks");
this.busInfo.aRegions[this.busInfo.cRegions++] = {iBlock: iBlock, cBlocks: cBlocks, type: type};
return usr.initBitFields(Bus.BlockInfo, iBlock, cBlocks, 0, type);
};
/**

View file

@ -34,28 +34,6 @@
var usr = {};
/**
* indexOf(a, t, i)
*
* @param {Array} a
* @param {*} t
* @param {number} [i]
* @returns {number}
*/
usr.indexOf = function(a, t, i)
{
if (Array.prototype.indexOf) {
return a.indexOf(t, i);
}
i = i || 0;
if (i < 0) i += a.length;
if (i < 0) i = 0;
for (var n = a.length; i < n; i++) {
if (i in a && a[i] === t) return i;
}
return -1;
};
/**
* binarySearch(a, v, fnCompare)
*
@ -188,45 +166,154 @@ usr.formatDate = function(sFormat, date) {
for (var i = 0; i < sFormat.length; i++) {
var ch;
switch((ch = sFormat.charAt(i))) {
case 'a':
sDate += (iHour < 12? "am" : "pm");
break;
case 'd':
sDate += ('0' + iDay).slice(-2);
break;
case 'g':
sDate += (!iHour? 12 : (iHour > 12? iHour - 12 : iHour));
break;
case 'i':
sDate += ('0' + date.getMinutes()).slice(-2);
break;
case 'j':
sDate += iDay;
break;
case 'l':
sDate += usr.asDays[date.getDay()];
break;
case 'm':
sDate += ('0' + iMonth).slice(-2);
break;
case 's':
sDate += ('0' + date.getSeconds()).slice(-2);
break;
case 'F':
sDate += usr.asMonths[iMonth - 1];
break;
case 'H':
sDate += ('0' + iHour).slice(-2);
break;
case 'Y':
sDate += date.getFullYear();
break;
default:
sDate += ch;
break;
case 'a':
sDate += (iHour < 12? "am" : "pm");
break;
case 'd':
sDate += ('0' + iDay).slice(-2);
break;
case 'g':
sDate += (!iHour? 12 : (iHour > 12? iHour - 12 : iHour));
break;
case 'i':
sDate += ('0' + date.getMinutes()).slice(-2);
break;
case 'j':
sDate += iDay;
break;
case 'l':
sDate += usr.asDays[date.getDay()];
break;
case 'm':
sDate += ('0' + iMonth).slice(-2);
break;
case 's':
sDate += ('0' + date.getSeconds()).slice(-2);
break;
case 'F':
sDate += usr.asMonths[iMonth - 1];
break;
case 'H':
sDate += ('0' + iHour).slice(-2);
break;
case 'Y':
sDate += date.getFullYear();
break;
default:
sDate += ch;
break;
}
}
return sDate;
};
/**
* @typedef {{
* mask: number,
* shift: number
* }}
*/
var BitField;
/**
* @typedef {Object.<BitField>}
*/
var BitFields;
/**
* defineBitFields(bfs)
*
* Prepares a bit field definition for use with getBitField() and setBitField(); eg:
*
* var bfs = usr.defineBitFields({num:20, count:8, btmod:1, type:3});
*
* The above defines a set of bit fields containg four fields: num (bits 0-19), count (bits 20-27), btmod (bit 28), and type (bits 29-31).
*
* usr.setBitField(bfs.num, n, 1);
*
* The above set bit field "bfs.num" in numeric variable "n" to the value 1.
*
* @param {Object} bfs
* @return {*} (technically, we transform the bfs object into a BitFields object, but the Closure Compiler won't let us specify that)
*/
usr.defineBitFields = function(bfs)
{
var bit = 0;
for (var f in bfs) {
var width = bfs[f];
var mask = ((1 << width) - 1) << bit;
bfs[f] = {mask: mask, shift: bit};
bit += width;
}
// Component.assert(bit <= 32);
return bfs;
};
/**
* initBitFields(bfs, ...)
*
* @param {BitFields} bfs
* @param {...number} var_args
* @return {number} a value containing all supplied bit fields
*/
usr.initBitFields = function(bfs, var_args)
{
var v = 0, i = 1;
for (var f in bfs) {
if (i >= arguments.length) break;
v = usr.setBitField(bfs[f], v, arguments[i++]);
}
return v;
};
/**
* getBitField(bf, v)
*
* @param {BitField} bf
* @param {number} v is a value containing bit fields
* @return {number} the value of the bit field in v defined by bf
*/
usr.getBitField = function(bf, v)
{
return (v & bf.mask) >> bf.shift;
};
/**
* setBitField(bf, v, n)
*
* @param {BitField} bf
* @param {number} v is a value containing bit fields
* @param {number} n is a value to store in v in the bit field defined by bf
* @return {number} updated v
*/
usr.setBitField = function(bf, v, n)
{
// Component.assert(!(n & ~(bf.mask >>> bf.shift)));
return (v & ~bf.mask) | ((n << bf.shift) & bf.mask);
};
/**
* indexOf(a, t, i)
*
* Use this instead of Array.prototype.indexOf() if you can't be sure the browser supports it.
*
* @param {Array} a
* @param {*} t
* @param {number} [i]
* @returns {number}
*/
usr.indexOf = function(a, t, i)
{
if (Array.prototype.indexOf) {
return a.indexOf(t, i);
}
i = i || 0;
if (i < 0) i += a.length;
if (i < 0) i = 0;
for (var n = a.length; i < n; i++) {
if (i in a && a[i] === t) return i;
}
return -1;
};
if (typeof module !== 'undefined') module.exports = usr;