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:
parent
9a642f8cd0
commit
3db1439b40
11 changed files with 498 additions and 406 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue