Some Bus and Video tweaks
This commit is contained in:
parent
84572178c5
commit
2637a4ca85
9 changed files with 290 additions and 168 deletions
|
|
@ -28,7 +28,7 @@ result is always extended into the entire 52 "significand" bits of the underlyin
|
|||
impossible to simply "mask away" those additional sign bits, thanks to the fundamental restriction of JavaScript bitwise
|
||||
operators: they operate *only* on the low 32 bits.
|
||||
|
||||
The easiest way to remove the high-order sign bits from a negative 32-bit value is to add 0x100000000:
|
||||
The easiest way to remove the high-order sign bits from a negative 32-bit value is to add the 33-bit value 0x100000000:
|
||||
|
||||
> n = (n < 0? n + 0x100000000 : n)
|
||||
2147483648
|
||||
|
|
@ -36,15 +36,26 @@ The easiest way to remove the high-order sign bits from a negative 32-bit value
|
|||
'80000000'
|
||||
|
||||
This works because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value, as a positive
|
||||
number. But be careful, because as soon as you perform *any* bitwise operation on a value with bit 31 set, even
|
||||
an operation as innocuous-looking as:
|
||||
number, albeit in floating point. And be careful, because as soon as you perform *any* bitwise operation on a value
|
||||
with bit 31 set, even an operation as innocuous-looking as:
|
||||
|
||||
> n |= 0
|
||||
-2147483648
|
||||
> n.toString(16)
|
||||
'-80000000'
|
||||
|
||||
Viola: instant negative number! To continue the fun, set bit 0 of 0x80000000, which should give you 0x80000001:
|
||||
Viola: instant negative number!
|
||||
|
||||
This might tempt you to think that the right way to write negative 32-bit constants in hex is to simply precede
|
||||
them with a minus sign. But that would be wrong. For example, if you wrote the constant 0x80000080 as "-0x80000080",
|
||||
JavaScript would treat that as negation of 2147483776, resulting in a value whose low 32 bits are 0x7FFFFF80, not
|
||||
0x80000080.
|
||||
|
||||
The safest way to write a 32-bit constant like 0x80000080 is "0x80000080|0", which will produce -2147483520. If you
|
||||
write all your negative 32-bit constants that way, then you won't have to resort to 33-bit addition and potential
|
||||
floating point operations.
|
||||
|
||||
To continue the fun, try setting bit 0 of 0x80000000, which should give you 0x80000001:
|
||||
|
||||
> n |= 1
|
||||
-2147483647
|
||||
|
|
@ -54,7 +65,8 @@ Viola: instant negative number! To continue the fun, set bit 0 of 0x80000000, w
|
|||
WTF? Have all the low 32 bits flipped instead?
|
||||
|
||||
Actually, no, this time, I'm pulling your leg. The low 32 bits of the internal value are exactly what you would
|
||||
expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
|
||||
expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the
|
||||
[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
|
||||
explain, for a negative number, toString() returns the positive representation of the number, preceded by a - sign,
|
||||
*not* the "two's complement" of the number.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<panel id="panel" width="100%" padding="8px">
|
||||
<name>Control Panel</name>
|
||||
<control type="container" width="100%" height="auto">
|
||||
<control type="canvas" binding="btpanel" pos="relative" width="2048" height="1024" style="background-color:green;"/>
|
||||
<control type="canvas" binding="btpanel" pos="relative" width="2048" height="1024" style="color:white; background-color:green;"/>
|
||||
</control>
|
||||
<control type="container" class="pcjs-textarea" width="100%" padtop="8px">
|
||||
<control type="textarea" binding="print" width="100%" height="260px" pos="relative" padbottom="4px" padright="8px" style="resize:vertical;"/>
|
||||
|
|
|
|||
|
|
@ -98,19 +98,19 @@ function Bus(parmsBus, cpu, dbg)
|
|||
*
|
||||
* iBlock & this.blockMask
|
||||
*
|
||||
* While we *could* say that we mask addresses with this.addrMask to simulate "A20 wrap", the simple
|
||||
* 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.
|
||||
*
|
||||
* New property Old property Old hard-coded values (when nBusWidth was always 20)
|
||||
* ------------ ------------ ----------------------------------------------------
|
||||
* this.addrLimit Bus.ADDR.LIMIT 0xfffff
|
||||
* this.addrMask N/A N/A
|
||||
* 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.addrLimit + this.blockSize) / this.blockSize) | 0
|
||||
* this.blockTotal Bus.BLOCK.TOTAL ((this.busLimit + this.blockSize) / this.blockSize) | 0
|
||||
* this.blockMask Bus.BLOCK.MASK (this.blockTotal - 1) (ie, 0xff)
|
||||
*
|
||||
* Note that the blockShift calculation below chooses a 4Kb physical memory block size for a 20-bit bus
|
||||
|
|
@ -123,13 +123,14 @@ function Bus(parmsBus, cpu, dbg)
|
|||
* memory implementation detail, which should have no bearing on segment or page granularity of any future
|
||||
* virtual memory implementation.
|
||||
*/
|
||||
this.addrLimit = this.addrMask = (1 << this.nBusWidth) - 1;
|
||||
this.busLimit = this.busMask = (1 << this.nBusWidth) - 1;
|
||||
this.blockShift = (this.nBusWidth <= 20? 12 : 14);
|
||||
this.blockSize = 1 << this.blockShift;
|
||||
this.blockLen = this.blockSize >> 2;
|
||||
this.blockLimit = this.blockSize - 1;
|
||||
this.blockTotal = ((this.addrLimit + this.blockSize) / this.blockSize) | 0;
|
||||
this.blockTotal = ((this.busLimit + this.blockSize) / this.blockSize) | 0;
|
||||
this.blockMask = this.blockTotal - 1;
|
||||
this.assert(this.blockTotal <= Bus.BLOCK.MASK);
|
||||
|
||||
/*
|
||||
* Lists of I/O notification functions: aPortInputNotify and aPortOutputNotify are arrays, indexed by
|
||||
|
|
@ -235,6 +236,15 @@ if (BACKTRACK) {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* scanMemory() records block numbers in bits 0-14, a BackTrack "mod" bit in bit 15, and the block type at bit 16.
|
||||
*/
|
||||
Bus.BLOCK = {
|
||||
MASK: 0x7fff,
|
||||
BTMOD_SHIFT: 15,
|
||||
TYPE_SHIFT: 16
|
||||
};
|
||||
|
||||
/**
|
||||
* initMemory()
|
||||
*
|
||||
|
|
@ -250,8 +260,8 @@ Bus.prototype.initMemory = function()
|
|||
var block = this.aMemBlocks[iBlock] = new Memory(addr);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
|
||||
}
|
||||
this.cpu.initMemory(this.aMemBlocks, this.addrLimit, this.blockShift, this.blockLimit, this.blockMask);
|
||||
this.cpu.setAddressMask(this.addrMask);
|
||||
this.cpu.initMemory(this.aMemBlocks, this.blockShift, this.blockLimit, this.blockMask);
|
||||
this.cpu.setAddressMask(this.busMask);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -300,7 +310,7 @@ Bus.prototype.powerUp = function(data, fRepower)
|
|||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is the starting physical address of the memory address range
|
||||
* @param {number} size of the length in bytes of the range; must be a multiple of BLOCK_SIZE
|
||||
* @param {number} size of the length in bytes of the range; must be a multiple of blockSize
|
||||
* @param {number} type is one of the Memory.TYPE constants
|
||||
* @param {Object} [controller] is an optional memory controller component
|
||||
* @return {boolean} true if successful, false if not
|
||||
|
|
@ -348,27 +358,49 @@ Bus.prototype.cleanMemory = function(addr, size)
|
|||
};
|
||||
|
||||
/**
|
||||
* scanMemory(addr, size, stats)
|
||||
* scanMemory(stats, 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.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} [addr]
|
||||
* @param {number} [size]
|
||||
* @param {Object} [stats]
|
||||
* @return {number} bytes allocated
|
||||
* @param {Object} [stats] previous stats, 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)
|
||||
*/
|
||||
Bus.prototype.scanMemory = function(addr, size, stats)
|
||||
Bus.prototype.scanMemory = function(stats, addr, size)
|
||||
{
|
||||
var cbTotal = 0;
|
||||
if (addr === undefined) addr = 0;
|
||||
if (size === undefined) size = (this.addrLimit + 1) - addr;
|
||||
if (addr == null) addr = 0;
|
||||
if (size == null) size = (this.busLimit + 1) - addr;
|
||||
if (stats == null) stats = {cbTotal: 0, cBlocks: 0, aBlocks: new Array(this.blockTotal)};
|
||||
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
var iBlockMax = ((addr + size - 1) >>> this.blockShift);
|
||||
|
||||
stats.cbTotal = 0;
|
||||
stats.cBlocks = 0;
|
||||
while (iBlock <= iBlockMax) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
cbTotal += block.size;
|
||||
stats.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;
|
||||
}
|
||||
iBlock++;
|
||||
}
|
||||
return cbTotal;
|
||||
return stats;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -379,7 +411,7 @@ Bus.prototype.scanMemory = function(addr, size, stats)
|
|||
*/
|
||||
Bus.prototype.getA20 = function()
|
||||
{
|
||||
return this.addrLimit == this.addrMask;
|
||||
return this.busLimit == this.busMask;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -393,9 +425,9 @@ Bus.prototype.setA20 = function(fEnable)
|
|||
this.assert(fEnable !== undefined);
|
||||
if (fEnable !== undefined) {
|
||||
if (this.nBusWidth > 20) {
|
||||
var addrMask = (this.addrMask & ~0x100000) | (fEnable? 0x100000 : 0);
|
||||
if (addrMask != this.addrMask) {
|
||||
this.addrMask = addrMask;
|
||||
var addrMask = (this.busMask & ~0x100000) | (fEnable? 0x100000 : 0);
|
||||
if (addrMask != this.busMask) {
|
||||
this.busMask = addrMask;
|
||||
/*
|
||||
* This callback is required only because the CPU "insists" on using its own memory access functions.
|
||||
*/
|
||||
|
|
@ -484,7 +516,7 @@ Bus.prototype.removeMemory = function(addr, size)
|
|||
*/
|
||||
Bus.prototype.getByte = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -498,7 +530,7 @@ Bus.prototype.getByte = function(addr)
|
|||
*/
|
||||
Bus.prototype.getByteDirect = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].readByteDirect(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByteDirect(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -515,9 +547,9 @@ Bus.prototype.getByteDirect = function(addr)
|
|||
Bus.prototype.getShort = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
return this.aMemBlocks[iBlock].readWord(off);
|
||||
return this.aMemBlocks[iBlock].readShort(off);
|
||||
}
|
||||
return this.aMemBlocks[iBlock++].readByte(off) | (this.aMemBlocks[iBlock & this.blockMask].readByte(0) << 8);
|
||||
};
|
||||
|
|
@ -534,9 +566,9 @@ Bus.prototype.getShort = function(addr)
|
|||
Bus.prototype.getShortDirect = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
return this.aMemBlocks[iBlock].readWordDirect(off);
|
||||
return this.aMemBlocks[iBlock].readShortDirect(off);
|
||||
}
|
||||
return this.aMemBlocks[iBlock++].readByteDirect(off) | (this.aMemBlocks[iBlock & this.blockMask].readByteDirect(0) << 8);
|
||||
};
|
||||
|
|
@ -555,7 +587,7 @@ Bus.prototype.getShortDirect = function(addr)
|
|||
Bus.prototype.getLong = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
return this.aMemBlocks[iBlock].readLong(off);
|
||||
}
|
||||
|
|
@ -575,7 +607,7 @@ Bus.prototype.getLong = function(addr)
|
|||
Bus.prototype.getLongDirect = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
return this.aMemBlocks[iBlock].readLongDirect(off);
|
||||
}
|
||||
|
|
@ -595,7 +627,7 @@ Bus.prototype.getLongDirect = function(addr)
|
|||
*/
|
||||
Bus.prototype.setByte = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -610,7 +642,7 @@ Bus.prototype.setByte = function(addr, b)
|
|||
*/
|
||||
Bus.prototype.setByteDirect = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -627,9 +659,9 @@ Bus.prototype.setByteDirect = function(addr, b)
|
|||
Bus.prototype.setShort = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff);
|
||||
this.aMemBlocks[iBlock].writeShort(off, w & 0xffff);
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff);
|
||||
|
|
@ -649,9 +681,9 @@ Bus.prototype.setShort = function(addr, w)
|
|||
Bus.prototype.setShortDirect = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
this.aMemBlocks[iBlock].writeWordDirect(off, w & 0xffff);
|
||||
this.aMemBlocks[iBlock].writeShortDirect(off, w & 0xffff);
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff);
|
||||
|
|
@ -672,7 +704,7 @@ Bus.prototype.setShortDirect = function(addr, w)
|
|||
Bus.prototype.setLong = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
this.aMemBlocks[iBlock].writeLong(off, l);
|
||||
return;
|
||||
|
|
@ -699,7 +731,7 @@ Bus.prototype.setLong = function(addr, l)
|
|||
Bus.prototype.setLongDirect = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
this.aMemBlocks[iBlock].writeLongDirect(off, l);
|
||||
return;
|
||||
|
|
@ -819,7 +851,7 @@ Bus.prototype.writeBackTrackObject = function(addr, bto, off)
|
|||
Bus.prototype.readBackTrack = function(addr)
|
||||
{
|
||||
if (BACKTRACK) {
|
||||
return this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].readBackTrack(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readBackTrack(addr & this.blockLimit);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -835,9 +867,11 @@ Bus.prototype.writeBackTrack = function(addr, bti)
|
|||
{
|
||||
if (BACKTRACK) {
|
||||
var slot = bti >>> Bus.BACKTRACK.SLOT_SHIFT;
|
||||
var btiPrev = this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var btiPrev = this.aMemBlocks[iBlock].writeBackTrack(addr & this.blockLimit, bti);
|
||||
var slotPrev = btiPrev >>> Bus.BACKTRACK.SLOT_SHIFT;
|
||||
if (slot != slotPrev) {
|
||||
this.aMemBlocks[iBlock].modBackTrack(true);
|
||||
if (btiPrev && slotPrev) {
|
||||
var btoPrev = this.abtObjects[slotPrev-1];
|
||||
if (!btoPrev) {
|
||||
|
|
@ -935,7 +969,7 @@ Bus.prototype.updateBackTrackCode = function(addr, bti)
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[(addr & this.addrMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3076,7 +3076,7 @@ if (DEBUGGER) {
|
|||
* done later, by getAddr(), which returns a negative result (-1) for invalid segments, out-of-range offsets,
|
||||
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
|
||||
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
|
||||
* Bus.addrLimit; in the case of -1, that will generally refer to the last byte of physical address space.
|
||||
* Bus.busLimit; in the case of -1, that will generally refer to the last byte of physical address space.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
|
|
@ -4596,7 +4596,7 @@ if (DEBUGGER) {
|
|||
return;
|
||||
|
||||
if (n === undefined) n = 1;
|
||||
var aAddrEnd = this.newAddr(0xffff, aAddr[1], this.bus.addrLimit);
|
||||
var aAddrEnd = this.newAddr(0xffff, aAddr[1], this.bus.busLimit);
|
||||
|
||||
if (sAddrEnd !== undefined) {
|
||||
aAddrEnd = this.parseAddr(sAddrEnd, Debugger.ADDR_CODE);
|
||||
|
|
|
|||
|
|
@ -1167,7 +1167,7 @@ HDC.prototype.loadDisk = function(iDrive, sDiskName, sDiskPath, fAutoMount)
|
|||
* @param {string} sDiskName
|
||||
* @param {string} sDiskPath
|
||||
*/
|
||||
HDC.prototype.doneLoadDisk = function onHDCLoadNotify(drive, disk, sDiskName, sDiskPath)
|
||||
HDC.prototype.doneLoadDisk = function(drive, disk, sDiskName, sDiskPath)
|
||||
{
|
||||
drive.fBusy = false;
|
||||
if ((drive.disk = disk)) {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ if (typeof module !== 'undefined') {
|
|||
* The Bus allocates empty blocks for the entire address space during initialization, so that
|
||||
* any reads/writes to undefined addresses will have no effect. Later, the ROM and RAM
|
||||
* components will ask the Bus to allocate memory for specific ranges, and the Bus will allocate
|
||||
* as many new BLOCK_SIZE Memory objects as the ranges require. Partial Memory blocks could
|
||||
* as many new blockSize Memory objects as the ranges require. Partial Memory blocks could
|
||||
* also be supported in theory, but in practice, they're not.
|
||||
*
|
||||
* Because Memory blocks now allow us to have a "sparse" address space, we could choose to
|
||||
|
|
@ -105,9 +105,9 @@ if (typeof module !== 'undefined') {
|
|||
function Memory(addr, size, type, controller)
|
||||
{
|
||||
var i;
|
||||
this.cb = size || 0;
|
||||
this.adw = null;
|
||||
this.offset = 0;
|
||||
this.size = size || 0;
|
||||
this.type = type || Memory.TYPE.NONE;
|
||||
this.fReadOnly = (type == Memory.TYPE.ROM);
|
||||
this.controller = null;
|
||||
|
|
@ -115,11 +115,15 @@ function Memory(addr, size, type, controller)
|
|||
|
||||
if (BACKTRACK) {
|
||||
if (!size || controller) {
|
||||
this.fModBackTrack = false;
|
||||
this.readBackTrack = Memory.readBackTrackNone;
|
||||
this.writeBackTrack = Memory.writeBackTrackNone;
|
||||
this.modBackTrack = Memory.modBackTrackNone;
|
||||
} else {
|
||||
this.fModBackTrack = true;
|
||||
this.readBackTrack = Memory.readBackTrackIndex;
|
||||
this.writeBackTrack = Memory.writeBackTrackIndex;
|
||||
this.modBackTrack = Memory.modBackTrackIndex;
|
||||
this.abtIndexes = new Array(size);
|
||||
for (i = 0; i < size; i++) this.abtIndexes[i] = 0;
|
||||
}
|
||||
|
|
@ -153,7 +157,7 @@ function Memory(addr, size, type, controller)
|
|||
* know how to deal with this simple 1-1 mapping of addresses to bytes and words.
|
||||
*
|
||||
* TODO: Consider initializing the memory array to random (or pseudo-random) values in DEBUG
|
||||
* mode; pseudo-random might be best, because if that uncovers a bug, it might be reproducible.
|
||||
* mode; pseudo-random might be best, because if it uncovers a bug, the bug should be reproducible.
|
||||
*/
|
||||
if (TYPEDARRAYS) {
|
||||
this.buffer = new ArrayBuffer(size);
|
||||
|
|
@ -203,6 +207,10 @@ Memory.TYPE = {
|
|||
VIDEO: 3
|
||||
};
|
||||
|
||||
if (DEBUG) {
|
||||
Memory.TYPE.NAMES = ["NONE", "RAM", "ROM", "VIDEO"];
|
||||
}
|
||||
|
||||
/**
|
||||
* readNone(off)
|
||||
*
|
||||
|
|
@ -241,7 +249,7 @@ Memory.writeNone = function writeNone(off, v)
|
|||
*/
|
||||
Memory.readByteMemory = function readByteMemory(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
Component.assert(off >= 0 && off < this.size);
|
||||
if (FATARRAYS) {
|
||||
return this.ab[off];
|
||||
}
|
||||
|
|
@ -249,15 +257,15 @@ Memory.readByteMemory = function readByteMemory(off)
|
|||
};
|
||||
|
||||
/**
|
||||
* readWordMemory(off)
|
||||
* readShortMemory(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
Memory.readWordMemory = function readWordMemory(off)
|
||||
Memory.readShortMemory = function readShortMemory(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 1);
|
||||
Component.assert(off >= 0 && off < this.size - 1);
|
||||
if (FATARRAYS) {
|
||||
return this.ab[off] | (this.ab[off + 1] << 8);
|
||||
}
|
||||
|
|
@ -282,7 +290,7 @@ Memory.readWordMemory = function readWordMemory(off)
|
|||
*/
|
||||
Memory.readLongMemory = function readLongMemory(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 3);
|
||||
Component.assert(off >= 0 && off < this.size - 3);
|
||||
if (FATARRAYS) {
|
||||
return this.ab[off] | (this.ab[off + 1] << 8) | (this.ab[off + 2] << 16) | (this.ab[off + 3] << 24);
|
||||
}
|
||||
|
|
@ -305,7 +313,7 @@ Memory.readLongMemory = function readLongMemory(off)
|
|||
*/
|
||||
Memory.writeByteMemory = function writeByteMemory(off, b)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb && (b & 0xff) == b);
|
||||
Component.assert(off >= 0 && off < this.size && (b & 0xff) == b);
|
||||
if (FATARRAYS) {
|
||||
this.ab[off] = b;
|
||||
} else {
|
||||
|
|
@ -317,15 +325,15 @@ Memory.writeByteMemory = function writeByteMemory(off, b)
|
|||
};
|
||||
|
||||
/**
|
||||
* writeWordMemory(off, w)
|
||||
* writeShortMemory(off, w)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
*/
|
||||
Memory.writeWordMemory = function writeWordMemory(off, w)
|
||||
Memory.writeShortMemory = function writeShortMemory(off, w)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 1 && (w & 0xffff) == w);
|
||||
Component.assert(off >= 0 && off < this.size - 1 && (w & 0xffff) == w);
|
||||
if (FATARRAYS) {
|
||||
this.ab[off] = (w & 0xff);
|
||||
this.ab[off + 1] = (w >> 8);
|
||||
|
|
@ -352,7 +360,7 @@ Memory.writeWordMemory = function writeWordMemory(off, w)
|
|||
*/
|
||||
Memory.writeLongMemory = function writeLongMemory(off, l)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 3);
|
||||
Component.assert(off >= 0 && off < this.size - 3);
|
||||
if (FATARRAYS) {
|
||||
this.ab[off] = (l & 0xff);
|
||||
this.ab[off + 1] = (l >> 8) & 0xff;
|
||||
|
|
@ -386,19 +394,19 @@ Memory.readByteChecked = function readByteChecked(off)
|
|||
};
|
||||
|
||||
/**
|
||||
* readWordChecked(off)
|
||||
* readShortChecked(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
Memory.readWordChecked = function readWordChecked(off)
|
||||
Memory.readShortChecked = function readShortChecked(off)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
this.dbg.checkMemoryRead(this.addr + off) ||
|
||||
this.dbg.checkMemoryRead(this.addr + off + 1);
|
||||
}
|
||||
return this.readWordDirect(off);
|
||||
return this.readShortDirect(off);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -433,19 +441,19 @@ Memory.writeByteChecked = function writeByteChecked(off, b)
|
|||
};
|
||||
|
||||
/**
|
||||
* writeWordChecked(off, w)
|
||||
* writeShortChecked(off, w)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
*/
|
||||
Memory.writeWordChecked = function writeWordChecked(off, w)
|
||||
Memory.writeShortChecked = function writeShortChecked(off, w)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
this.dbg.checkMemoryWrite(this.addr + off) ||
|
||||
this.dbg.checkMemoryWrite(this.addr + off + 1);
|
||||
}
|
||||
this.writeWordDirect(off, w);
|
||||
this.writeShortDirect(off, w);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -475,20 +483,20 @@ Memory.writeLongChecked = function writeLongChecked(off, l)
|
|||
*/
|
||||
Memory.readByteTypedArray = function readByteTypedArray(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
Component.assert(off >= 0 && off < this.size);
|
||||
return this.ab[off];
|
||||
};
|
||||
|
||||
/**
|
||||
* readWordTypedArray(off)
|
||||
* readShortTypedArray(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
Memory.readWordTypedArray = function readWordTypedArray(off)
|
||||
Memory.readShortTypedArray = function readShortTypedArray(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 1);
|
||||
Component.assert(off >= 0 && off < this.size - 1);
|
||||
return this.dv.getUint16(off, true);
|
||||
};
|
||||
|
||||
|
|
@ -501,7 +509,7 @@ Memory.readWordTypedArray = function readWordTypedArray(off)
|
|||
*/
|
||||
Memory.readLongTypedArray = function readLongTypedArray(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 3);
|
||||
Component.assert(off >= 0 && off < this.size - 3);
|
||||
return this.dv.getInt32(off, true);
|
||||
};
|
||||
|
||||
|
|
@ -514,21 +522,21 @@ Memory.readLongTypedArray = function readLongTypedArray(off)
|
|||
*/
|
||||
Memory.writeByteTypedArray = function writeByteTypedArray(off, b)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb && (b & 0xff) == b);
|
||||
Component.assert(off >= 0 && off < this.size && (b & 0xff) == b);
|
||||
this.ab[off] = b;
|
||||
this.fDirty = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeWordTypedArray(off, w)
|
||||
* writeShortTypedArray(off, w)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
*/
|
||||
Memory.writeWordTypedArray = function writeWordTypedArray(off, w)
|
||||
Memory.writeShortTypedArray = function writeShortTypedArray(off, w)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 1 && (w & 0xffff) == w);
|
||||
Component.assert(off >= 0 && off < this.size - 1 && (w & 0xffff) == w);
|
||||
this.dv.setUint16(off, w, true);
|
||||
this.fDirty = true;
|
||||
};
|
||||
|
|
@ -542,7 +550,7 @@ Memory.writeWordTypedArray = function writeWordTypedArray(off, w)
|
|||
*/
|
||||
Memory.writeLongTypedArray = function writeLongTypedArray(off, l)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb - 3);
|
||||
Component.assert(off >= 0 && off < this.size - 3);
|
||||
this.dv.setInt32(off, l, true);
|
||||
this.fDirty = true;
|
||||
};
|
||||
|
|
@ -570,6 +578,17 @@ Memory.writeBackTrackNone = function writeBackTrackNone(off, bti)
|
|||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* modBackTrackNone(fMod)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {boolean} fMod
|
||||
*/
|
||||
Memory.modBackTrackNone = function modBackTrackNone(fMod)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* readBackTrackIndex(off)
|
||||
*
|
||||
|
|
@ -579,7 +598,7 @@ Memory.writeBackTrackNone = function writeBackTrackNone(off, bti)
|
|||
*/
|
||||
Memory.readBackTrackIndex = function readBackTrackIndex(off)
|
||||
{
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
Component.assert(off >= 0 && off < this.size);
|
||||
return this.abtIndexes[off];
|
||||
};
|
||||
|
||||
|
|
@ -594,17 +613,31 @@ Memory.readBackTrackIndex = function readBackTrackIndex(off)
|
|||
Memory.writeBackTrackIndex = function writeBackTrackIndex(off, bti)
|
||||
{
|
||||
var btiPrev;
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
Component.assert(off >= 0 && off < this.size);
|
||||
btiPrev = this.abtIndexes[off];
|
||||
this.abtIndexes[off] = bti;
|
||||
return btiPrev;
|
||||
};
|
||||
|
||||
Memory.afnMemory = [Memory.readByteMemory, Memory.readWordMemory, Memory.readLongMemory, Memory.writeByteMemory, Memory.writeWordMemory, Memory.writeLongMemory];
|
||||
Memory.afnChecked = [Memory.readByteChecked, Memory.readWordChecked, Memory.readLongChecked, Memory.writeByteChecked, Memory.writeWordChecked, Memory.writeLongChecked];
|
||||
/**
|
||||
* modBackTrackIndex(fMod)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {boolean} fMod
|
||||
* @return {boolean} previous value
|
||||
*/
|
||||
Memory.modBackTrackIndex = function modBackTrackIndex(fMod)
|
||||
{
|
||||
var fModPrev = this.fModBackTrack;
|
||||
this.fModBackTrack = fMod;
|
||||
return fModPrev;
|
||||
};
|
||||
|
||||
Memory.afnMemory = [Memory.readByteMemory, Memory.readShortMemory, Memory.readLongMemory, Memory.writeByteMemory, Memory.writeShortMemory, Memory.writeLongMemory];
|
||||
Memory.afnChecked = [Memory.readByteChecked, Memory.readShortChecked, Memory.readLongChecked, Memory.writeByteChecked, Memory.writeShortChecked, Memory.writeLongChecked];
|
||||
|
||||
if (TYPEDARRAYS) {
|
||||
Memory.afnTypedArray = [Memory.readByteTypedArray, Memory.readWordTypedArray, Memory.readLongTypedArray, Memory.writeByteTypedArray, Memory.writeWordTypedArray, Memory.writeLongTypedArray];
|
||||
Memory.afnTypedArray = [Memory.readByteTypedArray, Memory.readShortTypedArray, Memory.readLongTypedArray, Memory.writeByteTypedArray, Memory.writeShortTypedArray, Memory.writeLongTypedArray];
|
||||
}
|
||||
|
||||
Memory.prototype = {
|
||||
|
|
@ -628,7 +661,7 @@ Memory.prototype = {
|
|||
adw = null;
|
||||
}
|
||||
else if (FATARRAYS) {
|
||||
adw = new Array(this.cb >> 2);
|
||||
adw = new Array(this.size >> 2);
|
||||
var off = 0;
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
adw[i] = this.ab[off] | (this.ab[off + 1] << 8) | (this.ab[off + 2] << 16) | (this.ab[off + 3] << 24);
|
||||
|
|
@ -637,7 +670,7 @@ Memory.prototype = {
|
|||
}
|
||||
else if (TYPEDARRAYS) {
|
||||
/*
|
||||
* It might be tempting to just return a copy of Int32Array(this.buffer, 0, this.cb >> 2),
|
||||
* It might be tempting to just return a copy of Int32Array(this.buffer, 0, this.size >> 2),
|
||||
* but we can't be sure of the "endianness" of an Int32Array -- which would be OK if the array
|
||||
* was always saved/restored on the same machine, but there's no guarantee of that, either.
|
||||
* So we use getInt32() and require little-endian values.
|
||||
|
|
@ -646,7 +679,7 @@ Memory.prototype = {
|
|||
* a normal array; it's serialized as an Object rather than an Array, so it lacks a "length"
|
||||
* property and causes problems for State.store() and State.parse().
|
||||
*/
|
||||
adw = new Array(this.cb >> 2);
|
||||
adw = new Array(this.size >> 2);
|
||||
for (i = 0; i < adw.length; i++) {
|
||||
adw[i] = this.dv.getInt32(i << 2, true);
|
||||
}
|
||||
|
|
@ -680,7 +713,7 @@ Memory.prototype = {
|
|||
* no controller AND no data.
|
||||
*/
|
||||
Component.assert(adw != null);
|
||||
if (adw && this.cb == adw.length << 2) {
|
||||
if (adw && this.size == adw.length << 2) {
|
||||
var i;
|
||||
if (FATARRAYS) {
|
||||
var off = 0;
|
||||
|
|
@ -725,11 +758,11 @@ Memory.prototype = {
|
|||
*/
|
||||
setReadAccess: function(afn, fDirect) {
|
||||
this.readByte = afn[0] || Memory.readNone;
|
||||
this.readWord = afn[1] || Memory.readNone;
|
||||
this.readShort = afn[1] || Memory.readNone;
|
||||
this.readLong = afn[2] || Memory.readNone;
|
||||
if (fDirect) {
|
||||
this.readByteDirect = afn[0] || Memory.readNone;
|
||||
this.readWordDirect = afn[1] || Memory.readNone;
|
||||
this.readShortDirect = afn[1] || Memory.readNone;
|
||||
this.readLongDirect = afn[2] || Memory.readNone;
|
||||
}
|
||||
},
|
||||
|
|
@ -742,11 +775,11 @@ Memory.prototype = {
|
|||
*/
|
||||
setWriteAccess: function(afn, fDirect) {
|
||||
this.writeByte = !this.fReadOnly && afn[3] || Memory.writeNone;
|
||||
this.writeWord = !this.fReadOnly && afn[4] || Memory.writeNone;
|
||||
this.writeShort = !this.fReadOnly && afn[4] || Memory.writeNone;
|
||||
this.writeLong = !this.fReadOnly && afn[5] || Memory.writeNone;
|
||||
if (fDirect) {
|
||||
this.writeByteDirect = afn[3] || Memory.writeNone;
|
||||
this.writeWordDirect = afn[4] || Memory.writeNone;
|
||||
this.writeShortDirect = afn[4] || Memory.writeNone;
|
||||
this.writeLongDirect = afn[5] || Memory.writeNone;
|
||||
}
|
||||
},
|
||||
|
|
@ -757,7 +790,7 @@ Memory.prototype = {
|
|||
*/
|
||||
resetReadAccess: function() {
|
||||
this.readByte = this.readByteDirect;
|
||||
this.readWord = this.readWordDirect;
|
||||
this.readShort = this.readShortDirect;
|
||||
this.readLong = this.readLongDirect;
|
||||
},
|
||||
/**
|
||||
|
|
@ -767,7 +800,7 @@ Memory.prototype = {
|
|||
*/
|
||||
resetWriteAccess: function() {
|
||||
this.writeByte = this.fReadOnly? Memory.writeNone : this.writeByteDirect;
|
||||
this.writeWord = this.fReadOnly? Memory.writeNone : this.writeWordDirect;
|
||||
this.writeShort = this.fReadOnly? Memory.writeNone : this.writeShortDirect;
|
||||
this.writeLong = this.fReadOnly? Memory.writeNone : this.writeLongDirect;
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
if (typeof module !== 'undefined') {
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var Bus = require("./bus");
|
||||
var Memory = require("./memory");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,7 +51,10 @@ if (typeof module !== 'undefined') {
|
|||
function Panel(parmsPanel) {
|
||||
Component.call(this, "Panel", parmsPanel, Panel);
|
||||
this.canvas = null;
|
||||
if (BACKTRACK) this.fBackTrack = false;
|
||||
if (BACKTRACK) {
|
||||
this.stats = null;
|
||||
this.fBackTrack = false;
|
||||
}
|
||||
}
|
||||
|
||||
Component.subclass(Component, Panel);
|
||||
|
|
@ -82,7 +87,7 @@ Panel.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
this.canvas = control;
|
||||
this.canvasContext = this.canvas.getContext("2d");
|
||||
/*
|
||||
* this.canvas.width and this.canvas.height contain the width and height of the canvas, in pixels
|
||||
* NOTE: this.canvas.width and this.canvas.height contain the width and height of the canvas, in pixels
|
||||
*/
|
||||
this.fRedraw = true;
|
||||
return true;
|
||||
|
|
@ -121,25 +126,6 @@ Panel.prototype.powerUp = function(data, fRepower)
|
|||
{
|
||||
if (!fRepower) {
|
||||
Panel.init();
|
||||
if (this.canvas) {
|
||||
if (this.fBackTrack) {
|
||||
/*
|
||||
* We need to calculate some parameters based on the canvas pixel dimensions. For now, we're
|
||||
* going to use 100% of the canvas height, and 75% of the canvas width (starting with the left edge),
|
||||
* for the live memory display.
|
||||
*
|
||||
* For a 640x350 canvas, that means 480x350 pixels for the live memory display, or 168,000 pixels.
|
||||
* For a 16Mb (24-bit) address space, with 16,777,216 locations, the worst case would mean each pixel
|
||||
* represents roughly 100 memory locations. But in a machine with only 1152Kb of RAM, 32Kb of Video RAM,
|
||||
* and 128Kb of ROM, that's only 1,343,488 locations we need to worry about, because we can ignore all
|
||||
* the unallocated regions.
|
||||
*
|
||||
* So to start, we need a function that scans the entire address space, and reports how many bytes are
|
||||
* actually allocated. At the same time, we should also count how many different objects exist.
|
||||
*/
|
||||
this.bus.scanMemory()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
@ -156,11 +142,6 @@ Panel.prototype.powerDown = function(fSave)
|
|||
return true;
|
||||
};
|
||||
|
||||
Panel.prototype.scanMemory = function()
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* updateAnimation()
|
||||
*
|
||||
|
|
@ -175,7 +156,11 @@ Panel.prototype.updateAnimation = function()
|
|||
if (this.fRedraw) {
|
||||
if (BACKTRACK && this.fBackTrack) {
|
||||
context.font = "40px Arial";
|
||||
context.fillStyle = "#FFFFFF";
|
||||
/*
|
||||
* Even when the canvas from whence the context came has an explicit "color" style,
|
||||
* the context still defaults to "black", so we must manually propagate the canvas color.
|
||||
*/
|
||||
context.fillStyle = this.canvas.style.color;
|
||||
context.fillText("BackTrack Panel",10,50);
|
||||
}
|
||||
this.fRedraw = false;
|
||||
|
|
@ -194,6 +179,62 @@ Panel.prototype.updateAnimation = function()
|
|||
*/
|
||||
Panel.prototype.updateStatus = function()
|
||||
{
|
||||
if (this.canvas) {
|
||||
if (this.fBackTrack) {
|
||||
if (MAXDEBUG) this.log("begin scanMemory()");
|
||||
this.stats = this.bus.scanMemory(this.stats);
|
||||
this.findRegions();
|
||||
if (MAXDEBUG) this.log("end scanMemory(): total bytes: " + this.stats.cbTotal + ", total blocks: " + this.stats.cBlocks + ", total regions: " + this.stats.cRegions);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* findRegions()
|
||||
*
|
||||
* This takes the stats object produced by scanMemory() and adds the following:
|
||||
*
|
||||
* cRegions: number of contiguous memory regions
|
||||
* aRegions: array of aBlocks indexes (bits 0-15) combined with block counts (bits 16-31)
|
||||
*
|
||||
* It calls addRegion() for each discrete region (set of contiguous blocks with the same type) that it finds.
|
||||
*
|
||||
* @this {Panel}
|
||||
*/
|
||||
Panel.prototype.findRegions = function()
|
||||
{
|
||||
this.stats.cRegions = 0;
|
||||
if (!this.stats.aRegions) this.stats.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.MASK);
|
||||
if (type != typeRegion || nBlockCurr != nBlockPrev + 1) {
|
||||
var cBlocks = iBlock - iBlockRegion;
|
||||
if (cBlocks) this.addRegion(addrRegion, iBlockRegion, cBlocks, typeRegion);
|
||||
typeRegion = type;
|
||||
iBlockRegion = iBlock;
|
||||
addrRegion = (nBlock & Bus.BLOCK.MASK) << this.bus.blockShift;
|
||||
}
|
||||
nBlockPrev = nBlockCurr;
|
||||
}
|
||||
this.addRegion(addrRegion, iBlockRegion, iBlock - iBlockRegion, typeRegion);
|
||||
};
|
||||
|
||||
/**
|
||||
* addRegion(addr, iBlock, cBlocks, type)
|
||||
*
|
||||
* @this {Panel}
|
||||
* @param {number} addr
|
||||
* @param {number} iBlock
|
||||
* @param {number} cBlocks
|
||||
* @param {number} type
|
||||
*/
|
||||
Panel.prototype.addRegion = function(addr, iBlock, cBlocks, type)
|
||||
{
|
||||
this.stats.aRegions[this.stats.cRegions++] = (iBlock | cBlocks << 16);
|
||||
if (MAXDEBUG) this.log("region " + this.stats.cRegions + " (addr " + str.toHex(addr) + ", type " + Memory.TYPE.NAMES[type] + ") contains " + cBlocks + " blocks");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -710,10 +710,10 @@ Video.aCGAColorSet2 = [Video.ATTRS.FGND_CYAN, Video.ATTRS.FGND_MAGENTA, Video.A
|
|||
Video.aEGAPalDef = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F];
|
||||
|
||||
Video.aEGAByteToDW = [
|
||||
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
|
||||
0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
||||
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
||||
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
|
||||
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
|
||||
0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
||||
0xff000000|0, 0xff0000ff|0, 0xff00ff00|0, 0xff00ffff|0,
|
||||
0xffff0000|0, 0xffff00ff|0, 0xffffff00|0, 0xffffffff|0
|
||||
];
|
||||
|
||||
Video.aEGADWToByte = [];
|
||||
|
|
@ -725,14 +725,14 @@ Video.aEGADWToByte[0x00800000] = 0x4;
|
|||
Video.aEGADWToByte[0x00800080] = 0x5;
|
||||
Video.aEGADWToByte[0x00808000] = 0x6;
|
||||
Video.aEGADWToByte[0x00808080] = 0x7;
|
||||
Video.aEGADWToByte[0x80000000] = 0x8;
|
||||
Video.aEGADWToByte[0x80000080] = 0x9;
|
||||
Video.aEGADWToByte[0x80008000] = 0xa;
|
||||
Video.aEGADWToByte[0x80008080] = 0xb;
|
||||
Video.aEGADWToByte[0x80800000] = 0xc;
|
||||
Video.aEGADWToByte[0x80800080] = 0xd;
|
||||
Video.aEGADWToByte[0x80808000] = 0xe;
|
||||
Video.aEGADWToByte[0x80808080] = 0xf;
|
||||
Video.aEGADWToByte[0x80000000|0] = 0x8;
|
||||
Video.aEGADWToByte[0x80000080|0] = 0x9;
|
||||
Video.aEGADWToByte[0x80008000|0] = 0xa;
|
||||
Video.aEGADWToByte[0x80008080|0] = 0xb;
|
||||
Video.aEGADWToByte[0x80800000|0] = 0xc;
|
||||
Video.aEGADWToByte[0x80800080|0] = 0xd;
|
||||
Video.aEGADWToByte[0x80808000|0] = 0xe;
|
||||
Video.aEGADWToByte[0x80808080|0] = 0xf;
|
||||
|
||||
/**
|
||||
* Card(video, iCard, data, cbMemory)
|
||||
|
|
@ -1241,13 +1241,13 @@ Card.ACCESS.WRITE.MODE2XOR = 0xE000;
|
|||
Card.ACCESS.WRITE.MASK = 0xff00;
|
||||
|
||||
/**
|
||||
* readWord(off)
|
||||
* readShort(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
Card.ACCESS.readWord = function readWord(off)
|
||||
Card.ACCESS.readShort = function readShort(off)
|
||||
{
|
||||
return this.readByte(off) | (this.readByte(off + 1) << 8);
|
||||
};
|
||||
|
|
@ -1265,13 +1265,13 @@ Card.ACCESS.readLong = function readLong(off)
|
|||
};
|
||||
|
||||
/**
|
||||
* writeWord(off, w)
|
||||
* writeShort(off, w)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} w
|
||||
*/
|
||||
Card.ACCESS.writeWord = function writeWord(off, w)
|
||||
Card.ACCESS.writeShort = function writeShort(off, w)
|
||||
{
|
||||
Component.assert(!(w & ~0xffff));
|
||||
this.writeByte(off, w & 0xff);
|
||||
|
|
@ -1655,11 +1655,11 @@ Card.prototype.initEGA = function(data, nMonitorType)
|
|||
*/
|
||||
/*15*/ Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD | Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.EVENODD,
|
||||
/*16*/ 0,
|
||||
/*17*/ 0xffffffff,
|
||||
/*17*/ 0xffffffff|0,
|
||||
/*18*/ 0,
|
||||
/*19*/ 0xffffffff,
|
||||
/*19*/ 0xffffffff|0,
|
||||
/*20*/ 0,
|
||||
/*21*/ 0xffffffff,
|
||||
/*21*/ 0xffffffff|0,
|
||||
/*22*/ 0,
|
||||
/*23*/ 0,
|
||||
/*24*/ 0
|
||||
|
|
@ -1953,7 +1953,7 @@ Card.prototype.setMemoryAccess = function(nAccess)
|
|||
}
|
||||
}
|
||||
if (!this.afnAccess) {
|
||||
this.afnAccess = [null, Card.ACCESS.readWord, Card.ACCESS.readLong, null, Card.ACCESS.writeWord, Card.ACCESS.writeLong];
|
||||
this.afnAccess = [null, Card.ACCESS.readShort, Card.ACCESS.readLong, null, Card.ACCESS.writeShort, Card.ACCESS.writeLong];
|
||||
}
|
||||
this.afnAccess[0] = fnReadByte;
|
||||
this.afnAccess[3] = fnWriteByte;
|
||||
|
|
@ -4340,8 +4340,8 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
if (x < xDirty) xDirty = x;
|
||||
for (var iPixel = 0; iPixel < nPixelsPerCell; iPixel++) {
|
||||
/*
|
||||
* JavaScript Alert: if adwMemory contains a 32-bit value such as -1526726656, and then we mask it
|
||||
* with 0x80808080, we end up with -2147483648, which in a perfect 32-bit world, would be equivalent
|
||||
* JavaScript Alert: if adwMemory contains a 32-bit value such as -1526726656, and then we mask
|
||||
* it with 0x80808080, we end up with -2147483648, which in a perfect 32-bit world, would be equal
|
||||
* to 0x80000000, which means that when we look up "Video.aEGADWToByte[0x80000000]", we should get
|
||||
* the entry containing 0x8. But no, in JavaScript, since the original value was negative, the
|
||||
* masked value is still negative, because there are 52 "significand" bits in JavaScript numbers,
|
||||
|
|
@ -4349,13 +4349,17 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
*
|
||||
* This can be confirmed by looking at dwPixel.toString(16), which returns "-80000000". One solution
|
||||
* is to add 4294967296 (0x100000000) to any negative 32-bit value for which we need the positive
|
||||
* representation.
|
||||
* representation. A better solution (ie, one that doesn't require 33-bit values, triggering floating
|
||||
* point arithmetic) is storing negative indexes in the lookup array (aEGADWToByte). However, you CANNOT
|
||||
* do that by simply writing a value like 0x80000080 as "-0x80000080", because JavaScript will interpret
|
||||
* that as the negation of 2147483776, yielding -2147483776, the low 32 bits of which are 0x7FFFFFF80,
|
||||
* not 0x80000080 as intended. So, the safest way to write a constant like that is "0x80000080|0".
|
||||
*
|
||||
* And, since assertions don't fix problems (only catch them, and only in DEBUG builds), I'm also
|
||||
* ensuring that bPixel will always default to 0 if an undefined value ever slips through again.
|
||||
*/
|
||||
var dwPixel = data & 0x80808080;
|
||||
if (dwPixel < 0) dwPixel += 0x100000000;
|
||||
// if (dwPixel < 0) dwPixel += 0x100000000;
|
||||
this.assert(Video.aEGADWToByte[dwPixel] !== undefined);
|
||||
var bPixel = Video.aEGADWToByte[dwPixel] || 0;
|
||||
this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]);
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ function X86CPU(parmsCPU) {
|
|||
* when the Bus is initialized.
|
||||
*/
|
||||
this.aMemBlocks = [];
|
||||
this.addrMemMask = this.blockShift = this.blockLimit = this.blockMask = 0;
|
||||
this.busMask = this.blockShift = this.blockLimit = this.blockMask = 0;
|
||||
|
||||
if (SAMPLER) {
|
||||
/*
|
||||
|
|
@ -442,7 +442,7 @@ X86CPU.PREFETCH = {
|
|||
};
|
||||
|
||||
/**
|
||||
* initMemory(aMemBlocks, addrLimit, blockShift, blockLimit, blockMask)
|
||||
* initMemory(aMemBlocks, busMask, blockShift, blockLimit, blockMask)
|
||||
*
|
||||
* Notification from Bus.initMemory(), giving us direct access to the entire memory space
|
||||
* (aMemBlocks).
|
||||
|
|
@ -485,15 +485,13 @@ X86CPU.PREFETCH = {
|
|||
*
|
||||
* @this {X86CPU}
|
||||
* @param {Array} aMemBlocks
|
||||
* @param {number} addrLimit
|
||||
* @param {number} blockShift
|
||||
* @param {number} blockLimit
|
||||
* @param {number} blockMask
|
||||
*/
|
||||
X86CPU.prototype.initMemory = function(aMemBlocks, addrLimit, blockShift, blockLimit, blockMask)
|
||||
X86CPU.prototype.initMemory = function(aMemBlocks, blockShift, blockLimit, blockMask)
|
||||
{
|
||||
this.aMemBlocks = aMemBlocks;
|
||||
this.addrMemMask = addrLimit;
|
||||
this.blockShift = blockShift;
|
||||
this.blockLimit = blockLimit;
|
||||
this.blockMask = blockMask;
|
||||
|
|
@ -508,16 +506,16 @@ X86CPU.prototype.initMemory = function(aMemBlocks, addrLimit, blockShift, blockL
|
|||
};
|
||||
|
||||
/**
|
||||
* setAddressMask(addrMask)
|
||||
* setAddressMask(busMask)
|
||||
*
|
||||
* Notification from Bus.setA20(), called whenever the A20 line changes.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} addrMask
|
||||
* @param {number} busMask
|
||||
*/
|
||||
X86CPU.prototype.setAddressMask = function(addrMask)
|
||||
X86CPU.prototype.setAddressMask = function(busMask)
|
||||
{
|
||||
this.addrMemMask = addrMask;
|
||||
this.busMask = busMask;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1879,7 +1877,7 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
X86CPU.prototype.getByte = function(addr)
|
||||
{
|
||||
if (BACKTRACK) this.backTrack.btiMemLo = this.bus.readBackTrack(addr);
|
||||
return this.aMemBlocks[(addr & this.addrMemMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1892,7 +1890,7 @@ X86CPU.prototype.getByte = function(addr)
|
|||
X86CPU.prototype.getShort = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMemMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
/*
|
||||
* On the 8088, it takes 4 cycles to read the additional byte REGARDLESS whether the address is odd or even.
|
||||
* TODO: For the 8086, the penalty is actually "(addr & 0x1) << 2" (4 additional cycles only when the address is odd).
|
||||
|
|
@ -1904,7 +1902,7 @@ X86CPU.prototype.getShort = function(addr)
|
|||
this.backTrack.btiMemHi = this.bus.readBackTrack(addr + 1);
|
||||
}
|
||||
if (off < this.blockLimit) {
|
||||
return this.aMemBlocks[iBlock].readWord(off);
|
||||
return this.aMemBlocks[iBlock].readShort(off);
|
||||
}
|
||||
return this.aMemBlocks[iBlock].readByte(off) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readByte(0) << 8);
|
||||
};
|
||||
|
|
@ -1919,7 +1917,7 @@ X86CPU.prototype.getShort = function(addr)
|
|||
X86CPU.prototype.getLong = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMemMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiMemLo = this.bus.readBackTrack(addr);
|
||||
this.backTrack.btiMemHi = this.bus.readBackTrack(addr + 1);
|
||||
|
|
@ -1941,7 +1939,7 @@ X86CPU.prototype.getLong = function(addr)
|
|||
X86CPU.prototype.setByte = function(addr, b)
|
||||
{
|
||||
if (BACKTRACK) this.bus.writeBackTrack(addr, this.backTrack.btiMemLo);
|
||||
this.aMemBlocks[(addr & this.addrMemMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1954,7 +1952,7 @@ X86CPU.prototype.setByte = function(addr, b)
|
|||
X86CPU.prototype.setShort = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMemMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
/*
|
||||
* On the 8088, it takes 4 cycles to write the additional byte REGARDLESS whether the address is odd or even.
|
||||
* TODO: For the 8086, the penalty is actually "(addr & 0x1) << 2" (4 additional cycles only when the address is odd).
|
||||
|
|
@ -1966,7 +1964,7 @@ X86CPU.prototype.setShort = function(addr, w)
|
|||
this.bus.writeBackTrack(addr + 1, this.backTrack.btiMemHi);
|
||||
}
|
||||
if (off < this.blockLimit) {
|
||||
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff);
|
||||
this.aMemBlocks[iBlock].writeShort(off, w & 0xffff);
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff);
|
||||
|
|
@ -1983,7 +1981,7 @@ X86CPU.prototype.setShort = function(addr, w)
|
|||
X86CPU.prototype.setLong = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.addrMemMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
this.nStepCycles -= this.CYCLES.nWordCyclePenalty;
|
||||
|
||||
if (BACKTRACK) {
|
||||
|
|
@ -2292,10 +2290,10 @@ X86CPU.prototype.getBytePrefetch = function(addr)
|
|||
* with side-effects we may not want, and in any case, while it seemed to improve Safari's performance slightly,
|
||||
* it did nothing for the oddball Chrome performance I'm seeing with PREFETCH enabled.
|
||||
*
|
||||
* b = this.aMemBlocks[(addr & this.addrMemMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
* b = this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
* this.nBusCycles += 4;
|
||||
* this.cbPrefetchValid = 0;
|
||||
* this.addrPrefetchHead = (addr + 1) & this.addrMemMask;
|
||||
* this.addrPrefetchHead = (addr + 1) & this.busMask;
|
||||
* return b;
|
||||
*/
|
||||
}
|
||||
|
|
@ -2342,10 +2340,10 @@ X86CPU.prototype.fillPrefetch = function(n)
|
|||
{
|
||||
while (n-- > 0 && this.cbPrefetchQueued < X86CPU.PREFETCH.QUEUE) {
|
||||
var addr = this.addrPrefetchHead;
|
||||
var b = this.aMemBlocks[(addr & this.addrMemMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
var b = this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
this.aPrefetch[this.iPrefetchHead] = b | (addr << 8);
|
||||
if (MAXDEBUG) this.printMessage(" fillPrefetch[" + this.iPrefetchHead + "]: " + str.toHex(addr) + ":" + str.toHexByte(b));
|
||||
this.addrPrefetchHead = (addr + 1) & this.addrMemMask;
|
||||
this.addrPrefetchHead = (addr + 1) & this.busMask;
|
||||
this.iPrefetchHead = (this.iPrefetchHead + 1) & X86CPU.PREFETCH.MASK;
|
||||
this.cbPrefetchQueued++;
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue