protected override void ExecuteSpecialFunction2Early(MicroInstruction instruction) { EmulatorF2 ef2 = (EmulatorF2)instruction.F2; switch (ef2) { case EmulatorF2.ACSOURCE: // Early: modify R select field: // "...it replaces the two-low order bits of the R select field with // the complement of the SrcAC field of IR, (IR[1-2] XOR 3), allowing the emulator // to address its accumulators (which are assigned to R0-R3)." _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x6000) >> 13) ^ 3); break; case EmulatorF2.ACDEST: // "...causes (IR[3-4] XOR 3) to be used as the low-order two bits of the RSELECT field. // This address the accumulators from the destination field of the instruction. The selected // register may be loaded or read." case EmulatorF2.LoadDNS: // // "...DNS also addresses R from (3-IR[3 - 4])..." // _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); break; } }
private static string DisassembleEmulatorSpecialFunction2(MicroInstruction instruction) { EmulatorF2 ef2 = (EmulatorF2)instruction.F2; switch (ef2) { case EmulatorF2.ACDEST: return("ACDEST "); case EmulatorF2.ACSOURCE: return("ACSOURCE "); case EmulatorF2.MAGIC: return("MAGIC "); case EmulatorF2.LoadDNS: return("DNS← "); case EmulatorF2.BUSODD: return("BUSODD "); case EmulatorF2.LoadIR: return("IR← "); case EmulatorF2.IDISP: return("IDISP "); default: return(String.Format("F2 {0}", Conversion.ToOctal((int)ef2))); } }
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; } }
protected override void ExecuteSpecialFunction2(MicroInstruction instruction) { EmulatorF2 ef2 = (EmulatorF2)instruction.F2; switch (ef2) { case EmulatorF2.LoadIR: // Load IR from the bus _cpu._ir = _busData; // "IR<- also merges bus bits 0, 5, 6 and 7 into NEXT[6-9] which does a first level // instruction dispatch." _nextModifier |= (ushort)(((_busData & 0x8000) >> 12) | ((_busData & 0x0700) >> 8)); // "IR<- clears SKIP" _skip = 0; break; case EmulatorF2.IDISP: // "The IDISP function (F2=15B) does a 16 way dispatch under control of a PROM and a // multiplexer. The values are tabulated below: // Conditions ORed onto NEXT Comment // // if IR[0] = 1 3-IR[8-9] complement of SH field of IR // elseif IR[1-2] = 0 IR[3-4] JMP, JSR, ISZ, DSZ ; dispatch selects register // elseif IR[1-2] = 1 4 LDA // elseif IR[1-2] = 2 5 STA // elseif IR[4-7] = 0 1 // elseif IR[4-7] = 1 0 // elseif IR[4-7] = 6 16B CONVERT // elseif IR[4-7] = 16B 6 // else IR[4-7] // NB: as always, Xerox labels bits in the opposite order from modern convention; // (bit 0 is the msb...) // // NOTE: The above table is accurate and functions correctly; using the PROM is faster. // if ((_cpu._ir & 0x8000) != 0) { _nextModifier |= (ushort)(3 - ((_cpu._ir & 0xc0) >> 6)); } else { _nextModifier |= ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8) + 0x80]; } break; case EmulatorF2.ACSOURCE: // Late: // "...a dispatch is performed: // Conditions ORed onto NEXT Comment // // if IR[0] = 1 3-IR[8-9] complement of SH field of IR // if IR[1-2] != 3 IR[5] the Indirect bit of R // if IR[3-7] = 0 2 CYCLE // if IR[3-7] = 1 5 RAMTRAP // if IR[3-7] = 2 3 NOPAR -- parameterless opcode group // if IR[3-7] = 3 6 RAMTRAP // if IR[3-7] = 4 7 RAMTRAP // if IR[3-7] = 11B 4 JSRII // if IR[3-7] = 12B 4 JSRIS // if IR[3-7] = 16B 1 CONVERT // if IR[3-7] = 37B 17B ROMTRAP -- used by Swat, the debugger // else 16B ROMTRAP // // NOTE: The above table is accurate and functions correctly; using the PROM is faster. // if ((_cpu._ir & 0x8000) != 0) { // 3-IR[8-9] (shift field of arithmetic instruction) _nextModifier |= (ushort)(3 - ((_cpu._ir & 0xc0) >> 6)); } else { // Use the PROM. _nextModifier |= ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8)]; } break; case EmulatorF2.ACDEST: // Handled in early handler, nothing to do here. break; case EmulatorF2.BUSODD: // "...merges BUS[15] into NEXT[9]." _nextModifier |= (ushort)(_busData & 0x1); break; case EmulatorF2.MAGIC: Shifter.SetModifier(ShifterModifier.Magic); break; case EmulatorF2.LoadDNS: // 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) // [NOTE: This overrides a LoadR BS field if present -- that is, if IR[12] is set and // BS=LoadR, no load into R will take place. Note also that the standard // microcode apparently always specifies a LoadR BS for LoadDNS microinstructions. Need to // look at the schematics more closely to see if this is required or just a convention // of the PARC microassembler.] // - 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) >> 4) { case 0x0: // Nothing; CARRY unaffected. carry = _carry; break; case 0x1: carry = 0; // Z break; case 0x2: carry = 1; // O break; case 0x3: carry = (~_carry) & 0x1; // C break; } // Now modify the result based on the current ALU result switch ((_cpu._ir & 0x700) >> 8) { case 0x0: case 0x2: case 0x7: // COM, MOV, AND - Carry unaffected break; case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: // NEG, INC, ADC, SUB, ADD - invert the carry bit if (_cpu._aluC0 != 0) { carry = (~carry) & 0x1; } break; } // Tell the Shifter to do a Nova-style shift with the // given carry bit. Shifter.SetModifier(ShifterModifier.DNS); Shifter.DNSCarry = carry; break; default: throw new InvalidOperationException(String.Format("Unhandled emulator F2 {0}.", ef2)); } }