diff --git a/Contralto/CPU/ALU.cs b/Contralto/CPU/ALU.cs index dca2374..1714fb5 100644 --- a/Contralto/CPU/ALU.cs +++ b/Contralto/CPU/ALU.cs @@ -1,4 +1,5 @@ -using System; +using Contralto.Logging; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -65,7 +66,13 @@ namespace Contralto.CPU case AluFunction.BusMinus1: r = bus - 1; - _carry = (r < 0) ? 1 : 0; + + // Just for clarification; the datasheet specifies: + // "Because subtraction is actually performed by complementary + // addition (1s complement), a carry out means borrow; thus, + // a carry is generated when there is no underflow and no carry + // is generated when there is underflow." + _carry = (r <= 0) ? 0 : 1; break; case AluFunction.BusPlusT: @@ -75,12 +82,12 @@ namespace Contralto.CPU case AluFunction.BusMinusT: r = bus - t; - _carry = (r < 0) ? 1 : 0; + _carry = (r <= 0) ? 0 : 1; break; case AluFunction.BusMinusTMinus1: r = bus - t - 1; - _carry = (r < 0) ? 1 : 0; + _carry = (r <= 0) ? 0 : 1; break; case AluFunction.BusPlusTPlus1: diff --git a/Contralto/CPU/ConstantMemory.cs b/Contralto/CPU/ConstantMemory.cs index f2a8501..5912e89 100644 --- a/Contralto/CPU/ConstantMemory.cs +++ b/Contralto/CPU/ConstantMemory.cs @@ -134,8 +134,8 @@ namespace Contralto.CPU } } - // And invert lines 1-7 - return ((~mappedData) & 0x7f) | (mappedData & 0x80); + // And invert data lines + return (~mappedData) & 0xff; } private static RomFile[] _constantRoms = diff --git a/Contralto/CPU/NovaDisassembler.cs b/Contralto/CPU/NovaDisassembler.cs index 7e8052d..5eab7ab 100644 --- a/Contralto/CPU/NovaDisassembler.cs +++ b/Contralto/CPU/NovaDisassembler.cs @@ -12,6 +12,36 @@ namespace Contralto.CPU.Nova /// public static class NovaDisassembler { + + static NovaDisassembler() + { + _altoIOTable = new Dictionary(); + + _altoIOTable.Add(0x6210, "MUL"); + _altoIOTable.Add(0x6211, "DIV"); + _altoIOTable.Add(0x6000, "CYCLE"); + _altoIOTable.Add(0x6900, "JSRII"); + _altoIOTable.Add(0x6a00, "JSRIS"); + _altoIOTable.Add(0x6e00, "CONVERT"); + _altoIOTable.Add(0x6203, "RCLK"); + _altoIOTable.Add(0x6204, "SIO"); + _altoIOTable.Add(0x6205, "BLT"); + _altoIOTable.Add(0x6206, "BLKS"); + _altoIOTable.Add(0x6207, "SIT"); + _altoIOTable.Add(0x6208, "JMPRAM"); + _altoIOTable.Add(0x6209, "RDRAM"); + _altoIOTable.Add(0x620a, "WRTRAM"); + _altoIOTable.Add(0x620c, "VERSION"); + _altoIOTable.Add(0x620d, "DREAD"); + _altoIOTable.Add(0x620e, "DWRITE"); + _altoIOTable.Add(0x620f, "DEXCH"); + _altoIOTable.Add(0x6212, "DIAGNOSE1"); + _altoIOTable.Add(0x6213, "DIAGNOSE2"); + _altoIOTable.Add(0x6214, "BITBLT"); + _altoIOTable.Add(0x6215, "XMLDA"); + _altoIOTable.Add(0x6216, "XMSTA"); + } + /// /// Disassembles the specified instruction /// @@ -165,6 +195,15 @@ namespace Contralto.CPU.Nova { StringBuilder d = new StringBuilder(); + // + // First see if this is an Alto-specific instruction; if so + // use those mnemonics. Otherwise decode as a Nova I/O instruction. + // + if (_altoIOTable.ContainsKey(instructionWord)) + { + return _altoIOTable[instructionWord]; + } + // Accumulator int ac = (instructionWord & 0x1800) >> 11; @@ -239,6 +278,11 @@ namespace Contralto.CPU.Nova return d.ToString(); } + /// + /// Holds a map from opcode to Alto I/O mnemonics + /// + private static Dictionary _altoIOTable; + private enum InstructionClass { MEM = 0x0000, diff --git a/Contralto/CPU/Shifter.cs b/Contralto/CPU/Shifter.cs index 743e6fa..b07454b 100644 --- a/Contralto/CPU/Shifter.cs +++ b/Contralto/CPU/Shifter.cs @@ -29,9 +29,26 @@ namespace Contralto.CPU { _op = ShifterOp.Invalid; _count = 0; + _output = 0; _magic = false; } + /// + /// Returns the result of the last Shifter operation (via DoOperation). + /// + public static ushort Output + { + get { return _output; } + } + + /// + /// Returns the last DNS-style Carry bit from the last operation (via DoOperation). + /// + public static int DNSCarry + { + get { return _dnsCarry; } + } + public static void SetOperation(ShifterOp op, int count) { _op = op; @@ -52,17 +69,18 @@ namespace Contralto.CPU /// TODO: this is still kind of clumsy. /// /// - public static void SetDNS(bool dns) + public static void SetDNS(bool dns, int carry) { _dns = dns; + _dnsCarry = carry; } /// - /// Does the last specified operation to the specified inputs + /// Does the last specified operation to the specified inputs; the result + /// can be read from Output. /// /// Normal input to be shifted - /// CPU t register, for MAGIC shifts only - /// + /// CPU t register, for MAGIC shifts only public static ushort DoOperation(ushort input, ushort t) { // Sanity check: MAGIC and DNS cannot be set at the same time. @@ -70,25 +88,24 @@ namespace Contralto.CPU { throw new InvalidOperationException("Both MAGIC and DNS bits are set."); } - - ushort output = 0; + switch(_op) { case ShifterOp.Invalid: throw new InvalidOperationException("Shifter op has not been set."); case ShifterOp.None: - output = input; + _output = input; break; case ShifterOp.ShiftLeft: - output = (ushort)(input << _count); + _output = (ushort)(input << _count); if (_magic) { // "MAGIC places the high order bit of T into the low order bit of the // shifter output on left shifts..." - output |= (ushort)((t & 0x8000) >> 15); + _output |= (ushort)((t & 0x8000) >> 15); } else if (_dns) { @@ -97,13 +114,13 @@ namespace Contralto.CPU break; case ShifterOp.ShiftRight: - output = (ushort)(input >> _count); + _output = (ushort)(input >> _count); if (_magic) { // "...and places the low order bit of T into the high order bit position // of the shifter output on right shifts." - output |= (ushort)((t & 0x1) << 15); + _output |= (ushort)((t & 0x1) << 15); } else if (_dns) { @@ -113,11 +130,11 @@ namespace Contralto.CPU case ShifterOp.RotateLeft: // TODO: optimize, this is stupid - output = input; + _output = input; for (int i = 0; i < _count; i++) { - int c = (output & 0x8000) >> 15; - output = (ushort)((output << 1) | c); + int c = (_output & 0x8000) >> 15; + _output = (ushort)((_output << 1) | c); } if (_dns) @@ -128,11 +145,11 @@ namespace Contralto.CPU case ShifterOp.RotateRight: // TODO: optimize, this is still stupid - output = input; + _output = input; for (int i = 0; i < _count; i++) { - int c = (output & 0x1) << 15; - output = (ushort)((output >> 1) | c); + int c = (_output & 0x1) << 15; + _output = (ushort)((_output >> 1) | c); } break; @@ -140,12 +157,14 @@ namespace Contralto.CPU throw new InvalidOperationException(String.Format("Unhandled shift operation {0}", _op)); } - return output; + return _output; } private static ShifterOp _op; + private static ushort _output; private static int _count; private static bool _magic; private static bool _dns; + private static int _dnsCarry; } } diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index 4c20df4..700c326 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -99,6 +99,13 @@ namespace Contralto.CPU _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); break; + case EmulatorF2.LoadDNS: + // + // "...DNS also addresses R from (3-IR[3 - 4])..." + // + _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); + break; + } } @@ -206,10 +213,8 @@ namespace Contralto.CPU } else { - // Use the PROM. - // We OR in 0x80 because the top address line is controlled by the value of ACSOURCE(2), which is always - // 1 here (since ACSOURCE is 14 decimal). - _nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8) | 0x80]; + // Use the PROM. + _nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8)]; } break; @@ -228,8 +233,67 @@ namespace Contralto.CPU break; case EmulatorF2.LoadDNS: - // DNS<- modifies the normal shift operations. - Shifter.SetDNS(true); + // DNS<- does the following: + // - modifies the normal shift operations to perform Nova-style shifts (done here) + // - addresses R from 3-IR[3-4] (destination AC) (see Early LoadDNS handler) + // - stores into R unless IR[12] is set (done here) + // - calculates Nova-style CARRY bit (done here) + // - sets the SKIP and CARRY flip-flops appropriately (see Late LoadDNS handler) + int carry = 0; + + // Also indicates modifying CARRY + _loadR = (_cpu._ir & 0x0008) == 0; + + // At this point the ALU has already done its operation but the shifter has not yet run. + // We need to set the CARRY bit that will be passed through the shifter appropriately. + + // Select carry input value based on carry control + switch(_cpu._ir & 0x30) + { + case 0x00: + // Nothing; CARRY unaffected. + carry = _carry; + break; + + case 0x10: + carry = 0; // Z + break; + + case 0x20: + carry = 1; // O + break; + + case 0x30: + carry = (~_carry) & 0x1; // C + break; + } + + // Now modify the result based on the current ALU result + switch (_cpu._ir & 0x700) + { + case 0x000: + case 0x200: + case 0x700: + // COM, MOV, AND - Carry unaffected + break; + + case 0x100: + case 0x300: + case 0x400: + case 0x500: + case 0x600: + // NEG, INC, ADC, SUB, ADD, AND - invert the carry bit + if (ALU.Carry != 0) + { + carry = (~carry) & 0x1; + } + break; + } + + // Tell the Shifter to do a Nova-style shift with the + // given carry bit. + Shifter.SetDNS(true, carry); + break; default: @@ -238,6 +302,69 @@ namespace Contralto.CPU } } + protected override void ExecuteSpecialFunction2Late(MicroInstruction instruction) + { + EmulatorF2 ef2 = (EmulatorF2)instruction.F2; + switch (ef2) + { + case EmulatorF2.LoadDNS: + // + // Set SKIP and CARRY flip-flops based on the final result of the operation after having + // passed through the shifter. + // + ushort result = Shifter.Output; + int carry = Shifter.DNSCarry; + switch (_cpu._ir & 0x7) + { + case 0: + // None, SKIP is reset + _skip = 0; + break; + + case 1: // SKP + // Always skip + _skip = 1; + break; + + case 2: // SZC + // Skip if carry result is zero + _skip = (carry == 0) ? 1 : 0; + break; + + case 3: // SNC + // Skip if carry result is nonzero + _skip = carry; + break; + + case 4: // SZR + _skip = (result == 0) ? 1 : 0; + break; + + case 5: // SNR + _skip = (result != 0) ? 1 : 0; + break; + + case 6: // SEZ + _skip = (result == 0 || carry == 0) ? 1 : 0; + break; + + case 7: // SBN + _skip = (result != 0 && carry != 0) ? 1 : 0; + break; + } + + if (_loadR) + { + // Write carry flag back. + _carry = carry; + } + + break; + } + + + } + // From Section 3, Pg. 31: // "The emulator has two additional bits of state, the SKIP and CARRY flip flops. CARRY is distinct from the // microprocessor’s ALUC0 bit, tested by the ALUCY function. CARRY is set or cleared as a function of IR and diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index 45b3721..27a4e6b 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -90,16 +90,17 @@ namespace Contralto.CPU /// True if a task switch has been requested by a TASK instruction, false otherwise. protected virtual bool ExecuteInstruction(MicroInstruction instruction) { - bool nextTask = false; - bool loadR = false; + bool nextTask = false; ushort aluData = 0; ushort nextModifier = 0; + _loadR = false; _loadS = false; _rSelect = 0; _busData = 0; Shifter.SetMagic(false); + Shifter.SetDNS(false, 0); // // Wait for memory state machine if a memory operation is requested by this instruction and @@ -157,7 +158,7 @@ namespace Contralto.CPU case BusSource.LoadR: _busData = 0; // "Loading R forces the BUS to 0 so that an ALU function of 0 and T may be executed simultaneously" - loadR = true; + _loadR = true; break; case BusSource.None: @@ -215,7 +216,7 @@ namespace Contralto.CPU // more than one source is gated to it. Up to 32 such mask contans can be provided for each of the four bus sources // > 4. // NOTE also: - // "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR aaffect only the + // "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR affect only the // selection of R; they do not affect the address supplied to the constant ROM." // Hence we use the unmodified RSELECT value here and above. if ((int)instruction.BS > 4 || @@ -342,9 +343,9 @@ namespace Contralto.CPU // Write back to registers: // // Do writeback to selected R register from shifter output - if (loadR) - { - _cpu._r[_rSelect] = Shifter.DoOperation(_cpu._l, _cpu._t); + if (_loadR) + { + _cpu._r[_rSelect] = Shifter.DoOperation(_cpu._l, _cpu._t); } // Do writeback to selected R register from M @@ -384,6 +385,11 @@ namespace Contralto.CPU _cpu._aluC0 = (ushort)ALU.Carry; } + // + // Execute special functions that happen late in the cycle + // + ExecuteSpecialFunction2Late(instruction); + // // Select next address, using the address modifier from the last instruction. // @@ -405,6 +411,11 @@ namespace Contralto.CPU protected abstract void ExecuteSpecialFunction2(MicroInstruction instruction); + protected virtual void ExecuteSpecialFunction2Late(MicroInstruction instruction) + { + // Nothing by default. + } + // // Per uInstruction Task Data: // Modified by both the base Task implementation and any subclasses @@ -415,6 +426,7 @@ namespace Contralto.CPU protected ushort _nextModifier; // Bits ORed onto the NEXT field of the current instruction protected uint _rSelect; // RSELECT field from current instruction, potentially modified by task protected bool _loadS; // Whether to load S from M at and of cycle + protected bool _loadR; // Whether to load R from shifter at end of cycle. //