Finished the PDP-10 Move Magnitude opcodes, along with the Push Down and Pop Up opcodes (uncovering some SIMH weirdness along the way)
This commit is contained in:
parent
85a338b8c4
commit
affaa1075c
8 changed files with 398 additions and 224 deletions
|
|
@ -841,6 +841,8 @@ PDP10.opMOVNI = function(op, acc)
|
|||
* We perform an in-line two's complement of regEA, since negate() 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.
|
||||
*
|
||||
* TODO: Verify the "set no flags" assertion.
|
||||
*/
|
||||
this.writeWord(acc, this.regEA? PDP10.TWO_POW36 - this.regEA : 0);
|
||||
};
|
||||
|
|
@ -898,7 +900,20 @@ PDP10.opMOVNS = function(op, acc)
|
|||
};
|
||||
|
||||
/**
|
||||
* opMOVM(0o214000)
|
||||
* opMOVM(0o214000): Move Magnitude
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-12:
|
||||
*
|
||||
* Take the magnitude of the word contained in the source specified by M and move it to the
|
||||
* specified destination. If the source word is fixed point -2^35 (400000 000000) set the
|
||||
* Overflow and Carry 1 flags.
|
||||
*
|
||||
* (Negating the equivalent floating point -1 * 2^27 sets the flags, but this is not a normalized
|
||||
* number).
|
||||
*
|
||||
* The source is unaffected, the original contents of the destination are lost.
|
||||
*
|
||||
* NOTE: This is a "Basic" mode instruction: the source is [E] and the destination is [A] (opposite of "Memory").
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -906,11 +921,26 @@ PDP10.opMOVNS = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVM = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
this.writeWord(acc, this.abs(this.readWord(this.regEA)));
|
||||
};
|
||||
|
||||
/**
|
||||
* opMOVMI(0o215000)
|
||||
* opMOVMI(0o215000): Move Magnitude Immediate
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-12:
|
||||
*
|
||||
* Take the magnitude of the word contained in the source specified by M and move it to the
|
||||
* specified destination. If the source word is fixed point -2^35 (400000 000000) set the
|
||||
* Overflow and Carry 1 flags.
|
||||
*
|
||||
* (Negating the equivalent floating point -1 * 2^27 sets the flags, but this is not a normalized
|
||||
* number).
|
||||
*
|
||||
* The source is unaffected, the original contents of the destination are lost.
|
||||
*
|
||||
* SIDEBAR: The word 0,E is equivalent to its magnitude, so MOVMI is equivalent to MOVEI.
|
||||
*
|
||||
* NOTE: This is an "Immediate" mode instruction: the source is the word 0,E and the destination is [A].
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -918,11 +948,24 @@ PDP10.opMOVM = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMI = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
this.writeWord(acc, this.regEA);
|
||||
};
|
||||
|
||||
/**
|
||||
* opMOVMM(0o216000)
|
||||
* opMOVMM(0o216000): Move Magnitude to Memory
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-12:
|
||||
*
|
||||
* Take the magnitude of the word contained in the source specified by M and move it to the
|
||||
* specified destination. If the source word is fixed point -2^35 (400000 000000) set the
|
||||
* Overflow and Carry 1 flags.
|
||||
*
|
||||
* (Negating the equivalent floating point -1 * 2^27 sets the flags, but this is not a normalized
|
||||
* number).
|
||||
*
|
||||
* The source is unaffected, the original contents of the destination are lost.
|
||||
*
|
||||
* NOTE: This is a "Memory" mode instruction: the source is [A] and the destination is [E] (opposite of "Basic").
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -930,11 +973,24 @@ PDP10.opMOVMI = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMM = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
this.writeWord(this.regEA, this.abs(this.readWord(acc)));
|
||||
};
|
||||
|
||||
/**
|
||||
* opMOVMS(0o217000)
|
||||
* opMOVMS(0o217000): Move Magnitude to Self
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-12:
|
||||
*
|
||||
* Take the magnitude of the word contained in the source specified by M and move it to the
|
||||
* specified destination. If the source word is fixed point -2^35 (400000 000000) set the
|
||||
* Overflow and Carry 1 flags.
|
||||
*
|
||||
* (Negating the equivalent floating point -1 * 2^27 sets the flags, but this is not a normalized
|
||||
* number).
|
||||
*
|
||||
* The source is unaffected, the original contents of the destination are lost.
|
||||
*
|
||||
* NOTE: This is a "Self" mode instruction: the source AND destination is [E] (and also [A] if A is non-zero).
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -942,7 +998,9 @@ PDP10.opMOVMM = function(op, acc)
|
|||
*/
|
||||
PDP10.opMOVMS = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
var dst = this.abs(this.readWord(this.regEA));
|
||||
this.writeWord(this.regEA, dst);
|
||||
if (acc) this.writeWord(acc, dst);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1257,6 +1315,9 @@ PDP10.opEXCH = function(op, acc)
|
|||
* is the final location being loaded. Furthermore, the program cannot assume that AC is the same after the BLT
|
||||
* as it was before.
|
||||
*
|
||||
* TODO: Determine the logic behind SIMH's bizarre treatment of the AC register when it's part of the memory
|
||||
* being transferred.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
* @param {number} acc
|
||||
|
|
@ -1357,7 +1418,28 @@ PDP10.opPUSHJ = function(op, acc)
|
|||
};
|
||||
|
||||
/**
|
||||
* opPUSH(0o261000)
|
||||
* opPUSH(0o261000): Push Down
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-13:
|
||||
*
|
||||
* Add 000001 000001 to AC to increment both halves by one, then move the contents of location E to the
|
||||
* location now addressed by AC right. If the addition causes the count in AC left to reach zero, set the
|
||||
* Pushdown Overflow flag. The contents of E are unaffected, the original contents of the location added
|
||||
* to the list are lost.
|
||||
*
|
||||
* If we trusted SIMH, then we could not literally interpret this instruction as adding 000001 000001 to AC,
|
||||
* because if AC contained 777777 777777, adding 000001 000001 would result in 000001 000000, not 000000 000000,
|
||||
* and yet 000000 000000 is EXACTLY what we get in SIMH.
|
||||
*
|
||||
* And yet, DEC's documentation clearly contradicts what SIMH is doing:
|
||||
*
|
||||
* The incrementing and decrementing of both halves of AC simultaneously is effected by adding and subtracting
|
||||
* 000001 000001. Hence a count of -2 in AC left is increased to zero if 2^18 - 1 is incremented in AC right,
|
||||
* and conversely, 1 in AC left is decreased to -1 if zero is decremented in AC right.
|
||||
*
|
||||
* Perhaps SIMH is simply emulating the behavior of some later PDP-10 model, which performed independent increments?
|
||||
*
|
||||
* TODO: Investigate.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -1365,11 +1447,41 @@ PDP10.opPUSHJ = function(op, acc)
|
|||
*/
|
||||
PDP10.opPUSH = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
var p = this.readWord(acc);
|
||||
if (!SIMH) {
|
||||
/*
|
||||
* This is the behavior that is clearly documented by DEC.
|
||||
*/
|
||||
p += 0o000001000001;
|
||||
this.writeWord(p & PDP10.WORD_MASK, this.readWord(this.regEA));
|
||||
if (p >= PDP10.DATA_LIMIT) p -= PDP10.DATA_LIMIT;
|
||||
if (!((p / PDP10.WORD_SHIFT) | 0)) this.fPDOverflow = true;
|
||||
} else {
|
||||
/*
|
||||
* This is the SIMH behavior, which appears to increment each half of AC independently.
|
||||
*/
|
||||
var addr = (p + 1) & PDP10.WORD_MASK;
|
||||
this.writeWord(addr, this.readWord(this.regEA));
|
||||
p = (p + 0o000001000000) - (p & PDP10.WORD_MASK) + addr;
|
||||
if (p >= PDP10.DATA_LIMIT) {
|
||||
p -= PDP10.DATA_LIMIT;
|
||||
this.fPDOverflow = true;
|
||||
}
|
||||
}
|
||||
this.writeWord(acc, p);
|
||||
};
|
||||
|
||||
/**
|
||||
* opPOP(0o262000)
|
||||
* opPOP(0o262000): Pop Up
|
||||
*
|
||||
* From the DEC PDP-10 System Reference Manual (May 1968), p. 2-13:
|
||||
*
|
||||
* Move the contents of the location addressed by AC right to location E, then subtract 000001 000001
|
||||
* from AC to decrement both halves by one. If the subtraction causes the count in AC left to reach -1,
|
||||
* set the Pushdown Overflow flag. The original contents of E are lost.
|
||||
*
|
||||
* NOTE: Because of the order in which the operands are stored, the instruction POP AC,AC would load the
|
||||
* contents of the location addressed by AC right into AC on top of the pushdown count, destroying it.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} op
|
||||
|
|
@ -1377,7 +1489,14 @@ PDP10.opPUSH = function(op, acc)
|
|||
*/
|
||||
PDP10.opPOP = function(op, acc)
|
||||
{
|
||||
this.opUndefined(op);
|
||||
var p = this.readWord(acc);
|
||||
var src = this.readWord(p & PDP10.WORD_MASK);
|
||||
this.writeWord(this.regEA, src);
|
||||
if (this.regEA == acc) p = src; // this avoids re-reading the accumulator if the write just overwrote it
|
||||
p -= 0o000001000001;
|
||||
if (p < 0) p += PDP10.DATA_LIMIT;
|
||||
if (((p / PDP10.WORD_SHIFT) | 0) == PDP10.WORD_MASK) this.fPDOverflow = true;
|
||||
this.writeWord(acc, p);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
{
|
||||
this.regEA = this.regRA = this.regOP = 0;
|
||||
this.regPC = this.lastPC = this.addrReset;
|
||||
this.fOverflow = this.fCarry0 = this.fCarry1 = this.fNoDivide = false;
|
||||
this.fOverflow = this.fCarry0 = this.fCarry1 = this.fNoDivide = this.fPDOverflow = false;
|
||||
|
||||
/*
|
||||
* This is queried and displayed by the Panel when it's not displaying its own ADDRESS register
|
||||
|
|
@ -439,11 +439,34 @@ class CPUStatePDP10 extends CPUPDP10 {
|
|||
}
|
||||
|
||||
/**
|
||||
* negate(src)
|
||||
* abs(src)
|
||||
*
|
||||
* Used by the MOVM* (Move Magnitude) instructions.
|
||||
*
|
||||
* @this {CPUStatePDP10}
|
||||
* @param {number} src (36-bit)
|
||||
* @return {number} (negated, but as an unsigned 36-bit result)
|
||||
* @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.fOverflow = this.fCarry1 = true;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -885,6 +885,9 @@ class DebuggerPDP10 extends Debugger {
|
|||
case DebuggerPDP10.REGS.ND:
|
||||
value = this.cpu.fNoDivide? 1 : 0;
|
||||
break;
|
||||
case DebuggerPDP10.REGS.PD:
|
||||
value = this.cpu.fPDOverflow? 1 : 0;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
@ -3145,15 +3148,16 @@ class DebuggerPDP10 extends Debugger {
|
|||
*/
|
||||
doStep(sCmd, sOption)
|
||||
{
|
||||
if (sOption == '?') {
|
||||
var fCallStep = true;
|
||||
var nRegs = (sCmd == "p"? 0 : (sCmd == "pr"? 1 : -1));
|
||||
|
||||
if (sOption == '?' || nRegs < 0) {
|
||||
this.println("step commands:");
|
||||
this.println("\tp\tstep over instruction");
|
||||
this.println("\tpr\tstep over instruction with register update");
|
||||
return;
|
||||
}
|
||||
|
||||
var fCallStep = true;
|
||||
var nRegs = (sCmd == "pr"? 1 : 0);
|
||||
/*
|
||||
* Set up the value for this.nStep (ie, 1 or 2) depending on whether the user wants
|
||||
* a subsequent register dump ("pr") or not ("p").
|
||||
|
|
@ -3819,10 +3823,11 @@ if (DEBUGGER) {
|
|||
C1: 4, // single-bit "register" representing the Carry 1 flag
|
||||
OV: 5, // single-bit "register" representing the Overflow flag
|
||||
ND: 6, // single-bit "register" representing the No Divide flag
|
||||
PD: 7, // single-bit "register" representing the Pushdown Overflow flag
|
||||
};
|
||||
|
||||
DebuggerPDP10.REGNAMES = [
|
||||
"PC", "RA", "EA", "C0", "C1", "OV", "ND"
|
||||
"PC", "RA", "EA", "C0", "C1", "OV", "ND", "PD"
|
||||
];
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ var APPNAME = "PDPjs"; // this @define is the default application name
|
|||
*/
|
||||
var DEBUGGER = true; // this @define is overridden by the Closure Compiler to remove Debugger-related support
|
||||
|
||||
/*
|
||||
* Set this to true to enable behavior compatible with SIMH.
|
||||
*/
|
||||
var SIMH = false;
|
||||
|
||||
/*
|
||||
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
|
||||
* which all machine components should start using; eg: "if (PDP10.DEBUG) ..." instead of "if (DEBUG) ...".
|
||||
|
|
|
|||
Loading…
Reference in a new issue