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); return this.reportError(1, addr, size);
} }
block = this.aMemBlocks[iBlock++] = new Memory(addr, sizeBlock, this.nBlockSize, type, controller); var blockOld = this.aMemBlocks[iBlock];
if (DEBUGGER && this.dbg) { var blockNew = new Memory(addr, sizeBlock, this.nBlockSize, type, controller);
block.setDebugger(this.dbg, addr, this.nBlockSize); blockNew.copyBreakpoints(blockOld, this.dbg);
} this.aMemBlocks[iBlock++] = blockNew;
size -= sizeBlock;
addr = addrBlock + this.nBlockSize; addr = addrBlock + this.nBlockSize;
size -= sizeBlock;
} }
if (size > 0) { if (size > 0) {
return this.reportError(2, addr, size); return this.reportError(2, addr, size);
@ -563,7 +563,7 @@ Bus.prototype.setMemoryAccess = function(addr, size, afn)
/** /**
* removeMemory(addr, size) * 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. * 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)) { if (!(addr & this.nBlockLimit) && size && !(size & this.nBlockLimit)) {
var iBlock = addr >>> this.nBlockShift; var iBlock = addr >>> this.nBlockShift;
while (size > 0) { 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; 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; size -= this.nBlockSize;
} }
return true; return true;
@ -633,10 +633,7 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
if (!block) break; if (!block) break;
if (type !== undefined) { if (type !== undefined) {
var blockNew = new Memory(addr); var blockNew = new Memory(addr);
if (DEBUGGER && this.dbg) { blockNew.clone(block, type, this.dbg);
blockNew.setDebugger(this.dbg, addr, this.nBlockSize);
}
blockNew.clone(block, type);
block = blockNew; block = blockNew;
} }
this.aMemBlocks[iBlock++] = block; this.aMemBlocks[iBlock++] = block;

View file

@ -3234,36 +3234,6 @@ if (DEBUGGER) {
return aBreak.length - 1; 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) * setTempBreakpoint(dbgAddr)
* *

View file

@ -110,6 +110,7 @@ function Memory(addr, used, size, type, controller, cpu)
this.controller = null; this.controller = null;
this.cpu = cpu; // If a CPU reference is provided, then this must be an UNPAGED Memory block allocation this.cpu = cpu; // If a CPU reference is provided, then this must be an UNPAGED Memory block allocation
this.fDirty = this.fDirtyEver = false; this.fDirty = this.fDirtyEver = false;
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
this.setPhysBlock(); this.setPhysBlock();
if (BACKTRACK) { if (BACKTRACK) {
@ -265,8 +266,9 @@ Memory.prototype = {
* @this {Memory} * @this {Memory}
* @param {Memory} mem * @param {Memory} mem
* @param {number} [type] * @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; * 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 * 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.type = type;
this.fReadOnly = (type == Memory.TYPE.ROM); this.fReadOnly = (type == Memory.TYPE.ROM);
} }
this.dbg = dbg;
if (TYPEDARRAYS) { if (TYPEDARRAYS) {
this.buffer = mem.buffer; this.buffer = mem.buffer;
this.dv = mem.dv; this.dv = mem.dv;
@ -471,22 +474,6 @@ Memory.prototype = {
this.writeShort = this.fReadOnly? this.writeShortDefault : this.writeShortDirect; this.writeShort = this.fReadOnly? this.writeShortDefault : this.writeShortDirect;
this.writeLong = this.fReadOnly? this.writeLongDefault : this.writeLongDirect; 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) * 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 * existence of a CPU reference only impacts the performance of the "checked" memory access functions, so it's
* not critical to eliminate it. * not critical to eliminate it.
* *
* TODO: Another option would be to count CPU references separately from Debugger references, so that when the * TODO: Another option would be to count CPU references separately from Debugger references, so that when
* former goes to zero, we can unconditionally remove the CPU reference; UNPAGED blocks would automatically * 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. * increment that reference count, so their CPU reference would never go away.
* *
* @this {Memory} * @this {Memory}
@ -583,6 +570,25 @@ Memory.prototype = {
Component.assert(this.cWriteBreakpoints >= 0); 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) * readNone(off)
* *