/// <summary> /// Writes one byte of the microcode word currently pointed to by /// TPC[6]. /// </summary> /// <param name="b"></param> /// <param name="value"></param> private void WriteIOPMicrocodeWord(int b, byte value) { // // In true Xerox fashion, the 48 bits of the microcode word provided by the IOP // are not in order from MSB to LSB or anything simple like that. Though most of them are. // From SysDefs.asm: // // "; Write (all CSi are complemented values): // CSa equ CSBase + 0; CS Byte a: rA[0:3],,rB[0:3] // CSb equ CSBase + 1; CS Byte b: aS[0:2],,aF[0:2],,aD[0:1] // CSc equ CSBase + 2; CS Byte c: EP,,CIN,,EnSU,,mem,,fS[0:3] // CSd equ CSBase + 3; CS Byte d: fY[0:3], INIA[0:3] // CSe equ CSBase + 4; CS Byte e: fX[0:3], INIA[4:7] // CSf equ CSBase + 5; CS Byte f: fZ[0:3], INIA[8:11]" // // "value" is expected to already be complemented on call to WriteIOPMicrocodeWord. // // TPC register 6 is always used for IOP microcode writes. ulong word = _microcode[_tpc[6]]; switch (b) { case 0: word = (word & 0x00ffffffffff) | ((ulong)value << 40); break; case 1: word = (word & 0xff00ffffffff) | ((ulong)value << 32); break; case 2: word = (word & 0xffff00ffffff) | ((ulong)value << 24); break; case 3: { // FY[0:3], INIA[0:3] ulong fy = ((ulong)value & 0xf0) >> 4; ulong inia = ((ulong)value & 0xf); word = (word & 0xfffffff0f0ff) | (fy << 16) | (inia << 8); } break; case 4: { // FX[0:3], INIA[4:7] ulong fx = ((ulong)value & 0xf0) >> 4; ulong inia = ((ulong)value & 0xf); word = (word & 0xffffff0fff0f) | (fx << 20) | (inia << 4); } break; case 5: { // FZ[0:3], INIA[8:11] ulong fz = ((ulong)value & 0xf0) >> 4; ulong inia = ((ulong)value & 0xf); word = (word & 0xffffffff0ff0) | (fz << 12) | inia; } break; default: throw new InvalidOperationException("Invalid byte number for microcode word."); } _microcode[_tpc[6]] = word; _microcodeCache[_tpc[6]] = new Microinstruction(word); }
/// <summary> /// Executes the ALU operation specified by the given microinstruction. /// </summary> /// <param name="i">The microinstruction</param> /// <param name="d">The ALU D input</param> /// <param name="carryIn">The ALU Carry in</param> /// <param name="loadMAR">Whether this operation is taking place during an MAR<- operation, /// in which case the top half of the ALU needs to be treated specially.</param> public ushort Execute(Microinstruction i, ushort d, bool carryIn, bool loadMAR) { // // Save R[a] for the A-bypass case. // (If the ALU op ends up modifying R[a] in A-Bypass mode (because a == b) // it will happen much later than A-bypass and we want // Y to get the original value of R[a], not the later value.) // // Select source data int r, s; switch (i.aS) { case AluSourcePair.AQ: r = _r[i.rA]; s = _q; break; case AluSourcePair.AB: r = _r[i.rA]; s = _r[i.rB]; break; case AluSourcePair.ZQ: r = 0; s = _q; break; case AluSourcePair.ZB: r = 0; s = _r[i.rB]; break; case AluSourcePair.ZA: r = 0; s = _r[i.rA]; break; case AluSourcePair.DA: r = d; s = _r[i.rA]; break; case AluSourcePair.DQ: r = d; s = _q; break; case AluSourcePair.D0: r = d; s = 0; break; default: throw new InvalidOperationException( String.Format("Unhandled source pair {0}", i.aS)); } // // Do ALU op // int f; int cIn = (carryIn ? 1 : 0); switch (i.aF) { case AluFunction.RplusS: { f = r + s + cIn; CarryOut = (f > 0xffff); NibCarry = (r & 0xf) + (s & 0xf) + cIn > 0xf; PgCarry = (r & 0xff) + (s & 0xff) + cIn > 0xff; int cn = (r & 0xfff) + (s & 0xfff) + cIn > 0xfff ? 1 : 0; Overflow = _overflowTable[r >> 12, s >> 12, cn]; } break; case AluFunction.SminusR: { f = s + (~r & 0xffff) + cIn; CarryOut = (f > 0xffff); NibCarry = ((~r & 0xf) + (s & 0xf) + cIn > 0xf); PgCarry = ((~r & 0xff) + (s & 0xff) + cIn > 0xff); int cn = (~r & 0xfff) + (s & 0xfff) + cIn > 0xfff ? 1 : 0; Overflow = _overflowTable[(~r & 0xffff) >> 12, s >> 12, cn]; } break; case AluFunction.RminusS: { f = r + (~s & 0xffff) + cIn; CarryOut = (f > 0xffff); NibCarry = ((r & 0xf) + (~s & 0xf) + cIn > 0xf); PgCarry = ((r & 0xff) + (~s & 0xff) + cIn > 0xff); int cn = (r & 0xfff) + (~s & 0xfff) + cIn > 0xfff ? 1 : 0; Overflow = _overflowTable[r >> 12, (~s & 0xffff) >> 12, cn]; } break; case AluFunction.RorS: f = r | s; // A few microinstructions do an MAR<- with RorS and expect PgCarry to be set appropriately. NibCarry = _carryTableOr[r & 0xf, s & 0xf, cIn]; PgCarry = _carryTableOr[(r >> 4) & 0xf, (s >> 4) & 0xf, NibCarry ? 1 : 0]; CarryOut = false; Overflow = false; break; case AluFunction.RandS: f = r & s; NibCarry = false; PgCarry = false; CarryOut = false; Overflow = false; break; case AluFunction.notRandS: f = (~r) & s; NibCarry = false; PgCarry = false; CarryOut = false; Overflow = false; break; case AluFunction.RxorS: f = r ^ s; NibCarry = false; PgCarry = false; CarryOut = false; Overflow = false; break; case AluFunction.notRxorS: f = (~r) ^ s; NibCarry = false; PgCarry = false; CarryOut = false; Overflow = false; break; default: throw new InvalidOperationException( String.Format("Unhandled function {0}", i.aF)); } // Clip F to 16 bits f = f & 0xffff; if (loadMAR) { // // If the ALU is being run during a MAR<- operation, the top 8 bits of the ALU are // computed using an operator specified by aF | 3, with the source set to 0,B. // The CarryOut and Overflow flags are clear (since they are not affected by the // OR/notXOR operation), and the carry from the least-significant byte of the ALU does not // carry over to the most-significant byte. // // See page 25 of the microcode ref for details. // // We implement this here by overwriting the upper byte of F with the upper bits of rB // (or its complement). Interlisp microcode expects Overflow and Carry to be set appropriately. // switch ((AluFunction)((int)i.aF | 0x3)) { case AluFunction.RorS: { f = (f & 0xff) | (_r[i.rB] & 0xff00); bool midCarry = _carryTableOr[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0]; Overflow = CarryOut = _carryTableOr[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0]; } break; case AluFunction.notRxorS: { f = (f & 0xff) | ((~_r[i.rB]) & 0xff00); bool midCarry = _carryTableNotXor[(r >> 8) & 0xf, (s >> 8) & 0xf, PgCarry ? 1 : 0]; CarryOut = _carryTableNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0]; Overflow = _overflowNotXor[(r >> 12) & 0xf, (s >> 12) & 0xf, midCarry ? 1 : 0]; } break; } } Zero = (f == 0); Neg = ((f & 0x8000) != 0); // // Write outputs, do shifts and cycles as appropriate before writing back. // (Shifts and cycles do not affect the Y output, only the register being written back to.) // switch (i.AluDestination) { case 0: _q = (ushort)f; Y = (ushort)f; break; case 1: Y = (ushort)f; break; case 2: Y = _r[i.rA]; _r[i.rB] = (ushort)f; break; case 3: _r[i.rB] = (ushort)f; Y = (ushort)f; break; case 4: Y = (ushort)f; if (i.Cycle) { // double-word right shift // MSB of Q gets inverted LSB of F. _q = (ushort)((_q >> 1) | ((~f & 0x1) << 15)); // MSB of F gets Carry in. f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0)); } else { // double-word arithmetic right shift. // MSB of Q gets inverted LSB of F. _q = (ushort)((_q >> 1) | ((~f & 0x1) << 15)); // MSB of F gets Carry out. f = (ushort)((f >> 1) | (CarryOut ? 0x8000 : 0x0)); } _r[i.rB] = (ushort)f; break; case 5: Y = (ushort)f; if (i.Cycle) { // F: single-word right rotate: f = (ushort)((f >> 1) | ((f & 0x1) << 15)); } else { // F: single-word right shift w/carryIn to MSB: f = (ushort)((f >> 1) | (carryIn ? 0x8000 : 0x0)); } _r[i.rB] = (ushort)f; break; case 6: Y = (ushort)f; // double-word left shift (apparently identical for cycle and shift) // LSB of F gets MSB of Q, not inverted. f = (ushort)((f << 1) | ((_q & 0x8000) >> 15)); // LSB of Q gets Cin, inverted _q = (ushort)((_q << 1) | (1 - cIn)); _r[i.rB] = (ushort)f; break; case 7: Y = (ushort)f; if (i.Cycle) { // F: single-word left rotate: f = (ushort)((f << 1) | ((f & 0x8000) >> 15)); } else { // F: single-word left shift w/carryIn to LSB: f = (ushort)((f << 1) | cIn); } _r[i.rB] = (ushort)f; break; default: throw new InvalidOperationException( String.Format("Unhandled destination {0}", i.aF)); } return(Y); }