From df7e7945edafb5e75975a7ff9cfb8a029ec0a693 Mon Sep 17 00:00:00 2001 From: Ilkka Lehtoranta Date: Tue, 23 Jun 2026 14:14:27 +0300 Subject: [PATCH] Refactor 020 timing formulas --- Copper68k/M68020Interpreter.cs | 18 +- Copper68k/M68kTimingEngine.cs | 467 +---- Copper68k/M68kTimingFormula.cs | 1721 +++++++++++++++++ .../M68kTimingFormulaTests.cs | 308 +++ 4 files changed, 2090 insertions(+), 424 deletions(-) create mode 100644 Copper68k/M68kTimingFormula.cs create mode 100644 CopperMod.Amiga.Tests/M68kTimingFormulaTests.cs diff --git a/Copper68k/M68020Interpreter.cs b/Copper68k/M68020Interpreter.cs index bde8b63..1c6281b 100644 --- a/Copper68k/M68020Interpreter.cs +++ b/Copper68k/M68020Interpreter.cs @@ -4254,17 +4254,13 @@ private void CompleteMovemLongTiming( int registerCount, bool registerToMemory) { - const int registerListImmediateAddressCycles = 4; - var nativeCycles = _profile.FixedInstructionNativeCycles ?? (_profile.Model == M68kAcceleratorModel.M68030 - ? registerToMemory - ? 4 + (2 * registerCount) + registerListImmediateAddressCycles - : 8 + (4 * registerCount) + registerListImmediateAddressCycles - : registerToMemory - ? 4 + (3 * registerCount) + registerListImmediateAddressCycles - : 8 + (4 * registerCount) + registerListImmediateAddressCycles); - var plan = _profile.Model == M68kAcceleratorModel.M68030 - ? M68kInstructionPlan.CreateHeadTail(key, name, nativeCycles, 2, 0) - : M68kInstructionPlan.CreateFlat(key, name, nativeCycles); + var plan = M68kTimingFormula.CreateMovemLongPlan( + key, + name, + registerCount, + registerToMemory, + _profile.Model, + _profile.FixedInstructionNativeCycles); _timing.CompleteInstruction(plan); } diff --git a/Copper68k/M68kTimingEngine.cs b/Copper68k/M68kTimingEngine.cs index 0ddf92c..f2cb00e 100644 --- a/Copper68k/M68kTimingEngine.cs +++ b/Copper68k/M68kTimingEngine.cs @@ -408,7 +408,7 @@ public void Reset() public M68kInstructionPlan GetPlan(M68kInstructionTimingKey key) => _profile.FixedInstructionNativeCycles is int fixedCycles - ? M68kInstructionPlan.CreateFlat(key, "fixed JIT fallback", fixedCycles) + ? M68kTimingFormula.CreateFixedPlan(key, fixedCycles) : _profile.Model is M68kAcceleratorModel.M68030 or M68kAcceleratorModel.M68040 ? M68030TimingModel.GetPlan(key) : M68020TimingModel.GetPlan(key); @@ -540,434 +540,75 @@ private void SynchronizeMachineToNative() } } + internal static class M68kTimingDescriptorCache + { + public static M68kTimingDescriptor[] Create(bool useHeadTail) + { + var keys = Enum.GetValues(); + var descriptors = new M68kTimingDescriptor[keys.Length]; + foreach (var key in keys) + { + if (M68kTimingDescriptor.TryCreateSpecialControlDescriptor(key, useHeadTail, out var descriptor) || + M68kTimingDescriptor.TryCreateOperandShapeDescriptor(key, useHeadTail, out descriptor)) + { + descriptors[(int)key] = descriptor; + } + } + + return descriptors; + } + } + internal static class M68020TimingModel { + private static readonly M68kTimingDescriptor[] Descriptors = M68kTimingDescriptorCache.Create(useHeadTail: false); + public static M68kInstructionPlan GetPlan(M68kInstructionTimingKey key) + => M68kTimingFormula.CreatePlan(GetDescriptor(key)); + + internal static M68kTimingDescriptor GetDescriptor(M68kInstructionTimingKey key) { - return key switch + var index = (int)key; + if ((uint)index < (uint)Descriptors.Length && + Descriptors[index].LegacyLabel is not null) { - M68kInstructionTimingKey.Idle => M68kInstructionPlan.CreateFlat(key, "IDLE", 2, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Nop => M68kInstructionPlan.CreateFlat(key, "NOP", 4, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.LineAException => M68kInstructionPlan.CreateFlat(key, "LINEA", 34, ExceptionBarrier()), - M68kInstructionTimingKey.LineFException => M68kInstructionPlan.CreateFlat(key, "LINEF", 34, ExceptionBarrier()), - M68kInstructionTimingKey.IllegalInstruction => M68kInstructionPlan.CreateFlat(key, "ILLEGAL", 20, ExceptionBarrier()), - M68kInstructionTimingKey.PrivilegeViolation => M68kInstructionPlan.CreateFlat(key, "PRIVILEGE", 20, ExceptionBarrier()), - M68kInstructionTimingKey.FormatError => M68kInstructionPlan.CreateFlat(key, "FORMAT", 20, ExceptionBarrier()), - M68kInstructionTimingKey.InterruptAcknowledge => M68kInstructionPlan.CreateFlat(key, "INTERRUPT", 44, ExceptionBarrier()), - M68kInstructionTimingKey.Movec => M68kInstructionPlan.CreateFlat(key, "MOVEC", 12, M68kTimingBarrier.CacheControl), - M68kInstructionTimingKey.ImmediateWordToConditionCodeRegister => M68kInstructionPlan.CreateFlat(key, "ORI/ANDI/EORI.W #,CCR", 8), - M68kInstructionTimingKey.ImmediateWordToStatusRegister => M68kInstructionPlan.CreateFlat(key, "ORI/ANDI/EORI.W #,SR", 20, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Rte => M68kInstructionPlan.CreateFlat(key, "RTE", 20, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Rtd => M68kInstructionPlan.CreateFlat(key, "RTD", 16, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.Rts => M68kInstructionPlan.CreateFlat(key, "RTS", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.LinkLong => M68kInstructionPlan.CreateFlat(key, "LINK.L", 16), - M68kInstructionTimingKey.ExtbLong => M68kInstructionPlan.CreateFlat(key, "EXTB.L", 4), - M68kInstructionTimingKey.ExtWordData => M68kInstructionPlan.CreateFlat(key, "EXT.W Dn", 2), - M68kInstructionTimingKey.TstWordData => M68kInstructionPlan.CreateFlat(key, "TST.W Dn", 2), - M68kInstructionTimingKey.Moveq => M68kInstructionPlan.CreateFlat(key, "MOVEQ #,Dn", 2), - M68kInstructionTimingKey.NegLongData => M68kInstructionPlan.CreateFlat(key, "NEG.L Dn", 2), - M68kInstructionTimingKey.NotByteData => M68kInstructionPlan.CreateFlat(key, "NOT.B Dn", 2), - M68kInstructionTimingKey.ClrDataLong => M68kInstructionPlan.CreateFlat(key, "CLR.L Dn", 2), - M68kInstructionTimingKey.ClrDataWord => M68kInstructionPlan.CreateFlat(key, "CLR.W Dn", 2), - M68kInstructionTimingKey.ClrLongAddressIndirect => M68kInstructionPlan.CreateFlat(key, "CLR.L (An)", 6), - M68kInstructionTimingKey.ClrLongAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CLR.L (d16,An)", 8), - M68kInstructionTimingKey.ClrLongAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "CLR.L (xxx).L", 8), - M68kInstructionTimingKey.ClrByteAddressIndirect => M68kInstructionPlan.CreateFlat(key, "CLR.B (An)", 4), - M68kInstructionTimingKey.ClrByteAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CLR.B (d16,An)", 6), - M68kInstructionTimingKey.ClrWordAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CLR.W (d16,An)", 6), - M68kInstructionTimingKey.ClrLongPostIncrement => M68kInstructionPlan.CreateFlat(key, "CLR.L (An)+", 6), - M68kInstructionTimingKey.LeaAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "LEA (xxx).L,An", 6), - M68kInstructionTimingKey.LeaAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "LEA (d16,An),An", 4), - M68kInstructionTimingKey.MoveByteImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.B #,(xxx).L", 8), - M68kInstructionTimingKey.MoveWordImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.W #,(xxx).L", 8), - M68kInstructionTimingKey.MoveLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.L #,(xxx).L", 10), - M68kInstructionTimingKey.MoveLongImmediateToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.L #,(An)", 8), - M68kInstructionTimingKey.MoveLongImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.L #,(d16,An)", 10), - M68kInstructionTimingKey.MoveLongImmediateToPostIncrement => M68kInstructionPlan.CreateFlat(key, "MOVE.L #,(An)+", 8), - M68kInstructionTimingKey.MoveLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L #,Dn", 6), - M68kInstructionTimingKey.MoveLongImmediateToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L #,An", 6), - M68kInstructionTimingKey.MoveLongDataToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L Dn,Dn", 2), - M68kInstructionTimingKey.MoveLongDataToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L Dn,An", 2), - M68kInstructionTimingKey.MoveLongDataToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.L Dn,(An)", 4), - M68kInstructionTimingKey.MoveLongDataToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.L Dn,(d16,An)", 6), - M68kInstructionTimingKey.MoveLongAddressToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L An,Dn", 2), - M68kInstructionTimingKey.MoveLongAddressToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L An,An", 2), - M68kInstructionTimingKey.MoveLongAddressToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.L An,(An)", 4), - M68kInstructionTimingKey.MoveLongAddressToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.L An,(d16,An)", 6), - M68kInstructionTimingKey.MoveLongAddressToPostIncrement => M68kInstructionPlan.CreateFlat(key, "MOVE.L An,(An)+", 4), - M68kInstructionTimingKey.MoveLongAddressIndirectToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L (An),Dn", 6), - M68kInstructionTimingKey.MoveLongAddressIndirectToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L (An),An", 6), - M68kInstructionTimingKey.MoveLongPostIncrementToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L (An)+,Dn", 6), - M68kInstructionTimingKey.MoveLongPostIncrementToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L (An)+,An", 6), - M68kInstructionTimingKey.MoveLongAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L (d16,An),Dn", 6), - M68kInstructionTimingKey.MoveLongAddressDisplacementToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L (d16,An),An", 6), - M68kInstructionTimingKey.MoveLongAddressDisplacementToPostIncrement => M68kInstructionPlan.CreateFlat(key, "MOVE.L (d16,An),(An)+", 10), - M68kInstructionTimingKey.MoveLongBriefIndexedToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L (d8,An,Xn),Dn", 8), - M68kInstructionTimingKey.MoveLongBriefIndexedToAddress => M68kInstructionPlan.CreateFlat(key, "MOVEA.L (d8,An,Xn),An", 8), - M68kInstructionTimingKey.MoveLongAddressIndirectToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.L (An),(An)", 8), - M68kInstructionTimingKey.MoveLongAbsoluteLongToData => M68kInstructionPlan.CreateFlat(key, "MOVE.L (xxx).L,Dn", 8), - M68kInstructionTimingKey.MoveLongAbsoluteWordToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.L (xxx).W,(d16,An)", 8), - M68kInstructionTimingKey.MoveLongAbsoluteLongToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.L (xxx).L,(d16,An)", 10), - M68kInstructionTimingKey.MoveLongDataToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.L Dn,(xxx).L", 6), - M68kInstructionTimingKey.MoveLongAddressToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.L An,(xxx).L", 6), - M68kInstructionTimingKey.MoveByteDataToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,Dn", 2), - M68kInstructionTimingKey.MoveByteImmediateToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B #,Dn", 4), - M68kInstructionTimingKey.MoveByteImmediateToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.B #,(An)", 6), - M68kInstructionTimingKey.MoveByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.B #,(d16,An)", 6), - M68kInstructionTimingKey.MoveByteImmediateToBriefIndexed => M68kInstructionPlan.CreateFlat(key, "MOVE.B #,(d8,An,Xn)", 8), - M68kInstructionTimingKey.MoveByteAddressIndirectToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B (An),Dn", 6), - M68kInstructionTimingKey.MoveBytePostIncrementToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B (An)+,Dn", 6), - M68kInstructionTimingKey.MoveByteAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B (d16,An),Dn", 6), - M68kInstructionTimingKey.MoveByteAbsoluteLongToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B (xxx).L,Dn", 6), - M68kInstructionTimingKey.MoveByteBriefIndexedToData => M68kInstructionPlan.CreateFlat(key, "MOVE.B (d8,An,Xn),Dn", 8), - M68kInstructionTimingKey.MoveByteDataToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,(xxx).L", 6), - M68kInstructionTimingKey.MoveByteDataToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,(An)", 4), - M68kInstructionTimingKey.MoveByteDataToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,(d16,An)", 6), - M68kInstructionTimingKey.MoveByteDataToBriefIndexed => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,(d8,An,Xn)", 8), - M68kInstructionTimingKey.MoveByteDataToPostIncrement => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,(An)+", 4), - M68kInstructionTimingKey.MoveByteDataToPredecrement => M68kInstructionPlan.CreateFlat(key, "MOVE.B Dn,-(An)", 4), - M68kInstructionTimingKey.MoveByteBriefIndexedToPredecrement => M68kInstructionPlan.CreateFlat(key, "MOVE.B (d8,An,Xn),-(An)", 10), - M68kInstructionTimingKey.MoveBytePostIncrementToPostIncrement => M68kInstructionPlan.CreateFlat(key, "MOVE.B (An)+,(An)+", 8), - M68kInstructionTimingKey.MoveByteAddressIndirectToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.B (An),(xxx).L", 8), - M68kInstructionTimingKey.MoveByteAbsoluteLongToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.B (xxx).L,(xxx).L", 10), - M68kInstructionTimingKey.MoveWordAbsoluteLongToData => M68kInstructionPlan.CreateFlat(key, "MOVE.W (xxx).L,Dn", 6), - M68kInstructionTimingKey.MoveWordAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "MOVE.W (d16,An),Dn", 6), - M68kInstructionTimingKey.MoveWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "MOVE.W #,Dn", 4), - M68kInstructionTimingKey.MoveWordImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.W #,(d16,An)", 6), - M68kInstructionTimingKey.MoveWordDataToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.W Dn,(d16,An)", 6), - M68kInstructionTimingKey.MoveWordDataToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.W Dn,(xxx).L", 6), - M68kInstructionTimingKey.MoveWordAbsoluteLongToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "MOVE.W (xxx).L,(xxx).L", 10), - M68kInstructionTimingKey.MoveWordAbsoluteLongToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "MOVE.W (xxx).L,(d16,An)", 8), - M68kInstructionTimingKey.ImmediateLogicalByteToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "ORI/ANDI.B #,(xxx).L", 10), - M68kInstructionTimingKey.AddiByteImmediateToData => M68kInstructionPlan.CreateFlat(key, "ADDI.B #,Dn", 4), - M68kInstructionTimingKey.AddiByteImmediateToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "ADDI.B #,(An)", 6), - M68kInstructionTimingKey.AddiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "ADDI.B #,(d16,An)", 8), - M68kInstructionTimingKey.AddiWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "ADDI.W #,Dn", 4), - M68kInstructionTimingKey.AddiLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "ADDI.L #,Dn", 6), - M68kInstructionTimingKey.AddiLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "ADDI.L #,(xxx).L", 12), - M68kInstructionTimingKey.SubiByteImmediateToData => M68kInstructionPlan.CreateFlat(key, "SUBI.B #,Dn", 4), - M68kInstructionTimingKey.SubiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "SUBI.B #,(d16,An)", 8), - M68kInstructionTimingKey.SubiLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "SUBI.L #,Dn", 6), - M68kInstructionTimingKey.SubByteDataToData => M68kInstructionPlan.CreateFlat(key, "SUB.B Dn,Dn", 2), - M68kInstructionTimingKey.SubLongDataToData => M68kInstructionPlan.CreateFlat(key, "SUB.L Dn,Dn", 2), - M68kInstructionTimingKey.SubLongAddressToData => M68kInstructionPlan.CreateFlat(key, "SUB.L An,Dn", 2), - M68kInstructionTimingKey.SubLongAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "SUB.L (d16,An),Dn", 7), - M68kInstructionTimingKey.AddWordDataToData => M68kInstructionPlan.CreateFlat(key, "ADD.W Dn,Dn", 2), - M68kInstructionTimingKey.AddLongDataToData => M68kInstructionPlan.CreateFlat(key, "ADD.L Dn,Dn", 2), - M68kInstructionTimingKey.AddxLongDataToData => M68kInstructionPlan.CreateFlat(key, "ADDX.L Dn,Dn", 2), - M68kInstructionTimingKey.AddLongPostIncrementToData => M68kInstructionPlan.CreateFlat(key, "ADD.L (An)+,Dn", 6), - M68kInstructionTimingKey.AddWordDataToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "ADD.W Dn,(d16,An)", 9), - M68kInstructionTimingKey.AddLongDataToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "ADD.L Dn,(d16,An)", 13), - M68kInstructionTimingKey.AddaLongImmediateToAddress => M68kInstructionPlan.CreateFlat(key, "ADDA.L #,An", 6), - M68kInstructionTimingKey.AddaLongDataToAddress => M68kInstructionPlan.CreateFlat(key, "ADDA.L Dn,An", 2), - M68kInstructionTimingKey.AddaLongAddressDisplacementToAddress => M68kInstructionPlan.CreateFlat(key, "ADDA.L (d16,An),An", 7), - M68kInstructionTimingKey.SubaLongImmediateToAddress => M68kInstructionPlan.CreateFlat(key, "SUBA.L #,An", 6), - M68kInstructionTimingKey.DivuWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "DIVU.W #,Dn", 46), - M68kInstructionTimingKey.DivsWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "DIVS.W #,Dn", 58), - M68kInstructionTimingKey.MuluLong => M68kInstructionPlan.CreateFlat(key, "MULU.L ,Dn", 44), - M68kInstructionTimingKey.MulsLong => M68kInstructionPlan.CreateFlat(key, "MULS.L ,Dn", 44), - M68kInstructionTimingKey.DivuLong => M68kInstructionPlan.CreateFlat(key, "DIVU.L ,Dr:Dq", 76), - M68kInstructionTimingKey.DivsLong => M68kInstructionPlan.CreateFlat(key, "DIVS.L ,Dr:Dq", 82), - M68kInstructionTimingKey.AbcdByteDataToData => M68kInstructionPlan.CreateFlat(key, "ABCD.B Dn,Dn", 6), - M68kInstructionTimingKey.AbcdBytePredecrementMemory => M68kInstructionPlan.CreateFlat(key, "ABCD.B -(An),-(An)", 18), - M68kInstructionTimingKey.SbcdByteDataToData => M68kInstructionPlan.CreateFlat(key, "SBCD.B Dn,Dn", 6), - M68kInstructionTimingKey.SbcdBytePredecrementMemory => M68kInstructionPlan.CreateFlat(key, "SBCD.B -(An),-(An)", 18), - M68kInstructionTimingKey.NbcdByteData => M68kInstructionPlan.CreateFlat(key, "NBCD.B Dn", 6), - M68kInstructionTimingKey.NbcdByteAddressIndirect => M68kInstructionPlan.CreateFlat(key, "NBCD.B (An)", 8), - M68kInstructionTimingKey.NbcdBytePostIncrement => M68kInstructionPlan.CreateFlat(key, "NBCD.B (An)+", 8), - M68kInstructionTimingKey.NbcdBytePredecrement => M68kInstructionPlan.CreateFlat(key, "NBCD.B -(An)", 8), - M68kInstructionTimingKey.NbcdByteAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "NBCD.B (d16,An)", 10), - M68kInstructionTimingKey.NbcdByteBriefIndexed => M68kInstructionPlan.CreateFlat(key, "NBCD.B (d8,An,Xn)", 12), - M68kInstructionTimingKey.NbcdByteAbsoluteWord => M68kInstructionPlan.CreateFlat(key, "NBCD.B (xxx).W", 10), - M68kInstructionTimingKey.NbcdByteAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "NBCD.B (xxx).L", 12), - M68kInstructionTimingKey.AndByteDataToData => M68kInstructionPlan.CreateFlat(key, "AND.B Dn,Dn", 2), - M68kInstructionTimingKey.AndWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "AND.W #,Dn", 4), - M68kInstructionTimingKey.AndLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "AND.L #,Dn", 6), - M68kInstructionTimingKey.MuluWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "MULU.W #,Dn", 46), - M68kInstructionTimingKey.EoriWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "EORI.W #,Dn", 4), - M68kInstructionTimingKey.EoriLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "EORI.L #,Dn", 6), - M68kInstructionTimingKey.CmpiLongImmediateToData => M68kInstructionPlan.CreateFlat(key, "CMPI.L #,Dn", 6), - M68kInstructionTimingKey.CmpiLongImmediateToPostIncrement => M68kInstructionPlan.CreateFlat(key, "CMPI.L #,(An)+", 10), - M68kInstructionTimingKey.CmpiLongImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CMPI.L #,(d16,An)", 9), - M68kInstructionTimingKey.CmpiLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "CMPI.L #,(xxx).L", 10), - M68kInstructionTimingKey.CmpiByteImmediateToData => M68kInstructionPlan.CreateFlat(key, "CMPI.B #,Dn", 4), - M68kInstructionTimingKey.CmpiByteImmediateToAddressIndirect => M68kInstructionPlan.CreateFlat(key, "CMPI.B #,(An)", 6), - M68kInstructionTimingKey.CmpiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CMPI.B #,(d16,An)", 8), - M68kInstructionTimingKey.CmpiWordImmediateToData => M68kInstructionPlan.CreateFlat(key, "CMPI.W #,Dn", 4), - M68kInstructionTimingKey.CmpiWordImmediateToAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "CMPI.W #,(d16,An)", 8), - M68kInstructionTimingKey.CmpiWordImmediateToAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "CMPI.W #,(xxx).L", 8), - M68kInstructionTimingKey.CmpaLongImmediateToAddress => M68kInstructionPlan.CreateFlat(key, "CMPA.L #,An", 6), - M68kInstructionTimingKey.CmpaLongDataToAddress => M68kInstructionPlan.CreateFlat(key, "CMPA.L Dn,An", 2), - M68kInstructionTimingKey.CmpaLongAddressToAddress => M68kInstructionPlan.CreateFlat(key, "CMPA.L An,An", 2), - M68kInstructionTimingKey.CmpaLongAddressIndirectToAddress => M68kInstructionPlan.CreateFlat(key, "CMPA.L (An),An", 6), - M68kInstructionTimingKey.CmpLongDataToData => M68kInstructionPlan.CreateFlat(key, "CMP.L Dn,Dn", 2), - M68kInstructionTimingKey.CmpLongAddressToData => M68kInstructionPlan.CreateFlat(key, "CMP.L An,Dn", 2), - M68kInstructionTimingKey.CmpLongAddressIndirectToData => M68kInstructionPlan.CreateFlat(key, "CMP.L (An),Dn", 6), - M68kInstructionTimingKey.CmpLongPostIncrementToData => M68kInstructionPlan.CreateFlat(key, "CMP.L (An)+,Dn", 6), - M68kInstructionTimingKey.CmpByteDataToData => M68kInstructionPlan.CreateFlat(key, "CMP.B Dn,Dn", 2), - M68kInstructionTimingKey.CmpByteAddressIndirectToData => M68kInstructionPlan.CreateFlat(key, "CMP.B (An),Dn", 6), - M68kInstructionTimingKey.CmpByteAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "CMP.B (d16,An),Dn", 6), - M68kInstructionTimingKey.CmpByteAbsoluteLongToData => M68kInstructionPlan.CreateFlat(key, "CMP.B (xxx).L,Dn", 6), - M68kInstructionTimingKey.CmpWordDataToData => M68kInstructionPlan.CreateFlat(key, "CMP.W Dn,Dn", 2), - M68kInstructionTimingKey.CmpWordAddressDisplacementToData => M68kInstructionPlan.CreateFlat(key, "CMP.W (d16,An),Dn", 6), - M68kInstructionTimingKey.LeaAbsoluteWord => M68kInstructionPlan.CreateFlat(key, "LEA (xxx).W,An", 4), - M68kInstructionTimingKey.SwapData => M68kInstructionPlan.CreateFlat(key, "SWAP Dn", 4), - M68kInstructionTimingKey.AsrLongImmediateData => M68kInstructionPlan.CreateFlat(key, "ASR.L #,Dn", 6), - M68kInstructionTimingKey.AsrWordImmediateData => M68kInstructionPlan.CreateFlat(key, "ASR.W #,Dn", 6), - M68kInstructionTimingKey.LsrLongImmediateData => M68kInstructionPlan.CreateFlat(key, "LSR.L #,Dn", 6), - M68kInstructionTimingKey.AslLongImmediateData => M68kInstructionPlan.CreateFlat(key, "ASL.L #,Dn", 8), - M68kInstructionTimingKey.AslWordImmediateData => M68kInstructionPlan.CreateFlat(key, "ASL.W #,Dn", 8), - M68kInstructionTimingKey.RorByteImmediateData => M68kInstructionPlan.CreateFlat(key, "ROR.B #,Dn", 8), - M68kInstructionTimingKey.RorWordImmediateData => M68kInstructionPlan.CreateFlat(key, "ROR.W #,Dn", 8), - M68kInstructionTimingKey.RolWordImmediateData => M68kInstructionPlan.CreateFlat(key, "ROL.W #,Dn", 8), - M68kInstructionTimingKey.JsrAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "JSR (xxx).L", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.JmpAddressIndirect => M68kInstructionPlan.CreateFlat(key, "JMP (An)", 4, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.JmpAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "JMP (xxx).L", 6, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.OriByteImmediateToData => M68kInstructionPlan.CreateFlat(key, "ORI.B #,Dn", 4), - M68kInstructionTimingKey.BtstByteImmediateAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "BTST #,(xxx).L", 10), - M68kInstructionTimingKey.BchgByteImmediateAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "BCHG #,(xxx).L", 12), - M68kInstructionTimingKey.BclrByteImmediateAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "BCLR #,(xxx).L", 12), - M68kInstructionTimingKey.BsetByteImmediateAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "BSET #,(xxx).L", 12), - M68kInstructionTimingKey.BsetByteImmediateAddressDisplacement => M68kInstructionPlan.CreateFlat(key, "BSET #,(d16,An)", 10), - M68kInstructionTimingKey.BtstImmediateData => M68kInstructionPlan.CreateFlat(key, "BTST #,Dn", 4), - M68kInstructionTimingKey.BclrImmediateData => M68kInstructionPlan.CreateFlat(key, "BCLR #,Dn", 4), - M68kInstructionTimingKey.BsetImmediateData => M68kInstructionPlan.CreateFlat(key, "BSET #,Dn", 4), - M68kInstructionTimingKey.BtstDynamicData => M68kInstructionPlan.CreateFlat(key, "BTST Dn,Dn", 4), - M68kInstructionTimingKey.BclrDynamicData => M68kInstructionPlan.CreateFlat(key, "BCLR Dn,Dn", 4), - M68kInstructionTimingKey.SccAbsoluteLong => M68kInstructionPlan.CreateFlat(key, "Scc (xxx).L", 10), - M68kInstructionTimingKey.BranchByteTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.B taken", 6, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchByteNotTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.B not taken", 4), - M68kInstructionTimingKey.BsrByte => M68kInstructionPlan.CreateFlat(key, "BSR.B", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchWordTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.W taken", 6, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchWordNotTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.W not taken", 6), - M68kInstructionTimingKey.BsrWord => M68kInstructionPlan.CreateFlat(key, "BSR.W", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.DbccConditionTrue => M68kInstructionPlan.CreateFlat(key, "DBcc condition true", 6), - M68kInstructionTimingKey.DbccBranchTaken => M68kInstructionPlan.CreateFlat(key, "DBcc branch taken", 10, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.DbccExpired => M68kInstructionPlan.CreateFlat(key, "DBcc expired", 12), - M68kInstructionTimingKey.BranchLongTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.L taken", 10, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchLongNotTaken => M68kInstructionPlan.CreateFlat(key, "Bcc.L not taken", 8), - M68kInstructionTimingKey.BsrLong => M68kInstructionPlan.CreateFlat(key, "BSR.L", 18, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - _ => throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68020) - }; + return Descriptors[index]; + } + + throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68020); + } + + internal static M68kInstructionPlan GetCompatibilityPlan(M68kInstructionTimingKey key) + { + throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68020); } - private static M68kTimingBarrier ExceptionBarrier() - => M68kTimingBarrier.Exception | M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus; } internal static class M68030TimingModel { + private static readonly M68kTimingDescriptor[] Descriptors = M68kTimingDescriptorCache.Create(useHeadTail: true); + public static M68kInstructionPlan GetPlan(M68kInstructionTimingKey key) + => M68kTimingFormula.CreatePlan(GetDescriptor(key)); + + internal static M68kTimingDescriptor GetDescriptor(M68kInstructionTimingKey key) { - return key switch + var index = (int)key; + if ((uint)index < (uint)Descriptors.Length && + Descriptors[index].LegacyLabel is not null) { - M68kInstructionTimingKey.Idle => M68kInstructionPlan.CreateHeadTail(key, "IDLE", 2, 0, 0, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Nop => M68kInstructionPlan.CreateHeadTail(key, "NOP", 4, 0, 0, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.LineAException => M68kInstructionPlan.CreateHeadTail(key, "LINEA", 34, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.LineFException => M68kInstructionPlan.CreateHeadTail(key, "LINEF", 34, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.IllegalInstruction => M68kInstructionPlan.CreateHeadTail(key, "ILLEGAL", 20, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.PrivilegeViolation => M68kInstructionPlan.CreateHeadTail(key, "PRIVILEGE", 20, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.FormatError => M68kInstructionPlan.CreateHeadTail(key, "FORMAT", 20, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.InterruptAcknowledge => M68kInstructionPlan.CreateHeadTail(key, "INTERRUPT", 44, 0, 0, ExceptionBarrier()), - M68kInstructionTimingKey.Movec => M68kInstructionPlan.CreateHeadTail(key, "MOVEC", 12, 0, 0, M68kTimingBarrier.CacheControl | M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.ImmediateWordToConditionCodeRegister => M68kInstructionPlan.CreateHeadTail(key, "ORI/ANDI/EORI.W #,CCR", 8, 1, 1), - M68kInstructionTimingKey.ImmediateWordToStatusRegister => M68kInstructionPlan.CreateHeadTail(key, "ORI/ANDI/EORI.W #,SR", 20, 0, 0, M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Rte => M68kInstructionPlan.CreateHeadTail(key, "RTE", 20, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus), - M68kInstructionTimingKey.Rtd => M68kInstructionPlan.CreateHeadTail(key, "RTD", 16, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.Rts => M68kInstructionPlan.CreateHeadTail(key, "RTS", 7, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.LinkLong => M68kInstructionPlan.CreateHeadTail(key, "LINK.L", 16, 2, 2), - M68kInstructionTimingKey.ExtbLong => M68kInstructionPlan.CreateHeadTail(key, "EXTB.L", 4, 2, 2), - M68kInstructionTimingKey.ExtWordData => M68kInstructionPlan.CreateHeadTail(key, "EXT.W Dn", 2, 1, 1), - M68kInstructionTimingKey.TstWordData => M68kInstructionPlan.CreateHeadTail(key, "TST.W Dn", 2, 1, 1), - M68kInstructionTimingKey.Moveq => M68kInstructionPlan.CreateHeadTail(key, "MOVEQ #,Dn", 2, 1, 1), - M68kInstructionTimingKey.NegLongData => M68kInstructionPlan.CreateHeadTail(key, "NEG.L Dn", 2, 1, 1), - M68kInstructionTimingKey.NotByteData => M68kInstructionPlan.CreateHeadTail(key, "NOT.B Dn", 2, 1, 1), - M68kInstructionTimingKey.ClrDataLong => M68kInstructionPlan.CreateHeadTail(key, "CLR.L Dn", 2, 1, 1), - M68kInstructionTimingKey.ClrDataWord => M68kInstructionPlan.CreateHeadTail(key, "CLR.W Dn", 2, 1, 1), - M68kInstructionTimingKey.ClrLongAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "CLR.L (An)", 6, 1, 1), - M68kInstructionTimingKey.ClrLongAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CLR.L (d16,An)", 8, 1, 1), - M68kInstructionTimingKey.ClrLongAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "CLR.L (xxx).L", 8, 1, 1), - M68kInstructionTimingKey.ClrByteAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "CLR.B (An)", 4, 1, 1), - M68kInstructionTimingKey.ClrByteAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CLR.B (d16,An)", 6, 1, 1), - M68kInstructionTimingKey.ClrWordAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CLR.W (d16,An)", 6, 1, 1), - M68kInstructionTimingKey.ClrLongPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "CLR.L (An)+", 6, 1, 1), - M68kInstructionTimingKey.LeaAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "LEA (xxx).L,An", 6, 1, 1), - M68kInstructionTimingKey.LeaAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "LEA (d16,An),An", 4, 1, 1), - M68kInstructionTimingKey.MoveByteImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B #,(xxx).L", 8, 1, 1), - M68kInstructionTimingKey.MoveWordImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W #,(xxx).L", 8, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L #,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L #,(An)", 8, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L #,(d16,An)", 10, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L #,(An)+", 8, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveLongImmediateToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L #,An", 6, 1, 1), - M68kInstructionTimingKey.MoveLongDataToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.MoveLongDataToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L Dn,An", 2, 1, 1), - M68kInstructionTimingKey.MoveLongDataToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L Dn,(An)", 4, 1, 1), - M68kInstructionTimingKey.MoveLongDataToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L Dn,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L An,Dn", 2, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L An,An", 2, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L An,(An)", 4, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L An,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L An,(An)+", 4, 1, 1), - M68kInstructionTimingKey.MoveLongAddressIndirectToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (An),Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressIndirectToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L (An),An", 6, 1, 1), - M68kInstructionTimingKey.MoveLongPostIncrementToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (An)+,Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveLongPostIncrementToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L (An)+,An", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (d16,An),Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressDisplacementToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L (d16,An),An", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressDisplacementToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (d16,An),(An)+", 10, 1, 1), - M68kInstructionTimingKey.MoveLongBriefIndexedToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (d8,An,Xn),Dn", 8, 1, 1), - M68kInstructionTimingKey.MoveLongBriefIndexedToAddress => M68kInstructionPlan.CreateHeadTail(key, "MOVEA.L (d8,An,Xn),An", 8, 1, 1), - M68kInstructionTimingKey.MoveLongAddressIndirectToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (An),(An)", 8, 1, 1), - M68kInstructionTimingKey.MoveLongAbsoluteLongToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (xxx).L,Dn", 8, 1, 1), - M68kInstructionTimingKey.MoveLongAbsoluteWordToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (xxx).W,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.MoveLongAbsoluteLongToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L (xxx).L,(d16,An)", 10, 1, 1), - M68kInstructionTimingKey.MoveLongDataToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L Dn,(xxx).L", 6, 1, 1), - M68kInstructionTimingKey.MoveLongAddressToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.L An,(xxx).L", 6, 1, 1), - M68kInstructionTimingKey.MoveByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.MoveByteImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B #,Dn", 4, 1, 1), - M68kInstructionTimingKey.MoveByteImmediateToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B #,(An)", 6, 1, 1), - M68kInstructionTimingKey.MoveByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B #,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveByteImmediateToBriefIndexed => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B #,(d8,An,Xn)", 8, 1, 1), - M68kInstructionTimingKey.MoveByteAddressIndirectToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (An),Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveBytePostIncrementToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (An)+,Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveByteAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (d16,An),Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveByteAbsoluteLongToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (xxx).L,Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveByteBriefIndexedToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (d8,An,Xn),Dn", 8, 1, 1), - M68kInstructionTimingKey.MoveByteDataToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,(xxx).L", 6, 1, 1), - M68kInstructionTimingKey.MoveByteDataToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,(An)", 4, 1, 1), - M68kInstructionTimingKey.MoveByteDataToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveByteDataToBriefIndexed => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,(d8,An,Xn)", 8, 1, 1), - M68kInstructionTimingKey.MoveByteDataToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,(An)+", 4, 1, 1), - M68kInstructionTimingKey.MoveByteDataToPredecrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B Dn,-(An)", 4, 1, 1), - M68kInstructionTimingKey.MoveByteBriefIndexedToPredecrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (d8,An,Xn),-(An)", 10, 1, 1), - M68kInstructionTimingKey.MoveBytePostIncrementToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (An)+,(An)+", 8, 1, 1), - M68kInstructionTimingKey.MoveByteAddressIndirectToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (An),(xxx).L", 8, 1, 1), - M68kInstructionTimingKey.MoveByteAbsoluteLongToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.B (xxx).L,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.MoveWordAbsoluteLongToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W (xxx).L,Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveWordAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W (d16,An),Dn", 6, 1, 1), - M68kInstructionTimingKey.MoveWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W #,Dn", 4, 1, 1), - M68kInstructionTimingKey.MoveWordImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W #,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveWordDataToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W Dn,(d16,An)", 6, 1, 1), - M68kInstructionTimingKey.MoveWordDataToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W Dn,(xxx).L", 6, 1, 1), - M68kInstructionTimingKey.MoveWordAbsoluteLongToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W (xxx).L,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.MoveWordAbsoluteLongToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "MOVE.W (xxx).L,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.ImmediateLogicalByteToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "ORI/ANDI.B #,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.AddiByteImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "ADDI.B #,Dn", 4, 1, 1), - M68kInstructionTimingKey.AddiByteImmediateToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "ADDI.B #,(An)", 6, 1, 1), - M68kInstructionTimingKey.AddiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "ADDI.B #,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.AddiWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "ADDI.W #,Dn", 4, 1, 1), - M68kInstructionTimingKey.AddiLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "ADDI.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.AddiLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "ADDI.L #,(xxx).L", 12, 1, 1), - M68kInstructionTimingKey.SubiByteImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "SUBI.B #,Dn", 4, 1, 1), - M68kInstructionTimingKey.SubiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "SUBI.B #,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.SubiLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "SUBI.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.SubByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "SUB.B Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.SubLongDataToData => M68kInstructionPlan.CreateHeadTail(key, "SUB.L Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.SubLongAddressToData => M68kInstructionPlan.CreateHeadTail(key, "SUB.L An,Dn", 2, 1, 1), - M68kInstructionTimingKey.SubLongAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "SUB.L (d16,An),Dn", 7, 1, 1), - M68kInstructionTimingKey.AddWordDataToData => M68kInstructionPlan.CreateHeadTail(key, "ADD.W Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.AddLongDataToData => M68kInstructionPlan.CreateHeadTail(key, "ADD.L Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.AddxLongDataToData => M68kInstructionPlan.CreateHeadTail(key, "ADDX.L Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.AddLongPostIncrementToData => M68kInstructionPlan.CreateHeadTail(key, "ADD.L (An)+,Dn", 6, 1, 1), - M68kInstructionTimingKey.AddWordDataToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "ADD.W Dn,(d16,An)", 9, 1, 1), - M68kInstructionTimingKey.AddLongDataToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "ADD.L Dn,(d16,An)", 13, 1, 1), - M68kInstructionTimingKey.AddaLongImmediateToAddress => M68kInstructionPlan.CreateHeadTail(key, "ADDA.L #,An", 6, 1, 1), - M68kInstructionTimingKey.AddaLongDataToAddress => M68kInstructionPlan.CreateHeadTail(key, "ADDA.L Dn,An", 2, 1, 1), - M68kInstructionTimingKey.AddaLongAddressDisplacementToAddress => M68kInstructionPlan.CreateHeadTail(key, "ADDA.L (d16,An),An", 7, 1, 1), - M68kInstructionTimingKey.SubaLongImmediateToAddress => M68kInstructionPlan.CreateHeadTail(key, "SUBA.L #,An", 6, 1, 1), - M68kInstructionTimingKey.DivuWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "DIVU.W #,Dn", 46, 1, 1), - M68kInstructionTimingKey.DivsWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "DIVS.W #,Dn", 58, 1, 1), - M68kInstructionTimingKey.MuluLong => M68kInstructionPlan.CreateHeadTail(key, "MULU.L ,Dn", 44, 1, 1), - M68kInstructionTimingKey.MulsLong => M68kInstructionPlan.CreateHeadTail(key, "MULS.L ,Dn", 44, 1, 1), - M68kInstructionTimingKey.DivuLong => M68kInstructionPlan.CreateHeadTail(key, "DIVU.L ,Dr:Dq", 76, 1, 1), - M68kInstructionTimingKey.DivsLong => M68kInstructionPlan.CreateHeadTail(key, "DIVS.L ,Dr:Dq", 82, 1, 1), - M68kInstructionTimingKey.AbcdByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "ABCD.B Dn,Dn", 6, 1, 1), - M68kInstructionTimingKey.AbcdBytePredecrementMemory => M68kInstructionPlan.CreateHeadTail(key, "ABCD.B -(An),-(An)", 18, 1, 1), - M68kInstructionTimingKey.SbcdByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "SBCD.B Dn,Dn", 6, 1, 1), - M68kInstructionTimingKey.SbcdBytePredecrementMemory => M68kInstructionPlan.CreateHeadTail(key, "SBCD.B -(An),-(An)", 18, 1, 1), - M68kInstructionTimingKey.NbcdByteData => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B Dn", 6, 1, 1), - M68kInstructionTimingKey.NbcdByteAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (An)", 8, 1, 1), - M68kInstructionTimingKey.NbcdBytePostIncrement => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (An)+", 8, 1, 1), - M68kInstructionTimingKey.NbcdBytePredecrement => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B -(An)", 8, 1, 1), - M68kInstructionTimingKey.NbcdByteAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (d16,An)", 10, 1, 1), - M68kInstructionTimingKey.NbcdByteBriefIndexed => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (d8,An,Xn)", 12, 1, 1), - M68kInstructionTimingKey.NbcdByteAbsoluteWord => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (xxx).W", 10, 1, 1), - M68kInstructionTimingKey.NbcdByteAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "NBCD.B (xxx).L", 12, 1, 1), - M68kInstructionTimingKey.AndByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "AND.B Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.AndWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "AND.W #,Dn", 4, 1, 1), - M68kInstructionTimingKey.AndLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "AND.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.MuluWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "MULU.W #,Dn", 46, 1, 1), - M68kInstructionTimingKey.EoriWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "EORI.W #,Dn", 4, 1, 1), - M68kInstructionTimingKey.EoriLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "EORI.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpiLongImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "CMPI.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpiLongImmediateToPostIncrement => M68kInstructionPlan.CreateHeadTail(key, "CMPI.L #,(An)+", 10, 1, 1), - M68kInstructionTimingKey.CmpiLongImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CMPI.L #,(d16,An)", 9, 1, 1), - M68kInstructionTimingKey.CmpiLongImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "CMPI.L #,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.CmpiByteImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "CMPI.B #,Dn", 4, 1, 1), - M68kInstructionTimingKey.CmpiByteImmediateToAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "CMPI.B #,(An)", 6, 1, 1), - M68kInstructionTimingKey.CmpiByteImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CMPI.B #,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.CmpiWordImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "CMPI.W #,Dn", 4, 1, 1), - M68kInstructionTimingKey.CmpiWordImmediateToAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "CMPI.W #,(d16,An)", 8, 1, 1), - M68kInstructionTimingKey.CmpiWordImmediateToAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "CMPI.W #,(xxx).L", 8, 1, 1), - M68kInstructionTimingKey.CmpaLongImmediateToAddress => M68kInstructionPlan.CreateHeadTail(key, "CMPA.L #,An", 6, 1, 1), - M68kInstructionTimingKey.CmpaLongDataToAddress => M68kInstructionPlan.CreateHeadTail(key, "CMPA.L Dn,An", 2, 1, 1), - M68kInstructionTimingKey.CmpaLongAddressToAddress => M68kInstructionPlan.CreateHeadTail(key, "CMPA.L An,An", 2, 1, 1), - M68kInstructionTimingKey.CmpaLongAddressIndirectToAddress => M68kInstructionPlan.CreateHeadTail(key, "CMPA.L (An),An", 6, 1, 1), - M68kInstructionTimingKey.CmpLongDataToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.L Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.CmpLongAddressToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.L An,Dn", 2, 1, 1), - M68kInstructionTimingKey.CmpLongAddressIndirectToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.L (An),Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpLongPostIncrementToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.L (An)+,Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpByteDataToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.B Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.CmpByteAddressIndirectToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.B (An),Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpByteAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.B (d16,An),Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpByteAbsoluteLongToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.B (xxx).L,Dn", 6, 1, 1), - M68kInstructionTimingKey.CmpWordDataToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.W Dn,Dn", 2, 1, 1), - M68kInstructionTimingKey.CmpWordAddressDisplacementToData => M68kInstructionPlan.CreateHeadTail(key, "CMP.W (d16,An),Dn", 6, 1, 1), - M68kInstructionTimingKey.LeaAbsoluteWord => M68kInstructionPlan.CreateHeadTail(key, "LEA (xxx).W,An", 4, 1, 1), - M68kInstructionTimingKey.SwapData => M68kInstructionPlan.CreateHeadTail(key, "SWAP Dn", 4, 1, 1), - M68kInstructionTimingKey.AsrLongImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ASR.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.AsrWordImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ASR.W #,Dn", 6, 1, 1), - M68kInstructionTimingKey.LsrLongImmediateData => M68kInstructionPlan.CreateHeadTail(key, "LSR.L #,Dn", 6, 1, 1), - M68kInstructionTimingKey.AslLongImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ASL.L #,Dn", 8, 1, 1), - M68kInstructionTimingKey.AslWordImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ASL.W #,Dn", 8, 1, 1), - M68kInstructionTimingKey.RorByteImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ROR.B #,Dn", 8, 1, 1), - M68kInstructionTimingKey.RorWordImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ROR.W #,Dn", 8, 1, 1), - M68kInstructionTimingKey.RolWordImmediateData => M68kInstructionPlan.CreateHeadTail(key, "ROL.W #,Dn", 8, 1, 1), - M68kInstructionTimingKey.JsrAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "JSR (xxx).L", 7, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.JmpAddressIndirect => M68kInstructionPlan.CreateHeadTail(key, "JMP (An)", 4, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.JmpAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "JMP (xxx).L", 6, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.OriByteImmediateToData => M68kInstructionPlan.CreateHeadTail(key, "ORI.B #,Dn", 4, 1, 1), - M68kInstructionTimingKey.BtstByteImmediateAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "BTST #,(xxx).L", 10, 1, 1), - M68kInstructionTimingKey.BchgByteImmediateAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "BCHG #,(xxx).L", 12, 1, 1), - M68kInstructionTimingKey.BclrByteImmediateAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "BCLR #,(xxx).L", 12, 1, 1), - M68kInstructionTimingKey.BsetByteImmediateAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "BSET #,(xxx).L", 12, 1, 1), - M68kInstructionTimingKey.BsetByteImmediateAddressDisplacement => M68kInstructionPlan.CreateHeadTail(key, "BSET #,(d16,An)", 10, 1, 1), - M68kInstructionTimingKey.BtstImmediateData => M68kInstructionPlan.CreateHeadTail(key, "BTST #,Dn", 4, 1, 1), - M68kInstructionTimingKey.BclrImmediateData => M68kInstructionPlan.CreateHeadTail(key, "BCLR #,Dn", 4, 1, 1), - M68kInstructionTimingKey.BsetImmediateData => M68kInstructionPlan.CreateHeadTail(key, "BSET #,Dn", 4, 1, 1), - M68kInstructionTimingKey.BtstDynamicData => M68kInstructionPlan.CreateHeadTail(key, "BTST Dn,Dn", 4, 1, 1), - M68kInstructionTimingKey.BclrDynamicData => M68kInstructionPlan.CreateHeadTail(key, "BCLR Dn,Dn", 4, 1, 1), - M68kInstructionTimingKey.SccAbsoluteLong => M68kInstructionPlan.CreateHeadTail(key, "Scc (xxx).L", 10, 1, 1), - M68kInstructionTimingKey.BranchByteTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.B taken", 6, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchByteNotTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.B not taken", 4, 1, 1), - M68kInstructionTimingKey.BsrByte => M68kInstructionPlan.CreateHeadTail(key, "BSR.B", 7, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchWordTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.W taken", 6, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchWordNotTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.W not taken", 6, 1, 1), - M68kInstructionTimingKey.BsrWord => M68kInstructionPlan.CreateHeadTail(key, "BSR.W", 7, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.DbccConditionTrue => M68kInstructionPlan.CreateHeadTail(key, "DBcc condition true", 6, 1, 1), - M68kInstructionTimingKey.DbccBranchTaken => M68kInstructionPlan.CreateHeadTail(key, "DBcc branch taken", 10, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.DbccExpired => M68kInstructionPlan.CreateHeadTail(key, "DBcc expired", 12, 1, 1), - M68kInstructionTimingKey.BranchLongTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.L taken", 10, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - M68kInstructionTimingKey.BranchLongNotTaken => M68kInstructionPlan.CreateHeadTail(key, "Bcc.L not taken", 8, 1, 1), - M68kInstructionTimingKey.BsrLong => M68kInstructionPlan.CreateHeadTail(key, "BSR.L", 18, 0, 0, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), - _ => throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68030) - }; + return Descriptors[index]; + } + + throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68030); + } + + internal static M68kInstructionPlan GetCompatibilityPlan(M68kInstructionTimingKey key) + { + throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68030); } - private static M68kTimingBarrier ExceptionBarrier() - => M68kTimingBarrier.Exception | M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus; } internal sealed class M68kAcceleratorBusBridge diff --git a/Copper68k/M68kTimingFormula.cs b/Copper68k/M68kTimingFormula.cs new file mode 100644 index 0000000..a505fdc --- /dev/null +++ b/Copper68k/M68kTimingFormula.cs @@ -0,0 +1,1721 @@ +using System; + +namespace Copper68k +{ + internal enum M68kTimingOperation + { + Other, + Idle, + NoOperation, + Exception, + Move, + MoveAddress, + MoveQuick, + MoveControl, + MoveMultiple, + Clear, + Test, + LoadEffectiveAddress, + Add, + AddAddress, + AddExtended, + Subtract, + SubtractAddress, + Compare, + CompareAddress, + Logical, + Bit, + ShiftRotate, + Decimal, + Multiply, + Divide, + Branch, + DecrementBranch, + Jump, + JumpSubroutine, + Return, + Link, + Extend, + Swap, + SetCondition + } + + internal enum M68kTimingOperandForm + { + None, + DataRegister, + AddressRegister, + AddressIndirect, + PostIncrement, + Predecrement, + AddressDisplacement, + BriefIndexed, + AbsoluteWord, + AbsoluteLong, + Immediate, + ConditionCodeRegister, + StatusRegister, + ControlRegister, + RegisterList, + EffectiveAddress, + Stack, + Other + } + + internal enum M68kTimingBranchOutcome + { + None, + Taken, + NotTaken, + ConditionTrue, + Expired + } + + internal enum M68kTimingFormulaKind + { + Compatibility, + OperandShape, + SpecialControl, + MovemLong + } + + internal readonly record struct M68kTimingOperand(M68kTimingOperandForm Form, string Label) + { + public static M68kTimingOperand None => new(M68kTimingOperandForm.None, string.Empty); + + public static M68kTimingOperand FromLabel(string label) + { + label = label.Trim(); + if (label.Length == 0) + { + return None; + } + + if (label == "Dn") + { + return new(M68kTimingOperandForm.DataRegister, label); + } + + if (label == "An") + { + return new(M68kTimingOperandForm.AddressRegister, label); + } + + if (label == "CCR") + { + return new(M68kTimingOperandForm.ConditionCodeRegister, label); + } + + if (label == "SR") + { + return new(M68kTimingOperandForm.StatusRegister, label); + } + + if (label == "MOVEC") + { + return new(M68kTimingOperandForm.ControlRegister, label); + } + + if (label == "#") + { + return new(M68kTimingOperandForm.Immediate, label); + } + + if (label == "") + { + return new(M68kTimingOperandForm.EffectiveAddress, label); + } + + if (label.Contains("register", StringComparison.OrdinalIgnoreCase)) + { + return new(M68kTimingOperandForm.RegisterList, label); + } + + if (label == "(An)") + { + return new(M68kTimingOperandForm.AddressIndirect, label); + } + + if (label == "(An)+") + { + return new(M68kTimingOperandForm.PostIncrement, label); + } + + if (label == "-(An)") + { + return new(M68kTimingOperandForm.Predecrement, label); + } + + if (label == "(d16,An)") + { + return new(M68kTimingOperandForm.AddressDisplacement, label); + } + + if (label == "(d8,An,Xn)") + { + return new(M68kTimingOperandForm.BriefIndexed, label); + } + + if (label == "(xxx).W") + { + return new(M68kTimingOperandForm.AbsoluteWord, label); + } + + if (label == "(xxx).L") + { + return new(M68kTimingOperandForm.AbsoluteLong, label); + } + + if (label.IndexOf("stack", StringComparison.OrdinalIgnoreCase) >= 0) + { + return new(M68kTimingOperandForm.Stack, label); + } + + return new(M68kTimingOperandForm.Other, label); + } + } + + internal readonly record struct M68kTimingPlanShape( + int NativeCycles, + int HeadCycles, + int TailCycles, + bool UsesHeadTail) + { + public static M68kTimingPlanShape FromPlan(M68kInstructionPlan plan) + => new(plan.NativeCycles, plan.HeadCycles, plan.TailCycles, plan.UsesHeadTail); + } + + internal readonly record struct M68kTimingDescriptor( + M68kTimingFormulaKind FormulaKind, + M68kTimingOperation Operation, + M68kOperandSize? Size, + M68kTimingOperand Source, + M68kTimingOperand Destination, + M68kTimingBranchOutcome BranchOutcome, + int RegisterCount, + M68kTimingBarrier Barriers, + M68kInstructionTimingKey LegacyKey, + string LegacyLabel, + M68kTimingPlanShape Plan) + { + public static bool TryCreateOperandShapeDescriptor( + M68kInstructionTimingKey key, + bool useHeadTail, + out M68kTimingDescriptor descriptor) + { + descriptor = default; + if (!UsesOperandShapeFormula(key) || + !TryCreateOperandShapeLabel(key, out var label)) + { + return false; + } + + var (source, destination) = SplitOperands(label); + descriptor = new( + M68kTimingFormulaKind.OperandShape, + ClassifyOperation(key, label), + ParseSize(label), + source, + destination, + ClassifyBranchOutcome(key, label), + RegisterCount: 0, + GetOperandShapeBarriers(key), + key, + label, + GetOperandShapePlanShape(key, useHeadTail)); + return true; + } + + public static bool TryCreateSpecialControlDescriptor( + M68kInstructionTimingKey key, + bool useHeadTail, + out M68kTimingDescriptor descriptor) + { + descriptor = default; + if (!UsesSpecialControlFormula(key)) + { + return false; + } + + var label = GetSpecialControlLabel(key); + var (source, destination) = SplitOperands(label); + descriptor = new( + M68kTimingFormulaKind.SpecialControl, + ClassifyOperation(key, label), + ParseSize(label), + source, + destination, + ClassifyBranchOutcome(key, label), + RegisterCount: 0, + GetSpecialControlBarriers(key, useHeadTail), + key, + label, + GetSpecialControlPlanShape(key, useHeadTail)); + return true; + } + + public static M68kTimingDescriptor ForMovemLong( + M68kInstructionTimingKey key, + string label, + int registerCount, + bool registerToMemory, + M68kAcceleratorModel model, + int? fixedCycles) + { + const int registerListImmediateAddressCycles = 4; + var nativeCycles = fixedCycles ?? (model == M68kAcceleratorModel.M68030 + ? registerToMemory + ? 4 + (2 * registerCount) + registerListImmediateAddressCycles + : 8 + (4 * registerCount) + registerListImmediateAddressCycles + : registerToMemory + ? 4 + (3 * registerCount) + registerListImmediateAddressCycles + : 8 + (4 * registerCount) + registerListImmediateAddressCycles); + var plan = model == M68kAcceleratorModel.M68030 + ? new M68kTimingPlanShape(nativeCycles, HeadCycles: 2, TailCycles: 0, UsesHeadTail: true) + : new M68kTimingPlanShape(nativeCycles, HeadCycles: 0, TailCycles: 0, UsesHeadTail: false); + + return new( + M68kTimingFormulaKind.MovemLong, + M68kTimingOperation.MoveMultiple, + M68kOperandSize.Long, + registerToMemory + ? new M68kTimingOperand(M68kTimingOperandForm.RegisterList, "registers") + : M68kTimingOperand.FromLabel("(An)+"), + registerToMemory + ? M68kTimingOperand.FromLabel("-(An)") + : new M68kTimingOperand(M68kTimingOperandForm.RegisterList, "registers"), + M68kTimingBranchOutcome.None, + registerCount, + M68kTimingBarrier.None, + key, + label, + plan); + } + + public static bool UsesOperandShapeFormula(M68kInstructionTimingKey key) + { + var keyName = key.ToString(); + return key is M68kInstructionTimingKey.Moveq or M68kInstructionTimingKey.TstWordData || + keyName.StartsWith("MoveLong", StringComparison.Ordinal) || + keyName.StartsWith("MoveWord", StringComparison.Ordinal) || + keyName.StartsWith("MoveByte", StringComparison.Ordinal) || + keyName.StartsWith("Lea", StringComparison.Ordinal) || + keyName.StartsWith("Clr", StringComparison.Ordinal) || + keyName.StartsWith("ImmediateWordTo", StringComparison.Ordinal) || + keyName.StartsWith("ImmediateLogical", StringComparison.Ordinal) || + keyName.StartsWith("Add", StringComparison.Ordinal) || + keyName.StartsWith("Sub", StringComparison.Ordinal) || + keyName.StartsWith("Neg", StringComparison.Ordinal) || + keyName.StartsWith("Not", StringComparison.Ordinal) || + keyName.StartsWith("Ori", StringComparison.Ordinal) || + keyName.StartsWith("And", StringComparison.Ordinal) || + keyName.StartsWith("Eori", StringComparison.Ordinal) || + keyName.StartsWith("Cmpi", StringComparison.Ordinal) || + keyName.StartsWith("Cmpa", StringComparison.Ordinal) || + keyName.StartsWith("Cmp", StringComparison.Ordinal) || + keyName.StartsWith("Branch", StringComparison.Ordinal) || + keyName.StartsWith("Bsr", StringComparison.Ordinal) || + keyName.StartsWith("Dbcc", StringComparison.Ordinal) || + keyName.StartsWith("Scc", StringComparison.Ordinal) || + keyName.StartsWith("Btst", StringComparison.Ordinal) || + keyName.StartsWith("Bchg", StringComparison.Ordinal) || + keyName.StartsWith("Bclr", StringComparison.Ordinal) || + keyName.StartsWith("Bset", StringComparison.Ordinal) || + keyName.StartsWith("Asr", StringComparison.Ordinal) || + keyName.StartsWith("Asl", StringComparison.Ordinal) || + keyName.StartsWith("Lsr", StringComparison.Ordinal) || + keyName.StartsWith("Lsl", StringComparison.Ordinal) || + keyName.StartsWith("Ror", StringComparison.Ordinal) || + keyName.StartsWith("Rol", StringComparison.Ordinal) || + keyName.StartsWith("Roxr", StringComparison.Ordinal) || + keyName.StartsWith("Roxl", StringComparison.Ordinal) || + keyName.StartsWith("Abcd", StringComparison.Ordinal) || + keyName.StartsWith("Sbcd", StringComparison.Ordinal) || + keyName.StartsWith("Nbcd", StringComparison.Ordinal) || + keyName.StartsWith("Mulu", StringComparison.Ordinal) || + keyName.StartsWith("Muls", StringComparison.Ordinal) || + keyName.StartsWith("Divu", StringComparison.Ordinal) || + keyName.StartsWith("Divs", StringComparison.Ordinal); + } + + public static bool UsesSpecialControlFormula(M68kInstructionTimingKey key) + => key is M68kInstructionTimingKey.Idle or + M68kInstructionTimingKey.Nop or + M68kInstructionTimingKey.LineAException or + M68kInstructionTimingKey.LineFException or + M68kInstructionTimingKey.IllegalInstruction or + M68kInstructionTimingKey.PrivilegeViolation or + M68kInstructionTimingKey.FormatError or + M68kInstructionTimingKey.InterruptAcknowledge or + M68kInstructionTimingKey.Movec or + M68kInstructionTimingKey.Rte or + M68kInstructionTimingKey.Rtd or + M68kInstructionTimingKey.Rts or + M68kInstructionTimingKey.LinkLong or + M68kInstructionTimingKey.ExtbLong or + M68kInstructionTimingKey.ExtWordData or + M68kInstructionTimingKey.SwapData or + M68kInstructionTimingKey.JsrAbsoluteLong or + M68kInstructionTimingKey.JmpAddressIndirect or + M68kInstructionTimingKey.JmpAbsoluteLong; + + private static string GetSpecialControlLabel(M68kInstructionTimingKey key) + => key switch + { + M68kInstructionTimingKey.Idle => "IDLE", + M68kInstructionTimingKey.Nop => "NOP", + M68kInstructionTimingKey.LineAException => "LINEA", + M68kInstructionTimingKey.LineFException => "LINEF", + M68kInstructionTimingKey.IllegalInstruction => "ILLEGAL", + M68kInstructionTimingKey.PrivilegeViolation => "PRIVILEGE", + M68kInstructionTimingKey.FormatError => "FORMAT", + M68kInstructionTimingKey.InterruptAcknowledge => "INTERRUPT", + M68kInstructionTimingKey.Movec => "MOVEC", + M68kInstructionTimingKey.Rte => "RTE", + M68kInstructionTimingKey.Rtd => "RTD", + M68kInstructionTimingKey.Rts => "RTS", + M68kInstructionTimingKey.LinkLong => "LINK.L", + M68kInstructionTimingKey.ExtbLong => "EXTB.L", + M68kInstructionTimingKey.ExtWordData => "EXT.W Dn", + M68kInstructionTimingKey.SwapData => "SWAP Dn", + M68kInstructionTimingKey.JsrAbsoluteLong => "JSR (xxx).L", + M68kInstructionTimingKey.JmpAddressIndirect => "JMP (An)", + M68kInstructionTimingKey.JmpAbsoluteLong => "JMP (xxx).L", + _ => throw new UnsupportedM68kTimingException(key, M68kAcceleratorModel.M68020) + }; + + private static M68kTimingPlanShape GetSpecialControlPlanShape(M68kInstructionTimingKey key, bool useHeadTail) + { + if (!useHeadTail) + { + return new M68kTimingPlanShape(0, HeadCycles: 0, TailCycles: 0, UsesHeadTail: false); + } + + var (headCycles, tailCycles) = key switch + { + M68kInstructionTimingKey.LinkLong => (2, 2), + M68kInstructionTimingKey.ExtbLong => (2, 2), + M68kInstructionTimingKey.ExtWordData => (1, 1), + M68kInstructionTimingKey.SwapData => (1, 1), + _ => (0, 0) + }; + return new M68kTimingPlanShape(0, headCycles, tailCycles, UsesHeadTail: true); + } + + private static M68kTimingBarrier GetSpecialControlBarriers(M68kInstructionTimingKey key, bool useHeadTail) + => key switch + { + M68kInstructionTimingKey.Idle => M68kTimingBarrier.SynchronizeBus, + M68kInstructionTimingKey.Nop => M68kTimingBarrier.SynchronizeBus, + M68kInstructionTimingKey.LineAException => ExceptionBarrier(), + M68kInstructionTimingKey.LineFException => ExceptionBarrier(), + M68kInstructionTimingKey.IllegalInstruction => ExceptionBarrier(), + M68kInstructionTimingKey.PrivilegeViolation => ExceptionBarrier(), + M68kInstructionTimingKey.FormatError => ExceptionBarrier(), + M68kInstructionTimingKey.InterruptAcknowledge => ExceptionBarrier(), + M68kInstructionTimingKey.Movec => useHeadTail + ? M68kTimingBarrier.CacheControl | M68kTimingBarrier.SynchronizeBus + : M68kTimingBarrier.CacheControl, + M68kInstructionTimingKey.Rte => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus, + M68kInstructionTimingKey.Rtd => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch, + M68kInstructionTimingKey.Rts => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch, + M68kInstructionTimingKey.JsrAbsoluteLong => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch, + M68kInstructionTimingKey.JmpAddressIndirect => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch, + M68kInstructionTimingKey.JmpAbsoluteLong => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch, + _ => M68kTimingBarrier.None + }; + + private static M68kTimingBarrier ExceptionBarrier() + => M68kTimingBarrier.Exception | M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus; + + private static M68kTimingPlanShape GetOperandShapePlanShape(M68kInstructionTimingKey key, bool useHeadTail) + { + if (!useHeadTail) + { + return new M68kTimingPlanShape(0, HeadCycles: 0, TailCycles: 0, UsesHeadTail: false); + } + + var headTailCycles = UsesZeroHeadTailOperandShape(key) ? 0 : 1; + return new M68kTimingPlanShape(0, headTailCycles, headTailCycles, UsesHeadTail: true); + } + + private static bool UsesZeroHeadTailOperandShape(M68kInstructionTimingKey key) + => key is M68kInstructionTimingKey.ImmediateWordToStatusRegister or + M68kInstructionTimingKey.BranchByteTaken or + M68kInstructionTimingKey.BsrByte or + M68kInstructionTimingKey.BranchWordTaken or + M68kInstructionTimingKey.BsrWord or + M68kInstructionTimingKey.DbccBranchTaken or + M68kInstructionTimingKey.BranchLongTaken or + M68kInstructionTimingKey.BsrLong; + + private static M68kTimingBarrier GetOperandShapeBarriers(M68kInstructionTimingKey key) + => key switch + { + M68kInstructionTimingKey.ImmediateWordToStatusRegister => M68kTimingBarrier.SynchronizeBus, + M68kInstructionTimingKey.BranchByteTaken => BranchBarrier(), + M68kInstructionTimingKey.BsrByte => BranchBarrier(), + M68kInstructionTimingKey.BranchWordTaken => BranchBarrier(), + M68kInstructionTimingKey.BsrWord => BranchBarrier(), + M68kInstructionTimingKey.DbccBranchTaken => BranchBarrier(), + M68kInstructionTimingKey.BranchLongTaken => BranchBarrier(), + M68kInstructionTimingKey.BsrLong => BranchBarrier(), + _ => M68kTimingBarrier.None + }; + + private static M68kTimingBarrier BranchBarrier() + => M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch; + + private static bool TryCreateOperandShapeLabel(M68kInstructionTimingKey key, out string label) + { + var keyName = key.ToString(); + label = key switch + { + M68kInstructionTimingKey.Moveq => "MOVEQ #,Dn", + M68kInstructionTimingKey.ImmediateWordToConditionCodeRegister => "ORI/ANDI/EORI.W #,CCR", + M68kInstructionTimingKey.ImmediateWordToStatusRegister => "ORI/ANDI/EORI.W #,SR", + M68kInstructionTimingKey.MuluLong => "MULU.L ,Dn", + M68kInstructionTimingKey.MulsLong => "MULS.L ,Dn", + M68kInstructionTimingKey.DivuLong => "DIVU.L ,Dr:Dq", + M68kInstructionTimingKey.DivsLong => "DIVS.L ,Dr:Dq", + M68kInstructionTimingKey.DbccConditionTrue => "DBcc condition true", + M68kInstructionTimingKey.DbccBranchTaken => "DBcc branch taken", + M68kInstructionTimingKey.DbccExpired => "DBcc expired", + M68kInstructionTimingKey.SccAbsoluteLong => "Scc (xxx).L", + _ => string.Empty + }; + + if (label.Length != 0) + { + return true; + } + + return TryCreateMoveLabel(keyName, out label) || + TryCreateLeaLabel(keyName, out label) || + TryCreateClearLabel(keyName, out label) || + TryCreateUnaryInstructionLabel(keyName, "Tst", "TST", out label) || + TryCreateUnaryInstructionLabel(keyName, "Neg", "NEG", out label) || + TryCreateUnaryInstructionLabel(keyName, "Not", "NOT", out label) || + TryCreateImmediateLogicalLabel(keyName, out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Addi", "ADDI", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Adda", "ADDA", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Addx", "ADDX", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Add", "ADD", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Subi", "SUBI", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Suba", "SUBA", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Sub", "SUB", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Cmpi", "CMPI", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Cmpa", "CMPA", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Cmp", "CMP", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "And", "AND", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Eori", "EORI", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Ori", "ORI", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Mulu", "MULU", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Muls", "MULS", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Divu", "DIVU", out label) || + TryCreateSizedBinaryInstructionLabel(keyName, "Divs", "DIVS", out label) || + TryCreateDecimalLabel(keyName, "Abcd", "ABCD", out label) || + TryCreateDecimalLabel(keyName, "Sbcd", "SBCD", out label) || + TryCreateNbcdLabel(keyName, out label) || + TryCreateBitLabel(keyName, out label) || + TryCreateShiftRotateLabel(keyName, out label) || + TryCreateBranchLabel(keyName, out label); + } + + private static bool TryCreateMoveLabel(string keyName, out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, "Move", out var size, out var operands) || + !TrySplitOperandTokens(operands, out var sourceToken, out var destinationToken) || + !TryGetOperandLabel(sourceToken, out var source) || + !TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + var mnemonic = destinationToken == "Address" ? "MOVEA" : "MOVE"; + label = $"{mnemonic}.{GetSizeSuffix(size)} {source},{destination}"; + return true; + } + + private static bool TryCreateLeaLabel(string keyName, out string label) + { + label = string.Empty; + if (!keyName.StartsWith("Lea", StringComparison.Ordinal) || + !TryGetOperandLabel(keyName["Lea".Length..], out var source)) + { + return false; + } + + label = $"LEA {source},An"; + return true; + } + + private static bool TryCreateClearLabel(string keyName, out string label) + { + label = string.Empty; + if (!keyName.StartsWith("Clr", StringComparison.Ordinal)) + { + return false; + } + + var remainder = keyName["Clr".Length..]; + string destinationToken; + M68kOperandSize size; + if (remainder.StartsWith("Data", StringComparison.Ordinal)) + { + destinationToken = "Data"; + if (!TryReadSizePrefix(remainder["Data".Length..], out size, out var extra) || + extra.Length != 0) + { + return false; + } + } + else if (!TryReadSizePrefix(remainder, out size, out destinationToken)) + { + return false; + } + + if (!TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"CLR.{GetSizeSuffix(size)} {destination}"; + return true; + } + + private static bool TryCreateUnaryInstructionLabel( + string keyName, + string keyPrefix, + string mnemonic, + out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, keyPrefix, out var size, out var destinationToken) || + !TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"{mnemonic}.{GetSizeSuffix(size)} {destination}"; + return true; + } + + private static bool TryCreateImmediateLogicalLabel(string keyName, out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, "ImmediateLogical", out var size, out var remainder) || + !remainder.StartsWith("To", StringComparison.Ordinal) || + !TryGetOperandLabel(remainder["To".Length..], out var destination)) + { + return false; + } + + label = $"ORI/ANDI.{GetSizeSuffix(size)} #,{destination}"; + return true; + } + + private static bool TryCreateSizedBinaryInstructionLabel( + string keyName, + string keyPrefix, + string mnemonic, + out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, keyPrefix, out var size, out var operands) || + !TrySplitOperandTokens(operands, out var sourceToken, out var destinationToken) || + !TryGetOperandLabel(sourceToken, out var source) || + !TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"{mnemonic}.{GetSizeSuffix(size)} {source},{destination}"; + return true; + } + + private static bool TryCreateDecimalLabel( + string keyName, + string keyPrefix, + string mnemonic, + out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, keyPrefix, out var size, out var operands) || + size != M68kOperandSize.Byte) + { + return false; + } + + if (operands == "PredecrementMemory") + { + label = $"{mnemonic}.B -(An),-(An)"; + return true; + } + + if (!TrySplitOperandTokens(operands, out var sourceToken, out var destinationToken) || + !TryGetOperandLabel(sourceToken, out var source) || + !TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"{mnemonic}.B {source},{destination}"; + return true; + } + + private static bool TryCreateNbcdLabel(string keyName, out string label) + { + label = string.Empty; + if (!TryReadPrefixedSize(keyName, "Nbcd", out var size, out var destinationToken) || + size != M68kOperandSize.Byte || + !TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"NBCD.B {destination}"; + return true; + } + + private static bool TryCreateBitLabel(string keyName, out string label) + { + label = string.Empty; + foreach (var (keyPrefix, mnemonic) in new[] + { + ("Btst", "BTST"), + ("Bchg", "BCHG"), + ("Bclr", "BCLR"), + ("Bset", "BSET") + }) + { + if (!keyName.StartsWith(keyPrefix, StringComparison.Ordinal)) + { + continue; + } + + var operands = keyName[keyPrefix.Length..]; + if (operands.StartsWith("Byte", StringComparison.Ordinal)) + { + operands = operands["Byte".Length..]; + } + + string source; + string destinationToken; + if (operands.StartsWith("Immediate", StringComparison.Ordinal)) + { + source = "#"; + destinationToken = operands["Immediate".Length..]; + } + else if (operands.StartsWith("Dynamic", StringComparison.Ordinal)) + { + source = "Dn"; + destinationToken = operands["Dynamic".Length..]; + } + else + { + return false; + } + + if (!TryGetOperandLabel(destinationToken, out var destination)) + { + return false; + } + + label = $"{mnemonic} {source},{destination}"; + return true; + } + + return false; + } + + private static bool TryCreateShiftRotateLabel(string keyName, out string label) + { + label = string.Empty; + foreach (var (keyPrefix, mnemonic) in new[] + { + ("Roxr", "ROXR"), + ("Roxl", "ROXL"), + ("Asr", "ASR"), + ("Asl", "ASL"), + ("Lsr", "LSR"), + ("Lsl", "LSL"), + ("Ror", "ROR"), + ("Rol", "ROL") + }) + { + if (!TryReadPrefixedSize(keyName, keyPrefix, out var size, out var operands)) + { + continue; + } + + if (operands != "ImmediateData") + { + return false; + } + + label = $"{mnemonic}.{GetSizeSuffix(size)} #,Dn"; + return true; + } + + return false; + } + + private static bool TryCreateBranchLabel(string keyName, out string label) + { + label = string.Empty; + if (TryReadPrefixedSize(keyName, "Branch", out var size, out var branchOutcome)) + { + label = branchOutcome switch + { + "Taken" => $"Bcc.{GetSizeSuffix(size)} taken", + "NotTaken" => $"Bcc.{GetSizeSuffix(size)} not taken", + _ => string.Empty + }; + return label.Length != 0; + } + + if (TryReadPrefixedSize(keyName, "Bsr", out size, out var remainder) && + remainder.Length == 0) + { + label = $"BSR.{GetSizeSuffix(size)}"; + return true; + } + + return false; + } + + private static bool TryReadPrefixedSize( + string keyName, + string keyPrefix, + out M68kOperandSize size, + out string remainder) + { + size = default; + remainder = string.Empty; + return keyName.StartsWith(keyPrefix, StringComparison.Ordinal) && + TryReadSizePrefix(keyName[keyPrefix.Length..], out size, out remainder); + } + + private static bool TryReadSizePrefix( + string text, + out M68kOperandSize size, + out string remainder) + { + if (text.StartsWith("Byte", StringComparison.Ordinal)) + { + size = M68kOperandSize.Byte; + remainder = text["Byte".Length..]; + return true; + } + + if (text.StartsWith("Word", StringComparison.Ordinal)) + { + size = M68kOperandSize.Word; + remainder = text["Word".Length..]; + return true; + } + + if (text.StartsWith("Long", StringComparison.Ordinal)) + { + size = M68kOperandSize.Long; + remainder = text["Long".Length..]; + return true; + } + + size = default; + remainder = string.Empty; + return false; + } + + private static bool TrySplitOperandTokens( + string operands, + out string sourceToken, + out string destinationToken) + { + var separator = operands.IndexOf("To", StringComparison.Ordinal); + if (separator <= 0 || separator == operands.Length - "To".Length) + { + sourceToken = string.Empty; + destinationToken = string.Empty; + return false; + } + + sourceToken = operands[..separator]; + destinationToken = operands[(separator + "To".Length)..]; + return true; + } + + private static bool TryGetOperandLabel(string token, out string label) + { + label = token switch + { + "Data" => "Dn", + "Address" => "An", + "AddressIndirect" => "(An)", + "PostIncrement" => "(An)+", + "Predecrement" => "-(An)", + "AddressDisplacement" => "(d16,An)", + "BriefIndexed" => "(d8,An,Xn)", + "AbsoluteWord" => "(xxx).W", + "AbsoluteLong" => "(xxx).L", + "Immediate" => "#", + "ConditionCodeRegister" => "CCR", + "StatusRegister" => "SR", + _ => string.Empty + }; + return label.Length != 0; + } + + private static char GetSizeSuffix(M68kOperandSize size) + => size switch + { + M68kOperandSize.Byte => 'B', + M68kOperandSize.Word => 'W', + M68kOperandSize.Long => 'L', + _ => throw new ArgumentOutOfRangeException(nameof(size), size, null) + }; + + private static (M68kTimingOperand Source, M68kTimingOperand Destination) SplitOperands(string label) + { + var firstSpace = label.IndexOf(' '); + if (firstSpace < 0 || firstSpace == label.Length - 1) + { + return (M68kTimingOperand.None, M68kTimingOperand.None); + } + + var operands = label[(firstSpace + 1)..].Trim(); + if (operands.Length == 0 || operands.Contains("taken", StringComparison.OrdinalIgnoreCase)) + { + return (M68kTimingOperand.None, M68kTimingOperand.None); + } + + var comma = FindTopLevelOperandSeparator(operands); + if (comma >= 0) + { + return ( + M68kTimingOperand.FromLabel(operands[..comma]), + M68kTimingOperand.FromLabel(operands[(comma + 1)..])); + } + + return (M68kTimingOperand.None, M68kTimingOperand.FromLabel(operands)); + } + + private static int FindTopLevelOperandSeparator(string operands) + { + var depth = 0; + for (var i = 0; i < operands.Length; i++) + { + depth += operands[i] switch + { + '(' => 1, + ')' => depth > 0 ? -1 : 0, + _ => 0 + }; + + if (operands[i] == ',' && depth == 0) + { + return i; + } + } + + return -1; + } + + private static M68kOperandSize? ParseSize(string label) + { + var space = label.IndexOf(' '); + var mnemonic = space >= 0 ? label[..space] : label; + if (mnemonic.Contains(".B", StringComparison.Ordinal)) + { + return M68kOperandSize.Byte; + } + + if (mnemonic.Contains(".W", StringComparison.Ordinal)) + { + return M68kOperandSize.Word; + } + + if (mnemonic.Contains(".L", StringComparison.Ordinal)) + { + return M68kOperandSize.Long; + } + + return mnemonic == "MOVEQ" ? M68kOperandSize.Long : null; + } + + private static M68kTimingBranchOutcome ClassifyBranchOutcome(M68kInstructionTimingKey key, string label) + { + return key switch + { + M68kInstructionTimingKey.DbccConditionTrue => M68kTimingBranchOutcome.ConditionTrue, + M68kInstructionTimingKey.DbccExpired => M68kTimingBranchOutcome.Expired, + _ when label.Contains("not taken", StringComparison.OrdinalIgnoreCase) => M68kTimingBranchOutcome.NotTaken, + _ when label.Contains("taken", StringComparison.OrdinalIgnoreCase) => M68kTimingBranchOutcome.Taken, + _ => M68kTimingBranchOutcome.None + }; + } + + private static M68kTimingOperation ClassifyOperation(M68kInstructionTimingKey key, string label) + { + var keyName = key.ToString(); + if (key == M68kInstructionTimingKey.Idle) + { + return M68kTimingOperation.Idle; + } + + if (key == M68kInstructionTimingKey.Nop) + { + return M68kTimingOperation.NoOperation; + } + + if (keyName.Contains("Exception", StringComparison.Ordinal) || + key is M68kInstructionTimingKey.IllegalInstruction or + M68kInstructionTimingKey.PrivilegeViolation or + M68kInstructionTimingKey.FormatError or + M68kInstructionTimingKey.InterruptAcknowledge) + { + return M68kTimingOperation.Exception; + } + + if (keyName.StartsWith("MoveLong", StringComparison.Ordinal) || + keyName.StartsWith("MoveWord", StringComparison.Ordinal) || + keyName.StartsWith("MoveByte", StringComparison.Ordinal)) + { + return label.StartsWith("MOVEA", StringComparison.Ordinal) + ? M68kTimingOperation.MoveAddress + : M68kTimingOperation.Move; + } + + if (key == M68kInstructionTimingKey.Moveq) + { + return M68kTimingOperation.MoveQuick; + } + + if (key == M68kInstructionTimingKey.Movec) + { + return M68kTimingOperation.MoveControl; + } + + if (keyName.StartsWith("Movem", StringComparison.Ordinal)) + { + return M68kTimingOperation.MoveMultiple; + } + + if (keyName.StartsWith("Clr", StringComparison.Ordinal)) return M68kTimingOperation.Clear; + if (keyName.StartsWith("Tst", StringComparison.Ordinal)) return M68kTimingOperation.Test; + if (keyName.StartsWith("Lea", StringComparison.Ordinal)) return M68kTimingOperation.LoadEffectiveAddress; + if (keyName.StartsWith("Adda", StringComparison.Ordinal)) return M68kTimingOperation.AddAddress; + if (keyName.StartsWith("Addx", StringComparison.Ordinal)) return M68kTimingOperation.AddExtended; + if (keyName.StartsWith("Add", StringComparison.Ordinal)) return M68kTimingOperation.Add; + if (keyName.StartsWith("Neg", StringComparison.Ordinal)) return M68kTimingOperation.Subtract; + if (keyName.StartsWith("Suba", StringComparison.Ordinal)) return M68kTimingOperation.SubtractAddress; + if (keyName.StartsWith("Sub", StringComparison.Ordinal)) return M68kTimingOperation.Subtract; + if (keyName.StartsWith("Cmpa", StringComparison.Ordinal)) return M68kTimingOperation.CompareAddress; + if (keyName.StartsWith("Cmp", StringComparison.Ordinal)) return M68kTimingOperation.Compare; + if (keyName.StartsWith("And", StringComparison.Ordinal) || keyName.StartsWith("Eori", StringComparison.Ordinal) || keyName.StartsWith("Ori", StringComparison.Ordinal) || keyName.StartsWith("Not", StringComparison.Ordinal) || keyName.StartsWith("ImmediateLogical", StringComparison.Ordinal) || keyName.StartsWith("ImmediateWordTo", StringComparison.Ordinal)) return M68kTimingOperation.Logical; + if (keyName.StartsWith("Btst", StringComparison.Ordinal) || keyName.StartsWith("Bchg", StringComparison.Ordinal) || keyName.StartsWith("Bclr", StringComparison.Ordinal) || keyName.StartsWith("Bset", StringComparison.Ordinal)) return M68kTimingOperation.Bit; + if (keyName.StartsWith("As", StringComparison.Ordinal) || keyName.StartsWith("Ls", StringComparison.Ordinal) || keyName.StartsWith("Ro", StringComparison.Ordinal)) return M68kTimingOperation.ShiftRotate; + if (keyName.StartsWith("Abcd", StringComparison.Ordinal) || keyName.StartsWith("Sbcd", StringComparison.Ordinal) || keyName.StartsWith("Nbcd", StringComparison.Ordinal)) return M68kTimingOperation.Decimal; + if (keyName.StartsWith("Mulu", StringComparison.Ordinal) || keyName.StartsWith("Muls", StringComparison.Ordinal)) return M68kTimingOperation.Multiply; + if (keyName.StartsWith("Divu", StringComparison.Ordinal) || keyName.StartsWith("Divs", StringComparison.Ordinal)) return M68kTimingOperation.Divide; + if (keyName.StartsWith("Branch", StringComparison.Ordinal) || keyName.StartsWith("Bsr", StringComparison.Ordinal)) return M68kTimingOperation.Branch; + if (keyName.StartsWith("Dbcc", StringComparison.Ordinal)) return M68kTimingOperation.DecrementBranch; + if (keyName.StartsWith("Jmp", StringComparison.Ordinal)) return M68kTimingOperation.Jump; + if (keyName.StartsWith("Jsr", StringComparison.Ordinal)) return M68kTimingOperation.JumpSubroutine; + if (keyName is nameof(M68kInstructionTimingKey.Rte) or nameof(M68kInstructionTimingKey.Rtd) or nameof(M68kInstructionTimingKey.Rts)) return M68kTimingOperation.Return; + if (keyName.StartsWith("Link", StringComparison.Ordinal)) return M68kTimingOperation.Link; + if (keyName.StartsWith("Ext", StringComparison.Ordinal)) return M68kTimingOperation.Extend; + if (keyName.StartsWith("Swap", StringComparison.Ordinal)) return M68kTimingOperation.Swap; + if (keyName.StartsWith("Scc", StringComparison.Ordinal)) return M68kTimingOperation.SetCondition; + + return M68kTimingOperation.Other; + } + } + + internal static class M68kTimingFormula + { + public static M68kInstructionPlan CreatePlan(M68kTimingDescriptor descriptor) + { + if (descriptor.FormulaKind is M68kTimingFormulaKind.OperandShape or M68kTimingFormulaKind.SpecialControl) + { + return CreateOperandShapePlan(descriptor); + } + + return CreateCompatibilityPlan(descriptor); + } + + private static M68kInstructionPlan CreateCompatibilityPlan(M68kTimingDescriptor descriptor) + { + return descriptor.Plan.UsesHeadTail + ? M68kInstructionPlan.CreateHeadTail( + descriptor.LegacyKey, + descriptor.LegacyLabel, + descriptor.Plan.NativeCycles, + descriptor.Plan.HeadCycles, + descriptor.Plan.TailCycles, + descriptor.Barriers) + : M68kInstructionPlan.CreateFlat( + descriptor.LegacyKey, + descriptor.LegacyLabel, + descriptor.Plan.NativeCycles, + descriptor.Barriers); + } + + private static M68kInstructionPlan CreateOperandShapePlan(M68kTimingDescriptor descriptor) + { + var cycles = descriptor.Operation switch + { + M68kTimingOperation.Idle => 2, + M68kTimingOperation.NoOperation => 4, + M68kTimingOperation.Exception => CalculateExceptionCycles(descriptor), + M68kTimingOperation.Move => CalculateMoveCycles(descriptor), + M68kTimingOperation.MoveAddress => CalculateMoveCycles(descriptor), + M68kTimingOperation.MoveQuick => 2, + M68kTimingOperation.MoveControl => 12, + M68kTimingOperation.Clear => CalculateClearCycles(descriptor), + M68kTimingOperation.Test => CalculateTestCycles(descriptor), + M68kTimingOperation.LoadEffectiveAddress => CalculateLeaCycles(descriptor), + M68kTimingOperation.Add => CalculateArithmeticCycles(descriptor), + M68kTimingOperation.AddAddress => CalculateArithmeticCycles(descriptor), + M68kTimingOperation.AddExtended => CalculateArithmeticCycles(descriptor), + M68kTimingOperation.Subtract => CalculateArithmeticCycles(descriptor), + M68kTimingOperation.SubtractAddress => CalculateArithmeticCycles(descriptor), + M68kTimingOperation.Compare => CalculateCompareCycles(descriptor), + M68kTimingOperation.CompareAddress => CalculateCompareCycles(descriptor), + M68kTimingOperation.Logical => CalculateLogicalCycles(descriptor), + M68kTimingOperation.Branch => CalculateBranchCycles(descriptor), + M68kTimingOperation.DecrementBranch => CalculateDecrementBranchCycles(descriptor), + M68kTimingOperation.SetCondition => CalculateSetConditionCycles(descriptor), + M68kTimingOperation.Bit => CalculateBitCycles(descriptor), + M68kTimingOperation.ShiftRotate => CalculateShiftRotateCycles(descriptor), + M68kTimingOperation.Decimal => CalculateDecimalCycles(descriptor), + M68kTimingOperation.Multiply => CalculateMultiplyCycles(descriptor), + M68kTimingOperation.Divide => CalculateDivideCycles(descriptor), + M68kTimingOperation.Jump => CalculateJumpCycles(descriptor), + M68kTimingOperation.JumpSubroutine => 7, + M68kTimingOperation.Return => CalculateReturnCycles(descriptor), + M68kTimingOperation.Link => 16, + M68kTimingOperation.Extend => CalculateExtendCycles(descriptor), + M68kTimingOperation.Swap => 4, + _ => throw Unsupported(descriptor) + }; + + return descriptor.Plan.UsesHeadTail + ? M68kInstructionPlan.CreateHeadTail( + descriptor.LegacyKey, + descriptor.LegacyLabel, + cycles, + descriptor.Plan.HeadCycles, + descriptor.Plan.TailCycles, + descriptor.Barriers) + : M68kInstructionPlan.CreateFlat( + descriptor.LegacyKey, + descriptor.LegacyLabel, + cycles, + descriptor.Barriers); + } + + private static int CalculateMoveCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var source = descriptor.Source.Form; + var destination = descriptor.Destination.Form; + + if (IsRegister(destination)) + { + return CalculateMoveToRegisterCycles(descriptor, source, size); + } + + if (IsMemory(destination)) + { + if (IsRegister(source)) + { + return CalculateRegisterToMemoryMoveCycles(descriptor, destination); + } + + if (source == M68kTimingOperandForm.Immediate) + { + return CalculateImmediateToMemoryMoveCycles(descriptor, destination, size); + } + + if (IsMemory(source)) + { + return CalculateMemoryToMemoryMoveCycles(descriptor, source, destination, size); + } + } + + throw Unsupported(descriptor); + } + + private static int CalculateArithmeticCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var source = descriptor.Source.Form; + var destination = descriptor.Destination.Form; + + if (source == M68kTimingOperandForm.None && destination == M68kTimingOperandForm.DataRegister) + { + return 2; + } + + if (destination is M68kTimingOperandForm.DataRegister or M68kTimingOperandForm.AddressRegister) + { + return CalculateReadToRegisterArithmeticCycles(descriptor, source, size); + } + + if (IsMemory(destination)) + { + if (IsRegister(source)) + { + return CalculateRegisterToMemoryArithmeticCycles(descriptor, destination, size); + } + + if (source == M68kTimingOperandForm.Immediate) + { + return CalculateImmediateToMemoryArithmeticCycles(descriptor, destination, size); + } + } + + throw Unsupported(descriptor); + } + + private static int CalculateCompareCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var source = descriptor.Source.Form; + var destination = descriptor.Destination.Form; + + if (destination is M68kTimingOperandForm.DataRegister or M68kTimingOperandForm.AddressRegister) + { + return CalculateReadToRegisterCompareCycles(descriptor, source, size); + } + + if (source == M68kTimingOperandForm.Immediate && IsMemory(destination)) + { + return CalculateImmediateCompareToMemoryCycles(descriptor, destination, size); + } + + throw Unsupported(descriptor); + } + + private static int CalculateLogicalCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var source = descriptor.Source.Form; + var destination = descriptor.Destination.Form; + + if (source == M68kTimingOperandForm.None && destination == M68kTimingOperandForm.DataRegister) + { + return 2; + } + + if (source == M68kTimingOperandForm.Immediate && + destination is M68kTimingOperandForm.ConditionCodeRegister or M68kTimingOperandForm.StatusRegister) + { + return destination == M68kTimingOperandForm.StatusRegister ? 20 : 8; + } + + if (destination == M68kTimingOperandForm.DataRegister) + { + if (source == M68kTimingOperandForm.Immediate) + { + return CalculateImmediateToRegisterCycles(size); + } + + if (source == M68kTimingOperandForm.DataRegister) + { + return 2; + } + } + + if (source == M68kTimingOperandForm.Immediate && IsMemory(destination)) + { + return CalculateImmediateLogicalToMemoryCycles(descriptor, destination, size); + } + + throw Unsupported(descriptor); + } + + private static int CalculateBranchCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + if (descriptor.LegacyKey.ToString().StartsWith("Bsr", StringComparison.Ordinal)) + { + return size == M68kOperandSize.Long ? 18 : 7; + } + + return descriptor.BranchOutcome switch + { + M68kTimingBranchOutcome.Taken => size == M68kOperandSize.Long ? 10 : 6, + M68kTimingBranchOutcome.NotTaken => size switch + { + M68kOperandSize.Byte => 4, + M68kOperandSize.Word => 6, + M68kOperandSize.Long => 8, + _ => throw Unsupported(descriptor) + }, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateExceptionCycles(M68kTimingDescriptor descriptor) + => descriptor.LegacyKey switch + { + M68kInstructionTimingKey.LineAException => 34, + M68kInstructionTimingKey.LineFException => 34, + M68kInstructionTimingKey.IllegalInstruction => 20, + M68kInstructionTimingKey.PrivilegeViolation => 20, + M68kInstructionTimingKey.FormatError => 20, + M68kInstructionTimingKey.InterruptAcknowledge => 44, + _ => throw Unsupported(descriptor) + }; + + private static int CalculateJumpCycles(M68kTimingDescriptor descriptor) + => descriptor.Destination.Form switch + { + M68kTimingOperandForm.AddressIndirect => 4, + M68kTimingOperandForm.AbsoluteLong => 6, + _ => throw Unsupported(descriptor) + }; + + private static int CalculateReturnCycles(M68kTimingDescriptor descriptor) + => descriptor.LegacyKey switch + { + M68kInstructionTimingKey.Rte => 20, + M68kInstructionTimingKey.Rtd => 16, + M68kInstructionTimingKey.Rts => 7, + _ => throw Unsupported(descriptor) + }; + + private static int CalculateExtendCycles(M68kTimingDescriptor descriptor) + => RequireSize(descriptor) switch + { + M68kOperandSize.Word => 2, + M68kOperandSize.Long => 4, + _ => throw Unsupported(descriptor) + }; + + private static int CalculateDecrementBranchCycles(M68kTimingDescriptor descriptor) + { + return descriptor.BranchOutcome switch + { + M68kTimingBranchOutcome.ConditionTrue => 6, + M68kTimingBranchOutcome.Taken => 10, + M68kTimingBranchOutcome.Expired => 12, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateSetConditionCycles(M68kTimingDescriptor descriptor) + { + return descriptor.Destination.Form switch + { + M68kTimingOperandForm.AbsoluteLong => 10, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateBitCycles(M68kTimingDescriptor descriptor) + { + if (descriptor.Destination.Form == M68kTimingOperandForm.DataRegister) + { + return 4; + } + + return descriptor.Destination.Form switch + { + M68kTimingOperandForm.AbsoluteLong => descriptor.LegacyKey.ToString().StartsWith("Btst", StringComparison.Ordinal) ? 10 : 12, + M68kTimingOperandForm.AddressDisplacement when descriptor.LegacyKey.ToString().StartsWith("Bset", StringComparison.Ordinal) => 10, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateShiftRotateCycles(M68kTimingDescriptor descriptor) + { + if (descriptor.Source.Form != M68kTimingOperandForm.Immediate || + descriptor.Destination.Form != M68kTimingOperandForm.DataRegister) + { + throw Unsupported(descriptor); + } + + return descriptor.LegacyLabel.StartsWith("ASR", StringComparison.Ordinal) || + descriptor.LegacyLabel.StartsWith("LSR", StringComparison.Ordinal) + ? 6 + : 8; + } + + private static int CalculateDecimalCycles(M68kTimingDescriptor descriptor) + { + var source = descriptor.Source.Form; + var destination = descriptor.Destination.Form; + + if (source == M68kTimingOperandForm.DataRegister && + destination == M68kTimingOperandForm.DataRegister) + { + return 6; + } + + if (source == M68kTimingOperandForm.Predecrement && + destination == M68kTimingOperandForm.Predecrement) + { + return 18; + } + + if (source == M68kTimingOperandForm.None) + { + return destination switch + { + M68kTimingOperandForm.DataRegister => 6, + M68kTimingOperandForm.AddressIndirect => 8, + M68kTimingOperandForm.PostIncrement => 8, + M68kTimingOperandForm.Predecrement => 8, + M68kTimingOperandForm.AddressDisplacement => 10, + M68kTimingOperandForm.BriefIndexed => 12, + M68kTimingOperandForm.AbsoluteWord => 10, + M68kTimingOperandForm.AbsoluteLong => 12, + _ => throw Unsupported(descriptor) + }; + } + + throw Unsupported(descriptor); + } + + private static int CalculateMultiplyCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + return size switch + { + M68kOperandSize.Word => 46, + M68kOperandSize.Long => 44, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateDivideCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var signed = descriptor.LegacyKey.ToString().StartsWith("Divs", StringComparison.Ordinal); + return size switch + { + M68kOperandSize.Word => signed ? 58 : 46, + M68kOperandSize.Long => signed ? 82 : 76, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateReadToRegisterArithmeticCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm source, + M68kOperandSize size) + { + if (IsRegister(source)) + { + return 2; + } + + if (source == M68kTimingOperandForm.Immediate) + { + return CalculateImmediateToRegisterCycles(size); + } + + return source switch + { + M68kTimingOperandForm.PostIncrement => 6, + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 7 : 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateReadToRegisterCompareCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm source, + M68kOperandSize size) + { + if (IsRegister(source)) + { + return 2; + } + + if (source == M68kTimingOperandForm.Immediate) + { + return CalculateImmediateToRegisterCycles(size); + } + + return source switch + { + M68kTimingOperandForm.AddressIndirect => 6, + M68kTimingOperandForm.PostIncrement => 6, + M68kTimingOperandForm.AddressDisplacement => 6, + M68kTimingOperandForm.AbsoluteLong => 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateRegisterToMemoryArithmeticCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return destination switch + { + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 13 : 9, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateImmediateToMemoryArithmeticCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return destination switch + { + M68kTimingOperandForm.AddressIndirect => 6, + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 10 : 8, + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 12 : 10, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateImmediateCompareToMemoryCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return destination switch + { + M68kTimingOperandForm.AddressIndirect => 6, + M68kTimingOperandForm.PostIncrement => size == M68kOperandSize.Long ? 10 : 8, + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 9 : 8, + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 10 : 8, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateImmediateLogicalToMemoryCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return destination switch + { + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 12 : 10, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateImmediateToRegisterCycles(M68kOperandSize size) + => size == M68kOperandSize.Long ? 6 : 4; + + private static int CalculateMoveToRegisterCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm source, + M68kOperandSize size) + { + if (IsRegister(source)) + { + return 2; + } + + return source switch + { + M68kTimingOperandForm.Immediate => size == M68kOperandSize.Long ? 6 : 4, + M68kTimingOperandForm.AddressIndirect => 6, + M68kTimingOperandForm.PostIncrement => 6, + M68kTimingOperandForm.AddressDisplacement => 6, + M68kTimingOperandForm.BriefIndexed => 8, + M68kTimingOperandForm.AbsoluteWord => 6, + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 8 : 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateRegisterToMemoryMoveCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination) + { + return destination switch + { + M68kTimingOperandForm.AddressIndirect => 4, + M68kTimingOperandForm.PostIncrement => 4, + M68kTimingOperandForm.Predecrement => 4, + M68kTimingOperandForm.AddressDisplacement => 6, + M68kTimingOperandForm.BriefIndexed => 8, + M68kTimingOperandForm.AbsoluteLong => 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateImmediateToMemoryMoveCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return destination switch + { + M68kTimingOperandForm.AddressIndirect => size == M68kOperandSize.Long ? 8 : 6, + M68kTimingOperandForm.PostIncrement => size == M68kOperandSize.Long ? 8 : 6, + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 10 : 6, + M68kTimingOperandForm.BriefIndexed => 8, + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 10 : 8, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateMemoryToMemoryMoveCycles( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm source, + M68kTimingOperandForm destination, + M68kOperandSize size) + { + return (source, destination, size) switch + { + (M68kTimingOperandForm.AddressIndirect, M68kTimingOperandForm.AddressIndirect, M68kOperandSize.Long) => 8, + (M68kTimingOperandForm.AddressDisplacement, M68kTimingOperandForm.PostIncrement, M68kOperandSize.Long) => 10, + (M68kTimingOperandForm.AbsoluteWord, M68kTimingOperandForm.AddressDisplacement, M68kOperandSize.Long) => 8, + (M68kTimingOperandForm.AbsoluteLong, M68kTimingOperandForm.AddressDisplacement, M68kOperandSize.Long) => 10, + (M68kTimingOperandForm.AbsoluteLong, M68kTimingOperandForm.AddressDisplacement, M68kOperandSize.Word) => 8, + (M68kTimingOperandForm.BriefIndexed, M68kTimingOperandForm.Predecrement, M68kOperandSize.Byte) => 10, + (M68kTimingOperandForm.PostIncrement, M68kTimingOperandForm.PostIncrement, M68kOperandSize.Byte) => 8, + (M68kTimingOperandForm.AddressIndirect, M68kTimingOperandForm.AbsoluteLong, M68kOperandSize.Byte) => 8, + (M68kTimingOperandForm.AbsoluteLong, M68kTimingOperandForm.AbsoluteLong, M68kOperandSize.Byte) => 10, + (M68kTimingOperandForm.AbsoluteLong, M68kTimingOperandForm.AbsoluteLong, M68kOperandSize.Word) => 10, + _ => CalculateMemorySourceContribution(descriptor, source, size) + CalculateMemoryDestinationContribution(descriptor, destination) + }; + } + + private static int CalculateMemorySourceContribution( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm source, + M68kOperandSize size) + { + return source switch + { + M68kTimingOperandForm.AddressIndirect => 4, + M68kTimingOperandForm.PostIncrement => 4, + M68kTimingOperandForm.AddressDisplacement => 6, + M68kTimingOperandForm.BriefIndexed => 6, + M68kTimingOperandForm.AbsoluteWord => size == M68kOperandSize.Long ? 4 : 6, + M68kTimingOperandForm.AbsoluteLong => 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateMemoryDestinationContribution( + M68kTimingDescriptor descriptor, + M68kTimingOperandForm destination) + { + return destination switch + { + M68kTimingOperandForm.AddressIndirect => 4, + M68kTimingOperandForm.PostIncrement => 4, + M68kTimingOperandForm.Predecrement => 4, + M68kTimingOperandForm.AddressDisplacement => 4, + M68kTimingOperandForm.AbsoluteLong => 4, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateClearCycles(M68kTimingDescriptor descriptor) + { + var size = RequireSize(descriptor); + var destination = descriptor.Destination.Form; + if (IsRegister(destination)) + { + return 2; + } + + return destination switch + { + M68kTimingOperandForm.AddressIndirect => size == M68kOperandSize.Long ? 6 : 4, + M68kTimingOperandForm.PostIncrement => size == M68kOperandSize.Long ? 6 : 4, + M68kTimingOperandForm.Predecrement => size == M68kOperandSize.Long ? 6 : 4, + M68kTimingOperandForm.AddressDisplacement => size == M68kOperandSize.Long ? 8 : 6, + M68kTimingOperandForm.AbsoluteLong => size == M68kOperandSize.Long ? 8 : 6, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateTestCycles(M68kTimingDescriptor descriptor) + { + return descriptor.Destination.Form switch + { + M68kTimingOperandForm.DataRegister => 2, + _ => throw Unsupported(descriptor) + }; + } + + private static int CalculateLeaCycles(M68kTimingDescriptor descriptor) + { + return descriptor.Source.Form switch + { + M68kTimingOperandForm.AddressDisplacement => 4, + M68kTimingOperandForm.AbsoluteWord => 4, + M68kTimingOperandForm.AbsoluteLong => 6, + M68kTimingOperandForm.BriefIndexed => 8, + _ => throw Unsupported(descriptor) + }; + } + + private static M68kOperandSize RequireSize(M68kTimingDescriptor descriptor) + { + if (descriptor.Size is M68kOperandSize size) + { + return size; + } + + throw Unsupported(descriptor); + } + + private static UnsupportedM68kTimingException Unsupported(M68kTimingDescriptor descriptor) + => new( + descriptor.LegacyKey, + descriptor.Plan.UsesHeadTail ? M68kAcceleratorModel.M68030 : M68kAcceleratorModel.M68020); + + private static bool IsRegister(M68kTimingOperandForm form) + => form is M68kTimingOperandForm.DataRegister or M68kTimingOperandForm.AddressRegister; + + private static bool IsMemory(M68kTimingOperandForm form) + => form is M68kTimingOperandForm.AddressIndirect or + M68kTimingOperandForm.PostIncrement or + M68kTimingOperandForm.Predecrement or + M68kTimingOperandForm.AddressDisplacement or + M68kTimingOperandForm.BriefIndexed or + M68kTimingOperandForm.AbsoluteWord or + M68kTimingOperandForm.AbsoluteLong; + + public static M68kInstructionPlan CreateFixedPlan(M68kInstructionTimingKey key, int nativeCycles) + { + var descriptor = new M68kTimingDescriptor( + M68kTimingFormulaKind.Compatibility, + M68kTimingOperation.Other, + null, + M68kTimingOperand.None, + M68kTimingOperand.None, + M68kTimingBranchOutcome.None, + RegisterCount: 0, + M68kTimingBarrier.None, + key, + "fixed JIT fallback", + new M68kTimingPlanShape(nativeCycles, HeadCycles: 0, TailCycles: 0, UsesHeadTail: false)); + return CreatePlan(descriptor); + } + + public static M68kInstructionPlan CreateMovemLongPlan( + M68kInstructionTimingKey key, + string label, + int registerCount, + bool registerToMemory, + M68kAcceleratorModel model, + int? fixedCycles) + => CreatePlan(M68kTimingDescriptor.ForMovemLong( + key, + label, + registerCount, + registerToMemory, + model, + fixedCycles)); + } +} diff --git a/CopperMod.Amiga.Tests/M68kTimingFormulaTests.cs b/CopperMod.Amiga.Tests/M68kTimingFormulaTests.cs new file mode 100644 index 0000000..698a033 --- /dev/null +++ b/CopperMod.Amiga.Tests/M68kTimingFormulaTests.cs @@ -0,0 +1,308 @@ +using Copper68k; + +namespace CopperMod.Amiga.Tests; + +public sealed class M68kTimingFormulaTests +{ + [Fact] + public void M68020DescriptorsRoundTripCompatibilityPlans() + { + foreach (var key in Enum.GetValues()) + { + if (IsDynamicTimingKey(key)) + { + continue; + } + + var descriptor = M68020TimingModel.GetDescriptor(key); + var formulaPlan = M68kTimingFormula.CreatePlan(descriptor); + var expectedPlan = M68kTimingDescriptor.UsesSpecialControlFormula(key) + ? CreateExpectedSpecialControlPlan(key, useHeadTail: false) + : formulaPlan; + + Assert.Equal(expectedPlan, formulaPlan); + AssertDescriptorMatchesPlanShape(descriptor, expectedPlan); + Assert.False(descriptor.Plan.UsesHeadTail); + Assert.Equal( + ExpectedFormulaKind(key), + descriptor.FormulaKind); + } + } + + [Fact] + public void M68030DescriptorsRoundTripCompatibilityPlans() + { + foreach (var key in Enum.GetValues()) + { + if (IsDynamicTimingKey(key)) + { + continue; + } + + var descriptor = M68030TimingModel.GetDescriptor(key); + var formulaPlan = M68kTimingFormula.CreatePlan(descriptor); + var expectedPlan = M68kTimingDescriptor.UsesSpecialControlFormula(key) + ? CreateExpectedSpecialControlPlan(key, useHeadTail: true) + : formulaPlan; + + Assert.Equal(expectedPlan, formulaPlan); + AssertDescriptorMatchesPlanShape(descriptor, expectedPlan); + Assert.Equal( + ExpectedFormulaKind(key), + descriptor.FormulaKind); + } + } + + [Fact] + public void OperandShapeFamiliesComputeCyclesWithoutTheCompatibilitySwitch() + { + foreach (var key in Enum.GetValues()) + { + if (!IsOperandShapeFormulaKey(key)) + { + continue; + } + + Assert.Throws(() => M68020TimingModel.GetCompatibilityPlan(key)); + Assert.Throws(() => M68030TimingModel.GetCompatibilityPlan(key)); + + var m68020Descriptor = M68020TimingModel.GetDescriptor(key); + var m68030Descriptor = M68030TimingModel.GetDescriptor(key); + var m68020Plan = M68kTimingFormula.CreatePlan(m68020Descriptor); + var m68030Plan = M68kTimingFormula.CreatePlan(m68030Descriptor); + + Assert.Equal(M68kTimingFormulaKind.OperandShape, m68020Descriptor.FormulaKind); + Assert.Equal(M68kTimingFormulaKind.OperandShape, m68030Descriptor.FormulaKind); + Assert.Equal(0, m68020Descriptor.Plan.NativeCycles); + Assert.Equal(0, m68030Descriptor.Plan.NativeCycles); + Assert.False(m68020Descriptor.Plan.UsesHeadTail); + Assert.True(m68030Descriptor.Plan.UsesHeadTail); + Assert.Equal(ExpectedOperandShapeHeadTail(key), m68030Descriptor.Plan.HeadCycles); + Assert.Equal(ExpectedOperandShapeHeadTail(key), m68030Descriptor.Plan.TailCycles); + Assert.NotEqual(0, m68020Plan.NativeCycles); + Assert.Equal(m68020Plan.NativeCycles, m68030Plan.NativeCycles); + Assert.Equal(m68020Plan.Name, m68030Plan.Name); + Assert.Equal(m68020Plan.Barriers, m68030Plan.Barriers); + } + } + + [Fact] + public void SpecialControlFamiliesComputeCyclesWithoutTheCompatibilitySwitch() + { + foreach (var key in Enum.GetValues()) + { + if (!M68kTimingDescriptor.UsesSpecialControlFormula(key)) + { + continue; + } + + Assert.Throws(() => M68020TimingModel.GetCompatibilityPlan(key)); + Assert.Throws(() => M68030TimingModel.GetCompatibilityPlan(key)); + + var m68020Descriptor = M68020TimingModel.GetDescriptor(key); + var m68030Descriptor = M68030TimingModel.GetDescriptor(key); + + Assert.Equal(M68kTimingFormulaKind.SpecialControl, m68020Descriptor.FormulaKind); + Assert.Equal(M68kTimingFormulaKind.SpecialControl, m68030Descriptor.FormulaKind); + Assert.Equal(0, m68020Descriptor.Plan.NativeCycles); + Assert.Equal(0, m68030Descriptor.Plan.NativeCycles); + Assert.Equal(CreateExpectedSpecialControlPlan(key, useHeadTail: false), M68kTimingFormula.CreatePlan(m68020Descriptor)); + Assert.Equal(CreateExpectedSpecialControlPlan(key, useHeadTail: true), M68kTimingFormula.CreatePlan(m68030Descriptor)); + } + } + + [Theory] + [InlineData((int)M68kAcceleratorModel.M68020, true, 3, 17, false, 0, 0)] + [InlineData((int)M68kAcceleratorModel.M68020, false, 3, 24, false, 0, 0)] + [InlineData((int)M68kAcceleratorModel.M68030, true, 3, 14, true, 2, 0)] + [InlineData((int)M68kAcceleratorModel.M68030, false, 3, 24, true, 2, 0)] + [InlineData((int)M68kAcceleratorModel.M68040, true, 3, 17, false, 0, 0)] + public void MovemLongFormulaPreservesRegisterCountTiming( + int modelValue, + bool registerToMemory, + int registerCount, + int expectedCycles, + bool expectedHeadTail, + int expectedHeadCycles, + int expectedTailCycles) + { + var plan = M68kTimingFormula.CreateMovemLongPlan( + M68kInstructionTimingKey.MovemLongRegistersToPredecrement, + "MOVEM.L registers,-(An)", + registerCount, + registerToMemory, + (M68kAcceleratorModel)modelValue, + fixedCycles: null); + + Assert.Equal(expectedCycles, plan.NativeCycles); + Assert.Equal(expectedHeadTail, plan.UsesHeadTail); + Assert.Equal(expectedHeadCycles, plan.HeadCycles); + Assert.Equal(expectedTailCycles, plan.TailCycles); + } + + [Fact] + public void MovemLongFormulaUsesFixedCyclesForFixedTimingProfiles() + { + var plan = M68kTimingFormula.CreateMovemLongPlan( + M68kInstructionTimingKey.MovemLongRegistersToPredecrement, + "MOVEM.L registers,-(An)", + registerCount: 16, + registerToMemory: true, + M68kAcceleratorModel.M68040, + fixedCycles: 1); + + Assert.Equal(1, plan.NativeCycles); + Assert.False(plan.UsesHeadTail); + Assert.Equal(0, plan.HeadCycles); + Assert.Equal(0, plan.TailCycles); + } + + [Fact] + public void MovemLongFormulaKeepsM68030HeadTailShapeWithFixedCycles() + { + var plan = M68kTimingFormula.CreateMovemLongPlan( + M68kInstructionTimingKey.MovemLongRegistersToPredecrement, + "MOVEM.L registers,-(An)", + registerCount: 16, + registerToMemory: true, + M68kAcceleratorModel.M68030, + fixedCycles: 1); + + Assert.Equal(1, plan.NativeCycles); + Assert.True(plan.UsesHeadTail); + Assert.Equal(2, plan.HeadCycles); + Assert.Equal(0, plan.TailCycles); + } + + private static bool IsDynamicTimingKey(M68kInstructionTimingKey key) + => key is M68kInstructionTimingKey.MovemLongRegistersToPredecrement or + M68kInstructionTimingKey.MovemLongPostIncrementToRegisters; + + private static bool IsOperandShapeFormulaKey(M68kInstructionTimingKey key) + { + var keyName = key.ToString(); + return key is M68kInstructionTimingKey.Moveq or M68kInstructionTimingKey.TstWordData || + keyName.StartsWith("MoveLong", StringComparison.Ordinal) || + keyName.StartsWith("MoveWord", StringComparison.Ordinal) || + keyName.StartsWith("MoveByte", StringComparison.Ordinal) || + keyName.StartsWith("Lea", StringComparison.Ordinal) || + keyName.StartsWith("Clr", StringComparison.Ordinal) || + keyName.StartsWith("ImmediateWordTo", StringComparison.Ordinal) || + keyName.StartsWith("ImmediateLogical", StringComparison.Ordinal) || + keyName.StartsWith("Add", StringComparison.Ordinal) || + keyName.StartsWith("Sub", StringComparison.Ordinal) || + keyName.StartsWith("Neg", StringComparison.Ordinal) || + keyName.StartsWith("Not", StringComparison.Ordinal) || + keyName.StartsWith("Ori", StringComparison.Ordinal) || + keyName.StartsWith("And", StringComparison.Ordinal) || + keyName.StartsWith("Eori", StringComparison.Ordinal) || + keyName.StartsWith("Cmpi", StringComparison.Ordinal) || + keyName.StartsWith("Cmpa", StringComparison.Ordinal) || + keyName.StartsWith("Cmp", StringComparison.Ordinal) || + keyName.StartsWith("Branch", StringComparison.Ordinal) || + keyName.StartsWith("Bsr", StringComparison.Ordinal) || + keyName.StartsWith("Dbcc", StringComparison.Ordinal) || + keyName.StartsWith("Scc", StringComparison.Ordinal) || + keyName.StartsWith("Btst", StringComparison.Ordinal) || + keyName.StartsWith("Bchg", StringComparison.Ordinal) || + keyName.StartsWith("Bclr", StringComparison.Ordinal) || + keyName.StartsWith("Bset", StringComparison.Ordinal) || + keyName.StartsWith("Asr", StringComparison.Ordinal) || + keyName.StartsWith("Asl", StringComparison.Ordinal) || + keyName.StartsWith("Lsr", StringComparison.Ordinal) || + keyName.StartsWith("Lsl", StringComparison.Ordinal) || + keyName.StartsWith("Ror", StringComparison.Ordinal) || + keyName.StartsWith("Rol", StringComparison.Ordinal) || + keyName.StartsWith("Roxr", StringComparison.Ordinal) || + keyName.StartsWith("Roxl", StringComparison.Ordinal) || + keyName.StartsWith("Abcd", StringComparison.Ordinal) || + keyName.StartsWith("Sbcd", StringComparison.Ordinal) || + keyName.StartsWith("Nbcd", StringComparison.Ordinal) || + keyName.StartsWith("Mulu", StringComparison.Ordinal) || + keyName.StartsWith("Muls", StringComparison.Ordinal) || + keyName.StartsWith("Divu", StringComparison.Ordinal) || + keyName.StartsWith("Divs", StringComparison.Ordinal); + } + + private static M68kTimingFormulaKind ExpectedFormulaKind(M68kInstructionTimingKey key) + { + if (M68kTimingDescriptor.UsesSpecialControlFormula(key)) + { + return M68kTimingFormulaKind.SpecialControl; + } + + return IsOperandShapeFormulaKey(key) + ? M68kTimingFormulaKind.OperandShape + : throw new ArgumentOutOfRangeException(nameof(key), key, null); + } + + private static int ExpectedOperandShapeHeadTail(M68kInstructionTimingKey key) + => key is M68kInstructionTimingKey.ImmediateWordToStatusRegister or + M68kInstructionTimingKey.BranchByteTaken or + M68kInstructionTimingKey.BsrByte or + M68kInstructionTimingKey.BranchWordTaken or + M68kInstructionTimingKey.BsrWord or + M68kInstructionTimingKey.DbccBranchTaken or + M68kInstructionTimingKey.BranchLongTaken or + M68kInstructionTimingKey.BsrLong + ? 0 + : 1; + + private static M68kInstructionPlan CreateExpectedSpecialControlPlan(M68kInstructionTimingKey key, bool useHeadTail) + { + var (label, cycles, barriers) = key switch + { + M68kInstructionTimingKey.Idle => ("IDLE", 2, M68kTimingBarrier.SynchronizeBus), + M68kInstructionTimingKey.Nop => ("NOP", 4, M68kTimingBarrier.SynchronizeBus), + M68kInstructionTimingKey.LineAException => ("LINEA", 34, ExceptionBarrier()), + M68kInstructionTimingKey.LineFException => ("LINEF", 34, ExceptionBarrier()), + M68kInstructionTimingKey.IllegalInstruction => ("ILLEGAL", 20, ExceptionBarrier()), + M68kInstructionTimingKey.PrivilegeViolation => ("PRIVILEGE", 20, ExceptionBarrier()), + M68kInstructionTimingKey.FormatError => ("FORMAT", 20, ExceptionBarrier()), + M68kInstructionTimingKey.InterruptAcknowledge => ("INTERRUPT", 44, ExceptionBarrier()), + M68kInstructionTimingKey.Movec => ("MOVEC", 12, useHeadTail + ? M68kTimingBarrier.CacheControl | M68kTimingBarrier.SynchronizeBus + : M68kTimingBarrier.CacheControl), + M68kInstructionTimingKey.Rte => ("RTE", 20, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus), + M68kInstructionTimingKey.Rtd => ("RTD", 16, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), + M68kInstructionTimingKey.Rts => ("RTS", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), + M68kInstructionTimingKey.LinkLong => ("LINK.L", 16, M68kTimingBarrier.None), + M68kInstructionTimingKey.ExtbLong => ("EXTB.L", 4, M68kTimingBarrier.None), + M68kInstructionTimingKey.ExtWordData => ("EXT.W Dn", 2, M68kTimingBarrier.None), + M68kInstructionTimingKey.SwapData => ("SWAP Dn", 4, M68kTimingBarrier.None), + M68kInstructionTimingKey.JsrAbsoluteLong => ("JSR (xxx).L", 7, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), + M68kInstructionTimingKey.JmpAddressIndirect => ("JMP (An)", 4, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), + M68kInstructionTimingKey.JmpAbsoluteLong => ("JMP (xxx).L", 6, M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.Branch), + _ => throw new ArgumentOutOfRangeException(nameof(key), key, null) + }; + + if (!useHeadTail) + { + return M68kInstructionPlan.CreateFlat(key, label, cycles, barriers); + } + + var (headCycles, tailCycles) = key switch + { + M68kInstructionTimingKey.LinkLong => (2, 2), + M68kInstructionTimingKey.ExtbLong => (2, 2), + M68kInstructionTimingKey.ExtWordData => (1, 1), + M68kInstructionTimingKey.SwapData => (1, 1), + _ => (0, 0) + }; + return M68kInstructionPlan.CreateHeadTail(key, label, cycles, headCycles, tailCycles, barriers); + } + + private static M68kTimingBarrier ExceptionBarrier() + => M68kTimingBarrier.Exception | M68kTimingBarrier.FlushPipeline | M68kTimingBarrier.SynchronizeBus; + + private static void AssertDescriptorMatchesPlanShape(M68kTimingDescriptor descriptor, M68kInstructionPlan plan) + { + Assert.Equal(plan.Key, descriptor.LegacyKey); + Assert.Equal(plan.Name, descriptor.LegacyLabel); + Assert.Equal(plan.Barriers, descriptor.Barriers); + Assert.Equal(plan.HeadCycles, descriptor.Plan.HeadCycles); + Assert.Equal(plan.TailCycles, descriptor.Plan.TailCycles); + Assert.Equal(plan.UsesHeadTail, descriptor.Plan.UsesHeadTail); + Assert.NotEqual(M68kTimingFormulaKind.Compatibility, descriptor.FormulaKind); + } +}