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
This commit is contained in:
Jeff Parsons 2015-08-06 11:25:56 -07:00
commit 33d28a29de
3 changed files with 36 additions and 63 deletions

View file

@ -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;

View file

@ -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)
*

View file

@ -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)
*