From 6108fb25ab117d298b377f742de4ed64d17678a2 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Tue, 28 Apr 2015 17:45:29 -0700 Subject: [PATCH] Initial scaffolding for paged memory --- modules/pcjs/lib/bus.js | 137 +++++++++---- modules/pcjs/lib/debugger.js | 2 +- modules/pcjs/lib/fdc.js | 32 +-- modules/pcjs/lib/hdc.js | 52 ++--- modules/pcjs/lib/memory.js | 365 +++++++++++++++++++++-------------- modules/pcjs/lib/ram.js | 16 +- modules/pcjs/lib/video.js | 70 ++++--- modules/pcjs/lib/x86cpu.js | 33 ++-- 8 files changed, 431 insertions(+), 276 deletions(-) diff --git a/modules/pcjs/lib/bus.js b/modules/pcjs/lib/bus.js index ad0e42e47..d8f393f20 100644 --- a/modules/pcjs/lib/bus.js +++ b/modules/pcjs/lib/bus.js @@ -99,13 +99,13 @@ function Bus(parmsBus, cpu, dbg) * Bus Property Old hard-coded values (when nBusWidth was always 20) * ------------ ---------------------------------------------------- * this.busLimit 0xfffff - * this.busMask N/A + * this.busMask [same as busLimit] * 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) + * this.blockMask (this.blockTotal - 1) [ie, 0xff] * * Note that we choose a blockShift value (and thus a physical memory block size) based on "buswidth": * @@ -120,7 +120,7 @@ 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). * - * However, if PAGEBLOCKS is set, then for a bus width of 32 bits, the block size is fixed at 4Kb. + * Note that 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; @@ -171,6 +171,11 @@ function Bus(parmsBus, cpu, dbg) this.ibtLastDelete = 0; } + if (PAGEBLOCKS) { + this.addrPD = null; + this.aPhysBlocks = null; + } + this.setReady(); } @@ -394,7 +399,9 @@ 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 && this.dbg) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize); + if (DEBUGGER && this.dbg) { + block.setDebugger(this.dbg, addr, this.blockSize); + } size -= sizeBlock; addr = addrBlock + this.blockSize; } @@ -478,10 +485,10 @@ Bus.prototype.getA20 = function() * * On 32-bit bus machines, I've adopted the approach that Compaq took with DeskPro 386 machines, * which is to map the 1st Mb to the 2nd Mb whenever A20 is disabled, rather than blindly masking - * the A20 address bit from all addresses; this is what the DeskPro 386 ROM BIOS requires. + * the A20 address bit from all addresses; in fact, this is what the DeskPro 386 ROM BIOS requires. * * For 24-bit bus machines, we take the same approach that most if not all 80286 systems took, which - * is simply masking the A20 address bit. + * is simply masking the A20 address bit. A lot of 32-bit machines probably took the same approach. * * TODO: On machines with a 32-bit bus, look into whether we can eliminate address masking altogether, * which seems feasible, provided all incoming addresses are already pre-truncated to 32 bits. Also, @@ -579,7 +586,9 @@ Bus.prototype.removeMemory = function(addr, size) while (size > 0) { addr = iBlock * this.blockSize; var block = this.aMemBlocks[iBlock++] = new Memory(addr); - if (DEBUGGER && this.dbg) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize); + if (DEBUGGER && this.dbg) { + block.setDebugger(this.dbg, addr, this.blockSize); + } size -= this.blockSize; } return true; @@ -631,7 +640,9 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type) if (!block) break; if (type !== undefined) { var blockNew = new Memory(addr); - if (DEBUGGER && this.dbg) blockNew.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize); + if (DEBUGGER && this.dbg) { + blockNew.setDebugger(this.dbg, addr, this.blockSize); + } blockNew.clone(block, type); block = blockNew; } @@ -640,6 +651,60 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type) } }; +/** + * enablePageBlocks(addrPD) + * + * Whenever the CPU turns on paging and/or updates CR3, this function is called to leverage the Bus's + * memory-mapping abilities and simulate the effects of the CPU's page directory and page table entries. + * Whenever the CPU turns paging off, disablePageBlocks() must be called to restore the original physical + * memory mapping. + * + * This also requires that PAGEBLOCKS be true, ensuring that the Bus is preconfigured with 4Kb memory + * mapping granularity. + * + * The first time this function is called, aMemBlocks is stashed in aPhysBlocks, and aMemBlocks is then + * reinitialized with special "unpaged" Memory blocks that know how to perform page directory/page table + * lookup and replace themselves with special "paged" Memory blocks that reference memory from the + * appropriate block in aPhysBlocks. A parallel array, aMemPaged, keeps track of which blocks have been + * "paged", so that whenever CR3 is updated, just those blocks can be "unpaged" again. + * + * @this {Bus} + * @param {number} addrPD is the starting physical address of the CPU's page directory + */ +Bus.prototype.enablePageBlocks = function(addrPD) +{ + if (!PAGEBLOCKS) { + Component.error("PAGEBLOCK support missing"); + return; + } + this.addrPD = addrPD; + if (!this.aPhysBlocks) { + this.aPhysBlocks = this.aMemBlocks; + var block = new Memory(null, 0, 0, Memory.TYPE.UNPAGED, null, this); + this.aMemBlocks = new Array(this.blockTotal); + for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) { + this.aMemBlocks[iBlock] = block; + } + } +}; + +/** + * mapPageBlock(addr) + * + * If addr is a valid linear address, then we must obtain the following information: + * + * (1) the physical Memory object corresponding to the linear address in addr + * (2) the physical Memory object containing that memory's PTE + * (3) the offset within the preceding physical Memory object of that memory's PTE + * + * Item (1) allows us to convert an "unpaged" Memory block in to a "paged" Memory block, and the combination of + * (2) and (3) allows us to efficiently update the PTE whenever the page is accesssed and/or modified. + */ +Bus.prototype.mapPageBlock = function(addr) +{ + return null; +}; + /** * getByte(addr) * @@ -652,7 +717,7 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type) */ Bus.prototype.getByte = function(addr) { - return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit); + return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit, addr); }; /** @@ -666,7 +731,7 @@ Bus.prototype.getByte = function(addr) */ Bus.prototype.getByteDirect = function(addr) { - return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByteDirect(addr & this.blockLimit); + return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByteDirect(addr & this.blockLimit, addr); }; /** @@ -685,9 +750,9 @@ Bus.prototype.getShort = function(addr) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off != this.blockLimit) { - return this.aMemBlocks[iBlock].readShort(off); + return this.aMemBlocks[iBlock].readShort(off, addr); } - return this.aMemBlocks[iBlock++].readByte(off) | (this.aMemBlocks[iBlock & this.blockMask].readByte(0) << 8); + return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.blockMask].readByte(0, addr + 1) << 8); }; /** @@ -704,9 +769,9 @@ Bus.prototype.getShortDirect = function(addr) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off != this.blockLimit) { - return this.aMemBlocks[iBlock].readShortDirect(off); + return this.aMemBlocks[iBlock].readShortDirect(off, addr); } - return this.aMemBlocks[iBlock++].readByteDirect(off) | (this.aMemBlocks[iBlock & this.blockMask].readByteDirect(0) << 8); + return this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.blockMask].readByteDirect(0, addr + 1) << 8); }; /** @@ -725,10 +790,10 @@ Bus.prototype.getLong = function(addr) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off < this.blockLimit - 2) { - return this.aMemBlocks[iBlock].readLong(off); + return this.aMemBlocks[iBlock].readLong(off, addr); } var nShift = (off & 0x3) << 3; - return (this.aMemBlocks[iBlock].readLong(off & ~0x3) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLong(0) << (32 - nShift)); + return (this.aMemBlocks[iBlock].readLong(off & ~0x3, addr) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLong(0, addr + 3) << (32 - nShift)); }; /** @@ -745,10 +810,10 @@ Bus.prototype.getLongDirect = function(addr) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off < this.blockLimit - 2) { - return this.aMemBlocks[iBlock].readLongDirect(off); + return this.aMemBlocks[iBlock].readLongDirect(off, addr); } var nShift = (off & 0x3) << 3; - return (this.aMemBlocks[iBlock].readLongDirect(off & ~0x3) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLongDirect(0) << (32 - nShift)); + return (this.aMemBlocks[iBlock].readLongDirect(off & ~0x3, addr) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLongDirect(0, addr + 3) << (32 - nShift)); }; /** @@ -763,7 +828,7 @@ Bus.prototype.getLongDirect = function(addr) */ Bus.prototype.setByte = function(addr, b) { - this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff); + this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff, addr); }; /** @@ -778,7 +843,7 @@ Bus.prototype.setByte = function(addr, b) */ Bus.prototype.setByteDirect = function(addr, b) { - this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff); + this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff, addr); }; /** @@ -797,11 +862,11 @@ Bus.prototype.setShort = function(addr, w) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off != this.blockLimit) { - this.aMemBlocks[iBlock].writeShort(off, w & 0xffff); + this.aMemBlocks[iBlock].writeShort(off, w & 0xffff, addr); return; } - this.aMemBlocks[iBlock++].writeByte(off, w & 0xff); - this.aMemBlocks[iBlock & this.blockMask].writeByte(0, (w >> 8) & 0xff); + this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr); + this.aMemBlocks[iBlock & this.blockMask].writeByte(0, (w >> 8) & 0xff, addr + 1); }; /** @@ -819,11 +884,11 @@ Bus.prototype.setShortDirect = function(addr, w) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off != this.blockLimit) { - this.aMemBlocks[iBlock].writeShortDirect(off, w & 0xffff); + this.aMemBlocks[iBlock].writeShortDirect(off, w & 0xffff, addr); return; } - this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff); - this.aMemBlocks[iBlock & this.blockMask].writeByteDirect(0, (w >> 8) & 0xff); + this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr); + this.aMemBlocks[iBlock & this.blockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1); }; /** @@ -847,11 +912,12 @@ Bus.prototype.setLong = function(addr, l) } var lPrev, nShift = (off & 0x3) << 3; off &= ~0x3; - lPrev = this.aMemBlocks[iBlock].readLong(off); - this.aMemBlocks[iBlock].writeLong(off, (lPrev & ~((0xffffffff|0) << nShift)) | (l << nShift)); + lPrev = this.aMemBlocks[iBlock].readLong(off, addr); + this.aMemBlocks[iBlock].writeLong(off, (lPrev & ~((0xffffffff|0) << nShift)) | (l << nShift), addr); iBlock = (iBlock + 1) & this.blockMask; - lPrev = this.aMemBlocks[iBlock].readLong(0); - this.aMemBlocks[iBlock].writeLong(0, (lPrev & ((0xffffffff|0) << nShift)) | (l >>> (32 - nShift))); + addr += 3; + lPrev = this.aMemBlocks[iBlock].readLong(0, addr); + this.aMemBlocks[iBlock].writeLong(0, (lPrev & ((0xffffffff|0) << nShift)) | (l >>> (32 - nShift)), addr); }; /** @@ -869,16 +935,17 @@ Bus.prototype.setLongDirect = function(addr, l) var off = addr & this.blockLimit; var iBlock = (addr & this.busMask) >>> this.blockShift; if (off < this.blockLimit - 2) { - this.aMemBlocks[iBlock].writeLongDirect(off, l); + this.aMemBlocks[iBlock].writeLongDirect(off, l, addr); return; } var lPrev, nShift = (off & 0x3) << 3; off &= ~0x3; - lPrev = this.aMemBlocks[iBlock].readLongDirect(off); - this.aMemBlocks[iBlock].writeLongDirect(off, (lPrev & ~((0xffffffff|0) << nShift)) | (l << nShift)); + lPrev = this.aMemBlocks[iBlock].readLongDirect(off, addr); + this.aMemBlocks[iBlock].writeLongDirect(off, (lPrev & ~((0xffffffff|0) << nShift)) | (l << nShift), addr); iBlock = (iBlock + 1) & this.blockMask; - lPrev = this.aMemBlocks[iBlock].readLongDirect(0); - this.aMemBlocks[iBlock].writeLongDirect(0, (lPrev & ((0xffffffff|0) << nShift)) | (l >>> (32 - nShift))); + addr += 3; + lPrev = this.aMemBlocks[iBlock].readLongDirect(0, addr); + this.aMemBlocks[iBlock].writeLongDirect(0, (lPrev & ((0xffffffff|0) << nShift)) | (l >>> (32 - nShift)), addr); }; /** diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 5a914b3ab..0bfb4a68f 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -2807,7 +2807,7 @@ if (DEBUGGER) { * redoBreakpoints() * * This function is for the Memory component: whenever the Bus allocates a new Memory block, it calls - * the block's setDebugInfo() method, which clears the memory block's breakpoint counts. setDebugInfo(), + * the block's setDebugger() method, which clears the memory block's breakpoint counts. setDebugger(), * in turn, must call this function to re-apply any existing breakpoints to that block. * * This ensures that, even if a memory region is remapped (which creates new Memory blocks in the process), diff --git a/modules/pcjs/lib/fdc.js b/modules/pcjs/lib/fdc.js index 150fc378a..43a9d64dd 100644 --- a/modules/pcjs/lib/fdc.js +++ b/modules/pcjs/lib/fdc.js @@ -861,7 +861,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data) /* * resCode used to be an FDC global, but in order to insulate FDC state from the operation of various functions - * that operate on drive objects (eg, readByte and writeByte), I've made it a per-drive variable. This choice, + * that operate on drive objects (eg, readData and writeData), I've made it a per-drive variable. This choice, * similar to my choice for handling PCN, may be contrary to how the actual hardware works, but I prefer this * approach, as long as it doesn't expose any incompatibilities that any software actually cares about. */ @@ -1110,7 +1110,7 @@ FDC.prototype.copyDrive = function(iDrive) * * Also note that in an actual FDC request, drive.nBytes is initialized to the size of a single sector; the extent * of the entire transfer is actually determined by a count that has been pre-loaded into the DMA controller. The FDC - * isn't even aware of the extent of the transfer, so in the case of a read request, all readByte() can do is return + * isn't even aware of the extent of the transfer, so in the case of a read request, all readData() can do is return * bytes until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * Since seekDrive() is for use with non-DMA requests, we use nBytes to specify the length of the entire transfer. @@ -1144,7 +1144,7 @@ FDC.prototype.seekDrive = function(drive, iSector, nSectors) drive.resCode = FDC.REG_DATA.RES.NONE; /* * At this point, we've finished simulating what an FDC.REG_DATA.CMD.READ_DATA command would have performed, - * up through doRead(). Now it's the caller responsibility to call readByte(), just like the DMA Controller would. + * up through doRead(). Now it's the caller responsibility to call readData(), just like the DMA Controller would. */ return true; } @@ -2147,7 +2147,7 @@ FDC.prototype.pushST3 = function(drive) FDC.prototype.dmaRead = function(drive, b, done) { if (b === undefined || b < 0) { - this.readByte(drive, done); + this.readData(drive, done); return; } /* @@ -2168,7 +2168,7 @@ FDC.prototype.dmaRead = function(drive, b, done) FDC.prototype.dmaWrite = function(drive, b) { if (b !== undefined && b >= 0) - return this.writeByte(drive, b); + return this.writeData(drive, b); /* * The DMA controller should be GIVING us data, not ASKING for data; this suggests an internal DMA miscommunication */ @@ -2285,7 +2285,7 @@ FDC.prototype.doFormat = function(drive) }; /** - * readByte(drive, done) + * readData(drive, done) * * The following drive properties must have been setup prior to our first call: * @@ -2294,11 +2294,11 @@ FDC.prototype.doFormat = function(drive) * drive.bSector * drive.sector (initialized to null) * - * On the first readByte() request, since drive.sector will be null, we ask the Disk object to look + * On the first readData() request, since drive.sector will be null, we ask the Disk object to look * up the first sector of the request. We then ask the Disk for bytes from that sector until the sector * is exhausted, and then we look up the next sector and continue the process. * - * NOTE: Since the FDC isn't aware of the extent of the transfer, all readByte() can do is return bytes + * NOTE: Since the FDC isn't aware of the extent of the transfer, all readData() can do is return bytes * until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * TODO: Research the requirements, if any, for multi-track I/O and determine what else needs to be done. @@ -2307,7 +2307,7 @@ FDC.prototype.doFormat = function(drive) * @param {Object} drive * @param {function(number,boolean,Object,number)} done (number is next available byte from drive, or -1 if no more bytes available) */ -FDC.prototype.readByte = function(drive, done) +FDC.prototype.readData = function(drive, done) { var b = -1; var obj = null, off = 0; // these variables are purely for BACKTRACK purposes @@ -2332,7 +2332,7 @@ FDC.prototype.readByte = function(drive, done) drive.ibSector = 0; /* * We "pre-advance" bSector et al now, instead of waiting to advance it right before the seek(). - * This allows the initial call to readByte() to perform a seek without triggering an unwanted advance. + * This allows the initial call to readData() to perform a seek without triggering an unwanted advance. */ this.advanceSector(drive); } while (true); @@ -2341,7 +2341,7 @@ FDC.prototype.readByte = function(drive, done) }; /** - * writeByte(drive, b) + * writeData(drive, b) * * The following drive properties must have been setup prior to our first call: * @@ -2350,11 +2350,11 @@ FDC.prototype.readByte = function(drive, done) * drive.bSector * drive.sector (initialized to null) * - * On the first writeByte() request, since drive.sector will be null, we ask the Disk object to look + * On the first writeData() request, since drive.sector will be null, we ask the Disk object to look * up the first sector of the request. We then send the Disk bytes for that sector until the sector * is full, and then we look up the next sector and continue the process. * - * NOTE: Since the FDC isn't aware of the extent of the transfer, all writeByte() can do is accept bytes + * NOTE: Since the FDC isn't aware of the extent of the transfer, all writeData() can do is accept bytes * until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * TODO: Research the requirements, if any, for multi-track I/O and determine what else needs to be done. @@ -2364,7 +2364,7 @@ FDC.prototype.readByte = function(drive, done) * @param {number} b containing next byte to write * @return {number} (b unchanged; return -1 if command should be terminated) */ -FDC.prototype.writeByte = function(drive, b) +FDC.prototype.writeData = function(drive, b) { if (drive.resCode || !drive.disk) return -1; do { @@ -2387,7 +2387,7 @@ FDC.prototype.writeByte = function(drive, b) drive.ibSector = 0; /* * We "pre-advance" bSector et al now, instead of waiting to advance it right before the seek(). - * This allows the initial call to writeByte() to perform a seek without triggering an unwanted advance. + * This allows the initial call to writeData() to perform a seek without triggering an unwanted advance. */ this.advanceSector(drive); } while (true); @@ -2441,7 +2441,7 @@ FDC.prototype.writeFormat = function(drive, b) this.printMessage("writeFormat(head=" + str.toHexByte(drive.bHead) + ",cyl=" + str.toHexByte(drive.bCylinder) + ",sec=" + str.toHexByte(drive.bSector) + ",len=" + str.toHexWord(drive.nBytes) + ")"); } for (var i = 0; i < drive.nBytes; i++) { - if (this.writeByte(drive, drive.bFiller) < 0) { + if (this.writeData(drive, drive.bFiller) < 0) { return -1; } } diff --git a/modules/pcjs/lib/hdc.js b/modules/pcjs/lib/hdc.js index b0d05033c..4595a1c18 100644 --- a/modules/pcjs/lib/hdc.js +++ b/modules/pcjs/lib/hdc.js @@ -814,7 +814,7 @@ HDC.prototype.initDrive = function(iDrive, drive, driveConfig, data, fHard) /* * errorCode could be an HDC global, but in order to insulate HDC state from the operation of various functions - * that operate on drive objects (eg, readByte and writeByte), I've made it a per-drive variable. This choice may + * that operate on drive objects (eg, readData and writeData), I've made it a per-drive variable. This choice may * be contrary to how the actual hardware works, but I prefer this approach, as long as it doesn't expose any * incompatibilities that any software actually cares about. */ @@ -1039,7 +1039,7 @@ HDC.prototype.verifyDrive = function(drive, type) * * Also note that in an actual HDC request, drive.nBytes is initialized to the size of a single sector; the extent * of the entire transfer is actually determined by a count that has been pre-loaded into the DMA controller. The HDC - * isn't aware of the extent of the transfer, so in the case of a read request, all readByte() can do is return bytes + * isn't aware of the extent of the transfer, so in the case of a read request, all readData() can do is return bytes * until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * Since seekDrive() is for use with non-DMA requests, we use nBytes to specify the length of the entire transfer. @@ -1082,7 +1082,7 @@ HDC.prototype.seekDrive = function(drive, iSector, nSectors) drive.errorCode = HDC.XTC.DATA.ERR.NONE; /* * At this point, we've finished simulating what an HDC.XTC.DATA.CMD.READ_DATA command would have performed, - * up through doDMARead(). Now it's the caller responsibility to call readByte(), like the DMA Controller would. + * up through doDMARead(). Now it's the caller responsibility to call readData(), like the DMA Controller would. */ return true; } @@ -1389,13 +1389,13 @@ HDC.prototype.inATCData = function(port, addrFrom) if (this.drive) { /* - * We use the synchronous form of readByte() at this point because we have no choice; an I/O instruction + * We use the synchronous form of readData() at this point because we have no choice; an I/O instruction * has just occurred and cannot be delayed. The good news is that doATCommand() should have already primed * the pump; all we can do is assert that the pump has something in it. If bIn is inexplicably negative, * well, then the caller will get 0xff. */ var hdc = this; - bIn = this.readByte(this.drive, function(b, fAsync, obj, off) { + bIn = this.readData(this.drive, function(b, fAsync, obj, off) { hdc.assert(!fAsync); if (BACKTRACK) { if (!off && obj.file && hdc.messageEnabled(Messages.DISK)) { @@ -1440,7 +1440,7 @@ HDC.prototype.inATCData = function(port, addrFrom) */ if (this.drive.nBytes >= this.drive.cbSector) { hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ; - this.readByte(this.drive, function(b, fAsync) { + this.readData(this.drive, function(b, fAsync) { if (b >= 0) { hdc.setATCIRR(); hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK; @@ -1475,7 +1475,7 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom) { if (this.drive) { if (this.drive.nBytes >= this.drive.cbSector) { - if (this.writeByte(this.drive, bOut) < 0) { + if (this.writeData(this.drive, bOut) < 0) { /* * TODO: It would be nice to be a bit more specific about the error (if any) that just occurred. * Consult drive.errorCode (it uses older XTC error codes, but mapping those codes should be trivial). @@ -1833,11 +1833,11 @@ HDC.prototype.doATC = function() /* * Since the ATC doesn't use DMA, we must now set some additional Drive state for the benefit of any * follow-up I/O instructions. For example, any subsequent inATCData() and outATCData() calls need to - * know which drive to talk to ("this.drive"), to issue their own readByte() and writeByte() calls. + * know which drive to talk to ("this.drive"), to issue their own readData() and writeData() calls. * * The XTC didn't need this, because it used doDMARead(), doDMAWrite(), doDMAFormat() helper functions, * which reset the current drive's "sector" and "errorCode" properties themselves and then used DMA - * functions that delivered drive data with direct calls to readByte() and writeByte(). + * functions that delivered drive data with direct calls to readData() and writeData(). */ drive.sector = null; drive.ibSector = 0; @@ -1856,7 +1856,7 @@ HDC.prototype.doATC = function() this.printMessage("HDC.doRead(" + iDrive + ',' + drive.wCylinder + ':' + drive.bHead + ':' + drive.bSector + ',' + nSectors + ")", true); } /* - * We're using a call to readByte() that disables auto-increment, so that once we've got the first + * We're using a call to readData() that disables auto-increment, so that once we've got the first * byte of the next sector, we can signal an interrupt without also consuming the first byte, allowing * inATCData() to begin with that byte. * @@ -1865,7 +1865,7 @@ HDC.prototype.doATC = function() */ hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ; - this.readByte(drive, function(b, fAsync) { + this.readData(drive, function(b, fAsync) { if (b >= 0 && hdc.chipset) { hdc.setATCIRR(); hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK; @@ -2211,7 +2211,7 @@ HDC.prototype.pushResult = function(bResult) HDC.prototype.dmaRead = function(drive, b, done) { if (b === undefined || b < 0) { - this.readByte(drive, done); + this.readData(drive, done); return; } /* @@ -2232,7 +2232,7 @@ HDC.prototype.dmaRead = function(drive, b, done) HDC.prototype.dmaWrite = function(drive, b) { if (b !== undefined && b >= 0) - return this.writeByte(drive, b); + return this.writeData(drive, b); /* * The DMA controller should be GIVING us data, not ASKING for data; this suggests an internal DMA miscommunication */ @@ -2298,7 +2298,7 @@ HDC.prototype.doDMARead = function(drive, done) if (this.chipset) { /* * We need to reverse the original logic, and default to success unless/until an actual error occurs; - * otherwise dmaRead()/readByte() will bail on us. The original approach used to work because requestDMA() + * otherwise dmaRead()/readData() will bail on us. The original approach used to work because requestDMA() * would immediately call us back with fComplete set to true EVEN if the DMA channel was not yet unmasked; * now the callback is deferred until the DMA channel has been unmasked and the DMA request has finished. */ @@ -2342,7 +2342,7 @@ HDC.prototype.doDMAWrite = function(drive, done) if (this.chipset) { /* * We need to reverse the original logic, and default to success unless/until an actual error occurs; - * otherwise dmaWrite()/writeByte() will bail on us. The original approach would work because requestDMA() + * otherwise dmaWrite()/writeData() will bail on us. The original approach would work because requestDMA() * would immediately call us back with fComplete set to true EVEN if the DMA channel was not yet unmasked; * now the callback is deferred until the DMA channel has been unmasked and the DMA request has finished. */ @@ -2474,7 +2474,7 @@ HDC.prototype.doDMAFormat = function(drive, done) */ /** - * readByte(drive, done) + * readData(drive, done) * * The following drive variable properties must have been setup prior to our first call: * @@ -2483,11 +2483,11 @@ HDC.prototype.doDMAFormat = function(drive, done) * drive.bSector * drive.sector (initialized to null) * - * On the first readByte() request, since drive.sector will be null, we ask the Disk object to look + * On the first readData() request, since drive.sector will be null, we ask the Disk object to look * up the first sector of the request. We then ask the Disk for bytes from that sector until the sector * is exhausted, and then we look up the next sector and continue the process. * - * NOTE: Since the HDC isn't aware of the extent of the transfer, all readByte() can do is return bytes + * NOTE: Since the HDC isn't aware of the extent of the transfer, all readData() can do is return bytes * until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * @this {HDC} @@ -2496,7 +2496,7 @@ HDC.prototype.doDMAFormat = function(drive, done) * @param {boolean} [fAutoInc] (default is true to auto-increment) * @return {number} the requested byte, or -1 if unavailable */ -HDC.prototype.readByte = function(drive, done, fAutoInc) +HDC.prototype.readData = function(drive, done, fAutoInc) { var b = -1; var obj = null, off = 0; // these variables are purely for BACKTRACK purposes @@ -2535,7 +2535,7 @@ HDC.prototype.readByte = function(drive, done, fAutoInc) off = drive.ibSector = 0; /* * We "pre-advance" bSector et al now, instead of waiting to advance it right before the seek(). - * This allows the initial call to readByte() to perform a seek without triggering an unwanted advance. + * This allows the initial call to readData() to perform a seek without triggering an unwanted advance. */ hdc.advanceSector(drive); b = drive.disk.read(drive.sector, drive.ibSector); @@ -2554,7 +2554,7 @@ HDC.prototype.readByte = function(drive, done, fAutoInc) }; /** - * writeByte(drive, b) + * writeData(drive, b) * * The following drive variable properties must have been setup prior to our first call: * @@ -2563,11 +2563,11 @@ HDC.prototype.readByte = function(drive, done, fAutoInc) * drive.bSector * drive.sector (initialized to null) * - * On the first writeByte() request, since drive.sector will be null, we ask the Disk object to look + * On the first writeData() request, since drive.sector will be null, we ask the Disk object to look * up the first sector of the request. We then send the Disk bytes for that sector until the sector * is full, and then we look up the next sector and continue the process. * - * NOTE: Since the HDC isn't aware of the extent of the transfer, all writeByte() can do is accept bytes + * NOTE: Since the HDC isn't aware of the extent of the transfer, all writeData() can do is accept bytes * until the current track (or, in the case of a multi-track request, the current cylinder) has been exhausted. * * @this {HDC} @@ -2575,7 +2575,7 @@ HDC.prototype.readByte = function(drive, done, fAutoInc) * @param {number} b containing next byte to write * @return {number} (b unchanged; return -1 if command should be terminated) */ -HDC.prototype.writeByte = function(drive, b) +HDC.prototype.writeData = function(drive, b) { if (drive.errorCode) return -1; do { @@ -2603,7 +2603,7 @@ HDC.prototype.writeByte = function(drive, b) drive.ibSector = 0; /* * We "pre-advance" bSector et al now, instead of waiting to advance it right before the seek(). - * This allows the initial call to writeByte() to perform a seek without triggering an unwanted advance. + * This allows the initial call to writeData() to perform a seek without triggering an unwanted advance. */ this.advanceSector(drive); } while (true); @@ -2691,7 +2691,7 @@ HDC.prototype.writeFormat = function(drive, b) } for (var i = 0; i < drive.nBytes; i++) { - if (this.writeByte(drive, drive.bFiller) < 0) { + if (this.writeData(drive, drive.bFiller) < 0) { return -1; } } diff --git a/modules/pcjs/lib/memory.js b/modules/pcjs/lib/memory.js index 73102c774..22338d3dc 100644 --- a/modules/pcjs/lib/memory.js +++ b/modules/pcjs/lib/memory.js @@ -30,22 +30,6 @@ * any copyright as to their contents. */ -/* - * Historical Notes - * - * To minimize possible future confusion with regard to the 80386's page tables - * and page-based virtual memory, the original Page component was converted into - * this new Memory component, which provides callers with "blocks" of physical - * memory rather than "pages". Callers have been updated to refer to their Memory - * allocations as "blocks" as well. - * - * Note that the Bus component continues to specify a default block size of 4Kb (for - * the default "buswidth" of 20), but only because that seems to strike a good balance - * between data structure overhead and the memory granularity requirements of most - * system components. For larger bus widths, larger physical block sizes may be used; - * see the Bus constructor for details. - */ - "use strict"; if (typeof module !== 'undefined') { @@ -104,13 +88,14 @@ var littleEndian = (TYPEDARRAYS? (function() { * is available). * * @constructor - * @param {number} [addr] of lowest used address in block + * @param {number|null} [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) * @param {Object} [controller] is an optional memory controller component + * @param {Bus} [bus] */ -function Memory(addr, used, size, type, controller) +function Memory(addr, used, size, type, controller, bus) { var i; this.id = (Memory.idBlock += 2); @@ -122,6 +107,7 @@ function Memory(addr, used, size, type, controller) this.type = type || Memory.TYPE.NONE; this.fReadOnly = (type == Memory.TYPE.ROM); this.controller = null; + this.bus = bus; this.fDirty = this.fDirtyEver = false; if (BACKTRACK) { @@ -142,7 +128,7 @@ function Memory(addr, used, size, type, controller) /* * For empty memory blocks, all we need to do is ensure all access functions - * are mapped to "none" handlers. + * are mapped to "none" handlers (or "unpaged" handlers if paging is enabled). */ if (!size) { this.setAccess(); @@ -168,7 +154,7 @@ function Memory(addr, used, 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 it uncovers a bug, the bug should be reproducible. + * mode; pseudo-random might be best, to help make any bugs reproducible. */ if (TYPEDARRAYS) { this.buffer = new ArrayBuffer(size); @@ -220,6 +206,7 @@ Memory.TYPE = { ROM: 2, VIDEO: 3, CTRL: 4, + UNPAGED:5, NAMES: ["NONE", "RAM", "ROM", "VIDEO", "H/W"], COLORS: ["black", "blue", "green", "cyan"] }; @@ -367,12 +354,14 @@ Memory.prototype = { /** * setAccess(afn) * + * If no afn is specified, a default is selected based on the Memory type. + * * @this {Memory} * @param {Array.} [afn] * @param {boolean} [fDirect] */ setAccess: function(afn, fDirect) { - if (!afn) afn = []; + afn = afn || (this.type == Memory.TYPE.UNPAGED? Memory.afnUnpaged : Memory.afnNone); if (fDirect === undefined) fDirect = true; // TODO: Verify that this is desired default behavior this.setReadAccess(afn, fDirect); this.setWriteAccess(afn, fDirect); @@ -432,23 +421,31 @@ Memory.prototype = { this.writeLong = this.fReadOnly? this.writeNone : this.writeLongDirect; }, /** - * setDebugInfo(cpu, dbg, addr, size) + * setDebugger(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, addr, size) { + setDebugger: function(dbg, addr, size) { if (DEBUGGER) { - this.cpu = cpu; this.dbg = dbg; this.cReadBreakpoints = this.cWriteBreakpoints = 0; Component.assert(this.dbg); this.dbg.redoBreakpoints(addr, size); } }, + /** + * setPageBlock(addr) + * + * @this {Memory} + * @param {number} addr + * @return {Memory} + */ + setPageBlock: function(addr) { + return this.bus.mapPageBlock(addr); + }, /** * addBreakpoint(off, fWrite) * @@ -514,85 +511,86 @@ Memory.prototype = { * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readNone: function readNone(off) { + readNone: function readNone(off, addr) { 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())); + this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr), true); } return 0xff; }, /** - * writeNone(off, v) + * writeNone(off, v, addr) * * @this {Memory} * @param {number} off * @param {number} v (could be either a byte or word value, since we use the same handler for both kinds of accesses) + * @param {number} addr */ - writeNone: function writeNone(off, v) - { + writeNone: function writeNone(off, v, addr) { 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); } }, /** - * readShortDefault(off) + * readShortDefault(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readShortDefault: function readShortDefault(off) - { - return this.readByteDirect(off) | (this.readByteDirect(off + 1) << 8); + readShortDefault: function readShortDefault(off, addr) { + return this.readByteDirect(off, addr) | (this.readByteDirect(off + 1, addr) << 8); }, /** - * readLongDefault(off) + * readLongDefault(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readLongDefault: function readLongDefault(off) - { - return this.readByteDirect(off) | (this.readByteDirect(off + 1) << 8) | (this.readByteDirect(off + 2) << 16) | (this.readByteDirect(off + 3) << 24); + readLongDefault: function readLongDefault(off, addr) { + return this.readByteDirect(off, addr) | (this.readByteDirect(off + 1, addr) << 8) | (this.readByteDirect(off + 2, addr) << 16) | (this.readByteDirect(off + 3, addr) << 24); }, /** - * writeShortDefault(off, w) + * writeShortDefault(off, w, addr) * * @this {Memory} * @param {number} off * @param {number} w + * @param {number} addr */ - writeShortDefault: function writeShortDefault(off, w) - { + writeShortDefault: function writeShortDefault(off, w, addr) { Component.assert(!(w & ~0xffff)); this.writeByteDirect(off, w & 0xff); this.writeByteDirect(off + 1, w >> 8); }, /** - * writeLongDefault(off, w) + * writeLongDefault(off, w, addr) * * @this {Memory} * @param {number} off * @param {number} w + * @param {number} addr */ - writeLongDefault: function writeLongDefault(off, w) - { + writeLongDefault: function writeLongDefault(off, w, addr) { this.writeByteDirect(off, w & 0xff); this.writeByteDirect(off + 1, (w >> 8) & 0xff); this.writeByteDirect(off + 2, (w >> 16) & 0xff); this.writeByteDirect(off + 3, (w >>> 24)); }, /** - * readByteMemory(off) + * readByteMemory(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readByteMemory: function readByteMemory(off) - { + readByteMemory: function readByteMemory(off, addr) { Component.assert(off >= 0 && off < this.size); if (FATARRAYS) { return this.ab[off]; @@ -600,14 +598,14 @@ Memory.prototype = { return ((this.adw[off >> 2] >>> ((off & 0x3) << 3)) & 0xff); }, /** - * readShortMemory(off) + * readShortMemory(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readShortMemory: function readShortMemory(off) - { + readShortMemory: function readShortMemory(off, addr) { Component.assert(off >= 0 && off < this.size - 1); if (FATARRAYS) { return this.ab[off] | (this.ab[off + 1] << 8); @@ -624,14 +622,14 @@ Memory.prototype = { return w; }, /** - * readLongMemory(off) + * readLongMemory(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readLongMemory: function readLongMemory(off) - { + readLongMemory: function readLongMemory(off, addr) { 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); @@ -646,14 +644,14 @@ Memory.prototype = { return l; }, /** - * writeByteMemory(off, b) + * writeByteMemory(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b + * @param {number} addr */ - writeByteMemory: function writeByteMemory(off, b) - { + writeByteMemory: function writeByteMemory(off, b, addr) { Component.assert(off >= 0 && off < this.size && (b & 0xff) == b); if (FATARRAYS) { this.ab[off] = b; @@ -665,14 +663,14 @@ Memory.prototype = { this.fDirty = true; }, /** - * writeShortMemory(off, w) + * writeShortMemory(off, w, addr) * * @this {Memory} * @param {number} off * @param {number} w + * @param {number} addr */ - writeShortMemory: function writeShortMemory(off, w) - { + writeShortMemory: function writeShortMemory(off, w, addr) { Component.assert(off >= 0 && off < this.size - 1 && (w & 0xffff) == w); if (FATARRAYS) { this.ab[off] = (w & 0xff); @@ -696,14 +694,14 @@ Memory.prototype = { this.fDirty = true; }, /** - * writeLongMemory(off, l) + * writeLongMemory(off, l, addr) * * @this {Memory} * @param {number} off * @param {number} l + * @param {number} addr */ - writeLongMemory: function writeLongMemory(off, l) - { + writeLongMemory: function writeLongMemory(off, l, addr) { Component.assert(off >= 0 && off < this.size - 3); if (FATARRAYS) { this.ab[off] = (l & 0xff); @@ -730,85 +728,85 @@ Memory.prototype = { this.fDirty = true; }, /** - * readByteChecked(off) + * readByteChecked(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readByteChecked: function readByteChecked(off) - { - if (DEBUGGER && this.dbg) this.dbg.checkMemoryRead(this.addr + off); - return this.readByteDirect(off); + readByteChecked: function readByteChecked(off, addr) { + if (DEBUGGER && this.dbg) this.dbg.checkMemoryRead(addr); + return this.readByteDirect(off, addr); }, /** - * readShortChecked(off) + * readShortChecked(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readShortChecked: function readShortChecked(off) - { + readShortChecked: function readShortChecked(off, addr) { if (DEBUGGER && this.dbg) { - this.dbg.checkMemoryRead(this.addr + off) || - this.dbg.checkMemoryRead(this.addr + off + 1); + this.dbg.checkMemoryRead(addr) || + this.dbg.checkMemoryRead(addr + 1); } - return this.readShortDirect(off); + return this.readShortDirect(off, addr); }, /** - * readLongChecked(off) + * readLongChecked(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readLongChecked: function readLongChecked(off) - { + readLongChecked: function readLongChecked(off, addr) { if (DEBUGGER && this.dbg) { - this.dbg.checkMemoryRead(this.addr + off) || - this.dbg.checkMemoryRead(this.addr + off + 1) || - this.dbg.checkMemoryRead(this.addr + off + 2) || - this.dbg.checkMemoryRead(this.addr + off + 3); + this.dbg.checkMemoryRead(addr) || + this.dbg.checkMemoryRead(addr + 1) || + this.dbg.checkMemoryRead(addr + 2) || + this.dbg.checkMemoryRead(addr + 3); } - return this.readLongDirect(off); + return this.readLongDirect(off, addr); }, /** - * writeByteChecked(off, b) + * writeByteChecked(off, b, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @param {number} b */ - writeByteChecked: function writeByteChecked(off, b) - { - if (DEBUGGER && this.dbg) this.dbg.checkMemoryWrite(this.addr + off); - this.writeByteDirect(off, b); + writeByteChecked: function writeByteChecked(off, b, addr) { + if (DEBUGGER && this.dbg) this.dbg.checkMemoryWrite(addr); + this.writeByteDirect(off, b, addr); }, /** - * writeShortChecked(off, w) + * writeShortChecked(off, w, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @param {number} w */ - writeShortChecked: function writeShortChecked(off, w) - { + writeShortChecked: function writeShortChecked(off, w, addr) { if (DEBUGGER && this.dbg) { - this.dbg.checkMemoryWrite(this.addr + off) || - this.dbg.checkMemoryWrite(this.addr + off + 1); + this.dbg.checkMemoryWrite(addr) || + this.dbg.checkMemoryWrite(addr + 1); } - this.writeShortDirect(off, w); + this.writeShortDirect(off, w, addr); }, /** - * writeLongChecked(off, l) + * writeLongChecked(off, l, addr) * * @this {Memory} * @param {number} off * @param {number} l + * @param {number} addr */ - writeLongChecked: function writeLongChecked(off, l) - { + writeLongChecked: function writeLongChecked(off, l, addr) { if (DEBUGGER && this.dbg) { this.dbg.checkMemoryWrite(this.addr + off) || this.dbg.checkMemoryWrite(this.addr + off + 1) || @@ -818,50 +816,116 @@ Memory.prototype = { this.writeLongDirect(off, l); }, /** - * readByteBigEndian(off) + * readByteUnpaged(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readByteBigEndian: function readByteBigEndian(off) - { + readByteUnpaged: function readByteUnpaged(off, addr) { + return this.setPageBlock(addr).readByte(off, addr); + }, + /** + * readShortUnpaged(off, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} addr + * @return {number} + */ + readShortUnpaged: function readShortUnpaged(off, addr) { + return this.setPageBlock(addr).readShort(off, addr); + }, + /** + * readLongUnpaged(off, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} addr + * @return {number} + */ + readLongUnpaged: function readLongUnpaged(off, addr) { + return this.setPageBlock(addr).readLong(off, addr); + }, + /** + * writeByteUnpaged(off, b, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} b + * @param {number} addr + */ + writeByteUnpaged: function writeByteUnpaged(off, b, addr) { + this.setPageBlock(addr).writeByte(off, b, addr); + }, + /** + * writeShortUnpaged(off, w, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} w + * @param {number} addr + */ + writeShortUnpaged: function writeShortUnpaged(off, w, addr) { + this.setPageBlock(addr).writeShort(off, w, addr); + }, + /** + * writeLongUnpaged(off, l, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} l + * @param {number} addr + */ + writeLongUnpaged: function writeLongUnpaged(off, l, addr) { + this.setPageBlock(addr).writeLong(off, l, addr); + }, + /** + * readByteBigEndian(off, addr) + * + * @this {Memory} + * @param {number} off + * @param {number} addr + * @return {number} + */ + readByteBigEndian: function readByteBigEndian(off, addr) { Component.assert(off >= 0 && off < this.size); return this.ab[off]; }, /** - * readByteLittleEndian(off) + * readByteLittleEndian(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readByteLittleEndian: function readByteLittleEndian(off) - { + readByteLittleEndian: function readByteLittleEndian(off, addr) { Component.assert(off >= 0 && off < this.size); return this.ab[off]; }, /** - * readShortBigEndian(off) + * readShortBigEndian(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readShortBigEndian: function readShortBigEndian(off) - { + readShortBigEndian: function readShortBigEndian(off, addr) { Component.assert(off >= 0 && off < this.size - 1); return this.dv.getUint16(off, true); }, /** - * readShortLittleEndian(off) + * readShortLittleEndian(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readShortLittleEndian: function readShortLittleEndian(off) - { + readShortLittleEndian: function readShortLittleEndian(off, addr) { Component.assert(off >= 0 && off < this.size - 1); /* * TODO: It remains to be seen if there's any advantage to checking the offset @@ -871,26 +935,26 @@ Memory.prototype = { return (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1]; }, /** - * readLongBigEndian(off) + * readLongBigEndian(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readLongBigEndian: function readLongBigEndian(off) - { + readLongBigEndian: function readLongBigEndian(off, addr) { Component.assert(off >= 0 && off < this.size - 3); return this.dv.getInt32(off, true); }, /** - * readLongLittleEndian(off) + * readLongLittleEndian(off, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @return {number} */ - readLongLittleEndian: function readLongLittleEndian(off) - { + readLongLittleEndian: function readLongLittleEndian(off, addr) { Component.assert(off >= 0 && off < this.size - 3); /* * TODO: It remains to be seen if there's any advantage to checking the offset @@ -900,53 +964,53 @@ Memory.prototype = { return (off & 0x3)? (this.ab[off] | (this.ab[off+1] << 8) | (this.ab[off+2] << 16) | (this.ab[off+3] << 24)) : this.adw[off >> 2]; }, /** - * writeByteBigEndian(off, b) + * writeByteBigEndian(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b + * @param {number} addr */ - writeByteBigEndian: function writeByteBigEndian(off, b) - { + writeByteBigEndian: function writeByteBigEndian(off, b, addr) { Component.assert(off >= 0 && off < this.size); this.ab[off] = b; this.fDirty = true; }, /** - * writeByteLittleEndian(off, b) + * writeByteLittleEndian(off, b, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @param {number} b */ - writeByteLittleEndian: function writeByteLittleEndian(off, b) - { + writeByteLittleEndian: function writeByteLittleEndian(off, b, addr) { Component.assert(off >= 0 && off < this.size); this.ab[off] = b; this.fDirty = true; }, /** - * writeShortBigEndian(off, w) + * writeShortBigEndian(off, w, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @param {number} w */ - writeShortBigEndian: function writeShortBigEndian(off, w) - { + writeShortBigEndian: function writeShortBigEndian(off, w, addr) { Component.assert(off >= 0 && off < this.size - 1); this.dv.setUint16(off, w, true); this.fDirty = true; }, /** - * writeShortLittleEndian(off, w) + * writeShortLittleEndian(off, w, addr) * * @this {Memory} * @param {number} off + * @param {number} addr * @param {number} w */ - writeShortLittleEndian: function writeShortLittleEndian(off, w) - { + writeShortLittleEndian: function writeShortLittleEndian(off, w, addr) { Component.assert(off >= 0 && off < this.size - 1); /* * TODO: It remains to be seen if there's any advantage to checking the offset @@ -962,27 +1026,27 @@ Memory.prototype = { this.fDirty = true; }, /** - * writeLongBigEndian(off, l) + * writeLongBigEndian(off, l, addr) * * @this {Memory} * @param {number} off * @param {number} l + * @param {number} addr */ - writeLongBigEndian: function writeLongBigEndian(off, l) - { + writeLongBigEndian: function writeLongBigEndian(off, l, addr) { Component.assert(off >= 0 && off < this.size - 3); this.dv.setInt32(off, l, true); this.fDirty = true; }, /** - * writeLongLittleEndian(off, l) + * writeLongLittleEndian(off, l, addr) * * @this {Memory} * @param {number} off * @param {number} l + * @param {number} addr */ - writeLongLittleEndian: function writeLongLittleEndian(off, l) - { + writeLongLittleEndian: function writeLongLittleEndian(off, l, addr) { Component.assert(off >= 0 && off < this.size - 3); /* * TODO: It remains to be seen if there's any advantage to checking the offset @@ -1006,8 +1070,7 @@ Memory.prototype = { * @param {number} off * @return {number} */ - readBackTrackNone: function readBackTrackNone(off) - { + readBackTrackNone: function readBackTrackNone(off) { return 0; }, /** @@ -1017,8 +1080,7 @@ Memory.prototype = { * @param {number} off * @param {number} bti */ - writeBackTrackNone: function writeBackTrackNone(off, bti) - { + writeBackTrackNone: function writeBackTrackNone(off, bti) { }, /** * modBackTrackNone(fMod) @@ -1026,8 +1088,7 @@ Memory.prototype = { * @this {Memory} * @param {boolean} fMod */ - modBackTrackNone: function modBackTrackNone(fMod) - { + modBackTrackNone: function modBackTrackNone(fMod) { return false; }, /** @@ -1037,8 +1098,7 @@ Memory.prototype = { * @param {number} off * @return {number} */ - readBackTrackIndex: function readBackTrackIndex(off) - { + readBackTrackIndex: function readBackTrackIndex(off) { Component.assert(off >= 0 && off < this.size); return this.abtIndexes[off]; }, @@ -1050,8 +1110,7 @@ Memory.prototype = { * @param {number} bti * @return {number} previous bti (0 if none) */ - writeBackTrackIndex: function writeBackTrackIndex(off, bti) - { + writeBackTrackIndex: function writeBackTrackIndex(off, bti) { var btiPrev; Component.assert(off >= 0 && off < this.size); btiPrev = this.abtIndexes[off]; @@ -1065,20 +1124,32 @@ Memory.prototype = { * @param {boolean} fMod * @return {boolean} previous value */ - modBackTrackIndex: function modBackTrackIndex(fMod) - { + modBackTrackIndex: function modBackTrackIndex(fMod) { var fModPrev = this.fModBackTrack; this.fModBackTrack = fMod; return fModPrev; } }; -Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory]; -Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked]; +/* + * This is the effective definition of afnNone, but we need not fully define it, because setAccess() already + * uses these defaults when any of the 6 handlers (ie, 3 read handlers followed by 3 write handlers) are undefined. + * +Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault]; + */ + +Memory.afnNone = []; +Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototype.readShortMemory, Memory.prototype.readLongMemory, Memory.prototype.writeByteMemory, Memory.prototype.writeShortMemory, Memory.prototype.writeLongMemory]; +Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked]; + +if (PAGEBLOCKS) { +// Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged]; + Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged]; +} if (TYPEDARRAYS) { - Memory.afnBigEndian = [Memory.prototype.readByteBigEndian, Memory.prototype.readShortBigEndian, Memory.prototype.readLongBigEndian, Memory.prototype.writeByteBigEndian, Memory.prototype.writeShortBigEndian, Memory.prototype.writeLongBigEndian]; - Memory.afnLittleEndian = [Memory.prototype.readByteLittleEndian, Memory.prototype.readShortLittleEndian, Memory.prototype.readLongLittleEndian, Memory.prototype.writeByteLittleEndian, Memory.prototype.writeShortLittleEndian, Memory.prototype.writeLongLittleEndian]; + Memory.afnBigEndian = [Memory.prototype.readByteBigEndian, Memory.prototype.readShortBigEndian, Memory.prototype.readLongBigEndian, Memory.prototype.writeByteBigEndian, Memory.prototype.writeShortBigEndian, Memory.prototype.writeLongBigEndian]; + Memory.afnLittleEndian = [Memory.prototype.readByteLittleEndian, Memory.prototype.readShortLittleEndian, Memory.prototype.readLongLittleEndian, Memory.prototype.writeByteLittleEndian, Memory.prototype.writeShortLittleEndian, Memory.prototype.writeLongLittleEndian]; } if (typeof module !== 'undefined') module.exports = Memory; diff --git a/modules/pcjs/lib/ram.js b/modules/pcjs/lib/ram.js index ad4adfa8c..8603cfaa3 100644 --- a/modules/pcjs/lib/ram.js +++ b/modules/pcjs/lib/ram.js @@ -383,13 +383,14 @@ CompaqController.RAMSETUP = { }; /** - * readByte(off) + * readByte(off, addr) * * @this {Memory} * @param {number} off (relative to 0x80C00000) + * @param {number} [addr] * @return {number} */ -CompaqController.readByte = function readCompaqControllerByte(off) +CompaqController.readByte = function readCompaqControllerByte(off, addr) { var b = 0xff; if (off < 0x02) { @@ -400,19 +401,20 @@ CompaqController.readByte = function readCompaqControllerByte(off) } if (DEBUG) { this.controller.ram.printMessage("CompaqController.readByte(" + str.toHexWord(off) + ") returned " + str.toHexByte(b), true, true); - if (MAXDEBUG && DEBUGGER && off >= 0x2) this.cpu.stopCPU(); + if (MAXDEBUG && DEBUGGER && off >= 0x2) this.dbg.stopCPU(); } return b; }; /** - * writeByte(off, b) + * writeByte(off, b, addr) * * @this {Memory} * @param {number} off (relative to 0x80C00000) * @param {number} b + * @param {number} [addr] */ -CompaqController.writeByte = function writeCompaqControllerByte(off, b) +CompaqController.writeByte = function writeCompaqControllerByte(off, b, addr) { var controller = this.controller; @@ -443,7 +445,7 @@ CompaqController.writeByte = function writeCompaqControllerByte(off, b) } } controller.wMappings = (controller.wMappings & ~0xff) | b; - if (MAXDEBUG && DEBUGGER) this.cpu.stopCPU(); + if (MAXDEBUG && DEBUGGER) this.dbg.stopCPU(); } } /* @@ -451,7 +453,7 @@ CompaqController.writeByte = function writeCompaqControllerByte(off, b) */ else if (off == 0x2) { controller.wRAMSetup = (controller.wRAMSetup & ~0xff) | b; - if (MAXDEBUG && DEBUGGER) this.cpu.stopCPU(); + if (MAXDEBUG && DEBUGGER) this.dbg.stopCPU(); } /* * All bits in 0x80C00001 and 0x80C00003 are reserved, so we can simply ignore those writes. diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index 474e276c3..72e6fef37 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -1241,13 +1241,14 @@ Card.ACCESS.WRITE.MODE2XOR = 0xE000; Card.ACCESS.WRITE.MASK = 0xff00; /** - * readByteMode0(off) + * readByteMode0(off, addr) * * @this {Memory} * @param {number} off + * @param {number} [addr] * @return {number} */ -Card.ACCESS.readByteMode0 = function readByteMode0(off) +Card.ACCESS.readByteMode0 = function readByteMode0(off, addr) { off += this.offset; var dw = this.controller.latches = this.adw[off]; @@ -1255,13 +1256,14 @@ Card.ACCESS.readByteMode0 = function readByteMode0(off) }; /** - * readByteMode0EvenOdd(off) + * readByteMode0EvenOdd(off, addr) * * @this {Memory} * @param {number} off + * @param {number} [addr] * @return {number} */ -Card.ACCESS.readByteMode0EvenOdd = function readByteMode0EvenOdd(off) +Card.ACCESS.readByteMode0EvenOdd = function readByteMode0EvenOdd(off, addr) { off += this.offset; var idw = off & ~0x1; @@ -1269,13 +1271,14 @@ Card.ACCESS.readByteMode0EvenOdd = function readByteMode0EvenOdd(off) }; /** - * readByteMode1(off) + * readByteMode1(off, addr) * * @this {Memory} * @param {number} off + * @param {number} [addr] * @return {number} */ -Card.ACCESS.readByteMode1 = function readByteMode1(off) +Card.ACCESS.readByteMode1 = function readByteMode1(off, addr) { off += this.offset; var dw = this.adw[off]; @@ -1290,13 +1293,14 @@ Card.ACCESS.readByteMode1 = function readByteMode1(off) }; /** - * writeByteMode0(off, b) + * writeByteMode0(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b) +Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr) { var idw = off + this.offset; var dw = b | (b << 8) | (b << 16) | (b << 24); @@ -1309,13 +1313,14 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b) }; /** - * writeByteMode0EvenOdd(off, b) + * writeByteMode0EvenOdd(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b) +Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b, addr) { off += this.offset; var dw = b | (b << 8) | (b << 16) | (b << 24); @@ -1334,7 +1339,7 @@ Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b) }; /** - * writeByteMode0Rot(off, b) + * writeByteMode0Rot(off, b, addr) * * Supporting Set/Reset means that for every plane for which Set/Reset is enabled, we must * replace the corresponding byte in "dw" with a byte of zeros or ones. This is accomplished with @@ -1352,8 +1357,9 @@ Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b) * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0Rot = function writeByteMode0Rot(off, b) +Card.ACCESS.writeByteMode0Rot = function writeByteMode0Rot(off, b, addr) { var idw = off + this.offset; b = ((b >> this.controller.nDataRotate) | (b << (8 - this.controller.nDataRotate)) & 0xff); @@ -1368,13 +1374,14 @@ Card.ACCESS.writeByteMode0Rot = function writeByteMode0Rot(off, b) }; /** - * writeByteMode0And(off, b) + * writeByteMode0And(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0And = function writeByteMode0And(off, b) +Card.ACCESS.writeByteMode0And = function writeByteMode0And(off, b, addr) { var idw = off + this.offset; b = ((b >> this.controller.nDataRotate) | (b << (8 - this.controller.nDataRotate)) & 0xff); @@ -1390,13 +1397,14 @@ Card.ACCESS.writeByteMode0And = function writeByteMode0And(off, b) }; /** - * writeByteMode0Or(off, b) + * writeByteMode0Or(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0Or = function writeByteMode0Or(off, b) +Card.ACCESS.writeByteMode0Or = function writeByteMode0Or(off, b, addr) { var idw = off + this.offset; b = ((b >> this.controller.nDataRotate) | (b << (8 - this.controller.nDataRotate)) & 0xff); @@ -1412,13 +1420,14 @@ Card.ACCESS.writeByteMode0Or = function writeByteMode0Or(off, b) }; /** - * writeByteMode0Xor(off, b) + * writeByteMode0Xor(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode0Xor = function writeByteMode0Xor(off, b) +Card.ACCESS.writeByteMode0Xor = function writeByteMode0Xor(off, b, addr) { var idw = off + this.offset; b = ((b >> this.controller.nDataRotate) | (b << (8 - this.controller.nDataRotate)) & 0xff); @@ -1434,13 +1443,14 @@ Card.ACCESS.writeByteMode0Xor = function writeByteMode0Xor(off, b) }; /** - * writeByteMode1(off, b) + * writeByteMode1(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (ignored; the EGA latches provide the source data) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode1 = function writeByteMode1(off, b) +Card.ACCESS.writeByteMode1 = function writeByteMode1(off, b, addr) { var idw = off + this.offset; var dw = (this.adw[idw] & ~this.controller.nWriteMapMask) | (this.controller.latches & this.controller.nWriteMapMask); @@ -1451,13 +1461,14 @@ Card.ACCESS.writeByteMode1 = function writeByteMode1(off, b) }; /** - * writeByteMode2(off, b) + * writeByteMode2(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode2 = function writeByteMode2(off, b) +Card.ACCESS.writeByteMode2 = function writeByteMode2(off, b, addr) { var idw = off + this.offset; var dw = Video.aEGAByteToDW[b & 0xf]; @@ -1470,13 +1481,14 @@ Card.ACCESS.writeByteMode2 = function writeByteMode2(off, b) }; /** - * writeByteMode2And(off, b) + * writeByteMode2And(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode2And = function writeByteMode2And(off, b) +Card.ACCESS.writeByteMode2And = function writeByteMode2And(off, b, addr) { var idw = off + this.offset; var dw = Video.aEGAByteToDW[b & 0xf]; @@ -1490,13 +1502,14 @@ Card.ACCESS.writeByteMode2And = function writeByteMode2And(off, b) }; /** - * writeByteMode2Or(off, b) + * writeByteMode2Or(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode2Or = function writeByteMode2Or(off, b) +Card.ACCESS.writeByteMode2Or = function writeByteMode2Or(off, b, addr) { var idw = off + this.offset; var dw = Video.aEGAByteToDW[b & 0xf]; @@ -1510,13 +1523,14 @@ Card.ACCESS.writeByteMode2Or = function writeByteMode2Or(off, b) }; /** - * writeByteMode2Xor(off, b) + * writeByteMode2Xor(off, b, addr) * * @this {Memory} * @param {number} off * @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect) + * @param {number} [addr] */ -Card.ACCESS.writeByteMode2Xor = function writeByteMode2Xor(off, b) +Card.ACCESS.writeByteMode2Xor = function writeByteMode2Xor(off, b, addr) { var idw = off + this.offset; var dw = Video.aEGAByteToDW[b & 0xf]; diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 58d9040ae..878b01146 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -2525,7 +2525,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.busMask) >>> this.blockShift].readByte(addr & this.blockLimit); + return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit, addr); }; /** @@ -2550,9 +2550,9 @@ X86CPU.prototype.getShort = function(addr) this.backTrack.btiMemHi = this.bus.readBackTrack(addr + 1); } if (off < this.blockLimit) { - return this.aMemBlocks[iBlock].readShort(off); + return this.aMemBlocks[iBlock].readShort(off, addr); } - return this.aMemBlocks[iBlock].readByte(off) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readByte(0) << 8); + return this.aMemBlocks[iBlock].readByte(off, addr) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readByte(0, addr + 1) << 8); }; /** @@ -2571,10 +2571,10 @@ X86CPU.prototype.getLong = function(addr) this.backTrack.btiMemHi = this.bus.readBackTrack(addr + 1); } if (off < this.blockLimit - 2) { - return this.aMemBlocks[iBlock].readLong(off); + return this.aMemBlocks[iBlock].readLong(off, addr); } var nShift = (off & 0x3) << 3; - return (this.aMemBlocks[iBlock].readLong(off & ~0x3) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLong(0) << (32 - nShift)); + return (this.aMemBlocks[iBlock].readLong(off & ~0x3, addr) >>> nShift) | (this.aMemBlocks[(iBlock + 1) & this.blockMask].readLong(0, addr + 3) << (32 - nShift)); }; /** @@ -2587,7 +2587,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.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff); + this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff, addr); }; /** @@ -2612,11 +2612,11 @@ X86CPU.prototype.setShort = function(addr, w) this.bus.writeBackTrack(addr + 1, this.backTrack.btiMemHi); } if (off < this.blockLimit) { - this.aMemBlocks[iBlock].writeShort(off, w & 0xffff); + this.aMemBlocks[iBlock].writeShort(off, w & 0xffff, addr); return; } - this.aMemBlocks[iBlock++].writeByte(off, w & 0xff); - this.aMemBlocks[iBlock & this.blockMask].writeByte(0, (w >> 8) & 0xff); + this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr); + this.aMemBlocks[iBlock & this.blockMask].writeByte(0, (w >> 8) & 0xff, addr + 1); }; /** @@ -2637,16 +2637,17 @@ X86CPU.prototype.setLong = function(addr, l) this.bus.writeBackTrack(addr + 1, this.backTrack.btiMemHi); } if (off < this.blockLimit - 2) { - this.aMemBlocks[iBlock].writeLong(off, l); + this.aMemBlocks[iBlock].writeLong(off, l, addr); return; } var lPrev, nShift = (off & 0x3) << 3; off &= ~0x3; - lPrev = this.aMemBlocks[iBlock].readLong(off); - this.aMemBlocks[iBlock].writeLong(off, (lPrev & ~(-1 << nShift)) | (l << nShift)); + lPrev = this.aMemBlocks[iBlock].readLong(off, addr); + this.aMemBlocks[iBlock].writeLong(off, (lPrev & ~(-1 << nShift)) | (l << nShift), addr); iBlock = (iBlock + 1) & this.blockMask; - lPrev = this.aMemBlocks[iBlock].readLong(0); - this.aMemBlocks[iBlock].writeLong(0, (lPrev & (-1 << nShift)) | (l >>> (32 - nShift))); + addr += 3; + lPrev = this.aMemBlocks[iBlock].readLong(0, addr); + this.aMemBlocks[iBlock].writeLong(0, (lPrev & (-1 << nShift)) | (l >>> (32 - nShift)), addr); }; /** @@ -2950,7 +2951,7 @@ 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.busMask) >>> this.blockShift].readByte(addr & this.blockLimit); + * b = this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit, addr); * this.nBusCycles += 4; * this.cbPrefetchValid = 0; * this.addrPrefetchHead = (addr + 1) & this.busMask; @@ -3027,7 +3028,7 @@ X86CPU.prototype.fillPrefetch = function(n) { while (n-- > 0 && this.cbPrefetchQueued < X86CPU.PREFETCH.QUEUE) { var addr = this.addrPrefetchHead; - var b = this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit); + var b = this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit, addr); 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.busMask;