Improved Debugger focus handling, added a new zeroMemory() Bus interface, and updated the RAM component's reset() function to re-zero RAM and then restore any predefined contents

This commit is contained in:
Jeff Parsons 2016-10-21 12:15:02 -07:00 committed by Jeff Parsons
commit 3db1439b40
11 changed files with 498 additions and 406 deletions

View file

@ -151,7 +151,7 @@ function BusPDP11(parmsBus, cpu, dbg)
*/
this.aIOHandlers = [];
this.fIOBreakAll = false;
this.nDisableTraps = 0;
this.nDisableFaults = 0;
/*
* Array of RESET notification handlers registered by Device components.
@ -558,7 +558,7 @@ BusPDP11.prototype.unknownAccess = function(addr, fByte, data)
this.dbg.printMessage("warning: unknown I/O access (" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(data, fByte?1:2) + ")", true, true);
if (this.dbg.stopInstruction()) return 0;
}
if (!this.nDisableTraps) {
if (!this.nDisableFaults) {
this.cpu.trap(PDP11.TRAP.BUS_ERROR, addr);
}
return 0;
@ -700,6 +700,25 @@ BusPDP11.prototype.cleanMemory = function(addr, size)
return fClean;
};
/**
* zeroMemory(addr, size)
*
* @this {BusPDP11}
* @param {number} addr
* @param {number} size
*/
BusPDP11.prototype.zeroMemory = function(addr, size)
{
var off = addr & this.nBlockLimit;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
this.aMemBlocks[iBlock].zero(off, size);
size -= this.nBlockSize;
iBlock++;
off = 0;
}
};
/*
* Data types used by scanMemory()
*/
@ -906,9 +925,9 @@ BusPDP11.prototype.getByte = function(addr)
*/
BusPDP11.prototype.getByteDirect = function(addr)
{
this.nDisableTraps++;
this.nDisableFaults++;
var b = this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByteDirect(addr & this.nBlockLimit, addr);
this.nDisableTraps--;
this.nDisableFaults--;
return b;
};
@ -943,13 +962,13 @@ BusPDP11.prototype.getWordDirect = function(addr)
var w;
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.nDisableTraps++;
this.nDisableFaults++;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
w = this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
} else {
w = this.aMemBlocks[iBlock].readWordDirect(off, addr);
}
this.nDisableTraps--;
this.nDisableFaults--;
return w;
};
@ -977,9 +996,9 @@ BusPDP11.prototype.setByte = function(addr, b)
*/
BusPDP11.prototype.setByteDirect = function(addr, b)
{
this.nDisableTraps++;
this.nDisableFaults++;
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
this.nDisableTraps--;
this.nDisableFaults--;
};
/**
@ -1015,14 +1034,14 @@ BusPDP11.prototype.setWordDirect = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.nDisableTraps++;
this.nDisableFaults++;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByteDirect(0, (w >> 8) & 0xff, addr + 1);
} else {
this.aMemBlocks[iBlock].writeWordDirect(off, w & 0xffff, addr);
}
this.nDisableTraps--;
this.nDisableFaults--;
};
/**
@ -1245,7 +1264,7 @@ BusPDP11.prototype.addResetHandler = function(fnReset)
*/
BusPDP11.prototype.fault = function(addr)
{
if (!this.nDisableTraps) {
if (!this.nDisableFaults) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.WARN)) {
this.dbg.printMessage("memory fault on address " + this.dbg.toStrBase(addr), true, true);
}

View file

@ -1398,7 +1398,7 @@ ComputerPDP11.prototype.getMachineComponent = function(sType, componentPrev)
};
/**
* updateFocus(fScroll)
* setFocus(fScroll)
*
* NOTE: When soft keyboard buttons call us to return focus to the machine (and away from the button),
* the browser's default behavior is to scroll the element into view, which can be annoying, especially on iOS,
@ -1407,8 +1407,9 @@ ComputerPDP11.prototype.getMachineComponent = function(sType, componentPrev)
* @this {ComputerPDP11}
* @param {boolean} [fScroll]
*/
ComputerPDP11.prototype.updateFocus = function(fScroll)
ComputerPDP11.prototype.setFocus = function(fScroll)
{
if (this.controlPrint) this.controlPrint.focus();
};
/**

View file

@ -260,10 +260,10 @@ CPUPDP11.prototype.powerUp = function(data, fRepower)
this.resetChecksum();
}
/*
* Give the Debugger a chance to do/print something once we've powered up
* Give the Debugger a chance to do/print something once we've powered up.
*/
if (DEBUGGER && this.dbg) {
this.dbg.init();
this.dbg.init(this.flags.autoStart);
} else {
/*
* The Computer (this.cmp) knows if there's a Control Panel (this.cmp.panel), and the Control Panel
@ -722,7 +722,7 @@ CPUPDP11.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
if (controlSpeed) controlSpeed.textContent = sSpeed;
this.println("target speed: " + sSpeed);
}
if (fUpdateFocus && this.cmp) this.cmp.updateFocus();
if (fUpdateFocus && this.cmp) this.cmp.setFocus();
}
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
@ -1130,7 +1130,7 @@ CPUPDP11.prototype.startCPU = function(fUpdateFocus)
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
if (fUpdateFocus) this.cmp.updateFocus(true);
if (fUpdateFocus) this.cmp.setFocus(true);
this.cmp.start(this.msStartRun, this.getCycles());
}
setTimeout(this.onRunTimeout, 0);

View file

@ -360,16 +360,17 @@ CPUStatePDP11.prototype.setMMR3 = function(newMMR3)
};
/**
* setReset(addr)
* setReset(addr, fReset)
*
* @this {CPUStatePDP11}
* @param {number} addr
* @param {boolean} [fReset] (true if called in the context of a complete reset, obviating the need to notify the Debugger)
*/
CPUStatePDP11.prototype.setReset = function(addr)
CPUStatePDP11.prototype.setReset = function(addr, fReset)
{
this.addrReset = addr;
this.setPC(addr);
if (this.dbg) {
if (!fReset && this.dbg) {
/*
* TODO: Review the decision to always stop the CPU if the Debugger is loaded.
*/

View file

@ -572,11 +572,11 @@ if (DEBUGGER) {
};
/**
* updateFocus()
* setFocus()
*
* @this {DebuggerPDP11}
*/
DebuggerPDP11.prototype.updateFocus = function()
DebuggerPDP11.prototype.setFocus = function()
{
if (this.controlDebug) this.controlDebug.focus();
};
@ -1216,12 +1216,14 @@ if (DEBUGGER) {
* init()
*
* @this {DebuggerPDP11}
* @param {boolean} [fAutoStart]
*/
DebuggerPDP11.prototype.init = function()
DebuggerPDP11.prototype.init = function(fAutoStart)
{
this.fInit = true;
this.println("Type ? for help with PDP11 Debugger commands");
this.updateStatus();
if (!fAutoStart) this.setFocus();
if (this.sInitCommands) {
var sCmds = this.sInitCommands;
this.sInitCommands = null;
@ -1567,7 +1569,7 @@ if (DEBUGGER) {
this.println(sStopped);
}
this.updateStatus(true);
this.updateFocus();
this.setFocus();
this.clearTempBreakpoint(this.cpu.getPC());
}
};
@ -2903,9 +2905,12 @@ if (DEBUGGER) {
* And while we used to always call getByte() and assemble them into words or dwords as appropriate, I've
* changed the logic below to honor "dw" by calling getWord(), since the Bus interfaces have been updated
* to prevent generating traps due to to Debugger access of unaligned memory and/or undefined IOPAGE addresses.
*
* Besides, it's nice for "db" and "dw" to generate the same Bus activity that typical byte and word reads do.
*/
for (i = (size == 4? 16 : this.nBase); i > 0 && nBytes > 0; i--) {
var n, v = size == 2? this.getWord(dbgAddr, n = 2) : this.getByte(dbgAddr, n = 1);
var n = 1;
var v = size == 1? this.getByte(dbgAddr, n) : this.getWord(dbgAddr, (n = 2));
data |= (v << (shift << 3));
shift += n;
if (shift == size) {
@ -3445,7 +3450,7 @@ if (DEBUGGER) {
if (this.nStep) {
this.setTempBreakpoint(dbgAddr);
if (!this.startCPU()) {
if (this.cmp) this.cmp.updateFocus();
if (this.cmp) this.cmp.setFocus();
this.nStep = 0;
}
/*

View file

@ -98,7 +98,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
*/
function MemoryPDP11(bus, addr, used, size, type, controller)
{
var i;
var a, i;
this.bus = bus;
this.id = (MemoryPDP11.idBlock += 2);
this.adw = null;
@ -127,7 +127,7 @@ function MemoryPDP11(bus, addr, used, size, type, controller)
/*
* For empty memory blocks, all we need to do is ensure all access functions are mapped to "none" handlers.
*/
if (!size) {
if (!this.size) {
this.setAccess();
return;
}
@ -138,7 +138,7 @@ function MemoryPDP11(bus, addr, used, size, type, controller)
*/
if (controller) {
this.controller = controller;
var a = controller.getControllerBuffer(addr);
a = controller.getControllerBuffer(addr);
this.adw = a[0];
this.offset = a[1];
this.setAccess(controller.getControllerAccess());
@ -154,20 +154,24 @@ function MemoryPDP11(bus, addr, used, size, type, controller)
* mode; pseudo-random might be best, to help make any bugs reproducible.
*/
if (TYPEDARRAYS) {
this.buffer = new ArrayBuffer(size);
this.dv = new DataView(this.buffer, 0, size);
this.buffer = new ArrayBuffer(this.size);
this.dv = new DataView(this.buffer, 0, this.size);
/*
* If littleEndian is true, we can use ab[], aw[] and adw[] directly; well, we can use them
* whenever the offset is a multiple of 1, 2 or 4, respectively. Otherwise, we must fallback to
* dv.getUint8()/dv.setUint8(), dv.getUint16()/dv.setUint16() and dv.getInt32()/dv.setInt32().
*/
this.ab = new Uint8Array(this.buffer, 0, size);
this.aw = new Uint16Array(this.buffer, 0, size >> 1);
this.adw = new Int32Array(this.buffer, 0, size >> 2);
this.ab = new Uint8Array(this.buffer, 0, this.size);
this.aw = new Uint16Array(this.buffer, 0, this.size >> 1);
this.adw = new Int32Array(this.buffer, 0, this.size >> 2);
this.setAccess(littleEndian? MemoryPDP11.afnArrayLE : MemoryPDP11.afnArrayBE);
} else {
/*
* NOTE: An ArrayBuffer is defined as being zero-initialized, but the elements of a new
* Array are not, so this code path takes care of zero-initialization ourselves.
*/
if (BYTEARRAYS) {
this.ab = new Array(size);
a = this.ab = new Array(this.size);
} else {
/*
* NOTE: This is the default mode of operation (!TYPEDARRAYS && !BYTEARRAYS), because it
@ -175,9 +179,9 @@ function MemoryPDP11(bus, addr, used, size, type, controller)
* come at twice the overhead of TYPEDARRAYS, it's increasingly likely that the JavaScript
* runtime will notice that all we ever store are 32-bit values, and optimize accordingly.
*/
this.adw = new Array(size >> 2);
for (i = 0; i < this.adw.length; i++) this.adw[i] = 0;
a = this.adw = new Array(this.size >> 2);
}
for (i = 0; i < a.length; i++) a[i] = 0;
this.setAccess(MemoryPDP11.afnMemory);
}
}
@ -247,7 +251,7 @@ MemoryPDP11.prototype = {
this.addr = addr;
},
/**
* clone(mem, type)
* clone(mem, type, dbg)
*
* Converts the current Memory block (this) into a clone of the given Memory block (mem),
* and optionally overrides the current block's type with the specified type.
@ -381,6 +385,31 @@ MemoryPDP11.prototype = {
}
return false;
},
/**
* zero(off, len)
*
* Zeros the block. Supporting off and len parameters is probably overkill, and makes more
* work in the non-TYPEDARRAY, non-BYTEARRAY case, because there all we have is an array of DWORDs,
* but that's not the typical case.
*
* @this {MemoryPDP11}
* @param {number} [off] (optional starting byte offset within block)
* @param {number} [len] (optional maximum number of bytes; default is the entire block)
*/
zero: function(off, len) {
var i;
off = off || 0;
/*
* NOTE: If len happens to be larger than the block, that's OK, because we also bounds-check the index.
*/
if (len === undefined) len = this.size;
Component.assert(off >= 0 && off < this.size);
if (TYPEDARRAYS || BYTEARRAYS) {
for (i = off; len-- && i < this.ab.length; i++) this.ab[i] = 0;
} else {
for (i = off; len-- && i < this.size; i++) this.writeByteDirect(off, 0, this.addr + off);
}
},
/**
* setAccess(afn, fDirect)
*

View file

@ -324,6 +324,9 @@ PC11.prototype.powerDown = function(fSave, fShutdown)
/**
* reset()
*
* TODO: Consider making our reset() handler ALSO restore the original attached tape, in much the same
* way the RAM component now restores the original predefined memory or tape image after resetting the RAM.
*
* @this {PC11}
*/
PC11.prototype.reset = function()
@ -691,7 +694,7 @@ PC11.prototype.parseTape = function(sTapeName, sTapePath, nTapeTarget, aBytes, a
this.sTapePath = "";
this.sTapeSource = PC11.SOURCE.NONE;
this.nTapeTarget = PC11.TARGET.NONE;
this.status("error loading tape: " + sTapeName);
this.notice("No load address available for tape: " + sTapeName);
return;
}
this.status("tape loaded: " + sTapeName);

View file

@ -126,6 +126,16 @@ RAMPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
RAMPDP11.prototype.powerUp = function(data, fRepower)
{
if (this.aSymbols) {
if (this.dbg) {
this.dbg.addSymbols(this.id, this.addrRAM, this.sizeRAM, this.aSymbols);
}
/*
* Our only role in the handling of symbols is to hand them off to the Debugger at our
* first opportunity. Now that we've done that, our copy of the symbols, if any, are toast.
*/
delete this.aSymbols;
}
/*
* The Computer powers up the CPU last, at which point CPUState state is restored,
* which includes the Bus state, and since we use the Bus to allocate all our memory,
@ -210,13 +220,12 @@ RAMPDP11.prototype.initRAM = function()
* Too early...
*/
if (!this.abInit || !this.bus) return;
this.loadImage(this.abInit, this.addrLoad, this.addrExec, this.addrRAM);
/*
* TODO: Consider an option to retain this data and give the user a way of restoring the initial contents.
* NOTE: We now retain this data, so that reset() can return the RAM to its predefined state.
*
* delete this.abInit;
*/
delete this.abInit;
}
this.setReady();
}
@ -229,22 +238,26 @@ RAMPDP11.prototype.initRAM = function()
*/
RAMPDP11.prototype.reset = function()
{
/*
* If you want to zero RAM on reset, then this would be a good place to do it.
*/
if (this.fAllocated) {
this.bus.zeroMemory(this.addrRAM, this.sizeRAM);
if (this.abInit) {
this.loadImage(this.abInit, this.addrLoad, this.addrExec, this.addrRAM, true);
}
}
};
/**
* loadImage(aBytes, addrLoad, addrExec, addrInit)
* loadImage(aBytes, addrLoad, addrExec, addrInit, fReset)
*
* @this {RAMPDP11}
* @param {Array|Uint8Array} aBytes
* @param {number|null} [addrLoad]
* @param {number|null} [addrExec]
* @param {number|null} [addrInit]
* @param {boolean} [fReset]
* @return {boolean} (true if loaded, false if not)
*/
RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit)
RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fReset)
{
var fLoaded = false;
/*
@ -315,7 +328,7 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit)
if (addr & 0x1) {
this.cpu.stopCPU();
} else {
this.cpu.setReset(addr);
this.cpu.setReset(addr, fReset);
}
} else {
while (cbData--) {
@ -331,7 +344,7 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit)
for (var i = 0; i < aBytes.length; i++) {
this.cpu.setByteDirect(addrLoad + i, aBytes[i]);
}
if (addrExec != null) this.cpu.setReset(addrExec);
if (addrExec != null) this.cpu.setReset(addrExec, fReset);
fLoaded = true;
}
}