Eliminated block size constraints, added support for bit fields
This commit is contained in:
parent
a1b7fae65c
commit
bf8148611d
6 changed files with 341 additions and 212 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) ||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue