Debugger improvements (dumping/setting MMU registers)

This commit is contained in:
Jeff 2016-11-16 17:44:52 -08:00 committed by Jeff Parsons
commit 2b23c86649
8 changed files with 709 additions and 473 deletions

View file

@ -243,17 +243,18 @@ CPUStatePDP11.prototype.resetCPU = function()
*/
CPUStatePDP11.prototype.resetMMU = function()
{
this.regSL = 0xff; // 177774
this.regErr = 0; // 177766
this.regPIR = 0; // 177772
this.regMMR0 = 0; // 177572
this.regMMR1 = 0; // 177574
this.regMMR2 = 0; // 177576
this.regMMR3 = 0; // 172516
this.regErr = 0; // 177766
this.regPIR = 0; // 177772
this.regSL = 0xff; // 177774
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
this.mmuLastMode = 0;
this.mmuMask = 0x3ffff;
this.lastAddr = 0; // this is queried by the Panel when it's not using its own ADDRESS register
this.lastOp = 0; // stores the PC and any auto-incs or auto-decs from the last opcode; used to update MMR1 and MMR2
this.resetTriggers();
@ -684,6 +685,17 @@ CPUStatePDP11.prototype.getPC = function()
return this.regsGen[PDP11.REG.PC];
};
/**
* getLastAddr()
*
* @this {CPUStatePDP11}
* @return {number}
*/
CPUStatePDP11.prototype.getLastAddr = function()
{
return this.lastAddr;
};
/**
* getLastPC()
*
@ -1605,8 +1617,13 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
newMMR0 |= (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
this.assert(!(newMMR0 & ~PDP11.MMR0.UPDATE));
this.setMMR0((this.regMMR0 & ~PDP11.MMR0.UPDATE) | newMMR0);
/*
* I've decided to NOT set fAbort if MMR0 already indicates an ABORT condition,
* because otherwise we run the risk of infinitely looping; eg, we call trap(), which
* calls mapVirtualToPhysical() on the trap vector, which faults again, etc.
*/
fAbort = true;
}
fAbort = true;
}
if (!(this.regMMR0 & (PDP11.MMR0.ABORT | PDP11.MMR0.TRAP_MMU))) {
/*
@ -1999,7 +2016,7 @@ CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, accessFlags)
*/
CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
{
return this.bus.getWord(physicalAddress);
return this.bus.getWord(this.lastAddr = physicalAddress);
};
/**
@ -2013,7 +2030,7 @@ CPUStatePDP11.prototype.readWordFromPhysical = function(physicalAddress)
*/
CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
{
return this.bus.getWord(this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
return this.bus.getWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.READ_WORD));
};
/**
@ -2027,7 +2044,7 @@ CPUStatePDP11.prototype.readWordFromVirtual = function(virtualAddress)
*/
CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
{
this.bus.setWord(physicalAddress, data & 0xffff);
this.bus.setWord(this.lastAddr = physicalAddress, data & 0xffff);
};
/**
@ -2041,7 +2058,7 @@ CPUStatePDP11.prototype.writeWordToPhysical = function(physicalAddress, data)
*/
CPUStatePDP11.prototype.writeWordToVirtual = function(virtualAddress, data)
{
this.bus.setWord(this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.WRITE_WORD), data);
this.bus.setWord(this.lastAddr = this.mapVirtualToPhysical(virtualAddress, PDP11.ACCESS.WRITE_WORD), data);
};
/**

View file

@ -266,7 +266,32 @@ if (DEBUGGER) {
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt, and 16-19 for cpu.regsStack.
*/
DebuggerPDP11.REG_PSW = 20;
DebuggerPDP11.REG_SR = 21; // SWITCH register; see Panel's getSR() and setSR()
DebuggerPDP11.REG_PIR = 21;
DebuggerPDP11.REG_ERR = 22;
DebuggerPDP11.REG_SL = 23;
DebuggerPDP11.REG_MMR0 = 24;
DebuggerPDP11.REG_MMR1 = 25;
DebuggerPDP11.REG_MMR2 = 26;
DebuggerPDP11.REG_MMR3 = 27;
DebuggerPDP11.REG_AR = 28; // ADDRESS register; see Panel's getAR() and setAR()
DebuggerPDP11.REG_DR = 29; // DISPLAY/DATA register; see Panel's getDR() and setDR()
DebuggerPDP11.REG_SR = 30; // SWITCH register; see Panel's getSR() and setSR()
DebuggerPDP11.REGS = {
"SP": 6,
"PC": 7,
"PS": DebuggerPDP11.REG_PSW,
"IR": DebuggerPDP11.REG_PIR,
"ER": DebuggerPDP11.REG_ERR,
"SL": DebuggerPDP11.REG_SL,
"MMR0": DebuggerPDP11.REG_MMR0,
"MMR1": DebuggerPDP11.REG_MMR1,
"MMR2": DebuggerPDP11.REG_MMR2,
"MMR3": DebuggerPDP11.REG_MMR3,
"AR": DebuggerPDP11.REG_AR,
"DR": DebuggerPDP11.REG_DR,
"SR": DebuggerPDP11.REG_SR
};
/*
* Operand type masks; anything that's not covered by OP_SRC or OP_DST must be a OP_OTHER value.
@ -1118,24 +1143,14 @@ if (DEBUGGER) {
*/
DebuggerPDP11.prototype.getRegIndex = function(sReg, off)
{
var iReg = -1;
sReg = sReg.toUpperCase();
switch(sReg) {
case "SP":
iReg = 6;
break;
case "PC":
iReg = 7;
break;
case "SR":
iReg = 21;
break;
default:
var iReg = DebuggerPDP11.REGS[sReg];
if (iReg == null) {
iReg = -1;
if (sReg.charAt(0) == "R") {
iReg = +sReg.charAt(1);
if (iReg < 0 || iReg > 7) iReg = -1;
}
break;
}
return iReg;
};
@ -1156,7 +1171,7 @@ if (DEBUGGER) {
* getRegValue(iReg)
*
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt,
* 16-19 for cpu.regsAltStack, 20 for regPSW, and 21 for SR (SWITCH register).
* 16-19 for cpu.regsAltStack, 20 for regPSW, etc.
*
* @this {DebuggerPDP11}
* @param {number} iReg
@ -1175,11 +1190,50 @@ if (DEBUGGER) {
else if (iReg < 20) {
value = this.cpu.regsAltStack[iReg-16];
}
else if (iReg == DebuggerPDP11.REG_PSW) {
value = this.cpu.getPSW();
}
else if (iReg == DebuggerPDP11.REG_SR && this.panel && this.panel.hasSwitches()) {
value = this.panel.getSR();
else {
var cpu = this.cpu;
var panel = this.panel;
switch(iReg) {
case DebuggerPDP11.REG_PSW:
value = this.cpu.getPSW();
break;
case DebuggerPDP11.REG_PIR:
value = cpu.regPIR;
break;
case DebuggerPDP11.REG_ERR:
value = cpu.regErr;
break;
case DebuggerPDP11.REG_SL:
value = cpu.regSL;
break;
case DebuggerPDP11.REG_MMR0:
value = panel.getMMR0();
break;
case DebuggerPDP11.REG_MMR1:
value = panel.getMMR1();
break;
case DebuggerPDP11.REG_MMR2:
value = panel.getMMR2();
break;
case DebuggerPDP11.REG_MMR3:
value = panel.getMMR3();
break;
case DebuggerPDP11.REG_AR:
if (panel) {
value = panel.getAR();
}
break;
case DebuggerPDP11.REG_DR:
if (panel) {
value = panel.getDR();
}
break;
case DebuggerPDP11.REG_SR:
if (panel && panel.hasSwitches()) {
value = panel.getSR();
}
break;
}
}
}
return value;
@ -1371,6 +1425,7 @@ if (DEBUGGER) {
* is calling us in a loop, in which case it will perform its own updateDisplays() when it's done.
*/
if (fUpdateDisplays !== false) {
if (this.panel && this.panel.stop) this.panel.stop();
this.cmp.updateDisplays(-1);
}
@ -2393,28 +2448,86 @@ if (DEBUGGER) {
else if (iReg >= 16 && iReg < 20) {
sReg = "S" + (iReg - 16) + '=' + this.toStrBase(cpu.regsAltStack[iReg - 16]);
}
else if (iReg == DebuggerPDP11.REG_PSW) {
sReg = "PS=" + this.toStrBase(cpu.getPSW());
}
else if (iReg == DebuggerPDP11.REG_SR && this.panel && this.panel.hasSwitches()) {
sReg = "SR=" + this.toStrBase(this.panel.getSR(), 3);
else {
switch(iReg) {
case DebuggerPDP11.REG_PSW:
sReg = "PS=" + this.toStrBase(cpu.getPSW());
break;
case DebuggerPDP11.REG_PIR:
sReg = "IR=" + this.toStrBase(cpu.regPIR);
break;
case DebuggerPDP11.REG_ERR:
sReg = "ER=" + this.toStrBase(cpu.regErr);
break;
case DebuggerPDP11.REG_SL:
sReg = "SL=" + this.toStrBase(cpu.regSL);
break;
case DebuggerPDP11.REG_MMR0:
sReg = "MMR0=" + this.toStrBase(cpu.getMMR0());
break;
case DebuggerPDP11.REG_MMR1:
sReg = "MMR1=" + this.toStrBase(cpu.getMMR1());
break;
case DebuggerPDP11.REG_MMR2:
sReg = "MMR2=" + this.toStrBase(cpu.getMMR2());
break;
case DebuggerPDP11.REG_MMR3:
sReg = "MMR3=" + this.toStrBase(cpu.getMMR3());
break;
case DebuggerPDP11.REG_AR:
if (this.panel) {
sReg = "AR=" + this.toStrBase(this.panel.getAR(), 3);
}
break;
case DebuggerPDP11.REG_DR:
if (this.panel) {
sReg = "DR=" + this.toStrBase(this.panel.getDR());
}
break;
case DebuggerPDP11.REG_SR:
if (this.panel && this.panel.hasSwitches()) {
sReg = "SR=" + this.toStrBase(this.panel.getSR(), 3);
}
break;
}
}
if (sReg) sReg += ' ';
return sReg;
};
/**
* getRegDump()
* getMiscDump()
*
* Sample register dump:
*
* R0=xxxx R1=xxxx R2=xxxx R3=xxxx R4=xxxx R5=xxxx
* SP=xxxx PC=xxxx PS=xxxx T0 N0 Z0 V0 C0
* MMR0=xxxxxx MMR1=xxxxxx MMR2=xxxxxx MMR3=xxxxxx ER=xxxxxx
*
* @this {DebuggerPDP11}
* @return {string}
*/
DebuggerPDP11.prototype.getRegDump = function()
DebuggerPDP11.prototype.getMiscDump = function()
{
var sDump = "";
sDump += this.getRegOutput(DebuggerPDP11.REG_MMR0) + this.getRegOutput(DebuggerPDP11.REG_MMR1);
sDump += this.getRegOutput(DebuggerPDP11.REG_MMR2) + this.getRegOutput(DebuggerPDP11.REG_MMR3) + this.getRegOutput(DebuggerPDP11.REG_ERR);
sDump += '\n';
sDump += this.getRegOutput(DebuggerPDP11.REG_SR) + this.getRegOutput(DebuggerPDP11.REG_AR) + this.getRegOutput(DebuggerPDP11.REG_DR);
return sDump;
};
/**
* getRegDump(fMisc)
*
* Sample register dump:
*
* R0=xxxxxx R1=xxxxxx R2=xxxxxx R3=xxxxxx R4=xxxxxx R5=xxxxxx
* SP=xxxxxx PC=xxxxxx PS=xxxxxx IR=xxxxxx SL=xxxxxx T0 N0 Z0 V0 C0
*
* @this {DebuggerPDP11}
* @param {boolean} [fMisc] (true to include misc registers)
* @return {string}
*/
DebuggerPDP11.prototype.getRegDump = function(fMisc)
{
var i;
var sDump = "";
@ -2423,8 +2536,9 @@ if (DEBUGGER) {
}
sDump += '\n';
sDump += this.getRegOutput(PDP11.REG.SP) + this.getRegOutput(PDP11.REG.PC);
sDump += this.getRegOutput(DebuggerPDP11.REG_PSW) + this.getRegOutput(DebuggerPDP11.REG_SR);
sDump += this.getRegOutput(DebuggerPDP11.REG_PSW) + this.getRegOutput(DebuggerPDP11.REG_PIR) + this.getRegOutput(DebuggerPDP11.REG_SL);
sDump += this.getFlagOutput('T') + this.getFlagOutput('N') + this.getFlagOutput('Z') + this.getFlagOutput('V') + this.getFlagOutput('C');
if (fMisc) sDump += '\n' + this.getMiscDump();
return sDump;
};
@ -3390,77 +3504,115 @@ if (DEBUGGER) {
if (asArgs && asArgs[1] == '?') {
this.println("register commands:");
this.println("\tr\tdump registers");
this.println("\trm\tdump misc registers");
this.println("\trx [#]\tset flag or register x to [#]");
return;
}
var fMisc = false;
var cpu = this.cpu;
if (fInstruction == null) fInstruction = true;
if (asArgs != null && asArgs.length > 1) {
var sReg = asArgs[1];
var sValue = null;
var i = sReg.indexOf('=');
if (i > 0) {
sValue = sReg.substr(i + 1);
sReg = sReg.substr(0, i);
}
else if (asArgs.length > 2) {
sValue = asArgs[2];
if (sReg == 'm') {
fMisc = true;
}
else {
this.println("missing value for " + asArgs[1]);
return;
}
var w = this.parseExpression(sValue);
if (w === undefined) return;
var sRegMatch = sReg.toUpperCase();
switch (sRegMatch) {
case "SP":
case "R6":
cpu.setSP(w);
break;
case "PC":
case "R7":
cpu.setPC(w);
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
break;
case "N":
if (w) cpu.setNF(); else cpu.clearNF();
break;
case "Z":
if (w) cpu.setZF(); else cpu.clearZF();
break;
case "V":
if (w) cpu.setVF(); else cpu.clearVF();
break;
case "C":
if (w) cpu.setCF(); else cpu.clearCF();
break;
case "SR":
if (this.panel && this.panel.hasSwitches()) {
this.panel.setSR(w);
break;
var sValue = null;
var i = sReg.indexOf('=');
if (i > 0) {
sValue = sReg.substr(i + 1);
sReg = sReg.substr(0, i);
}
/* falls through */
default:
if (sRegMatch.charAt(0) == 'R') {
var iReg = +sRegMatch.charAt(1);
if (iReg >= 0 && iReg < 6) {
cpu.regsGen[iReg] = w & 0xffff;
else if (asArgs.length > 2) {
sValue = asArgs[2];
}
else {
this.println("missing value for " + asArgs[1]);
return;
}
var w = this.parseExpression(sValue);
if (w === undefined) return;
var sRegMatch = sReg.toUpperCase();
switch (sRegMatch) {
case "SP":
case "R6":
cpu.setSP(w);
break;
case "PC":
case "R7":
cpu.setPC(w);
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
break;
case "N":
if (w) cpu.setNF(); else cpu.clearNF();
break;
case "Z":
if (w) cpu.setZF(); else cpu.clearZF();
break;
case "V":
if (w) cpu.setVF(); else cpu.clearVF();
break;
case "C":
if (w) cpu.setCF(); else cpu.clearCF();
break;
case "PS":
cpu.setPSW(w);
break;
case "IR":
cpu.setPIR(w);
break;
case "ER":
cpu.regErr = w;
fMisc = true;
break;
case "SL":
cpu.setSL(w);
break;
case "MMR0":
cpu.setMMR0(w);
fMisc = true;
break;
case "MMR3":
cpu.setMMR3(w);
fMisc = true;
break;
case "AR":
if (this.panel) this.panel.setAR(w);
fMisc = true;
break;
case "DR":
if (this.panel) this.panel.setDR(w);
fMisc = true;
break;
case "SR":
if (this.panel && this.panel.hasSwitches()) {
this.panel.setSR(w);
fMisc = true;
break;
}
/* falls through */
default:
if (sRegMatch.charAt(0) == 'R') {
var iReg = +sRegMatch.charAt(1);
if (iReg >= 0 && iReg < 6) {
cpu.regsGen[iReg] = w & 0xffff;
break;
}
}
this.println("unknown register: " + sReg);
return;
}
this.println("unknown register: " + sReg);
return;
this.cmp.updateDisplays();
this.println("updated registers:");
}
this.cmp.updateDisplays();
this.println("updated registers:");
}
this.println(this.getRegDump());
this.println(this.getRegDump(fMisc));
if (fInstruction) {
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
@ -3716,7 +3868,7 @@ if (DEBUGGER) {
* a final updateDisplays().
*/
if (dbg.panel && dbg.panel.stop) dbg.panel.stop();
dbg.cmp.updateDisplays();
dbg.cmp.updateDisplays(-1);
dbg.setBusy(false);
}
);

View file

@ -323,7 +323,7 @@ var PDP11 = {
ENABLED: 0x0001, // 000001 address relocation enabled
PAGE_NUM: 0x000E, // 000016 page number of last fault
PAGE_D: 0x0010, // 000020 last fault occurred in D space
PAGE: 0x001E, // 000176 all of the PAGE bits
PAGE: 0x001E, // 000176 (all of the PAGE bits)
MODE: 0x0060, // 000140 processor mode as of last fault
COMPLETED: 0x0080, // 000200 last instruction completed (R/O)
DSTMODE: 0x0100, // 000400 only destination mode references will be relocated (aka MAINT bit)
@ -333,7 +333,7 @@ var PDP11 = {
ABORT_RO: 0x2000, // 020000 abort: read-only
ABORT_PL: 0x4000, // 040000 abort: page length
ABORT_NR: 0x8000, // 100000 abort: non-resident
ABORT: 0xE000, // 160000
ABORT: 0xE000, // 160000 (all of the ABORT bits)
UPDATE: 0xF0FE // Includes all of: ABORT, TRAP, COMPLETED, MODE, and PAGE bits
},
MMR1: { // 177574: general purpose auto-inc/auto-dec register

View file

@ -176,6 +176,50 @@ PanelPDP11.LED = {
B22: 'B22'
};
/**
* getAR()
*
* @this {PanelPDP11}
* @return {number} (current ADDRESS register)
*/
PanelPDP11.prototype.getAR = function()
{
return this.regAddr;
};
/**
* setAR(value)
*
* @this {PanelPDP11}
* @param {number} value (new ADDRESS register)
*/
PanelPDP11.prototype.setAR = function(value)
{
this.updateAddr(this.regAddr = value);
};
/**
* getDR()
*
* @this {PanelPDP11}
* @return {number} (current DISPLAY register)
*/
PanelPDP11.prototype.getDR = function()
{
return this.regDisplay;
};
/**
* setDR(value)
*
* @this {PanelPDP11}
* @param {number} value (new DISPLAY register)
*/
PanelPDP11.prototype.setDR = function(value)
{
this.updateData(this.regDisplay = value);
};
/**
* getSR()
*
@ -1004,7 +1048,7 @@ PanelPDP11.prototype.updateDisplay = function(nUpdate)
* TODO: There is currently no mechanism for selecting regData over regDisplay;
* we are acting as if the DATASEL switch setting is locked to "DISPLAY REGISTER".
*/
this.updateAddr(nUpdate > 0 && fRunning && !fWaiting? this.cpu.getPC() : this.regAddr);
this.updateAddr(nUpdate > 0 && fRunning && !fWaiting? this.cpu.getLastAddr() : this.regAddr);
this.updateData(this.regDisplay);
var bits = this.cpu.getMMUState();

View file

@ -244,6 +244,10 @@ SerialPortPDP11.prototype.setBinding = function(sType, sBinding, control, sValue
if (event.preventDefault) event.preventDefault();
var clipboardData = event.clipboardData || window.clipboardData;
if (clipboardData) {
/*
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
* this is dealt with in receiveData() whenever it receives a string of characters.
*/
serial.receiveData(clipboardData.getData('Text'));
}
};
@ -522,8 +526,20 @@ SerialPortPDP11.prototype.receiveData = function(data)
this.abReceive.push(data);
}
else if (typeof data == "string") {
var b = 0, bPrev;
for (var i = 0; i < data.length; i++) {
this.abReceive.push(data.charCodeAt(i));
bPrev = b;
b = data.charCodeAt(i);
/*
* NOTE: Multiple lines of pasted text will (at least on macOS) contain LFs instead of CRs;
* we convert them to CRs below. Windows may do something different, but in the worst case,
* even if we receive CR/LF pairs, this code should keep the CRs and lose the LFs.
*/
if (b == str.ASCII.LF) {
if (bPrev == str.ASCII.CR) continue;
b = str.ASCII.CR;
}
this.abReceive.push(b);
}
}
else {

View file

@ -551,6 +551,10 @@ str.trim = function(s)
/*
* Any codes commented out in the following table are deemed "printable"
*/
str.ASCII = {
LF: 0x0A,
CR: 0x0D
};
str.aASCIICodes = {
0x00: "NUL",
0x01: "SOH", // (CTRL_A) Start of Heading