Added rudimentary PDP-10 assembler capability to the built-in debugger

This commit is contained in:
Jeff Parsons 2017-02-27 18:06:08 -08:00 committed by Jeff Parsons
commit 8863f1127c
14 changed files with 501 additions and 262 deletions

View file

@ -337,10 +337,14 @@ class CPUStatePDP10 extends CPUPDP10 {
*/
getOpcode()
{
/*
* Technically, we don't REALLY need to mask regRA with R_MASK, because all regRA accesses
* ignore any higher opcode bits, but let's keep things tidy.
*/
if ((this.regRA & PDP10.OPCODE.I_BIT)) {
this.regRA = this.readWord(this.regEA);
this.regRA = this.readWord(this.regEA) & PDP10.OPCODE.R_MASK;
} else {
this.regRA = this.regOP = this.readWord(this.lastPC = this.regPC);
this.regRA = (this.regOP = this.readWord(this.lastPC = this.regPC)) & PDP10.OPCODE.R_MASK;
}
/*

View file

@ -227,6 +227,24 @@ class DebuggerPDP10 extends Debugger {
return {addr: addr, fPhysical: fPhysical, fTemporary: false, nBase: nBase};
}
/**
* copyAddr(dbgAddr, dbgCopy)
*
* Updates an EXISTING DbgAddrPDP10 object, initialized with specified values and/or defaults.
*
* @this {DebuggerPDP10}
* @param {DbgAddrPDP10} dbgAddr
* @param {DbgAddrPDP10} dbgCopy
* @return {DbgAddrPDP10}
*/
copyAddr(dbgAddr, dbgCopy)
{
dbgAddr.addr = dbgCopy.addr;
dbgAddr.fTemporary = dbgCopy.fTemporary;
dbgAddr.nBase = dbgCopy.nBase;
return dbgAddr;
}
/**
* setAddr(dbgAddr, addr)
*
@ -1083,7 +1101,7 @@ class DebuggerPDP10 extends Debugger {
this.println(DebuggerPDP10.PROMPT + sCmd);
}
this.dbgAddrNextCode = this.newAddr(this.cpu.getPC());
this.setAddr(this.dbgAddrNextCode, this.cpu.getPC());
/*
* this.nStep used to be a simple boolean, but now it's 0 (or undefined)
@ -1170,7 +1188,7 @@ class DebuggerPDP10 extends Debugger {
this.cInstructions = this.cInstructionsStart = 0;
this.sMessagePrev = null;
this.nCycles = 0;
this.dbgAddrNextCode = this.newAddr(this.cpu.getPC());
this.setAddr(this.dbgAddrNextCode, this.cpu.getPC());
/*
* fRunning is set by start() and cleared by stop(). In addition, we clear
* it here, so that if the CPU is reset while running, we can prevent stop()
@ -1480,21 +1498,129 @@ class DebuggerPDP10 extends Debugger {
}
/**
* parseInstruction(sOp, sOperand, addr)
*
* TODO: Unimplemented. See parseInstruction() in modules/c1pjs/lib/debugger.js for a sample implementation.
* parseInstruction(sOpCode, sOperands, addr)
*
* @this {DebuggerPDP10}
* @param {string} sOp
* @param {string|undefined} sOperand
* @param {string} sOpCode
* @param {string|undefined} sOperands
* @param {DbgAddrPDP10} dbgAddr of memory where this instruction is being assembled
* @return {Array.<number>} of opcode bytes; if the instruction can't be parsed, the array will be empty
* @return {number} (opcode, or -1 if unrecognized instruction)
*/
parseInstruction(sOp, sOperand, dbgAddr)
parseInstruction(sOpCode, sOperands, dbgAddr)
{
var aOpBytes = [];
this.println("not supported yet");
return aOpBytes;
var opCode = -1, opMask;
var sMnemonic = sOpCode.toUpperCase();
for (var mask in DebuggerPDP10.OPTABLE) {
var aModes;
opMask = +mask;
var opMasks = DebuggerPDP10.OPTABLE[mask];
switch(opMask) {
case PDP10.OPCODE.OPMODE:
aModes = DebuggerPDP10.OPMODES;
break;
case PDP10.OPCODE.OPCOMP:
aModes = DebuggerPDP10.OPCOMPS;
break;
case PDP10.OPCODE.OPTEST:
aModes = DebuggerPDP10.OPTESTS;
break;
default:
aModes = [""];
break;
}
var opMode = 0;
for (var op in opMasks) {
var opNum = opMasks[op];
for (var iMode = 0; iMode < aModes.length; iMode++) {
var sMode = aModes[iMode];
if (sMode == "S" && opNum > DebuggerPDP10.OPS.MOVM) sMode = "B";
var sCandidate = DebuggerPDP10.OPNAMES[opNum] + sMode;
if (sMnemonic == sCandidate) {
if (opMask != PDP10.OPCODE.OPTEST) {
opMode = iMode;
} else {
opMode = ((iMode & 0o3) << 1) | ((iMode & 0o14) << 2);
}
opCode = (op | (opMode << 6)) * PDP10.OPCODE.OPSHIFT;
break;
}
}
if (opCode >= 0) break;
}
if (opCode >= 0) break;
}
if (opCode >= 0) {
if (!sOperands) {
this.println("missing operand(s)");
opCode = -1;
}
else {
var aOperands = sOperands.split(',');
for (var i = 0; i < aOperands.length; i++) {
var sOperand = aOperands[i].trim();
if (!sOperand) continue;
if (i > 1) {
this.println("too many operands: " + sOperand);
opCode = -1;
break;
}
var match = sOperand.match(/(@?)([^(]*)\(?([^)]*)\)?/);
if (!match) {
this.println("unknown operand: " + sOperand);
opCode = -1;
break;
}
var operand = this.parseExpression(match[2]);
if (operand == undefined) {
opCode = -1;
break;
}
if (!i && aOperands.length > 1) {
if (operand < 0 || operand > PDP10.OPCODE.A_MASK) {
this.println("accumulator index out of range: " + match[2]);
opCode = -1;
break;
}
opCode += (operand << PDP10.OPCODE.A_SHIFT);
continue;
}
if (operand < 0 || operand > PDP10.OPCODE.Y_MASK) {
this.println("address out of range: " + match[2]);
opCode = -1;
break;
}
opCode += operand;
if (match[3]) {
operand = this.parseExpression(match[3]);
if (operand == undefined) {
opCode = -1;
break;
}
if (operand < 0 || operand > PDP10.OPCODE.X_MASK) {
this.println("index out of range: " + match[3]);
opCode = -1;
break;
}
opCode += operand << PDP10.OPCODE.X_SHIFT;
}
if (match[1]) {
opCode += PDP10.OPCODE.I_BIT;
}
}
}
}
else {
this.println("unknown instruction: " + sOpCode);
}
return opCode;
}
/**
@ -1903,7 +2029,8 @@ class DebuggerPDP10 extends Debugger {
{
var sReg = this.getRegName(iReg);
if (sReg) {
sReg += '=' + this.toStrBase(this.getRegValue(iReg)) + ' ';
var nBits = (iReg == DebuggerPDP10.REGS.RA? 23 : 18);
sReg += '=' + this.toStrBase(this.getRegValue(iReg), nBits) + ' ';
}
return sReg;
}
@ -2230,19 +2357,24 @@ class DebuggerPDP10 extends Debugger {
var dbgAddr = this.parseAddr(asArgs[1], true);
if (!dbgAddr) return;
var sOpCode = asArgs[2];
this.dbgAddrAssemble = dbgAddr;
if (asArgs[2] === undefined) {
if (sOpCode == undefined) {
this.println("begin assemble at " + this.toStrAddr(dbgAddr));
this.fAssemble = true;
this.cmp.updateDisplays();
return;
}
var aOps = this.parseInstruction(asArgs[2], asArgs[3], dbgAddr);
if (aOps.length) {
for (var i = 0; i < aOps.length; i++) {
this.setWord(dbgAddr, aOps[i], 1);
}
asArgs.shift();
asArgs.shift();
asArgs.shift();
var sOperands = asArgs.join("");
var opCode = this.parseInstruction(sOpCode, sOperands, dbgAddr);
if (opCode >= 0) {
this.setWord(dbgAddr, opCode);
/*
* Since getInstruction() also updates the specified address, dbgAddrAssemble is automatically advanced.
*/
@ -2500,8 +2632,7 @@ class DebuggerPDP10 extends Debugger {
}
if (sDump) this.println(sDump);
this.dbgAddrNextData = dbgAddr;
this.copyAddr(this.dbgAddrNextData, dbgAddr);
this.nBase = nBase;
}
@ -2532,7 +2663,7 @@ class DebuggerPDP10 extends Debugger {
for (var i = 2; i < asArgs.length; i++) {
var vNew = this.parseExpression(asArgs[i]);
if (vNew === undefined) {
this.println("unrecognized value: " + asArgs[i]);
this.println("unknown value: " + asArgs[i]);
break;
}
this.println("changing " + this.toStrAddr(dbgAddr) + " from " + this.toStrWord(fnGet.call(this, dbgAddr)) + " to " + this.toStrWord(vNew));
@ -2897,7 +3028,7 @@ class DebuggerPDP10 extends Debugger {
switch (sRegMatch) {
case "PC":
cpu.setPC(w);
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
this.setAddr(this.dbgAddrNextCode, cpu.getPC());
break;
default:
this.println("unknown register: " + sReg);
@ -2911,7 +3042,7 @@ class DebuggerPDP10 extends Debugger {
this.println(this.getRegDump(fMisc));
if (fInstruction) {
this.dbgAddrNextCode = this.newAddr(cpu.getPC());
this.setAddr(this.dbgAddrNextCode, cpu.getPC());
this.doUnassemble(this.toStrAddr(this.dbgAddrNextCode));
}
}
@ -3243,7 +3374,7 @@ class DebuggerPDP10 extends Debugger {
sInstruction = this.getInstruction(dbgAddr, sComment, nSequence);
this.println(sInstruction);
this.dbgAddrNextCode = dbgAddr;
this.copyAddr(this.dbgAddrNextCode, dbgAddr);
nBytes -= dbgAddr.addr - addr;
nPrinted++;
}
@ -3295,7 +3426,7 @@ class DebuggerPDP10 extends Debugger {
if (!sCmd.length || sCmd == "end") {
if (this.fAssemble) {
this.println("ended assemble at " + this.toStrAddr(this.dbgAddrAssemble));
this.dbgAddrNextCode = this.dbgAddrAssemble;
this.copyAddr(this.dbgAddrNextCode, this.dbgAddrAssemble);
this.fAssemble = false;
}
sCmd = "";

View file

@ -147,13 +147,14 @@ var PDP10 = {
OPSHIFT: Math.pow(2, 21), // operation shift
IOSHIFT: Math.pow(2, 26), // input-output device code shift
IOMASK: 0o177, // input-output device code mask (after shift)
ACSHIFT: Math.pow(2, 23), // used to isolate the high 13 bits, with A starting at bit 0
ACSHIFT: Math.pow(2, 23), // used to shift down the high 13 bits, with A starting at bit 0
A_SHIFT: 23, // A shift
A_MASK: 0o17, // A mask (after shift)
I_BIT: 0o20000000, // indirect bit
X_SHIFT: 18, // X shift
X_MASK: 0o17, // X mask (after shift)
Y_MASK: 0o777777, // Y mask
R_MASK: 0o37777777, // used to isolate the low 23 bits (I,X,Y)
HALT: 0o5304 // operation code for HALT
},