From 33d28a29deae10a8d3c70d81762639b6b31c450d Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Thu, 6 Aug 2015 11:25:56 -0700 Subject: [PATCH] Improved preservation of read/write breakpoints in dynamic memory regions The old solution was Debugger-specific (ie, redoBreakpoints), the new solution works for both the Debugger and for Debug register support --- modules/pcjs/lib/bus.js | 25 +++++++++----------- modules/pcjs/lib/debugger.js | 30 ------------------------ modules/pcjs/lib/memory.js | 44 ++++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 63 deletions(-) diff --git a/modules/pcjs/lib/bus.js b/modules/pcjs/lib/bus.js index e3effc737..3e015c55b 100644 --- a/modules/pcjs/lib/bus.js +++ b/modules/pcjs/lib/bus.js @@ -394,12 +394,12 @@ Bus.prototype.addMemory = function(addr, size, type, controller) } return this.reportError(1, addr, size); } - block = this.aMemBlocks[iBlock++] = new Memory(addr, sizeBlock, this.nBlockSize, type, controller); - if (DEBUGGER && this.dbg) { - block.setDebugger(this.dbg, addr, this.nBlockSize); - } - size -= sizeBlock; + var blockOld = this.aMemBlocks[iBlock]; + var blockNew = new Memory(addr, sizeBlock, this.nBlockSize, type, controller); + blockNew.copyBreakpoints(blockOld, this.dbg); + this.aMemBlocks[iBlock++] = blockNew; addr = addrBlock + this.nBlockSize; + size -= sizeBlock; } if (size > 0) { return this.reportError(2, addr, size); @@ -563,7 +563,7 @@ Bus.prototype.setMemoryAccess = function(addr, size, afn) /** * removeMemory(addr, size) * - * Replaces every block in the specified address range with empty Memory blocks that will ignore all reads/writes. + * Replaces every block in the specified address range with empty Memory blocks that ignore all reads/writes. * * TODO: Update the removeMemory() interface to reflect the relaxed requirements of the addMemory() interface. * @@ -577,11 +577,11 @@ Bus.prototype.removeMemory = function(addr, size) if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) { var iBlock = addr >>> this.nBlockShift; while (size > 0) { + var blockOld = this.aMemBlocks[iBlock]; + var blockNew = new Memory(addr); + blockNew.copyBreakpoints(blockOld, this.dbg); + this.aMemBlocks[iBlock++] = blockNew; addr = iBlock * this.nBlockSize; - var block = this.aMemBlocks[iBlock++] = new Memory(addr); - if (DEBUGGER && this.dbg) { - block.setDebugger(this.dbg, addr, this.nBlockSize); - } size -= this.nBlockSize; } return true; @@ -633,10 +633,7 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type) if (!block) break; if (type !== undefined) { var blockNew = new Memory(addr); - if (DEBUGGER && this.dbg) { - blockNew.setDebugger(this.dbg, addr, this.nBlockSize); - } - blockNew.clone(block, type); + blockNew.clone(block, type, this.dbg); block = blockNew; } this.aMemBlocks[iBlock++] = block; diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index 55d7109bf..5ee82d3a5 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -3234,36 +3234,6 @@ if (DEBUGGER) { return aBreak.length - 1; }; - /** - * redoBreakpoints() - * - * This function is for the Memory component: whenever the Bus allocates a new Memory block, it calls - * 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), - * any breakpoints that were previously applied to that region will still work. - * - * @this {Debugger} - * @param {number} addr of memory block - * @param {number} size of memory block - * @param {Array} [aBreak] - */ - Debugger.prototype.redoBreakpoints = function(addr, size, aBreak) - { - if (aBreak === undefined) { - this.redoBreakpoints(addr, size, this.aBreakRead); - this.redoBreakpoints(addr, size, this.aBreakWrite); - return; - } - for (var i = 1; i < aBreak.length; i++) { - var addrBreak = this.getAddr(aBreak[i]); - if (addrBreak >= addr && addrBreak < addr + size) { - this.bus.addMemBreak(addrBreak, aBreak == this.aBreakWrite); - } - } - }; - /** * setTempBreakpoint(dbgAddr) * diff --git a/modules/pcjs/lib/memory.js b/modules/pcjs/lib/memory.js index 5234082fc..8de150ab5 100644 --- a/modules/pcjs/lib/memory.js +++ b/modules/pcjs/lib/memory.js @@ -110,6 +110,7 @@ function Memory(addr, used, size, type, controller, cpu) this.controller = null; this.cpu = cpu; // If a CPU reference is provided, then this must be an UNPAGED Memory block allocation this.fDirty = this.fDirtyEver = false; + this.cReadBreakpoints = this.cWriteBreakpoints = 0; this.setPhysBlock(); if (BACKTRACK) { @@ -265,8 +266,9 @@ Memory.prototype = { * @this {Memory} * @param {Memory} mem * @param {number} [type] + * @param {Debugger} [dbg] */ - clone: function(mem, type) { + clone: function(mem, type, dbg) { /* * Original memory block IDs are even; cloned memory block IDs are odd; * the original ID of the current block is lost, but that's OK, since it was presumably @@ -279,6 +281,7 @@ Memory.prototype = { this.type = type; this.fReadOnly = (type == Memory.TYPE.ROM); } + this.dbg = dbg; if (TYPEDARRAYS) { this.buffer = mem.buffer; this.dv = mem.dv; @@ -471,22 +474,6 @@ Memory.prototype = { this.writeShort = this.fReadOnly? this.writeShortDefault : this.writeShortDirect; this.writeLong = this.fReadOnly? this.writeLongDefault : this.writeLongDirect; }, - /** - * setDebugger(dbg, addr, size) - * - * @this {Memory} - * @param {Debugger} dbg - * @param {number} addr of block - * @param {number} size of block - */ - setDebugger: function(dbg, addr, size) { - if (DEBUGGER) { - this.dbg = dbg; - this.cReadBreakpoints = this.cWriteBreakpoints = 0; - Component.assert(this.dbg); - this.dbg.redoBreakpoints(addr, size); - } - }, /** * getPageBlock(addr, fWrite) * @@ -559,8 +546,8 @@ Memory.prototype = { * existence of a CPU reference only impacts the performance of the "checked" memory access functions, so it's * not critical to eliminate it. * - * TODO: Another option would be to count CPU references separately from Debugger references, so that when the - * former goes to zero, we can unconditionally remove the CPU reference; UNPAGED blocks would automatically + * TODO: Another option would be to count CPU references separately from Debugger references, so that when + * the former goes to zero, we can unconditionally remove the CPU reference; UNPAGED blocks would automatically * increment that reference count, so their CPU reference would never go away. * * @this {Memory} @@ -583,6 +570,25 @@ Memory.prototype = { Component.assert(this.cWriteBreakpoints >= 0); } }, + /** + * copyBreakpoints(mem) + * + * @this {Memory} + * @param {Memory|undefined} mem (outgoing Memory block to copy breakpoints from, if any) + * @param {Debugger} [dbg] + */ + copyBreakpoints: function(mem, dbg) { + if (mem) { + if (dbg) this.dbg = dbg; + if (mem.cpu) this.cpu = mem.cpu; + if ((this.cReadBreakpoints = mem.cReadBreakpoints)) { + this.setReadAccess(Memory.afnChecked, false); + } + if ((this.cWriteBreakpoints = mem.cWriteBreakpoints)) { + this.setWriteAccess(Memory.afnChecked, false); + } + } + }, /** * readNone(off) *