Minor Debugger improvements, notes, etc, while debugging the PDP-11 BASIC
This commit is contained in:
parent
8f4537c2bc
commit
aa2a25c5ec
7 changed files with 163 additions and 94 deletions
|
|
@ -16,4 +16,4 @@ PCjs has archived the following DEC resources:
|
||||||
|
|
||||||
- [Absolute Loader](DEC-11-L2PC-PO.json)
|
- [Absolute Loader](DEC-11-L2PC-PO.json)
|
||||||
- [LISTING OF PDP-11 ABSOLUTE LOADER (June 1975)](http://archive.pcjs.org/pubs/dec/pdp11/other/DEC-11-UABLA-A-LA_PDP-11AbsoluteLoaderListing_Jun75.pdf)
|
- [LISTING OF PDP-11 ABSOLUTE LOADER (June 1975)](http://archive.pcjs.org/pubs/dec/pdp11/other/DEC-11-UABLA-A-LA_PDP-11AbsoluteLoaderListing_Jun75.pdf)
|
||||||
- "APPENDIX D: THE BOOTSTRAP AND ABSOLUTE LOADERS" from the [PDP-11 BASIC PROGRAMMING MANUAL (December 1970)](http://archive.pcjs.org/pubs/dec/pdp11/other/BASIC_Programming_Manual_Dec70.pdf)
|
- "APPENDIX D: THE BOOTSTRAP AND ABSOLUTE LOADERS" from the [PDP-11 BASIC PROGRAMMING MANUAL (December 1970)](http://archive.pcjs.org/pubs/dec/pdp11/basic/DEC-11-AJPB-D_PDP-11_BASIC_Programming_Manual_Dec70.pdf)
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,55 @@ Try our [DEC BASIC Demo](/devices/pdp11/machine/1120/basic/).
|
||||||
PCjs has archived the following DEC resources:
|
PCjs has archived the following DEC resources:
|
||||||
|
|
||||||
- [BASIC (Single User)](DEC-11-AJPB-PB.json)
|
- [BASIC (Single User)](DEC-11-AJPB-PB.json)
|
||||||
- [PDP-11 BASIC PROGRAMMING MANUAL (December 1970)](http://archive.pcjs.org/pubs/dec/pdp11/other/BASIC_Programming_Manual_Dec70.pdf)
|
- [PDP-11 BASIC PROGRAMMING MANUAL (December 1970)](http://archive.pcjs.org/pubs/dec/pdp11/basic/DEC-11-AJPB-D_PDP-11_BASIC_Programming_Manual_Dec70.pdf)
|
||||||
|
|
||||||
Third-party resources include:
|
Third-party resources include:
|
||||||
|
|
||||||
- "[PDP-11 Paper Tape BASIC](http://www.avitech.com.au/ptb/ptb.html)", written March 24, 2013
|
- "[PDP-11 Paper Tape BASIC](http://www.avitech.com.au/ptb/ptb.html)", written March 24, 2013
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Debugging Notes
|
||||||
|
---------------
|
||||||
|
|
||||||
|
One of the first things I noticed when debugging PDP-11 BASIC was its reliance on TRAP instructions.
|
||||||
|
For example, `TRAP 000` used to output the character in R2 to the terminal. Let's take a closer look at how its
|
||||||
|
TRAP handler works.
|
||||||
|
|
||||||
|
First, if you check the table of PDP-11 trap vectors, you'll see that the vector for TRAP instructions is 000034.
|
||||||
|
So let's dump the contents of the two-word vector at 000034:
|
||||||
|
|
||||||
|
>> dw 034 l2
|
||||||
|
000034 000100 000000
|
||||||
|
|
||||||
|
The BASIC TRAP handler is at 000100, and here's that code:
|
||||||
|
|
||||||
|
>> u 000100
|
||||||
|
000100: 011666 000002 MOV @SP,2(SP)
|
||||||
|
000104: 162716 000002 SUB #2,@SP
|
||||||
|
000110: 013646 MOV @(SP)+,-(SP)
|
||||||
|
000112: 006216 ASR @SP
|
||||||
|
000114: 103404 BCS 000126
|
||||||
|
000116: 006316 ASL @SP
|
||||||
|
000120: 062716 073654 ADD #73654,@SP
|
||||||
|
000124: 013607 MOV @(SP)+,PC
|
||||||
|
|
||||||
|
When the code starts, the TRAP instruction has already pushed two words onto the stack:
|
||||||
|
|
||||||
|
0(SP): previous PC
|
||||||
|
2(SP): previous PSW
|
||||||
|
|
||||||
|
The first instruction, `MOV @SP,2(SP)`, copies the *previous PC* onto the *previous PSW*, which is where we'll eventually want
|
||||||
|
*previous PC*, so that the handler can eventually return with a simple `RTS PC`.
|
||||||
|
|
||||||
|
The next instruction, `SUB #2,@SP`, subtracts 2 from the original *previous PC*, so that it now points to the TRAP instruction.
|
||||||
|
|
||||||
|
Then `@(SP)+,-(SP)` fetches the TRAP instruction while also "popping" the original *previous PC* and then "pushing" TRAP
|
||||||
|
instruction onto the stack, overwriting the original *previous PC*.
|
||||||
|
|
||||||
|
The next few instructions shift the TRAP right to see if bit 0 is set, and if it is not, then the TRAP instruction is restored
|
||||||
|
by shifting it left again, and then a large offset is added to it, transforming the TRAP instruction (which is now known to be
|
||||||
|
an *even* value) into a jump table index.
|
||||||
|
|
||||||
|
The final instruction, `MOV @(SP)+,PC`, moves the address at the jump table index into PC, while also removing the TRAP
|
||||||
|
instruction from the stack, leaving only the *previous PC* on the stack.
|
||||||
|
|
|
||||||
|
|
@ -827,7 +827,7 @@ PDP11.opBPL = function(opCode)
|
||||||
*/
|
*/
|
||||||
PDP11.opBPT = function(opCode)
|
PDP11.opBPT = function(opCode)
|
||||||
{
|
{
|
||||||
this.trap(PDP11.TRAP.BREAKPOINT, PDP11.REASON.BPT);
|
this.trap(PDP11.TRAP.BPT, PDP11.REASON.BPT);
|
||||||
this.nStepCycles -= (4 + 1);
|
this.nStepCycles -= (4 + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1101,7 +1101,7 @@ PDP11.opDIV = function(opCode)
|
||||||
*/
|
*/
|
||||||
PDP11.opEMT = function(opCode)
|
PDP11.opEMT = function(opCode)
|
||||||
{
|
{
|
||||||
this.trap(PDP11.TRAP.EMULATOR, PDP11.REASON.EMT);
|
this.trap(PDP11.TRAP.EMT, PDP11.REASON.EMT);
|
||||||
this.nStepCycles -= (22 + 3);
|
this.nStepCycles -= (22 + 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1496,8 +1496,15 @@ PDP11.opRTS = function(opCode)
|
||||||
}
|
}
|
||||||
var src = this.popWord();
|
var src = this.popWord();
|
||||||
var reg = opCode & PDP11.OPREG.MASK;
|
var reg = opCode & PDP11.OPREG.MASK;
|
||||||
this.setPC(this.regsGen[reg]);
|
/*
|
||||||
this.regsGen[reg] = src;
|
* When the popular "RTS PC" form is used, we might as well eliminate the useless setting of PC to itself.
|
||||||
|
*/
|
||||||
|
if (reg == PDP11.REG.PC) {
|
||||||
|
this.setPC(src);
|
||||||
|
} else {
|
||||||
|
this.setPC(this.regsGen[reg]);
|
||||||
|
this.regsGen[reg] = src;
|
||||||
|
}
|
||||||
this.nStepCycles -= (7 + 2);
|
this.nStepCycles -= (7 + 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -547,31 +547,6 @@ CPUStatePDP11.prototype.setNF = function()
|
||||||
this.flagN = 0x8000;
|
this.flagN = 0x8000;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* getPC()
|
|
||||||
*
|
|
||||||
* @this {CPUStatePDP11}
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
CPUStatePDP11.prototype.getPC = function()
|
|
||||||
{
|
|
||||||
return this.regsGen[PDP11.REG.PC];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getLastPC()
|
|
||||||
*
|
|
||||||
* @this {CPUStatePDP11}
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
CPUStatePDP11.prototype.getLastPC = function()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* As long as we're always snapping the PC before every opcode, we might as well use it.
|
|
||||||
*/
|
|
||||||
return this.regMMR2;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getOpcode()
|
* getOpcode()
|
||||||
*
|
*
|
||||||
|
|
@ -595,6 +570,8 @@ CPUStatePDP11.prototype.getOpcode = function()
|
||||||
/**
|
/**
|
||||||
* advancePC(off)
|
* advancePC(off)
|
||||||
*
|
*
|
||||||
|
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||||
|
*
|
||||||
* @this {CPUStatePDP11}
|
* @this {CPUStatePDP11}
|
||||||
* @param {number} off
|
* @param {number} off
|
||||||
* @return {number} (original PC)
|
* @return {number} (original PC)
|
||||||
|
|
@ -606,9 +583,40 @@ CPUStatePDP11.prototype.advancePC = function(off)
|
||||||
return pc;
|
return pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getPC()
|
||||||
|
*
|
||||||
|
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||||
|
*
|
||||||
|
* @this {CPUStatePDP11}
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
CPUStatePDP11.prototype.getPC = function()
|
||||||
|
{
|
||||||
|
return this.regsGen[PDP11.REG.PC];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getLastPC()
|
||||||
|
*
|
||||||
|
* @this {CPUStatePDP11}
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
CPUStatePDP11.prototype.getLastPC = function()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* As long as we're always snapping the PC into regMMR2 before every opcode, we might as well use it.
|
||||||
|
*/
|
||||||
|
return this.regMMR2;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setPC()
|
* setPC()
|
||||||
*
|
*
|
||||||
|
* NOTE: Unlike other PCjs emulators, such as PCx86, where all PC updates MUST go through the setPC()
|
||||||
|
* function, this function is nothing more than a convenience, because in the PDP-11, the PC can be loaded
|
||||||
|
* like any other general register. We fully expect this function to be inlined at runtime.
|
||||||
|
*
|
||||||
* @this {CPUStatePDP11}
|
* @this {CPUStatePDP11}
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
|
|
@ -620,6 +628,8 @@ CPUStatePDP11.prototype.setPC = function(addr)
|
||||||
/**
|
/**
|
||||||
* getSP()
|
* getSP()
|
||||||
*
|
*
|
||||||
|
* NOTE: This function is nothing more than a convenience, and we fully expect it to be inlined at runtime.
|
||||||
|
*
|
||||||
* @this {CPUStatePDP11}
|
* @this {CPUStatePDP11}
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
|
|
@ -631,6 +641,10 @@ CPUStatePDP11.prototype.getSP = function()
|
||||||
/**
|
/**
|
||||||
* setSP()
|
* setSP()
|
||||||
*
|
*
|
||||||
|
* NOTE: Unlike other PCjs emulators, such as PCx86, where all SP updates MUST go through the setSP()
|
||||||
|
* function, this function is nothing more than a convenience, because in the PDP-11, the PC can be loaded
|
||||||
|
* like any other general register. We fully expect this function to be inlined at runtime.
|
||||||
|
*
|
||||||
* @this {CPUStatePDP11}
|
* @this {CPUStatePDP11}
|
||||||
* @param {number} addr
|
* @param {number} addr
|
||||||
*/
|
*/
|
||||||
|
|
@ -1326,7 +1340,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
||||||
if (!(this.regMMR0 & 0xe000)) {
|
if (!(this.regMMR0 & 0xe000)) {
|
||||||
this.regMMR0 |= errorMask | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
this.regMMR0 |= errorMask | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
|
||||||
}
|
}
|
||||||
this.trap(PDP11.TRAP.MMU_FAULT, PDP11.REASON.MAPERROR);
|
this.trap(PDP11.TRAP.MMU, PDP11.REASON.MAPERROR);
|
||||||
}
|
}
|
||||||
if (!(this.regMMR0 & 0xf000)) {
|
if (!(this.regMMR0 & 0xf000)) {
|
||||||
//if (physicalAddress < 017772200 || physicalAddress > 017777677) {
|
//if (physicalAddress < 017772200 || physicalAddress > 017777677) {
|
||||||
|
|
@ -2083,13 +2097,13 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
||||||
*/
|
*/
|
||||||
if (this.opFlags & PDP11.OPFLAG.TRAP_MASK) {
|
if (this.opFlags & PDP11.OPFLAG.TRAP_MASK) {
|
||||||
if (this.opFlags & PDP11.OPFLAG.TRAP_MMU) {
|
if (this.opFlags & PDP11.OPFLAG.TRAP_MMU) {
|
||||||
this.trap(PDP11.TRAP.MMU_FAULT, PDP11.REASON.TRAPMMU); // MMU trap has priority
|
this.trap(PDP11.TRAP.MMU, PDP11.REASON.TRAPMMU); // MMU trap has priority
|
||||||
} else {
|
} else {
|
||||||
if (this.opFlags & PDP11.OPFLAG.TRAP_SP) {
|
if (this.opFlags & PDP11.OPFLAG.TRAP_SP) {
|
||||||
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.TRAPSP); // then SP trap
|
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.TRAPSP); // then SP trap
|
||||||
} else {
|
} else {
|
||||||
if (this.opFlags & PDP11.OPFLAG.TRAP_TF) {
|
if (this.opFlags & PDP11.OPFLAG.TRAP_TF) {
|
||||||
this.trap(PDP11.TRAP.BREAKPOINT, PDP11.REASON.TRAPTF); // and finally a TF trap
|
this.trap(PDP11.TRAP.BPT, PDP11.REASON.TRAPTF); // and finally a TF trap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ function DebuggerPDP11(parmsDbg)
|
||||||
* The new "bn" command allows you to specify a number of instructions to execute and then stop;
|
* The new "bn" command allows you to specify a number of instructions to execute and then stop;
|
||||||
* "bn 0" disables any outstanding count.
|
* "bn 0" disables any outstanding count.
|
||||||
*/
|
*/
|
||||||
this.nBreakIns = 0;
|
this.nBreakInstructions = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execution history is allocated by historyInit() whenever checksEnabled() conditions change.
|
* Execution history is allocated by historyInit() whenever checksEnabled() conditions change.
|
||||||
|
|
@ -280,6 +280,7 @@ if (DEBUGGER) {
|
||||||
DebuggerPDP11.OP_DSTOFF = 0x2000;
|
DebuggerPDP11.OP_DSTOFF = 0x2000;
|
||||||
DebuggerPDP11.OP_DSTNUM3 = 0x3000; // DST 3-bit number (ie, just the DSTREG field)
|
DebuggerPDP11.OP_DSTNUM3 = 0x3000; // DST 3-bit number (ie, just the DSTREG field)
|
||||||
DebuggerPDP11.OP_DSTNUM6 = 0x6000; // DST 6-bit number (ie, both the DSTREG and DSTMODE fields)
|
DebuggerPDP11.OP_DSTNUM6 = 0x6000; // DST 6-bit number (ie, both the DSTREG and DSTMODE fields)
|
||||||
|
DebuggerPDP11.OP_DSTNUM8 = 0x8000; // DST 8-bit number
|
||||||
DebuggerPDP11.OP_OTHER = 0xF000;
|
DebuggerPDP11.OP_OTHER = 0xF000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -339,8 +340,8 @@ if (DEBUGGER) {
|
||||||
0x8500: [DebuggerPDP11.OPS.BVS, DebuggerPDP11.OP_BRANCH],
|
0x8500: [DebuggerPDP11.OPS.BVS, DebuggerPDP11.OP_BRANCH],
|
||||||
0x8600: [DebuggerPDP11.OPS.BCC, DebuggerPDP11.OP_BRANCH],
|
0x8600: [DebuggerPDP11.OPS.BCC, DebuggerPDP11.OP_BRANCH],
|
||||||
0x8700: [DebuggerPDP11.OPS.BCS, DebuggerPDP11.OP_BRANCH],
|
0x8700: [DebuggerPDP11.OPS.BCS, DebuggerPDP11.OP_BRANCH],
|
||||||
0x8800: [DebuggerPDP11.OPS.EMT], // 104000..104377
|
0x8800: [DebuggerPDP11.OPS.EMT, DebuggerPDP11.OP_DSTNUM8], // 104000..104377
|
||||||
0x8900: [DebuggerPDP11.OPS.TRAP] // 104400..104777
|
0x8900: [DebuggerPDP11.OPS.TRAP, DebuggerPDP11.OP_DSTNUM8] // 104400..104777
|
||||||
},
|
},
|
||||||
0xFFC0: {
|
0xFFC0: {
|
||||||
0x0040: [DebuggerPDP11.OPS.JMP, DebuggerPDP11.OP_DST], // 0001DD
|
0x0040: [DebuggerPDP11.OPS.JMP, DebuggerPDP11.OP_DST], // 0001DD
|
||||||
|
|
@ -932,8 +933,8 @@ if (DEBUGGER) {
|
||||||
{
|
{
|
||||||
var sMore = "";
|
var sMore = "";
|
||||||
var cHistory = 0;
|
var cHistory = 0;
|
||||||
var iHistory = this.iOpcodeHistory;
|
var iHistory = this.iInstructionHistory;
|
||||||
var aHistory = this.aOpcodeHistory;
|
var aHistory = this.aInstructionHistory;
|
||||||
|
|
||||||
if (aHistory.length) {
|
if (aHistory.length) {
|
||||||
var nPrev = +sPrev || this.nextHistory;
|
var nPrev = +sPrev || this.nextHistory;
|
||||||
|
|
@ -985,7 +986,7 @@ if (DEBUGGER) {
|
||||||
*
|
*
|
||||||
* If you re-enable this protection, be sure to re-enable the decrement below, too.
|
* If you re-enable this protection, be sure to re-enable the decrement below, too.
|
||||||
*/
|
*/
|
||||||
while (nLines > 0 && iHistory != this.iOpcodeHistory) {
|
while (nLines > 0 && iHistory != this.iInstructionHistory) {
|
||||||
|
|
||||||
var dbgAddr = aHistory[iHistory++];
|
var dbgAddr = aHistory[iHistory++];
|
||||||
if (dbgAddr.addr == null) break;
|
if (dbgAddr.addr == null) break;
|
||||||
|
|
@ -1245,34 +1246,27 @@ if (DEBUGGER) {
|
||||||
{
|
{
|
||||||
var i;
|
var i;
|
||||||
if (!this.checksEnabled()) {
|
if (!this.checksEnabled()) {
|
||||||
if (this.aOpcodeHistory && this.aOpcodeHistory.length && !fQuiet) {
|
if (this.aInstructionHistory && this.aInstructionHistory.length && !fQuiet) {
|
||||||
this.println("instruction history buffer freed");
|
this.println("instruction history buffer freed");
|
||||||
}
|
}
|
||||||
this.iOpcodeHistory = 0;
|
this.iInstructionHistory = 0;
|
||||||
this.aOpcodeHistory = [];
|
this.aInstructionHistory = [];
|
||||||
this.aaOpcodeCounts = [];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.aOpcodeHistory || !this.aOpcodeHistory.length) {
|
if (!this.aInstructionHistory || !this.aInstructionHistory.length) {
|
||||||
this.aOpcodeHistory = new Array(DebuggerPDP11.HISTORY_LIMIT);
|
this.aInstructionHistory = new Array(DebuggerPDP11.HISTORY_LIMIT);
|
||||||
for (i = 0; i < this.aOpcodeHistory.length; i++) {
|
for (i = 0; i < this.aInstructionHistory.length; i++) {
|
||||||
/*
|
/*
|
||||||
* Preallocate dummy Addr (Array) objects in every history slot, so that
|
* Preallocate dummy Addr (Array) objects in every history slot, so that
|
||||||
* checkInstruction() doesn't need to call newAddr() on every slot update.
|
* checkInstruction() doesn't need to call newAddr() on every slot update.
|
||||||
*/
|
*/
|
||||||
this.aOpcodeHistory[i] = this.newAddr();
|
this.aInstructionHistory[i] = this.newAddr();
|
||||||
}
|
}
|
||||||
this.iOpcodeHistory = 0;
|
this.iInstructionHistory = 0;
|
||||||
if (!fQuiet) {
|
if (!fQuiet) {
|
||||||
this.println("instruction history buffer allocated");
|
this.println("instruction history buffer allocated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.aaOpcodeCounts || !this.aaOpcodeCounts.length) {
|
|
||||||
this.aaOpcodeCounts = new Array(256);
|
|
||||||
for (i = 0; i < this.aaOpcodeCounts.length; i++) {
|
|
||||||
this.aaOpcodeCounts[i] = [i, 0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1324,7 +1318,7 @@ if (DEBUGGER) {
|
||||||
this.nCycles += nCyclesStep;
|
this.nCycles += nCyclesStep;
|
||||||
this.cpu.addCycles(nCyclesStep, true);
|
this.cpu.addCycles(nCyclesStep, true);
|
||||||
this.cpu.updateChecksum(nCyclesStep);
|
this.cpu.updateChecksum(nCyclesStep);
|
||||||
this.cOpcodes++;
|
this.cInstructions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(exception) {
|
catch(exception) {
|
||||||
|
|
@ -1456,7 +1450,7 @@ if (DEBUGGER) {
|
||||||
DebuggerPDP11.prototype.reset = function(fQuiet)
|
DebuggerPDP11.prototype.reset = function(fQuiet)
|
||||||
{
|
{
|
||||||
this.historyInit();
|
this.historyInit();
|
||||||
this.cOpcodes = this.cOpcodesStart = 0;
|
this.cInstructions = this.cInstructionsStart = 0;
|
||||||
this.sMessagePrev = null;
|
this.sMessagePrev = null;
|
||||||
this.nCycles = 0;
|
this.nCycles = 0;
|
||||||
this.dbgAddrNextCode = this.newAddr(this.cpu.getPC());
|
this.dbgAddrNextCode = this.newAddr(this.cpu.getPC());
|
||||||
|
|
@ -1550,14 +1544,14 @@ if (DEBUGGER) {
|
||||||
var nCyclesPerSecond = (msTotal > 0? Math.round(this.nCycles * 1000 / msTotal) : 0);
|
var nCyclesPerSecond = (msTotal > 0? Math.round(this.nCycles * 1000 / msTotal) : 0);
|
||||||
sStopped += " (";
|
sStopped += " (";
|
||||||
if (this.checksEnabled()) {
|
if (this.checksEnabled()) {
|
||||||
sStopped += this.cOpcodes + " opcodes, ";
|
sStopped += this.cInstructions + " instructions, ";
|
||||||
/*
|
/*
|
||||||
* $ops displays progress by calculating cOpcodes - cOpcodesStart, so before
|
* $ops displays progress by calculating cOpcodes - cOpcodesStart, so before
|
||||||
* zeroing cOpcodes, we should subtract cOpcodes from cOpcodesStart (since we're
|
* zeroing cOpcodes, we should subtract cOpcodes from cOpcodesStart (since we're
|
||||||
* effectively subtracting cOpcodes from cOpcodes as well).
|
* effectively subtracting cOpcodes from cOpcodes as well).
|
||||||
*/
|
*/
|
||||||
this.cOpcodesStart -= this.cOpcodes;
|
this.cInstructionsStart -= this.cInstructions;
|
||||||
this.cOpcodes = 0;
|
this.cInstructions = 0;
|
||||||
}
|
}
|
||||||
sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)";
|
sStopped += this.nCycles + " cycles, " + msTotal + " ms, " + nCyclesPerSecond + " hz)";
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1594,7 +1588,7 @@ if (DEBUGGER) {
|
||||||
*/
|
*/
|
||||||
DebuggerPDP11.prototype.checksEnabled = function(fRelease)
|
DebuggerPDP11.prototype.checksEnabled = function(fRelease)
|
||||||
{
|
{
|
||||||
return ((DEBUG && !fRelease)? true : (this.aBreakExec.length > 1 || !!this.nBreakIns));
|
return ((DEBUG && !fRelease)? true : (this.aBreakExec.length > 1 || !!this.nBreakInstructions));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1610,7 +1604,7 @@ if (DEBUGGER) {
|
||||||
*/
|
*/
|
||||||
DebuggerPDP11.prototype.checkInstruction = function(addr, nState)
|
DebuggerPDP11.prototype.checkInstruction = function(addr, nState)
|
||||||
{
|
{
|
||||||
var opCode = -1
|
var opCode = -1;
|
||||||
var cpu = this.cpu;
|
var cpu = this.cpu;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1620,13 +1614,13 @@ if (DEBUGGER) {
|
||||||
if (nState == 0) {
|
if (nState == 0) {
|
||||||
opCode = this.cpu.getWordDirect(addr);
|
opCode = this.cpu.getWordDirect(addr);
|
||||||
if (opCode == PDP11.OPCODE.HALT) {
|
if (opCode == PDP11.OPCODE.HALT) {
|
||||||
this.cpu.advancePC(2);
|
addr = this.cpu.advancePC(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nState > 0) {
|
if (nState > 0) {
|
||||||
if (this.nBreakIns && !--this.nBreakIns) {
|
if (this.nBreakInstructions) {
|
||||||
return true;
|
if (!--this.nBreakInstructions) return true;
|
||||||
}
|
}
|
||||||
if (this.checkBreakpoint(addr, 1, this.aBreakExec)) {
|
if (this.checkBreakpoint(addr, 1, this.aBreakExec)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1639,14 +1633,16 @@ if (DEBUGGER) {
|
||||||
* adding/removing breakpoints, simply because it's breakpoints that trigger the call to checkInstruction();
|
* adding/removing breakpoints, simply because it's breakpoints that trigger the call to checkInstruction();
|
||||||
* well, OK, and a few other things now, like enabling MessagesPDP11.INT messages.
|
* well, OK, and a few other things now, like enabling MessagesPDP11.INT messages.
|
||||||
*/
|
*/
|
||||||
if (nState >= 0 && this.aaOpcodeCounts.length) {
|
if (nState >= 0 && this.aInstructionHistory.length) {
|
||||||
this.cOpcodes++;
|
this.cInstructions++;
|
||||||
if (opCode < 0) opCode = this.cpu.getWordDirect(addr);
|
if (opCode < 0) {
|
||||||
if (opCode != null) {
|
opCode = this.cpu.getWordDirect(addr);
|
||||||
var dbgAddr = this.aOpcodeHistory[this.iOpcodeHistory];
|
}
|
||||||
this.setAddr(dbgAddr, cpu.getPC());
|
if ((opCode & 0xffff) != PDP11.OPCODE.INVALID) {
|
||||||
|
var dbgAddr = this.aInstructionHistory[this.iInstructionHistory];
|
||||||
|
this.setAddr(dbgAddr, addr);
|
||||||
// if (DEBUG) dbgAddr.cycleCount = cpu.getCycles();
|
// if (DEBUG) dbgAddr.cycleCount = cpu.getCycles();
|
||||||
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
|
if (++this.iInstructionHistory == this.aInstructionHistory.length) this.iInstructionHistory = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2161,13 +2157,17 @@ if (DEBUGGER) {
|
||||||
sOperand = this.toStrBase(addr);
|
sOperand = this.toStrBase(addr);
|
||||||
}
|
}
|
||||||
else if (opTypeOther == DebuggerPDP11.OP_DSTNUM3) {
|
else if (opTypeOther == DebuggerPDP11.OP_DSTNUM3) {
|
||||||
disp = (opCode & 0x7);
|
disp = (opCode & 0x07);
|
||||||
sOperand = this.toStrBase(disp, 1);
|
sOperand = this.toStrBase(disp, 1);
|
||||||
}
|
}
|
||||||
else if (opTypeOther == DebuggerPDP11.OP_DSTNUM6) {
|
else if (opTypeOther == DebuggerPDP11.OP_DSTNUM6) {
|
||||||
disp = (opCode & 0x3f);
|
disp = (opCode & 0x3f);
|
||||||
sOperand = this.toStrBase(disp, 1);
|
sOperand = this.toStrBase(disp, 1);
|
||||||
}
|
}
|
||||||
|
else if (opTypeOther == DebuggerPDP11.OP_DSTNUM8) {
|
||||||
|
disp = (opCode & 0xff);
|
||||||
|
sOperand = this.toStrBase(disp, 1);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/*
|
/*
|
||||||
* Isolate all OP_SRC or OP_DST bits from opcode in the opMode variable.
|
* Isolate all OP_SRC or OP_DST bits from opcode in the opMode variable.
|
||||||
|
|
@ -2724,8 +2724,8 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sParm == 'n') {
|
if (sParm == 'n') {
|
||||||
this.nBreakIns = this.parseValue(sAddr);
|
this.nBreakInstructions = this.parseValue(sAddr);
|
||||||
this.println("break after " + this.nBreakIns + " instruction(s)");
|
this.println("break after " + this.nBreakInstructions + " instruction(s)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2914,7 +2914,7 @@ if (DEBUGGER) {
|
||||||
data = shift = 0;
|
data = shift = 0;
|
||||||
}
|
}
|
||||||
sChars += (v >= 32 && v < 128? String.fromCharCode(v) : '.');
|
sChars += (v >= 32 && v < 128? String.fromCharCode(v) : '.');
|
||||||
nBytes--;
|
nBytes -= n;
|
||||||
}
|
}
|
||||||
if (sDump) sDump += '\n';
|
if (sDump) sDump += '\n';
|
||||||
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
|
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
|
||||||
|
|
@ -3680,16 +3680,16 @@ if (DEBUGGER) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shiftArgs(asArgs)
|
* splitArgs(sCmd)
|
||||||
*
|
|
||||||
* Used with any command (eg, "r") that allows but doesn't require whitespace between command and first argument.
|
|
||||||
*
|
*
|
||||||
* @this {DebuggerPDP11}
|
* @this {DebuggerPDP11}
|
||||||
* @param {Array.<string>} asArgs
|
* @param {string} sCmd
|
||||||
* @return {Array.<string>}
|
* @return {Array.<string>}
|
||||||
*/
|
*/
|
||||||
DebuggerPDP11.prototype.shiftArgs = function(asArgs)
|
DebuggerPDP11.prototype.splitArgs = function(sCmd)
|
||||||
{
|
{
|
||||||
|
var asArgs = sCmd.replace(/ +/g, ' ').split(' ');
|
||||||
|
asArgs[0] = asArgs[0].toLowerCase();
|
||||||
if (asArgs && asArgs.length) {
|
if (asArgs && asArgs.length) {
|
||||||
var s0 = asArgs[0];
|
var s0 = asArgs[0];
|
||||||
var ch0 = s0.charAt(0);
|
var ch0 = s0.charAt(0);
|
||||||
|
|
@ -3749,8 +3749,7 @@ if (DEBUGGER) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fError = false;
|
var fError = false;
|
||||||
var asArgs = this.shiftArgs(sCmd.replace(/ +/g, ' ').split(' '));
|
var asArgs = this.splitArgs(sCmd);
|
||||||
asArgs[0] = asArgs[0].toLowerCase();
|
|
||||||
|
|
||||||
switch (asArgs[0].charAt(0)) {
|
switch (asArgs[0].charAt(0)) {
|
||||||
case 'a':
|
case 'a':
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,8 @@ var PDP11 = {
|
||||||
* Assorted common opcodes
|
* Assorted common opcodes
|
||||||
*/
|
*/
|
||||||
OPCODE: {
|
OPCODE: {
|
||||||
HALT: 0x0000
|
HALT: 0x0000,
|
||||||
|
INVALID: 0xFFFF // far from the only invalid opcode, just a KNOWN invalid opcode
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
* Internal operation state flags
|
* Internal operation state flags
|
||||||
|
|
@ -249,13 +250,13 @@ var PDP11 = {
|
||||||
UNDEFINED: 0x00, // 000 (reserved)
|
UNDEFINED: 0x00, // 000 (reserved)
|
||||||
BUS_ERROR: 0x04, // 004 illegal instructions, bus errors, stack limit, illegal internal address, microbreak
|
BUS_ERROR: 0x04, // 004 illegal instructions, bus errors, stack limit, illegal internal address, microbreak
|
||||||
RESERVED: 0x08, // 010 reserved instructions
|
RESERVED: 0x08, // 010 reserved instructions
|
||||||
BREAKPOINT: 0x0C, // 014 BPT, breakpoint trap (trace)
|
BPT: 0x0C, // 014 BPT: breakpoint trap (trace)
|
||||||
IOT: 0x10, // 020 IOT, input/output trap
|
IOT: 0x10, // 020 IOT: input/output trap
|
||||||
POWER_FAIL: 0x14, // 024 power fail
|
PF: 0x14, // 024 power fail
|
||||||
EMULATOR: 0x18, // 030 EMT, emulator trap
|
EMT: 0x18, // 030 EMT: emulator trap
|
||||||
TRAP: 0x1C, // 034 TRAP instruction
|
TRAP: 0x1C, // 034 TRAP instruction
|
||||||
PIRQ: 0xA0, // 240 PIRQ, program interrupt request
|
PIRQ: 0xA0, // 240 PIRQ: program interrupt request
|
||||||
MMU_FAULT: 0xA8 // 250 MMU aborts and traps
|
MMU: 0xA8 // 250 MMU: aborts and traps
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
* PDP-11 trap reasons (for diagnostic purposes only)
|
* PDP-11 trap reasons (for diagnostic purposes only)
|
||||||
|
|
|
||||||
|
|
@ -258,8 +258,8 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit)
|
||||||
* unless the address is odd (usually 1). DEC's "Absolute Loader" jumps to the exec address
|
* unless the address is odd (usually 1). DEC's "Absolute Loader" jumps to the exec address
|
||||||
* in former case, halts in the latter.
|
* in former case, halts in the latter.
|
||||||
*
|
*
|
||||||
* All values are stored "little endian" (low byte first, followed by high byte), just like
|
* All values are stored "little endian" (low byte followed by high byte), just like the
|
||||||
* the PDP-11 does.
|
* PDP-11's memory architecture.
|
||||||
*
|
*
|
||||||
* After the data bytes, there is a single checksum byte. The 8-bit sum of all the bytes in
|
* After the data bytes, there is a single checksum byte. The 8-bit sum of all the bytes in
|
||||||
* the block (including the header bytes and checksum byte) should be zero.
|
* the block (including the header bytes and checksum byte) should be zero.
|
||||||
|
|
@ -267,7 +267,9 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit)
|
||||||
* ANOMALIES: Tape files don't always begin with a signature word, so I allow any number of
|
* ANOMALIES: Tape files don't always begin with a signature word, so I allow any number of
|
||||||
* leading zeros before the first signature. Tape files don't always end cleanly either, so as
|
* leading zeros before the first signature. Tape files don't always end cleanly either, so as
|
||||||
* soon as I see an invalid signature, I break out of the loop without signalling an error, as
|
* soon as I see an invalid signature, I break out of the loop without signalling an error, as
|
||||||
* long as at least ONE block was successfully processed.
|
* long as at least ONE block was successfully processed. In fact, it's possible that as
|
||||||
|
* soon as a block with ZERO data bytes is encountered, processing is supposed to stop, but
|
||||||
|
* I haven't examined enough tapes (or the Absolute Loader code) to know for sure.
|
||||||
*/
|
*/
|
||||||
if (addrLoad == null) {
|
if (addrLoad == null) {
|
||||||
var off = 0, fError = false;
|
var off = 0, fError = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue