Added PDP-10 JSP and JFCL operations (which seem to be the simplest means of getting and setting the processor flags)
This commit is contained in:
parent
0e18448423
commit
7e207db2a1
8 changed files with 456 additions and 350 deletions
|
|
@ -972,7 +972,7 @@ PDP10.opMOVSS = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVN = function(op, acc)
|
||||
{
|
||||
this.writeWord(acc, this.negate(this.readWord(this.regEA)));
|
||||
this.writeWord(acc, PDP10.doNEG.call(this, this.readWord(this.regEA)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1000,7 +1000,7 @@ PDP10.opMOVN = function(op, acc)
|
|||
PDP10.opMOVNI = function(op, acc)
|
||||
{
|
||||
/*
|
||||
* We perform an in-line twos complement of regEA, since negate() updates the flags, and the documentation
|
||||
* We perform an in-line twos complement of regEA, since doNEG() updates the flags, and the documentation
|
||||
* above claims that this operation must "set no flags." It's certainly true that regEA, being an 18-bit value,
|
||||
* could never be -2^35, but it COULD be zero -- and apparently this instruction treats zero differently from MOVN.
|
||||
*
|
||||
|
|
@ -1031,7 +1031,7 @@ PDP10.opMOVNI = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVNM = function(op, acc)
|
||||
{
|
||||
this.writeWord(this.regEA, this.negate(this.readWord(acc)));
|
||||
this.writeWord(this.regEA, PDP10.doNEG.call(this, this.readWord(acc)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1056,7 +1056,7 @@ PDP10.opMOVNM = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVNS = function(op, acc)
|
||||
{
|
||||
var dst = this.negate(this.readWord(this.regEA));
|
||||
var dst = PDP10.doNEG.call(this, this.readWord(this.regEA));
|
||||
this.writeWord(this.regEA, dst);
|
||||
if (acc) this.writeWord(acc, dst);
|
||||
};
|
||||
|
|
@ -1083,7 +1083,7 @@ PDP10.opMOVNS = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVM = function(op, acc)
|
||||
{
|
||||
this.writeWord(acc, this.abs(this.readWord(this.regEA)));
|
||||
this.writeWord(acc, PDP10.doABS.call(this, this.readWord(this.regEA)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1110,6 +1110,9 @@ PDP10.opMOVM = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMI = function(op, acc)
|
||||
{
|
||||
/*
|
||||
* Since the src is an 18-bit immediate value, there's no need to call doABS().
|
||||
*/
|
||||
this.writeWord(acc, this.regEA);
|
||||
};
|
||||
|
||||
|
|
@ -1135,7 +1138,7 @@ PDP10.opMOVMI = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMM = function(op, acc)
|
||||
{
|
||||
this.writeWord(this.regEA, this.abs(this.readWord(acc)));
|
||||
this.writeWord(this.regEA, PDP10.doABS.call(this, this.readWord(acc)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1160,7 +1163,7 @@ PDP10.opMOVMM = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMS = function(op, acc)
|
||||
{
|
||||
var dst = this.abs(this.readWord(this.regEA));
|
||||
var dst = PDP10.doABS.call(this, this.readWord(this.regEA));
|
||||
this.writeWord(this.regEA, dst);
|
||||
if (acc) this.writeWord(acc, dst);
|
||||
};
|
||||
|
|
@ -1936,7 +1939,31 @@ PDP10.opJRST = function(op, acc)
|
|||
};
|
||||
|
||||
/**
|
||||
* opJFCL(0o255000)
|
||||
* opJFCL(0o255000): Jump on Flag and Clear
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-57:
|
||||
*
|
||||
* If any flag specified by F [acc] is set, clear it and take the next instruction from location E,
|
||||
* continuing sequential operation from there.
|
||||
*
|
||||
* To select one or a combination of these flags (which are among those described above) the programmer can specify
|
||||
* the equivalent of an AC address that places 1s in the appropriate bits, but MACRO recognizes mnemonics for some of
|
||||
* the 13-bit instruction codes (bits 0-12).
|
||||
*
|
||||
* JFCL JFCL 0, No-op 25500
|
||||
* JOV JFCL 10, Jump on Overflow 25540
|
||||
* JCRY0 JFCL 4, Jump on Carry 0 25520
|
||||
* JCRY1 JFCL 2, Jump on Carry 1 25510
|
||||
* JCRY JFCL 6, Jump on Carry 0 or 1 25530
|
||||
* JFOV JFCL 1, Jump on Floating Overflow 25504
|
||||
*
|
||||
* SIDEBAR: This instruction can be used simply to clear the selected flags by having the jump address point to the
|
||||
* next consecutive location, as in:
|
||||
*
|
||||
* JFCL 17,.+1
|
||||
*
|
||||
* which clears all four flags without disrupting the normal program sequence. A JFCL that selects no flag is the fastest
|
||||
* no-op as it neither fetches nor stores an operand, and bits 18-35 of the instruction word can be used to store information.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -1944,7 +1971,14 @@ PDP10.opJRST = function(op, acc)
|
|||
*/
|
||||
PDP10.opJFCL = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
/*
|
||||
* The acc (F) bits from the opcode align perfectly with the top 4 bits of regPS; all we have to do is shift acc left 14.
|
||||
*/
|
||||
var bitsPS = acc << 14;
|
||||
if (this.regPS & bitsPS) {
|
||||
this.regPS &= ~bitsPS;
|
||||
this.setPC(this.regEA);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2078,7 +2112,15 @@ PDP10.opJSR = function(op, acc)
|
|||
};
|
||||
|
||||
/**
|
||||
* opJSP(0o265000)
|
||||
* opJSP(0o265000): Jump and Save PC
|
||||
*
|
||||
* Place the current contents of the flags (as described above) in AC left and the contents of PC in AC right
|
||||
* (at this time PC contains an address one greater than the location of the JSP instruction). Take the next
|
||||
* instruction from location E and continue sequential operation from there. The flags are unaffected except
|
||||
* Byte Interrupt, which is cleared.
|
||||
*
|
||||
* If this instruction is executed as a result of a priority interrupt or in un-relocated 41 or 61 while the
|
||||
* processor is in user mode, bit 5 of the PC word stored is 1 and the processor leaves user mode.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -2086,7 +2128,8 @@ PDP10.opJSR = function(op, acc)
|
|||
*/
|
||||
PDP10.opJSP = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
this.writeWord(acc, this.getPS() + this.getPC());
|
||||
this.setPC(this.regEA);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5234,6 +5277,27 @@ PDP10.opUndefined = function(op, acc)
|
|||
this.stopCPU();
|
||||
};
|
||||
|
||||
/**
|
||||
* doABS(src)
|
||||
*
|
||||
* Used by the MOVM* (Move Magnitude) instructions.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} src (36-bit)
|
||||
* @return {number} (absolute value of src)
|
||||
*/
|
||||
PDP10.doABS = function(src)
|
||||
{
|
||||
if (src > PDP10.MAX_POS36) {
|
||||
if (src != PDP10.MIN_NEG36) {
|
||||
src = PDP10.TWO_POW36 - src;
|
||||
} else {
|
||||
this.regPS |= (PDP10.PSFLAG.OVFL | PDP10.PSFLAG.CARRY1);
|
||||
}
|
||||
}
|
||||
return src;
|
||||
};
|
||||
|
||||
/**
|
||||
* doADD(dst, src)
|
||||
*
|
||||
|
|
@ -5431,6 +5495,34 @@ PDP10.doMUL = function(dst, src)
|
|||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* doNEG(src)
|
||||
*
|
||||
* Used by the MOVN* (Move Negative) instructions.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} src (36-bit)
|
||||
* @return {number} (src negated, but as an unsigned 36-bit result)
|
||||
*/
|
||||
PDP10.doNEG = function(src)
|
||||
{
|
||||
if (!src) {
|
||||
this.regPS |= (PDP10.PSFLAG.CARRY0 | PDP10.PSFLAG.CARRY1);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* In the non-zero case, it's always safe to subtract src from TWO_POW36, but since we have to check for
|
||||
* the MIN_NEG36 case anyway, and since subtraction in that case doesn't alter src, we skip the subtraction.
|
||||
*/
|
||||
if (src == PDP10.MIN_NEG36) {
|
||||
this.regPS |= (PDP10.PSFLAG.OVFL | PDP10.PSFLAG.CARRY1);
|
||||
} else {
|
||||
src = PDP10.TWO_POW36 - src;
|
||||
}
|
||||
}
|
||||
return src;
|
||||
};
|
||||
|
||||
/**
|
||||
* doSUB(dst, src)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -336,14 +336,15 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
/**
|
||||
* getPS()
|
||||
*
|
||||
* Gets the processor state flags in the format required by various program control operations.
|
||||
* Gets the processor state flags in the format required by various program control operations (eg, JSP),
|
||||
* pre-masked and pre-shifted for convenient loading into the left half of an accumulator.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @return {number}
|
||||
*/
|
||||
getPS()
|
||||
{
|
||||
return this.regPS & PDP10.HALF_MASK;
|
||||
return (this.regPS & PDP10.HALF_MASK) * PDP10.HALF_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -489,55 +490,6 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
this.regPC = addr % PDP10.ADDR_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* abs(src)
|
||||
*
|
||||
* Used by the MOVM* (Move Magnitude) instructions.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} src (36-bit)
|
||||
* @return {number} (absolute value of src)
|
||||
*/
|
||||
abs(src)
|
||||
{
|
||||
if (src > PDP10.MAX_POS36) {
|
||||
if (src != PDP10.MIN_NEG36) {
|
||||
src = PDP10.TWO_POW36 - src;
|
||||
} else {
|
||||
this.regPS |= (PDP10.PSFLAG.OVFL | PDP10.PSFLAG.CARRY1);
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* negate(src)
|
||||
*
|
||||
* Used by the MOVN* (Move Negative) instructions.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} src (36-bit)
|
||||
* @return {number} (src negated, but as an unsigned 36-bit result)
|
||||
*/
|
||||
negate(src)
|
||||
{
|
||||
if (!src) {
|
||||
this.regPS |= (PDP10.PSFLAG.CARRY0 | PDP10.PSFLAG.CARRY1);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* In the non-zero case, it's always safe to subtract src from TWO_POW36, but since we have to check for
|
||||
* the MIN_NEG36 case anyway, and since subtraction in that case doesn't alter src, we skip the subtraction.
|
||||
*/
|
||||
if (src == PDP10.MIN_NEG36) {
|
||||
this.regPS |= (PDP10.PSFLAG.OVFL | PDP10.PSFLAG.CARRY1);
|
||||
} else {
|
||||
src = PDP10.TWO_POW36 - src;
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* addIRQ(vector, priority, message)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1497,16 +1497,25 @@ class DebuggerPDP10 extends Debugger {
|
|||
if (!fOperands) {
|
||||
if (!opNum) sOperation = "";
|
||||
} else {
|
||||
sOperation = Str.pad(sOperation, 8);
|
||||
if (!opNum) {
|
||||
sOperation += this.toStrWord(opCode);
|
||||
sOperation = Str.pad(sOperation, 8) + this.toStrWord(opCode);
|
||||
} else {
|
||||
var n, sOperand;
|
||||
if (opMask == PDP10.OPCODE.OPIO) {
|
||||
sOperation += this.toStrBase((opCode / PDP10.OPCODE.IO_SCALE) & PDP10.OPCODE.IO_MASK, -1);
|
||||
n = (opCode / PDP10.OPCODE.IO_SCALE) & PDP10.OPCODE.IO_MASK;
|
||||
sOperand = this.toStrBase(n, -1);
|
||||
} else {
|
||||
sOperation += this.toStrBase((opCode >> PDP10.OPCODE.A_SHIFT) & PDP10.OPCODE.A_MASK, -1);
|
||||
n = (opCode >> PDP10.OPCODE.A_SHIFT) & PDP10.OPCODE.A_MASK;
|
||||
sOperand = this.toStrBase(n, -1);
|
||||
if (opNum == DebuggerPDP10.OPS.JFCL) {
|
||||
var opAlt = DebuggerPDP10.JFCL[n];
|
||||
if (opAlt) {
|
||||
sOperation = DebuggerPDP10.OPNAMES[opAlt];
|
||||
sOperand = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
sOperation += ',';
|
||||
sOperation = Str.pad(sOperation, 8) + (sOperand? sOperand + ',' : "");
|
||||
if (opCode & PDP10.OPCODE.I_BIT) sOperation += '@';
|
||||
sOperation += this.toStrBase(opCode & PDP10.OPCODE.Y_MASK, -1);
|
||||
var i = (opCode >> PDP10.OPCODE.X_SHIFT) & PDP10.OPCODE.X_MASK;
|
||||
|
|
@ -1558,7 +1567,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* parseInstruction(sOpCode, sOperands, addr)
|
||||
* parseInstruction(sOpCode, sOperands, dbgAddr)
|
||||
*
|
||||
* @this {DebuggerPDP10}
|
||||
* @param {string} sOpCode
|
||||
|
|
@ -1568,9 +1577,21 @@ class DebuggerPDP10 extends Debugger {
|
|||
*/
|
||||
parseInstruction(sOpCode, sOperands, dbgAddr)
|
||||
{
|
||||
var opCode = -1, opMask;
|
||||
var opCode = -1, opMask, opNum;
|
||||
var sMnemonic = sOpCode.toUpperCase();
|
||||
|
||||
/*
|
||||
* Perform any alternate mnemonic substitutions first
|
||||
*/
|
||||
for (var n in DebuggerPDP10.JFCL) {
|
||||
opNum = DebuggerPDP10.JFCL[n];
|
||||
if (sMnemonic == DebuggerPDP10.OPNAMES[opNum]) {
|
||||
sMnemonic = DebuggerPDP10.OPNAMES[DebuggerPDP10.OPS.JFCL];
|
||||
sOperands = this.toStrBase(+n) + ',' + sOperands;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var mask in DebuggerPDP10.OPTABLE) {
|
||||
|
||||
var aModes;
|
||||
|
|
@ -1595,7 +1616,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
var opMode = 0;
|
||||
for (var op in opMasks) {
|
||||
|
||||
var opNum = opMasks[op];
|
||||
opNum = opMasks[op];
|
||||
for (var iMode = 0; iMode < aModes.length; iMode++) {
|
||||
|
||||
var sMode = aModes[iMode];
|
||||
|
|
@ -1638,7 +1659,15 @@ class DebuggerPDP10 extends Debugger {
|
|||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
var operand = this.parseExpression(match[2]);
|
||||
sOperand = match[2];
|
||||
if (i) {
|
||||
/*
|
||||
* If this is NOT the first operand, then replace all periods NOT preceded
|
||||
* by a digit with the current address.
|
||||
*/
|
||||
sOperand = sOperand.replace(/(^|[^0-9])\./g, "$1" + this.toStrAddr(dbgAddr));
|
||||
}
|
||||
var operand = this.parseExpression(sOperand);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
|
|
@ -1646,7 +1675,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
if (!i && aOperands.length > 1) {
|
||||
if (opMask == PDP10.OPCODE.OPIO) {
|
||||
if (operand < 0 || operand > PDP10.OPCODE.IO_MASK) {
|
||||
this.println("device code out of range: " + match[2]);
|
||||
this.println("device code out of range: " + sOperand);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1654,7 +1683,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
}
|
||||
else {
|
||||
if (operand < 0 || operand > PDP10.OPCODE.A_MASK) {
|
||||
this.println("accumulator address out of range: " + match[2]);
|
||||
this.println("accumulator address out of range: " + sOperand);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1663,19 +1692,20 @@ class DebuggerPDP10 extends Debugger {
|
|||
continue;
|
||||
}
|
||||
if (operand < 0 || operand > PDP10.OPCODE.Y_MASK) {
|
||||
this.println("memory address out of range: " + match[2]);
|
||||
this.println("memory address out of range: " + sOperand);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
opCode += operand;
|
||||
if (match[3]) {
|
||||
operand = this.parseExpression(match[3]);
|
||||
sOperand = match[3];
|
||||
if (sOperand) {
|
||||
operand = this.parseExpression(sOperand);
|
||||
if (operand == undefined) {
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
if (operand < 0 || operand > PDP10.OPCODE.X_MASK) {
|
||||
this.println("memory index out of range: " + match[3]);
|
||||
this.println("memory index out of range: " + sOperand);
|
||||
opCode = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2141,7 +2171,7 @@ class DebuggerPDP10 extends Debugger {
|
|||
* For now, fMisc defaults to true, providing a full register dump by default.
|
||||
*
|
||||
* @this {DebuggerPDP10}
|
||||
* @param {boolean} [fMisc] (true to include misc registers)
|
||||
* @param {boolean|undefined} [fMisc] (true to include misc registers)
|
||||
* @return {string}
|
||||
*/
|
||||
getRegDump(fMisc = true)
|
||||
|
|
@ -3821,7 +3851,8 @@ if (DEBUGGER) {
|
|||
JSA: 89, JRA: 90, PUSHJ: 91, POPJ: 92,
|
||||
BLKI: 93, DATAI: 94, BLKO: 95, DATAO: 96,
|
||||
CONO: 97, CONI: 98, CONSZ: 99, CONSO: 100,
|
||||
UUO: 101
|
||||
UUO: 101, JOV: 102, JCRY0: 103, JCRY1: 104,
|
||||
JCRY: 105, JFOV: 106
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -3854,7 +3885,8 @@ if (DEBUGGER) {
|
|||
"JSA", "JRA", "PUSHJ", "POPJ",
|
||||
"BLKI", "DATAI", "BLKO", "DATAO",
|
||||
"CONO", "CONI", "CONSZ", "CONSO",
|
||||
"UUO"
|
||||
"UUO", "JOV", "JCRY0", "JCRY1",
|
||||
"JCRY", "JFOV"
|
||||
];
|
||||
|
||||
DebuggerPDP10.REGS = {
|
||||
|
|
@ -3998,6 +4030,20 @@ if (DEBUGGER) {
|
|||
DebuggerPDP10.OPCOMPS = ["", "L", "E", "LE", "A", "GE", "N", "G"];
|
||||
DebuggerPDP10.OPTESTS = ["N", "NE", "NA", "NN", "Z", "ZE", "ZA", "ZN", "C", "CE", "CA", "CN", "O", "OE", "OA", "ON"];
|
||||
|
||||
/*
|
||||
* Apparently, DEC's MACRO program permits "JFCL xxx" (with a single argument) as an alternate for "JFCL 0,xxx"
|
||||
* (which in turn is long-hand for "No-op", since JFCL with 0 does nothing). If we allowed JFCL to be both a
|
||||
* primary and a alternate mnemonic, that would complicate our simplistic parseInstruction() logic, so we don't.
|
||||
*/
|
||||
DebuggerPDP10.JFCL = {
|
||||
// 0o00: DebuggerPDP10.OPS.JFCL,
|
||||
0o10: DebuggerPDP10.OPS.JOV,
|
||||
0o04: DebuggerPDP10.OPS.JCRY0,
|
||||
0o02: DebuggerPDP10.OPS.JCRY1,
|
||||
0o06: DebuggerPDP10.OPS.JCRY,
|
||||
0o01: DebuggerPDP10.OPS.JFOV
|
||||
};
|
||||
|
||||
DebuggerPDP10.HISTORY_LIMIT = DEBUG? 100000 : 1000;
|
||||
|
||||
DebuggerPDP10.PROMPT = ">> ";
|
||||
|
|
|
|||
|
|
@ -198,6 +198,11 @@ var PDP10 = {
|
|||
|
||||
/*
|
||||
* Flags returned by getPS() for various program control operations.
|
||||
*
|
||||
* NOTE: I see SIMH setting PS bits like 0o000200 and 0o000400, which are not documented for the KA10.
|
||||
* The SIMH docs only refer to the KS10 ("KS10 CPU with 1MW of memory"), so I'm guessing it doesn't have
|
||||
* a KA10 emulation option. The `pdp10` SIMH binary does have some SET CPU options, but unlike the `pdp11`
|
||||
* binary, the only options you can set relate to the operating system to be run -- which seems very hacky.
|
||||
*/
|
||||
PSFLAG: {
|
||||
OVFL: 0o400000, // Overflow
|
||||
|
|
|
|||
Loading…
Reference in a new issue