From e8582f66a8e82cc6649e477fbd8cd915042ddb91 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:29:31 +1000 Subject: [PATCH 01/21] Refactor instruction logging into own class. This makes it easier to share the instruction log building this once we start to break other parts of CPU emulation into own classes Split out Program Counter into own class with helper methods to read bytes from memory using PC and setting/moving PC --- Kmse.Console/EmulatorService.cs | 2 +- Kmse.Console/Logging/SerilogCpuLogger.cs | 1 + Kmse.Console/Program.cs | 1 + .../Z80CpuTests/CpuExecutionFixture.cs | 13 +- .../Z80CpuTests/CpuInstructionsFixture.cs | 9 +- .../Z80InstructionLoggerFixture.cs | 94 +++++++ .../Z80CpuTests/Z80ProgramCounterFixture.cs | 105 +++++++ Kmse.Core/Infrastructure/EmulatorModule.cs | 2 + Kmse.Core/MasterSystemMk2.cs | 1 + Kmse.Core/Z80/{ => Logging}/ICpuLogger.cs | 2 +- .../Z80/Logging/IZ80InstructionLogger.cs | 10 + Kmse.Core/Z80/Logging/Z80InstructionLogger.cs | 79 ++++++ Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs | 31 ++ Kmse.Core/Z80/Registers/Z80ProgramCounter.cs | 92 ++++++ Kmse.Core/Z80/Support/CpuStatus.cs | 2 +- Kmse.Core/Z80/Z80Cpu.CoreOperations.cs | 43 +-- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 265 +++++++++--------- Kmse.Core/Z80/Z80Cpu.cs | 71 +++-- 18 files changed, 607 insertions(+), 216 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/Z80InstructionLoggerFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs rename Kmse.Core/Z80/{ => Logging}/ICpuLogger.cs (85%) create mode 100644 Kmse.Core/Z80/Logging/IZ80InstructionLogger.cs create mode 100644 Kmse.Core/Z80/Logging/Z80InstructionLogger.cs create mode 100644 Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs create mode 100644 Kmse.Core/Z80/Registers/Z80ProgramCounter.cs diff --git a/Kmse.Console/EmulatorService.cs b/Kmse.Console/EmulatorService.cs index 02d5084..3718d05 100644 --- a/Kmse.Console/EmulatorService.cs +++ b/Kmse.Console/EmulatorService.cs @@ -217,7 +217,7 @@ private string GetRegistersAsString(CpuStatus status) data += $"HL: {status.Hl.Word:X4} "; data += $"IX: {status.Ix.Word:X4} "; data += $"IY: {status.Iy.Word:X4} "; - data += $"PC: {status.Pc.Word:X4} "; + data += $"PC: {status.Pc:X4} "; data += $"SP: {status.StackPointer.Word:X4} "; data += $"I: {status.IRegister:X2} "; data += $"R: {status.RRegister:X2} "; diff --git a/Kmse.Console/Logging/SerilogCpuLogger.cs b/Kmse.Console/Logging/SerilogCpuLogger.cs index ff583f0..6b6392b 100644 --- a/Kmse.Console/Logging/SerilogCpuLogger.cs +++ b/Kmse.Console/Logging/SerilogCpuLogger.cs @@ -1,4 +1,5 @@ using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; using Serilog; namespace Kmse.Console.Logging; diff --git a/Kmse.Console/Program.cs b/Kmse.Console/Program.cs index ae9b4e2..bc8b598 100644 --- a/Kmse.Console/Program.cs +++ b/Kmse.Console/Program.cs @@ -8,6 +8,7 @@ using Kmse.Core.IO.Logging; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index d4b76d7..42b9701 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -2,6 +2,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; using NSubstitute; using NUnit.Framework; @@ -16,7 +17,7 @@ public void Setup() _cpuLogger = Substitute.For(); _memory = Substitute.For(); _io = Substitute.For(); - _cpu = new Z80Cpu(_cpuLogger); + _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); _cpu.Initialize(_memory, _io); } @@ -43,7 +44,7 @@ public void WhenResettingCpu() var status = _cpu.GetStatus(); status.CurrentCycleCount.Should().Be(0); - status.Pc.Word.Should().Be(0); + status.Pc.Should().Be(0); status.Halted.Should().BeFalse(); status.InterruptFlipFlop1.Should().BeFalse(); status.InterruptFlipFlop2.Should().BeFalse(); @@ -73,7 +74,7 @@ public void WhenExecutingSimpleInstruction() var status = _cpu.GetStatus(); status.Halted.Should().Be(true); - status.Pc.Word.Should().Be(0x01); + status.Pc.Should().Be(0x01); } [Test] @@ -88,7 +89,7 @@ public void WhenExecutingDoubleByteInstruction() var status = _cpu.GetStatus(); status.InterruptMode.Should().Be(2); - status.Pc.Word.Should().Be(0x02); + status.Pc.Should().Be(0x02); } [Test] @@ -123,7 +124,7 @@ public void WhenExecutingAndNmiSet() status.Halted.Should().Be(false); status.InterruptFlipFlop1.Should().BeFalse(); status.InterruptFlipFlop2.Should().BeTrue(); - status.Pc.Word.Should().Be(0x66); + status.Pc.Should().Be(0x66); // Pc put onto stack status.StackPointer.Word.Should().Be(0xDFEE); @@ -175,7 +176,7 @@ public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) // Interrupt is not cleared in these modes until a RETI or DI is called _io.DidNotReceive().ClearMaskableInterrupt(); - status.Pc.Word.Should().Be(0x38); + status.Pc.Should().Be(0x38); // Pc put onto stack status.StackPointer.Word.Should().Be(0xDFEE); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index 0fc8116..f5b0c57 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs @@ -2,6 +2,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Support; using NSubstitute; using NSubstitute.ClearExtensions; @@ -18,7 +19,7 @@ public void Setup() _cpuLogger = Substitute.For(); _memory = Substitute.For(); _io = Substitute.For(); - _cpu = new Z80Cpu(_cpuLogger); + _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); _cpu.Initialize(_memory, _io); } @@ -69,7 +70,7 @@ public void WhenExecutingRelativeJump(byte offset, ushort expectedPc) _cpu.ExecuteNextCycle().Should().Be(12); var status = _cpu.GetStatus(); - status.Pc.Word.Should().Be(expectedPc); + status.Pc.Should().Be(expectedPc); } [Test] @@ -96,7 +97,7 @@ public void WhenExecutingAddWithCarryWithNoCarry() _cpu.ExecuteNextCycle().Should().Be(4); var status = _cpu.GetStatus(); - status.Pc.Word.Should().Be(0x03); + status.Pc.Should().Be(0x03); var expectedValue = (byte)(i + i); var result = originalValue + valueToAdd; @@ -151,7 +152,7 @@ public void WhenExecutingAddWithCarryWithCarry() _cpu.ExecuteNextCycle().Should().Be(4); var status = _cpu.GetStatus(); - status.Pc.Word.Should().Be(0x04); + status.Pc.Should().Be(0x04); var expectedValue = (byte)(originalValue + valueToAdd); var result = originalValue + valueToAdd; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80InstructionLoggerFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80InstructionLoggerFixture.cs new file mode 100644 index 0000000..96baeb0 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80InstructionLoggerFixture.cs @@ -0,0 +1,94 @@ +using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests; + +public class Z80InstructionLoggerFixture +{ + private const byte TestInstructionAddress = 0x03; + private const string OpCode = "03"; + private const string Name = "Test instruction"; + private const string Description = "Test instruction for testing"; + private ICpuLogger _cpuLogger; + private Z80InstructionLogger _instructionLogger; + + + [SetUp] + public void Setup() + { + _cpuLogger = Substitute.For(); + _instructionLogger = new Z80InstructionLogger(_cpuLogger); + } + + [Test] + public void WhenStartingANewInstructionAllDataIsClearedExceptPc() + { + _instructionLogger.StartNewInstruction(0x05); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(0x05, string.Empty, string.Empty, string.Empty, string.Empty); + } + + private void AddSimpleInstruction() + { + _instructionLogger.StartNewInstruction(TestInstructionAddress); + _instructionLogger.SetOpCode(OpCode, Name, Description); + } + + [Test] + public void WhenSettingOpCodeItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, string.Empty); + } + + [Test] + public void WhenAddingByteDataItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.AddOperationData(0x4B); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, "4B"); + } + + [Test] + public void WhenAddingUshortDataItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.AddOperationData(0x1234); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, "1234"); + } + + [Test] + public void WhenAddingMultipleByteDataItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.AddOperationData(0x4B); + _instructionLogger.AddOperationData(0x7A); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, "4B7A"); + } + + [Test] + public void WhenAddingMultipleUshortDataItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.AddOperationData(0x1234); + _instructionLogger.AddOperationData(0x4321); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, "12344321"); + } + + [Test] + public void WhenAddingByteAndUshortDataItIsIncludedInLog() + { + AddSimpleInstruction(); + _instructionLogger.AddOperationData(0x12); + _instructionLogger.AddOperationData(0x3456); + _instructionLogger.Log(); + _cpuLogger.Received(1).LogInstruction(TestInstructionAddress, OpCode, Name, Description, "123456"); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs new file mode 100644 index 0000000..7aab98a --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs @@ -0,0 +1,105 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests; + +public class Z80ProgramCounterFixture +{ + private IZ80InstructionLogger _instructionLogger; + private Z80ProgramCounter _programCounter; + private IMasterSystemMemory _memory; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _instructionLogger = Substitute.For(); + _programCounter = new Z80ProgramCounter(_memory, _instructionLogger); + } + + [Test] + public void WhenResetThenValueIsZero() + { + _programCounter.Reset(); + _programCounter.GetValue().Should().Be(0); + _programCounter.AsRegister().Word.Should().Be(0); + } + + [Test] + public void WhenSettingProgramCounterThenValueIsUpdated() + { + _programCounter.SetProgramCounter(0x1234); + _programCounter.GetValue().Should().Be(0x1234); + _programCounter.AsRegister().Word.Should().Be(0x1234); + } + + [Test] + public void WhenProgramCounterMovedForwardOffsetWhichOverflowsThenWrapsAround() + { + _programCounter.SetProgramCounter(ushort.MaxValue); + _programCounter.MoveProgramCounterForward(3); + _programCounter.GetValue().Should().Be(0x02); + _programCounter.AsRegister().Word.Should().Be(0x02); + } + + [Test] + public void WhenProgramCounterMovedForwardByOffsetThenValueIsUpdated() + { + _programCounter.SetProgramCounter(0x1234); + _programCounter.MoveProgramCounterForward(10); + _programCounter.GetValue().Should().Be(0x123E); + _programCounter.AsRegister().Word.Should().Be(0x123E); + } + + [Test] + public void WhenProgramCounterMovedBackwardByOffsetThenValueIsUpdated() + { + _programCounter.SetProgramCounter(0x1234); + _programCounter.MoveProgramCounterBackward(10); + _programCounter.GetValue().Should().Be(0x122A); + _programCounter.AsRegister().Word.Should().Be(0x122A); + } + + [Test] + public void WhenProgramCounterMovedBackwardByOffsetWhichOverflowsThenWrapsAround() + { + _programCounter.SetProgramCounter(0); + _programCounter.MoveProgramCounterBackward(3); + _programCounter.GetValue().Should().Be(ushort.MaxValue-2); + _programCounter.AsRegister().Word.Should().Be(ushort.MaxValue - 2); + } + + [Test] + public void WhenGettingNextDataByteFromMemoryUsingPcValueTheCorrectDataIsReturnedAndPcIncremented() + { + _memory[0x05].Returns((byte)0x12); + _programCounter.SetProgramCounter(0x05); + var data = _programCounter.GetNextDataByte(); + data.Should().Be(0x12); + + _programCounter.GetValue().Should().Be(0x06); + _programCounter.AsRegister().Word.Should().Be(0x06); + + _instructionLogger.Received(1).AddOperationData(0x12); + } + + [Test] + public void WhenGettingNextTwoByteDataFromMemoryUsingPcValueTheCorrectDataIsReturnedAndPcIncrementedByTwo() + { + _memory[0x05].Returns((byte)0x12); + _memory[0x06].Returns((byte)0x34); + _programCounter.SetProgramCounter(0x05); + var data = _programCounter.GetNextTwoDataBytes(); + // Loading byte order is low and then high byte hence swapped order + data.Should().Be(0x3412); + + _programCounter.GetValue().Should().Be(0x07); + _programCounter.AsRegister().Word.Should().Be(0x07); + + _instructionLogger.Received(1).AddOperationData(0x3412); + } +} \ No newline at end of file diff --git a/Kmse.Core/Infrastructure/EmulatorModule.cs b/Kmse.Core/Infrastructure/EmulatorModule.cs index 7aca787..a5d5969 100644 --- a/Kmse.Core/Infrastructure/EmulatorModule.cs +++ b/Kmse.Core/Infrastructure/EmulatorModule.cs @@ -4,6 +4,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; namespace Kmse.Core.Infrastructure; @@ -15,6 +16,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterModule(); builder.RegisterType().As(); } diff --git a/Kmse.Core/MasterSystemMk2.cs b/Kmse.Core/MasterSystemMk2.cs index adc6248..ea60ce0 100644 --- a/Kmse.Core/MasterSystemMk2.cs +++ b/Kmse.Core/MasterSystemMk2.cs @@ -7,6 +7,7 @@ using Kmse.Core.IO.Vdp; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Support; namespace Kmse.Core; diff --git a/Kmse.Core/Z80/ICpuLogger.cs b/Kmse.Core/Z80/Logging/ICpuLogger.cs similarity index 85% rename from Kmse.Core/Z80/ICpuLogger.cs rename to Kmse.Core/Z80/Logging/ICpuLogger.cs index 6d73ee5..c4cd438 100644 --- a/Kmse.Core/Z80/ICpuLogger.cs +++ b/Kmse.Core/Z80/Logging/ICpuLogger.cs @@ -1,4 +1,4 @@ -namespace Kmse.Core.Z80; +namespace Kmse.Core.Z80.Logging; public interface ICpuLogger { diff --git a/Kmse.Core/Z80/Logging/IZ80InstructionLogger.cs b/Kmse.Core/Z80/Logging/IZ80InstructionLogger.cs new file mode 100644 index 0000000..5b8e9b6 --- /dev/null +++ b/Kmse.Core/Z80/Logging/IZ80InstructionLogger.cs @@ -0,0 +1,10 @@ +namespace Kmse.Core.Z80.Logging; + +public interface IZ80InstructionLogger +{ + IZ80InstructionLogger StartNewInstruction(ushort currentProgramCounter); + IZ80InstructionLogger SetOpCode(string opCodeHexString, string name, string description); + IZ80InstructionLogger AddOperationData(byte data); + IZ80InstructionLogger AddOperationData(ushort data); + void Log(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Logging/Z80InstructionLogger.cs b/Kmse.Core/Z80/Logging/Z80InstructionLogger.cs new file mode 100644 index 0000000..a37a1ee --- /dev/null +++ b/Kmse.Core/Z80/Logging/Z80InstructionLogger.cs @@ -0,0 +1,79 @@ +using System.Text; + +namespace Kmse.Core.Z80.Logging; + +public class Z80InstructionLogger : IZ80InstructionLogger +{ + private readonly ICpuLogger _cpuLogger; + private readonly StringBuilder _currentData = new(); + private ushort _instructionMemoryAddressStart; + private string _opCodeString; + private string _description; + private string _name; + + public Z80InstructionLogger(ICpuLogger cpuLogger) + { + _cpuLogger = cpuLogger; + } + + /// + /// Start a new log entry for an instruction given the provided base program counter address + /// + /// Current program counter address/value + /// + public IZ80InstructionLogger StartNewInstruction(ushort currentProgramCounter) + { + _instructionMemoryAddressStart = currentProgramCounter; + _currentData.Clear(); + _opCodeString = string.Empty; + _name = string.Empty; + _description = string.Empty; + return this; + } + + /// + /// Set the op code for this instruction + /// + /// Op code value formatted as Hex string + /// Name of instruction + /// Description for instruction + /// + public IZ80InstructionLogger SetOpCode(string opCodeHexString, string name, string description) + { + _opCodeString = opCodeHexString; + _name = name; + _description = description; + return this; + } + + /// + /// Add operation data for this instruction. Any data is appended to existing data as a string + /// + /// Byte data to add + /// + public IZ80InstructionLogger AddOperationData(byte data) + { + _currentData.Append(data.ToString("X2")); + return this; + } + + /// + /// Add operation data for this instruction. Any data is appended to existing data as a string + /// + /// unsigned short data to add + /// + public IZ80InstructionLogger AddOperationData(ushort data) + { + _currentData.Append(data.ToString("X4")); + return this; + } + + /// + /// Log the instruction to the CPU logger + /// + public void Log() + { + _cpuLogger.LogInstruction(_instructionMemoryAddressStart, _opCodeString, _name, + _description, _currentData.ToString()); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs new file mode 100644 index 0000000..19e75e2 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs @@ -0,0 +1,31 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ80ProgramCounter +{ + void Reset(); + ushort GetValue(); + Z80Register AsRegister(); + byte GetNextInstruction(); + byte GetNextDataByte(); + ushort GetNextTwoDataBytes(); + + /// + /// Set program counter to new value, but don't save the old value to the stack + /// + /// New address to set PC to + void SetProgramCounter(ushort address); + + /// + /// Move program counter forward by the provided value + /// + /// Add this offset to the current PC + void MoveProgramCounterForward(ushort offset); + + /// + /// Move program counter backward by the provided value + /// + /// Subtract this offset from the current PC + void MoveProgramCounterBackward(ushort offset); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs new file mode 100644 index 0000000..240fcd3 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs @@ -0,0 +1,92 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers +{ + public class Z80ProgramCounter : IZ80ProgramCounter + { + private Z80Register _pc; + private readonly IMasterSystemMemory _memory; + private readonly IZ80InstructionLogger _z80InstructionLogger; + + public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger) + { + _memory = memory; + _z80InstructionLogger = z80InstructionLogger; + } + + public void Reset() + { + _pc.Word = 0x00; + } + + public ushort GetValue() + { + return _pc.Word; + } + + public Z80Register AsRegister() + { + return _pc; + } + + public byte GetNextInstruction() + { + return GetNextByteByProgramCounter(); + } + + public byte GetNextDataByte() + { + var data = GetNextByteByProgramCounter(); + _z80InstructionLogger.AddOperationData(data); + return data; + } + + public ushort GetNextTwoDataBytes() + { + ushort data = GetNextByteByProgramCounter(); + data += (ushort)(GetNextByteByProgramCounter() << 8); + _z80InstructionLogger.AddOperationData(data); + return data; + } + + /// + /// Set program counter to new value, but don't save the old value to the stack + /// + /// New address to set PC to + public void SetProgramCounter(ushort address) + { + _pc.Word = address; + } + + /// + /// Move program counter forward by the provided value + /// + /// Add this offset to the current PC + public void MoveProgramCounterForward(ushort offset) + { + // If this goes above ushort max, we assume that when PC hits the limit it just wraps around instead of throwing an error or failing + _pc.Word += offset; + } + + /// + /// Move program counter backward by the provided value + /// + /// Subtract this offset from the current PC + public void MoveProgramCounterBackward(ushort offset) + { + // If this goes below zero/negative, we assume that when PC hits -1 it just wraps around instead of throwing an error or failing + _pc.Word -= offset; + } + + private byte GetNextByteByProgramCounter() + { + // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction + var data = _memory[_pc.Word]; + _pc.Word++; + return data; + } + + } +} diff --git a/Kmse.Core/Z80/Support/CpuStatus.cs b/Kmse.Core/Z80/Support/CpuStatus.cs index c987bb5..41171d3 100644 --- a/Kmse.Core/Z80/Support/CpuStatus.cs +++ b/Kmse.Core/Z80/Support/CpuStatus.cs @@ -15,7 +15,7 @@ public class CpuStatus public Z80Register HlShadow { get; init; } public Z80Register Ix { get; init; } public Z80Register Iy { get; init; } - public Z80Register Pc { get; init; } + public ushort Pc { get; init; } public Z80Register StackPointer { get; init; } public byte IRegister { get; init; } public byte RRegister { get; init; } diff --git a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs index 3d4ca67..08ec045 100644 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs @@ -9,21 +9,6 @@ namespace Kmse.Core.Z80 /// public partial class Z80Cpu { - private byte GetNextDataByte() - { - var data = GetNextByteByProgramCounter(); - _currentData.Append(data.ToString("X2")); - return data; - } - - private ushort GetNextTwoDataBytes() - { - ushort data = GetNextByteByProgramCounter(); - data += (ushort)(GetNextByteByProgramCounter() << 8); - _currentData.Append(data.ToString("X4")); - return data; - } - private void SetFlag(Z80StatusFlags flags) { _af.Low |= (byte)flags; @@ -57,34 +42,26 @@ private bool IsFlagSet(Z80StatusFlags flags) return currentSetFlags == flags; } - /// - /// Set program counter to new value, but don't save the old value to the stack - /// - /// New address to set PC to - private void SetProgramCounter(ushort address) - { - // Update PC to execute from new address - _pc.Word = address; - } - private void SaveAndUpdateProgramCounter(ushort address) { // Storing PC in Stack so can resume later - PushRegisterToStack(_pc); + PushRegisterToStack(_pc.AsRegister()); // Update PC to execute from new address - _pc.Word = address; + _pc.SetProgramCounter(address); } private void SetProgramCounterFromRegister(Z80Register register) { // Update PC to execute from the value of the register - _pc.Word = register.Word; + _pc.SetProgramCounter(register.Word); } private void ResetProgramCounterFromStack() { - PopRegisterFromStack(ref _pc); + var register = new Z80Register(); + PopRegisterFromStack(ref register); + _pc.SetProgramCounter(register.Word); } private void PushRegisterToStack(Z80Register register) @@ -111,7 +88,7 @@ private void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) { if (IsFlagSet(flag)) { - SetProgramCounter(address); + _pc.SetProgramCounter(address); } } @@ -119,13 +96,13 @@ private void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) { if (!IsFlagSet(flag)) { - SetProgramCounter(address); + _pc.SetProgramCounter(address); } } private void JumpByOffset(byte offset) { - var newPcLocation = _pc.Word; + var newPcLocation = _pc.GetValue(); // Range is -126 to +129 so we need a signed version // However sbyte only goes from -128 to +127 but we need -126 to +129 so have to do this manually @@ -141,7 +118,7 @@ private void JumpByOffset(byte offset) } // Note we don't need to add one here since we always increment the Pc after we read from it, so it's already pointing to next command at this point - SetProgramCounter(newPcLocation); + _pc.SetProgramCounter(newPcLocation); } private void JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status) diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index 9b5588d..c269cc3 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -123,27 +123,27 @@ private void PopulateJumpCallAndReturnOperations() AddStandardInstruction(0xE9, 4, "JP (HL)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_hl); }); AddDoubleByteInstruction(0xDD, 0xE9, 8, "JP (IX)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_ix); }); AddDoubleByteInstruction(0xFD, 0xE9, 8, "JP (IY)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_iy); }); - AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { SetProgramCounter(GetNextTwoDataBytes()); }); - - AddStandardInstruction(0xDA, 10, "JP C,$NN", "Conditional Jump If Carry Set", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.CarryC, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xD2, 10, "JP NC,$NN", "Conditional Jump If Carry Not Set", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.CarryC, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xFA, 10, "JP M,$NN", "Conditional Jump If Negative", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.SignS, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xF2, 10, "JP P,$NN", "Conditional Jump If Positive", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.SignS, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xCA, 10, "JP Z,$NN", "Conditional Jump if Zero", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ZeroZ, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xC2, 10, "JP NZ,$NN", "Conditional Jump If Not Zero", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ZeroZ, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xEA, 10, "JP PE,$NN", "Conditional Jump If Parity Even", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ParityOverflowPV, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xE2, 10, "JP PO,$NN", "Conditional Jump If Parity Odd", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x18, 12, "JR $N+2", "Relative Jump By Offset", _ => { JumpByOffset(GetNextDataByte()); }); - AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.CarryC, GetNextDataByte()); }); - AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, GetNextDataByte()); }); - AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, GetNextDataByte()); }); - AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, GetNextDataByte()); }); + AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { _pc.SetProgramCounter(_pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0xDA, 10, "JP C,$NN", "Conditional Jump If Carry Set", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xD2, 10, "JP NC,$NN", "Conditional Jump If Carry Not Set", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xFA, 10, "JP M,$NN", "Conditional Jump If Negative", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xF2, 10, "JP P,$NN", "Conditional Jump If Positive", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xCA, 10, "JP Z,$NN", "Conditional Jump if Zero", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xC2, 10, "JP NZ,$NN", "Conditional Jump If Not Zero", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xEA, 10, "JP PE,$NN", "Conditional Jump If Parity Even", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xE2, 10, "JP PO,$NN", "Conditional Jump If Parity Odd", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x18, 12, "JR $N+2", "Relative Jump By Offset", _ => { JumpByOffset(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()); }); AddStandardInstruction(0x10, DynamicCycleHandling, "DJNZ $+2", "Decrement, Jump if Non-Zero", _ => { Decrement8Bit(ref _bc.High); - var offset = GetNextDataByte(); + var offset = _pc.GetNextDataByte(); if (_bc.High != 0) { JumpByOffset(offset); @@ -154,15 +154,15 @@ private void PopulateJumpCallAndReturnOperations() _currentCycleCount += 8; }); - AddStandardInstruction(0xCD, 17, "CALL NN", "Unconditional Call", _ => { SaveAndUpdateProgramCounter(GetNextTwoDataBytes()); }); - AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { CallIfFlagCondition(Z80StatusFlags.CarryC, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { CallIfNotFlagCondition(Z80StatusFlags.CarryC, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { CallIfFlagCondition(Z80StatusFlags.SignS, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { CallIfNotFlagCondition(Z80StatusFlags.SignS, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { CallIfFlagCondition(Z80StatusFlags.ZeroZ, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, GetNextTwoDataBytes()); }); - AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, GetNextTwoDataBytes()); }); + AddStandardInstruction(0xCD, 17, "CALL NN", "Unconditional Call", _ => { SaveAndUpdateProgramCounter(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { CallIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { CallIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { CallIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { CallIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { CallIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0xC9, 10, "RET", "Return", _ => { ResetProgramCounterFromStack(); }); AddStandardInstruction(0xD8, DynamicCycleHandling, "RET C", "Conditional Return If Carry Set", _ => { ReturnIfFlag(Z80StatusFlags.CarryC); }); @@ -196,7 +196,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x83, 4, "ADD A, E", "Add E to A", _ => { AddValueTo8BitRegister(_de.Low, ref _af.High); }); AddStandardInstruction(0x84, 4, "ADD A, H", "Add H to A", _ => { AddValueTo8BitRegister(_hl.High, ref _af.High); }); AddStandardInstruction(0x85, 4, "ADD A, L", "Add L to A", _ => { AddValueTo8BitRegister(_hl.Low, ref _af.High); }); - AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { AddValueTo8BitRegister(GetNextDataByte(), ref _af.High);}); + AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { AddValueTo8BitRegister(_pc.GetNextDataByte(), ref _af.High);}); AddStandardInstruction(0x09, 11, "ADD HL,BC", "Add BC to HL", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl); }); AddStandardInstruction(0x19, 11, "ADD HL,DE", "Add DE to HL", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl); }); @@ -213,8 +213,8 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xFD, 0x39, 15, "ADD IY,SP", "Add SP to IY", _ => { Add16BitRegisterTo16BitRegister(_stackPointer, ref _iy); }); AddStandardInstruction(0x86, 4, "ADD A,(HL)", "Add Data at memory location from HL to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_hl, 0, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x86, 19, "ADD A,(IX+d)", "Add Data at memory location from IX + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, GetNextDataByte(), ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x86, 19, "ADD A,(IY+d)", "Add Data at memory location from IY + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, GetNextDataByte(), ref _af.High); }); + AddDoubleByteInstruction(0xDD, 0x86, 19, "ADD A,(IX+d)", "Add Data at memory location from IX + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High); }); + AddDoubleByteInstruction(0xFD, 0x86, 19, "ADD A,(IY+d)", "Add Data at memory location from IY + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0x8F, 4, "ADC A, A", "Add A to A with Carry", _ => { AddValueTo8BitRegister(_af.High, ref _af.High, true); }); AddStandardInstruction(0x88, 4, "ADC B, B", "Add B to A with Carry", _ => { AddValueTo8BitRegister(_bc.High, ref _af.High, true); }); @@ -223,11 +223,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x8B, 4, "ADC E, E", "Add E to A with Carry", _ => { AddValueTo8BitRegister(_de.Low, ref _af.High, true); }); AddStandardInstruction(0x8C, 4, "ADC H, H", "Add H to A with Carry", _ => { AddValueTo8BitRegister(_hl.High, ref _af.High, true); }); AddStandardInstruction(0x8D, 4, "ADC L, L", "Add L to A with Carry", _ => { AddValueTo8BitRegister(_hl.Low, ref _af.High, true); }); - AddStandardInstruction(0xCE, 7, "ADC A, N", "Add n to A with Carry", _ => { AddValueTo8BitRegister(GetNextDataByte(), ref _af.High, true); }); + AddStandardInstruction(0xCE, 7, "ADC A, N", "Add n to A with Carry", _ => { AddValueTo8BitRegister(_pc.GetNextDataByte(), ref _af.High, true); }); AddStandardInstruction(0x8E, 4, "ADC (HL)", "Add (HL) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_hl, 0, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x8E, 19, "ADC A,(IX+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, GetNextDataByte(), ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8E, 19, "ADC A,(IY+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, GetNextDataByte(), ref _af.High, true); }); + AddDoubleByteInstruction(0xDD, 0x8E, 19, "ADC A,(IX+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High, true); }); + AddDoubleByteInstruction(0xFD, 0x8E, 19, "ADC A,(IY+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High, true); }); AddDoubleByteInstruction(0xED, 0x4A, 15, "ADC HL,BC", "Add BC to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x5A, 15, "ADC HL,DE", "Add DE to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl, true); }); @@ -241,11 +241,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x93, 4, "SUB A, E", "Subtract E from A", _ => { SubtractValueFrom8BitRegister(_de.Low, ref _af.High); }); AddStandardInstruction(0x94, 4, "SUB A, H", "Subtract H from A", _ => { SubtractValueFrom8BitRegister(_hl.High, ref _af.High); }); AddStandardInstruction(0x95, 4, "SUB A, L", "Subtract L from A", _ => { SubtractValueFrom8BitRegister(_hl.Low, ref _af.High); }); - AddStandardInstruction(0xD6, 7, "SUB A, N", "Subtract n from A", _ => { SubtractValueFrom8BitRegister(GetNextDataByte(), ref _af.High); }); + AddStandardInstruction(0xD6, 7, "SUB A, N", "Subtract n from A", _ => { SubtractValueFrom8BitRegister(_pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0x96, 7, "SUB (HL)", "Subtract Data at memory location HL from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_hl, 0, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x96, 19, "SUB (IX+d)", "Subtract Data at memory location IX + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, GetNextDataByte(), ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x96, 19, "SUB (IY+d)", "Subtract Data at memory location IY + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, GetNextDataByte(), ref _af.High); }); + AddDoubleByteInstruction(0xDD, 0x96, 19, "SUB (IX+d)", "Subtract Data at memory location IX + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High); }); + AddDoubleByteInstruction(0xFD, 0x96, 19, "SUB (IY+d)", "Subtract Data at memory location IY + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0x9F, 4, "SBC A, A", "Subtract A from A with Carry", _ => { SubtractValueFrom8BitRegister(_af.High, ref _af.High, true); }); AddStandardInstruction(0x98, 4, "SBC A, B", "Subtract B from A with Carry", _ => { SubtractValueFrom8BitRegister(_bc.High, ref _af.High, true); }); @@ -254,11 +254,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x9B, 4, "SBC A, E", "Subtract E from A with Carry", _ => { SubtractValueFrom8BitRegister(_de.Low, ref _af.High, true); }); AddStandardInstruction(0x9C, 4, "SBC A, H", "Subtract H from A with Carry", _ => { SubtractValueFrom8BitRegister(_hl.High, ref _af.High, true); }); AddStandardInstruction(0x9D, 4, "SBC A, L", "Subtract L from A with Carry", _ => { SubtractValueFrom8BitRegister(_hl.Low, ref _af.High, true); }); - AddStandardInstruction(0xDE, 7, "SBC A,N", "Subtract N from A with Carry", _ => { SubtractValueFrom8BitRegister(GetNextDataByte(), ref _af.High, true); }); + AddStandardInstruction(0xDE, 7, "SBC A,N", "Subtract N from A with Carry", _ => { SubtractValueFrom8BitRegister(_pc.GetNextDataByte(), ref _af.High, true); }); AddStandardInstruction(0x9E, 4, "SBC (HL)", "Subtract (HL) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_hl, 0, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x9E, 19, "SBC A,(IX+d)", "Subtract (IX+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, GetNextDataByte(), ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x9E, 19, "SBC A,(IY+d)", "Subtract (Iy+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, GetNextDataByte(), ref _af.High, true); }); + AddDoubleByteInstruction(0xDD, 0x9E, 19, "SBC A,(IX+d)", "Subtract (IX+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High, true); }); + AddDoubleByteInstruction(0xFD, 0x9E, 19, "SBC A,(IY+d)", "Subtract (Iy+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High, true); }); AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_bc, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_de, ref _hl, true); }); @@ -274,9 +274,9 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0xBD, 4, "CP L", "Compare L to A", _ => { Compare8Bit(_hl.Low, _af.High); }); AddStandardInstruction(0xBE, 7, "CP (HL)", "Compare Data at memory location in (HL) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High); }); - AddStandardInstruction(0xFE, 7, "CP N", "Compare value N to A", _ => { Compare8Bit(GetNextDataByte(), _af.High); }); - AddDoubleByteInstruction(0xDD, 0xBE, 19, "CP (IX+d)", "Compare Data at memory location in (IX + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_ix, GetNextDataByte(), _af.High); }); - AddDoubleByteInstruction(0xFD, 0xBE, 19, "CP (IY+d)", "Compare Data at memory location in (IY + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_iy, GetNextDataByte(), _af.High); }); + AddStandardInstruction(0xFE, 7, "CP N", "Compare value N to A", _ => { Compare8Bit(_pc.GetNextDataByte(), _af.High); }); + AddDoubleByteInstruction(0xDD, 0xBE, 19, "CP (IX+d)", "Compare Data at memory location in (IX + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High); }); + AddDoubleByteInstruction(0xFD, 0xBE, 19, "CP (IY+d)", "Compare Data at memory location in (IY + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High); }); AddDoubleByteInstruction(0xED, 0xA1, 16, "CPI", "Compare and Increment", _ => { CompareIncrement(); }); AddDoubleByteInstruction(0xED, 0xB1, DynamicCycleHandling, "CPIR", "Compare, Increment, Repeat", _ => @@ -297,7 +297,8 @@ private void PopulateArthmeticAndLogicalInstructions() // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + var currentPc = _pc.GetValue(); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -325,7 +326,7 @@ private void PopulateArthmeticAndLogicalInstructions() // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -342,11 +343,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0xA4, 4, "AND H", "AND A with H and Store in A", _ => { And8Bit(_af.High, _hl.High, ref _af.High); }); AddStandardInstruction(0xA5, 4, "AND L", "AND A with L and Store in A", _ => { And8Bit(_af.High, _hl.Low, ref _af.High); }); - AddStandardInstruction(0xE6, 7, "AND N", "AND A with N and Store in A", _ => { And8Bit(_af.High, GetNextDataByte(), ref _af.High); }); + AddStandardInstruction(0xE6, 7, "AND N", "AND A with N and Store in A", _ => { And8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0xA6, 7, "AND (HL)", "AND A with data in memory location in (HL) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xA6, 19, "AND (IX+d)", "AND A with data in memory location in (IX+d) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_ix, GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xA6, 19, "AND (IY+d)", "AND A with data in memory location in (IX+y) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_iy, GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xDD, 0xA6, 19, "AND (IX+d)", "AND A with data in memory location in (IX+d) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xFD, 0xA6, 19, "AND (IY+d)", "AND A with data in memory location in (IX+y) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); AddStandardInstruction(0xB7, 4, "OR A", "OR A with A and Store in A", _ => { Or8Bit(_af.High, _af.High, ref _af.High); }); AddStandardInstruction(0xB0, 4, "OR B", "OR A with B and Store in A", _ => { Or8Bit(_af.High, _bc.High, ref _af.High); }); @@ -355,11 +356,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0xB3, 4, "OR E", "OR A with E and Store in A", _ => { Or8Bit(_af.High, _de.Low, ref _af.High); }); AddStandardInstruction(0xB4, 4, "OR H", "OR A with H and Store in A", _ => { Or8Bit(_af.High, _hl.High, ref _af.High); }); AddStandardInstruction(0xB5, 4, "OR L", "OR A with L and Store in A", _ => { Or8Bit(_af.High, _hl.Low, ref _af.High); }); - AddStandardInstruction(0xF6, 7, "OR N", "Or A with N and Store in A", _ => { Or8Bit(_af.High, GetNextDataByte(), ref _af.High); }); + AddStandardInstruction(0xF6, 7, "OR N", "Or A with N and Store in A", _ => { Or8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0xB6, 7, "OR (HL)", "OR A with data in memory location in (HL) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xB6, 19, "OR (IX+d)", "OR A with data in memory location in (IX+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_ix, GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xB6, 19, "OR (IY+d)", "OR A with data in memory location in (IY+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_iy, GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xDD, 0xB6, 19, "OR (IX+d)", "OR A with data in memory location in (IX+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xFD, 0xB6, 19, "OR (IY+d)", "OR A with data in memory location in (IY+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); AddStandardInstruction(0xAF, 4, "XOR A", "XOR A with A and Store in A", _ => { Xor8Bit(_af.High, _af.High, ref _af.High); }); AddStandardInstruction(0xA8, 4, "XOR B", "XOR A with B and Store in A", _ => { Xor8Bit(_af.High, _bc.High, ref _af.High); }); @@ -368,11 +369,11 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0xAB, 4, "XOR E", "XOR A with E and Store in A", _ => { Xor8Bit(_af.High, _de.Low, ref _af.High); }); AddStandardInstruction(0xAC, 4, "XOR H", "XOR A with H and Store in A", _ => { Xor8Bit(_af.High, _hl.High, ref _af.High); }); AddStandardInstruction(0xAD, 4, "XOR L", "XOR A with L and Store in A", _ => { Xor8Bit(_af.High, _hl.Low, ref _af.High); }); - AddStandardInstruction(0xEE, 7, "XOR N", "XOr A with N and Store in A", _ => { Xor8Bit(_af.High, GetNextDataByte(), ref _af.High); }); + AddStandardInstruction(0xEE, 7, "XOR N", "XOr A with N and Store in A", _ => { Xor8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); AddStandardInstruction(0xAE, 7, "XOR (HL)", "XOR A with data in memory location in (HL) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xAE, 19, "XOR (IX+d)", "XOR A with data in memory location in (IX+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_ix, GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xAE, 19, "XOR (IY+d)", "XOR A with data in memory location in (IY+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_iy, GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xDD, 0xAE, 19, "XOR (IX+d)", "XOR A with data in memory location in (IX+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); + AddDoubleByteInstruction(0xFD, 0xAE, 19, "XOR (IY+d)", "XOR A with data in memory location in (IY+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); AddStandardInstruction(0x3C, 4, "INC A", "Increment A", _ => { Increment8Bit(ref _af.High); }); AddStandardInstruction(0x04, 4, "INC B", "Increment B", _ => { Increment8Bit(ref _bc.High); }); @@ -390,8 +391,8 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xFD, 0x23, 10, "INC IY", "Increment", _ => { Increment16Bit(ref _iy); }); AddStandardInstruction(0x34, 11, "INC (HL)", "Increment (indirect)", _ => { IncrementAtRegisterMemoryLocation(_hl, 0); }); - AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_ix, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_iy, GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_iy, _pc.GetNextDataByte()); }); AddStandardInstruction(0x3D, 4, "DEC A", "Decrement A", _ => { Decrement8Bit(ref _af.High); }); AddStandardInstruction(0x05, 4, "DEC B", "Decrement B", _ => { Decrement8Bit(ref _bc.High); }); @@ -409,8 +410,8 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { Decrement16Bit(ref _iy); }); AddStandardInstruction(0x35, 11, "DEC (HL)", "", _ => { DecrementAtRegisterMemoryLocation(_hl, 0); }); - AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_ix, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_iy, GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_iy, _pc.GetNextDataByte()); }); AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => { @@ -653,21 +654,21 @@ private void PopulateLoadAndExchangeInstructions() AddStandardInstruction(0x2, 7, "LD (BC),A", "Load memory location at BC into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_bc, _af.High); }); AddStandardInstruction(0x12, 7, "LD (DE),A", "Load memory location at DE into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_de, _af.High); }); - AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _af.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x70, 19, "LD (IX+d),B", "Load B into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x71, 19, "LD (IX+d),C", "Load C into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.Low, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x72, 19, "LD (IX+d),D", "Load D into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x73, 19, "LD (IX+d),E", "Load E into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.Low, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x74, 19, "LD (IX+d),H", "Load H into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x75, 19, "LD (IX+d),L", "Load L into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.Low, GetNextDataByte()); }); - - AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _af.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x70, 19, "LD (IY+d),B", "Load B into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x71, 19, "LD (IY+d),C", "Load C into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.Low, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x72, 19, "LD (IY+d),D", "Load D into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x73, 19, "LD (IY+d),E", "Load E into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.Low, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.Low, GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _af.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x70, 19, "LD (IX+d),B", "Load B into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x71, 19, "LD (IX+d),C", "Load C into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.Low, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x72, 19, "LD (IX+d),D", "Load D into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x73, 19, "LD (IX+d),E", "Load E into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.Low, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x74, 19, "LD (IX+d),H", "Load H into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x75, 19, "LD (IX+d),L", "Load L into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.Low, _pc.GetNextDataByte()); }); + + AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _af.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x70, 19, "LD (IY+d),B", "Load B into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x71, 19, "LD (IY+d),C", "Load C into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.Low, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x72, 19, "LD (IY+d),D", "Load D into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x73, 19, "LD (IY+d),E", "Load E into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.Low, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.Low, _pc.GetNextDataByte()); }); AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { Load16BitRegisterFrom16BitRegister(ref _stackPointer, _hl); }); AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { Load16BitRegisterFrom16BitRegister(ref _stackPointer, _ix); }); @@ -677,13 +678,13 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _rRegister); }); AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { LoadSpecial8BitRegisterToAccumulator(_iRegister); }); AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { LoadSpecial8BitRegisterToAccumulator(_rRegister); }); - AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { LoadValueInto8BitRegister(ref _af.High, GetNextDataByte()); }); - AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { LoadValueInto8BitRegister(ref _bc.High, GetNextDataByte()); }); - AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { LoadValueInto8BitRegister(ref _bc.Low, GetNextDataByte()); }); - AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { LoadValueInto8BitRegister(ref _de.High, GetNextDataByte()); }); - AddStandardInstruction(0x1E, 7, "LD E,N", "Load n into E", _ => { LoadValueInto8BitRegister(ref _de.Low, GetNextDataByte()); }); - AddStandardInstruction(0x26, 7, "LD H,N", "Load n into H", _ => { LoadValueInto8BitRegister(ref _hl.High, GetNextDataByte()); }); - AddStandardInstruction(0x2E, 7, "LD L,N", "Load n into L", _ => { LoadValueInto8BitRegister(ref _hl.Low, GetNextDataByte()); }); + AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { LoadValueInto8BitRegister(ref _af.High, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { LoadValueInto8BitRegister(ref _bc.High, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { LoadValueInto8BitRegister(ref _bc.Low, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { LoadValueInto8BitRegister(ref _de.High, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x1E, 7, "LD E,N", "Load n into E", _ => { LoadValueInto8BitRegister(ref _de.Low, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x26, 7, "LD H,N", "Load n into H", _ => { LoadValueInto8BitRegister(ref _hl.High, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x2E, 7, "LD L,N", "Load n into L", _ => { LoadValueInto8BitRegister(ref _hl.Low, _pc.GetNextDataByte()); }); // These are undocumented but zxdoc still runs them // https://iot.onl/asm/z80/opcodes/undocumented/ @@ -714,10 +715,10 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x5F, 4, "LD A,E", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); // These are undocumented instructions which operate on the IX/IY high and low nibbles - AddDoubleByteInstruction(0xDD, 0x26, 7, "LD IXH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _ix.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x2E, 7, "LD IXL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _ix.Low, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x26, 7, "LD IYH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _iy.High, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x2E, 7, "LD IYL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _iy.Low, GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x26, 7, "LD IXH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _ix.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x2E, 7, "LD IXL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _ix.Low, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x26, 7, "LD IYH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _iy.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x2E, 7, "LD IYL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _iy.Low, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x7C, 9, "LD A, IXH", "Load IXH into A", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _af.High); }); AddDoubleByteInstruction(0xDD, 0x44, 9, "LD B, IXH", "Load IXH into B", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _bc.High); }); @@ -774,48 +775,48 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x6D, 9, "LD IYL, IYL", "Load IYL into IYL", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _iy.Low); }); // end undocumented instructions - AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x46, 19, "LD B,(IX+d)", "Load memory at IX + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x4E, 19, "LD C,(IX+d)", "Load memory at IX + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x56, 19, "LD D,(IX+d)", "Load memory at IX + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x5E, 19, "LD E,(IX+d)", "Load memory at IX + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x66, 19, "LD H,(IX+d)", "Load memory at IX + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _ix.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x6E, 19, "LD L,(IX+d)", "Load memory at IX + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _ix.Word, GetNextDataByte()); }); - - AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _iy.Word, GetNextDataByte());}); - AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _iy.Word, GetNextDataByte());}); - AddDoubleByteInstruction(0xFD, 0x4E, 19, "LD C,(IY+d)", "Load memory at IY + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _iy.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x56, 19, "LD D,(IY+d)", "Load memory at IY + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _iy.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x5E, 19, "LD E,(IY+d)", "Load memory at IY + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _iy.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x66, 19, "LD H,(IY+d)", "Load memory at IY + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _iy.Word, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _iy.Word, GetNextDataByte()); }); - - AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { LoadValueIntoRegisterMemoryLocation(GetNextDataByte(), _hl); }); - AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = GetNextDataByte(); var value = GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _ix, offset); }); - AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = GetNextDataByte(); var value = GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _iy, offset); }); - - AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, GetNextTwoDataBytes()); }); - AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { LoadInto16BitRegisterFromMemory(ref _hl, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x4B, 20, "LD BC, (NN)", "Load value at memory location NN into BC", _ => { LoadInto16BitRegisterFromMemory(ref _bc, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x5B, 20, "LD DE, (NN)", "Load value at memory location NN into DE", _ => { LoadInto16BitRegisterFromMemory(ref _de, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { LoadInto16BitRegisterFromMemory(ref _stackPointer, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x2A, 20, "LD IX, (NN)", "Load value at memory location NN into IX", _ => { LoadInto16BitRegisterFromMemory(ref _ix, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x2A, 20, "LD IY, (NN)", "Load value at memory location NN into IY", _ => { LoadInto16BitRegisterFromMemory(ref _iy, GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { LoadValueInto16BitRegister(ref _bc, GetNextTwoDataBytes()); }); - AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { LoadValueInto16BitRegister(ref _de, GetNextTwoDataBytes()); }); - AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { LoadValueInto16BitRegister(ref _hl, GetNextTwoDataBytes()); }); - AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { LoadValueInto16BitRegister(ref _stackPointer, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { LoadValueInto16BitRegister(ref _ix, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { LoadValueInto16BitRegister(ref _iy, GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x32, 13, "LD (NN),A", "Load A into memory location NN", _ => { Save8BitRegisterValueToMemory(_af.High, GetNextTwoDataBytes()); }); - AddStandardInstruction(0x22, 16, "LD (NN),HL", "Load HL into memory location NN", _ => { Save16BitRegisterToMemory(_hl, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x43, 20, "LD(NN), BC", "Load BC into memory location NN", _ => { Save16BitRegisterToMemory(_bc, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { Save16BitRegisterToMemory(_de, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { Save16BitRegisterToMemory(_ix, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { Save16BitRegisterToMemory(_iy, GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { Save16BitRegisterToMemory(_stackPointer, GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x46, 19, "LD B,(IX+d)", "Load memory at IX + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x4E, 19, "LD C,(IX+d)", "Load memory at IX + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x56, 19, "LD D,(IX+d)", "Load memory at IX + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x5E, 19, "LD E,(IX+d)", "Load memory at IX + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x66, 19, "LD H,(IX+d)", "Load memory at IX + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x6E, 19, "LD L,(IX+d)", "Load memory at IX + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _ix.Word, _pc.GetNextDataByte()); }); + + AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _iy.Word, _pc.GetNextDataByte());}); + AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _iy.Word, _pc.GetNextDataByte());}); + AddDoubleByteInstruction(0xFD, 0x4E, 19, "LD C,(IY+d)", "Load memory at IY + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _iy.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x56, 19, "LD D,(IY+d)", "Load memory at IY + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _iy.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x5E, 19, "LD E,(IY+d)", "Load memory at IY + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _iy.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x66, 19, "LD H,(IY+d)", "Load memory at IY + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _iy.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _iy.Word, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { LoadValueIntoRegisterMemoryLocation(_pc.GetNextDataByte(), _hl); }); + AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _ix, offset); }); + AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _iy, offset); }); + + AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { LoadInto16BitRegisterFromMemory(ref _hl, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x4B, 20, "LD BC, (NN)", "Load value at memory location NN into BC", _ => { LoadInto16BitRegisterFromMemory(ref _bc, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x5B, 20, "LD DE, (NN)", "Load value at memory location NN into DE", _ => { LoadInto16BitRegisterFromMemory(ref _de, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { LoadInto16BitRegisterFromMemory(ref _stackPointer, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x2A, 20, "LD IX, (NN)", "Load value at memory location NN into IX", _ => { LoadInto16BitRegisterFromMemory(ref _ix, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x2A, 20, "LD IY, (NN)", "Load value at memory location NN into IY", _ => { LoadInto16BitRegisterFromMemory(ref _iy, _pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { LoadValueInto16BitRegister(ref _bc, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { LoadValueInto16BitRegister(ref _de, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { LoadValueInto16BitRegister(ref _hl, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { LoadValueInto16BitRegister(ref _stackPointer, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { LoadValueInto16BitRegister(ref _ix, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { LoadValueInto16BitRegister(ref _iy, _pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x32, 13, "LD (NN),A", "Load A into memory location NN", _ => { Save8BitRegisterValueToMemory(_af.High, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x22, 16, "LD (NN),HL", "Load HL into memory location NN", _ => { Save16BitRegisterToMemory(_hl, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x43, 20, "LD(NN), BC", "Load BC into memory location NN", _ => { Save16BitRegisterToMemory(_bc, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { Save16BitRegisterToMemory(_de, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { Save16BitRegisterToMemory(_ix, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { Save16BitRegisterToMemory(_iy, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { Save16BitRegisterToMemory(_stackPointer, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => { @@ -851,7 +852,7 @@ private void PopulateLoadAndExchangeInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -894,7 +895,7 @@ private void PopulateLoadAndExchangeInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -930,7 +931,7 @@ private void PopulateExchangeBlockTransferAndSearchInstructions() private void PopulateInputOutputInstructions() { - AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _af.High = ReadFromIo(_af.High, GetNextDataByte()); }); + AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _af.High = ReadFromIo(_af.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xED, 0x70, 12, "IN (C)", "Read I/O at B/C But Only Set Flags", _ => { ReadFromIoAndSetFlags(_bc.High, _bc.Low); }); AddDoubleByteInstruction(0xED, 0x78, 12, "IN A,(C)", "Read I/O at B/C into A with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _af.High); }); AddDoubleByteInstruction(0xED, 0x40, 12, "IN B,(C)", "Read I/O at B/C into B with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _bc.High); }); @@ -974,7 +975,7 @@ private void PopulateInputOutputInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -1017,7 +1018,7 @@ private void PopulateInputOutputInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -1027,7 +1028,7 @@ private void PopulateInputOutputInstructions() }); - AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { WriteToIo(_af.High, GetNextDataByte(), _af.High); }); + AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { WriteToIo(_af.High, _pc.GetNextDataByte(), _af.High); }); AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { WriteToIo(_bc.High, _bc.Low, _af.High); }); AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { WriteToIo(_bc.High, _bc.Low, _bc.High); }); AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { WriteToIo(_bc.High, _bc.Low, _bc.Low); }); @@ -1072,7 +1073,7 @@ private void PopulateInputOutputInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else @@ -1117,7 +1118,7 @@ private void PopulateInputOutputInstructions() // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); + _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } else diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 95b7e50..dd34018 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,6 +1,8 @@ using System.Text; using Kmse.Core.IO; using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80; @@ -8,6 +10,8 @@ namespace Kmse.Core.Z80; public partial class Z80Cpu : IZ80Cpu { private readonly ICpuLogger _cpuLogger; + private readonly IZ80InstructionLogger _instructionLogger; + private int _currentCycleCount; private IMasterSystemIoManager _io; private IMasterSystemMemory _memory; @@ -18,27 +22,25 @@ public partial class Z80Cpu : IZ80Cpu private Z80Register _af, _bc, _de, _hl; private Z80Register _afShadow, _bcShadow, _deShadow, _hlShadow; private Z80Register _ix, _iy; - private Z80Register _pc, _stackPointer; + private Z80Register _stackPointer; private byte _iRegister, _rRegister; + private IZ80ProgramCounter _pc; + /// /// Disables interrupts from being accepted if set to False /// - private bool _interruptFlipFlop1 = false; + private bool _interruptFlipFlop1; /// /// Temporary storage location for FF1 above /// - private bool _interruptFlipFlop2 = false; - private byte _interruptMode = 0; - - // TODO: Can we improve handling of logging of instructions and fetching of memory reads - private ushort _instructionMemoryAddressStart; - private readonly StringBuilder _currentData = new(); + private bool _interruptFlipFlop2; + private byte _interruptMode; - public Z80Cpu(ICpuLogger cpuLogger) + public Z80Cpu(ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger) { _cpuLogger = cpuLogger; - _pc = new Z80Register(); + _instructionLogger = instructionLogger; _af = new Z80Register(); _bc = new Z80Register(); _de = new Z80Register(); @@ -60,6 +62,7 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _cpuLogger.Debug("Initializing CPU"); _memory = memory; _io = io; + _pc = new Z80ProgramCounter(memory, _instructionLogger); } public CpuStatus GetStatus() @@ -79,7 +82,7 @@ public CpuStatus GetStatus() HlShadow = _hlShadow, Ix = _ix, Iy = _iy, - Pc = _pc, + Pc = _pc.GetValue(), StackPointer = _stackPointer, IRegister = _iRegister, RRegister = _rRegister, @@ -98,8 +101,7 @@ public void Reset() _cpuLogger.Debug("Resetting CPU"); _currentCycleCount = 0; - // Reset program counter back to start - _pc.Word = 0x00; + _pc.Reset(); _halted = false; _interruptFlipFlop1 = false; @@ -126,17 +128,18 @@ public void Reset() _io.ClearNonMaskableInterrupt(); _io.ClearMaskableInterrupt(); + + _instructionLogger.StartNewInstruction(0x00); } public int ExecuteNextCycle() { _currentCycleCount = 0; - _instructionMemoryAddressStart = _pc.Word; - _currentData.Clear(); + _instructionLogger.StartNewInstruction(_pc.GetValue()); if (_io.NonMaskableInterrupt) { - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", string.Empty); + _cpuLogger.LogInstruction(_pc.GetValue(), "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", string.Empty); // Copy state of IFF1 into IFF2 to keep a copy and reset IFF1 so processing can continue without a masked interrupt occuring // This gets copied back with a RETN occurs @@ -159,7 +162,7 @@ public int ExecuteNextCycle() if (_interruptFlipFlop1 && _io.MaskableInterrupt) { - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {_interruptMode}"); + _cpuLogger.LogInstruction(_pc.GetValue(), "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {_interruptMode}"); _interruptFlipFlop1 = false; _interruptFlipFlop2 = false; @@ -199,7 +202,7 @@ public int ExecuteNextCycle() return NopCycleCount; } - var opCode = GetNextInstruction(); + var opCode = _pc.GetNextInstruction(); var instruction = opCode switch { 0xCB => ProcessCBOpCode(CbInstructionModes.Normal), @@ -212,7 +215,10 @@ public int ExecuteNextCycle() if (instruction == null) { // Unhandled instruction, just do a NOP - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, opCode.ToString("X2"), "Unimplemented Instruction", "Unimplemented Instruction", string.Empty); + _instructionLogger + .SetOpCode(opCode.ToString("X2"), "Unimplemented Instruction", "Unimplemented Instruction") + .Log(); + _currentCycleCount += NopCycleCount; return _currentCycleCount; } @@ -224,24 +230,13 @@ public int ExecuteNextCycle() _currentCycleCount += instruction.ClockCycles; } - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, instruction.GetOpCode(), instruction.Name, instruction.Description, _currentData.ToString()); + _instructionLogger + .SetOpCode(instruction.GetOpCode(), instruction.Name, instruction.Description) + .Log(); return _currentCycleCount; } - private byte GetNextByteByProgramCounter() - { - // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction - var data = _memory[_pc.Word]; - _pc.Word++; - return data; - } - - private byte GetNextInstruction() - { - return GetNextByteByProgramCounter(); - } - private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) { if (!_genericInstructions.TryGetValue(opCode, out var instruction)) @@ -255,7 +250,7 @@ private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) private Instruction ProcessCBOpCode(CbInstructionModes mode) { // Two byte op code, so get next part of instruction and use that to lookup instruction - var opCode = GetNextInstruction(); + var opCode = _pc.GetNextInstruction(); // Normal CB instruction, just do a lookup for the instruction if (mode == CbInstructionModes.Normal) @@ -272,7 +267,7 @@ private Instruction ProcessCBOpCode(CbInstructionModes mode) // FD/DD CB XX OpCode // We need to read another byte which is the actual op code we use to lookup since third byte is data - var fourthOpCode = GetNextInstruction(); + var fourthOpCode = _pc.GetNextInstruction(); SpecialCbInstruction specialCbInstruction; bool foundInstruction; switch (mode) @@ -300,7 +295,7 @@ private Instruction ProcessCBOpCode(CbInstructionModes mode) private Instruction ProcessDDOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = GetNextInstruction(); + var secondOpCode = _pc.GetNextInstruction(); if (secondOpCode == 0xCB) { // Not a normal instruction, but a special instruction @@ -319,7 +314,7 @@ private Instruction ProcessDDOpCode() private Instruction ProcessFDOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = GetNextInstruction(); + var secondOpCode = _pc.GetNextInstruction(); if (secondOpCode == 0xCB) { // Not a normal instruction, but a special instruction @@ -337,7 +332,7 @@ private Instruction ProcessFDOpCode() private Instruction ProcessEDOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = GetNextInstruction(); + var secondOpCode = _pc.GetNextInstruction(); if (!_edInstructions.TryGetValue(secondOpCode, out var instruction)) { _cpuLogger.Error($"Unhandled 0xED instruction - {secondOpCode:X2}"); From 1c642acca2fa8ace12656d036cafcc42c82f211c Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Thu, 15 Sep 2022 22:45:37 +1000 Subject: [PATCH 02/21] Split out stack handling and stack pointer into own class. This fixes an issue where instructions which loaded a 16 bit register into the stack pointer would not have worked since Load16BitRegisterFrom16BitRegister did not have the parameters named correct so actual source was being set which never updated since not passed by reference anyway. These instructions appear not to be used or exercised by zexdoc. --- Kmse.Console/EmulatorService.cs | 2 +- .../Z80CpuTests/CpuExecutionFixture.cs | 6 +- .../Z80CpuTests/Z80StackManagementFixture.cs | 151 ++++++++++++++++++ Kmse.Core/Memory/IMasterSystemMemory.cs | 2 + Kmse.Core/Memory/MasterSystemMemory.cs | 35 +++- Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs | 9 +- Kmse.Core/Z80/Registers/IZ80Register.cs | 10 ++ Kmse.Core/Z80/Registers/IZ80StackManager.cs | 14 ++ Kmse.Core/Z80/Registers/Z80ProgramCounter.cs | 2 +- Kmse.Core/Z80/Registers/Z80StackManager.cs | 104 ++++++++++++ Kmse.Core/Z80/Support/CpuStatus.cs | 2 +- Kmse.Core/Z80/Z80Cpu.CoreOperations.cs | 40 +---- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 58 +++---- Kmse.Core/Z80/Z80Cpu.cs | 14 +- 14 files changed, 359 insertions(+), 90 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs create mode 100644 Kmse.Core/Z80/Registers/IZ80Register.cs create mode 100644 Kmse.Core/Z80/Registers/IZ80StackManager.cs create mode 100644 Kmse.Core/Z80/Registers/Z80StackManager.cs diff --git a/Kmse.Console/EmulatorService.cs b/Kmse.Console/EmulatorService.cs index 3718d05..c2fa92e 100644 --- a/Kmse.Console/EmulatorService.cs +++ b/Kmse.Console/EmulatorService.cs @@ -218,7 +218,7 @@ private string GetRegistersAsString(CpuStatus status) data += $"IX: {status.Ix.Word:X4} "; data += $"IY: {status.Iy.Word:X4} "; data += $"PC: {status.Pc:X4} "; - data += $"SP: {status.StackPointer.Word:X4} "; + data += $"SP: {status.StackPointer:X4} "; data += $"I: {status.IRegister:X2} "; data += $"R: {status.RRegister:X2} "; data += $"Halt: {status.Halted}"; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index 42b9701..29d2e4b 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -52,7 +52,7 @@ public void WhenResettingCpu() status.IRegister.Should().Be(0); status.RRegister.Should().Be(0); - status.StackPointer.Word.Should().Be(0xDFF0); + status.StackPointer.Should().Be(0xDFF0); status.Af.Word.Should().Be(0); status.Bc.Word.Should().Be(0); status.De.Word.Should().Be(0); @@ -127,7 +127,7 @@ public void WhenExecutingAndNmiSet() status.Pc.Should().Be(0x66); // Pc put onto stack - status.StackPointer.Word.Should().Be(0xDFEE); + status.StackPointer.Should().Be(0xDFEE); // Wrote current PC (0x00) onto stack _memory.Received(1)[0xDFEF] = 0x00; _memory.Received(1)[0xDFEE] = 0x01; @@ -179,7 +179,7 @@ public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) status.Pc.Should().Be(0x38); // Pc put onto stack - status.StackPointer.Word.Should().Be(0xDFEE); + status.StackPointer.Should().Be(0xDFEE); // Wrote current PC (0x00) onto stack _memory.Received(1)[0xDFEF] = 0x00; _memory.Received(1)[0xDFEE] = 0x03; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs new file mode 100644 index 0000000..4c67356 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -0,0 +1,151 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Support; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests; + +public class Z80StackManagementFixture +{ + private ICpuLogger _cpuLogger; + private IMasterSystemMemory _memory; + private Z80StackManager _stackManager; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _cpuLogger = Substitute.For(); + _stackManager = new Z80StackManager(_memory, _cpuLogger); + + _memory.GetMinimumAvailableMemorySize().Returns(100); + _memory.GetMaximumAvailableMemorySize().Returns(0x5000); + _stackManager.Reset(); + } + + [Test] + public void WhenResetThenValueIsTopOfMemoryStack() + { + _stackManager.Reset(); + _stackManager.GetValue().Should().Be(0xDFF0); + _stackManager.AsRegister().Word.Should().Be(0xDFF0); + } + + [Test] + public void WhenSettingStackPointerThenValueIsUpdated() + { + _stackManager.SetStackPointer(0x1235); + _stackManager.GetValue().Should().Be(0x1235); + _stackManager.AsRegister().Word.Should().Be(0x1235); + } + + [Test] + public void WhenSettingStackPointerFromDataInMemoryThenValueIsUpdated() + { + _memory[0x1122].Returns((byte)0x56); + _memory[0x1123].Returns((byte)0x27); + _stackManager.SetStackPointerFromDataInMemory(0x1122); + + _stackManager.GetValue().Should().Be(0x2756); + _stackManager.AsRegister().Word.Should().Be(0x2756); + } + + [Test] + public void WhenStackPointerIncrementedThenValueIsUpdated() + { + _stackManager.SetStackPointer(0x1133); + _stackManager.IncrementStackPointer(); + _stackManager.GetValue().Should().Be(0x1134); + _stackManager.AsRegister().Word.Should().Be(0x1134); + } + + [Test] + public void WhenStackPointerDecrementedThenValueIsUpdated() + { + _stackManager.SetStackPointer(0x1133); + _stackManager.DecrementStackPointer(); + _stackManager.GetValue().Should().Be(0x1132); + _stackManager.AsRegister().Word.Should().Be(0x1132); + } + + [Test] + public void WhenStackPointerIncrementedWhichOverflowsThenThrowsException() + { + _memory.GetMaximumAvailableMemorySize().Returns(1000); + _stackManager.Reset(); + _stackManager.SetStackPointer(1000); + var action = () => _stackManager.IncrementStackPointer(); + action.Should().Throw(); + } + + [Test] + public void WhenStackPointerDecrementedWhichOverflowsThenThrowsException() + { + _memory.GetMinimumAvailableMemorySize().Returns(100); + _stackManager.Reset(); + _stackManager.SetStackPointer(100); + var action = () => _stackManager.DecrementStackPointer(); + action.Should().Throw(); + } + + [Test] + public void WhenPushingRegisterToStackThenValueIsWrittenToMemoryAtStackPointerAddressAndPointerDecremented() + { + var register = new Z80Register + { + Word = 0x1234 + }; + _stackManager.SetStackPointer(1000); + _stackManager.PushRegisterToStack(register); + + _memory.Received()[999] = 0x12; + _memory.Received()[998] = 0x34; + + _stackManager.GetValue().Should().Be(998); + _stackManager.AsRegister().Word.Should().Be(998); + } + + [Test] + public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointerIncremented() + { + _memory[1000].Returns((byte)0x23); + _memory[1001].Returns((byte)0x67); + + var register = new Z80Register + { + Word = 0 + }; + _stackManager.SetStackPointer(1000); + _stackManager.PopRegisterFromStack(ref register); + + register.Word.Should().Be(0x6723); + + _stackManager.GetValue().Should().Be(1002); + _stackManager.AsRegister().Word.Should().Be(1002); + } + + [Test] + public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueIsReturnedAndStackpointerUnchanged() + { + _memory[1000].Returns((byte)0x23); + _memory[1001].Returns((byte)0x47); + + var register = new Z80Register + { + Word = 0x1234 + }; + _stackManager.SetStackPointer(1000); + _stackManager.SwapRegisterWithStackPointerLocation(ref register); + + register.Word.Should().Be(0x4723); + + _memory.Received()[1000] = 0x34; + _memory.Received()[1001] = 0x12; + + _stackManager.GetValue().Should().Be(1000); + _stackManager.AsRegister().Word.Should().Be(1000); + } +} \ No newline at end of file diff --git a/Kmse.Core/Memory/IMasterSystemMemory.cs b/Kmse.Core/Memory/IMasterSystemMemory.cs index f1275cd..5956df4 100644 --- a/Kmse.Core/Memory/IMasterSystemMemory.cs +++ b/Kmse.Core/Memory/IMasterSystemMemory.cs @@ -6,6 +6,8 @@ public interface IMasterSystemMemory { void LoadCartridge(IMasterSystemCartridge masterSystemCartridge); byte this[ushort address] { get; set; } + int GetMaximumAvailableMemorySize(); + int GetMinimumAvailableMemorySize(); // TODO: Add methods to load/save current RAM memory for loading/saving games } \ No newline at end of file diff --git a/Kmse.Core/Memory/MasterSystemMemory.cs b/Kmse.Core/Memory/MasterSystemMemory.cs index 0e13101..3050179 100644 --- a/Kmse.Core/Memory/MasterSystemMemory.cs +++ b/Kmse.Core/Memory/MasterSystemMemory.cs @@ -89,6 +89,26 @@ public byte this[ushort address] set => WriteMemory(address, value); } + public int GetMaximumAvailableMemorySize() + { + return _internalRAM.Length; + } + + public int GetMinimumAvailableMemorySize() + { + if (IsPagingEnabled()) + { + // If paging enabled, we can access RAM down to the start of the 3rd memory slot + // since being used as RAM bank + return MemorySlot3; + } + else + { + // If paging not enabled, then all three memory slots are for ROM usage only so cannot be accessed + return MemorySlot3 + MemoryPageSize; + } + } + private void Reset() { _oneMegCartridge = false; @@ -100,6 +120,11 @@ private void Reset() _thirdBankPage = 2; } + private bool IsPagingEnabled() + { + return Bitwise.IsSet(_pagingControl, 3); + } + private byte ReadRam(ushort address) { var data = _internalRAM.Span[address]; @@ -152,7 +177,7 @@ private void WriteMemory(ushort address, byte data) return; // Allow writing to 3rd slot if RAM bank is mapped to slot 3 - case < MemorySlot3 + MemoryPageSize when Bitwise.IsSet(_pagingControl, 3): + case < MemorySlot3 + MemoryPageSize when IsPagingEnabled(): if (_currentRamBank is 0 or 1) { WriteRamBank(_currentRamBank, (ushort)(address & 0x3FFF), data); @@ -176,8 +201,10 @@ private void WriteMemory(ushort address, byte data) { // Writing memory control page registers if (_isCodeMasters) + { throw new NotImplementedException("Codemasters ROM not supported"); - + } + // Set memory page since writing to memory control registers WriteToMemoryControlRegisters(address, data); } @@ -186,11 +213,15 @@ private void WriteMemory(ushort address, byte data) // RAM from $C000 -$DFFF is mirrored to $E000 - $FFFF if (address < 0xE000) + { // Anything written to normal memory gets written to mirror, hence add mirror offset WriteRam((ushort)(address + MirrorOffset), data); + } else + { // Anything written to mirror gets written to original memory, hence minus mirror offset WriteRam((ushort)(address - MirrorOffset), data); + } } private byte ReadMemory(ushort address) diff --git a/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs index 19e75e2..dc94551 100644 --- a/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs @@ -1,12 +1,7 @@ -using Kmse.Core.Z80.Support; +namespace Kmse.Core.Z80.Registers; -namespace Kmse.Core.Z80.Registers; - -public interface IZ80ProgramCounter +public interface IZ80ProgramCounter : IZ80Register { - void Reset(); - ushort GetValue(); - Z80Register AsRegister(); byte GetNextInstruction(); byte GetNextDataByte(); ushort GetNextTwoDataBytes(); diff --git a/Kmse.Core/Z80/Registers/IZ80Register.cs b/Kmse.Core/Z80/Registers/IZ80Register.cs new file mode 100644 index 0000000..acfe767 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ80Register.cs @@ -0,0 +1,10 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ80Register +{ + public void Reset(); + public ushort GetValue(); + public Z80Register AsRegister(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ80StackManager.cs b/Kmse.Core/Z80/Registers/IZ80StackManager.cs new file mode 100644 index 0000000..dd4eb94 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ80StackManager.cs @@ -0,0 +1,14 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ80StackManager : IZ80Register +{ + void IncrementStackPointer(); + void DecrementStackPointer(); + void PushRegisterToStack(Z80Register register); + void PopRegisterFromStack(ref Z80Register register); + void SwapRegisterWithStackPointerLocation(ref Z80Register register); + void SetStackPointer(ushort value); + void SetStackPointerFromDataInMemory(ushort memoryLocation); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs index 240fcd3..0bbe614 100644 --- a/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs @@ -52,7 +52,7 @@ public ushort GetNextTwoDataBytes() } /// - /// Set program counter to new value, but don't save the old value to the stack + /// Set program counter to new value /// /// New address to set PC to public void SetProgramCounter(ushort address) diff --git a/Kmse.Core/Z80/Registers/Z80StackManager.cs b/Kmse.Core/Z80/Registers/Z80StackManager.cs new file mode 100644 index 0000000..f101343 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z80StackManager.cs @@ -0,0 +1,104 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Support; +using System; + +namespace Kmse.Core.Z80.Registers; + +public class Z80StackManager : IZ80StackManager +{ + private readonly IMasterSystemMemory _memory; + private readonly ICpuLogger _cpuLogger; + private Z80Register _stackPointer; + private int _maximumMemorySize; + + public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger) + { + _memory = memory; + _cpuLogger = cpuLogger; + Reset(); + } + + public void Reset() + { + // Stack pointer starts at highest point in RAM + // but various hardware register control writes, generally we set to 0xDFF0 + // https://www.smspower.org/Development/Stack + _stackPointer.Word = 0xDFF0; + _maximumMemorySize = _memory.GetMaximumAvailableMemorySize(); + } + + public ushort GetValue() + { + return _stackPointer.Word; + } + + public Z80Register AsRegister() + { + return _stackPointer; + } + + public void IncrementStackPointer() + { + if (_stackPointer.Word >= _maximumMemorySize) + { + throw new InvalidOperationException($"Cannot increment Stack Pointer higher than available RAM - {_maximumMemorySize} bytes"); + } + + _stackPointer.Word++; + } + + public void DecrementStackPointer() + { + // We check this each time instead of caching since this can change if RAM banking is enabled + // Although it is unlikely any ROM is going to use so much stack that it basically uses all the RAM + if (_stackPointer.Word <= _memory.GetMinimumAvailableMemorySize()) + { + throw new InvalidOperationException($"Cannot decrement Stack Pointer lower than available RAM - {_memory.GetMinimumAvailableMemorySize()} bytes"); + } + _stackPointer.Word--; + } + + public void PushRegisterToStack(Z80Register register) + { + var oldPointer = _stackPointer.Word; + var currentPointer = _stackPointer.Word; + _memory[--currentPointer] = register.High; + _memory[--currentPointer] = register.Low; + _stackPointer.Word = currentPointer; + _cpuLogger.Debug($"Push to stack - Old - {oldPointer}, New = {_stackPointer.Word}"); + } + + public void PopRegisterFromStack(ref Z80Register register) + { + var oldPointer = _stackPointer.Word; + var currentPointer = _stackPointer.Word; + register.Low = _memory[currentPointer++]; + register.High = _memory[currentPointer++]; + _stackPointer.Word = currentPointer; + _cpuLogger.Debug($"Pop from stack - Old - {oldPointer}, New = {_stackPointer.Word}"); + } + + public void SwapRegisterWithStackPointerLocation(ref Z80Register register) + { + var currentRegisterDataLow = register.Low; + var currentRegisterDataHigh = register.High; + register.Low = _memory[_stackPointer.Word]; + register.High = _memory[(ushort)(_stackPointer.Word + 1)]; + + _memory[_stackPointer.Word] = currentRegisterDataLow; + _memory[(ushort)(_stackPointer.Word + 1)] = currentRegisterDataHigh; + } + + public void SetStackPointer(ushort value) + { + _stackPointer.Word = value; + } + + public void SetStackPointerFromDataInMemory(ushort memoryLocation) + { + _stackPointer.Low = _memory[memoryLocation]; + memoryLocation++; + _stackPointer.High = _memory[memoryLocation]; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Support/CpuStatus.cs b/Kmse.Core/Z80/Support/CpuStatus.cs index 41171d3..59ed2db 100644 --- a/Kmse.Core/Z80/Support/CpuStatus.cs +++ b/Kmse.Core/Z80/Support/CpuStatus.cs @@ -16,7 +16,7 @@ public class CpuStatus public Z80Register Ix { get; init; } public Z80Register Iy { get; init; } public ushort Pc { get; init; } - public Z80Register StackPointer { get; init; } + public ushort StackPointer { get; init; } public byte IRegister { get; init; } public byte RRegister { get; init; } public bool InterruptFlipFlop1 { get; init; } diff --git a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs index 08ec045..285b489 100644 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs @@ -45,7 +45,7 @@ private bool IsFlagSet(Z80StatusFlags flags) private void SaveAndUpdateProgramCounter(ushort address) { // Storing PC in Stack so can resume later - PushRegisterToStack(_pc.AsRegister()); + _stack.PushRegisterToStack(_pc.AsRegister()); // Update PC to execute from new address _pc.SetProgramCounter(address); @@ -60,30 +60,10 @@ private void SetProgramCounterFromRegister(Z80Register register) private void ResetProgramCounterFromStack() { var register = new Z80Register(); - PopRegisterFromStack(ref register); + _stack.PopRegisterFromStack(ref register); _pc.SetProgramCounter(register.Word); } - private void PushRegisterToStack(Z80Register register) - { - var oldPointer = _stackPointer.Word; - var currentPointer = _stackPointer.Word; - _memory[--currentPointer] = register.High; - _memory[--currentPointer] = register.Low; - _stackPointer.Word = currentPointer; - _cpuLogger.Debug($"Push to stack - Old - {oldPointer}, New = {_stackPointer.Word}"); - } - - private void PopRegisterFromStack(ref Z80Register register) - { - var oldPointer = _stackPointer.Word; - var currentPointer = _stackPointer.Word; - register.Low = _memory[currentPointer++]; - register.High = _memory[currentPointer++]; - _stackPointer.Word = currentPointer; - _cpuLogger.Debug($"Pop from stack - Old - {oldPointer}, New = {_stackPointer.Word}"); - } - private void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) { if (IsFlagSet(flag)) @@ -234,17 +214,6 @@ private void SwapRegisters(ref Z80Register register1, ref Z80Register register2) (register1.Word, register2.Word) = (register2.Word, register1.Word); } - private void SwapRegisterWithStackPointerLocation(ref Z80Register register) - { - var currentRegisterDataLow = register.Low; - var currentRegisterDataHigh = register.High; - register.Low = _memory[_stackPointer.Word]; - register.High = _memory[(ushort)(_stackPointer.Word + 1)]; - - _memory[_stackPointer.Word] = currentRegisterDataLow; - _memory[(ushort)(_stackPointer.Word + 1)] = currentRegisterDataHigh; - } - private void LoadRR(byte opCode) { // LD r,r' is 0 1 r r r r' r' r' @@ -358,11 +327,6 @@ private void LoadValueIntoRegisterMemoryLocation(byte value, Z80Register registe _memory[(ushort)(register.Word + offset)] = value; } - private void Load16BitRegisterFrom16BitRegister(ref Z80Register source, Z80Register destination) - { - destination.Word = source.Word; - } - private void Load8BitRegisterFrom8BitRegister(byte sourceData, ref byte destination) { destination = sourceData; diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index c269cc3..be99509 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -201,16 +201,16 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x09, 11, "ADD HL,BC", "Add BC to HL", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl); }); AddStandardInstruction(0x19, 11, "ADD HL,DE", "Add DE to HL", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl); }); AddStandardInstruction(0x29, 11, "ADD HL,HL", "Add HL to HL", _ => { Add16BitRegisterTo16BitRegister(_hl, ref _hl); }); - AddStandardInstruction(0x39, 11, "ADD HL,SP", "Add SP to HL", _ => { Add16BitRegisterTo16BitRegister(_stackPointer, ref _hl); }); + AddStandardInstruction(0x39, 11, "ADD HL,SP", "Add SP to HL", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _hl); }); AddDoubleByteInstruction(0xDD, 0x19, 15, "ADD IX,DE", "Add DE to IX", _ => { Add16BitRegisterTo16BitRegister(_de, ref _ix); }); AddDoubleByteInstruction(0xDD, 0x29, 15, "ADD IX,IX", "Add IX to IX", _ => { Add16BitRegisterTo16BitRegister(_ix, ref _ix); }); - AddDoubleByteInstruction(0xDD, 0x39, 15, "ADD IX,SP", "Add SP to IX", _ => { Add16BitRegisterTo16BitRegister(_stackPointer, ref _ix); }); + AddDoubleByteInstruction(0xDD, 0x39, 15, "ADD IX,SP", "Add SP to IX", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _ix); }); AddDoubleByteInstruction(0xDD, 0x09, 15, "ADD IX,BC", "Add BC to IX", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _ix); }); AddDoubleByteInstruction(0xFD, 0x09, 15, "ADD IY,BC", "Add BC to IY", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _iy); }); AddDoubleByteInstruction(0xFD, 0x19, 15, "ADD IY,DE", "Add DE to IY", _ => { Add16BitRegisterTo16BitRegister(_de, ref _iy); }); AddDoubleByteInstruction(0xFD, 0x29, 15, "ADD IY,IY", "Add IY to IY", _ => { Add16BitRegisterTo16BitRegister(_iy, ref _iy); }); - AddDoubleByteInstruction(0xFD, 0x39, 15, "ADD IY,SP", "Add SP to IY", _ => { Add16BitRegisterTo16BitRegister(_stackPointer, ref _iy); }); + AddDoubleByteInstruction(0xFD, 0x39, 15, "ADD IY,SP", "Add SP to IY", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _iy); }); AddStandardInstruction(0x86, 4, "ADD A,(HL)", "Add Data at memory location from HL to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_hl, 0, ref _af.High); }); AddDoubleByteInstruction(0xDD, 0x86, 19, "ADD A,(IX+d)", "Add Data at memory location from IX + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High); }); @@ -232,7 +232,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xED, 0x4A, 15, "ADC HL,BC", "Add BC to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x5A, 15, "ADC HL,DE", "Add DE to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x6A, 15, "ADC HL,HL", "Add HL to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_hl, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x7A, 15, "ADC HL,SP", "Add SP to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_stackPointer, ref _hl, true); }); + AddDoubleByteInstruction(0xED, 0x7A, 15, "ADC HL,SP", "Add SP to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _hl, true); }); AddStandardInstruction(0x97, 4, "SUB A, A", "Subtract A from A", _ => { SubtractValueFrom8BitRegister(_af.High, ref _af.High); }); AddStandardInstruction(0x90, 4, "SUB A, B", "Subtract B from A", _ => { SubtractValueFrom8BitRegister(_bc.High, ref _af.High); }); @@ -263,7 +263,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_bc, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_de, ref _hl, true); }); AddDoubleByteInstruction(0xED, 0x62, 15, "SBC HL,HL", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_hl, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_stackPointer, ref _hl, true); }); + AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_stack.AsRegister(), ref _hl, true); }); AddStandardInstruction(0xBF, 4, "CP A", "Compare A to A", _ => { Compare8Bit(_af.High, _af.High); }); AddStandardInstruction(0xB8, 4, "CP B", "Compare B to A", _ => { Compare8Bit(_bc.High, _af.High); }); @@ -386,7 +386,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x3, 6, "INC BC", "Increment BC", _ => { Increment16Bit(ref _bc); }); AddStandardInstruction(0x13, 6, "INC DE", "Increment DE", _ => { Increment16Bit(ref _de); }); AddStandardInstruction(0x23, 6, "INC HL", "Increment HL", _ => { Increment16Bit(ref _hl); }); - AddStandardInstruction(0x33, 6, "INC SP", "Increment SP", _ => { Increment16Bit(ref _stackPointer); }); + AddStandardInstruction(0x33, 6, "INC SP", "Increment SP", _ => { _stack.IncrementStackPointer(); }); AddDoubleByteInstruction(0xDD, 0x23, 10, "INC IX", "Increment", _ => { Increment16Bit(ref _ix); }); AddDoubleByteInstruction(0xFD, 0x23, 10, "INC IY", "Increment", _ => { Increment16Bit(ref _iy); }); @@ -405,7 +405,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x0B, 6, "DEC BC", "Decrement BC", _ => { Decrement16Bit(ref _bc); }); AddStandardInstruction(0x1B, 6, "DEC DE", "Decrement DE", _ => { Decrement16Bit(ref _de); }); AddStandardInstruction(0x2B, 6, "DEC HL", "Decrement HL", _ => { Decrement16Bit(ref _hl); }); - AddStandardInstruction(0x3B, 6, "DEC SP", "Decrement SP", _ => { Decrement16Bit(ref _stackPointer); }); + AddStandardInstruction(0x3B, 6, "DEC SP", "Decrement SP", _ => { _stack.DecrementStackPointer(); }); AddDoubleByteInstruction(0xDD, 0x2B, 10, "DEC IX", "Decrement IX", _ => { Decrement16Bit(ref _ix); }); AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { Decrement16Bit(ref _iy); }); @@ -670,9 +670,9 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.Low, _pc.GetNextDataByte()); }); - AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { Load16BitRegisterFrom16BitRegister(ref _stackPointer, _hl); }); - AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { Load16BitRegisterFrom16BitRegister(ref _stackPointer, _ix); }); - AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { Load16BitRegisterFrom16BitRegister(ref _stackPointer, _iy); }); + AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { _stack.SetStackPointer(_hl.Word); }); + AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { _stack.SetStackPointer(_ix.Word); }); + AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { _stack.SetStackPointer(_iy.Word); }); AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _iRegister); }); AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _rRegister); }); @@ -799,14 +799,14 @@ private void PopulateLoadAndExchangeInstructions() AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { LoadInto16BitRegisterFromMemory(ref _hl, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0x4B, 20, "LD BC, (NN)", "Load value at memory location NN into BC", _ => { LoadInto16BitRegisterFromMemory(ref _bc, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0x5B, 20, "LD DE, (NN)", "Load value at memory location NN into DE", _ => { LoadInto16BitRegisterFromMemory(ref _de, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { LoadInto16BitRegisterFromMemory(ref _stackPointer, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { _stack.SetStackPointerFromDataInMemory(_pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xDD, 0x2A, 20, "LD IX, (NN)", "Load value at memory location NN into IX", _ => { LoadInto16BitRegisterFromMemory(ref _ix, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xFD, 0x2A, 20, "LD IY, (NN)", "Load value at memory location NN into IY", _ => { LoadInto16BitRegisterFromMemory(ref _iy, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { LoadValueInto16BitRegister(ref _bc, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { LoadValueInto16BitRegister(ref _de, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { LoadValueInto16BitRegister(ref _hl, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { LoadValueInto16BitRegister(ref _stackPointer, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { _stack.SetStackPointer(_pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { LoadValueInto16BitRegister(ref _ix, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { LoadValueInto16BitRegister(ref _iy, _pc.GetNextTwoDataBytes()); }); @@ -816,7 +816,7 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { Save16BitRegisterToMemory(_de, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { Save16BitRegisterToMemory(_ix, _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { Save16BitRegisterToMemory(_iy, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { Save16BitRegisterToMemory(_stackPointer, _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { Save16BitRegisterToMemory(_stack.AsRegister(), _pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => { @@ -904,29 +904,29 @@ private void PopulateLoadAndExchangeInstructions() } }); - AddStandardInstruction(0xF5, 11, "PUSH AF", "Push AF", _ => { PushRegisterToStack(_af); }); - AddStandardInstruction(0xC5, 11, "PUSH BC", "Push BC", _ => { PushRegisterToStack(_bc); }); - AddStandardInstruction(0xD5, 11, "PUSH DE", "Push DE", _ => { PushRegisterToStack(_de); }); - AddStandardInstruction(0xE5, 11, "PUSH HL", "Push HL", _ => { PushRegisterToStack(_hl); }); - AddDoubleByteInstruction(0xDD, 0xE5, 15, "PUSH IX", "Push IX", _ => { PushRegisterToStack(_ix); }); - AddDoubleByteInstruction(0xFD, 0xE5, 15, "PUSH IY", "Push IY", _ => { PushRegisterToStack(_iy); }); - - AddStandardInstruction(0xF1, 10, "POP AF", "Pop AF from Stack", _ => { PopRegisterFromStack(ref _af); }); - AddStandardInstruction(0xC1, 10, "POP BC", "Pop BC from Stack", _ => { PopRegisterFromStack(ref _bc); }); - AddStandardInstruction(0xD1, 10, "POP DE", "Pop DE from Stack", _ => { PopRegisterFromStack(ref _de); }); - AddStandardInstruction(0xE1, 10, "POP HL", "Pop HL from Stack", _ => { PopRegisterFromStack(ref _hl); }); - AddDoubleByteInstruction(0xDD, 0xE1, 14, "POP IX", "Pop IX from Stack", _ => { PopRegisterFromStack(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0xE1, 14, "POP IY", "Pop IY from Stack", _ => { PopRegisterFromStack(ref _iy); }); + AddStandardInstruction(0xF5, 11, "PUSH AF", "Push AF", _ => { _stack.PushRegisterToStack(_af); }); + AddStandardInstruction(0xC5, 11, "PUSH BC", "Push BC", _ => { _stack.PushRegisterToStack(_bc); }); + AddStandardInstruction(0xD5, 11, "PUSH DE", "Push DE", _ => { _stack.PushRegisterToStack(_de); }); + AddStandardInstruction(0xE5, 11, "PUSH HL", "Push HL", _ => { _stack.PushRegisterToStack(_hl); }); + AddDoubleByteInstruction(0xDD, 0xE5, 15, "PUSH IX", "Push IX", _ => { _stack.PushRegisterToStack(_ix); }); + AddDoubleByteInstruction(0xFD, 0xE5, 15, "PUSH IY", "Push IY", _ => { _stack.PushRegisterToStack(_iy); }); + + AddStandardInstruction(0xF1, 10, "POP AF", "Pop AF from Stack", _ => { _stack.PopRegisterFromStack(ref _af); }); + AddStandardInstruction(0xC1, 10, "POP BC", "Pop BC from Stack", _ => { _stack.PopRegisterFromStack(ref _bc); }); + AddStandardInstruction(0xD1, 10, "POP DE", "Pop DE from Stack", _ => { _stack.PopRegisterFromStack(ref _de); }); + AddStandardInstruction(0xE1, 10, "POP HL", "Pop HL from Stack", _ => { _stack.PopRegisterFromStack(ref _hl); }); + AddDoubleByteInstruction(0xDD, 0xE1, 14, "POP IX", "Pop IX from Stack", _ => { _stack.PopRegisterFromStack(ref _ix); }); + AddDoubleByteInstruction(0xFD, 0xE1, 14, "POP IY", "Pop IY from Stack", _ => { _stack.PopRegisterFromStack(ref _iy); }); } private void PopulateExchangeBlockTransferAndSearchInstructions() { - AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { SwapRegisterWithStackPointerLocation(ref _hl); }); + AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _hl); }); AddStandardInstruction(0x8, 4, "EX AF,AF'", "Exchange AF and AF Shadow", _ => { SwapRegisters(ref _af, ref _afShadow); }); AddStandardInstruction(0xEB, 4, "EX DE,HL", "Exchange DE and HL", _ => { SwapRegisters(ref _de, ref _hl); }); AddStandardInstruction(0xD9, 4, "EXX", "Exchange BC, DE, HL with Shadow Registers", _ => { SwapRegisters(ref _bc, ref _bcShadow); SwapRegisters(ref _de, ref _deShadow); SwapRegisters(ref _hl, ref _hlShadow); }); - AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { SwapRegisterWithStackPointerLocation(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { SwapRegisterWithStackPointerLocation(ref _iy); }); + AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _ix); }); + AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _iy); }); } private void PopulateInputOutputInstructions() diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index dd34018..87cb61a 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -22,10 +22,10 @@ public partial class Z80Cpu : IZ80Cpu private Z80Register _af, _bc, _de, _hl; private Z80Register _afShadow, _bcShadow, _deShadow, _hlShadow; private Z80Register _ix, _iy; - private Z80Register _stackPointer; private byte _iRegister, _rRegister; private IZ80ProgramCounter _pc; + private IZ80StackManager _stack; /// /// Disables interrupts from being accepted if set to False @@ -51,7 +51,6 @@ public Z80Cpu(ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger) _hlShadow = new Z80Register(); _ix = new Z80Register(); _iy = new Z80Register(); - _stackPointer = new Z80Register(); _iRegister = 0; _rRegister = 0; PopulateInstructions(); @@ -62,7 +61,10 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _cpuLogger.Debug("Initializing CPU"); _memory = memory; _io = io; + + // TODO: Need to create these indirectly to allow mock interfaces to be injected for testing _pc = new Z80ProgramCounter(memory, _instructionLogger); + _stack = new Z80StackManager(memory, _cpuLogger); } public CpuStatus GetStatus() @@ -83,7 +85,7 @@ public CpuStatus GetStatus() Ix = _ix, Iy = _iy, Pc = _pc.GetValue(), - StackPointer = _stackPointer, + StackPointer = _stack.GetValue(), IRegister = _iRegister, RRegister = _rRegister, InterruptFlipFlop1 = _interruptFlipFlop1, @@ -102,6 +104,7 @@ public void Reset() _currentCycleCount = 0; _pc.Reset(); + _stack.Reset(); _halted = false; _interruptFlipFlop1 = false; @@ -111,11 +114,6 @@ public void Reset() _iRegister = 0; _rRegister = 0; - // Stack pointer starts at highest point in RAM - // but various hardware register control writes, generally we set to 0xDFF0 - // https://www.smspower.org/Development/Stack - _stackPointer.Word = 0xDFF0; - _af.Word = 0x00; _bc.Word = 0x00; _de.Word = 0x00; From ac6823758f4a08dcd2d0b099af6adddf71312e13 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 17 Sep 2022 22:14:08 +1000 Subject: [PATCH 03/21] Create structure for registers to be split out. This allows for true 16 bit registers as well as 8 bit registers which can be combined into 16 bit registers. This creates a separate class for each 16 bit register, for the 16 bit combined registers (composed of two 8 bit registers) and the special purpose registers These use a common base class but have their own interfaces which allow enforcing of certain method calls where only allowed on a specific register, for example loading memory refresh register into A which is handled uniquely to other load operations The majority of the core, math and logic operations should be able to be moved into the register classes (or the base register class if shared) All the instructions are broken currently until the operations are refactored into the register classes. --- .../Z80CpuTests/Z80ProgramCounterFixture.cs | 41 +- .../Z80CpuTests/Z80StackManagementFixture.cs | 67 ++-- .../Z80/Registers/General/IZ80Accumulator.cs | 9 + .../Z80/Registers/General/IZ80AfRegister.cs | 10 + .../Z80/Registers/General/IZ80BcRegister.cs | 7 + .../Z80/Registers/General/IZ80DeRegister.cs | 7 + .../Z80/Registers/General/IZ80FlagsManager.cs | 13 + .../Z80/Registers/General/IZ80HlRegister.cs | 7 + .../Z80/Registers/General/Z80Accumulator.cs | 85 +++++ .../Z80/Registers/General/Z80AfRegister.cs | 18 + .../Z80/Registers/General/Z80BcRegister.cs | 18 + .../Z80/Registers/General/Z80DeRegister.cs | 18 + .../Z80/Registers/General/Z80FlagsManager.cs | 61 +++ .../Z80/Registers/General/Z80HlRegister.cs | 18 + .../Registers/IZ8016BitCombinedRegister.cs | 10 + Kmse.Core/Z80/Registers/IZ8016BitRegister.cs | 17 + Kmse.Core/Z80/Registers/IZ808BitRegister.cs | 12 + Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs | 26 -- Kmse.Core/Z80/Registers/IZ80Register.cs | 10 - Kmse.Core/Z80/Registers/IZ80StackManager.cs | 14 - .../SpecialPurpose/IZ80IndexRegisterX.cs | 3 + .../SpecialPurpose/IZ80IndexRegisterY.cs | 3 + .../IZ80InterruptPageAddressRegister.cs | 3 + .../IZ80MemoryRefreshRegister.cs | 3 + .../SpecialPurpose/IZ80ProgramCounter.cs | 52 +++ .../SpecialPurpose/IZ80StackManager.cs | 11 + .../SpecialPurpose/Z80IndexRegisterX.cs | 8 + .../SpecialPurpose/Z80IndexRegisterY.cs | 8 + .../Z80InterruptPageAddressRegister.cs | 8 + .../Z80MemoryRefreshRegister.cs | 9 + .../SpecialPurpose/Z80ProgramCounter.cs | 194 ++++++++++ .../SpecialPurpose/Z80StackManager.cs | 83 +++++ .../Registers/Z8016BitCombinedRegisterBase.cs | 82 ++++ .../Z80/Registers/Z8016BitRegisterBase.cs | 60 +++ Kmse.Core/Z80/Registers/Z808BitRegister.cs | 35 ++ Kmse.Core/Z80/Registers/Z80ProgramCounter.cs | 92 ----- Kmse.Core/Z80/Registers/Z80StackManager.cs | 104 ------ Kmse.Core/Z80/Support/Z80Register.cs | 1 + Kmse.Core/Z80/Z80Cpu.CoreOperations.cs | 209 +---------- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 122 +++--- .../Z80/Z80Cpu.MathAndLogicOperations.cs | 352 ++++++++---------- Kmse.Core/Z80/Z80Cpu.cs | 153 +++++--- 42 files changed, 1251 insertions(+), 812 deletions(-) create mode 100644 Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs create mode 100644 Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs create mode 100644 Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80Accumulator.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80AfRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80BcRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80DeRegister.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs create mode 100644 Kmse.Core/Z80/Registers/General/Z80HlRegister.cs create mode 100644 Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs create mode 100644 Kmse.Core/Z80/Registers/IZ8016BitRegister.cs create mode 100644 Kmse.Core/Z80/Registers/IZ808BitRegister.cs delete mode 100644 Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs delete mode 100644 Kmse.Core/Z80/Registers/IZ80Register.cs delete mode 100644 Kmse.Core/Z80/Registers/IZ80StackManager.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80InterruptPageAddressRegister.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80MemoryRefreshRegister.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs create mode 100644 Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs create mode 100644 Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs create mode 100644 Kmse.Core/Z80/Registers/Z808BitRegister.cs delete mode 100644 Kmse.Core/Z80/Registers/Z80ProgramCounter.cs delete mode 100644 Kmse.Core/Z80/Registers/Z80StackManager.cs diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs index 7aab98a..88452a4 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs @@ -1,7 +1,8 @@ using FluentAssertions; using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; -using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; using NSubstitute; using NUnit.Framework; @@ -9,67 +10,71 @@ namespace Kmse.Core.UnitTests.Z80CpuTests; public class Z80ProgramCounterFixture { + private IZ80FlagsManager _flags; private IZ80InstructionLogger _instructionLogger; - private Z80ProgramCounter _programCounter; private IMasterSystemMemory _memory; + private Z80ProgramCounter _programCounter; + private IZ80StackManager _stack; [SetUp] public void Setup() { _memory = Substitute.For(); _instructionLogger = Substitute.For(); - _programCounter = new Z80ProgramCounter(_memory, _instructionLogger); + _flags = Substitute.For(); + _stack = Substitute.For(); + _programCounter = new Z80ProgramCounter(_memory, _instructionLogger, _flags, _stack); } [Test] public void WhenResetThenValueIsZero() { _programCounter.Reset(); - _programCounter.GetValue().Should().Be(0); + _programCounter.Value.Should().Be(0); _programCounter.AsRegister().Word.Should().Be(0); } [Test] public void WhenSettingProgramCounterThenValueIsUpdated() { - _programCounter.SetProgramCounter(0x1234); - _programCounter.GetValue().Should().Be(0x1234); + _programCounter.Set(0x1234); + _programCounter.Value.Should().Be(0x1234); _programCounter.AsRegister().Word.Should().Be(0x1234); } [Test] public void WhenProgramCounterMovedForwardOffsetWhichOverflowsThenWrapsAround() { - _programCounter.SetProgramCounter(ushort.MaxValue); + _programCounter.Set(ushort.MaxValue); _programCounter.MoveProgramCounterForward(3); - _programCounter.GetValue().Should().Be(0x02); + _programCounter.Value.Should().Be(0x02); _programCounter.AsRegister().Word.Should().Be(0x02); } [Test] public void WhenProgramCounterMovedForwardByOffsetThenValueIsUpdated() { - _programCounter.SetProgramCounter(0x1234); + _programCounter.Set(0x1234); _programCounter.MoveProgramCounterForward(10); - _programCounter.GetValue().Should().Be(0x123E); + _programCounter.Value.Should().Be(0x123E); _programCounter.AsRegister().Word.Should().Be(0x123E); } [Test] public void WhenProgramCounterMovedBackwardByOffsetThenValueIsUpdated() { - _programCounter.SetProgramCounter(0x1234); + _programCounter.Set(0x1234); _programCounter.MoveProgramCounterBackward(10); - _programCounter.GetValue().Should().Be(0x122A); + _programCounter.Value.Should().Be(0x122A); _programCounter.AsRegister().Word.Should().Be(0x122A); } [Test] public void WhenProgramCounterMovedBackwardByOffsetWhichOverflowsThenWrapsAround() { - _programCounter.SetProgramCounter(0); + _programCounter.Set(0); _programCounter.MoveProgramCounterBackward(3); - _programCounter.GetValue().Should().Be(ushort.MaxValue-2); + _programCounter.Value.Should().Be(ushort.MaxValue - 2); _programCounter.AsRegister().Word.Should().Be(ushort.MaxValue - 2); } @@ -77,11 +82,11 @@ public void WhenProgramCounterMovedBackwardByOffsetWhichOverflowsThenWrapsAround public void WhenGettingNextDataByteFromMemoryUsingPcValueTheCorrectDataIsReturnedAndPcIncremented() { _memory[0x05].Returns((byte)0x12); - _programCounter.SetProgramCounter(0x05); + _programCounter.Set(0x05); var data = _programCounter.GetNextDataByte(); data.Should().Be(0x12); - _programCounter.GetValue().Should().Be(0x06); + _programCounter.Value.Should().Be(0x06); _programCounter.AsRegister().Word.Should().Be(0x06); _instructionLogger.Received(1).AddOperationData(0x12); @@ -92,12 +97,12 @@ public void WhenGettingNextTwoByteDataFromMemoryUsingPcValueTheCorrectDataIsRetu { _memory[0x05].Returns((byte)0x12); _memory[0x06].Returns((byte)0x34); - _programCounter.SetProgramCounter(0x05); + _programCounter.Set(0x05); var data = _programCounter.GetNextTwoDataBytes(); // Loading byte order is low and then high byte hence swapped order data.Should().Be(0x3412); - _programCounter.GetValue().Should().Be(0x07); + _programCounter.Value.Should().Be(0x07); _programCounter.AsRegister().Word.Should().Be(0x07); _instructionLogger.Received(1).AddOperationData(0x3412); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs index 4c67356..0b4cfaa 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -2,7 +2,7 @@ using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Registers; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Registers.SpecialPurpose; using NSubstitute; using NUnit.Framework; @@ -30,15 +30,15 @@ public void Setup() public void WhenResetThenValueIsTopOfMemoryStack() { _stackManager.Reset(); - _stackManager.GetValue().Should().Be(0xDFF0); + _stackManager.Value.Should().Be(0xDFF0); _stackManager.AsRegister().Word.Should().Be(0xDFF0); } [Test] public void WhenSettingStackPointerThenValueIsUpdated() { - _stackManager.SetStackPointer(0x1235); - _stackManager.GetValue().Should().Be(0x1235); + _stackManager.Set(0x1235); + _stackManager.Value.Should().Be(0x1235); _stackManager.AsRegister().Word.Should().Be(0x1235); } @@ -47,27 +47,27 @@ public void WhenSettingStackPointerFromDataInMemoryThenValueIsUpdated() { _memory[0x1122].Returns((byte)0x56); _memory[0x1123].Returns((byte)0x27); - _stackManager.SetStackPointerFromDataInMemory(0x1122); + _stackManager.SetFromDataInMemory(0x1122); - _stackManager.GetValue().Should().Be(0x2756); + _stackManager.Value.Should().Be(0x2756); _stackManager.AsRegister().Word.Should().Be(0x2756); } [Test] public void WhenStackPointerIncrementedThenValueIsUpdated() { - _stackManager.SetStackPointer(0x1133); + _stackManager.Set(0x1133); _stackManager.IncrementStackPointer(); - _stackManager.GetValue().Should().Be(0x1134); + _stackManager.Value.Should().Be(0x1134); _stackManager.AsRegister().Word.Should().Be(0x1134); } [Test] public void WhenStackPointerDecrementedThenValueIsUpdated() { - _stackManager.SetStackPointer(0x1133); + _stackManager.Set(0x1133); _stackManager.DecrementStackPointer(); - _stackManager.GetValue().Should().Be(0x1132); + _stackManager.Value.Should().Be(0x1132); _stackManager.AsRegister().Word.Should().Be(0x1132); } @@ -76,7 +76,7 @@ public void WhenStackPointerIncrementedWhichOverflowsThenThrowsException() { _memory.GetMaximumAvailableMemorySize().Returns(1000); _stackManager.Reset(); - _stackManager.SetStackPointer(1000); + _stackManager.Set(1000); var action = () => _stackManager.IncrementStackPointer(); action.Should().Throw(); } @@ -86,7 +86,7 @@ public void WhenStackPointerDecrementedWhichOverflowsThenThrowsException() { _memory.GetMinimumAvailableMemorySize().Returns(100); _stackManager.Reset(); - _stackManager.SetStackPointer(100); + _stackManager.Set(100); var action = () => _stackManager.DecrementStackPointer(); action.Should().Throw(); } @@ -94,17 +94,15 @@ public void WhenStackPointerDecrementedWhichOverflowsThenThrowsException() [Test] public void WhenPushingRegisterToStackThenValueIsWrittenToMemoryAtStackPointerAddressAndPointerDecremented() { - var register = new Z80Register - { - Word = 0x1234 - }; - _stackManager.SetStackPointer(1000); + var register = new Test16BitClass(_memory); + register.Set(0x1234); + _stackManager.Set(1000); _stackManager.PushRegisterToStack(register); _memory.Received()[999] = 0x12; _memory.Received()[998] = 0x34; - _stackManager.GetValue().Should().Be(998); + _stackManager.Value.Should().Be(998); _stackManager.AsRegister().Word.Should().Be(998); } @@ -114,16 +112,14 @@ public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointer _memory[1000].Returns((byte)0x23); _memory[1001].Returns((byte)0x67); - var register = new Z80Register - { - Word = 0 - }; - _stackManager.SetStackPointer(1000); - _stackManager.PopRegisterFromStack(ref register); + var register = new Test16BitClass(_memory); + register.Set(0x00); + _stackManager.Set(1000); + _stackManager.PopRegisterFromStack(register); - register.Word.Should().Be(0x6723); + register.Value.Should().Be(0x6723); - _stackManager.GetValue().Should().Be(1002); + _stackManager.Value.Should().Be(1002); _stackManager.AsRegister().Word.Should().Be(1002); } @@ -133,19 +129,22 @@ public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueI _memory[1000].Returns((byte)0x23); _memory[1001].Returns((byte)0x47); - var register = new Z80Register - { - Word = 0x1234 - }; - _stackManager.SetStackPointer(1000); - _stackManager.SwapRegisterWithStackPointerLocation(ref register); + var register = new Test16BitClass(_memory); + register.Set(0x1234); + _stackManager.Set(1000); + _stackManager.SwapRegisterWithDataAtStackPointerAddress(register); - register.Word.Should().Be(0x4723); + register.Value.Should().Be(0x4723); _memory.Received()[1000] = 0x34; _memory.Received()[1001] = 0x12; - _stackManager.GetValue().Should().Be(1000); + _stackManager.Value.Should().Be(1000); _stackManager.AsRegister().Word.Should().Be(1000); } + + private class Test16BitClass : Z8016BitRegisterBase + { + public Test16BitClass(IMasterSystemMemory memory) : base(memory) { } + } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs new file mode 100644 index 0000000..e650a17 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs @@ -0,0 +1,9 @@ +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80Accumulator : IZ808BitRegister +{ + void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status); + void SetFromMemoryRefreshRegister(IZ80MemoryRefreshRegister register, bool interruptFlipFlop2Status); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs new file mode 100644 index 0000000..e2e68cc --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs @@ -0,0 +1,10 @@ +namespace Kmse.Core.Z80.Registers.General; + +/// +/// Provides a 16 bit interface to the combined Accumulator and Flag (AF) register +/// +public interface IZ80AfRegister : IZ8016BitCombinedRegister +{ + IZ80Accumulator Accumulator { get; } + IZ80FlagsManager Flags { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs new file mode 100644 index 0000000..b4730cb --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs @@ -0,0 +1,7 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80BcRegister : IZ8016BitCombinedRegister +{ + IZ808BitRegister B { get; } + IZ808BitRegister C { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs new file mode 100644 index 0000000..7c3054a --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs @@ -0,0 +1,7 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80DeRegister : IZ8016BitCombinedRegister +{ + IZ808BitRegister D { get; } + IZ808BitRegister E { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs new file mode 100644 index 0000000..7e1cafa --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -0,0 +1,13 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80FlagsManager : IZ808BitRegister +{ + void SetFlag(Z80StatusFlags flags); + void ClearFlag(Z80StatusFlags flags); + void InvertFlag(Z80StatusFlags flag); + void SetClearFlagConditional(Z80StatusFlags flags, bool condition); + bool IsFlagSet(Z80StatusFlags flags); + void SetParityFromValue(byte value); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs new file mode 100644 index 0000000..a9ddc3c --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs @@ -0,0 +1,7 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80HlRegister : IZ8016BitCombinedRegister +{ + IZ808BitRegister H { get; } + IZ808BitRegister L { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs new file mode 100644 index 0000000..e70cd2a --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -0,0 +1,85 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80Accumulator : Z808BitRegister, IZ80Accumulator +{ + private readonly IZ80FlagsManager _flags; + + public Z80Accumulator(IZ80FlagsManager flags, IMasterSystemMemory memory) + : base(memory) + { + _flags = flags; + } + + public void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status) + { + LoadSpecial8BitRegisterToAccumulator(register.Value, interruptFlipFlop2Status); + } + + public void SetFromMemoryRefreshRegister(IZ80MemoryRefreshRegister register, bool interruptFlipFlop2Status) + { + LoadSpecial8BitRegisterToAccumulator(register.Value, interruptFlipFlop2Status); + } + + public void RotateLeftDigit(IZ80HlRegister hl) + { + var value = Memory[hl.Value]; + var hll = value & 0x0F; + var hlh = (byte)((value & 0xF0) >> 4); + var al = Value & 0x0F; + var ah = (byte)((Value & 0xF0) >> 4); + + // HL is lower bits of HL copied to high bits + lower 4 bits of A + var newHlValue = (byte)((hll << 4) + al); + // A is higher bits of A left same + higher bits of hl + var newAValue = (byte)((ah << 4) + hlh); + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newAValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + Memory[hl.Value] = newHlValue; + Value = newAValue; + } + + public void RotateRightDigit(IZ80HlRegister hl) + { + var value = Memory[hl.Value]; + var hll = value & 0x0F; + var hlh = (byte)((value & 0xF0) >> 4); + var al = Value & 0x0F; + var ah = (byte)((Value & 0xF0) >> 4); + + // HL is lower bits of HL copied to high bits + lower 4 bits of A + var newHlValue = (byte)((al << 4) + hlh); + // A is higher bits of A left same + higher bits of hl + var newAValue = (byte)((ah << 4) + hll); + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newAValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + Memory[hl.Value] = newHlValue; + Value = newAValue; + } + + private void LoadSpecial8BitRegisterToAccumulator(byte sourceData, bool interruptFlipFlop2Status) + { + Value = sourceData; + + // Check flags since copying from special register into accumulator + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, !Bitwise.IsSet(sourceData, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, sourceData == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, interruptFlipFlop2Status); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs new file mode 100644 index 0000000..985aaa4 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -0,0 +1,18 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80AfRegister : Z8016BitCombinedRegisterBase, IZ80AfRegister +{ + public Z80AfRegister(IMasterSystemMemory memory) + : base(memory) + { + Flags = new Z80FlagsManager(memory); + Accumulator = new Z80Accumulator(Flags, memory); + } + + protected override IZ808BitRegister HighRegister => Accumulator; + protected override IZ808BitRegister LowRegister => Flags; + public IZ80Accumulator Accumulator { get; } + public IZ80FlagsManager Flags { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs new file mode 100644 index 0000000..ec4946e --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs @@ -0,0 +1,18 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80BcRegister : Z8016BitCombinedRegisterBase, IZ80BcRegister +{ + public Z80BcRegister(IMasterSystemMemory memory) + : base(memory) + { + B = new Z808BitRegister(memory); + C = new Z808BitRegister(memory); + } + + protected override IZ808BitRegister HighRegister => B; + protected override IZ808BitRegister LowRegister => C; + public IZ808BitRegister B { get; } + public IZ808BitRegister C { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs new file mode 100644 index 0000000..0f85d52 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs @@ -0,0 +1,18 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80DeRegister : Z8016BitCombinedRegisterBase, IZ80DeRegister +{ + public Z80DeRegister(IMasterSystemMemory memory) + : base(memory) + { + D = new Z808BitRegister(memory); + E = new Z808BitRegister(memory); + } + + protected override IZ808BitRegister HighRegister => D; + protected override IZ808BitRegister LowRegister => E; + public IZ808BitRegister D { get; } + public IZ808BitRegister E { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs new file mode 100644 index 0000000..2cbde59 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -0,0 +1,61 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80FlagsManager : Z808BitRegister, IZ80FlagsManager +{ + public Z80FlagsManager(IMasterSystemMemory memory) : base(memory) { } + + public void SetFlag(Z80StatusFlags flags) + { + Value |= (byte)flags; + } + + public void ClearFlag(Z80StatusFlags flags) + { + Value &= (byte)~flags; + } + + public void InvertFlag(Z80StatusFlags flag) + { + SetClearFlagConditional(flag, !IsFlagSet(flag)); + } + + public void SetClearFlagConditional(Z80StatusFlags flags, bool condition) + { + if (condition) + { + SetFlag(flags); + } + else + { + ClearFlag(flags); + } + } + + public bool IsFlagSet(Z80StatusFlags flags) + { + var currentSetFlags = (Z80StatusFlags)Value & flags; + return currentSetFlags == flags; + } + + public void SetParityFromValue(byte value) + { + // Count the number of 1 bits in the value + // If odd, then clear flag and if even, then set flag + var bitsSet = 0; + for (var i = 0; i < 8; i++) + { + if (Bitwise.IsSet(value, i)) + { + bitsSet++; + } + } + + SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, bitsSet == 0 || bitsSet % 2 == 0); + } + + // TODO: Add generic methods for setting flags for sign, zero, carry to cut down on duplication as much as possible etc +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs new file mode 100644 index 0000000..53541ec --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs @@ -0,0 +1,18 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80HlRegister : Z8016BitCombinedRegisterBase, IZ80HlRegister +{ + public Z80HlRegister(IMasterSystemMemory memory) + : base(memory) + { + H = new Z808BitRegister(memory); + L = new Z808BitRegister(memory); + } + + protected override IZ808BitRegister HighRegister => H; + protected override IZ808BitRegister LowRegister => L; + public IZ808BitRegister H { get; } + public IZ808BitRegister L { get; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs new file mode 100644 index 0000000..fbb7917 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs @@ -0,0 +1,10 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitCombinedRegister : IZ8016BitRegister +{ + public ushort ShadowValue { get; } + public void SwapWithShadow(); + public Z80Register ShadowAsRegister(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs new file mode 100644 index 0000000..92d4dce --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -0,0 +1,17 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitRegister +{ + public ushort Value { get; } + public byte High { get; } + public byte Low { get; } + + public void Reset(); + public void Set(ushort value); + public void SetHigh(byte value); + public void SetLow(byte value); + public void SetFromDataInMemory(ushort address, byte offset = 0); + public Z80Register AsRegister(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ808BitRegister.cs b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs new file mode 100644 index 0000000..b21809e --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs @@ -0,0 +1,12 @@ +namespace Kmse.Core.Z80.Registers; + +public interface IZ808BitRegister +{ + public byte Value { get; } + public byte ShadowValue { get; } + + public void Reset(); + public void Set(byte value); + public void SetFromDataInMemory(ushort address, byte offset = 0); + public void SwapWithShadow(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs deleted file mode 100644 index dc94551..0000000 --- a/Kmse.Core/Z80/Registers/IZ80ProgramCounter.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Kmse.Core.Z80.Registers; - -public interface IZ80ProgramCounter : IZ80Register -{ - byte GetNextInstruction(); - byte GetNextDataByte(); - ushort GetNextTwoDataBytes(); - - /// - /// Set program counter to new value, but don't save the old value to the stack - /// - /// New address to set PC to - void SetProgramCounter(ushort address); - - /// - /// Move program counter forward by the provided value - /// - /// Add this offset to the current PC - void MoveProgramCounterForward(ushort offset); - - /// - /// Move program counter backward by the provided value - /// - /// Subtract this offset from the current PC - void MoveProgramCounterBackward(ushort offset); -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ80Register.cs b/Kmse.Core/Z80/Registers/IZ80Register.cs deleted file mode 100644 index acfe767..0000000 --- a/Kmse.Core/Z80/Registers/IZ80Register.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80.Registers; - -public interface IZ80Register -{ - public void Reset(); - public ushort GetValue(); - public Z80Register AsRegister(); -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ80StackManager.cs b/Kmse.Core/Z80/Registers/IZ80StackManager.cs deleted file mode 100644 index dd4eb94..0000000 --- a/Kmse.Core/Z80/Registers/IZ80StackManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80.Registers; - -public interface IZ80StackManager : IZ80Register -{ - void IncrementStackPointer(); - void DecrementStackPointer(); - void PushRegisterToStack(Z80Register register); - void PopRegisterFromStack(ref Z80Register register); - void SwapRegisterWithStackPointerLocation(ref Z80Register register); - void SetStackPointer(ushort value); - void SetStackPointerFromDataInMemory(ushort memoryLocation); -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs new file mode 100644 index 0000000..c0f0d77 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs @@ -0,0 +1,3 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80IndexRegisterX : IZ8016BitRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs new file mode 100644 index 0000000..fdbe550 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs @@ -0,0 +1,3 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80IndexRegisterY : IZ8016BitRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80InterruptPageAddressRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80InterruptPageAddressRegister.cs new file mode 100644 index 0000000..ec67ede --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80InterruptPageAddressRegister.cs @@ -0,0 +1,3 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80InterruptPageAddressRegister : IZ808BitRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80MemoryRefreshRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80MemoryRefreshRegister.cs new file mode 100644 index 0000000..e78a321 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80MemoryRefreshRegister.cs @@ -0,0 +1,3 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80MemoryRefreshRegister : IZ808BitRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs new file mode 100644 index 0000000..4bd2e90 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -0,0 +1,52 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80ProgramCounter : IZ8016BitRegister +{ + public byte GetNextInstruction(); + public byte GetNextDataByte(); + public ushort GetNextTwoDataBytes(); + + /// + /// Move program counter forward by the provided value + /// + /// Add this offset to the current PC + public void MoveProgramCounterForward(ushort offset); + + /// + /// Move program counter backward by the provided value + /// + /// Subtract this offset from the current PC + public void MoveProgramCounterBackward(ushort offset); + + /// + /// Set the Program Counter but first push the current PC value to the stack + /// + /// New address to set PC to + void SetAndSaveExisting(ushort address); + + /// + /// Set program counter value to register value + /// + /// Z80 register + void Set(Z80Register register); + + /// + /// Set the Program Counter by popping address from top of the stack + /// This will pop two bytes for a 16 bit address + /// + void SetFromStack(); + + void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address); + void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address); + void JumpByOffset(byte offset); + bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status); + void JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset); + void JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset); + void CallIfFlagCondition(Z80StatusFlags flag, ushort address); + void CallIfNotFlagCondition(Z80StatusFlags flag, ushort address); + bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status); + bool ReturnIfFlag(Z80StatusFlags flag); + bool ReturnIfNotFlag(Z80StatusFlags flag); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs new file mode 100644 index 0000000..08450a8 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs @@ -0,0 +1,11 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80StackManager : IZ8016BitRegister +{ + void IncrementStackPointer(); + void DecrementStackPointer(); + void PushRegisterToStack(IZ8016BitRegister register); + + void PopRegisterFromStack(IZ8016BitRegister register); + void SwapRegisterWithDataAtStackPointerAddress(IZ8016BitRegister register); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs new file mode 100644 index 0000000..44c5472 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs @@ -0,0 +1,8 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80IndexRegisterX : Z8016BitRegisterBase, IZ80IndexRegisterX +{ + public Z80IndexRegisterX(IMasterSystemMemory memory) : base(memory) { } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs new file mode 100644 index 0000000..9d8535d --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs @@ -0,0 +1,8 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80IndexRegisterY : Z8016BitRegisterBase, IZ80IndexRegisterY +{ + public Z80IndexRegisterY(IMasterSystemMemory memory) : base(memory) { } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs new file mode 100644 index 0000000..adc0136 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs @@ -0,0 +1,8 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80InterruptPageAddressRegister : Z808BitRegister, IZ80InterruptPageAddressRegister +{ + public Z80InterruptPageAddressRegister(IMasterSystemMemory memory) : base(memory) { } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs new file mode 100644 index 0000000..9423d70 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs @@ -0,0 +1,9 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +// TODO: This probably should be a special base class since this the memory refresh register has alot of restrictions on it +public class Z80MemoryRefreshRegister : Z808BitRegister, IZ80MemoryRefreshRegister +{ + public Z80MemoryRefreshRegister(IMasterSystemMemory memory) : base(memory) { } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs new file mode 100644 index 0000000..6089298 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -0,0 +1,194 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80ProgramCounter : Z8016BitRegisterBase, IZ80ProgramCounter +{ + private readonly IZ80FlagsManager _flags; + private readonly IMasterSystemMemory _memory; + private readonly IZ80StackManager _stack; + private readonly IZ80InstructionLogger _z80InstructionLogger; + private Z80Register _pc; + + public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger, + IZ80FlagsManager flags, IZ80StackManager stack) + : base(memory) + { + _memory = memory; + _z80InstructionLogger = z80InstructionLogger; + _flags = flags; + _stack = stack; + } + + public byte GetNextInstruction() + { + return GetNextByteByProgramCounter(); + } + + public byte GetNextDataByte() + { + var data = GetNextByteByProgramCounter(); + _z80InstructionLogger.AddOperationData(data); + return data; + } + + public ushort GetNextTwoDataBytes() + { + ushort data = GetNextByteByProgramCounter(); + data += (ushort)(GetNextByteByProgramCounter() << 8); + _z80InstructionLogger.AddOperationData(data); + return data; + } + + /// + /// Move program counter forward by the provided value + /// + /// Add this offset to the current PC + public void MoveProgramCounterForward(ushort offset) + { + // If this goes above ushort max, we assume that when PC hits the limit it just wraps around instead of throwing an error or failing + _pc.Word += offset; + } + + /// + /// Move program counter backward by the provided value + /// + /// Subtract this offset from the current PC + public void MoveProgramCounterBackward(ushort offset) + { + // If this goes below zero/negative, we assume that when PC hits -1 it just wraps around instead of throwing an error or failing + _pc.Word -= offset; + } + + public void SetAndSaveExisting(ushort address) + { + // Storing PC in Stack so can resume later + _stack.PushRegisterToStack(this); + + // Update PC to execute from new address + Set(address); + } + + public void Set(Z80Register register) + { + // Update PC to execute from the value of the register + Set(register.Word); + } + + public void SetFromStack() + { + var register = new Z80Register(); + _stack.PopRegisterFromStack(this); + Set(register.Word); + } + + public void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) + { + if (_flags.IsFlagSet(flag)) + { + Set(address); + } + } + + public void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) + { + if (!_flags.IsFlagSet(flag)) + { + Set(address); + } + } + + public void JumpByOffset(byte offset) + { + var newPcLocation = _pc.Word; + + // Range is -126 to +129 so we need a signed version + // However sbyte only goes from -128 to +127 but we need -126 to +129 so have to do this manually + if (offset <= 129) + { + newPcLocation += offset; + } + else + { + // 256 minus our offset gives us a positive number for where it would rollover at 129 + // And we minus this since this would be negative number + newPcLocation -= (ushort)(256 - offset); + } + + // Note we don't need to add one here since we always increment the Pc after we read from it, so it's already pointing to next command at this point + Set(newPcLocation); + } + + public bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status) + { + if (_flags.IsFlagSet(flag) == status) + { + JumpByOffset(offset); + return true; + //_currentCycleCount += 12; + } + + //_currentCycleCount += 7; + return false; + } + + public void JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset) + { + JumpByOffsetIfFlagHasStatus(flag, offset, true); + } + + public void JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) + { + JumpByOffsetIfFlagHasStatus(flag, offset, false); + } + + public void CallIfFlagCondition(Z80StatusFlags flag, ushort address) + { + if (_flags.IsFlagSet(flag)) + { + SetAndSaveExisting(address); + } + } + + public void CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) + { + if (!_flags.IsFlagSet(flag)) + { + SetAndSaveExisting(address); + } + } + + public bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) + { + if (_flags.IsFlagSet(flag) == status) + { + SetFromStack(); + return true; + //_currentCycleCount += 11; + } + + //_currentCycleCount += 5; + return false; + } + + public bool ReturnIfFlag(Z80StatusFlags flag) + { + return ReturnIfFlagHasStatus(flag, true); + } + + public bool ReturnIfNotFlag(Z80StatusFlags flag) + { + return ReturnIfFlagHasStatus(flag, false); + } + + private byte GetNextByteByProgramCounter() + { + // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction + var data = _memory[_pc.Word]; + _pc.Word++; + return data; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs new file mode 100644 index 0000000..42032a0 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs @@ -0,0 +1,83 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80StackManager : Z8016BitRegisterBase, IZ80StackManager +{ + private const ushort DefaultStackAddress = 0xDFF0; + private readonly ICpuLogger _cpuLogger; + private readonly IMasterSystemMemory _memory; + private int _maximumMemorySize; + + public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger) + : base(memory) + { + _memory = memory; + _cpuLogger = cpuLogger; + } + + public override void Reset() + { + // Stack pointer starts at highest point in RAM + // but various hardware register control writes, generally we set to 0xDFF0 + // https://www.smspower.org/Development/Stack + Register.Word = DefaultStackAddress; + _maximumMemorySize = _memory.GetMaximumAvailableMemorySize(); + } + + public void IncrementStackPointer() + { + if (Register.Word >= _maximumMemorySize) + { + throw new InvalidOperationException( + $"Cannot increment Stack Pointer higher than available RAM - {_maximumMemorySize} bytes"); + } + + Register.Word++; + } + + public void DecrementStackPointer() + { + // We check this each time instead of caching since this can change if RAM banking is enabled + // Although it is unlikely any ROM is going to use so much stack that it basically uses all the RAM + if (Register.Word <= _memory.GetMinimumAvailableMemorySize()) + { + throw new InvalidOperationException( + $"Cannot decrement Stack Pointer lower than available RAM - {_memory.GetMinimumAvailableMemorySize()} bytes"); + } + + Register.Word--; + } + + public void PushRegisterToStack(IZ8016BitRegister register) + { + var oldPointer = Register.Word; + var currentPointer = Register.Word; + _memory[--currentPointer] = register.High; + _memory[--currentPointer] = register.Low; + Register.Word = currentPointer; + _cpuLogger.Debug($"Push to stack - Old - {oldPointer}, New = {Register.Word}"); + } + + public void PopRegisterFromStack(IZ8016BitRegister register) + { + var oldPointer = Register.Word; + var currentPointer = Register.Word; + register.SetLow(_memory[currentPointer++]); + register.SetHigh(_memory[currentPointer++]); + Register.Word = currentPointer; + _cpuLogger.Debug($"Pop from stack - Old - {oldPointer}, New = {Register.Word}"); + } + + public void SwapRegisterWithDataAtStackPointerAddress(IZ8016BitRegister register) + { + var currentRegisterDataLow = register.Low; + var currentRegisterDataHigh = register.High; + register.SetLow(_memory[Register.Word]); + register.SetHigh(_memory[(ushort)(Register.Word + 1)]); + + _memory[Register.Word] = currentRegisterDataLow; + _memory[(ushort)(Register.Word + 1)] = currentRegisterDataHigh; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs new file mode 100644 index 0000000..385d91c --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs @@ -0,0 +1,82 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +/// +/// Base class for a 16 bit register which is composed of two 8 bit registers +/// +public abstract class Z8016BitCombinedRegisterBase : IZ8016BitCombinedRegister +{ + private readonly IMasterSystemMemory _memory; + + protected Z8016BitCombinedRegisterBase(IMasterSystemMemory memory) + { + _memory = memory; + } + + protected abstract IZ808BitRegister HighRegister { get; } + protected abstract IZ808BitRegister LowRegister { get; } + + public ushort Value => (ushort)(LowRegister.Value + (HighRegister.Value << 8)); + public byte High => HighRegister.Value; + public byte Low => LowRegister.Value; + public ushort ShadowValue => (ushort)(LowRegister.ShadowValue + (HighRegister.ShadowValue << 8)); + + public void Reset() + { + LowRegister.Reset(); + HighRegister.Reset(); + } + + public void Set(ushort value) + { + LowRegister.Set((byte)(value & 0x00FF)); + HighRegister.Set((byte)((value & 0xFF00) >> 8)); + } + + public void SetHigh(byte value) + { + HighRegister.Set(value); + } + + public void SetLow(byte value) + { + LowRegister.Set(value); + } + + public void SetFromDataInMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + var low = _memory[location]; + location++; + var high = _memory[location]; + + LowRegister.Set(low); + HighRegister.Set(high); + } + + public void SwapWithShadow() + { + LowRegister.SwapWithShadow(); + HighRegister.SwapWithShadow(); + } + + public Z80Register AsRegister() + { + return new Z80Register + { + Low = LowRegister.Value, + High = HighRegister.Value + }; + } + + public Z80Register ShadowAsRegister() + { + return new Z80Register + { + Low = LowRegister.ShadowValue, + High = HighRegister.ShadowValue + }; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs new file mode 100644 index 0000000..e6fca02 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -0,0 +1,60 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +/// +/// Base class for a true 16 bit register +/// +public abstract class Z8016BitRegisterBase : IZ8016BitRegister +{ + private readonly IMasterSystemMemory _memory; + protected Z80Register Register; + + protected Z8016BitRegisterBase(IMasterSystemMemory memory) + { + _memory = memory; + Register = new Z80Register(); + } + + public ushort Value => Register.Word; + public byte High => Register.High; + public byte Low => Register.Low; + + public virtual void Reset() + { + Register.Word = 0x00; + } + + /// + /// Set program counter to new value, but don't save the old value to the stack + /// + /// New address to set PC to + public void Set(ushort value) + { + Register.Word = value; + } + + public void SetHigh(byte value) + { + Register.High = value; + } + + public void SetLow(byte value) + { + Register.Low = value; + } + + public void SetFromDataInMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + SetLow(_memory[location]); + location++; + SetHigh(_memory[location]); + } + + public Z80Register AsRegister() + { + return Register; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z808BitRegister.cs b/Kmse.Core/Z80/Registers/Z808BitRegister.cs new file mode 100644 index 0000000..36cac0f --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z808BitRegister.cs @@ -0,0 +1,35 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers; + +public class Z808BitRegister : IZ808BitRegister +{ + protected readonly IMasterSystemMemory Memory; + public byte Value { get; protected set; } + public byte ShadowValue { get; protected set; } + + public Z808BitRegister(IMasterSystemMemory memory) + { + Memory = memory; + } + + public void Reset() + { + Value = 0; + } + + public void Set(byte value) + { + Value = value; + } + + public void SetFromDataInMemory(ushort address, byte offset = 0) + { + Value = Memory[(ushort)(address + offset)]; + } + + public void SwapWithShadow() + { + (Value, ShadowValue) = (ShadowValue, Value); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs deleted file mode 100644 index 0bbe614..0000000 --- a/Kmse.Core/Z80/Registers/Z80ProgramCounter.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Kmse.Core.Memory; -using Kmse.Core.Z80.Logging; -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80.Registers -{ - public class Z80ProgramCounter : IZ80ProgramCounter - { - private Z80Register _pc; - private readonly IMasterSystemMemory _memory; - private readonly IZ80InstructionLogger _z80InstructionLogger; - - public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger) - { - _memory = memory; - _z80InstructionLogger = z80InstructionLogger; - } - - public void Reset() - { - _pc.Word = 0x00; - } - - public ushort GetValue() - { - return _pc.Word; - } - - public Z80Register AsRegister() - { - return _pc; - } - - public byte GetNextInstruction() - { - return GetNextByteByProgramCounter(); - } - - public byte GetNextDataByte() - { - var data = GetNextByteByProgramCounter(); - _z80InstructionLogger.AddOperationData(data); - return data; - } - - public ushort GetNextTwoDataBytes() - { - ushort data = GetNextByteByProgramCounter(); - data += (ushort)(GetNextByteByProgramCounter() << 8); - _z80InstructionLogger.AddOperationData(data); - return data; - } - - /// - /// Set program counter to new value - /// - /// New address to set PC to - public void SetProgramCounter(ushort address) - { - _pc.Word = address; - } - - /// - /// Move program counter forward by the provided value - /// - /// Add this offset to the current PC - public void MoveProgramCounterForward(ushort offset) - { - // If this goes above ushort max, we assume that when PC hits the limit it just wraps around instead of throwing an error or failing - _pc.Word += offset; - } - - /// - /// Move program counter backward by the provided value - /// - /// Subtract this offset from the current PC - public void MoveProgramCounterBackward(ushort offset) - { - // If this goes below zero/negative, we assume that when PC hits -1 it just wraps around instead of throwing an error or failing - _pc.Word -= offset; - } - - private byte GetNextByteByProgramCounter() - { - // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction - var data = _memory[_pc.Word]; - _pc.Word++; - return data; - } - - } -} diff --git a/Kmse.Core/Z80/Registers/Z80StackManager.cs b/Kmse.Core/Z80/Registers/Z80StackManager.cs deleted file mode 100644 index f101343..0000000 --- a/Kmse.Core/Z80/Registers/Z80StackManager.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Kmse.Core.Memory; -using Kmse.Core.Z80.Logging; -using Kmse.Core.Z80.Support; -using System; - -namespace Kmse.Core.Z80.Registers; - -public class Z80StackManager : IZ80StackManager -{ - private readonly IMasterSystemMemory _memory; - private readonly ICpuLogger _cpuLogger; - private Z80Register _stackPointer; - private int _maximumMemorySize; - - public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger) - { - _memory = memory; - _cpuLogger = cpuLogger; - Reset(); - } - - public void Reset() - { - // Stack pointer starts at highest point in RAM - // but various hardware register control writes, generally we set to 0xDFF0 - // https://www.smspower.org/Development/Stack - _stackPointer.Word = 0xDFF0; - _maximumMemorySize = _memory.GetMaximumAvailableMemorySize(); - } - - public ushort GetValue() - { - return _stackPointer.Word; - } - - public Z80Register AsRegister() - { - return _stackPointer; - } - - public void IncrementStackPointer() - { - if (_stackPointer.Word >= _maximumMemorySize) - { - throw new InvalidOperationException($"Cannot increment Stack Pointer higher than available RAM - {_maximumMemorySize} bytes"); - } - - _stackPointer.Word++; - } - - public void DecrementStackPointer() - { - // We check this each time instead of caching since this can change if RAM banking is enabled - // Although it is unlikely any ROM is going to use so much stack that it basically uses all the RAM - if (_stackPointer.Word <= _memory.GetMinimumAvailableMemorySize()) - { - throw new InvalidOperationException($"Cannot decrement Stack Pointer lower than available RAM - {_memory.GetMinimumAvailableMemorySize()} bytes"); - } - _stackPointer.Word--; - } - - public void PushRegisterToStack(Z80Register register) - { - var oldPointer = _stackPointer.Word; - var currentPointer = _stackPointer.Word; - _memory[--currentPointer] = register.High; - _memory[--currentPointer] = register.Low; - _stackPointer.Word = currentPointer; - _cpuLogger.Debug($"Push to stack - Old - {oldPointer}, New = {_stackPointer.Word}"); - } - - public void PopRegisterFromStack(ref Z80Register register) - { - var oldPointer = _stackPointer.Word; - var currentPointer = _stackPointer.Word; - register.Low = _memory[currentPointer++]; - register.High = _memory[currentPointer++]; - _stackPointer.Word = currentPointer; - _cpuLogger.Debug($"Pop from stack - Old - {oldPointer}, New = {_stackPointer.Word}"); - } - - public void SwapRegisterWithStackPointerLocation(ref Z80Register register) - { - var currentRegisterDataLow = register.Low; - var currentRegisterDataHigh = register.High; - register.Low = _memory[_stackPointer.Word]; - register.High = _memory[(ushort)(_stackPointer.Word + 1)]; - - _memory[_stackPointer.Word] = currentRegisterDataLow; - _memory[(ushort)(_stackPointer.Word + 1)] = currentRegisterDataHigh; - } - - public void SetStackPointer(ushort value) - { - _stackPointer.Word = value; - } - - public void SetStackPointerFromDataInMemory(ushort memoryLocation) - { - _stackPointer.Low = _memory[memoryLocation]; - memoryLocation++; - _stackPointer.High = _memory[memoryLocation]; - } -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Support/Z80Register.cs b/Kmse.Core/Z80/Support/Z80Register.cs index 4bab802..eaba6b7 100644 --- a/Kmse.Core/Z80/Support/Z80Register.cs +++ b/Kmse.Core/Z80/Support/Z80Register.cs @@ -2,6 +2,7 @@ namespace Kmse.Core.Z80.Support; +// TODO: Rename to Unsigned16BitValue [StructLayout(LayoutKind.Explicit)] public struct Z80Register { diff --git a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs index 285b489..0c81149 100644 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs @@ -1,6 +1,5 @@ using Kmse.Core.Utilities; using Kmse.Core.Z80.Support; -using Microsoft.Win32; namespace Kmse.Core.Z80 { @@ -9,156 +8,6 @@ namespace Kmse.Core.Z80 /// public partial class Z80Cpu { - private void SetFlag(Z80StatusFlags flags) - { - _af.Low |= (byte)flags; - } - - private void ClearFlag(Z80StatusFlags flags) - { - _af.Low &= (byte)~flags; - } - - private void InvertFlag(Z80StatusFlags flag) - { - SetClearFlagConditional(flag, !IsFlagSet(flag)); - } - - private void SetClearFlagConditional(Z80StatusFlags flags, bool condition) - { - if (condition) - { - SetFlag(flags); - } - else - { - ClearFlag(flags); - } - } - - private bool IsFlagSet(Z80StatusFlags flags) - { - var currentSetFlags = (Z80StatusFlags)_af.Low & flags; - return currentSetFlags == flags; - } - - private void SaveAndUpdateProgramCounter(ushort address) - { - // Storing PC in Stack so can resume later - _stack.PushRegisterToStack(_pc.AsRegister()); - - // Update PC to execute from new address - _pc.SetProgramCounter(address); - } - - private void SetProgramCounterFromRegister(Z80Register register) - { - // Update PC to execute from the value of the register - _pc.SetProgramCounter(register.Word); - } - - private void ResetProgramCounterFromStack() - { - var register = new Z80Register(); - _stack.PopRegisterFromStack(ref register); - _pc.SetProgramCounter(register.Word); - } - - private void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) - { - if (IsFlagSet(flag)) - { - _pc.SetProgramCounter(address); - } - } - - private void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) - { - if (!IsFlagSet(flag)) - { - _pc.SetProgramCounter(address); - } - } - - private void JumpByOffset(byte offset) - { - var newPcLocation = _pc.GetValue(); - - // Range is -126 to +129 so we need a signed version - // However sbyte only goes from -128 to +127 but we need -126 to +129 so have to do this manually - if (offset <= 129) - { - newPcLocation += offset; - } - else - { - // 256 minus our offset gives us a positive number for where it would rollover at 129 - // And we minus this since this would be negative number - newPcLocation -= (ushort)(256 - offset); - } - - // Note we don't need to add one here since we always increment the Pc after we read from it, so it's already pointing to next command at this point - _pc.SetProgramCounter(newPcLocation); - } - - private void JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status) - { - if (IsFlagSet(flag) == status) - { - JumpByOffset(offset); - _currentCycleCount += 12; - } - - _currentCycleCount += 7; - } - - private void JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset) - { - JumpByOffsetIfFlagHasStatus(flag, offset, true); - } - - private void JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) - { - JumpByOffsetIfFlagHasStatus(flag, offset, false); - } - - private void CallIfFlagCondition(Z80StatusFlags flag, ushort address) - { - if (IsFlagSet(flag)) - { - SaveAndUpdateProgramCounter(address); - } - } - - private void CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) - { - if (!IsFlagSet(flag)) - { - SaveAndUpdateProgramCounter(address); - } - } - - private void ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) - { - if (IsFlagSet(flag) == status) - { - ResetProgramCounterFromStack(); - _currentCycleCount += 11; - } - - _currentCycleCount += 5; - } - - private void ReturnIfFlag(Z80StatusFlags flag) - { - ReturnIfFlagHasStatus(flag, true); - } - - private void ReturnIfNotFlag(Z80StatusFlags flag) - { - ReturnIfFlagHasStatus(flag, false); - } - private byte ReadFromIo(byte high, byte low) { var address = (ushort)((high << 8) + low); @@ -171,12 +20,12 @@ private byte ReadFromIoAndSetFlags(byte high, byte low) var data = _io.ReadPort(address); // If high bit set, then negative so set sign flag - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(data); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(data); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); return data; } @@ -193,22 +42,6 @@ private void WriteToIo(byte high, byte low, byte value) _io.WritePort(address, value); } - private void SetParityFromValue(byte value) - { - // Count the number of 1 bits in the value - // If odd, then clear flag and if even, then set flag - var bitsSet = 0; - for (var i = 0; i < 8; i++) - { - if (Bitwise.IsSet(value, i)) - { - bitsSet++; - } - } - - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, bitsSet == 0 || bitsSet % 2 == 0); - } - private void SwapRegisters(ref Z80Register register1, ref Z80Register register2) { (register1.Word, register2.Word) = (register2.Word, register1.Word); @@ -332,18 +165,6 @@ private void Load8BitRegisterFrom8BitRegister(byte sourceData, ref byte destinat destination = sourceData; } - private void LoadSpecial8BitRegisterToAccumulator(byte sourceData) - { - _af.High = sourceData; - - // Check flags since copying from special register into accumulator - SetClearFlagConditional(Z80StatusFlags.SignS, !Bitwise.IsSet(sourceData, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, sourceData == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _interruptFlipFlop2); - ClearFlag(Z80StatusFlags.AddSubtractN); - } - private void CopyMemoryByRegisterLocations(Z80Register source, Z80Register destination) { _memory[destination.Word] = _memory[source.Word]; @@ -367,14 +188,14 @@ private void IncrementAtRegisterMemoryLocation(Z80Register register, byte offset private void CheckIncrementFlags(byte newValue, byte oldValue) { - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); // Set half carry is carry from bit 3 // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble // https://en.wikipedia.org/wiki/Half-carry_flag - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); } private void Decrement8Bit(ref byte register) @@ -394,16 +215,16 @@ private void DecrementAtRegisterMemoryLocation(Z80Register register, byte offset private void CheckDecrementFlags(byte newValue, byte oldValue) { - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); // Set half carry is borrow from bit 4 // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits // ie. 0x20 - 1 = 0x1F // https://en.wikipedia.org/wiki/Half-carry_flag // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); } private void Increment16Bit(ref Z80Register register) diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index be99509..ae799ee 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -109,7 +109,7 @@ private void PopulateInstructions() private void PopulateCpuControlOperations() { AddStandardInstruction(0x00, 4, "NOP", "No Operation", (_) => { }); - AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { _halted = true; }); + AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { Halt(); }); AddStandardInstruction(0xF3, 4, "DI", "Disable Interrupts", (_) => { _interruptFlipFlop1 = false; _interruptFlipFlop2 = false; }); AddStandardInstruction(0xFB, 4, "EI", "Enable Interrupts", (_) => { _interruptFlipFlop1 = true; _interruptFlipFlop2 = true; }); @@ -292,7 +292,7 @@ private void PopulateArthmeticAndLogicalInstructions() CompareIncrement(); - if (_bc.Word != 0 && !IsFlagSet(Z80StatusFlags.ZeroZ)) + if (_bc.Word != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) { // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -321,7 +321,7 @@ private void PopulateArthmeticAndLogicalInstructions() CompareDecrement(); - if (_bc.Word != 0 && !IsFlagSet(Z80StatusFlags.ZeroZ)) + if (_bc.Word != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) { // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -415,15 +415,15 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => { - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetFlag(Z80StatusFlags.CarryC); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.CarryC); }); AddStandardInstruction(0x3F, 4, "CCF", "Complement Carry Flag", _ => { - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, IsFlagSet(Z80StatusFlags.CarryC)); - InvertFlag(Z80StatusFlags.CarryC); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, _flags.IsFlagSet(Z80StatusFlags.CarryC)); + _flags.InvertFlag(Z80StatusFlags.CarryC); }); AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { DecimalAdjustAccumulator(); }); AddStandardInstruction(0X2F, 4, "CPL", "Complement", _ => { InvertAccumulatorRegister(); }); @@ -434,10 +434,10 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xDD, 0x85, 4, "ADD A, IXL", "Add IXL to A", _ => { AddValueTo8BitRegister(_ix.Low, ref _af.High); }); AddDoubleByteInstruction(0xFD, 0x84, 4, "ADD A, IYH", "Add IYH to A", _ => { AddValueTo8BitRegister(_iy.High, ref _af.High); }); AddDoubleByteInstruction(0xFD, 0x85, 4, "ADD A, IYL", "Add IYL to A", _ => { AddValueTo8BitRegister(_iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x8C, 4, "ADC H, IXH", "Add IXH to A with Carry", _ => { AddValueTo8BitRegister(_ix.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x8D, 4, "ADC L, IXL", "Add IXL to A with Carry", _ => { AddValueTo8BitRegister(_ix.Low, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8C, 4, "ADC H, IYH", "Add IYH to A with Carry", _ => { AddValueTo8BitRegister(_iy.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8D, 4, "ADC L, IYL", "Add IYL to A with Carry", _ => { AddValueTo8BitRegister(_iy.Low, ref _af.High, true); }); + AddDoubleByteInstruction(0xDD, 0x8C, 4, "ADC A, IXH", "Add IXH to A with Carry", _ => { AddValueTo8BitRegister(_ix.High, ref _af.High, true); }); + AddDoubleByteInstruction(0xDD, 0x8D, 4, "ADC A, IXL", "Add IXL to A with Carry", _ => { AddValueTo8BitRegister(_ix.Low, ref _af.High, true); }); + AddDoubleByteInstruction(0xFD, 0x8C, 4, "ADC A, IYH", "Add IYH to A with Carry", _ => { AddValueTo8BitRegister(_iy.High, ref _af.High, true); }); + AddDoubleByteInstruction(0xFD, 0x8D, 4, "ADC A, IYL", "Add IYL to A with Carry", _ => { AddValueTo8BitRegister(_iy.Low, ref _af.High, true); }); AddDoubleByteInstruction(0xDD, 0x94, 4, "SUB A, IXH", "Subtract IXH from A", _ => { SubtractValueFrom8BitRegister(_ix.High, ref _af.High); }); AddDoubleByteInstruction(0xDD, 0x95, 4, "SUB A, IXH", "Subtract IXL from A", _ => { SubtractValueFrom8BitRegister(_ix.Low, ref _af.High); }); AddDoubleByteInstruction(0xFD, 0x94, 4, "SUB A, IYH", "Subtract IYH from A", _ => { SubtractValueFrom8BitRegister(_iy.High, ref _af.High); }); @@ -649,12 +649,12 @@ private void PopulateLoadAndExchangeInstructions() // Since 0x76 is a seperate instruction (halt) we can't do a mask using all lower 3 bits so skip 0x76 and add 0x77 manually AddStandardInstruction(0x77, 7, "LD (HL),A", "Load A into (HL)", i => { LoadRR(i.OpCode); }); - AddStandardInstruction(0x0A, 7, "LD A,(BC)", "Load A into memory location at BC", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _bc.Word); }); - AddStandardInstruction(0x1A, 7, "LD A,(DE)", "Load A into memory location at DE", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _de.Word); }); - AddStandardInstruction(0x2, 7, "LD (BC),A", "Load memory location at BC into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_bc, _af.High); }); - AddStandardInstruction(0x12, 7, "LD (DE),A", "Load memory location at DE into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_de, _af.High); }); + AddStandardInstruction(0x0A, 7, "LD A,(BC)", "Load A from data in memory location at BC", _ => { _accumulator.SetFromDataAtMemoryLocation(_bc.Word); }); + AddStandardInstruction(0x1A, 7, "LD A,(DE)", "Load A from data in memory location at DE", _ => { _accumulator.SetFromDataAtMemoryLocation(_de.Word); }); + AddStandardInstruction(0x2, 7, "LD (BC),A", "Load memory location at BC into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_bc, _accumulator.Value); }); + AddStandardInstruction(0x12, 7, "LD (DE),A", "Load memory location at DE into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_de, _accumulator.Value); }); - AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _af.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _accumulator.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x70, 19, "LD (IX+d),B", "Load B into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x71, 19, "LD (IX+d),C", "Load C into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.Low, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x72, 19, "LD (IX+d),D", "Load D into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.High, _pc.GetNextDataByte()); }); @@ -662,7 +662,7 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xDD, 0x74, 19, "LD (IX+d),H", "Load H into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x75, 19, "LD (IX+d),L", "Load L into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _af.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _accumulator.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x70, 19, "LD (IY+d),B", "Load B into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x71, 19, "LD (IY+d),C", "Load C into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.Low, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x72, 19, "LD (IY+d),D", "Load D into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.High, _pc.GetNextDataByte()); }); @@ -670,15 +670,15 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.High, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.Low, _pc.GetNextDataByte()); }); - AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { _stack.SetStackPointer(_hl.Word); }); - AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { _stack.SetStackPointer(_ix.Word); }); - AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { _stack.SetStackPointer(_iy.Word); }); + AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { _stack.Set(_hl.Word); }); + AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { _stack.Set(_ix.Word); }); + AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { _stack.Set(_iy.Word); }); - AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _iRegister); }); - AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _rRegister); }); - AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { LoadSpecial8BitRegisterToAccumulator(_iRegister); }); - AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { LoadSpecial8BitRegisterToAccumulator(_rRegister); }); - AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { LoadValueInto8BitRegister(ref _af.High, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { Load8BitRegisterFrom8BitRegister(_accumulator.Value, ref _iRegister); }); + AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { Load8BitRegisterFrom8BitRegister(_accumulator.Value, ref _rRegister); }); + AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { _accumulator.SetFromInterruptRegister(_iRegister, _interruptFlipFlop2); }); + AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { _accumulator.SetFromMemoryRefreshRegister(_rRegister, _interruptFlipFlop2); }); + AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { _accumulator.Set(_pc.GetNextDataByte()); }); AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { LoadValueInto8BitRegister(ref _bc.High, _pc.GetNextDataByte()); }); AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { LoadValueInto8BitRegister(ref _bc.Low, _pc.GetNextDataByte()); }); AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { LoadValueInto8BitRegister(ref _de.High, _pc.GetNextDataByte()); }); @@ -775,7 +775,11 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x6D, 9, "LD IYL, IYL", "Load IYL into IYL", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _iy.Low); }); // end undocumented instructions - AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _ix.Word, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => + { + _accumulator.LoadRegisterFromMemory(_ix.Word, _pc.GetNextDataByte()); + //LoadInto8BitRegisterFromMemory(ref _af.High, _ix.Word, _pc.GetNextDataByte()); + }); AddDoubleByteInstruction(0xDD, 0x46, 19, "LD B,(IX+d)", "Load memory at IX + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _ix.Word, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x4E, 19, "LD C,(IX+d)", "Load memory at IX + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _ix.Word, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x56, 19, "LD D,(IX+d)", "Load memory at IX + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _ix.Word, _pc.GetNextDataByte()); }); @@ -824,9 +828,9 @@ private void PopulateLoadAndExchangeInstructions() Increment16Bit(ref _hl); Increment16Bit(ref _de); Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); }); AddDoubleByteInstruction(0xED, 0xB0, DynamicCycleHandling, "LDIR", "Load, Increment, Repeat", _ => { @@ -843,9 +847,9 @@ private void PopulateLoadAndExchangeInstructions() Increment16Bit(ref _hl); Increment16Bit(ref _de); Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); if (_bc.Word != 0) { @@ -866,9 +870,9 @@ private void PopulateLoadAndExchangeInstructions() Decrement16Bit(ref _hl); Decrement16Bit(ref _de); Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); }); AddDoubleByteInstruction(0xED, 0xB8, DynamicCycleHandling, "LDDR", "Load, Decrement, Repeat", _ => { @@ -885,10 +889,10 @@ private void PopulateLoadAndExchangeInstructions() Decrement16Bit(ref _hl); Decrement16Bit(ref _de); Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // This is not a typo, for some reason in LDDR the PV flag is reset unlike the other LDx instructions - ClearFlag(Z80StatusFlags.ParityOverflowPV); + _flags.ClearFlag(Z80StatusFlags.ParityOverflowPV); if (_bc.Word != 0) { @@ -921,12 +925,12 @@ private void PopulateLoadAndExchangeInstructions() private void PopulateExchangeBlockTransferAndSearchInstructions() { - AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _hl); }); + AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _hl); }); AddStandardInstruction(0x8, 4, "EX AF,AF'", "Exchange AF and AF Shadow", _ => { SwapRegisters(ref _af, ref _afShadow); }); AddStandardInstruction(0xEB, 4, "EX DE,HL", "Exchange DE and HL", _ => { SwapRegisters(ref _de, ref _hl); }); AddStandardInstruction(0xD9, 4, "EXX", "Exchange BC, DE, HL with Shadow Registers", _ => { SwapRegisters(ref _bc, ref _bcShadow); SwapRegisters(ref _de, ref _deShadow); SwapRegisters(ref _hl, ref _hlShadow); }); - AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithStackPointerLocation(ref _iy); }); + AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _ix); }); + AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _iy); }); } private void PopulateInputOutputInstructions() @@ -948,8 +952,8 @@ private void PopulateInputOutputInstructions() Save8BitRegisterValueToMemory(data, _hl.Word); Decrement8Bit(ref _bc.High); Increment16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB2, DynamicCycleHandling, "INIR", "Input, Increment, Repeat", _ => { @@ -967,8 +971,8 @@ private void PopulateInputOutputInstructions() Save8BitRegisterValueToMemory(data, _hl.Word); Decrement8Bit(ref _bc.High); Increment16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); if (_bc.High != 0) { @@ -991,8 +995,8 @@ private void PopulateInputOutputInstructions() Save8BitRegisterValueToMemory(data, _hl.Word); Decrement8Bit(ref _bc.High); Decrement16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBA, DynamicCycleHandling, "INDR", "Input, Decrement, Repeat", _ => { @@ -1010,8 +1014,8 @@ private void PopulateInputOutputInstructions() Save8BitRegisterValueToMemory(data, _hl.Word); Decrement8Bit(ref _bc.High); Decrement16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); if (_bc.High != 0) { @@ -1045,8 +1049,8 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); Increment16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB3, DynamicCycleHandling, "OTIR", "Output, Increment, Repeat", _ => { @@ -1065,8 +1069,8 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); Increment16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); if (_bc.High != 0) { @@ -1090,8 +1094,8 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); Decrement16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => { @@ -1110,8 +1114,8 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); Decrement16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); if (_bc.High != 0) { diff --git a/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs b/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs index 2e83c10..c82f5d0 100644 --- a/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs @@ -50,18 +50,18 @@ from bit 3 to bit 4. byte factor = 0; var currentValue = _af.High; - if (_af.High > 0x99 || IsFlagSet(Z80StatusFlags.CarryC)) + if (_af.High > 0x99 || _flags.IsFlagSet(Z80StatusFlags.CarryC)) { factor |= (0x06 << 4); - SetFlag(Z80StatusFlags.CarryC); + _flags.SetFlag(Z80StatusFlags.CarryC); } else { factor &= 0x0F; - ClearFlag(Z80StatusFlags.CarryC); + _flags.ClearFlag(Z80StatusFlags.CarryC); } - if ((_af.High & 0x0F) > 9 || IsFlagSet(Z80StatusFlags.HalfCarryH)) + if ((_af.High & 0x0F) > 9 || _flags.IsFlagSet(Z80StatusFlags.HalfCarryH)) { factor |= 0x06; } @@ -70,7 +70,7 @@ from bit 3 to bit 4. factor &= 0xF0; } - if (!IsFlagSet(Z80StatusFlags.AddSubtractN)) + if (!_flags.IsFlagSet(Z80StatusFlags.AddSubtractN)) { _af.High += factor; } @@ -79,9 +79,9 @@ from bit 3 to bit 4. _af.High -= factor; } - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, Bitwise.IsSet(currentValue ^ _af.High, 4)); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(_af.High, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, Bitwise.IsSet(currentValue ^ _af.High, 4)); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(_af.High, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == 0); SetParityFromValue(_af.High); } @@ -89,8 +89,8 @@ private void InvertAccumulatorRegister() { var onesComplementValue = ~_af.High & 0xFF; - SetFlag(Z80StatusFlags.HalfCarryH); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); _af.High = (byte)onesComplementValue; } @@ -99,12 +99,12 @@ private void NegateAccumulatorRegister() { var twosComplementValue = (0 - _af.High) & 0xFF; - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, twosComplementValue == 0); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (_af.High & 0x0F) > 0); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _af.High == 0x80); - SetFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, _af.High != 0x00); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, twosComplementValue == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (_af.High & 0x0F) > 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _af.High == 0x80); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, _af.High != 0x00); _af.High = (byte)twosComplementValue; } @@ -244,13 +244,13 @@ private void TestBitByOpCode(byte opCode) _ => throw new ArgumentOutOfRangeException($"Register id {register} is not a valid register to test bit on") }; var bitSet = Bitwise.IsSet(valueToCheck, bit); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - SetFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // This behaviour is not documented - SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); } private void TestBitByRegisterLocation(Z80Register register, int bit, int offset) @@ -258,13 +258,13 @@ private void TestBitByRegisterLocation(Z80Register register, int bit, int offset var value = _memory[(ushort)(register.Word + offset)]; var bitSet = Bitwise.IsSet(value, bit); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - SetFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // This behaviour is not documented - SetClearFlagConditional(Z80StatusFlags.SignS, bit == 7 && bitSet); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, bit == 7 && bitSet); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); } private void AddValueAtRegisterMemoryLocationTo8BitRegister(Z80Register register, int offset, ref byte destination, bool withCarry = false) @@ -276,25 +276,25 @@ private void AddValueAtRegisterMemoryLocationTo8BitRegister(Z80Register register private void AddValueTo8BitRegister(byte value, ref byte destination, bool withCarry = false) { int valueWithCarry = value; - if (withCarry && IsFlagSet(Z80StatusFlags.CarryC)) + if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) { valueWithCarry = value + 0x01; } int newValue = destination + valueWithCarry; - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); // Half carry occurs if the result of adding the lower nibbles means it will set the next higher bit (basically overflows) // This is since the adding is done in two 4 bit operations not 1 8 bit operation internally // This is then combined with the DAA instruction which adjusts the result to get the valid value //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (((destination ^ newValue ^ value) & 0x10) != 0)); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination ^ 0x80) & (destination ^ newValue) & 0x80) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (((destination ^ newValue ^ value) & 0x10) != 0)); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination ^ 0x80) & (destination ^ newValue) & 0x80) != 0); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // A carry is same as half carry just on the overall value // Since we stored the sum as a 32 bit integer, we can see if went past the max value and bit 7 must have carried over into bit 8 - SetClearFlagConditional(Z80StatusFlags.CarryC, newValue > 0xFF); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, newValue > 0xFF); destination = (byte)newValue; } @@ -302,26 +302,26 @@ private void AddValueTo8BitRegister(byte value, ref byte destination, bool withC private void Add16BitRegisterTo16BitRegister(Z80Register source, ref Z80Register destination, bool withCarry = false) { int valueWithCarry = source.Word; - if (withCarry && IsFlagSet(Z80StatusFlags.CarryC)) + if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) { valueWithCarry += 0x01; } int newValue = destination.Word + valueWithCarry; // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (destination.Word & 0x0FFF) + (valueWithCarry & 0x0FFF) > 0x0FFF); - ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) - SetClearFlagConditional(Z80StatusFlags.CarryC, + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, (destination.Word & 0xFFFF) + (valueWithCarry & 0xFFFF) > 0xFFFF); if (withCarry) { - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((destination.Word ^ valueWithCarry) & 0x8000) == 0 && ((destination.Word ^ (newValue & 0xFFFF)) & 0x8000) != 0); } @@ -339,24 +339,24 @@ private void SubtractValueAtRegisterMemoryLocationFrom8BitRegister(Z80Register r private void SubtractValueFrom8BitRegister(byte value, ref byte destination, bool withCarry = false) { int valueWithCarry = value; - if (withCarry && IsFlagSet(Z80StatusFlags.CarryC)) + if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) { valueWithCarry += 0x01; } int newValue = destination - valueWithCarry; - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); // Half carry occurs if the result of subtracting the higher nibbles means it will set the next lower bit (basically underflows) // We check if the subtraction means that adding higher nibbles sets bit 3 // This is then combined with the DAA instruction which adjusts the result to get the valid value //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((destination ^ newValue ^ value) & 0x10) != 0); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination) & (destination ^ newValue) & 0x80) != 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((destination ^ newValue ^ value) & 0x10) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination) & (destination ^ newValue) & 0x80) != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit - SetClearFlagConditional(Z80StatusFlags.CarryC, destination < valueWithCarry); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, destination < valueWithCarry); destination = (byte)newValue; } @@ -364,22 +364,22 @@ private void SubtractValueFrom8BitRegister(byte value, ref byte destination, boo private void Sub16BitRegisterFrom16BitRegister(Z80Register source, ref Z80Register destination, bool withCarry = false) { int valueWithCarry = source.Word; - if (withCarry && IsFlagSet(Z80StatusFlags.CarryC)) + if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) { valueWithCarry += 0x01; } int newValue = destination.Word - valueWithCarry; - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((((destination.Word ^ newValue ^ source.Word) >> 8) & 0x10) != 0)); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((source.Word ^ destination.Word) & (destination.Word ^ newValue) & 0x8000) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((((destination.Word ^ newValue ^ source.Word) >> 8) & 0x10) != 0)); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((source.Word ^ destination.Word) & (destination.Word ^ newValue) & 0x8000) != 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) - SetClearFlagConditional(Z80StatusFlags.CarryC, ((newValue& 0x10000) != 0)); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, ((newValue& 0x10000) != 0)); destination.Word = (ushort)newValue; } @@ -389,14 +389,14 @@ private void Compare8Bit(byte value, byte valueToCompareTo) // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not var difference = valueToCompareTo - value; - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((valueToCompareTo ^ difference ^ value) & 0x10) != 0); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ valueToCompareTo) & (valueToCompareTo ^ difference) & 0x80) != 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((valueToCompareTo ^ difference ^ value) & 0x10) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ valueToCompareTo) & (valueToCompareTo ^ difference) & 0x80) != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit - //SetClearFlagConditional(Z80StatusFlags.CarryC, difference < 0); - SetClearFlagConditional(Z80StatusFlags.CarryC, valueToCompareTo < value); + //_flags.SetClearFlagConditional(Z80StatusFlags.CarryC, difference < 0); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, valueToCompareTo < value); } private void CompareIncrement() @@ -408,11 +408,11 @@ private void CompareIncrement() Increment16Bit(ref _hl); Decrement16Bit(ref _bc); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); - SetFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); } private void CompareDecrement() @@ -424,11 +424,11 @@ private void CompareDecrement() Decrement16Bit(ref _hl); Decrement16Bit(ref _bc); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); } private void Compare8BitToMemoryLocationFrom16BitRegister(Z80Register register, int offset, byte valueToCompareTo) @@ -441,12 +441,12 @@ private void And8Bit(byte value, byte valueToAndAgainst, ref byte register) { var newValue = (byte)(value & valueToAndAgainst); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - SetFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - ClearFlag(Z80StatusFlags.CarryC); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.CarryC); register = newValue; } @@ -462,12 +462,12 @@ private void Or8Bit(byte value, byte valueToAndAgainst, ref byte register) { var newValue = (byte)(value | valueToAndAgainst); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - ClearFlag(Z80StatusFlags.CarryC); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.CarryC); register = newValue; } @@ -483,12 +483,12 @@ private void Xor8Bit(byte value, byte valueToXorAgainst, ref byte register) { var newValue = (byte)(value ^ valueToXorAgainst); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - ClearFlag(Z80StatusFlags.CarryC); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.CarryC); register = newValue; } @@ -512,9 +512,9 @@ private void RotateLeftCircularAccumulator() Bitwise.Set(ref newValue, 0); } - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); _af.High = newValue; } @@ -525,15 +525,15 @@ private void RotateLeftAccumulator() // This is special method since RLA flags are set differently to RL r instruction var newValue = (byte)(_af.High << 1); var bit7Set = Bitwise.IsSet(_af.High, 7); - if (IsFlagSet(Z80StatusFlags.CarryC)) + if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) { // Copy carry flag to bit 0 Bitwise.Set(ref newValue, 0); } - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); _af.High = newValue; } @@ -550,9 +550,9 @@ private void RotateRightCircularAccumulator() Bitwise.Set(ref newValue, 7); } - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); _af.High = newValue; } @@ -563,15 +563,15 @@ private void RotateRightAccumulator() // This is special method since RRA flags are set differently to RR r instruction var newValue = (byte)(_af.High >> 1); var bit0Set = Bitwise.IsSet(_af.High, 0); - if (IsFlagSet(Z80StatusFlags.CarryC)) + if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) { // Copy carry flag to bit 7 Bitwise.Set(ref newValue, 7); } - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); _af.High = newValue; } @@ -587,12 +587,12 @@ private void RotateLeftCircular(ref byte register) Bitwise.Set(ref newValue, 0); } - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); register = newValue; } @@ -602,18 +602,18 @@ private void RotateLeft(ref byte register) // Rotate register left by 1 bit, bit 7 is copied to carry flag and carry flag copied to bit 0 var newValue = (byte)(register << 1); var bit7Set = Bitwise.IsSet(register, 7); - if (IsFlagSet(Z80StatusFlags.CarryC)) + if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) { // Copy carry flag to bit 0 Bitwise.Set(ref newValue, 0); } - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); register = newValue; } @@ -629,12 +629,12 @@ private void RotateRightCircular(ref byte register) Bitwise.Set(ref newValue, 7); } - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); register = newValue; } @@ -645,18 +645,18 @@ private void RotateRight(ref byte register) // This is special method since RRA flags are set differently to RR r instruction var newValue = (byte)(register >> 1); var bit0Set = Bitwise.IsSet(register, 0); - if (IsFlagSet(Z80StatusFlags.CarryC)) + if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) { // Copy carry flag to bit 7 Bitwise.Set(ref newValue, 7); } - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); register = newValue; } @@ -698,12 +698,12 @@ private void ShiftLeftArithmetic(ref byte register) // Shift register left by 1 bit, bit 7 is copied to carry flag var newValue = (byte)(register << 1); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); register = newValue; } @@ -721,12 +721,12 @@ private void ShiftRightArithmetic(ref byte register) Bitwise.Set(ref newValue, 7); } - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); register = newValue; } @@ -755,12 +755,12 @@ private void ShiftLeftLogical(ref byte register) // The difference between shift left logical and shift left arithmetic is this sets bit 0 Bitwise.Set(ref newValue, 0); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); register = newValue; } @@ -774,12 +774,12 @@ private void ShiftRightLogical(ref byte register) // The difference between shift right logical and shift right arithmetic is this does maintain bit 7 when shifting and just clears it Bitwise.Clear(ref newValue, 7); - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(newValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); register = newValue; } @@ -799,50 +799,4 @@ private void ShiftRightLogical16BitRegisterMemoryLocation(Z80Register register, ShiftRightLogical(ref value); _memory[location] = value; } - - private void RotateLeftDigit() - { - var value = _memory[_hl.Word]; - var hll = value & 0x0F; - var hlh = (byte)((value & 0xF0) >> 4); - var al = _af.High & 0x0F; - var ah = (byte)((_af.High & 0xF0) >> 4); - - // HL is lower bits of HL copied to high bits + lower 4 bits of A - var newHlValue = (byte)((hll << 4) + al); - // A is higher bits of A left same + higher bits of hl - var newAValue = (byte)((ah << 4) + hlh); - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newAValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - - _memory[_hl.Word] = newHlValue; - _af.High = newAValue; - } - - private void RotateRightDigit() - { - var value = _memory[_hl.Word]; - var hll = value & 0x0F; - var hlh = (byte)((value & 0xF0) >> 4); - var al = _af.High & 0x0F; - var ah = (byte)((_af.High & 0xF0) >> 4); - - // HL is lower bits of HL copied to high bits + lower 4 bits of A - var newHlValue = (byte)((al << 4) + hlh); - // A is higher bits of A left same + higher bits of hl - var newAValue = (byte)((ah << 4) + hll); - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newAValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - - _memory[_hl.Word] = newHlValue; - _af.High = newAValue; - } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 87cb61a..9152363 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,8 +1,9 @@ -using System.Text; -using Kmse.Core.IO; +using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80; @@ -19,13 +20,24 @@ public partial class Z80Cpu : IZ80Cpu private const int NopCycleCount = 4; - private Z80Register _af, _bc, _de, _hl; - private Z80Register _afShadow, _bcShadow, _deShadow, _hlShadow; - private Z80Register _ix, _iy; - private byte _iRegister, _rRegister; - private IZ80ProgramCounter _pc; private IZ80StackManager _stack; + private IZ80FlagsManager _flags; + private IZ80Accumulator _accumulator; + private IZ80AfRegister _af; + private IZ80BcRegister _bc; + private IZ80DeRegister _de; + private IZ80HlRegister _hl; + private IZ808BitRegister _b; + private IZ808BitRegister _c; + private IZ808BitRegister _d; + private IZ808BitRegister _e; + private IZ808BitRegister _h; + private IZ808BitRegister _l; + private IZ80IndexRegisterX _ix; + private IZ80IndexRegisterY _iy; + private IZ80MemoryRefreshRegister _rRegister; + private IZ80InterruptPageAddressRegister _iRegister; /// /// Disables interrupts from being accepted if set to False @@ -41,18 +53,6 @@ public Z80Cpu(ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger) { _cpuLogger = cpuLogger; _instructionLogger = instructionLogger; - _af = new Z80Register(); - _bc = new Z80Register(); - _de = new Z80Register(); - _hl = new Z80Register(); - _afShadow = new Z80Register(); - _bcShadow = new Z80Register(); - _deShadow = new Z80Register(); - _hlShadow = new Z80Register(); - _ix = new Z80Register(); - _iy = new Z80Register(); - _iRegister = 0; - _rRegister = 0; PopulateInstructions(); } @@ -63,8 +63,26 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _io = io; // TODO: Need to create these indirectly to allow mock interfaces to be injected for testing - _pc = new Z80ProgramCounter(memory, _instructionLogger); + _af = new Z80AfRegister(memory); + _bc = new Z80BcRegister(memory); + _de = new Z80DeRegister(memory); + _hl = new Z80HlRegister(memory); + _ix = new Z80IndexRegisterX(memory); + _iy = new Z80IndexRegisterY(memory); + _rRegister = new Z80MemoryRefreshRegister(memory); + _iRegister = new Z80InterruptPageAddressRegister(memory); + + _accumulator = _af.Accumulator; + _flags = _af.Flags; + _b = _bc.B; + _c = _bc.C; + _d = _de.D; + _e = _de.E; + _h = _hl.H; + _l = _hl.L; + _stack = new Z80StackManager(memory, _cpuLogger); + _pc = new Z80ProgramCounter(memory, _instructionLogger, _flags, _stack); } public CpuStatus GetStatus() @@ -74,20 +92,20 @@ public CpuStatus GetStatus() CurrentCycleCount = _currentCycleCount, Halted = _halted, - Af = _af, - Bc = _bc, - De = _de, - Hl = _hl, - AfShadow = _afShadow, - BcShadow = _bcShadow, - DeShadow = _deShadow, - HlShadow = _hlShadow, - Ix = _ix, - Iy = _iy, - Pc = _pc.GetValue(), - StackPointer = _stack.GetValue(), - IRegister = _iRegister, - RRegister = _rRegister, + Af = _af.AsRegister(), + Bc = _bc.AsRegister(), + De = _de.AsRegister(), + Hl = _hl.AsRegister(), + AfShadow = _af.ShadowAsRegister(), + BcShadow = _bc.ShadowAsRegister(), + DeShadow = _de.ShadowAsRegister(), + HlShadow = _hl.ShadowAsRegister(), + Ix = _ix.AsRegister(), + Iy = _iy.AsRegister(), + Pc = _pc.Value, + StackPointer = _stack.Value, + IRegister = _iRegister.Value, + RRegister = _rRegister.Value, InterruptFlipFlop1 = _interruptFlipFlop1, InterruptFlipFlop2 = _interruptFlipFlop2, @@ -111,18 +129,14 @@ public void Reset() _interruptFlipFlop2 = false; _interruptMode = 0; - _iRegister = 0; - _rRegister = 0; - - _af.Word = 0x00; - _bc.Word = 0x00; - _de.Word = 0x00; - _hl.Word = 0x00; - - _afShadow.Word = 0x00; - _bcShadow.Word = 0x00; - _deShadow.Word = 0x00; - _hlShadow.Word = 0x00; + _af.Reset(); + _bc.Reset(); + _de.Reset(); + _hl.Reset(); + _ix.Reset(); + _iy.Reset(); + _iRegister.Reset(); + _rRegister.Reset(); _io.ClearNonMaskableInterrupt(); _io.ClearMaskableInterrupt(); @@ -133,11 +147,11 @@ public void Reset() public int ExecuteNextCycle() { _currentCycleCount = 0; - _instructionLogger.StartNewInstruction(_pc.GetValue()); + _instructionLogger.StartNewInstruction(_pc.Value); if (_io.NonMaskableInterrupt) { - _cpuLogger.LogInstruction(_pc.GetValue(), "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", string.Empty); + _cpuLogger.LogInstruction(_pc.Value, "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", string.Empty); // Copy state of IFF1 into IFF2 to keep a copy and reset IFF1 so processing can continue without a masked interrupt occuring // This gets copied back with a RETN occurs @@ -149,10 +163,10 @@ public int ExecuteNextCycle() _io.ClearNonMaskableInterrupt(); // Handle NMI by jumping to 0x66 - SaveAndUpdateProgramCounter(0x66); + _pc.SetAndSaveExisting(0x66); // If halted then NMI starts it up again - _halted = false; + ResumeIfHalted(); _currentCycleCount += 11; return _currentCycleCount; @@ -160,7 +174,7 @@ public int ExecuteNextCycle() if (_interruptFlipFlop1 && _io.MaskableInterrupt) { - _cpuLogger.LogInstruction(_pc.GetValue(), "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {_interruptMode}"); + _cpuLogger.LogInstruction(_pc.Value, "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {_interruptMode}"); _interruptFlipFlop1 = false; _interruptFlipFlop2 = false; @@ -178,7 +192,7 @@ public int ExecuteNextCycle() // Basically mode 0 is the same as mode 1 // Mode 1, jump to address 0x0038h - SaveAndUpdateProgramCounter(0x0038); + _pc.SetAndSaveExisting(0x0038); _currentCycleCount += 11; return _currentCycleCount; @@ -203,10 +217,10 @@ public int ExecuteNextCycle() var opCode = _pc.GetNextInstruction(); var instruction = opCode switch { - 0xCB => ProcessCBOpCode(CbInstructionModes.Normal), - 0xDD => ProcessDDOpCode(), - 0xFD => ProcessFDOpCode(), - 0xED => ProcessEDOpCode(), + 0xCB => ProcessCbOpCode(CbInstructionModes.Normal), + 0xDD => ProcessDdOpCode(), + 0xFD => ProcessFdOpCode(), + 0xED => ProcessEdOpCode(), _ => ProcessGenericNonPrefixedOpCode(opCode) }; @@ -235,6 +249,21 @@ public int ExecuteNextCycle() return _currentCycleCount; } + private void Halt() + { + _cpuLogger.Debug("Halting CPU"); + _halted = true; + } + + private void ResumeIfHalted() + { + if (!_halted) + { + _cpuLogger.Debug("Resuming CPU"); + } + _halted = true; + } + private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) { if (!_genericInstructions.TryGetValue(opCode, out var instruction)) @@ -245,7 +274,7 @@ private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) return instruction; } - private Instruction ProcessCBOpCode(CbInstructionModes mode) + private Instruction ProcessCbOpCode(CbInstructionModes mode) { // Two byte op code, so get next part of instruction and use that to lookup instruction var opCode = _pc.GetNextInstruction(); @@ -290,7 +319,7 @@ private Instruction ProcessCBOpCode(CbInstructionModes mode) return specialCbInstruction; } - private Instruction ProcessDDOpCode() + private Instruction ProcessDdOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction var secondOpCode = _pc.GetNextInstruction(); @@ -298,7 +327,7 @@ private Instruction ProcessDDOpCode() { // Not a normal instruction, but a special instruction // ie. DD CB data opcode - return ProcessCBOpCode(CbInstructionModes.DD); + return ProcessCbOpCode(CbInstructionModes.DD); } if (!_ddInstructions.TryGetValue(secondOpCode, out var instruction)) @@ -309,7 +338,7 @@ private Instruction ProcessDDOpCode() return instruction; } - private Instruction ProcessFDOpCode() + private Instruction ProcessFdOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction var secondOpCode = _pc.GetNextInstruction(); @@ -317,7 +346,7 @@ private Instruction ProcessFDOpCode() { // Not a normal instruction, but a special instruction // ie. FD CB data opcode - return ProcessCBOpCode(CbInstructionModes.FD); + return ProcessCbOpCode(CbInstructionModes.FD); } if (!_fdInstructions.TryGetValue(secondOpCode, out var instruction)) { @@ -327,7 +356,7 @@ private Instruction ProcessFDOpCode() return instruction; } - private Instruction ProcessEDOpCode() + private Instruction ProcessEdOpCode() { // Two byte op code, so get next part of instruction and use that to lookup instruction var secondOpCode = _pc.GetNextInstruction(); From b1c4582fe103cce3251b95dc5392e1cbe38fcb64 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:46:54 +1000 Subject: [PATCH 04/21] Add helper method to BitWise to convert two bytes to unsigned 16 bit value Split out Z80 I/O and Memory Management into own classes both of which manage how the instructions and CPU work with I/O and Management but still call the IO and Memory classes and does not replace them. Move jump, call and return into program counter handling class and update instructions including handling of dynamic cycle counts where cycle count depends on if call or jump occurred or not Split 8 bit register into base class and general purpose register since while a, b,c,d,e,h,l registers can do math and logic operations, the flags register is a special case. Also register math operations need access to flags register, so need to split to avoid a unnecessary dependency where flags depends on itself Added additional methods for registers including set from memory and save to memory Moved handling for increment/decrement to registers Tidied up LoadRR special handling to make this far neater --- .../BitWiseTests/BitWiseTestFixture.cs | 17 + Kmse.Core/Utilities/Bitwise.cs | 10 + Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs | 11 + Kmse.Core/Z80/IO/Z80CpuInputOutput.cs | 57 +++ .../Z80/Memory/IZ80CpuMemoryManagement.cs | 12 + .../Z80/Memory/Z80CpuMemoryManagement.cs | 72 ++++ .../Z80/Registers/General/IZ80HlRegister.cs | 2 + .../Z80/Registers/General/Z80Accumulator.cs | 37 +- .../Z80/Registers/General/Z80BcRegister.cs | 6 +- .../Z80/Registers/General/Z80DeRegister.cs | 6 +- .../Z80/Registers/General/Z80HlRegister.cs | 14 +- Kmse.Core/Z80/Registers/IZ8016BitRegister.cs | 6 + .../IZ808BitGeneralPurposeRegister.cs | 11 + Kmse.Core/Z80/Registers/IZ808BitRegister.cs | 7 + .../SpecialPurpose/IZ80ProgramCounter.cs | 12 +- .../SpecialPurpose/Z80ProgramCounter.cs | 28 +- .../Registers/Z8016BitCombinedRegisterBase.cs | 37 +- .../Z80/Registers/Z8016BitRegisterBase.cs | 27 ++ .../Z808BitGeneralPurposeRegister.cs | 58 +++ Kmse.Core/Z80/Registers/Z808BitRegister.cs | 25 +- Kmse.Core/Z80/Z80Cpu.CoreOperations.cs | 218 +--------- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 380 +++++++++--------- Kmse.Core/Z80/Z80Cpu.cs | 25 +- 23 files changed, 631 insertions(+), 447 deletions(-) create mode 100644 Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs create mode 100644 Kmse.Core/Z80/IO/Z80CpuInputOutput.cs create mode 100644 Kmse.Core/Z80/Memory/IZ80CpuMemoryManagement.cs create mode 100644 Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs create mode 100644 Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs create mode 100644 Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs diff --git a/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs index 57dd29a..cc21c04 100644 --- a/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs +++ b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs @@ -322,4 +322,21 @@ public void WhenSettingOrClearingBitIf(byte source, int bit, byte expectedValue, Bitwise.SetOrClearIf(ref output, bit, func); output.Should().Be(expectedValue); } + + private static object[] _toUnsignedShortTestCases = + { + new object[] { (byte)0, (byte)0, (ushort)0 }, + new object[] { (byte)1, (byte)1, (ushort)0x0101 }, + new object[] { (byte)1, (byte)0xFF, (ushort)0x01FF }, + new object[] { (byte)0xFF, (byte)0x01, (ushort)0xFF01 }, + new object[] { (byte)0xAB, (byte)0xCD, (ushort)0xABCD }, + new object[] { (byte)0xFF, (byte)0xFF, (ushort)0xFFFF } + }; + + [Test] + [TestCaseSource(nameof(_toUnsignedShortTestCases))] + public void WhenCheckingIntIfBitSet(byte high, byte low, ushort value) + { + Bitwise.ToUnsigned16BitValue(high, low).Should().Be(value); + } } \ No newline at end of file diff --git a/Kmse.Core/Utilities/Bitwise.cs b/Kmse.Core/Utilities/Bitwise.cs index 3dbf311..cc31aad 100644 --- a/Kmse.Core/Utilities/Bitwise.cs +++ b/Kmse.Core/Utilities/Bitwise.cs @@ -2,6 +2,16 @@ public static class Bitwise { + public static ushort ToUnsigned16BitValue(byte high, byte low) + { + return (ushort)((high << 8) + low); + } + + public static (byte, byte) ToBytes(ushort value) + { + return ((byte)(value & 0xFF00 >> 8), (byte)(value & 0x00FF)); + } + public static bool IsSet(byte value, int bit) { if (bit is < 0 or > 7) diff --git a/Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs b/Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs new file mode 100644 index 0000000..28334b1 --- /dev/null +++ b/Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs @@ -0,0 +1,11 @@ +using Kmse.Core.Z80.Registers; + +namespace Kmse.Core.Z80.IO; + +public interface IZ80CpuInputOutput +{ + byte Read(byte high, byte low, bool setFlags); + void ReadAndSetRegister(byte high, byte low, IZ808BitRegister register); + void ReadAndSetRegister(IZ8016BitRegister addressRegister, IZ808BitRegister register); + void Write(byte high, byte low, IZ808BitRegister register); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs new file mode 100644 index 0000000..f12e71f --- /dev/null +++ b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs @@ -0,0 +1,57 @@ +using Kmse.Core.IO; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.IO; + +public class Z80CpuInputOutput : IZ80CpuInputOutput +{ + private readonly IZ80FlagsManager _flags; + private readonly IMasterSystemIoManager _io; + + public Z80CpuInputOutput(IMasterSystemIoManager io, IZ80FlagsManager flags) + { + _io = io; + _flags = flags; + } + + public byte Read(byte high, byte low, bool setFlags) + { + var address = Bitwise.ToUnsigned16BitValue(high, low); + var data = _io.ReadPort(address); + + if (!setFlags) + { + return data; + } + + // If high bit set, then negative so set sign flag + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); + + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(data); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + return data; + } + + public void ReadAndSetRegister(byte high, byte low, IZ808BitRegister register) + { + var data = Read(high, low, true); + register.Set(data); + } + + public void ReadAndSetRegister(IZ8016BitRegister addressRegister, IZ808BitRegister register) + { + ReadAndSetRegister(addressRegister.High, addressRegister.Low, register); + } + + public void Write(byte high, byte low, IZ808BitRegister register) + { + var address = Bitwise.ToUnsigned16BitValue(high, low); + _io.WritePort(address, register.Value); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Memory/IZ80CpuMemoryManagement.cs b/Kmse.Core/Z80/Memory/IZ80CpuMemoryManagement.cs new file mode 100644 index 0000000..acbfafa --- /dev/null +++ b/Kmse.Core/Z80/Memory/IZ80CpuMemoryManagement.cs @@ -0,0 +1,12 @@ +using Kmse.Core.Z80.Registers; + +namespace Kmse.Core.Z80.Memory; + +public interface IZ80CpuMemoryManagement +{ + void CopyMemory(IZ8016BitRegister source, IZ8016BitRegister destination); + byte ReadFromMemory(IZ8016BitRegister register, byte offset = 0); + void WriteToMemory(IZ8016BitRegister register, byte value, byte offset = 0); + void IncrementMemory(IZ8016BitRegister register, byte offset = 0); + void DecrementMemory(IZ8016BitRegister register, byte offset = 0); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs new file mode 100644 index 0000000..7adb408 --- /dev/null +++ b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs @@ -0,0 +1,72 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Memory; + +public class Z80CpuMemoryManagement : IZ80CpuMemoryManagement +{ + private readonly IZ80FlagsManager _flags; + private readonly IMasterSystemMemory _memory; + + public Z80CpuMemoryManagement(IMasterSystemMemory memory, IZ80FlagsManager flags) + { + _memory = memory; + _flags = flags; + } + + public void CopyMemory(IZ8016BitRegister source, IZ8016BitRegister destination) + { + _memory[destination.Value] = _memory[source.Value]; + } + + public byte ReadFromMemory(IZ8016BitRegister register, byte offset = 0) + { + return _memory[(ushort)(register.Value + offset)]; + } + + public void WriteToMemory(IZ8016BitRegister register, byte value, byte offset = 0) + { + _memory[(ushort)(register.Value + offset)] = value; + } + + public void IncrementMemory(IZ8016BitRegister register, byte offset = 0) + { + var address = (ushort)(register.Value + offset); + var value = _memory[address]; + var newValue = (byte)(value + 1); + + _memory[address] = newValue; + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is carry from bit 3 + // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble + // https://en.wikipedia.org/wiki/Half-carry_flag + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (value & 0x0F) == 0x0F); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x7F); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + public void DecrementMemory(IZ8016BitRegister register, byte offset = 0) + { + var address = (ushort)(register.Value + offset); + var value = _memory[address]; + var newValue = (byte)(value - 1); + + _memory[address] = newValue; + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is borrow from bit 4 + // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits + // ie. 0x20 - 1 = 0x1F + // https://en.wikipedia.org/wiki/Half-carry_flag + // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (value & 0x0F) == 0x00); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x80); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs index a9ddc3c..96a9869 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs @@ -4,4 +4,6 @@ public interface IZ80HlRegister : IZ8016BitCombinedRegister { IZ808BitRegister H { get; } IZ808BitRegister L { get; } + + void SwapWithDeRegister(IZ80DeRegister register); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index e70cd2a..d418c27 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -5,14 +5,11 @@ namespace Kmse.Core.Z80.Registers.General; -public class Z80Accumulator : Z808BitRegister, IZ80Accumulator +public class Z80Accumulator : Z808BitGeneralPurposeRegister, IZ80Accumulator { - private readonly IZ80FlagsManager _flags; - public Z80Accumulator(IZ80FlagsManager flags, IMasterSystemMemory memory) - : base(memory) + : base(memory, flags) { - _flags = flags; } public void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status) @@ -38,11 +35,11 @@ public void RotateLeftDigit(IZ80HlRegister hl) // A is higher bits of A left same + higher bits of hl var newAValue = (byte)((ah << 4) + hlh); - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newAValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newAValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); Memory[hl.Value] = newHlValue; Value = newAValue; @@ -61,11 +58,11 @@ public void RotateRightDigit(IZ80HlRegister hl) // A is higher bits of A left same + higher bits of hl var newAValue = (byte)((ah << 4) + hll); - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newAValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newAValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); Memory[hl.Value] = newHlValue; Value = newAValue; @@ -76,10 +73,10 @@ private void LoadSpecial8BitRegisterToAccumulator(byte sourceData, bool interrup Value = sourceData; // Check flags since copying from special register into accumulator - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, !Bitwise.IsSet(sourceData, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, sourceData == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, interruptFlipFlop2Status); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, !Bitwise.IsSet(sourceData, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, sourceData == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, interruptFlipFlop2Status); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs index ec4946e..e38be5c 100644 --- a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs @@ -4,11 +4,11 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80BcRegister : Z8016BitCombinedRegisterBase, IZ80BcRegister { - public Z80BcRegister(IMasterSystemMemory memory) + public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory) { - B = new Z808BitRegister(memory); - C = new Z808BitRegister(memory); + B = new Z808BitGeneralPurposeRegister(memory, flags); + C = new Z808BitGeneralPurposeRegister(memory, flags); } protected override IZ808BitRegister HighRegister => B; diff --git a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs index 0f85d52..d128689 100644 --- a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs @@ -4,11 +4,11 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80DeRegister : Z8016BitCombinedRegisterBase, IZ80DeRegister { - public Z80DeRegister(IMasterSystemMemory memory) + public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory) { - D = new Z808BitRegister(memory); - E = new Z808BitRegister(memory); + D = new Z808BitGeneralPurposeRegister(memory, flags); + E = new Z808BitGeneralPurposeRegister(memory, flags); } protected override IZ808BitRegister HighRegister => D; diff --git a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs index 53541ec..e2a3253 100644 --- a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs @@ -4,15 +4,23 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80HlRegister : Z8016BitCombinedRegisterBase, IZ80HlRegister { - public Z80HlRegister(IMasterSystemMemory memory) + public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory) { - H = new Z808BitRegister(memory); - L = new Z808BitRegister(memory); + H = new Z808BitGeneralPurposeRegister(memory, flags); + L = new Z808BitGeneralPurposeRegister(memory, flags); } protected override IZ808BitRegister HighRegister => H; protected override IZ808BitRegister LowRegister => L; public IZ808BitRegister H { get; } public IZ808BitRegister L { get; } + + public void SwapWithDeRegister(IZ80DeRegister register) + { + var otherRegisterValue = register.Value; + var thisRegisterValue = Value; + register.Set(thisRegisterValue); + Set(otherRegisterValue); + } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs index 92d4dce..3e9d660 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -10,8 +10,14 @@ public interface IZ8016BitRegister public void Reset(); public void Set(ushort value); + public void Set(IZ8016BitRegister register); public void SetHigh(byte value); public void SetLow(byte value); public void SetFromDataInMemory(ushort address, byte offset = 0); + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0); + public void SaveToMemory(ushort address, byte offset = 0); public Z80Register AsRegister(); + + void Increment(); + void Decrement(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs new file mode 100644 index 0000000..1f92044 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs @@ -0,0 +1,11 @@ +namespace Kmse.Core.Z80.Registers; + +/// +/// Interface for a Z80 8 bit general purpose register which can have operations on the value +/// This distinguishes is from the Flag register which cannot do various math operations on it +/// +public interface IZ808BitGeneralPurposeRegister : IZ808BitRegister +{ + void Increment(); + void Decrement(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ808BitRegister.cs b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs index b21809e..af6688c 100644 --- a/Kmse.Core/Z80/Registers/IZ808BitRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs @@ -1,5 +1,8 @@ namespace Kmse.Core.Z80.Registers; +/// +/// Base interface for Z80 8-bit register with core handling for the value +/// public interface IZ808BitRegister { public byte Value { get; } @@ -7,6 +10,10 @@ public interface IZ808BitRegister public void Reset(); public void Set(byte value); + public void Set(IZ808BitRegister register); public void SetFromDataInMemory(ushort address, byte offset = 0); + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0); + public void SaveToMemory(ushort address, byte offset = 0); + public void SaveToMemory(IZ8016BitRegister register, byte offset = 0); public void SwapWithShadow(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs index 4bd2e90..b32548c 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -38,14 +38,14 @@ public interface IZ80ProgramCounter : IZ8016BitRegister /// void SetFromStack(); - void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address); - void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address); + bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address); + bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address); void JumpByOffset(byte offset); bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status); - void JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset); - void JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset); - void CallIfFlagCondition(Z80StatusFlags flag, ushort address); - void CallIfNotFlagCondition(Z80StatusFlags flag, ushort address); + bool JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset); + bool JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset); + bool CallIfFlagCondition(Z80StatusFlags flag, ushort address); + bool CallIfNotFlagCondition(Z80StatusFlags flag, ushort address); bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status); bool ReturnIfFlag(Z80StatusFlags flag); bool ReturnIfNotFlag(Z80StatusFlags flag); diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index 6089298..dd9ca02 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -85,20 +85,26 @@ public void SetFromStack() Set(register.Word); } - public void Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) + public bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) { if (_flags.IsFlagSet(flag)) { Set(address); + return true; } + + return false; } - public void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) + public bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) { if (!_flags.IsFlagSet(flag)) { Set(address); + return true; } + + return false; } public void JumpByOffset(byte offset) @@ -135,30 +141,36 @@ public bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool s return false; } - public void JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset) + public bool JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset) { - JumpByOffsetIfFlagHasStatus(flag, offset, true); + return JumpByOffsetIfFlagHasStatus(flag, offset, true); } - public void JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) + public bool JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) { - JumpByOffsetIfFlagHasStatus(flag, offset, false); + return JumpByOffsetIfFlagHasStatus(flag, offset, false); } - public void CallIfFlagCondition(Z80StatusFlags flag, ushort address) + public bool CallIfFlagCondition(Z80StatusFlags flag, ushort address) { if (_flags.IsFlagSet(flag)) { SetAndSaveExisting(address); + return true; } + + return false; } - public void CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) + public bool CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) { if (!_flags.IsFlagSet(flag)) { SetAndSaveExisting(address); + return true; } + + return false; } public bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) diff --git a/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs index 385d91c..209a736 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs @@ -1,4 +1,5 @@ using Kmse.Core.Memory; +using Kmse.Core.Utilities; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; @@ -31,8 +32,14 @@ public void Reset() public void Set(ushort value) { - LowRegister.Set((byte)(value & 0x00FF)); - HighRegister.Set((byte)((value & 0xFF00) >> 8)); + var (high, low) = Bitwise.ToBytes(value); + LowRegister.Set(low); + HighRegister.Set(high); + } + + public void Set(IZ8016BitRegister register) + { + Set(register.Value); } public void SetHigh(byte value) @@ -56,6 +63,18 @@ public void SetFromDataInMemory(ushort address, byte offset = 0) HighRegister.Set(high); } + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + { + SetFromDataInMemory(register.Value, offset); + } + + public void SaveToMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + _memory[location] = HighRegister.Value; + _memory[(ushort)(location + 1)] = LowRegister.Value; + } + public void SwapWithShadow() { LowRegister.SwapWithShadow(); @@ -71,6 +90,20 @@ public Z80Register AsRegister() }; } + public void Increment() + { + var currentValue = Value; + currentValue++; + Set(currentValue); + } + + public void Decrement() + { + var currentValue = Value; + currentValue--; + Set(currentValue); + } + public Z80Register ShadowAsRegister() { return new Z80Register diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index e6fca02..59761a8 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -35,6 +35,11 @@ public void Set(ushort value) Register.Word = value; } + public void Set(IZ8016BitRegister register) + { + Register.Word = register.Value; + } + public void SetHigh(byte value) { Register.High = value; @@ -53,8 +58,30 @@ public void SetFromDataInMemory(ushort address, byte offset = 0) SetHigh(_memory[location]); } + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + { + SetFromDataInMemory(register.Value, offset); + } + + public void SaveToMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + _memory[location] = Register.Low; + _memory[(ushort)(location + 1)] = Register.High; + } + public Z80Register AsRegister() { return Register; } + + public void Increment() + { + Register.Word++; + } + + public void Decrement() + { + Register.Word--; + } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs new file mode 100644 index 0000000..1e3c263 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs @@ -0,0 +1,58 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public class Z808BitGeneralPurposeRegister : Z808BitRegister, IZ808BitGeneralPurposeRegister +{ + protected readonly IZ80FlagsManager Flags; + + public Z808BitGeneralPurposeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory) + { + Flags = flags; + } + + public void Increment() + { + var oldValue = Value; + var newValue = (byte)(Value + 1); + Set(newValue); + CheckIncrementFlags(newValue, oldValue); + } + + private void CheckIncrementFlags(byte newValue, byte oldValue) + { + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is carry from bit 3 + // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble + // https://en.wikipedia.org/wiki/Half-carry_flag + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + public void Decrement() + { + var oldValue = Value; + var newValue = (byte)(Value - 1); + Set(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + private void CheckDecrementFlags(byte newValue, byte oldValue) + { + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is borrow from bit 4 + // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits + // ie. 0x20 - 1 = 0x1F + // https://en.wikipedia.org/wiki/Half-carry_flag + // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z808BitRegister.cs b/Kmse.Core/Z80/Registers/Z808BitRegister.cs index 36cac0f..378eed6 100644 --- a/Kmse.Core/Z80/Registers/Z808BitRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitRegister.cs @@ -2,13 +2,13 @@ namespace Kmse.Core.Z80.Registers; -public class Z808BitRegister : IZ808BitRegister +public abstract class Z808BitRegister : IZ808BitRegister { protected readonly IMasterSystemMemory Memory; public byte Value { get; protected set; } public byte ShadowValue { get; protected set; } - public Z808BitRegister(IMasterSystemMemory memory) + protected Z808BitRegister(IMasterSystemMemory memory) { Memory = memory; } @@ -23,11 +23,32 @@ public void Set(byte value) Value = value; } + public void Set(IZ808BitRegister register) + { + Set(register.Value); + } + public void SetFromDataInMemory(ushort address, byte offset = 0) { Value = Memory[(ushort)(address + offset)]; } + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + { + SetFromDataInMemory(register.Value, offset); + } + + public void SaveToMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + Memory[location] = Value; + } + + public void SaveToMemory(IZ8016BitRegister register, byte offset = 0) + { + SaveToMemory(register.Value, offset); + } + public void SwapWithShadow() { (Value, ShadowValue) = (ShadowValue, Value); diff --git a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs index 0c81149..d42357b 100644 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs @@ -1,5 +1,4 @@ -using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Registers; namespace Kmse.Core.Z80 { @@ -8,45 +7,6 @@ namespace Kmse.Core.Z80 /// public partial class Z80Cpu { - private byte ReadFromIo(byte high, byte low) - { - var address = (ushort)((high << 8) + low); - return _io.ReadPort(address); - } - - private byte ReadFromIoAndSetFlags(byte high, byte low) - { - var address = (ushort)((high << 8) + low); - var data = _io.ReadPort(address); - - // If high bit set, then negative so set sign flag - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); - - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(data); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - - return data; - } - - private void ReadFromIoIntoRegister(byte high, byte low, ref byte destination) - { - var data = ReadFromIoAndSetFlags(high, low); - destination = data; - } - - private void WriteToIo(byte high, byte low, byte value) - { - var address = (ushort)((high << 8) + low); - _io.WritePort(address, value); - } - - private void SwapRegisters(ref Z80Register register1, ref Z80Register register2) - { - (register1.Word, register2.Word) = (register2.Word, register1.Word); - } - private void LoadRR(byte opCode) { // LD r,r' is 0 1 r r r r' r' r' @@ -62,179 +22,39 @@ private void LoadRR(byte opCode) // Special cases where we are loading from or into memory location referenced by HL register rather than actual register if (sourceRegisterId == 0x06) { - LoadFrom16BitRegisterMemoryLocationInto8BitRegisterById(_hl, destinationRegisterId); + Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); _currentCycleCount += 3; return; } if (destinationRegisterId == 0x06) { - SaveTo16BitRegisterMemoryLocationFrom8BitRegisterById(_hl, sourceRegisterId); + var register = Get8BitRegisterByRIdentifier(destinationRegisterId); + _memoryManagement.WriteToMemory(_hl, register.Value); _currentCycleCount += 3; return; } - ref byte sourceRegister = ref Get8BitRegisterByRIdentifier(sourceRegisterId); - ref byte destinationRegister = ref Get8BitRegisterByRIdentifier(destinationRegisterId); + var sourceRegister = Get8BitRegisterByRIdentifier(sourceRegisterId); + var destinationRegister = Get8BitRegisterByRIdentifier(destinationRegisterId); - destinationRegister = sourceRegister; - } - - private void LoadFrom16BitRegisterMemoryLocationInto8BitRegisterById(Z80Register sourceRegister, byte destinationRegisterId) - { - ref var destinationRegister = ref Get8BitRegisterByRIdentifier(destinationRegisterId); - LoadInto8BitRegisterFromMemory(ref destinationRegister, sourceRegister.Word); + destinationRegister.Set(sourceRegister); } - private void SaveTo16BitRegisterMemoryLocationFrom8BitRegisterById(Z80Register destinationRegister, byte sourceRegisterId, byte offset = 0) + private IZ808BitRegister Get8BitRegisterByRIdentifier(byte identifier) { - ref var sourceRegister = ref Get8BitRegisterByRIdentifier(sourceRegisterId); - Save8BitRegisterValueToMemory(sourceRegister, (ushort)(destinationRegister.Word + offset)); - } - - private void SaveTo16BitRegisterMemoryLocationFrom8BitRegister(Z80Register destinationRegister, byte sourceRegister, byte offset = 0) - { - Save8BitRegisterValueToMemory(sourceRegister, (ushort)(destinationRegister.Word + offset)); - } - - private ref byte Get8BitRegisterByRIdentifier(byte identifier) - { - // ReSharper disable once ConvertSwitchStatementToSwitchExpression - // Return ref is not supported for switch expressions - https://github.com/dotnet/csharplang/issues/3326 - switch (identifier) + return identifier switch { - case 0: return ref _bc.High; - case 1: return ref _bc.Low; - case 2: return ref _de.High; - case 3: return ref _de.Low; - case 4: return ref _hl.High; - case 5: return ref _hl.Low; - // 6 is HL - case 7: return ref _af.High; - default: - throw new ArgumentOutOfRangeException(); - } - } - - private void LoadInto8BitRegisterFromMemory(ref byte register, ushort memoryLocation, byte offset = 0) - { - register = _memory[(ushort)(memoryLocation + offset)]; - } - - private void LoadInto16BitRegisterFromMemory(ref Z80Register register, ushort memoryLocation, byte offset = 0) - { - var location = (ushort)(memoryLocation + offset); - register.Low = _memory[location]; - location++; - register.High = _memory[location]; - } - - private void LoadValueInto8BitRegister(ref byte register, byte value) - { - register = value; - } - - private void LoadValueInto16BitRegister(ref Z80Register register, ushort value) - { - register.Word = value; - } - - private void Save8BitRegisterValueToMemory(byte value, ushort memoryLocation) - { - _memory[memoryLocation] = value; - } - - private byte GetValueFromMemoryByRegisterLocation(Z80Register register, byte offset = 0) - { - return _memory[(ushort)(register.Word + offset)]; - } - - private void Save16BitRegisterToMemory(Z80Register register, ushort memoryLocation) - { - _memory[memoryLocation] = register.Low; - _memory[(ushort)(memoryLocation + 1)] = register.High; - } - - private void LoadValueIntoRegisterMemoryLocation(byte value, Z80Register register, byte offset = 0) - { - _memory[(ushort)(register.Word + offset)] = value; - } - - private void Load8BitRegisterFrom8BitRegister(byte sourceData, ref byte destination) - { - destination = sourceData; - } - - private void CopyMemoryByRegisterLocations(Z80Register source, Z80Register destination) - { - _memory[destination.Word] = _memory[source.Word]; - } - - private void Increment8Bit(ref byte register) - { - var oldValue = register; - register = (byte)(register + 1); - CheckIncrementFlags(register, oldValue); - } - - private void IncrementAtRegisterMemoryLocation(Z80Register register, byte offset = 0) - { - var address = (ushort)(register.Word + offset); - var value = _memory[address]; - Increment8Bit(ref value); - - _memory[address] = value; - } - - private void CheckIncrementFlags(byte newValue, byte oldValue) - { - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); - // Set half carry is carry from bit 3 - // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble - // https://en.wikipedia.org/wiki/Half-carry_flag - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - } - - private void Decrement8Bit(ref byte register) - { - var oldValue = register; - register = (byte)(register - 1); - CheckDecrementFlags(register, oldValue); - } - - private void DecrementAtRegisterMemoryLocation(Z80Register register, byte offset = 0) - { - var address = (ushort)(register.Word + offset); - var value = _memory[address]; - Decrement8Bit(ref value); - _memory[address] = value; - } - - private void CheckDecrementFlags(byte newValue, byte oldValue) - { - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); - // Set half carry is borrow from bit 4 - // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits - // ie. 0x20 - 1 = 0x1F - // https://en.wikipedia.org/wiki/Half-carry_flag - // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - } - - private void Increment16Bit(ref Z80Register register) - { - register.Word++; - } - - private void Decrement16Bit(ref Z80Register register) - { - register.Word--; + 0 => _b, + 1 => _c, + 2 => _d, + 3 => _e, + 4 => _h, + 5 => _l, + // 6 is HL so cannot return here since 16 bit register + 7 => _accumulator, + _ => throw new ArgumentOutOfRangeException() + }; } } } diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index ae799ee..bc4de3f 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -120,33 +120,33 @@ private void PopulateCpuControlOperations() private void PopulateJumpCallAndReturnOperations() { - AddStandardInstruction(0xE9, 4, "JP (HL)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_hl); }); - AddDoubleByteInstruction(0xDD, 0xE9, 8, "JP (IX)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_ix); }); - AddDoubleByteInstruction(0xFD, 0xE9, 8, "JP (IY)", "Unconditional Jump", _ => { SetProgramCounterFromRegister(_iy); }); - AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { _pc.SetProgramCounter(_pc.GetNextTwoDataBytes()); }); - - AddStandardInstruction(0xDA, 10, "JP C,$NN", "Conditional Jump If Carry Set", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xD2, 10, "JP NC,$NN", "Conditional Jump If Carry Not Set", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xFA, 10, "JP M,$NN", "Conditional Jump If Negative", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xF2, 10, "JP P,$NN", "Conditional Jump If Positive", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xCA, 10, "JP Z,$NN", "Conditional Jump if Zero", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xC2, 10, "JP NZ,$NN", "Conditional Jump If Not Zero", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xEA, 10, "JP PE,$NN", "Conditional Jump If Parity Even", _ => { Jump16BitIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xE2, 10, "JP PO,$NN", "Conditional Jump If Parity Odd", _ => { Jump16BitIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x18, 12, "JR $N+2", "Relative Jump By Offset", _ => { JumpByOffset(_pc.GetNextDataByte()); }); - AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()); }); + AddStandardInstruction(0xE9, 4, "JP (HL)", "Unconditional Jump", _ => { _pc.Set(_hl); }); + AddDoubleByteInstruction(0xDD, 0xE9, 8, "JP (IX)", "Unconditional Jump", _ => { _pc.Set(_ix); }); + AddDoubleByteInstruction(0xFD, 0xE9, 8, "JP (IY)", "Unconditional Jump", _ => { _pc.Set(_iy); }); + AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { _pc.Set(_pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0xDA, 10, "JP C,$NN", "Conditional Jump If Carry Set", _ => { _pc.Jump16BitIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xD2, 10, "JP NC,$NN", "Conditional Jump If Carry Not Set", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xFA, 10, "JP M,$NN", "Conditional Jump If Negative", _ => { _pc.Jump16BitIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xF2, 10, "JP P,$NN", "Conditional Jump If Positive", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xCA, 10, "JP Z,$NN", "Conditional Jump if Zero", _ => { _pc.Jump16BitIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xC2, 10, "JP NZ,$NN", "Conditional Jump If Not Zero", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xEA, 10, "JP PE,$NN", "Conditional Jump If Parity Even", _ => { _pc.Jump16BitIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xE2, 10, "JP PO,$NN", "Conditional Jump If Parity Odd", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x18, 12, "JR $N+2", "Relative Jump By Offset", _ => { _pc.JumpByOffset(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7; }); + AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7; }); + AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7; }); + AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7; }); AddStandardInstruction(0x10, DynamicCycleHandling, "DJNZ $+2", "Decrement, Jump if Non-Zero", _ => { - Decrement8Bit(ref _bc.High); + _b.Decrement(); var offset = _pc.GetNextDataByte(); if (_bc.High != 0) { - JumpByOffset(offset); + _pc.JumpByOffset(offset); _currentCycleCount += 13; } @@ -154,37 +154,37 @@ private void PopulateJumpCallAndReturnOperations() _currentCycleCount += 8; }); - AddStandardInstruction(0xCD, 17, "CALL NN", "Unconditional Call", _ => { SaveAndUpdateProgramCounter(_pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { CallIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { CallIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { CallIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { CallIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { CallIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); - - AddStandardInstruction(0xC9, 10, "RET", "Return", _ => { ResetProgramCounterFromStack(); }); - AddStandardInstruction(0xD8, DynamicCycleHandling, "RET C", "Conditional Return If Carry Set", _ => { ReturnIfFlag(Z80StatusFlags.CarryC); }); - AddStandardInstruction(0xD0, DynamicCycleHandling, "RET NC", "Conditional Return If Carry Not Set", _ => { ReturnIfNotFlag(Z80StatusFlags.CarryC); }); - AddStandardInstruction(0xF8, DynamicCycleHandling, "RET M", "Conditional Return If Negative", _ => { ReturnIfFlag(Z80StatusFlags.SignS); }); - AddStandardInstruction(0xF0, DynamicCycleHandling, "RET P", "Conditional Return If Positive", _ => { ReturnIfNotFlag(Z80StatusFlags.SignS); }); - AddStandardInstruction(0xC8, DynamicCycleHandling, "RET Z", "Conditional Return If Zero", _ => { ReturnIfFlag(Z80StatusFlags.ZeroZ); }); - AddStandardInstruction(0xC0, DynamicCycleHandling, "RET NZ", "Conditional Return If Not Zero", _ => { ReturnIfNotFlag(Z80StatusFlags.ZeroZ); }); - AddStandardInstruction(0xE8, DynamicCycleHandling, "RET PE", "Conditional Return If Parity Even", _ => { ReturnIfFlag(Z80StatusFlags.ParityOverflowPV); }); - AddStandardInstruction(0xE0, DynamicCycleHandling, "RET PO", "Conditional Return If Parity Odd", _ => { ReturnIfNotFlag(Z80StatusFlags.ParityOverflowPV); }); - - AddStandardInstruction(0xC7, 11, "RST 0", "Restart at 0h", _ => { SaveAndUpdateProgramCounter(0x00); }); - AddStandardInstruction(0xCF, 11, "RST 08H", "Restart at 08h", _ => { SaveAndUpdateProgramCounter(0x08); }); - AddStandardInstruction(0xD7, 11, "RST 10H", "Restart at 10h", _ => { SaveAndUpdateProgramCounter(0x10); }); - AddStandardInstruction(0xDF, 11, "RST 18H", "Restart at 18h", _ => { SaveAndUpdateProgramCounter(0x18); }); - AddStandardInstruction(0xE7, 11, "RST 20H", "Restart at 20h", _ => { SaveAndUpdateProgramCounter(0x20); }); - AddStandardInstruction(0xEF, 11, "RST 28H", "Restart at 28h", _ => { SaveAndUpdateProgramCounter(0x28); }); - AddStandardInstruction(0xF7, 11, "RST 30H", "Restart at 30h", _ => { SaveAndUpdateProgramCounter(0x30); }); - AddStandardInstruction(0xFF, 11, "RST 38H", "Restart at 38h", _ => { SaveAndUpdateProgramCounter(0x38); }); + AddStandardInstruction(0xCD, 17, "CALL NN", "Unconditional Call", _ => { _pc.SetAndSaveExisting(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + + AddStandardInstruction(0xC9, 10, "RET", "Return", _ => { _pc.SetFromStack(); }); + AddStandardInstruction(0xD8, DynamicCycleHandling, "RET C", "Conditional Return If Carry Set", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.CarryC) ? 11 : 5; }); + AddStandardInstruction(0xD0, DynamicCycleHandling, "RET NC", "Conditional Return If Carry Not Set", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.CarryC) ? 11 : 5; }); + AddStandardInstruction(0xF8, DynamicCycleHandling, "RET M", "Conditional Return If Negative", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.SignS) ? 11 : 5; }); + AddStandardInstruction(0xF0, DynamicCycleHandling, "RET P", "Conditional Return If Positive", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.SignS) ? 11 : 5; }); + AddStandardInstruction(0xC8, DynamicCycleHandling, "RET Z", "Conditional Return If Zero", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.ZeroZ) ? 11 : 5; }); + AddStandardInstruction(0xC0, DynamicCycleHandling, "RET NZ", "Conditional Return If Not Zero", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.ZeroZ) ? 11 : 5; }); + AddStandardInstruction(0xE8, DynamicCycleHandling, "RET PE", "Conditional Return If Parity Even", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5; }); + AddStandardInstruction(0xE0, DynamicCycleHandling, "RET PO", "Conditional Return If Parity Odd", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5; }); + + AddStandardInstruction(0xC7, 11, "RST 0", "Restart at 0h", _ => { _pc.SetAndSaveExisting(0x00); }); + AddStandardInstruction(0xCF, 11, "RST 08H", "Restart at 08h", _ => { _pc.SetAndSaveExisting(0x08); }); + AddStandardInstruction(0xD7, 11, "RST 10H", "Restart at 10h", _ => { _pc.SetAndSaveExisting(0x10); }); + AddStandardInstruction(0xDF, 11, "RST 18H", "Restart at 18h", _ => { _pc.SetAndSaveExisting(0x18); }); + AddStandardInstruction(0xE7, 11, "RST 20H", "Restart at 20h", _ => { _pc.SetAndSaveExisting(0x20); }); + AddStandardInstruction(0xEF, 11, "RST 28H", "Restart at 28h", _ => { _pc.SetAndSaveExisting(0x28); }); + AddStandardInstruction(0xF7, 11, "RST 30H", "Restart at 30h", _ => { _pc.SetAndSaveExisting(0x30); }); + AddStandardInstruction(0xFF, 11, "RST 38H", "Restart at 38h", _ => { _pc.SetAndSaveExisting(0x38); }); - AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { ResetProgramCounterFromStack(); _io.ClearMaskableInterrupt(); }); - AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { ResetProgramCounterFromStack(); _interruptFlipFlop1 = _interruptFlipFlop2; }); + AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { _pc.SetFromStack(); _io.ClearMaskableInterrupt(); }); + AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { _pc.SetFromStack(); _interruptFlipFlop1 = _interruptFlipFlop2; }); } private void PopulateArthmeticAndLogicalInstructions() @@ -292,12 +292,12 @@ private void PopulateArthmeticAndLogicalInstructions() CompareIncrement(); - if (_bc.Word != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) + if (_bc.Value != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) { // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - var currentPc = _pc.GetValue(); + var currentPc = _pc.Value; _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } @@ -321,7 +321,7 @@ private void PopulateArthmeticAndLogicalInstructions() CompareDecrement(); - if (_bc.Word != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) + if (_bc.Value != 0 && !_flags.IsFlagSet(Z80StatusFlags.ZeroZ)) { // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -390,9 +390,9 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xDD, 0x23, 10, "INC IX", "Increment", _ => { Increment16Bit(ref _ix); }); AddDoubleByteInstruction(0xFD, 0x23, 10, "INC IY", "Increment", _ => { Increment16Bit(ref _iy); }); - AddStandardInstruction(0x34, 11, "INC (HL)", "Increment (indirect)", _ => { IncrementAtRegisterMemoryLocation(_hl, 0); }); - AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_ix, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_iy, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x34, 11, "INC (HL)", "Increment (indirect)", _ => { IncrementMemory(_hl, 0); }); + AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { IncrementMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementMemory(_iy, _pc.GetNextDataByte()); }); AddStandardInstruction(0x3D, 4, "DEC A", "Decrement A", _ => { Decrement8Bit(ref _af.High); }); AddStandardInstruction(0x05, 4, "DEC B", "Decrement B", _ => { Decrement8Bit(ref _bc.High); }); @@ -409,9 +409,9 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xDD, 0x2B, 10, "DEC IX", "Decrement IX", _ => { Decrement16Bit(ref _ix); }); AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { Decrement16Bit(ref _iy); }); - AddStandardInstruction(0x35, 11, "DEC (HL)", "", _ => { DecrementAtRegisterMemoryLocation(_hl, 0); }); - AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_ix, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_iy, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x35, 11, "DEC (HL)", "", _ => { DecrementMemory(_hl); }); + AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement", _ => { DecrementMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementMemory(_iy, _pc.GetNextDataByte()); }); AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => { @@ -649,42 +649,42 @@ private void PopulateLoadAndExchangeInstructions() // Since 0x76 is a seperate instruction (halt) we can't do a mask using all lower 3 bits so skip 0x76 and add 0x77 manually AddStandardInstruction(0x77, 7, "LD (HL),A", "Load A into (HL)", i => { LoadRR(i.OpCode); }); - AddStandardInstruction(0x0A, 7, "LD A,(BC)", "Load A from data in memory location at BC", _ => { _accumulator.SetFromDataAtMemoryLocation(_bc.Word); }); - AddStandardInstruction(0x1A, 7, "LD A,(DE)", "Load A from data in memory location at DE", _ => { _accumulator.SetFromDataAtMemoryLocation(_de.Word); }); - AddStandardInstruction(0x2, 7, "LD (BC),A", "Load memory location at BC into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_bc, _accumulator.Value); }); - AddStandardInstruction(0x12, 7, "LD (DE),A", "Load memory location at DE into A", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_de, _accumulator.Value); }); - - AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _accumulator.Value, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x70, 19, "LD (IX+d),B", "Load B into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x71, 19, "LD (IX+d),C", "Load C into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _bc.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x72, 19, "LD (IX+d),D", "Load D into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x73, 19, "LD (IX+d),E", "Load E into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _de.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x74, 19, "LD (IX+d),H", "Load H into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x75, 19, "LD (IX+d),L", "Load L into memory location at IX+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_ix, _hl.Low, _pc.GetNextDataByte()); }); - - AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _accumulator.Value, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x70, 19, "LD (IY+d),B", "Load B into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x71, 19, "LD (IY+d),C", "Load C into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _bc.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x72, 19, "LD (IY+d),D", "Load D into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x73, 19, "LD (IY+d),E", "Load E into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _de.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { SaveTo16BitRegisterMemoryLocationFrom8BitRegister(_iy, _hl.Low, _pc.GetNextDataByte()); }); - - AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { _stack.Set(_hl.Word); }); - AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { _stack.Set(_ix.Word); }); - AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { _stack.Set(_iy.Word); }); - - AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { Load8BitRegisterFrom8BitRegister(_accumulator.Value, ref _iRegister); }); - AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { Load8BitRegisterFrom8BitRegister(_accumulator.Value, ref _rRegister); }); + AddStandardInstruction(0x0A, 7, "LD A,(BC)", "Load A from data in memory location at BC", _ => { _accumulator.SetFromDataInMemory(_bc); }); + AddStandardInstruction(0x1A, 7, "LD A,(DE)", "Load A from data in memory location at DE", _ => { _accumulator.SetFromDataInMemory(_de); }); + AddStandardInstruction(0x2, 7, "LD (BC),A", "Load A to memory at BC", _ => { _accumulator.SaveToMemory(_bc); }); + AddStandardInstruction(0x12, 7, "LD (DE),A", "Load A to memory at DE", _ => { _accumulator.SaveToMemory(_de); }); + + AddDoubleByteInstruction(0xDD, 0x77, 19, "LD (IX+d),A", "Load A into memory location at IX+D", _ => { _accumulator.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x70, 19, "LD (IX+d),B", "Load B into memory location at IX+D", _ => { _b.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x71, 19, "LD (IX+d),C", "Load C into memory location at IX+D", _ => { _c.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x72, 19, "LD (IX+d),D", "Load D into memory location at IX+D", _ => { _d.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x73, 19, "LD (IX+d),E", "Load E into memory location at IX+D", _ => { _e.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x74, 19, "LD (IX+d),H", "Load H into memory location at IX+D", _ => { _h.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x75, 19, "LD (IX+d),L", "Load L into memory location at IX+D", _ => { _l.SaveToMemory(_ix, _pc.GetNextDataByte()); }); + + AddDoubleByteInstruction(0xFD, 0x77, 19, "LD (IY+d),A", "Load A into memory location at IY+D", _ => { _accumulator.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x70, 19, "LD (IY+d),B", "Load B into memory location at IY+D", _ => { _b.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x71, 19, "LD (IY+d),C", "Load C into memory location at IY+D", _ => { _c.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x72, 19, "LD (IY+d),D", "Load D into memory location at IY+D", _ => { _d.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x73, 19, "LD (IY+d),E", "Load E into memory location at IY+D", _ => { _e.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x74, 19, "LD (IY+d),H", "Load H into memory location at IY+D", _ => { _h.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x75, 19, "LD (IY+d),L", "Load L into memory location at IY+D", _ => { _l.SaveToMemory(_iy, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0xF9, 6, "LD SP,HL", "Load HL into SP", _ => { _stack.Set(_hl); }); + AddDoubleByteInstruction(0xDD, 0xF9, 10, "LD SP,IX", "Load IX into SP", _ => { _stack.Set(_ix); }); + AddDoubleByteInstruction(0xFD, 0xF9, 10, "LD SP,IY", "Load IY into SP", _ => { _stack.Set(_iy); }); + + AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { _iRegister.Set(_accumulator); }); + AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { _rRegister.Set(_accumulator); }); AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { _accumulator.SetFromInterruptRegister(_iRegister, _interruptFlipFlop2); }); AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { _accumulator.SetFromMemoryRefreshRegister(_rRegister, _interruptFlipFlop2); }); AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { _accumulator.Set(_pc.GetNextDataByte()); }); - AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { LoadValueInto8BitRegister(ref _bc.High, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { LoadValueInto8BitRegister(ref _bc.Low, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { LoadValueInto8BitRegister(ref _de.High, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x1E, 7, "LD E,N", "Load n into E", _ => { LoadValueInto8BitRegister(ref _de.Low, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x26, 7, "LD H,N", "Load n into H", _ => { LoadValueInto8BitRegister(ref _hl.High, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x2E, 7, "LD L,N", "Load n into L", _ => { LoadValueInto8BitRegister(ref _hl.Low, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { _b.Set(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { _c.Set(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { _d.Set(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x1E, 7, "LD E,N", "Load n into E", _ => { _e.Set(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x26, 7, "LD H,N", "Load n into H", _ => { _h.Set(_pc.GetNextDataByte()); }); + AddStandardInstruction(0x2E, 7, "LD L,N", "Load n into L", _ => { _l.Set(_pc.GetNextDataByte()); }); // These are undocumented but zxdoc still runs them // https://iot.onl/asm/z80/opcodes/undocumented/ @@ -775,66 +775,62 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x6D, 9, "LD IYL, IYL", "Load IYL into IYL", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _iy.Low); }); // end undocumented instructions - AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => - { - _accumulator.LoadRegisterFromMemory(_ix.Word, _pc.GetNextDataByte()); - //LoadInto8BitRegisterFromMemory(ref _af.High, _ix.Word, _pc.GetNextDataByte()); - }); - AddDoubleByteInstruction(0xDD, 0x46, 19, "LD B,(IX+d)", "Load memory at IX + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _ix.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x4E, 19, "LD C,(IX+d)", "Load memory at IX + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _ix.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x56, 19, "LD D,(IX+d)", "Load memory at IX + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _ix.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x5E, 19, "LD E,(IX+d)", "Load memory at IX + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _ix.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x66, 19, "LD H,(IX+d)", "Load memory at IX + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _ix.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x6E, 19, "LD L,(IX+d)", "Load memory at IX + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _ix.Word, _pc.GetNextDataByte()); }); - - AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _iy.Word, _pc.GetNextDataByte());}); - AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { LoadInto8BitRegisterFromMemory(ref _bc.High, _iy.Word, _pc.GetNextDataByte());}); - AddDoubleByteInstruction(0xFD, 0x4E, 19, "LD C,(IY+d)", "Load memory at IY + d into C", _ => { LoadInto8BitRegisterFromMemory(ref _bc.Low, _iy.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x56, 19, "LD D,(IY+d)", "Load memory at IY + d into D", _ => { LoadInto8BitRegisterFromMemory(ref _de.High, _iy.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x5E, 19, "LD E,(IY+d)", "Load memory at IY + d into E", _ => { LoadInto8BitRegisterFromMemory(ref _de.Low, _iy.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x66, 19, "LD H,(IY+d)", "Load memory at IY + d into H", _ => { LoadInto8BitRegisterFromMemory(ref _hl.High, _iy.Word, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { LoadInto8BitRegisterFromMemory(ref _hl.Low, _iy.Word, _pc.GetNextDataByte()); }); - - AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { LoadValueIntoRegisterMemoryLocation(_pc.GetNextDataByte(), _hl); }); - AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _ix, offset); }); - AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); LoadValueIntoRegisterMemoryLocation(value, _iy, offset); }); - - AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { LoadInto8BitRegisterFromMemory(ref _af.High, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { LoadInto16BitRegisterFromMemory(ref _hl, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x4B, 20, "LD BC, (NN)", "Load value at memory location NN into BC", _ => { LoadInto16BitRegisterFromMemory(ref _bc, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x5B, 20, "LD DE, (NN)", "Load value at memory location NN into DE", _ => { LoadInto16BitRegisterFromMemory(ref _de, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { _stack.SetStackPointerFromDataInMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x2A, 20, "LD IX, (NN)", "Load value at memory location NN into IX", _ => { LoadInto16BitRegisterFromMemory(ref _ix, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x2A, 20, "LD IY, (NN)", "Load value at memory location NN into IY", _ => { LoadInto16BitRegisterFromMemory(ref _iy, _pc.GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { LoadValueInto16BitRegister(ref _bc, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { LoadValueInto16BitRegister(ref _de, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { LoadValueInto16BitRegister(ref _hl, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { _stack.SetStackPointer(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { LoadValueInto16BitRegister(ref _ix, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { LoadValueInto16BitRegister(ref _iy, _pc.GetNextTwoDataBytes()); }); - - AddStandardInstruction(0x32, 13, "LD (NN),A", "Load A into memory location NN", _ => { Save8BitRegisterValueToMemory(_af.High, _pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0x22, 16, "LD (NN),HL", "Load HL into memory location NN", _ => { Save16BitRegisterToMemory(_hl, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x43, 20, "LD(NN), BC", "Load BC into memory location NN", _ => { Save16BitRegisterToMemory(_bc, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { Save16BitRegisterToMemory(_de, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { Save16BitRegisterToMemory(_ix, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { Save16BitRegisterToMemory(_iy, _pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { Save16BitRegisterToMemory(_stack.AsRegister(), _pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => { _accumulator.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x46, 19, "LD B,(IX+d)", "Load memory at IX + d into B", _ => { _b.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x4E, 19, "LD C,(IX+d)", "Load memory at IX + d into C", _ => { _c.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x56, 19, "LD D,(IX+d)", "Load memory at IX + d into D", _ => { _d.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x5E, 19, "LD E,(IX+d)", "Load memory at IX + d into E", _ => { _e.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x66, 19, "LD H,(IX+d)", "Load memory at IX + d into H", _ => { _h.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x6E, 19, "LD L,(IX+d)", "Load memory at IX + d into L", _ => { _l.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); + + AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { _accumulator.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte());}); + AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { _b.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte());}); + AddDoubleByteInstruction(0xFD, 0x4E, 19, "LD C,(IY+d)", "Load memory at IY + d into C", _ => { _c.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x56, 19, "LD D,(IY+d)", "Load memory at IY + d into D", _ => { _d.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x5E, 19, "LD E,(IY+d)", "Load memory at IY + d into E", _ => { _e.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x66, 19, "LD H,(IY+d)", "Load memory at IY + d into H", _ => { _h.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { _l.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { WriteToMemory(_hl, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); WriteToMemory(_ix, value, offset); }); + AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); WriteToMemory(_iy, value, offset); }); + + AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { _accumulator.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { _hl.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x4B, 20, "LD BC, (NN)", "Load value at memory location NN into BC", _ => { _bc.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x5B, 20, "LD DE, (NN)", "Load value at memory location NN into DE", _ => { _de.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x7B, 20, "LD SP, (NN)", "Load value at memory location NN into SP", _ => { _stack.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x2A, 20, "LD IX, (NN)", "Load value at memory location NN into IX", _ => { _ix.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x2A, 20, "LD IY, (NN)", "Load value at memory location NN into IY", _ => { _iy.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { _bc.Set(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { _de.Set(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { _hl.Set(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { _stack.Set(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { _ix.Set(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { _iy.Set(_pc.GetNextTwoDataBytes()); }); + + AddStandardInstruction(0x32, 13, "LD (NN),A", "Load A into memory location NN", _ => { _accumulator.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0x22, 16, "LD (NN),HL", "Load HL into memory location NN", _ => { _hl.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x43, 20, "LD(NN), BC", "Load BC into memory location NN", _ => { _bc.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { _de.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { _ix.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { _iy.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { _stack.SaveToMemory(_pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => { - CopyMemoryByRegisterLocations(_hl, _de); + CopyMemory(_hl, _de); Increment16Bit(ref _hl); Increment16Bit(ref _de); Decrement16Bit(ref _bc); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); }); AddDoubleByteInstruction(0xED, 0xB0, DynamicCycleHandling, "LDIR", "Load, Increment, Repeat", _ => { - if (_bc.Word == 0) + if (_bc.Value == 0) { // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this //_bc.Word = 64 * 1024; @@ -843,15 +839,15 @@ private void PopulateLoadAndExchangeInstructions() return; } - CopyMemoryByRegisterLocations(_hl, _de); + CopyMemory(_hl, _de); Increment16Bit(ref _hl); Increment16Bit(ref _de); Decrement16Bit(ref _bc); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); - if (_bc.Word != 0) + if (_bc.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -866,17 +862,17 @@ private void PopulateLoadAndExchangeInstructions() }); AddDoubleByteInstruction(0xED, 0xA8, 16, "LDD", "Load and Decrement", _ => { - CopyMemoryByRegisterLocations(_hl, _de); + CopyMemory(_hl, _de); Decrement16Bit(ref _hl); Decrement16Bit(ref _de); Decrement16Bit(ref _bc); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); }); AddDoubleByteInstruction(0xED, 0xB8, DynamicCycleHandling, "LDDR", "Load, Decrement, Repeat", _ => { - if (_bc.Word == 0) + if (_bc.Value == 0) { // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this //_bc.Word = 64 * 1024; @@ -885,7 +881,7 @@ private void PopulateLoadAndExchangeInstructions() return; } - CopyMemoryByRegisterLocations(_hl, _de); + CopyMemory(_hl, _de); Decrement16Bit(ref _hl); Decrement16Bit(ref _de); Decrement16Bit(ref _bc); @@ -894,7 +890,7 @@ private void PopulateLoadAndExchangeInstructions() // This is not a typo, for some reason in LDDR the PV flag is reset unlike the other LDx instructions _flags.ClearFlag(Z80StatusFlags.ParityOverflowPV); - if (_bc.Word != 0) + if (_bc.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -927,7 +923,7 @@ private void PopulateExchangeBlockTransferAndSearchInstructions() { AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _hl); }); AddStandardInstruction(0x8, 4, "EX AF,AF'", "Exchange AF and AF Shadow", _ => { SwapRegisters(ref _af, ref _afShadow); }); - AddStandardInstruction(0xEB, 4, "EX DE,HL", "Exchange DE and HL", _ => { SwapRegisters(ref _de, ref _hl); }); + AddStandardInstruction(0xEB, 4, "EX DE,HL", "Exchange DE and HL", _ => { _hl.SwapWithDeRegister(_de); }); AddStandardInstruction(0xD9, 4, "EXX", "Exchange BC, DE, HL with Shadow Registers", _ => { SwapRegisters(ref _bc, ref _bcShadow); SwapRegisters(ref _de, ref _deShadow); SwapRegisters(ref _hl, ref _hlShadow); }); AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _ix); }); AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _iy); }); @@ -935,23 +931,23 @@ private void PopulateExchangeBlockTransferAndSearchInstructions() private void PopulateInputOutputInstructions() { - AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _af.High = ReadFromIo(_af.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xED, 0x70, 12, "IN (C)", "Read I/O at B/C But Only Set Flags", _ => { ReadFromIoAndSetFlags(_bc.High, _bc.Low); }); - AddDoubleByteInstruction(0xED, 0x78, 12, "IN A,(C)", "Read I/O at B/C into A with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _af.High); }); - AddDoubleByteInstruction(0xED, 0x40, 12, "IN B,(C)", "Read I/O at B/C into B with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _bc.High); }); - AddDoubleByteInstruction(0xED, 0x48, 12, "IN C,(C)", "Read I/O at B/C into C with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _bc.Low); }); - AddDoubleByteInstruction(0xED, 0x50, 12, "IN D,(C)", "Read I/O at B/C into D with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _de.High); }); - AddDoubleByteInstruction(0xED, 0x58, 12, "IN E,(C)", "Read I/O at B/C into E with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _de.Low); }); - AddDoubleByteInstruction(0xED, 0x60, 12, "IN H,(C)", "Read I/O at B/C into H with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _hl.High); }); - AddDoubleByteInstruction(0xED, 0x68, 12, "IN L,(C)", "Read I/O at B/C into L with flags", _ => { ReadFromIoIntoRegister(_bc.High, _bc.Low, ref _hl.Low); }); + AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _accumulator.Set(_ioManagement.Read(_af.High, _pc.GetNextDataByte(), false)); }); + AddDoubleByteInstruction(0xED, 0x70, 12, "IN (C)", "Read I/O at B/C But Only Set Flags", _ => { _ioManagement.Read(_bc.High, _bc.Low, true); }); + AddDoubleByteInstruction(0xED, 0x78, 12, "IN A,(C)", "Read I/O at B/C into A with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _accumulator); }); + AddDoubleByteInstruction(0xED, 0x40, 12, "IN B,(C)", "Read I/O at B/C into B with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _b); }); + AddDoubleByteInstruction(0xED, 0x48, 12, "IN C,(C)", "Read I/O at B/C into C with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _c); }); + AddDoubleByteInstruction(0xED, 0x50, 12, "IN D,(C)", "Read I/O at B/C into D with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _d); }); + AddDoubleByteInstruction(0xED, 0x58, 12, "IN E,(C)", "Read I/O at B/C into E with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _e); }); + AddDoubleByteInstruction(0xED, 0x60, 12, "IN H,(C)", "Read I/O at B/C into H with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _h); }); + AddDoubleByteInstruction(0xED, 0x68, 12, "IN L,(C)", "Read I/O at B/C into L with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _l); }); AddDoubleByteInstruction(0xED, 0xA2, 16, "INI", "Input and Increment", _ => { var portAddress = (ushort)((_bc.High << 8) + _bc.Low); var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Increment16Bit(ref _hl); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Increment(); _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); @@ -968,9 +964,9 @@ private void PopulateInputOutputInstructions() var portAddress = (ushort)((_bc.High << 8) + _bc.Low); var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Increment16Bit(ref _hl); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Increment(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); @@ -992,9 +988,9 @@ private void PopulateInputOutputInstructions() { var portAddress = (ushort)((_bc.High << 8) + _bc.Low); var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Decrement16Bit(ref _hl); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Decrement(); _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); @@ -1011,9 +1007,9 @@ private void PopulateInputOutputInstructions() var portAddress = (ushort)((_bc.High << 8) + _bc.Low); var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Decrement16Bit(ref _hl); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Decrement(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); @@ -1032,23 +1028,23 @@ private void PopulateInputOutputInstructions() }); - AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { WriteToIo(_af.High, _pc.GetNextDataByte(), _af.High); }); - AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { WriteToIo(_bc.High, _bc.Low, _af.High); }); - AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { WriteToIo(_bc.High, _bc.Low, _bc.High); }); - AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { WriteToIo(_bc.High, _bc.Low, _bc.Low); }); - AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { WriteToIo(_bc.High, _bc.Low, _de.High); }); - AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { WriteToIo(_bc.High, _bc.Low, _de.Low); }); - AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { WriteToIo(_bc.High, _bc.Low, _hl.High); }); - AddDoubleByteInstruction(0xED, 0x69, 12, "OUT (C),L", "Write I/O at B/C from L", _ => { WriteToIo(_bc.High, _bc.Low, _hl.Low); }); + AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { _ioManagement.Write(_af.High, _pc.GetNextDataByte(), _accumulator); }); + AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { _ioManagement.Write(_b.Value, _bc.Low, _accumulator); }); + AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { _ioManagement.Write(_b.Value, _bc.Low, _b); }); + AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { _ioManagement.Write(_b.Value, _bc.Low, _c); }); + AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { _ioManagement.Write(_b.Value, _bc.Low, _d); }); + AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { _ioManagement.Write(_b.Value, _bc.Low, _e); }); + AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { _ioManagement.Write(_b.Value, _bc.Low, _h); }); + AddDoubleByteInstruction(0xED, 0x69, 12, "OUT (C),L", "Write I/O at B/C from L", _ => { _ioManagement.Write(_b.Value, _bc.Low, _l); }); AddDoubleByteInstruction(0xED, 0xA3, 16, "OUTI", "Output and Increment", _ => { - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); var portAddress = (ushort)((_bc.High << 8) + _bc.Low); _io.WritePort(portAddress, data); - Increment16Bit(ref _hl); + _hl.Increment(); _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); @@ -1063,12 +1059,12 @@ private void PopulateInputOutputInstructions() return; } - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); var portAddress = (ushort)((_bc.High << 8) + _bc.Low); _io.WritePort(portAddress, data); - Increment16Bit(ref _hl); + _hl.Increment(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); @@ -1088,12 +1084,12 @@ private void PopulateInputOutputInstructions() AddDoubleByteInstruction(0xED, 0xAB, 16, "OUTD", "Output and Decrement", _ => { - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); var portAddress = (ushort)((_bc.High << 8) + _bc.Low); _io.WritePort(portAddress, data); - Decrement16Bit(ref _hl); + _hl.Decrement(); _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); @@ -1108,12 +1104,12 @@ private void PopulateInputOutputInstructions() return; } - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); var portAddress = (ushort)((_bc.High << 8) + _bc.Low); _io.WritePort(portAddress, data); - Decrement16Bit(ref _hl); + _hl.Decrement(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 9152363..c251f24 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,6 +1,8 @@ using Kmse.Core.IO; using Kmse.Core.Memory; +using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Memory; using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; @@ -28,16 +30,18 @@ public partial class Z80Cpu : IZ80Cpu private IZ80BcRegister _bc; private IZ80DeRegister _de; private IZ80HlRegister _hl; - private IZ808BitRegister _b; - private IZ808BitRegister _c; - private IZ808BitRegister _d; - private IZ808BitRegister _e; - private IZ808BitRegister _h; - private IZ808BitRegister _l; + private IZ808BitGeneralPurposeRegister _b; + private IZ808BitGeneralPurposeRegister _c; + private IZ808BitGeneralPurposeRegister _d; + private IZ808BitGeneralPurposeRegister _e; + private IZ808BitGeneralPurposeRegister _h; + private IZ808BitGeneralPurposeRegister _l; private IZ80IndexRegisterX _ix; private IZ80IndexRegisterY _iy; private IZ80MemoryRefreshRegister _rRegister; private IZ80InterruptPageAddressRegister _iRegister; + private IZ80CpuInputOutput _ioManagement; + private IZ80CpuMemoryManagement _memoryManagement; /// /// Disables interrupts from being accepted if set to False @@ -64,9 +68,9 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) // TODO: Need to create these indirectly to allow mock interfaces to be injected for testing _af = new Z80AfRegister(memory); - _bc = new Z80BcRegister(memory); - _de = new Z80DeRegister(memory); - _hl = new Z80HlRegister(memory); + _bc = new Z80BcRegister(memory, _af.Flags); + _de = new Z80DeRegister(memory, _af.Flags); + _hl = new Z80HlRegister(memory, _af.Flags); _ix = new Z80IndexRegisterX(memory); _iy = new Z80IndexRegisterY(memory); _rRegister = new Z80MemoryRefreshRegister(memory); @@ -83,6 +87,9 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _stack = new Z80StackManager(memory, _cpuLogger); _pc = new Z80ProgramCounter(memory, _instructionLogger, _flags, _stack); + + _ioManagement = new Z80CpuInputOutput(_io, _flags); + _memoryManagement = new Z80CpuMemoryManagement(_memory, _flags); } public CpuStatus GetStatus() From 30dd24eb11a7494be025a41c22b329ca3c06b048 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:56:50 +1000 Subject: [PATCH 05/21] Finish initial restructuring of registers and moving operations out of Z80CPU into registers This creates some common base classes but flags and AF register needs to be treated specially since many common base operations require flags which creates a wierd circular dependency so it reimplements some similar methods to avoid this --- .../Z80CpuTests/Z80StackManagementFixture.cs | 17 +- .../Z80/Registers/General/IZ80Accumulator.cs | 26 +- .../Z80/Registers/General/IZ80AfRegister.cs | 15 +- .../Z80/Registers/General/IZ80BcRegister.cs | 6 +- .../Z80/Registers/General/IZ80DeRegister.cs | 6 +- .../Z80/Registers/General/IZ80FlagsManager.cs | 19 +- .../Z80/Registers/General/IZ80HlRegister.cs | 6 +- .../Z80/Registers/General/Z80Accumulator.cs | 330 ++++++- .../Z80/Registers/General/Z80AfRegister.cs | 94 +- .../Z80/Registers/General/Z80BcRegister.cs | 8 +- .../Z80/Registers/General/Z80DeRegister.cs | 8 +- .../Z80/Registers/General/Z80FlagsManager.cs | 35 +- .../Z80/Registers/General/Z80HlRegister.cs | 8 +- .../Registers/IZ8016BitCombinedRegister.cs | 10 - .../IZ8016BitGeneralPurposeRegister.cs | 27 + Kmse.Core/Z80/Registers/IZ8016BitRegister.cs | 27 +- .../Z80/Registers/IZ8016BitSpecialRegister.cs | 20 + .../IZ808BitGeneralPurposeRegister.cs | 14 +- .../SpecialPurpose/IZ80IndexRegisterX.cs | 2 +- .../SpecialPurpose/IZ80IndexRegisterY.cs | 2 +- .../SpecialPurpose/IZ80ProgramCounter.cs | 12 +- .../SpecialPurpose/IZ80StackManager.cs | 2 +- .../SpecialPurpose/Z80IndexRegisterX.cs | 5 +- .../SpecialPurpose/Z80IndexRegisterY.cs | 5 +- .../Z80InterruptPageAddressRegister.cs | 3 +- .../Z80MemoryRefreshRegister.cs | 4 +- .../SpecialPurpose/Z80ProgramCounter.cs | 22 +- .../SpecialPurpose/Z80StackManager.cs | 7 +- ... => Z8016BitGeneralPurposeRegisterBase.cs} | 45 +- .../Z80/Registers/Z8016BitRegisterBase.cs | 171 +++- .../Registers/Z8016BitSpecialRegisterBase.cs | 87 ++ .../Z808BitGeneralPurposeRegister.cs | 83 +- Kmse.Core/Z80/Registers/Z808BitRegister.cs | 22 +- Kmse.Core/Z80/Registers/Z80RegisterBase.cs | 175 ++++ Kmse.Core/Z80/Z80Cpu.CoreOperations.cs | 60 -- .../Z80/Z80Cpu.MathAndLogicOperations.cs | 802 ------------------ Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs | 165 ++++ Kmse.Core/Z80/Z80Cpu.cs | 10 +- 38 files changed, 1305 insertions(+), 1055 deletions(-) delete mode 100644 Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs create mode 100644 Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs create mode 100644 Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs rename Kmse.Core/Z80/Registers/{Z8016BitCombinedRegisterBase.cs => Z8016BitGeneralPurposeRegisterBase.cs} (75%) create mode 100644 Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs create mode 100644 Kmse.Core/Z80/Registers/Z80RegisterBase.cs delete mode 100644 Kmse.Core/Z80/Z80Cpu.CoreOperations.cs delete mode 100644 Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs create mode 100644 Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs index 0b4cfaa..21401e0 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -2,6 +2,7 @@ using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; using NSubstitute; using NUnit.Framework; @@ -12,6 +13,7 @@ public class Z80StackManagementFixture { private ICpuLogger _cpuLogger; private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; private Z80StackManager _stackManager; [SetUp] @@ -19,7 +21,8 @@ public void Setup() { _memory = Substitute.For(); _cpuLogger = Substitute.For(); - _stackManager = new Z80StackManager(_memory, _cpuLogger); + _flags = Substitute.For(); + _stackManager = new Z80StackManager(_memory, _cpuLogger, _flags); _memory.GetMinimumAvailableMemorySize().Returns(100); _memory.GetMaximumAvailableMemorySize().Returns(0x5000); @@ -94,7 +97,7 @@ public void WhenStackPointerDecrementedWhichOverflowsThenThrowsException() [Test] public void WhenPushingRegisterToStackThenValueIsWrittenToMemoryAtStackPointerAddressAndPointerDecremented() { - var register = new Test16BitClass(_memory); + var register = new Test16BitClass(_memory, _flags); register.Set(0x1234); _stackManager.Set(1000); _stackManager.PushRegisterToStack(register); @@ -112,7 +115,7 @@ public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointer _memory[1000].Returns((byte)0x23); _memory[1001].Returns((byte)0x67); - var register = new Test16BitClass(_memory); + var register = new Test16BitClass(_memory, _flags); register.Set(0x00); _stackManager.Set(1000); _stackManager.PopRegisterFromStack(register); @@ -124,12 +127,12 @@ public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointer } [Test] - public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueIsReturnedAndStackpointerUnchanged() + public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueIsReturnedAndStackPointerUnchanged() { _memory[1000].Returns((byte)0x23); _memory[1001].Returns((byte)0x47); - var register = new Test16BitClass(_memory); + var register = new Test16BitClass(_memory, _flags); register.Set(0x1234); _stackManager.Set(1000); _stackManager.SwapRegisterWithDataAtStackPointerAddress(register); @@ -143,8 +146,8 @@ public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueI _stackManager.AsRegister().Word.Should().Be(1000); } - private class Test16BitClass : Z8016BitRegisterBase + private class Test16BitClass : Z8016BitSpecialRegisterBase { - public Test16BitClass(IMasterSystemMemory memory) : base(memory) { } + public Test16BitClass(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs index e650a17..3255688 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs @@ -1,9 +1,33 @@ using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; -public interface IZ80Accumulator : IZ808BitRegister +public interface IZ80Accumulator : IZ808BitGeneralPurposeRegister { void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status); void SetFromMemoryRefreshRegister(IZ80MemoryRefreshRegister register, bool interruptFlipFlop2Status); + + void RotateLeftDigit(IZ80HlRegister hl); + void RotateRightDigit(IZ80HlRegister hl); + + void DecimalAdjustAccumulator(); + void InvertAccumulatorRegister(); + void NegateAccumulatorRegister(); + void AddFromMemory(Z80Register register, int offset, bool withCarry = false); + void AddFromMemory(Z80Register register, int offset, byte valueToAndAgainst); + void Add(byte value, bool withCarry = false); + void SubtractFromMemory(Z80Register register, int offset, bool withCarry = false); + void Subtract(byte value, bool withCarry = false); + void Compare(byte value); + void CompareFromMemory(Z80Register register, int offset); + void And(byte value, byte valueToAndAgainst); + void Or(byte value, byte valueToAndAgainst); + void OrFromMemory(Z80Register register, int offset, byte valueToOrAgainst); + void Xor(byte value, byte valueToXorAgainst); + void XorFromMemory(Z80Register register, int offset, byte valueToXorAgainst); + void RotateLeftCircularAccumulator(); + void RotateLeftAccumulator(); + void RotateRightCircularAccumulator(); + void RotateRightAccumulator(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs index e2e68cc..ae00506 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs @@ -1,10 +1,21 @@ -namespace Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.General; /// /// Provides a 16 bit interface to the combined Accumulator and Flag (AF) register /// -public interface IZ80AfRegister : IZ8016BitCombinedRegister +/// +/// Note that AF is technically a special purpose register but all the others operates on Flags but this one contains +/// the flags +/// So it is a special case +/// +public interface IZ80AfRegister : IZ8016BitRegister { + ushort ShadowValue { get; } + IZ80Accumulator Accumulator { get; } IZ80FlagsManager Flags { get; } + void SwapWithShadow(); + Z80Register ShadowAsRegister(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs index b4730cb..8821b28 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs @@ -1,7 +1,7 @@ namespace Kmse.Core.Z80.Registers.General; -public interface IZ80BcRegister : IZ8016BitCombinedRegister +public interface IZ80BcRegister : IZ8016BitGeneralPurposeRegister { - IZ808BitRegister B { get; } - IZ808BitRegister C { get; } + IZ808BitGeneralPurposeRegister B { get; } + IZ808BitGeneralPurposeRegister C { get; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs index 7c3054a..a62e91e 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs @@ -1,7 +1,7 @@ namespace Kmse.Core.Z80.Registers.General; -public interface IZ80DeRegister : IZ8016BitCombinedRegister +public interface IZ80DeRegister : IZ8016BitGeneralPurposeRegister { - IZ808BitRegister D { get; } - IZ808BitRegister E { get; } + IZ808BitGeneralPurposeRegister D { get; } + IZ808BitGeneralPurposeRegister E { get; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs index 7e1cafa..20d848b 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -2,8 +2,25 @@ namespace Kmse.Core.Z80.Registers.General; -public interface IZ80FlagsManager : IZ808BitRegister +/// +/// Register for CPU Flags +/// +/// +/// Even though this is a general purpose register, it only is used via flag interfaces primarily +/// So we don't use the IZ80Register interface +/// Also we avoid implementing this same as normal general purpose registers which have helper methods that affect +/// flags +/// but obviously the flags register doesn't do any of that +/// +public interface IZ80FlagsManager { + byte Value { get; } + byte ShadowValue { get; } + + void Reset(); + void Set(byte value); + void SwapWithShadow(); + void SetFlag(Z80StatusFlags flags); void ClearFlag(Z80StatusFlags flags); void InvertFlag(Z80StatusFlags flag); diff --git a/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs index 96a9869..bc3579d 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs @@ -1,9 +1,9 @@ namespace Kmse.Core.Z80.Registers.General; -public interface IZ80HlRegister : IZ8016BitCombinedRegister +public interface IZ80HlRegister : IZ8016BitGeneralPurposeRegister { - IZ808BitRegister H { get; } - IZ808BitRegister L { get; } + IZ808BitGeneralPurposeRegister H { get; } + IZ808BitGeneralPurposeRegister L { get; } void SwapWithDeRegister(IZ80DeRegister register); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index d418c27..8752291 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -8,9 +8,7 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80Accumulator : Z808BitGeneralPurposeRegister, IZ80Accumulator { public Z80Accumulator(IZ80FlagsManager flags, IMasterSystemMemory memory) - : base(memory, flags) - { - } + : base(memory, flags) { } public void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status) { @@ -68,6 +66,332 @@ public void RotateRightDigit(IZ80HlRegister hl) Value = newAValue; } + public void DecimalAdjustAccumulator() + { + /* + Credit to https://github.com/xdanieldzd/MasterFudge for referencing this document - https://worldofspectrum.org/faq/reference/z80reference.htm which explained how to do this in a clear and simple way + + The purpose of the DAA (Decimal Adjust Accumulator) instruction is to make an adjustment to the value in the A register, after performing a binary mathematical operation, such that the result is as if the operation were performed with BCD (Binary Coded Decimal) maths. The Z80 achieves this by adjusting the A register by a value which is dependent upon the value of the A register, the Carry flag, Half-Carry flag (carry from bit 3 to 4), and the N-flag (which defines whether the last operation was an add or subtract). + + The algorithm used is as follows: + + - If the A register is greater than 0x99, OR the Carry flag is SET, then + The upper four bits of the Correction Factor are set to 6, + and the Carry flag will be SET. + Else + The upper four bits of the Correction Factor are set to 0, + and the Carry flag will be CLEARED. + + - If the lower four bits of the A register (A AND 0x0F) is greater than 9, + OR the Half-Carry (H) flag is SET, then + The lower four bits of the Correction Factor are set to 6. + Else + The lower four bits of the Correction Factor are set to 0. + + - This results in a Correction Factor of 0x00, 0x06, 0x60 or 0x66. + - If the N flag is CLEAR, then + ADD the Correction Factor to the A register. + Else + SUBTRACT the Correction Factor from the A register. + + - The Flags are set as follows: + + Carry: Set/clear as in the first step above. + Half-Carry: Set if the correction operation caused a binary carry/borrow + from bit 3 to bit 4. + For this purpose, may be calculated as: + Bit 4 of: A(before) XOR A(after). + S,Z,P,5,3: Set as for simple logic operations on the resultant A value. + N: Leave. + + */ + + byte factor = 0; + var currentValue = Value; + if (Value > 0x99 || Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + factor |= 0x06 << 4; + Flags.SetFlag(Z80StatusFlags.CarryC); + } + else + { + factor &= 0x0F; + Flags.ClearFlag(Z80StatusFlags.CarryC); + } + + if ((Value & 0x0F) > 9 || Flags.IsFlagSet(Z80StatusFlags.HalfCarryH)) + { + factor |= 0x06; + } + else + { + factor &= 0xF0; + } + + if (!Flags.IsFlagSet(Z80StatusFlags.AddSubtractN)) + { + Value += factor; + } + else + { + Value -= factor; + } + + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, Bitwise.IsSet(currentValue ^ Value, 4)); + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(Value, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, Value == 0); + Flags.SetParityFromValue(Value); + } + + public void InvertAccumulatorRegister() + { + var onesComplementValue = ~Value & 0xFF; + + Flags.SetFlag(Z80StatusFlags.HalfCarryH); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + + Value = (byte)onesComplementValue; + } + + public void NegateAccumulatorRegister() + { + var twosComplementValue = (0 - Value) & 0xFF; + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, twosComplementValue == 0); + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (Value & 0x0F) > 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, Value == 0x80); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value != 0x00); + + Value = (byte)twosComplementValue; + } + + public void AddFromMemory(Z80Register register, int offset, bool withCarry = false) + { + var value = Memory[(ushort)(register.Word + offset)]; + Add(value, withCarry); + } + + public void Add(byte value, bool withCarry = false) + { + int valueWithCarry = value; + if (withCarry && Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + valueWithCarry = value + 0x01; + } + + var newValue = Value + valueWithCarry; + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + // Half carry occurs if the result of adding the lower nibbles means it will set the next higher bit (basically overflows) + // This is since the adding is done in two 4 bit operations not 1 8 bit operation internally + // This is then combined with the DAA instruction which adjusts the result to get the valid value + //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ newValue ^ value) & 0x10) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value ^ 0x80) & (Value ^ newValue) & 0x80) != 0); + + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + // A carry is same as half carry just on the overall value + // Since we stored the sum as a 32 bit integer, we can see if went past the max value and bit 7 must have carried over into bit 8 + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, newValue > 0xFF); + + Set((byte)newValue); + } + + public void SubtractFromMemory(Z80Register register, int offset, bool withCarry = false) + { + var value = Memory[(ushort)(register.Word + offset)]; + Subtract(value, withCarry); + } + + public void Subtract(byte value, bool withCarry = false) + { + int valueWithCarry = value; + if (withCarry && Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + valueWithCarry += 0x01; + } + + var newValue = Value - valueWithCarry; + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + // Half carry occurs if the result of subtracting the higher nibbles means it will set the next lower bit (basically underflows) + // We check if the subtraction means that adding higher nibbles sets bit 3 + // This is then combined with the DAA instruction which adjusts the result to get the valid value + //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ newValue ^ value) & 0x10) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value) & (Value ^ newValue) & 0x80) != 0); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + + // Subtraction went negative, so carried over into next bit + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value < valueWithCarry); + + Set((byte)newValue); + } + + public void Compare(byte value) + { + // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not + var difference = Value - value; + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ difference ^ value) & 0x10) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value) & (Value ^ difference) & 0x80) != 0); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + + // Subtraction went negative, so carried over into next bit + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value < value); + } + + public void CompareFromMemory(Z80Register register, int offset) + { + var value = Memory[(ushort)(register.Word + offset)]; + Compare(value); + } + + public void And(byte value, byte valueToAndAgainst) + { + var newValue = (byte)(value & valueToAndAgainst); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void AddFromMemory(Z80Register register, int offset, byte valueToAndAgainst) + { + var value = Memory[(ushort)(register.Word + offset)]; + And(value, valueToAndAgainst); + } + + public void Or(byte value, byte valueToAndAgainst) + { + var newValue = (byte)(value | valueToAndAgainst); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void OrFromMemory(Z80Register register, int offset, byte valueToOrAgainst) + { + var value = Memory[(ushort)(register.Word + offset)]; + Or(value, valueToOrAgainst); + } + + public void Xor(byte value, byte valueToXorAgainst) + { + var newValue = (byte)(value ^ valueToXorAgainst); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void XorFromMemory(Z80Register register, int offset, byte valueToXorAgainst) + { + var value = Memory[(ushort)(register.Word + offset)]; + Xor(value, valueToXorAgainst); + } + + public void RotateLeftCircularAccumulator() + { + // Rotate A left by 1 bit, bit 7 is copied to carry flag and bit 0 + // This is special method since RLCA flags are set differently to RLC r instruction + var newValue = (byte)(Value << 1); + var bit7Set = Bitwise.IsSet(Value, 7); + if (bit7Set) + { + // Copy bit 7 to bit 0 + Bitwise.Set(ref newValue, 0); + } + + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + + Value = newValue; + } + + public void RotateLeftAccumulator() + { + // Rotate A left by 1 bit, bit 7 is copied to carry flag and carry flag copied to bit 0 + // This is special method since RLA flags are set differently to RL r instruction + var newValue = (byte)(Value << 1); + var bit7Set = Bitwise.IsSet(Value, 7); + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + // Copy carry flag to bit 0 + Bitwise.Set(ref newValue, 0); + } + + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + + Value = newValue; + } + + public void RotateRightCircularAccumulator() + { + // Rotate A right by 1 bit, bit 0 is copied to carry flag and bit 7 + // This is special method since RRCA flags are set differently to RRC r instruction + var newValue = (byte)(Value >> 1); + var bit0Set = Bitwise.IsSet(Value, 0); + if (bit0Set) + { + // Copy bit 0 to bit 7 + Bitwise.Set(ref newValue, 7); + } + + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + + Value = newValue; + } + + public void RotateRightAccumulator() + { + // Rotate A right by 1 bit, bit 0 is copied to carry flag and carry flag copied to bit 7 + // This is special method since RRA flags are set differently to RR r instruction + var newValue = (byte)(Value >> 1); + var bit0Set = Bitwise.IsSet(Value, 0); + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + // Copy carry flag to bit 7 + Bitwise.Set(ref newValue, 7); + } + + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + + Value = newValue; + } + private void LoadSpecial8BitRegisterToAccumulator(byte sourceData, bool interruptFlipFlop2Status) { Value = sourceData; diff --git a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs index 985aaa4..f162f96 100644 --- a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -1,18 +1,102 @@ using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; -public class Z80AfRegister : Z8016BitCombinedRegisterBase, IZ80AfRegister +public class Z80AfRegister : IZ80AfRegister { + private readonly IMasterSystemMemory _memory; + public Z80AfRegister(IMasterSystemMemory memory) - : base(memory) { - Flags = new Z80FlagsManager(memory); + _memory = memory; + Flags = new Z80FlagsManager(); Accumulator = new Z80Accumulator(Flags, memory); } - protected override IZ808BitRegister HighRegister => Accumulator; - protected override IZ808BitRegister LowRegister => Flags; public IZ80Accumulator Accumulator { get; } public IZ80FlagsManager Flags { get; } + + public ushort Value => (ushort)(Flags.Value + (Accumulator.Value << 8)); + public byte High => Accumulator.Value; + public byte Low => Flags.Value; + public ushort ShadowValue => (ushort)(Flags.ShadowValue + (Accumulator.ShadowValue << 8)); + + public void Reset() + { + Flags.Reset(); + Accumulator.Reset(); + } + + // TODO: Remove any methods not used, since mixed with flags, very few operations act on AF as a 16 bit register + + public void Set(ushort value) + { + var (high, low) = Bitwise.ToBytes(value); + Flags.Set(low); + Accumulator.Set(high); + } + + public void Set(IZ8016BitRegister register) + { + Set(register.Value); + } + + public void SetHigh(byte value) + { + Accumulator.Set(value); + } + + public void SetLow(byte value) + { + Flags.Set(value); + } + + public void SetFromDataInMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + var low = _memory[location]; + location++; + var high = _memory[location]; + + Flags.Set(low); + Accumulator.Set(high); + } + + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + { + SetFromDataInMemory(register.Value, offset); + } + + public void SaveToMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + _memory[location] = Accumulator.Value; + _memory[(ushort)(location + 1)] = Flags.Value; + } + + public void SwapWithShadow() + { + Flags.SwapWithShadow(); + Accumulator.SwapWithShadow(); + } + + public Z80Register AsRegister() + { + return new Z80Register + { + Low = Flags.Value, + High = Accumulator.Value + }; + } + + public Z80Register ShadowAsRegister() + { + return new Z80Register + { + Low = Flags.ShadowValue, + High = Accumulator.ShadowValue + }; + } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs index e38be5c..6b9e5c0 100644 --- a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs @@ -2,10 +2,10 @@ namespace Kmse.Core.Z80.Registers.General; -public class Z80BcRegister : Z8016BitCombinedRegisterBase, IZ80BcRegister +public class Z80BcRegister : Z8016BitGeneralPurposeRegisterBase, IZ80BcRegister { public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) - : base(memory) + : base(memory, flags) { B = new Z808BitGeneralPurposeRegister(memory, flags); C = new Z808BitGeneralPurposeRegister(memory, flags); @@ -13,6 +13,6 @@ public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) protected override IZ808BitRegister HighRegister => B; protected override IZ808BitRegister LowRegister => C; - public IZ808BitRegister B { get; } - public IZ808BitRegister C { get; } + public IZ808BitGeneralPurposeRegister B { get; } + public IZ808BitGeneralPurposeRegister C { get; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs index d128689..4963b75 100644 --- a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs @@ -2,10 +2,10 @@ namespace Kmse.Core.Z80.Registers.General; -public class Z80DeRegister : Z8016BitCombinedRegisterBase, IZ80DeRegister +public class Z80DeRegister : Z8016BitGeneralPurposeRegisterBase, IZ80DeRegister { public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) - : base(memory) + : base(memory, flags) { D = new Z808BitGeneralPurposeRegister(memory, flags); E = new Z808BitGeneralPurposeRegister(memory, flags); @@ -13,6 +13,6 @@ public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) protected override IZ808BitRegister HighRegister => D; protected override IZ808BitRegister LowRegister => E; - public IZ808BitRegister D { get; } - public IZ808BitRegister E { get; } + public IZ808BitGeneralPurposeRegister D { get; } + public IZ808BitGeneralPurposeRegister E { get; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs index 2cbde59..6a80b87 100644 --- a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -1,12 +1,39 @@ -using Kmse.Core.Memory; -using Kmse.Core.Utilities; +using Kmse.Core.Utilities; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; -public class Z80FlagsManager : Z808BitRegister, IZ80FlagsManager +public class Z80FlagsManager : IZ80FlagsManager { - public Z80FlagsManager(IMasterSystemMemory memory) : base(memory) { } + protected byte InternalShadowValue; + protected byte InternalValue; + + public byte Value + { + get => InternalValue; + protected set => InternalValue = value; + } + + public byte ShadowValue + { + get => InternalShadowValue; + protected set => InternalShadowValue = value; + } + + public void Reset() + { + Value = 0; + } + + public void Set(byte value) + { + Value = value; + } + + public void SwapWithShadow() + { + (Value, ShadowValue) = (ShadowValue, Value); + } public void SetFlag(Z80StatusFlags flags) { diff --git a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs index e2a3253..3bd1416 100644 --- a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs @@ -2,10 +2,10 @@ namespace Kmse.Core.Z80.Registers.General; -public class Z80HlRegister : Z8016BitCombinedRegisterBase, IZ80HlRegister +public class Z80HlRegister : Z8016BitGeneralPurposeRegisterBase, IZ80HlRegister { public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) - : base(memory) + : base(memory, flags) { H = new Z808BitGeneralPurposeRegister(memory, flags); L = new Z808BitGeneralPurposeRegister(memory, flags); @@ -13,8 +13,8 @@ public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) protected override IZ808BitRegister HighRegister => H; protected override IZ808BitRegister LowRegister => L; - public IZ808BitRegister H { get; } - public IZ808BitRegister L { get; } + public IZ808BitGeneralPurposeRegister H { get; } + public IZ808BitGeneralPurposeRegister L { get; } public void SwapWithDeRegister(IZ80DeRegister register) { diff --git a/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs deleted file mode 100644 index fbb7917..0000000 --- a/Kmse.Core/Z80/Registers/IZ8016BitCombinedRegister.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80.Registers; - -public interface IZ8016BitCombinedRegister : IZ8016BitRegister -{ - public ushort ShadowValue { get; } - public void SwapWithShadow(); - public Z80Register ShadowAsRegister(); -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs new file mode 100644 index 0000000..917f573 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -0,0 +1,27 @@ +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitGeneralPurposeRegister : IZ8016BitRegister +{ + ushort ShadowValue { get; } + void SwapWithShadow(); + Z80Register ShadowAsRegister(); + + void Increment(); + void Decrement(); + + void ResetBitByRegisterLocation(int bit, int offset); + void SetBitByRegisterLocation(int bit, int offset); + void TestBitByRegisterLocation(int bit, int offset); + void Add(ushort source, bool withCarry = false); + void Subtract(ushort source, bool withCarry = false); + void RotateLeftCircular(int offset); + void RotateLeft(int offset); + void RotateRightCircular(int offset); + void RotateRight(int offset); + void ShiftLeftArithmetic(int offset); + void ShiftRightArithmetic(int offset); + void ShiftLeftLogical(int offset); + void ShiftRightLogical(int offset); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs index 3e9d660..39fa71e 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -4,20 +4,17 @@ namespace Kmse.Core.Z80.Registers; public interface IZ8016BitRegister { - public ushort Value { get; } - public byte High { get; } - public byte Low { get; } + ushort Value { get; } + byte High { get; } + byte Low { get; } - public void Reset(); - public void Set(ushort value); - public void Set(IZ8016BitRegister register); - public void SetHigh(byte value); - public void SetLow(byte value); - public void SetFromDataInMemory(ushort address, byte offset = 0); - public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0); - public void SaveToMemory(ushort address, byte offset = 0); - public Z80Register AsRegister(); - - void Increment(); - void Decrement(); + void Reset(); + void Set(ushort value); + void Set(IZ8016BitRegister register); + void SetHigh(byte value); + void SetLow(byte value); + void SetFromDataInMemory(ushort address, byte offset = 0); + void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0); + void SaveToMemory(ushort address, byte offset = 0); + Z80Register AsRegister(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs new file mode 100644 index 0000000..923b118 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs @@ -0,0 +1,20 @@ +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitSpecialRegister : IZ8016BitRegister +{ + void Increment(); + void Decrement(); + void ResetBitByRegisterLocation(int bit, int offset); + void SetBitByRegisterLocation(int bit, int offset); + void TestBitByRegisterLocation(int bit, int offset); + void Add(ushort source, bool withCarry = false); + void Subtract(ushort source, bool withCarry = false); + void RotateLeftCircular(int offset); + void RotateLeft(int offset); + void RotateRightCircular(int offset); + void RotateRight(int offset); + void ShiftLeftArithmetic(int offset); + void ShiftRightArithmetic(int offset); + void ShiftLeftLogical(int offset); + void ShiftRightLogical(int offset); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs index 1f92044..3b84fb8 100644 --- a/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs @@ -6,6 +6,16 @@ /// public interface IZ808BitGeneralPurposeRegister : IZ808BitRegister { - void Increment(); - void Decrement(); + public void Increment(); + public void Decrement(); + public void ClearBit(int bit); + public void SetBit(int bit); + public void RotateLeftCircular(); + public void RotateLeft(); + public void RotateRightCircular(); + public void RotateRight(); + public void ShiftLeftArithmetic(); + public void ShiftRightArithmetic(); + public void ShiftLeftLogical(); + public void ShiftRightLogical(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs index c0f0d77..8ff8529 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs @@ -1,3 +1,3 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public interface IZ80IndexRegisterX : IZ8016BitRegister { } \ No newline at end of file +public interface IZ80IndexRegisterX : IZ8016BitSpecialRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs index fdbe550..ec634b1 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs @@ -1,3 +1,3 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public interface IZ80IndexRegisterY : IZ8016BitRegister { } \ No newline at end of file +public interface IZ80IndexRegisterY : IZ8016BitSpecialRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs index b32548c..f51bafd 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -2,23 +2,23 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public interface IZ80ProgramCounter : IZ8016BitRegister +public interface IZ80ProgramCounter : IZ8016BitSpecialRegister { - public byte GetNextInstruction(); - public byte GetNextDataByte(); - public ushort GetNextTwoDataBytes(); + byte GetNextInstruction(); + byte GetNextDataByte(); + ushort GetNextTwoDataBytes(); /// /// Move program counter forward by the provided value /// /// Add this offset to the current PC - public void MoveProgramCounterForward(ushort offset); + void MoveProgramCounterForward(ushort offset); /// /// Move program counter backward by the provided value /// /// Subtract this offset from the current PC - public void MoveProgramCounterBackward(ushort offset); + void MoveProgramCounterBackward(ushort offset); /// /// Set the Program Counter but first push the current PC value to the stack diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs index 08450a8..520ef53 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs @@ -1,6 +1,6 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public interface IZ80StackManager : IZ8016BitRegister +public interface IZ80StackManager : IZ8016BitSpecialRegister { void IncrementStackPointer(); void DecrementStackPointer(); diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs index 44c5472..bc61f4a 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs @@ -1,8 +1,9 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public class Z80IndexRegisterX : Z8016BitRegisterBase, IZ80IndexRegisterX +public class Z80IndexRegisterX : Z8016BitSpecialRegisterBase, IZ80IndexRegisterX { - public Z80IndexRegisterX(IMasterSystemMemory memory) : base(memory) { } + public Z80IndexRegisterX(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs index 9d8535d..055fffc 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs @@ -1,8 +1,9 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public class Z80IndexRegisterY : Z8016BitRegisterBase, IZ80IndexRegisterY +public class Z80IndexRegisterY : Z8016BitSpecialRegisterBase, IZ80IndexRegisterY { - public Z80IndexRegisterY(IMasterSystemMemory memory) : base(memory) { } + public Z80IndexRegisterY(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs index adc0136..3a91e47 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs @@ -1,8 +1,9 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers.SpecialPurpose; public class Z80InterruptPageAddressRegister : Z808BitRegister, IZ80InterruptPageAddressRegister { - public Z80InterruptPageAddressRegister(IMasterSystemMemory memory) : base(memory) { } + public Z80InterruptPageAddressRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs index 9423d70..a5f98fb 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs @@ -1,9 +1,9 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers.SpecialPurpose; -// TODO: This probably should be a special base class since this the memory refresh register has alot of restrictions on it public class Z80MemoryRefreshRegister : Z808BitRegister, IZ80MemoryRefreshRegister { - public Z80MemoryRefreshRegister(IMasterSystemMemory memory) : base(memory) { } + public Z80MemoryRefreshRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index dd9ca02..a77f3cf 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -5,21 +5,17 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public class Z80ProgramCounter : Z8016BitRegisterBase, IZ80ProgramCounter +public class Z80ProgramCounter : Z8016BitSpecialRegisterBase, IZ80ProgramCounter { - private readonly IZ80FlagsManager _flags; - private readonly IMasterSystemMemory _memory; private readonly IZ80StackManager _stack; private readonly IZ80InstructionLogger _z80InstructionLogger; private Z80Register _pc; public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger, IZ80FlagsManager flags, IZ80StackManager stack) - : base(memory) + : base(memory, flags) { - _memory = memory; _z80InstructionLogger = z80InstructionLogger; - _flags = flags; _stack = stack; } @@ -87,7 +83,7 @@ public void SetFromStack() public bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) { - if (_flags.IsFlagSet(flag)) + if (Flags.IsFlagSet(flag)) { Set(address); return true; @@ -98,7 +94,7 @@ public bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) public bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) { - if (!_flags.IsFlagSet(flag)) + if (!Flags.IsFlagSet(flag)) { Set(address); return true; @@ -130,7 +126,7 @@ public void JumpByOffset(byte offset) public bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status) { - if (_flags.IsFlagSet(flag) == status) + if (Flags.IsFlagSet(flag) == status) { JumpByOffset(offset); return true; @@ -153,7 +149,7 @@ public bool JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) public bool CallIfFlagCondition(Z80StatusFlags flag, ushort address) { - if (_flags.IsFlagSet(flag)) + if (Flags.IsFlagSet(flag)) { SetAndSaveExisting(address); return true; @@ -164,7 +160,7 @@ public bool CallIfFlagCondition(Z80StatusFlags flag, ushort address) public bool CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) { - if (!_flags.IsFlagSet(flag)) + if (!Flags.IsFlagSet(flag)) { SetAndSaveExisting(address); return true; @@ -175,7 +171,7 @@ public bool CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) public bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) { - if (_flags.IsFlagSet(flag) == status) + if (Flags.IsFlagSet(flag) == status) { SetFromStack(); return true; @@ -199,7 +195,7 @@ public bool ReturnIfNotFlag(Z80StatusFlags flag) private byte GetNextByteByProgramCounter() { // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction - var data = _memory[_pc.Word]; + var data = Memory[_pc.Word]; _pc.Word++; return data; } diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs index 42032a0..86f4683 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs @@ -1,17 +1,18 @@ using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers.SpecialPurpose; -public class Z80StackManager : Z8016BitRegisterBase, IZ80StackManager +public class Z80StackManager : Z8016BitSpecialRegisterBase, IZ80StackManager { private const ushort DefaultStackAddress = 0xDFF0; private readonly ICpuLogger _cpuLogger; private readonly IMasterSystemMemory _memory; private int _maximumMemorySize; - public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger) - : base(memory) + public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger, IZ80FlagsManager flags) + : base(memory, flags) { _memory = memory; _cpuLogger = cpuLogger; diff --git a/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs similarity index 75% rename from Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs rename to Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs index 209a736..76bd445 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitCombinedRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs @@ -1,5 +1,6 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; @@ -7,21 +8,17 @@ namespace Kmse.Core.Z80.Registers; /// /// Base class for a 16 bit register which is composed of two 8 bit registers /// -public abstract class Z8016BitCombinedRegisterBase : IZ8016BitCombinedRegister +public abstract class Z8016BitGeneralPurposeRegisterBase : Z8016BitRegisterBase, IZ8016BitGeneralPurposeRegister { - private readonly IMasterSystemMemory _memory; - - protected Z8016BitCombinedRegisterBase(IMasterSystemMemory memory) - { - _memory = memory; - } + protected Z8016BitGeneralPurposeRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(memory, flags) { } protected abstract IZ808BitRegister HighRegister { get; } protected abstract IZ808BitRegister LowRegister { get; } - public ushort Value => (ushort)(LowRegister.Value + (HighRegister.Value << 8)); - public byte High => HighRegister.Value; - public byte Low => LowRegister.Value; + public override ushort Value => (ushort)(LowRegister.Value + (HighRegister.Value << 8)); + public override byte High => HighRegister.Value; + public override byte Low => LowRegister.Value; public ushort ShadowValue => (ushort)(LowRegister.ShadowValue + (HighRegister.ShadowValue << 8)); public void Reset() @@ -30,7 +27,7 @@ public void Reset() HighRegister.Reset(); } - public void Set(ushort value) + public override void Set(ushort value) { var (high, low) = Bitwise.ToBytes(value); LowRegister.Set(low); @@ -55,9 +52,9 @@ public void SetLow(byte value) public void SetFromDataInMemory(ushort address, byte offset = 0) { var location = (ushort)(address + offset); - var low = _memory[location]; + var low = Memory[location]; location++; - var high = _memory[location]; + var high = Memory[location]; LowRegister.Set(low); HighRegister.Set(high); @@ -71,8 +68,8 @@ public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) public void SaveToMemory(ushort address, byte offset = 0) { var location = (ushort)(address + offset); - _memory[location] = HighRegister.Value; - _memory[(ushort)(location + 1)] = LowRegister.Value; + Memory[location] = HighRegister.Value; + Memory[(ushort)(location + 1)] = LowRegister.Value; } public void SwapWithShadow() @@ -90,6 +87,15 @@ public Z80Register AsRegister() }; } + public Z80Register ShadowAsRegister() + { + return new Z80Register + { + Low = LowRegister.ShadowValue, + High = HighRegister.ShadowValue + }; + } + public void Increment() { var currentValue = Value; @@ -103,13 +109,4 @@ public void Decrement() currentValue--; Set(currentValue); } - - public Z80Register ShadowAsRegister() - { - return new Z80Register - { - Low = LowRegister.ShadowValue, - High = HighRegister.ShadowValue - }; - } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index 59761a8..94c7e56 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -1,87 +1,172 @@ using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; -/// -/// Base class for a true 16 bit register -/// -public abstract class Z8016BitRegisterBase : IZ8016BitRegister +public abstract class Z8016BitRegisterBase : Z80RegisterBase { - private readonly IMasterSystemMemory _memory; - protected Z80Register Register; + protected readonly IMasterSystemMemory Memory; - protected Z8016BitRegisterBase(IMasterSystemMemory memory) + protected Z8016BitRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(flags) { - _memory = memory; - Register = new Z80Register(); + Memory = memory; } - public ushort Value => Register.Word; - public byte High => Register.High; - public byte Low => Register.Low; + public abstract ushort Value { get; } + public abstract byte High { get; } + public abstract byte Low { get; } + public abstract void Set(ushort value); - public virtual void Reset() + public void ResetBitByRegisterLocation(int bit, int offset) { - Register.Word = 0x00; + // Clear bit of value in memory pointed to by HL register + var value = Memory[(ushort)(Value + offset)]; + Bitwise.Clear(ref value, bit); + Memory[(ushort)(Value + offset)] = value; } - /// - /// Set program counter to new value, but don't save the old value to the stack - /// - /// New address to set PC to - public void Set(ushort value) + public void SetBitByRegisterLocation(int bit, int offset) { - Register.Word = value; + // Clear bit of value in memory pointed to by HL register + var value = Memory[(ushort)(Value + offset)]; + Bitwise.Set(ref value, bit); + Memory[(ushort)(Value + offset)] = value; } - public void Set(IZ8016BitRegister register) + public void TestBitByRegisterLocation(int bit, int offset) { - Register.Word = register.Value; + var value = Memory[(ushort)(Value + offset)]; + var bitSet = Bitwise.IsSet(value, bit); + + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); + Flags.SetFlag(Z80StatusFlags.HalfCarryH); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + // This behaviour is not documented + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, bit == 7 && bitSet); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); + } + + public void Add(ushort source, bool withCarry = false) + { + int valueWithCarry = source; + if (withCarry && Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + valueWithCarry += 0x01; + } + + var newValue = Value + valueWithCarry; + + // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, + (Value & 0x0FFF) + (valueWithCarry & 0x0FFF) > 0x0FFF); + + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, + (Value & 0xFFFF) + (valueWithCarry & 0xFFFF) > 0xFFFF); + + if (withCarry) + { + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((Value ^ valueWithCarry) & 0x8000) == 0 && + ((Value ^ (newValue & 0xFFFF)) & 0x8000) != 0); + } + + Set((ushort)newValue); + } + + public void Subtract(ushort source, bool withCarry = false) + { + int valueWithCarry = source; + if (withCarry && Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + valueWithCarry += 0x01; + } + + var newValue = Value - valueWithCarry; + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); + + // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (((Value ^ newValue ^ source) >> 8) & 0x10) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((source ^ Value) & (Value ^ newValue) & 0x8000) != 0); + + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, (newValue & 0x10000) != 0); + + Set((ushort)newValue); } - public void SetHigh(byte value) + public void RotateLeftCircular(int offset) { - Register.High = value; + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateLeftCircular(ref value); + Memory[location] = value; } - public void SetLow(byte value) + public void RotateLeft(int offset) { - Register.Low = value; + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateLeft(ref value); + Memory[location] = value; } - public void SetFromDataInMemory(ushort address, byte offset = 0) + public void RotateRightCircular(int offset) { - var location = (ushort)(address + offset); - SetLow(_memory[location]); - location++; - SetHigh(_memory[location]); + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateRightCircular(ref value); + Memory[location] = value; } - public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + public void RotateRight(int offset) { - SetFromDataInMemory(register.Value, offset); + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateRight(ref value); + Memory[location] = value; } - public void SaveToMemory(ushort address, byte offset = 0) + public void ShiftLeftArithmetic(int offset) { - var location = (ushort)(address + offset); - _memory[location] = Register.Low; - _memory[(ushort)(location + 1)] = Register.High; + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftLeftArithmetic(ref value); + Memory[location] = value; } - public Z80Register AsRegister() + public void ShiftRightArithmetic(int offset) { - return Register; + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftRightArithmetic(ref value); + Memory[location] = value; } - public void Increment() + public void ShiftLeftLogical(int offset) { - Register.Word++; + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftLeftLogical(ref value); + Memory[location] = value; } - public void Decrement() + public void ShiftRightLogical(int offset) { - Register.Word--; + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftRightLogical(ref value); + Memory[location] = value; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs new file mode 100644 index 0000000..74dbe5d --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs @@ -0,0 +1,87 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +/// +/// Base class for a true 16 bit register +/// +public abstract class Z8016BitSpecialRegisterBase : Z8016BitRegisterBase, IZ8016BitRegister +{ + protected Z80Register Register; + + protected Z8016BitSpecialRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(memory, flags) + { + Register = new Z80Register(); + } + + public override ushort Value => Register.Word; + public override byte High => Register.High; + public override byte Low => Register.Low; + + public virtual void Reset() + { + Register.Word = 0x00; + } + + /// + /// Set register to new value + /// + /// New value to this register to + public override void Set(ushort value) + { + Register.Word = value; + } + + public void Set(IZ8016BitRegister register) + { + Register.Word = register.Value; + } + + public void SetHigh(byte value) + { + Register.High = value; + } + + public void SetLow(byte value) + { + Register.Low = value; + } + + public void SetFromDataInMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + SetLow(Memory[location]); + location++; + SetHigh(Memory[location]); + } + + public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) + { + SetFromDataInMemory(register.Value, offset); + } + + public void SaveToMemory(ushort address, byte offset = 0) + { + var location = (ushort)(address + offset); + Memory[location] = Register.Low; + Memory[(ushort)(location + 1)] = Register.High; + } + + public Z80Register AsRegister() + { + return Register; + } + + public void Increment() + { + Register.Word++; + } + + public void Decrement() + { + Register.Word--; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs index 1e3c263..28d55a6 100644 --- a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs @@ -7,12 +7,7 @@ namespace Kmse.Core.Z80.Registers; public class Z808BitGeneralPurposeRegister : Z808BitRegister, IZ808BitGeneralPurposeRegister { - protected readonly IZ80FlagsManager Flags; - - public Z808BitGeneralPurposeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory) - { - Flags = flags; - } + public Z808BitGeneralPurposeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } public void Increment() { @@ -22,6 +17,74 @@ public void Increment() CheckIncrementFlags(newValue, oldValue); } + public void Decrement() + { + var oldValue = Value; + var newValue = (byte)(Value - 1); + Set(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + public void ClearBit(int bit) + { + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); + } + + Bitwise.Clear(ref InternalValue, bit); + } + + public void SetBit(int bit) + { + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); + } + + Bitwise.Set(ref InternalValue, bit); + } + + public void RotateLeftCircular() + { + RotateLeftCircular(ref InternalValue); + } + + public void RotateLeft() + { + RotateLeft(ref InternalValue); + } + + public void RotateRightCircular() + { + RotateRightCircular(ref InternalValue); + } + + public void RotateRight() + { + RotateRight(ref InternalValue); + } + + public void ShiftLeftArithmetic() + { + ShiftLeftArithmetic(ref InternalValue); + } + + public void ShiftRightArithmetic() + { + ShiftRightArithmetic(ref InternalValue); + } + + public void ShiftLeftLogical() + { + ShiftLeftLogical(ref InternalValue); + } + + public void ShiftRightLogical() + { + ShiftRightLogical(ref InternalValue); + } + private void CheckIncrementFlags(byte newValue, byte oldValue) { Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); @@ -34,14 +97,6 @@ private void CheckIncrementFlags(byte newValue, byte oldValue) Flags.ClearFlag(Z80StatusFlags.AddSubtractN); } - public void Decrement() - { - var oldValue = Value; - var newValue = (byte)(Value - 1); - Set(newValue); - CheckDecrementFlags(newValue, oldValue); - } - private void CheckDecrementFlags(byte newValue, byte oldValue) { Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); diff --git a/Kmse.Core/Z80/Registers/Z808BitRegister.cs b/Kmse.Core/Z80/Registers/Z808BitRegister.cs index 378eed6..cfff472 100644 --- a/Kmse.Core/Z80/Registers/Z808BitRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitRegister.cs @@ -1,18 +1,32 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; namespace Kmse.Core.Z80.Registers; -public abstract class Z808BitRegister : IZ808BitRegister +public abstract class Z808BitRegister : Z80RegisterBase, IZ808BitRegister { protected readonly IMasterSystemMemory Memory; - public byte Value { get; protected set; } - public byte ShadowValue { get; protected set; } + protected byte InternalShadowValue; + protected byte InternalValue; - protected Z808BitRegister(IMasterSystemMemory memory) + protected Z808BitRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(flags) { Memory = memory; } + public byte Value + { + get => InternalValue; + protected set => InternalValue = value; + } + + public byte ShadowValue + { + get => InternalShadowValue; + protected set => InternalShadowValue = value; + } + public void Reset() { Value = 0; diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs new file mode 100644 index 0000000..a7ec97f --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -0,0 +1,175 @@ +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers; + +public abstract class Z80RegisterBase +{ + protected readonly IZ80FlagsManager Flags; + + protected Z80RegisterBase(IZ80FlagsManager flags) + { + Flags = flags; + } + + protected void RotateLeftCircular(ref byte register) + { + // Rotate register left by 1 bit, bit 7 is copied to carry flag and bit 0 + var newValue = (byte)(register << 1); + var bit7Set = Bitwise.IsSet(register, 7); + if (bit7Set) + { + // Copy bit 7 to bit 0 + Bitwise.Set(ref newValue, 0); + } + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + + register = newValue; + } + + protected void RotateLeft(ref byte register) + { + // Rotate register left by 1 bit, bit 7 is copied to carry flag and carry flag copied to bit 0 + var newValue = (byte)(register << 1); + var bit7Set = Bitwise.IsSet(register, 7); + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + // Copy carry flag to bit 0 + Bitwise.Set(ref newValue, 0); + } + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); + + register = newValue; + } + + protected void RotateRightCircular(ref byte register) + { + // Rotate register right by 1 bit, bit 0 is copied to carry flag and bit 7 + var newValue = (byte)(register >> 1); + var bit0Set = Bitwise.IsSet(register, 0); + if (bit0Set) + { + // Copy bit 0 to bit 7 + Bitwise.Set(ref newValue, 7); + } + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + + register = newValue; + } + + protected void RotateRight(ref byte register) + { + // Rotate register right by 1 bit, bit 0 is copied to carry flag and carry flag copied to bit 7 + // This is special method since RRA flags are set differently to RR r instruction + var newValue = (byte)(register >> 1); + var bit0Set = Bitwise.IsSet(register, 0); + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + // Copy carry flag to bit 7 + Bitwise.Set(ref newValue, 7); + } + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); + + register = newValue; + } + + protected void ShiftLeftArithmetic(ref byte register) + { + // Shift register left by 1 bit, bit 7 is copied to carry flag + var newValue = (byte)(register << 1); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); + + register = newValue; + } + + protected void ShiftRightArithmetic(ref byte register) + { + // Rotate register right by 1 bit, bit 0 is copied to carry flag + // This is special method since RRA flags are set differently to RR r instruction + var newValue = (byte)(register >> 1); + var bit7Set = Bitwise.IsSet(register, 7); + + if (bit7Set) + { + // If bit 7 is set in original then leave as set even as we shift right + Bitwise.Set(ref newValue, 7); + } + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); + + register = newValue; + } + + protected void ShiftLeftLogical(ref byte register) + { + // Shift register left by 1 bit, bit 7 is copied to carry flag + var newValue = (byte)(register << 1); + + // The difference between shift left logical and shift left arithmetic is this sets bit 0 + Bitwise.Set(ref newValue, 0); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); + + register = newValue; + } + + protected void ShiftRightLogical(ref byte register) + { + // Rotate register right by 1 bit, bit 0 is copied to carry flag + // This is special method since RRA flags are set differently to RR r instruction + var newValue = (byte)(register >> 1); + + // The difference between shift right logical and shift right arithmetic is this does maintain bit 7 when shifting and just clears it + Bitwise.Clear(ref newValue, 7); + + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); + + register = newValue; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs b/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs deleted file mode 100644 index d42357b..0000000 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Kmse.Core.Z80.Registers; - -namespace Kmse.Core.Z80 -{ - /// - /// Core operations, memory operations, reset, flags, stack operations - /// - public partial class Z80Cpu - { - private void LoadRR(byte opCode) - { - // LD r,r' is 0 1 r r r r' r' r' - var sourceRegisterId = (byte)(opCode & 0x07); - var destinationRegisterId = (byte)((opCode & 0x38) >> 3); - - if (sourceRegisterId == 0x06 && destinationRegisterId == 0x06) - { - // 16 bit register load not supported in this method - throw new InvalidOperationException($"Invalid op code, 16-bit load to same register LoadRR - OP code {opCode:X2}"); - } - - // Special cases where we are loading from or into memory location referenced by HL register rather than actual register - if (sourceRegisterId == 0x06) - { - Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); - _currentCycleCount += 3; - return; - } - - if (destinationRegisterId == 0x06) - { - var register = Get8BitRegisterByRIdentifier(destinationRegisterId); - _memoryManagement.WriteToMemory(_hl, register.Value); - _currentCycleCount += 3; - return; - } - - var sourceRegister = Get8BitRegisterByRIdentifier(sourceRegisterId); - var destinationRegister = Get8BitRegisterByRIdentifier(destinationRegisterId); - - destinationRegister.Set(sourceRegister); - } - - private IZ808BitRegister Get8BitRegisterByRIdentifier(byte identifier) - { - return identifier switch - { - 0 => _b, - 1 => _c, - 2 => _d, - 3 => _e, - 4 => _h, - 5 => _l, - // 6 is HL so cannot return here since 16 bit register - 7 => _accumulator, - _ => throw new ArgumentOutOfRangeException() - }; - } - } -} diff --git a/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs b/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs deleted file mode 100644 index c82f5d0..0000000 --- a/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs +++ /dev/null @@ -1,802 +0,0 @@ -using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80; - -/// -/// Math and logic operations -/// -public partial class Z80Cpu -{ - private void DecimalAdjustAccumulator() - { - /* - Credit to https://github.com/xdanieldzd/MasterFudge for referencing this document - https://worldofspectrum.org/faq/reference/z80reference.htm which explained how to do this in a clear and simple way - - The purpose of the DAA (Decimal Adjust Accumulator) instruction is to make an adjustment to the value in the A register, after performing a binary mathematical operation, such that the result is as if the operation were performed with BCD (Binary Coded Decimal) maths. The Z80 achieves this by adjusting the A register by a value which is dependent upon the value of the A register, the Carry flag, Half-Carry flag (carry from bit 3 to 4), and the N-flag (which defines whether the last operation was an add or subtract). - - The algorithm used is as follows: - - - If the A register is greater than 0x99, OR the Carry flag is SET, then - The upper four bits of the Correction Factor are set to 6, - and the Carry flag will be SET. - Else - The upper four bits of the Correction Factor are set to 0, - and the Carry flag will be CLEARED. - - - If the lower four bits of the A register (A AND 0x0F) is greater than 9, - OR the Half-Carry (H) flag is SET, then - The lower four bits of the Correction Factor are set to 6. - Else - The lower four bits of the Correction Factor are set to 0. - - - This results in a Correction Factor of 0x00, 0x06, 0x60 or 0x66. - - If the N flag is CLEAR, then - ADD the Correction Factor to the A register. - Else - SUBTRACT the Correction Factor from the A register. - - - The Flags are set as follows: - - Carry: Set/clear as in the first step above. - Half-Carry: Set if the correction operation caused a binary carry/borrow - from bit 3 to bit 4. - For this purpose, may be calculated as: - Bit 4 of: A(before) XOR A(after). - S,Z,P,5,3: Set as for simple logic operations on the resultant A value. - N: Leave. - - */ - - byte factor = 0; - var currentValue = _af.High; - if (_af.High > 0x99 || _flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - factor |= (0x06 << 4); - _flags.SetFlag(Z80StatusFlags.CarryC); - } - else - { - factor &= 0x0F; - _flags.ClearFlag(Z80StatusFlags.CarryC); - } - - if ((_af.High & 0x0F) > 9 || _flags.IsFlagSet(Z80StatusFlags.HalfCarryH)) - { - factor |= 0x06; - } - else - { - factor &= 0xF0; - } - - if (!_flags.IsFlagSet(Z80StatusFlags.AddSubtractN)) - { - _af.High += factor; - } - else - { - _af.High -= factor; - } - - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, Bitwise.IsSet(currentValue ^ _af.High, 4)); - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(_af.High, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == 0); - SetParityFromValue(_af.High); - } - - private void InvertAccumulatorRegister() - { - var onesComplementValue = ~_af.High & 0xFF; - - _flags.SetFlag(Z80StatusFlags.HalfCarryH); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - - _af.High = (byte)onesComplementValue; - } - - private void NegateAccumulatorRegister() - { - var twosComplementValue = (0 - _af.High) & 0xFF; - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, twosComplementValue == 0); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (_af.High & 0x0F) > 0); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _af.High == 0x80); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, _af.High != 0x00); - - _af.High = (byte)twosComplementValue; - } - - private void ResetBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); - } - var register = opCode & 0x07; - - if (register == 0x06) - { - ResetBitByRegisterLocation(_hl, bit, 0); - // Accessing (HL) increases cycle count - _currentCycleCount += 7; - return; - } - - switch (register) - { - case 0: - Bitwise.Clear(ref _bc.High, bit); - break; - case 1: - Bitwise.Clear(ref _bc.Low, bit); - break; - case 2: - Bitwise.Clear(ref _de.High, bit); - break; - case 3: - Bitwise.Clear(ref _de.Low, bit); - break; - case 4: - Bitwise.Clear(ref _hl.High, bit); - break; - case 5: - Bitwise.Clear(ref _hl.Low, bit); - break; - case 7: - Bitwise.Clear(ref _af.High, bit); - break; - default: - throw new ArgumentOutOfRangeException($"Register id {register} is not a valid register to reset"); - } - } - - private void ResetBitByRegisterLocation(Z80Register register, int bit, int offset) - { - // Clear bit of value in memory pointed to by HL register - var value = _memory[(ushort)(register.Word + offset)]; - Bitwise.Clear(ref value, bit); - _memory[(ushort)(register.Word + offset)] = value; - } - - private void SetBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to set"); - } - var register = opCode & 0x07; - - if (register == 0x06) - { - SetBitByRegisterLocation(_hl, bit, 0); - // Accessing (HL) increases cycle count - _currentCycleCount += 7; - return; - } - - switch (register) - { - case 0: - Bitwise.Set(ref _bc.High, bit); - break; - case 1: - Bitwise.Set(ref _bc.Low, bit); - break; - case 2: - Bitwise.Set(ref _de.High, bit); - break; - case 3: - Bitwise.Set(ref _de.Low, bit); - break; - case 4: - Bitwise.Set(ref _hl.High, bit); - break; - case 5: - Bitwise.Set(ref _hl.Low, bit); - break; - case 7: - Bitwise.Set(ref _af.High, bit); - break; - default: - throw new ArgumentOutOfRangeException($"Register id {register} is not a valid register to set"); - } - } - - private void SetBitByRegisterLocation(Z80Register register, int bit, int offset) - { - // Clear bit of value in memory pointed to by HL register - var value = _memory[(ushort)(register.Word + offset)]; - Bitwise.Set(ref value, bit); - _memory[(ushort)(register.Word + offset)] = value; - } - - private void TestBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to test"); - } - var register = opCode & 0x07; - - if (register == 0x06) - { - TestBitByRegisterLocation(_hl, bit, 0); - // Testing bit via (HL) memory location increases cycle count - _currentCycleCount += 4; - return; - } - - var valueToCheck = register switch - { - 0 => _bc.High, - 1 => _bc.Low, - 2 => _de.High, - 3 => _de.Low, - 4 => _hl.High, - 5 => _hl.Low, - 7 => _af.High, - _ => throw new ArgumentOutOfRangeException($"Register id {register} is not a valid register to test bit on") - }; - var bitSet = Bitwise.IsSet(valueToCheck, bit); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - _flags.SetFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - - // This behaviour is not documented - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); - } - - private void TestBitByRegisterLocation(Z80Register register, int bit, int offset) - { - var value = _memory[(ushort)(register.Word + offset)]; - var bitSet = Bitwise.IsSet(value, bit); - - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - _flags.SetFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - - // This behaviour is not documented - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, bit == 7 && bitSet); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); - } - - private void AddValueAtRegisterMemoryLocationTo8BitRegister(Z80Register register, int offset, ref byte destination, bool withCarry = false) - { - var value = _memory[(ushort)(register.Word + offset)]; - AddValueTo8BitRegister(value, ref destination, withCarry); - } - - private void AddValueTo8BitRegister(byte value, ref byte destination, bool withCarry = false) - { - int valueWithCarry = value; - if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry = value + 0x01; - } - int newValue = destination + valueWithCarry; - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - // Half carry occurs if the result of adding the lower nibbles means it will set the next higher bit (basically overflows) - // This is since the adding is done in two 4 bit operations not 1 8 bit operation internally - // This is then combined with the DAA instruction which adjusts the result to get the valid value - //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (((destination ^ newValue ^ value) & 0x10) != 0)); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination ^ 0x80) & (destination ^ newValue) & 0x80) != 0); - - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - // A carry is same as half carry just on the overall value - // Since we stored the sum as a 32 bit integer, we can see if went past the max value and bit 7 must have carried over into bit 8 - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, newValue > 0xFF); - - destination = (byte)newValue; - } - - private void Add16BitRegisterTo16BitRegister(Z80Register source, ref Z80Register destination, bool withCarry = false) - { - int valueWithCarry = source.Word; - if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry += 0x01; - } - int newValue = destination.Word + valueWithCarry; - - // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, - (destination.Word & 0x0FFF) + (valueWithCarry & 0x0FFF) > 0x0FFF); - - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, - (destination.Word & 0xFFFF) + (valueWithCarry & 0xFFFF) > 0xFFFF); - - if (withCarry) - { - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((destination.Word ^ valueWithCarry) & 0x8000) == 0 && - ((destination.Word ^ (newValue & 0xFFFF)) & 0x8000) != 0); - } - - destination.Word = (ushort)newValue; - } - - private void SubtractValueAtRegisterMemoryLocationFrom8BitRegister(Z80Register register, int offset, - ref byte destination, bool withCarry = false) - { - var value = _memory[(ushort)(register.Word + offset)]; - SubtractValueFrom8BitRegister(value, ref destination, withCarry); - } - - private void SubtractValueFrom8BitRegister(byte value, ref byte destination, bool withCarry = false) - { - int valueWithCarry = value; - if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry += 0x01; - } - int newValue = destination - valueWithCarry; - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - // Half carry occurs if the result of subtracting the higher nibbles means it will set the next lower bit (basically underflows) - // We check if the subtraction means that adding higher nibbles sets bit 3 - // This is then combined with the DAA instruction which adjusts the result to get the valid value - //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((destination ^ newValue ^ value) & 0x10) != 0); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ destination) & (destination ^ newValue) & 0x80) != 0); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - - // Subtraction went negative, so carried over into next bit - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, destination < valueWithCarry); - - destination = (byte)newValue; - } - - private void Sub16BitRegisterFrom16BitRegister(Z80Register source, ref Z80Register destination, bool withCarry = false) - { - int valueWithCarry = source.Word; - if (withCarry && _flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry += 0x01; - } - int newValue = destination.Word - valueWithCarry; - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); - - // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((((destination.Word ^ newValue ^ source.Word) >> 8) & 0x10) != 0)); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((source.Word ^ destination.Word) & (destination.Word ^ newValue) & 0x8000) != 0); - - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, ((newValue& 0x10000) != 0)); - - destination.Word = (ushort)newValue; - } - - private void Compare8Bit(byte value, byte valueToCompareTo) - { - // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = valueToCompareTo - value; - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((valueToCompareTo ^ difference ^ value) & 0x10) != 0); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ valueToCompareTo) & (valueToCompareTo ^ difference) & 0x80) != 0); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - // Subtraction went negative, so carried over into next bit - //_flags.SetClearFlagConditional(Z80StatusFlags.CarryC, difference < 0); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, valueToCompareTo < value); - } - - private void CompareIncrement() - { - var value = _memory[_hl.Word]; - // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _af.High - (sbyte)value; - - Increment16Bit(ref _hl); - Decrement16Bit(ref _bc); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - } - - private void CompareDecrement() - { - var value = _memory[_hl.Word]; - // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _af.High - (sbyte)value; - - Decrement16Bit(ref _hl); - Decrement16Bit(ref _bc); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - } - - private void Compare8BitToMemoryLocationFrom16BitRegister(Z80Register register, int offset, byte valueToCompareTo) - { - var value = _memory[(ushort)(register.Word + offset)]; - Compare8Bit(value, valueToCompareTo); - } - - private void And8Bit(byte value, byte valueToAndAgainst, ref byte register) - { - var newValue = (byte)(value & valueToAndAgainst); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.SetFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.ClearFlag(Z80StatusFlags.CarryC); - - register = newValue; - } - - private void And8BitToMemoryLocationFrom16BitRegister(Z80Register register, int offset, byte valueToAndAgainst, - ref byte registerToStoreValueIn) - { - var value = _memory[(ushort)(register.Word + offset)]; - And8Bit(value, valueToAndAgainst, ref registerToStoreValueIn); - } - - private void Or8Bit(byte value, byte valueToAndAgainst, ref byte register) - { - var newValue = (byte)(value | valueToAndAgainst); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.ClearFlag(Z80StatusFlags.CarryC); - - register = newValue; - } - - private void Or8BitToMemoryLocationFrom16BitRegister(Z80Register register, int offset, byte valueToOrAgainst, - ref byte registerToStoreValueIn) - { - var value = _memory[(ushort)(register.Word + offset)]; - Or8Bit(value, valueToOrAgainst, ref registerToStoreValueIn); - } - - private void Xor8Bit(byte value, byte valueToXorAgainst, ref byte register) - { - var newValue = (byte)(value ^ valueToXorAgainst); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.ClearFlag(Z80StatusFlags.CarryC); - - register = newValue; - } - - private void Xor8BitToMemoryLocationFrom16BitRegister(Z80Register register, int offset, byte valueToXorAgainst, - ref byte registerToStoreValueIn) - { - var value = _memory[(ushort)(register.Word + offset)]; - Xor8Bit(value, valueToXorAgainst, ref registerToStoreValueIn); - } - - private void RotateLeftCircularAccumulator() - { - // Rotate A left by 1 bit, bit 7 is copied to carry flag and bit 0 - // This is special method since RLCA flags are set differently to RLC r instruction - var newValue = (byte)(_af.High << 1); - var bit7Set = Bitwise.IsSet(_af.High, 7); - if (bit7Set) - { - // Copy bit 7 to bit 0 - Bitwise.Set(ref newValue, 0); - } - - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); - - _af.High = newValue; - } - - private void RotateLeftAccumulator() - { - // Rotate A left by 1 bit, bit 7 is copied to carry flag and carry flag copied to bit 0 - // This is special method since RLA flags are set differently to RL r instruction - var newValue = (byte)(_af.High << 1); - var bit7Set = Bitwise.IsSet(_af.High, 7); - if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 0 - Bitwise.Set(ref newValue, 0); - } - - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); - - _af.High = newValue; - } - - private void RotateRightCircularAccumulator() - { - // Rotate A right by 1 bit, bit 0 is copied to carry flag and bit 7 - // This is special method since RRCA flags are set differently to RRC r instruction - var newValue = (byte)(_af.High >> 1); - var bit0Set = Bitwise.IsSet(_af.High, 0); - if (bit0Set) - { - // Copy bit 0 to bit 7 - Bitwise.Set(ref newValue, 7); - } - - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); - - _af.High = newValue; - } - - private void RotateRightAccumulator() - { - // Rotate A right by 1 bit, bit 0 is copied to carry flag and carry flag copied to bit 7 - // This is special method since RRA flags are set differently to RR r instruction - var newValue = (byte)(_af.High >> 1); - var bit0Set = Bitwise.IsSet(_af.High, 0); - if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 7 - Bitwise.Set(ref newValue, 7); - } - - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); - - _af.High = newValue; - } - - private void RotateLeftCircular(ref byte register) - { - // Rotate register left by 1 bit, bit 7 is copied to carry flag and bit 0 - var newValue = (byte)(register << 1); - var bit7Set = Bitwise.IsSet(register, 7); - if (bit7Set) - { - // Copy bit 7 to bit 0 - Bitwise.Set(ref newValue, 0); - } - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); - - register = newValue; - } - - private void RotateLeft(ref byte register) - { - // Rotate register left by 1 bit, bit 7 is copied to carry flag and carry flag copied to bit 0 - var newValue = (byte)(register << 1); - var bit7Set = Bitwise.IsSet(register, 7); - if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 0 - Bitwise.Set(ref newValue, 0); - } - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit7Set); - - register = newValue; - } - - private void RotateRightCircular(ref byte register) - { - // Rotate register right by 1 bit, bit 0 is copied to carry flag and bit 7 - var newValue = (byte)(register >> 1); - var bit0Set = Bitwise.IsSet(register, 0); - if (bit0Set) - { - // Copy bit 0 to bit 7 - Bitwise.Set(ref newValue, 7); - } - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); - - register = newValue; - } - - private void RotateRight(ref byte register) - { - // Rotate register right by 1 bit, bit 0 is copied to carry flag and carry flag copied to bit 7 - // This is special method since RRA flags are set differently to RR r instruction - var newValue = (byte)(register >> 1); - var bit0Set = Bitwise.IsSet(register, 0); - if (_flags.IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 7 - Bitwise.Set(ref newValue, 7); - } - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, bit0Set); - - register = newValue; - } - - private void RotateLeftCircular16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - RotateLeftCircular(ref value); - _memory[location] = value; - } - - private void RotateLeft16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - RotateLeft(ref value); - _memory[location] = value; - } - - private void RotateRightCircular16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - RotateRightCircular(ref value); - _memory[location] = value; - } - - private void RotateRight16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - RotateRight(ref value); - _memory[location] = value; - } - - private void ShiftLeftArithmetic(ref byte register) - { - // Shift register left by 1 bit, bit 7 is copied to carry flag - var newValue = (byte)(register << 1); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); - - register = newValue; - } - - private void ShiftRightArithmetic(ref byte register) - { - // Rotate register right by 1 bit, bit 0 is copied to carry flag - // This is special method since RRA flags are set differently to RR r instruction - var newValue = (byte)(register >> 1); - var bit7Set = Bitwise.IsSet(register, 7); - - if (bit7Set) - { - // If bit 7 is set in original then leave as set even as we shift right - Bitwise.Set(ref newValue, 7); - } - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); - - register = newValue; - } - - private void ShiftLeftArithmetic16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - ShiftLeftArithmetic(ref value); - _memory[location] = value; - } - - private void ShiftRightArithmetic16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - ShiftRightArithmetic(ref value); - _memory[location] = value; - } - - private void ShiftLeftLogical(ref byte register) - { - // Shift register left by 1 bit, bit 7 is copied to carry flag - var newValue = (byte)(register << 1); - - // The difference between shift left logical and shift left arithmetic is this sets bit 0 - Bitwise.Set(ref newValue, 0); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 7)); - - register = newValue; - } - - private void ShiftRightLogical(ref byte register) - { - // Rotate register right by 1 bit, bit 0 is copied to carry flag - // This is special method since RRA flags are set differently to RR r instruction - var newValue = (byte)(register >> 1); - - // The difference between shift right logical and shift right arithmetic is this does maintain bit 7 when shifting and just clears it - Bitwise.Clear(ref newValue, 7); - - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.SetParityFromValue(newValue); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Bitwise.IsSet(register, 0)); - - register = newValue; - } - - private void ShiftLeftLogical16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - ShiftLeftLogical(ref value); - _memory[location] = value; - } - - private void ShiftRightLogical16BitRegisterMemoryLocation(Z80Register register, int offset) - { - var location = (ushort)(register.Word + offset); - var value = _memory[location]; - ShiftRightLogical(ref value); - _memory[location] = value; - } -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs new file mode 100644 index 0000000..dc41529 --- /dev/null +++ b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs @@ -0,0 +1,165 @@ +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80 +{ + /// + /// Core operations, memory operations, reset, flags, stack operations + /// + public partial class Z80Cpu + { + private void LoadRR(byte opCode) + { + // LD r,r' is 0 1 r r r r' r' r' + var sourceRegisterId = (byte)(opCode & 0x07); + var destinationRegisterId = (byte)((opCode & 0x38) >> 3); + + if (sourceRegisterId == 0x06 && destinationRegisterId == 0x06) + { + // 16 bit register load not supported in this method + throw new InvalidOperationException($"Invalid op code, 16-bit load to same register LoadRR - OP code {opCode:X2}"); + } + + // Special cases where we are loading from or into memory location referenced by HL register rather than actual register + if (sourceRegisterId == 0x06) + { + Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); + _currentCycleCount += 3; + return; + } + + if (destinationRegisterId == 0x06) + { + var register = Get8BitRegisterByRIdentifier(destinationRegisterId); + _memoryManagement.WriteToMemory(_hl, register.Value); + _currentCycleCount += 3; + return; + } + + var sourceRegister = Get8BitRegisterByRIdentifier(sourceRegisterId); + var destinationRegister = Get8BitRegisterByRIdentifier(destinationRegisterId); + + destinationRegister.Set(sourceRegister); + } + + private IZ808BitGeneralPurposeRegister Get8BitRegisterByRIdentifier(byte identifier) + { + return identifier switch + { + 0 => _b, + 1 => _c, + 2 => _d, + 3 => _e, + 4 => _h, + 5 => _l, + // 6 is HL so cannot return here since 16 bit register + 7 => _accumulator, + _ => throw new ArgumentOutOfRangeException() + }; + } + + private void ResetBitByOpCode(byte opCode) + { + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); + } + var registerId = (byte)(opCode & 0x07); + + if (registerId == 0x06) + { + _hl.ResetBitByRegisterLocation(bit, 0); + // Accessing (HL) increases cycle count + _currentCycleCount += 7; + return; + } + + var register = Get8BitRegisterByRIdentifier(registerId); + register.ClearBit(bit); + } + + private void SetBitByOpCode(byte opCode) + { + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to set"); + } + var registerId = (byte)(opCode & 0x07); + + if (registerId == 0x06) + { + _hl.SetBitByRegisterLocation(bit, 0); + // Accessing (HL) increases cycle count + _currentCycleCount += 7; + return; + } + + var register = Get8BitRegisterByRIdentifier(registerId); + register.SetBit(bit); + } + + private void TestBitByOpCode(byte opCode) + { + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to test"); + } + var registerId = (byte)(opCode & 0x07); + + if (registerId == 0x06) + { + _hl.TestBitByRegisterLocation(bit, 0); + // Testing bit via (HL) memory location increases cycle count + _currentCycleCount += 4; + return; + } + + var register = Get8BitRegisterByRIdentifier(registerId); + var valueToCheck = register.Value; + var bitSet = Bitwise.IsSet(valueToCheck, bit); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + // This behaviour is not documented + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); + } + + private void CompareIncrement() + { + var value = _memory[_hl.Value]; + // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not + var difference = _af.High - (sbyte)value; + + _hl.Increment(); + _bc.Decrement(); + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); + } + + private void CompareDecrement() + { + var value = _memory[_hl.Value]; + // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not + var difference = _af.High - (sbyte)value; + + _hl.Decrement(); + _bc.Decrement(); + + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + } + } +} diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index c251f24..5b0490a 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -71,10 +71,10 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _bc = new Z80BcRegister(memory, _af.Flags); _de = new Z80DeRegister(memory, _af.Flags); _hl = new Z80HlRegister(memory, _af.Flags); - _ix = new Z80IndexRegisterX(memory); - _iy = new Z80IndexRegisterY(memory); - _rRegister = new Z80MemoryRefreshRegister(memory); - _iRegister = new Z80InterruptPageAddressRegister(memory); + _ix = new Z80IndexRegisterX(memory, _af.Flags); + _iy = new Z80IndexRegisterY(memory, _af.Flags); + _rRegister = new Z80MemoryRefreshRegister(memory, _af.Flags); + _iRegister = new Z80InterruptPageAddressRegister(memory, _af.Flags); _accumulator = _af.Accumulator; _flags = _af.Flags; @@ -85,7 +85,7 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _h = _hl.H; _l = _hl.L; - _stack = new Z80StackManager(memory, _cpuLogger); + _stack = new Z80StackManager(memory, _cpuLogger, _af.Flags); _pc = new Z80ProgramCounter(memory, _instructionLogger, _flags, _stack); _ioManagement = new Z80CpuInputOutput(_io, _flags); From b612661d9c6f29a812117a31dca42e47066998d3 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:29:17 +1000 Subject: [PATCH 06/21] Combine IndexRegister X and IndexRegisterY back into a single class since share common functionality Fixed some issues with program counter class Fix issue with CPU resume if halted where logic was backwards Fix bug in loadRR where using incorrect destination register id when should be using source register id --- .../BitWiseTests/BitWiseTestFixture.cs | 21 +++++- Kmse.Core/Utilities/Bitwise.cs | 4 +- .../Z80/Registers/General/IZ80Accumulator.cs | 12 ++-- .../Z80/Registers/General/Z80Accumulator.cs | 24 +++---- .../IZ8016BitGeneralPurposeRegister.cs | 2 + .../Z80/Registers/IZ8016BitSpecialRegister.cs | 2 + .../SpecialPurpose/IZ80IndexRegisterX.cs | 3 - .../SpecialPurpose/IZ80IndexRegisterXy.cs | 9 +++ .../SpecialPurpose/IZ80IndexRegisterY.cs | 3 - .../SpecialPurpose/Z80IndexRegisterX.cs | 9 --- .../SpecialPurpose/Z80IndexRegisterXy.cs | 69 +++++++++++++++++++ .../SpecialPurpose/Z80IndexRegisterY.cs | 9 --- .../SpecialPurpose/Z80ProgramCounter.cs | 13 ++-- .../Z80/Registers/Z8016BitRegisterBase.cs | 10 +++ Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs | 2 +- Kmse.Core/Z80/Z80Cpu.cs | 12 ++-- 16 files changed, 144 insertions(+), 60 deletions(-) delete mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterXy.cs delete mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs delete mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs create mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs delete mode 100644 Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs diff --git a/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs index cc21c04..e6fe4eb 100644 --- a/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs +++ b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs @@ -335,8 +335,27 @@ public void WhenSettingOrClearingBitIf(byte source, int bit, byte expectedValue, [Test] [TestCaseSource(nameof(_toUnsignedShortTestCases))] - public void WhenCheckingIntIfBitSet(byte high, byte low, ushort value) + public void WhenConvertingFromBytesToUnsignedShort(byte high, byte low, ushort value) { Bitwise.ToUnsigned16BitValue(high, low).Should().Be(value); } + + private static object[] _toBytesFromUnsignedShortTestCases = + { + new object[] { (ushort)0, (byte)0, (byte)0 }, + new object[] { (ushort)0x0101, (byte)1, (byte)1 }, + new object[] { (ushort)0x01FF, (byte)1, (byte)0xFF }, + new object[] { (ushort)0xFF01, (byte)0xFF, (byte)0x01 }, + new object[] { (ushort)0xCBAD, (byte)0xCB, (byte)0xAD }, + new object[] { (ushort)0xFFFF, (byte)0xFF, (byte)0xFF } + }; + + [Test] + [TestCaseSource(nameof(_toBytesFromUnsignedShortTestCases))] + public void WhenConvertingFromUnsignedShortToBytes(ushort value, byte high, byte low) + { + var output = Bitwise.ToBytes(value); + output.High.Should().Be(high); + output.Low.Should().Be(low); + } } \ No newline at end of file diff --git a/Kmse.Core/Utilities/Bitwise.cs b/Kmse.Core/Utilities/Bitwise.cs index cc31aad..6d0377e 100644 --- a/Kmse.Core/Utilities/Bitwise.cs +++ b/Kmse.Core/Utilities/Bitwise.cs @@ -7,9 +7,9 @@ public static ushort ToUnsigned16BitValue(byte high, byte low) return (ushort)((high << 8) + low); } - public static (byte, byte) ToBytes(ushort value) + public static (byte High, byte Low) ToBytes(ushort value) { - return ((byte)(value & 0xFF00 >> 8), (byte)(value & 0x00FF)); + return ((byte)((value & 0xFF00) >> 8), (byte)(value & 0x00FF)); } public static bool IsSet(byte value, int bit) diff --git a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs index 3255688..ae1f22a 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs @@ -14,18 +14,18 @@ public interface IZ80Accumulator : IZ808BitGeneralPurposeRegister void DecimalAdjustAccumulator(); void InvertAccumulatorRegister(); void NegateAccumulatorRegister(); - void AddFromMemory(Z80Register register, int offset, bool withCarry = false); - void AddFromMemory(Z80Register register, int offset, byte valueToAndAgainst); + void AddFromMemory(IZ8016BitRegister register, int offset, bool withCarry = false); void Add(byte value, bool withCarry = false); - void SubtractFromMemory(Z80Register register, int offset, bool withCarry = false); + void SubtractFromMemory(IZ8016BitRegister register, int offset, bool withCarry = false); void Subtract(byte value, bool withCarry = false); void Compare(byte value); - void CompareFromMemory(Z80Register register, int offset); + void CompareFromMemory(IZ8016BitRegister register, int offset); void And(byte value, byte valueToAndAgainst); + void AndFromMemory(IZ8016BitRegister register, int offset, byte valueToAndAgainst); void Or(byte value, byte valueToAndAgainst); - void OrFromMemory(Z80Register register, int offset, byte valueToOrAgainst); + void OrFromMemory(IZ8016BitRegister register, int offset, byte valueToOrAgainst); void Xor(byte value, byte valueToXorAgainst); - void XorFromMemory(Z80Register register, int offset, byte valueToXorAgainst); + void XorFromMemory(IZ8016BitRegister register, int offset, byte valueToXorAgainst); void RotateLeftCircularAccumulator(); void RotateLeftAccumulator(); void RotateRightCircularAccumulator(); diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index 8752291..1c53868 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -167,9 +167,9 @@ public void NegateAccumulatorRegister() Value = (byte)twosComplementValue; } - public void AddFromMemory(Z80Register register, int offset, bool withCarry = false) + public void AddFromMemory(IZ8016BitRegister register, int offset, bool withCarry = false) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; Add(value, withCarry); } @@ -201,9 +201,9 @@ public void Add(byte value, bool withCarry = false) Set((byte)newValue); } - public void SubtractFromMemory(Z80Register register, int offset, bool withCarry = false) + public void SubtractFromMemory(IZ8016BitRegister register, int offset, bool withCarry = false) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; Subtract(value, withCarry); } @@ -250,9 +250,9 @@ public void Compare(byte value) Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value < value); } - public void CompareFromMemory(Z80Register register, int offset) + public void CompareFromMemory(IZ8016BitRegister register, int offset) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; Compare(value); } @@ -270,9 +270,9 @@ public void And(byte value, byte valueToAndAgainst) Set(newValue); } - public void AddFromMemory(Z80Register register, int offset, byte valueToAndAgainst) + public void AndFromMemory(IZ8016BitRegister register, int offset, byte valueToAndAgainst) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; And(value, valueToAndAgainst); } @@ -290,9 +290,9 @@ public void Or(byte value, byte valueToAndAgainst) Set(newValue); } - public void OrFromMemory(Z80Register register, int offset, byte valueToOrAgainst) + public void OrFromMemory(IZ8016BitRegister register, int offset, byte valueToOrAgainst) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; Or(value, valueToOrAgainst); } @@ -310,9 +310,9 @@ public void Xor(byte value, byte valueToXorAgainst) Set(newValue); } - public void XorFromMemory(Z80Register register, int offset, byte valueToXorAgainst) + public void XorFromMemory(IZ8016BitRegister register, int offset, byte valueToXorAgainst) { - var value = Memory[(ushort)(register.Word + offset)]; + var value = Memory[(ushort)(register.Value + offset)]; Xor(value, valueToXorAgainst); } diff --git a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs index 917f573..54c8256 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -15,7 +15,9 @@ public interface IZ8016BitGeneralPurposeRegister : IZ8016BitRegister void SetBitByRegisterLocation(int bit, int offset); void TestBitByRegisterLocation(int bit, int offset); void Add(ushort source, bool withCarry = false); + void Add(IZ8016BitRegister register, bool withCarry = false); void Subtract(ushort source, bool withCarry = false); + void Subtract(IZ8016BitRegister register, bool withCarry = false); void RotateLeftCircular(int offset); void RotateLeft(int offset); void RotateRightCircular(int offset); diff --git a/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs index 923b118..f3a21ad 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs @@ -8,7 +8,9 @@ public interface IZ8016BitSpecialRegister : IZ8016BitRegister void SetBitByRegisterLocation(int bit, int offset); void TestBitByRegisterLocation(int bit, int offset); void Add(ushort source, bool withCarry = false); + void Add(IZ8016BitRegister register, bool withCarry = false); void Subtract(ushort source, bool withCarry = false); + void Subtract(IZ8016BitRegister register, bool withCarry = false); void RotateLeftCircular(int offset); void RotateLeft(int offset); void RotateRightCircular(int offset); diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs deleted file mode 100644 index 8ff8529..0000000 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterX.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Kmse.Core.Z80.Registers.SpecialPurpose; - -public interface IZ80IndexRegisterX : IZ8016BitSpecialRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterXy.cs new file mode 100644 index 0000000..da95849 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterXy.cs @@ -0,0 +1,9 @@ +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80IndexRegisterXy : IZ8016BitSpecialRegister +{ + void IncrementHigh(); + void IncrementLow(); + void DecrementHigh(); + void DecrementLow(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs deleted file mode 100644 index ec634b1..0000000 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80IndexRegisterY.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Kmse.Core.Z80.Registers.SpecialPurpose; - -public interface IZ80IndexRegisterY : IZ8016BitSpecialRegister { } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs deleted file mode 100644 index bc61f4a..0000000 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterX.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Kmse.Core.Memory; -using Kmse.Core.Z80.Registers.General; - -namespace Kmse.Core.Z80.Registers.SpecialPurpose; - -public class Z80IndexRegisterX : Z8016BitSpecialRegisterBase, IZ80IndexRegisterX -{ - public Z80IndexRegisterX(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs new file mode 100644 index 0000000..748d140 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -0,0 +1,69 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Support; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80IndexRegisterXy : Z8016BitSpecialRegisterBase, IZ80IndexRegisterXy +{ + public Z80IndexRegisterXy(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } + + public void IncrementHigh() + { + var oldValue = Register.High; + var newValue = (byte)(Register.High + 1); + Set(newValue); + CheckIncrementFlags(newValue, oldValue); + } + + public void DecrementHigh() + { + var oldValue = Register.High; + var newValue = (byte)(Register.High - 1); + Set(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + public void IncrementLow() + { + var oldValue = Register.High; + var newValue = (byte)(Register.High + 1); + Set(newValue); + CheckIncrementFlags(newValue, oldValue); + } + + public void DecrementLow() + { + var oldValue = Register.High; + var newValue = (byte)(Register.High - 1); + Set(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + private void CheckIncrementFlags(byte newValue, byte oldValue) + { + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is carry from bit 3 + // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble + // https://en.wikipedia.org/wiki/Half-carry_flag + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + private void CheckDecrementFlags(byte newValue, byte oldValue) + { + Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); + Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + // Set half carry is borrow from bit 4 + // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits + // ie. 0x20 - 1 = 0x1F + // https://en.wikipedia.org/wiki/Half-carry_flag + // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs deleted file mode 100644 index 055fffc..0000000 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterY.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Kmse.Core.Memory; -using Kmse.Core.Z80.Registers.General; - -namespace Kmse.Core.Z80.Registers.SpecialPurpose; - -public class Z80IndexRegisterY : Z8016BitSpecialRegisterBase, IZ80IndexRegisterY -{ - public Z80IndexRegisterY(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } -} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index a77f3cf..a9b91d9 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -9,7 +9,6 @@ public class Z80ProgramCounter : Z8016BitSpecialRegisterBase, IZ80ProgramCounter { private readonly IZ80StackManager _stack; private readonly IZ80InstructionLogger _z80InstructionLogger; - private Z80Register _pc; public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger, IZ80FlagsManager flags, IZ80StackManager stack) @@ -46,7 +45,7 @@ public ushort GetNextTwoDataBytes() public void MoveProgramCounterForward(ushort offset) { // If this goes above ushort max, we assume that when PC hits the limit it just wraps around instead of throwing an error or failing - _pc.Word += offset; + Register.Word += offset; } /// @@ -56,7 +55,7 @@ public void MoveProgramCounterForward(ushort offset) public void MoveProgramCounterBackward(ushort offset) { // If this goes below zero/negative, we assume that when PC hits -1 it just wraps around instead of throwing an error or failing - _pc.Word -= offset; + Register.Word -= offset; } public void SetAndSaveExisting(ushort address) @@ -76,9 +75,7 @@ public void Set(Z80Register register) public void SetFromStack() { - var register = new Z80Register(); _stack.PopRegisterFromStack(this); - Set(register.Word); } public bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) @@ -105,7 +102,7 @@ public bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) public void JumpByOffset(byte offset) { - var newPcLocation = _pc.Word; + var newPcLocation = Register.Word; // Range is -126 to +129 so we need a signed version // However sbyte only goes from -128 to +127 but we need -126 to +129 so have to do this manually @@ -195,8 +192,8 @@ public bool ReturnIfNotFlag(Z80StatusFlags flag) private byte GetNextByteByProgramCounter() { // Note: We don't increment the cycle count here since this operation is included in overall cycle count for each instruction - var data = Memory[_pc.Word]; - _pc.Word++; + var data = Memory[Register.Word]; + Register.Word++; return data; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index 94c7e56..bf4b5f9 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -81,6 +81,11 @@ public void Add(ushort source, bool withCarry = false) Set((ushort)newValue); } + public void Add(IZ8016BitRegister register, bool withCarry = false) + { + Add(register.Value, withCarry); + } + public void Subtract(ushort source, bool withCarry = false) { int valueWithCarry = source; @@ -106,6 +111,11 @@ public void Subtract(ushort source, bool withCarry = false) Set((ushort)newValue); } + public void Subtract(IZ8016BitRegister register, bool withCarry = false) + { + Subtract(register.Value, withCarry); + } + public void RotateLeftCircular(int offset) { var location = (ushort)(Value + offset); diff --git a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs index dc41529..7603bd3 100644 --- a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs @@ -31,7 +31,7 @@ private void LoadRR(byte opCode) if (destinationRegisterId == 0x06) { - var register = Get8BitRegisterByRIdentifier(destinationRegisterId); + var register = Get8BitRegisterByRIdentifier(sourceRegisterId); _memoryManagement.WriteToMemory(_hl, register.Value); _currentCycleCount += 3; return; diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 5b0490a..895e134 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -36,8 +36,8 @@ public partial class Z80Cpu : IZ80Cpu private IZ808BitGeneralPurposeRegister _e; private IZ808BitGeneralPurposeRegister _h; private IZ808BitGeneralPurposeRegister _l; - private IZ80IndexRegisterX _ix; - private IZ80IndexRegisterY _iy; + private IZ80IndexRegisterXy _ix; + private IZ80IndexRegisterXy _iy; private IZ80MemoryRefreshRegister _rRegister; private IZ80InterruptPageAddressRegister _iRegister; private IZ80CpuInputOutput _ioManagement; @@ -71,8 +71,8 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _bc = new Z80BcRegister(memory, _af.Flags); _de = new Z80DeRegister(memory, _af.Flags); _hl = new Z80HlRegister(memory, _af.Flags); - _ix = new Z80IndexRegisterX(memory, _af.Flags); - _iy = new Z80IndexRegisterY(memory, _af.Flags); + _ix = new Z80IndexRegisterXy(memory, _af.Flags); + _iy = new Z80IndexRegisterXy(memory, _af.Flags); _rRegister = new Z80MemoryRefreshRegister(memory, _af.Flags); _iRegister = new Z80InterruptPageAddressRegister(memory, _af.Flags); @@ -264,11 +264,11 @@ private void Halt() private void ResumeIfHalted() { - if (!_halted) + if (_halted) { _cpuLogger.Debug("Resuming CPU"); } - _halted = true; + _halted = false; } private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) From c8627f7a1be30c41f5ba233a8a300f33f0b5adf9 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:29:43 +1000 Subject: [PATCH 07/21] Update Z80 instructions to use new register classes --- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 945 +++++++++++++-------------- 1 file changed, 472 insertions(+), 473 deletions(-) diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index bc4de3f..6bae530 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -113,9 +113,9 @@ private void PopulateCpuControlOperations() AddStandardInstruction(0xF3, 4, "DI", "Disable Interrupts", (_) => { _interruptFlipFlop1 = false; _interruptFlipFlop2 = false; }); AddStandardInstruction(0xFB, 4, "EI", "Enable Interrupts", (_) => { _interruptFlipFlop1 = true; _interruptFlipFlop2 = true; }); - AddDoubleByteInstruction(0xED, 0x46, 8, "IM 0", "Set Maskable Interupt to Mode 0", (_) => { _interruptMode = 0; }); - AddDoubleByteInstruction(0xED, 0x56, 8, "IM 1", "Set Maskable Interupt to Mode 1", (_) => { _interruptMode = 1; }); - AddDoubleByteInstruction(0xED, 0x5E, 8, "IM 2", "Set Maskable Interupt to Mode 2", (_) => { _interruptMode = 2; }); + AddDoubleByteInstruction(0xED, 0x46, 8, "IM 0", "Set Maskable Interrupt to Mode 0", (_) => { _interruptMode = 0; }); + AddDoubleByteInstruction(0xED, 0x56, 8, "IM 1", "Set Maskable Interrupt to Mode 1", (_) => { _interruptMode = 1; }); + AddDoubleByteInstruction(0xED, 0x5E, 8, "IM 2", "Set Maskable Interrupt to Mode 2", (_) => { _interruptMode = 2; }); } private void PopulateJumpCallAndReturnOperations() @@ -144,7 +144,7 @@ private void PopulateJumpCallAndReturnOperations() { _b.Decrement(); var offset = _pc.GetNextDataByte(); - if (_bc.High != 0) + if (_b.Value != 0) { _pc.JumpByOffset(offset); _currentCycleCount += 13; @@ -189,94 +189,94 @@ private void PopulateJumpCallAndReturnOperations() private void PopulateArthmeticAndLogicalInstructions() { - AddStandardInstruction(0x87, 4, "ADD A, A", "Add A to A", _ => { AddValueTo8BitRegister(_af.High, ref _af.High); }); - AddStandardInstruction(0x80, 4, "ADD A, B", "Add B to A", _ => { AddValueTo8BitRegister(_bc.High, ref _af.High); }); - AddStandardInstruction(0x81, 4, "ADD A, C", "Add C to A", _ => { AddValueTo8BitRegister(_bc.Low, ref _af.High); }); - AddStandardInstruction(0x82, 4, "ADD A, D", "Add D to A", _ => { AddValueTo8BitRegister(_de.High, ref _af.High); }); - AddStandardInstruction(0x83, 4, "ADD A, E", "Add E to A", _ => { AddValueTo8BitRegister(_de.Low, ref _af.High); }); - AddStandardInstruction(0x84, 4, "ADD A, H", "Add H to A", _ => { AddValueTo8BitRegister(_hl.High, ref _af.High); }); - AddStandardInstruction(0x85, 4, "ADD A, L", "Add L to A", _ => { AddValueTo8BitRegister(_hl.Low, ref _af.High); }); - AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { AddValueTo8BitRegister(_pc.GetNextDataByte(), ref _af.High);}); - - AddStandardInstruction(0x09, 11, "ADD HL,BC", "Add BC to HL", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl); }); - AddStandardInstruction(0x19, 11, "ADD HL,DE", "Add DE to HL", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl); }); - AddStandardInstruction(0x29, 11, "ADD HL,HL", "Add HL to HL", _ => { Add16BitRegisterTo16BitRegister(_hl, ref _hl); }); - AddStandardInstruction(0x39, 11, "ADD HL,SP", "Add SP to HL", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _hl); }); - AddDoubleByteInstruction(0xDD, 0x19, 15, "ADD IX,DE", "Add DE to IX", _ => { Add16BitRegisterTo16BitRegister(_de, ref _ix); }); - AddDoubleByteInstruction(0xDD, 0x29, 15, "ADD IX,IX", "Add IX to IX", _ => { Add16BitRegisterTo16BitRegister(_ix, ref _ix); }); - AddDoubleByteInstruction(0xDD, 0x39, 15, "ADD IX,SP", "Add SP to IX", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _ix); }); - AddDoubleByteInstruction(0xDD, 0x09, 15, "ADD IX,BC", "Add BC to IX", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _ix); }); - - AddDoubleByteInstruction(0xFD, 0x09, 15, "ADD IY,BC", "Add BC to IY", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _iy); }); - AddDoubleByteInstruction(0xFD, 0x19, 15, "ADD IY,DE", "Add DE to IY", _ => { Add16BitRegisterTo16BitRegister(_de, ref _iy); }); - AddDoubleByteInstruction(0xFD, 0x29, 15, "ADD IY,IY", "Add IY to IY", _ => { Add16BitRegisterTo16BitRegister(_iy, ref _iy); }); - AddDoubleByteInstruction(0xFD, 0x39, 15, "ADD IY,SP", "Add SP to IY", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _iy); }); - - AddStandardInstruction(0x86, 4, "ADD A,(HL)", "Add Data at memory location from HL to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_hl, 0, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x86, 19, "ADD A,(IX+d)", "Add Data at memory location from IX + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x86, 19, "ADD A,(IY+d)", "Add Data at memory location from IY + d to A", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0x8F, 4, "ADC A, A", "Add A to A with Carry", _ => { AddValueTo8BitRegister(_af.High, ref _af.High, true); }); - AddStandardInstruction(0x88, 4, "ADC B, B", "Add B to A with Carry", _ => { AddValueTo8BitRegister(_bc.High, ref _af.High, true); }); - AddStandardInstruction(0x89, 4, "ADC C, C", "Add C to A with Carry", _ => { AddValueTo8BitRegister(_bc.Low, ref _af.High, true); }); - AddStandardInstruction(0x8A, 4, "ADC D, D", "Add D to A with Carry", _ => { AddValueTo8BitRegister(_de.High, ref _af.High, true); }); - AddStandardInstruction(0x8B, 4, "ADC E, E", "Add E to A with Carry", _ => { AddValueTo8BitRegister(_de.Low, ref _af.High, true); }); - AddStandardInstruction(0x8C, 4, "ADC H, H", "Add H to A with Carry", _ => { AddValueTo8BitRegister(_hl.High, ref _af.High, true); }); - AddStandardInstruction(0x8D, 4, "ADC L, L", "Add L to A with Carry", _ => { AddValueTo8BitRegister(_hl.Low, ref _af.High, true); }); - AddStandardInstruction(0xCE, 7, "ADC A, N", "Add n to A with Carry", _ => { AddValueTo8BitRegister(_pc.GetNextDataByte(), ref _af.High, true); }); - - AddStandardInstruction(0x8E, 4, "ADC (HL)", "Add (HL) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_hl, 0, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x8E, 19, "ADC A,(IX+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8E, 19, "ADC A,(IY+d)", "Add (IX+d) to A with Carry", _ => { AddValueAtRegisterMemoryLocationTo8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High, true); }); + AddStandardInstruction(0x87, 4, "ADD A, A", "Add A to A", _ => { _accumulator.Add(_accumulator.Value); }); + AddStandardInstruction(0x80, 4, "ADD A, B", "Add B to A", _ => { _accumulator.Add(_b.Value); }); + AddStandardInstruction(0x81, 4, "ADD A, C", "Add C to A", _ => { _accumulator.Add(_c.Value); }); + AddStandardInstruction(0x82, 4, "ADD A, D", "Add D to A", _ => { _accumulator.Add(_d.Value); }); + AddStandardInstruction(0x83, 4, "ADD A, E", "Add E to A", _ => { _accumulator.Add(_e.Value); }); + AddStandardInstruction(0x84, 4, "ADD A, H", "Add H to A", _ => { _accumulator.Add(_h.Value); }); + AddStandardInstruction(0x85, 4, "ADD A, L", "Add L to A", _ => { _accumulator.Add(_l.Value); }); + AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { _accumulator.Add(_pc.GetNextDataByte());}); + + AddStandardInstruction(0x09, 11, "ADD HL,BC", "Add BC to HL", _ => { _hl.Add(_bc); }); + AddStandardInstruction(0x19, 11, "ADD HL,DE", "Add DE to HL", _ => { _hl.Add(_de); }); + AddStandardInstruction(0x29, 11, "ADD HL,HL", "Add HL to HL", _ => { _hl.Add(_hl); }); + AddStandardInstruction(0x39, 11, "ADD HL,SP", "Add SP to HL", _ => { _hl.Add(_stack); }); + AddDoubleByteInstruction(0xDD, 0x19, 15, "ADD IX,DE", "Add DE to IX", _ => { _ix.Add(_de); }); + AddDoubleByteInstruction(0xDD, 0x29, 15, "ADD IX,IX", "Add IX to IX", _ => { _ix.Add(_ix); }); + AddDoubleByteInstruction(0xDD, 0x39, 15, "ADD IX,SP", "Add SP to IX", _ => { _ix.Add(_stack); }); + AddDoubleByteInstruction(0xDD, 0x09, 15, "ADD IX,BC", "Add BC to IX", _ => { _ix.Add(_bc); }); + + AddDoubleByteInstruction(0xFD, 0x09, 15, "ADD IY,BC", "Add BC to IY", _ => { _iy.Add(_bc); }); + AddDoubleByteInstruction(0xFD, 0x19, 15, "ADD IY,DE", "Add DE to IY", _ => { _iy.Add(_de); }); + AddDoubleByteInstruction(0xFD, 0x29, 15, "ADD IY,IY", "Add IY to IY", _ => { _iy.Add(_iy); }); + AddDoubleByteInstruction(0xFD, 0x39, 15, "ADD IY,SP", "Add SP to IY", _ => { _iy.Add(_stack); }); + + AddStandardInstruction(0x86, 4, "ADD A,(HL)", "Add Data at memory location from HL to A", _ => { _accumulator.AddFromMemory(_hl, 0); }); + AddDoubleByteInstruction(0xDD, 0x86, 19, "ADD A,(IX+d)", "Add Data at memory location from IX + d to A", _ => { _accumulator.AddFromMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x86, 19, "ADD A,(IY+d)", "Add Data at memory location from IY + d to A", _ => { _accumulator.AddFromMemory(_iy, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0x8F, 4, "ADC A, A", "Add A to A with Carry", _ => { _accumulator.Add(_accumulator.Value, true); }); + AddStandardInstruction(0x88, 4, "ADC B, B", "Add B to A with Carry", _ => { _accumulator.Add(_b.Value, true); }); + AddStandardInstruction(0x89, 4, "ADC C, C", "Add C to A with Carry", _ => { _accumulator.Add(_c.Value, true); }); + AddStandardInstruction(0x8A, 4, "ADC D, D", "Add D to A with Carry", _ => { _accumulator.Add(_d.Value, true); }); + AddStandardInstruction(0x8B, 4, "ADC E, E", "Add E to A with Carry", _ => { _accumulator.Add(_e.Value, true); }); + AddStandardInstruction(0x8C, 4, "ADC H, H", "Add H to A with Carry", _ => { _accumulator.Add(_h.Value, true); }); + AddStandardInstruction(0x8D, 4, "ADC L, L", "Add L to A with Carry", _ => { _accumulator.Add(_l.Value, true); }); + AddStandardInstruction(0xCE, 7, "ADC A, N", "Add n to A with Carry", _ => { _accumulator.Add(_pc.GetNextDataByte(), true); }); + + AddStandardInstruction(0x8E, 4, "ADC (HL)", "Add (HL) to A with Carry", _ => { _accumulator.AddFromMemory(_hl, 0, true); }); + AddDoubleByteInstruction(0xDD, 0x8E, 19, "ADC A,(IX+d)", "Add (IX+d) to A with Carry", _ => { _accumulator.AddFromMemory(_ix, _pc.GetNextDataByte(), true); }); + AddDoubleByteInstruction(0xFD, 0x8E, 19, "ADC A,(IY+d)", "Add (IX+d) to A with Carry", _ => { _accumulator.AddFromMemory(_iy, _pc.GetNextDataByte(), true); }); - AddDoubleByteInstruction(0xED, 0x4A, 15, "ADC HL,BC", "Add BC to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_bc, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x5A, 15, "ADC HL,DE", "Add DE to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_de, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x6A, 15, "ADC HL,HL", "Add HL to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_hl, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x7A, 15, "ADC HL,SP", "Add SP to HL with Carry", _ => { Add16BitRegisterTo16BitRegister(_stack.AsRegister(), ref _hl, true); }); - - AddStandardInstruction(0x97, 4, "SUB A, A", "Subtract A from A", _ => { SubtractValueFrom8BitRegister(_af.High, ref _af.High); }); - AddStandardInstruction(0x90, 4, "SUB A, B", "Subtract B from A", _ => { SubtractValueFrom8BitRegister(_bc.High, ref _af.High); }); - AddStandardInstruction(0x91, 4, "SUB A, C", "Subtract C from A", _ => { SubtractValueFrom8BitRegister(_bc.Low, ref _af.High); }); - AddStandardInstruction(0x92, 4, "SUB A, D", "Subtract D from A", _ => { SubtractValueFrom8BitRegister(_de.High, ref _af.High); }); - AddStandardInstruction(0x93, 4, "SUB A, E", "Subtract E from A", _ => { SubtractValueFrom8BitRegister(_de.Low, ref _af.High); }); - AddStandardInstruction(0x94, 4, "SUB A, H", "Subtract H from A", _ => { SubtractValueFrom8BitRegister(_hl.High, ref _af.High); }); - AddStandardInstruction(0x95, 4, "SUB A, L", "Subtract L from A", _ => { SubtractValueFrom8BitRegister(_hl.Low, ref _af.High); }); - AddStandardInstruction(0xD6, 7, "SUB A, N", "Subtract n from A", _ => { SubtractValueFrom8BitRegister(_pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0x96, 7, "SUB (HL)", "Subtract Data at memory location HL from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_hl, 0, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x96, 19, "SUB (IX+d)", "Subtract Data at memory location IX + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x96, 19, "SUB (IY+d)", "Subtract Data at memory location IY + d from A", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0x9F, 4, "SBC A, A", "Subtract A from A with Carry", _ => { SubtractValueFrom8BitRegister(_af.High, ref _af.High, true); }); - AddStandardInstruction(0x98, 4, "SBC A, B", "Subtract B from A with Carry", _ => { SubtractValueFrom8BitRegister(_bc.High, ref _af.High, true); }); - AddStandardInstruction(0x99, 4, "SBC A, C", "Subtract C from A with Carry", _ => { SubtractValueFrom8BitRegister(_bc.Low, ref _af.High, true); }); - AddStandardInstruction(0x9A, 4, "SBC A, D", "Subtract D from A with Carry", _ => { SubtractValueFrom8BitRegister(_de.High, ref _af.High, true); }); - AddStandardInstruction(0x9B, 4, "SBC A, E", "Subtract E from A with Carry", _ => { SubtractValueFrom8BitRegister(_de.Low, ref _af.High, true); }); - AddStandardInstruction(0x9C, 4, "SBC A, H", "Subtract H from A with Carry", _ => { SubtractValueFrom8BitRegister(_hl.High, ref _af.High, true); }); - AddStandardInstruction(0x9D, 4, "SBC A, L", "Subtract L from A with Carry", _ => { SubtractValueFrom8BitRegister(_hl.Low, ref _af.High, true); }); - AddStandardInstruction(0xDE, 7, "SBC A,N", "Subtract N from A with Carry", _ => { SubtractValueFrom8BitRegister(_pc.GetNextDataByte(), ref _af.High, true); }); - - AddStandardInstruction(0x9E, 4, "SBC (HL)", "Subtract (HL) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_hl, 0, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x9E, 19, "SBC A,(IX+d)", "Subtract (IX+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_ix, _pc.GetNextDataByte(), ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x9E, 19, "SBC A,(IY+d)", "Subtract (Iy+d) from A with Carry", _ => { SubtractValueAtRegisterMemoryLocationFrom8BitRegister(_iy, _pc.GetNextDataByte(), ref _af.High, true); }); - - AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_bc, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_de, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x62, 15, "SBC HL,HL", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_hl, ref _hl, true); }); - AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { Sub16BitRegisterFrom16BitRegister(_stack.AsRegister(), ref _hl, true); }); - - AddStandardInstruction(0xBF, 4, "CP A", "Compare A to A", _ => { Compare8Bit(_af.High, _af.High); }); - AddStandardInstruction(0xB8, 4, "CP B", "Compare B to A", _ => { Compare8Bit(_bc.High, _af.High); }); - AddStandardInstruction(0xB9, 4, "CP C", "Compare C to A", _ => { Compare8Bit(_bc.Low, _af.High); }); - AddStandardInstruction(0xBA, 4, "CP D", "Compare D to A", _ => { Compare8Bit(_de.High, _af.High); }); - AddStandardInstruction(0xBB, 4, "CP E", "Compare E to A", _ => { Compare8Bit(_de.Low, _af.High); }); - AddStandardInstruction(0xBC, 4, "CP H", "Compare H to A", _ => { Compare8Bit(_hl.High, _af.High); }); - AddStandardInstruction(0xBD, 4, "CP L", "Compare L to A", _ => { Compare8Bit(_hl.Low, _af.High); }); - AddStandardInstruction(0xBE, 7, "CP (HL)", "Compare Data at memory location in (HL) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High); }); - - AddStandardInstruction(0xFE, 7, "CP N", "Compare value N to A", _ => { Compare8Bit(_pc.GetNextDataByte(), _af.High); }); - AddDoubleByteInstruction(0xDD, 0xBE, 19, "CP (IX+d)", "Compare Data at memory location in (IX + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High); }); - AddDoubleByteInstruction(0xFD, 0xBE, 19, "CP (IY+d)", "Compare Data at memory location in (IY + d) to A", _ => { Compare8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High); }); + AddDoubleByteInstruction(0xED, 0x4A, 15, "ADC HL,BC", "Add BC to HL with Carry", _ => { _hl.Add(_bc, true); }); + AddDoubleByteInstruction(0xED, 0x5A, 15, "ADC HL,DE", "Add DE to HL with Carry", _ => { _hl.Add(_de, true); }); + AddDoubleByteInstruction(0xED, 0x6A, 15, "ADC HL,HL", "Add HL to HL with Carry", _ => { _hl.Add(_hl, true); }); + AddDoubleByteInstruction(0xED, 0x7A, 15, "ADC HL,SP", "Add SP to HL with Carry", _ => { _hl.Add(_stack, true); }); + + AddStandardInstruction(0x97, 4, "SUB A, A", "Subtract A from A", _ => { _accumulator.Subtract(_accumulator.Value); }); + AddStandardInstruction(0x90, 4, "SUB A, B", "Subtract B from A", _ => { _accumulator.Subtract(_b.Value); }); + AddStandardInstruction(0x91, 4, "SUB A, C", "Subtract C from A", _ => { _accumulator.Subtract(_c.Value); }); + AddStandardInstruction(0x92, 4, "SUB A, D", "Subtract D from A", _ => { _accumulator.Subtract(_d.Value); }); + AddStandardInstruction(0x93, 4, "SUB A, E", "Subtract E from A", _ => { _accumulator.Subtract(_e.Value); }); + AddStandardInstruction(0x94, 4, "SUB A, H", "Subtract H from A", _ => { _accumulator.Subtract(_h.Value); }); + AddStandardInstruction(0x95, 4, "SUB A, L", "Subtract L from A", _ => { _accumulator.Subtract(_l.Value); }); + AddStandardInstruction(0xD6, 7, "SUB A, N", "Subtract n from A", _ => { _accumulator.Subtract(_pc.GetNextDataByte()); }); + + AddStandardInstruction(0x96, 7, "SUB (HL)", "Subtract Data at memory location HL from A", _ => { _accumulator.SubtractFromMemory(_hl, 0); }); + AddDoubleByteInstruction(0xDD, 0x96, 19, "SUB (IX+d)", "Subtract Data at memory location IX + d from A", _ => { _accumulator.SubtractFromMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x96, 19, "SUB (IY+d)", "Subtract Data at memory location IY + d from A", _ => { _accumulator.SubtractFromMemory(_iy, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0x9F, 4, "SBC A, A", "Subtract A from A with Carry", _ => { _accumulator.Subtract(_accumulator.Value, true); }); + AddStandardInstruction(0x98, 4, "SBC A, B", "Subtract B from A with Carry", _ => { _accumulator.Subtract(_b.Value, true); }); + AddStandardInstruction(0x99, 4, "SBC A, C", "Subtract C from A with Carry", _ => { _accumulator.Subtract(_c.Value, true); }); + AddStandardInstruction(0x9A, 4, "SBC A, D", "Subtract D from A with Carry", _ => { _accumulator.Subtract(_d.Value, true); }); + AddStandardInstruction(0x9B, 4, "SBC A, E", "Subtract E from A with Carry", _ => { _accumulator.Subtract(_e.Value, true); }); + AddStandardInstruction(0x9C, 4, "SBC A, H", "Subtract H from A with Carry", _ => { _accumulator.Subtract(_h.Value, true); }); + AddStandardInstruction(0x9D, 4, "SBC A, L", "Subtract L from A with Carry", _ => { _accumulator.Subtract(_l.Value, true); }); + AddStandardInstruction(0xDE, 7, "SBC A, N", "Subtract N from A with Carry", _ => { _accumulator.Subtract(_pc.GetNextDataByte(), true); }); + + AddStandardInstruction(0x9E, 4, "SBC (HL)", "Subtract (HL) from A with Carry", _ => { _accumulator.SubtractFromMemory(_hl, 0, true); }); + AddDoubleByteInstruction(0xDD, 0x9E, 19, "SBC A,(IX+d)", "Subtract (IX+d) from A with Carry", _ => { _accumulator.SubtractFromMemory(_ix, _pc.GetNextDataByte(), true); }); + AddDoubleByteInstruction(0xFD, 0x9E, 19, "SBC A,(IY+d)", "Subtract (Iy+d) from A with Carry", _ => { _accumulator.SubtractFromMemory(_iy, _pc.GetNextDataByte(), true); }); + + AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { _hl.Subtract(_bc, true); }); + AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { _hl.Subtract(_de, true); }); + AddDoubleByteInstruction(0xED, 0x62, 15, "SBC HL,HL", "Subtract with Carry", _ => { _hl.Subtract(_hl, true); }); + AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { _hl.Subtract(_stack, true); }); + + AddStandardInstruction(0xBF, 4, "CP A", "Compare A to A", _ => { _accumulator.Compare(_accumulator.Value); }); + AddStandardInstruction(0xB8, 4, "CP B", "Compare B to A", _ => { _accumulator.Compare(_b.Value); }); + AddStandardInstruction(0xB9, 4, "CP C", "Compare C to A", _ => { _accumulator.Compare(_c.Value); }); + AddStandardInstruction(0xBA, 4, "CP D", "Compare D to A", _ => { _accumulator.Compare(_d.Value); }); + AddStandardInstruction(0xBB, 4, "CP E", "Compare E to A", _ => { _accumulator.Compare(_e.Value); }); + AddStandardInstruction(0xBC, 4, "CP H", "Compare H to A", _ => { _accumulator.Compare(_h.Value); }); + AddStandardInstruction(0xBD, 4, "CP L", "Compare L to A", _ => { _accumulator.Compare(_l.Value); }); + AddStandardInstruction(0xBE, 7, "CP (HL)", "Compare Data at memory location in (HL) to A", _ => { _accumulator.CompareFromMemory(_hl, 0); }); + + AddStandardInstruction(0xFE, 7, "CP N", "Compare value N to A", _ => { _accumulator.Compare(_pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0xBE, 19, "CP (IX+d)", "Compare Data at memory location in (IX + d) to A", _ => { _accumulator.CompareFromMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0xBE, 19, "CP (IY+d)", "Compare Data at memory location in (IY + d) to A", _ => { _accumulator.CompareFromMemory(_iy, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xED, 0xA1, 16, "CPI", "Compare and Increment", _ => { CompareIncrement(); }); AddDoubleByteInstruction(0xED, 0xB1, DynamicCycleHandling, "CPIR", "Compare, Increment, Repeat", _ => @@ -297,7 +297,6 @@ private void PopulateArthmeticAndLogicalInstructions() // If BC not zero and A != (HL), set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here - var currentPc = _pc.Value; _pc.MoveProgramCounterBackward(2); _currentCycleCount += 21; } @@ -335,83 +334,83 @@ private void PopulateArthmeticAndLogicalInstructions() } }); - AddStandardInstruction(0xA7, 4, "AND A", "AND A with A and Store in A", _ => { And8Bit(_af.High, _af.High, ref _af.High); }); - AddStandardInstruction(0xA0, 4, "AND B", "AND A with B and Store in A", _ => { And8Bit(_af.High, _bc.High, ref _af.High); }); - AddStandardInstruction(0xA1, 4, "AND C", "AND A with C and Store in A", _ => { And8Bit(_af.High, _bc.Low, ref _af.High); }); - AddStandardInstruction(0xA2, 4, "AND D", "AND A with D and Store in A", _ => { And8Bit(_af.High, _de.High, ref _af.High); }); - AddStandardInstruction(0xA3, 4, "AND E", "AND A with E and Store in A", _ => { And8Bit(_af.High, _de.Low, ref _af.High); }); - AddStandardInstruction(0xA4, 4, "AND H", "AND A with H and Store in A", _ => { And8Bit(_af.High, _hl.High, ref _af.High); }); - AddStandardInstruction(0xA5, 4, "AND L", "AND A with L and Store in A", _ => { And8Bit(_af.High, _hl.Low, ref _af.High); }); - - AddStandardInstruction(0xE6, 7, "AND N", "AND A with N and Store in A", _ => { And8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0xA6, 7, "AND (HL)", "AND A with data in memory location in (HL) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xA6, 19, "AND (IX+d)", "AND A with data in memory location in (IX+d) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xA6, 19, "AND (IY+d)", "AND A with data in memory location in (IX+y) and Store in A", _ => { And8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - - AddStandardInstruction(0xB7, 4, "OR A", "OR A with A and Store in A", _ => { Or8Bit(_af.High, _af.High, ref _af.High); }); - AddStandardInstruction(0xB0, 4, "OR B", "OR A with B and Store in A", _ => { Or8Bit(_af.High, _bc.High, ref _af.High); }); - AddStandardInstruction(0xB1, 4, "OR C", "OR A with C and Store in A", _ => { Or8Bit(_af.High, _bc.Low, ref _af.High); }); - AddStandardInstruction(0xB2, 4, "OR D", "OR A with D and Store in A", _ => { Or8Bit(_af.High, _de.High, ref _af.High); }); - AddStandardInstruction(0xB3, 4, "OR E", "OR A with E and Store in A", _ => { Or8Bit(_af.High, _de.Low, ref _af.High); }); - AddStandardInstruction(0xB4, 4, "OR H", "OR A with H and Store in A", _ => { Or8Bit(_af.High, _hl.High, ref _af.High); }); - AddStandardInstruction(0xB5, 4, "OR L", "OR A with L and Store in A", _ => { Or8Bit(_af.High, _hl.Low, ref _af.High); }); - AddStandardInstruction(0xF6, 7, "OR N", "Or A with N and Store in A", _ => { Or8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0xB6, 7, "OR (HL)", "OR A with data in memory location in (HL) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xB6, 19, "OR (IX+d)", "OR A with data in memory location in (IX+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xB6, 19, "OR (IY+d)", "OR A with data in memory location in (IY+d) and Store in A", _ => { Or8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - - AddStandardInstruction(0xAF, 4, "XOR A", "XOR A with A and Store in A", _ => { Xor8Bit(_af.High, _af.High, ref _af.High); }); - AddStandardInstruction(0xA8, 4, "XOR B", "XOR A with B and Store in A", _ => { Xor8Bit(_af.High, _bc.High, ref _af.High); }); - AddStandardInstruction(0xA9, 4, "XOR C", "XOR A with C and Store in A", _ => { Xor8Bit(_af.High, _bc.Low, ref _af.High); }); - AddStandardInstruction(0xAA, 4, "XOR D", "XOR A with D and Store in A", _ => { Xor8Bit(_af.High, _de.High, ref _af.High); }); - AddStandardInstruction(0xAB, 4, "XOR E", "XOR A with E and Store in A", _ => { Xor8Bit(_af.High, _de.Low, ref _af.High); }); - AddStandardInstruction(0xAC, 4, "XOR H", "XOR A with H and Store in A", _ => { Xor8Bit(_af.High, _hl.High, ref _af.High); }); - AddStandardInstruction(0xAD, 4, "XOR L", "XOR A with L and Store in A", _ => { Xor8Bit(_af.High, _hl.Low, ref _af.High); }); - AddStandardInstruction(0xEE, 7, "XOR N", "XOr A with N and Store in A", _ => { Xor8Bit(_af.High, _pc.GetNextDataByte(), ref _af.High); }); - - AddStandardInstruction(0xAE, 7, "XOR (HL)", "XOR A with data in memory location in (HL) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_hl, 0, _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xAE, 19, "XOR (IX+d)", "XOR A with data in memory location in (IX+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_ix, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xAE, 19, "XOR (IY+d)", "XOR A with data in memory location in (IY+d) and Store in A", _ => { Xor8BitToMemoryLocationFrom16BitRegister(_iy, _pc.GetNextDataByte(), _af.High, ref _af.High); }); - - AddStandardInstruction(0x3C, 4, "INC A", "Increment A", _ => { Increment8Bit(ref _af.High); }); - AddStandardInstruction(0x04, 4, "INC B", "Increment B", _ => { Increment8Bit(ref _bc.High); }); - AddStandardInstruction(0x0C, 4, "INC C", "Increment C", _ => { Increment8Bit(ref _bc.Low); }); - AddStandardInstruction(0x14, 4, "INC D", "Increment D", _ => { Increment8Bit(ref _de.High); }); - AddStandardInstruction(0x1C, 4, "INC E", "Increment E", _ => { Increment8Bit(ref _de.Low); }); - AddStandardInstruction(0x24, 4, "INC H", "Increment H", _ => { Increment8Bit(ref _hl.High); }); - AddStandardInstruction(0x2C, 4, "INC L", "Increment L", _ => { Increment8Bit(ref _hl.Low); }); - - AddStandardInstruction(0x3, 6, "INC BC", "Increment BC", _ => { Increment16Bit(ref _bc); }); - AddStandardInstruction(0x13, 6, "INC DE", "Increment DE", _ => { Increment16Bit(ref _de); }); - AddStandardInstruction(0x23, 6, "INC HL", "Increment HL", _ => { Increment16Bit(ref _hl); }); + AddStandardInstruction(0xA7, 4, "AND A", "AND A with A and Store in A", _ => { _accumulator.And(_accumulator.Value, _accumulator.Value); }); + AddStandardInstruction(0xA0, 4, "AND B", "AND A with B and Store in A", _ => { _accumulator.And(_accumulator.Value, _b.Value); }); + AddStandardInstruction(0xA1, 4, "AND C", "AND A with C and Store in A", _ => { _accumulator.And(_accumulator.Value, _c.Value); }); + AddStandardInstruction(0xA2, 4, "AND D", "AND A with D and Store in A", _ => { _accumulator.And(_accumulator.Value, _d.Value); }); + AddStandardInstruction(0xA3, 4, "AND E", "AND A with E and Store in A", _ => { _accumulator.And(_accumulator.Value, _e.Value); }); + AddStandardInstruction(0xA4, 4, "AND H", "AND A with H and Store in A", _ => { _accumulator.And(_accumulator.Value, _h.Value); }); + AddStandardInstruction(0xA5, 4, "AND L", "AND A with L and Store in A", _ => { _accumulator.And(_accumulator.Value, _l.Value); }); + + AddStandardInstruction(0xE6, 7, "AND N", "AND A with N and Store in A", _ => { _accumulator.And(_accumulator.Value, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0xA6, 7, "AND (HL)", "AND A with data in memory location in (HL) and Store in A", _ => { _accumulator.AndFromMemory(_hl, 0, _accumulator.Value); }); + AddDoubleByteInstruction(0xDD, 0xA6, 19, "AND (IX+d)", "AND A with data in memory location in (IX+d) and Store in A", _ => { _accumulator.AndFromMemory(_ix, _pc.GetNextDataByte(), _accumulator.Value); }); + AddDoubleByteInstruction(0xFD, 0xA6, 19, "AND (IY+d)", "AND A with data in memory location in (IX+y) and Store in A", _ => { _accumulator.AndFromMemory(_iy, _pc.GetNextDataByte(), _accumulator.Value); }); + + AddStandardInstruction(0xB7, 4, "OR A", "OR A with A and Store in A", _ => { _accumulator.Or(_accumulator.Value, _accumulator.Value); }); + AddStandardInstruction(0xB0, 4, "OR B", "OR A with B and Store in A", _ => { _accumulator.Or(_accumulator.Value, _b.Value); }); + AddStandardInstruction(0xB1, 4, "OR C", "OR A with C and Store in A", _ => { _accumulator.Or(_accumulator.Value, _c.Value); }); + AddStandardInstruction(0xB2, 4, "OR D", "OR A with D and Store in A", _ => { _accumulator.Or(_accumulator.Value, _d.Value); }); + AddStandardInstruction(0xB3, 4, "OR E", "OR A with E and Store in A", _ => { _accumulator.Or(_accumulator.Value, _e.Value); }); + AddStandardInstruction(0xB4, 4, "OR H", "OR A with H and Store in A", _ => { _accumulator.Or(_accumulator.Value, _h.Value); }); + AddStandardInstruction(0xB5, 4, "OR L", "OR A with L and Store in A", _ => { _accumulator.Or(_accumulator.Value, _l.Value); }); + AddStandardInstruction(0xF6, 7, "OR N", "Or A with N and Store in A", _ => { _accumulator.Or(_accumulator.Value, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0xB6, 7, "OR (HL)", "OR A with data in memory location in (HL) and Store in A", _ => { _accumulator.OrFromMemory(_hl, 0, _accumulator.Value); }); + AddDoubleByteInstruction(0xDD, 0xB6, 19, "OR (IX+d)", "OR A with data in memory location in (IX+d) and Store in A", _ => { _accumulator.OrFromMemory(_ix, _pc.GetNextDataByte(), _accumulator.Value); }); + AddDoubleByteInstruction(0xFD, 0xB6, 19, "OR (IY+d)", "OR A with data in memory location in (IY+d) and Store in A", _ => { _accumulator.OrFromMemory(_iy, _pc.GetNextDataByte(), _accumulator.Value); }); + + AddStandardInstruction(0xAF, 4, "XOR A", "XOR A with A and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _accumulator.Value); }); + AddStandardInstruction(0xA8, 4, "XOR B", "XOR A with B and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _b.Value); }); + AddStandardInstruction(0xA9, 4, "XOR C", "XOR A with C and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _c.Value); }); + AddStandardInstruction(0xAA, 4, "XOR D", "XOR A with D and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _d.Value); }); + AddStandardInstruction(0xAB, 4, "XOR E", "XOR A with E and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _e.Value); }); + AddStandardInstruction(0xAC, 4, "XOR H", "XOR A with H and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _h.Value); }); + AddStandardInstruction(0xAD, 4, "XOR L", "XOR A with L and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _l.Value); }); + AddStandardInstruction(0xEE, 7, "XOR N", "XOr A with N and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0xAE, 7, "XOR (HL)", "XOR A with data in memory location in (HL) and Store in A", _ => { _accumulator.XorFromMemory(_hl, 0, _accumulator.Value); }); + AddDoubleByteInstruction(0xDD, 0xAE, 19, "XOR (IX+d)", "XOR A with data in memory location in (IX+d) and Store in A", _ => { _accumulator.XorFromMemory(_ix, _pc.GetNextDataByte(), _accumulator.Value); }); + AddDoubleByteInstruction(0xFD, 0xAE, 19, "XOR (IY+d)", "XOR A with data in memory location in (IY+d) and Store in A", _ => { _accumulator.XorFromMemory(_iy, _pc.GetNextDataByte(), _accumulator.Value); }); + + AddStandardInstruction(0x3C, 4, "INC A", "Increment A", _ => { _accumulator.Increment(); }); + AddStandardInstruction(0x04, 4, "INC B", "Increment B", _ => { _b.Increment(); }); + AddStandardInstruction(0x0C, 4, "INC C", "Increment C", _ => { _c.Increment(); }); + AddStandardInstruction(0x14, 4, "INC D", "Increment D", _ => { _d.Increment(); }); + AddStandardInstruction(0x1C, 4, "INC E", "Increment E", _ => { _e.Increment(); }); + AddStandardInstruction(0x24, 4, "INC H", "Increment H", _ => { _h.Increment(); }); + AddStandardInstruction(0x2C, 4, "INC L", "Increment L", _ => { _l.Increment(); }); + + AddStandardInstruction(0x03, 6, "INC BC", "Increment BC", _ => { _bc.Increment(); }); + AddStandardInstruction(0x13, 6, "INC DE", "Increment DE", _ => { _de.Increment(); }); + AddStandardInstruction(0x23, 6, "INC HL", "Increment HL", _ => { _hl.Increment(); }); AddStandardInstruction(0x33, 6, "INC SP", "Increment SP", _ => { _stack.IncrementStackPointer(); }); - AddDoubleByteInstruction(0xDD, 0x23, 10, "INC IX", "Increment", _ => { Increment16Bit(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0x23, 10, "INC IY", "Increment", _ => { Increment16Bit(ref _iy); }); - - AddStandardInstruction(0x34, 11, "INC (HL)", "Increment (indirect)", _ => { IncrementMemory(_hl, 0); }); - AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { IncrementMemory(_ix, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementMemory(_iy, _pc.GetNextDataByte()); }); - - AddStandardInstruction(0x3D, 4, "DEC A", "Decrement A", _ => { Decrement8Bit(ref _af.High); }); - AddStandardInstruction(0x05, 4, "DEC B", "Decrement B", _ => { Decrement8Bit(ref _bc.High); }); - AddStandardInstruction(0x0D, 4, "DEC C", "Decrement C", _ => { Decrement8Bit(ref _bc.Low); }); - AddStandardInstruction(0x15, 4, "DEC D", "Decrement D", _ => { Decrement8Bit(ref _de.High); }); - AddStandardInstruction(0x1D, 4, "DEC E", "Decrement E", _ => { Decrement8Bit(ref _de.Low); }); - AddStandardInstruction(0x25, 4, "DEC H", "Decrement H", _ => { Decrement8Bit(ref _hl.High); }); - AddStandardInstruction(0x2D, 4, "DEC L", "Decrement L", _ => { Decrement8Bit(ref _hl.Low); }); - - AddStandardInstruction(0x0B, 6, "DEC BC", "Decrement BC", _ => { Decrement16Bit(ref _bc); }); - AddStandardInstruction(0x1B, 6, "DEC DE", "Decrement DE", _ => { Decrement16Bit(ref _de); }); - AddStandardInstruction(0x2B, 6, "DEC HL", "Decrement HL", _ => { Decrement16Bit(ref _hl); }); + AddDoubleByteInstruction(0xDD, 0x23, 10, "INC IX", "Increment", _ => { _ix.Increment(); }); + AddDoubleByteInstruction(0xFD, 0x23, 10, "INC IY", "Increment", _ => { _iy.Increment(); }); + + AddStandardInstruction(0x34, 11, "INC (HL)", "Increment (indirect)", _ => { _memoryManagement.IncrementMemory(_hl); }); + AddDoubleByteInstruction(0xDD, 0x34, 23, "INC (IX+d)", "Increment", _ => { _memoryManagement.IncrementMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { _memoryManagement.IncrementMemory(_iy, _pc.GetNextDataByte()); }); + + AddStandardInstruction(0x3D, 4, "DEC A", "Decrement A", _ => { _accumulator.Decrement(); }); + AddStandardInstruction(0x05, 4, "DEC B", "Decrement B", _ => { _b.Decrement(); }); + AddStandardInstruction(0x0D, 4, "DEC C", "Decrement C", _ => { _c.Decrement(); }); + AddStandardInstruction(0x15, 4, "DEC D", "Decrement D", _ => { _d.Decrement(); }); + AddStandardInstruction(0x1D, 4, "DEC E", "Decrement E", _ => { _e.Decrement(); }); + AddStandardInstruction(0x25, 4, "DEC H", "Decrement H", _ => { _h.Decrement(); }); + AddStandardInstruction(0x2D, 4, "DEC L", "Decrement L", _ => { _l.Decrement(); }); + + AddStandardInstruction(0x0B, 6, "DEC BC", "Decrement BC", _ => { _bc.Decrement(); }); + AddStandardInstruction(0x1B, 6, "DEC DE", "Decrement DE", _ => { _de.Decrement(); }); + AddStandardInstruction(0x2B, 6, "DEC HL", "Decrement HL", _ => { _hl.Decrement(); }); AddStandardInstruction(0x3B, 6, "DEC SP", "Decrement SP", _ => { _stack.DecrementStackPointer(); }); - AddDoubleByteInstruction(0xDD, 0x2B, 10, "DEC IX", "Decrement IX", _ => { Decrement16Bit(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { Decrement16Bit(ref _iy); }); + AddDoubleByteInstruction(0xDD, 0x2B, 10, "DEC IX", "Decrement IX", _ => { _ix.Decrement(); }); + AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { _iy.Decrement(); }); - AddStandardInstruction(0x35, 11, "DEC (HL)", "", _ => { DecrementMemory(_hl); }); - AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement", _ => { DecrementMemory(_ix, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementMemory(_iy, _pc.GetNextDataByte()); }); + AddStandardInstruction(0x35, 11, "DEC (HL)", "Decrement data in memory location at (HL)", _ => { _memoryManagement.DecrementMemory(_hl); }); + AddDoubleByteInstruction(0xDD, 0x35, 23, "DEC (IX+d)", "Decrement data in memory location at (IX+d)", _ => { _memoryManagement.DecrementMemory(_ix, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement data in memory location at (IY+d)", _ => { _memoryManagement.DecrementMemory(_iy, _pc.GetNextDataByte()); }); AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => { @@ -425,215 +424,215 @@ private void PopulateArthmeticAndLogicalInstructions() _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, _flags.IsFlagSet(Z80StatusFlags.CarryC)); _flags.InvertFlag(Z80StatusFlags.CarryC); }); - AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { DecimalAdjustAccumulator(); }); - AddStandardInstruction(0X2F, 4, "CPL", "Complement", _ => { InvertAccumulatorRegister(); }); - AddDoubleByteInstruction(0xED, 0x44, 8, "NEG", "Negate", _ => { NegateAccumulatorRegister(); }); + AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { _accumulator.DecimalAdjustAccumulator(); }); + AddStandardInstruction(0X2F, 4, "CPL", "Complement", _ => { _accumulator.InvertAccumulatorRegister(); }); + AddDoubleByteInstruction(0xED, 0x44, 8, "NEG", "Negate", _ => { _accumulator.NegateAccumulatorRegister(); }); // Undocumented instructions - AddDoubleByteInstruction(0xDD, 0x84, 4, "ADD A, IXH", "Add IXH to A", _ => { AddValueTo8BitRegister(_ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x85, 4, "ADD A, IXL", "Add IXL to A", _ => { AddValueTo8BitRegister(_ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x84, 4, "ADD A, IYH", "Add IYH to A", _ => { AddValueTo8BitRegister(_iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x85, 4, "ADD A, IYL", "Add IYL to A", _ => { AddValueTo8BitRegister(_iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x8C, 4, "ADC A, IXH", "Add IXH to A with Carry", _ => { AddValueTo8BitRegister(_ix.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x8D, 4, "ADC A, IXL", "Add IXL to A with Carry", _ => { AddValueTo8BitRegister(_ix.Low, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8C, 4, "ADC A, IYH", "Add IYH to A with Carry", _ => { AddValueTo8BitRegister(_iy.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x8D, 4, "ADC A, IYL", "Add IYL to A with Carry", _ => { AddValueTo8BitRegister(_iy.Low, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x94, 4, "SUB A, IXH", "Subtract IXH from A", _ => { SubtractValueFrom8BitRegister(_ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x95, 4, "SUB A, IXH", "Subtract IXL from A", _ => { SubtractValueFrom8BitRegister(_ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x94, 4, "SUB A, IYH", "Subtract IYH from A", _ => { SubtractValueFrom8BitRegister(_iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x95, 4, "SUB A, IYH", "Subtract IYL from A", _ => { SubtractValueFrom8BitRegister(_iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x9C, 4, "SBC A, IXH", "Subtract IXH from A with Carry", _ => { SubtractValueFrom8BitRegister(_ix.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0x9D, 4, "SBC A, IXL", "Subtract IXL from A with Carry", _ => { SubtractValueFrom8BitRegister(_ix.Low, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x9C, 4, "SBC A, IYH", "Subtract IYH from A with Carry", _ => { SubtractValueFrom8BitRegister(_iy.High, ref _af.High, true); }); - AddDoubleByteInstruction(0xFD, 0x9D, 4, "SBC A, IYL", "Subtract IYL from A with Carry", _ => { SubtractValueFrom8BitRegister(_iy.Low, ref _af.High, true); }); - AddDoubleByteInstruction(0xDD, 0xA4, 4, "AND IXH", "AND A with IXH and Store in A", _ => { And8Bit(_af.High, _ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xA5, 4, "AND IXL", "AND A with IXL and Store in A", _ => { And8Bit(_af.High, _ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xA4, 4, "AND IYH", "AND A with IYH and Store in A", _ => { And8Bit(_af.High, _iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xA5, 4, "AND IYL", "AND A with IYL and Store in A", _ => { And8Bit(_af.High, _iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xAC, 4, "XOR IXH", "XOR A with IXH and Store in A", _ => { Xor8Bit(_af.High, _ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xAD, 4, "XOR IXL", "XOR A with IXL and Store in A", _ => { Xor8Bit(_af.High, _ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xAC, 4, "XOR IYH", "XOR A with IYH and Store in A", _ => { Xor8Bit(_af.High, _iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xAD, 4, "XOR IYL", "XOR A with IYL and Store in A", _ => { Xor8Bit(_af.High, _iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xB4, 4, "OR IXH", "OR A with IXH and Store in A", _ => { Or8Bit(_af.High, _ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xB5, 4, "OR IXL", "OR A with IXL and Store in A", _ => { Or8Bit(_af.High, _ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xB4, 4, "OR IYH", "OR A with IYH and Store in A", _ => { Or8Bit(_af.High, _iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0xB5, 4, "OR IYL", "OR A with IYL and Store in A", _ => { Or8Bit(_af.High, _iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0xBC, 4, "CP IXH", "Compare IXH to A", _ => { Compare8Bit(_ix.High, _af.High); }); - AddDoubleByteInstruction(0xDD, 0xBD, 4, "CP IXL", "Compare IXL to A", _ => { Compare8Bit(_ix.Low, _af.High); }); - AddDoubleByteInstruction(0xFD, 0xBC, 4, "CP IYH", "Compare IYH to A", _ => { Compare8Bit(_iy.High, _af.High); }); - AddDoubleByteInstruction(0xFD, 0xBD, 4, "CP IYL", "Compare IYL to A", _ => { Compare8Bit(_iy.Low, _af.High); }); - - AddDoubleByteInstruction(0xDD, 0x24, 4, "INC IXH", "Increment IHX", _ => { Increment8Bit(ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x2C, 4, "INC IXL", "Increment IHL", _ => { Increment8Bit(ref _ix.Low); }); - AddDoubleByteInstruction(0xFD, 0x24, 4, "INC IYH", "Increment IYX", _ => { Increment8Bit(ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x2C, 4, "INC IYL", "Increment IYL", _ => { Increment8Bit(ref _iy.Low); }); - - AddDoubleByteInstruction(0xDD, 0x25, 4, "DEC IXH", "Decrement IHX", _ => { Decrement8Bit(ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x2D, 4, "DEC IXL", "Decrement IHL", _ => { Decrement8Bit(ref _ix.Low); }); - AddDoubleByteInstruction(0xFD, 0x25, 4, "DEC IYH", "Decrement IYX", _ => { Decrement8Bit(ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x2D, 4, "DEC IYL", "Decrement IYL", _ => { Decrement8Bit(ref _iy.Low); }); + AddDoubleByteInstruction(0xDD, 0x84, 4, "ADD A, IXH", "Add IXH to A", _ => { _accumulator.Add(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x85, 4, "ADD A, IXL", "Add IXL to A", _ => { _accumulator.Add(_ix.Low); }); + AddDoubleByteInstruction(0xFD, 0x84, 4, "ADD A, IYH", "Add IYH to A", _ => { _accumulator.Add(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x85, 4, "ADD A, IYL", "Add IYL to A", _ => { _accumulator.Add(_iy.Low); }); + AddDoubleByteInstruction(0xDD, 0x8C, 4, "ADC A, IXH", "Add IXH to A with Carry", _ => { _accumulator.Add(_ix.High, true); }); + AddDoubleByteInstruction(0xDD, 0x8D, 4, "ADC A, IXL", "Add IXL to A with Carry", _ => { _accumulator.Add(_ix.Low, true); }); + AddDoubleByteInstruction(0xFD, 0x8C, 4, "ADC A, IYH", "Add IYH to A with Carry", _ => { _accumulator.Add(_iy.High, true); }); + AddDoubleByteInstruction(0xFD, 0x8D, 4, "ADC A, IYL", "Add IYL to A with Carry", _ => { _accumulator.Add(_iy.Low, true); }); + AddDoubleByteInstruction(0xDD, 0x94, 4, "SUB A, IXH", "Subtract IXH from A", _ => { _accumulator.Subtract(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x95, 4, "SUB A, IXH", "Subtract IXL from A", _ => { _accumulator.Subtract(_ix.Low); }); + AddDoubleByteInstruction(0xFD, 0x94, 4, "SUB A, IYH", "Subtract IYH from A", _ => { _accumulator.Subtract(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x95, 4, "SUB A, IYH", "Subtract IYL from A", _ => { _accumulator.Subtract(_iy.Low); }); + AddDoubleByteInstruction(0xDD, 0x9C, 4, "SBC A, IXH", "Subtract IXH from A with Carry", _ => { _accumulator.Subtract(_ix.High, true); }); + AddDoubleByteInstruction(0xDD, 0x9D, 4, "SBC A, IXL", "Subtract IXL from A with Carry", _ => { _accumulator.Subtract(_ix.Low, true); }); + AddDoubleByteInstruction(0xFD, 0x9C, 4, "SBC A, IYH", "Subtract IYH from A with Carry", _ => { _accumulator.Subtract(_iy.High, true); }); + AddDoubleByteInstruction(0xFD, 0x9D, 4, "SBC A, IYL", "Subtract IYL from A with Carry", _ => { _accumulator.Subtract(_iy.Low, true); }); + AddDoubleByteInstruction(0xDD, 0xA4, 4, "AND IXH", "AND A with IXH and Store in A", _ => { _accumulator.And(_accumulator.Value, _ix.High); }); + AddDoubleByteInstruction(0xDD, 0xA5, 4, "AND IXL", "AND A with IXL and Store in A", _ => { _accumulator.And(_accumulator.Value, _ix.Low); }); + AddDoubleByteInstruction(0xFD, 0xA4, 4, "AND IYH", "AND A with IYH and Store in A", _ => { _accumulator.And(_accumulator.Value, _iy.High); }); + AddDoubleByteInstruction(0xFD, 0xA5, 4, "AND IYL", "AND A with IYL and Store in A", _ => { _accumulator.And(_accumulator.Value, _iy.Low); }); + AddDoubleByteInstruction(0xDD, 0xAC, 4, "XOR IXH", "XOR A with IXH and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _ix.High); }); + AddDoubleByteInstruction(0xDD, 0xAD, 4, "XOR IXL", "XOR A with IXL and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _ix.Low); }); + AddDoubleByteInstruction(0xFD, 0xAC, 4, "XOR IYH", "XOR A with IYH and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _iy.High); }); + AddDoubleByteInstruction(0xFD, 0xAD, 4, "XOR IYL", "XOR A with IYL and Store in A", _ => { _accumulator.Xor(_accumulator.Value, _iy.Low); }); + AddDoubleByteInstruction(0xDD, 0xB4, 4, "OR IXH", "OR A with IXH and Store in A", _ => { _accumulator.Or(_accumulator.Value, _ix.High); }); + AddDoubleByteInstruction(0xDD, 0xB5, 4, "OR IXL", "OR A with IXL and Store in A", _ => { _accumulator.Or(_accumulator.Value, _ix.Low); }); + AddDoubleByteInstruction(0xFD, 0xB4, 4, "OR IYH", "OR A with IYH and Store in A", _ => { _accumulator.Or(_accumulator.Value, _iy.High); }); + AddDoubleByteInstruction(0xFD, 0xB5, 4, "OR IYL", "OR A with IYL and Store in A", _ => { _accumulator.Or(_accumulator.Value, _iy.Low); }); + AddDoubleByteInstruction(0xDD, 0xBC, 4, "CP IXH", "Compare IXH to A", _ => { _accumulator.Compare(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0xBD, 4, "CP IXL", "Compare IXL to A", _ => { _accumulator.Compare(_ix.Low); }); + AddDoubleByteInstruction(0xFD, 0xBC, 4, "CP IYH", "Compare IYH to A", _ => { _accumulator.Compare(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0xBD, 4, "CP IYL", "Compare IYL to A", _ => { _accumulator.Compare(_iy.Low); }); + + AddDoubleByteInstruction(0xDD, 0x24, 4, "INC IXH", "Increment IHX", _ => { _ix.IncrementHigh(); }); + AddDoubleByteInstruction(0xDD, 0x2C, 4, "INC IXL", "Increment IHL", _ => { _ix.IncrementLow(); }); + AddDoubleByteInstruction(0xFD, 0x24, 4, "INC IYH", "Increment IYX", _ => { _iy.IncrementHigh(); }); + AddDoubleByteInstruction(0xFD, 0x2C, 4, "INC IYL", "Increment IYL", _ => { _iy.IncrementLow(); }); + + AddDoubleByteInstruction(0xDD, 0x25, 4, "DEC IXH", "Decrement IHX", _ => { _ix.DecrementHigh(); }); + AddDoubleByteInstruction(0xDD, 0x2D, 4, "DEC IXL", "Decrement IHL", _ => { _ix.DecrementLow(); }); + AddDoubleByteInstruction(0xFD, 0x25, 4, "DEC IYH", "Decrement IYX", _ => { _iy.DecrementHigh(); }); + AddDoubleByteInstruction(0xFD, 0x2D, 4, "DEC IYL", "Decrement IYL", _ => { _iy.DecrementLow(); }); } private void PopulateBitSetResetAndTestGroupInstructions() { AddDoubleByteInstructionWithMask(0xCB, 0x80, 0x3F, 8, "RES b,r", "Reset bit in register", i => { ResetBitByOpCode(i.OpCode); }); - AddSpecialCbInstruction(0xDD, 0x86, 23, "RES 0,(IX+d)", "Reset bit 0 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x8E, 23, "RES 1,(IX+d)", "Reset bit 1 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x96, 23, "RES 2,(IX+d)", "Reset bit 2 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x9E, 23, "RES 3,(IX+d)", "Reset bit 3 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xA6, 23, "RES 4,(IX+d)", "Reset bit 4 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xAE, 23, "RES 5,(IX+d)", "Reset bit 5 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xB6, 23, "RES 6,(IX+d)", "Reset bit 6 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xBE, 23, "RES 7,(IX+d)", "Reset bit 7 in (IX+d)", i => { ResetBitByRegisterLocation(_ix, 7, ((SpecialCbInstruction)i).DataByte); }); - - AddSpecialCbInstruction(0xFD, 0x86, 23, "RES 0,(IY+d)", "Reset bit 0 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x8E, 23, "RES 1,(IY+d)", "Reset bit 1 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x96, 23, "RES 2,(IY+d)", "Reset bit 2 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x9E, 23, "RES 3,(IY+d)", "Reset bit 3 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xA6, 23, "RES 4,(IY+d)", "Reset bit 4 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xAE, 23, "RES 5,(IY+d)", "Reset bit 5 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xB6, 23, "RES 6,(IY+d)", "Reset bit 6 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xBE, 23, "RES 7,(IY+d)", "Reset bit 7 in (IY+d)", i => { ResetBitByRegisterLocation(_iy, 7, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x86, 23, "RES 0,(IX+d)", "Reset bit 0 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x8E, 23, "RES 1,(IX+d)", "Reset bit 1 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x96, 23, "RES 2,(IX+d)", "Reset bit 2 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x9E, 23, "RES 3,(IX+d)", "Reset bit 3 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xA6, 23, "RES 4,(IX+d)", "Reset bit 4 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xAE, 23, "RES 5,(IX+d)", "Reset bit 5 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xB6, 23, "RES 6,(IX+d)", "Reset bit 6 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xBE, 23, "RES 7,(IX+d)", "Reset bit 7 in (IX+d)", i => { _ix.ResetBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); + + AddSpecialCbInstruction(0xFD, 0x86, 23, "RES 0,(IY+d)", "Reset bit 0 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x8E, 23, "RES 1,(IY+d)", "Reset bit 1 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x96, 23, "RES 2,(IY+d)", "Reset bit 2 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x9E, 23, "RES 3,(IY+d)", "Reset bit 3 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xA6, 23, "RES 4,(IY+d)", "Reset bit 4 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xAE, 23, "RES 5,(IY+d)", "Reset bit 5 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xB6, 23, "RES 6,(IY+d)", "Reset bit 6 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xBE, 23, "RES 7,(IY+d)", "Reset bit 7 in (IY+d)", i => { _iy.ResetBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); AddDoubleByteInstructionWithMask(0xCB, 0x40, 0x3F, 8, "BIT b,r", "Test Bit", i => { TestBitByOpCode(i.OpCode); }); - AddSpecialCbInstruction(0xDD, 0x46, 23, "BIT 0,(IX+d)", "Test bit 0 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x4E, 23, "BIT 1,(IX+d)", "Test bit 1 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x56, 23, "BIT 2,(IX+d)", "Test bit 2 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x5E, 23, "BIT 3,(IX+d)", "Test bit 3 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x66, 23, "BIT 4,(IX+d)", "Test bit 4 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x6E, 23, "BIT 5,(IX+d)", "Test bit 5 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x76, 23, "BIT 6,(IX+d)", "Test bit 6 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0x7E, 23, "BIT 7,(IX+d)", "Test bit 7 in (IX+d)", i => { TestBitByRegisterLocation(_ix, 7, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x46, 23, "BIT 0,(IX+d)", "Test bit 0 in (IX+d)", i => { _ix.TestBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x4E, 23, "BIT 1,(IX+d)", "Test bit 1 in (IX+d)", i => { _ix.TestBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x56, 23, "BIT 2,(IX+d)", "Test bit 2 in (IX+d)", i => { _ix.TestBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x5E, 23, "BIT 3,(IX+d)", "Test bit 3 in (IX+d)", i => { _ix.TestBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x66, 23, "BIT 4,(IX+d)", "Test bit 4 in (IX+d)", i => { _ix.TestBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x6E, 23, "BIT 5,(IX+d)", "Test bit 5 in (IX+d)", i => { _ix.TestBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x76, 23, "BIT 6,(IX+d)", "Test bit 6 in (IX+d)", i => { _ix.TestBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0x7E, 23, "BIT 7,(IX+d)", "Test bit 7 in (IX+d)", i => { _ix.TestBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x46, 23, "BIT 0,(IY+d)", "Test bit 0 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x4E, 23, "BIT 1,(IY+d)", "Test bit 1 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x56, 23, "BIT 2,(IY+d)", "Test bit 2 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x5E, 23, "BIT 3,(IY+d)", "Test bit 3 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x66, 23, "BIT 4,(IY+d)", "Test bit 4 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x6E, 23, "BIT 5,(IY+d)", "Test bit 5 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x76, 23, "BIT 6,(IY+d)", "Test bit 6 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x7E, 23, "BIT 7,(IY+d)", "Test bit 7 in (IY+d)", i => { TestBitByRegisterLocation(_iy, 7, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x46, 23, "BIT 0,(IY+d)", "Test bit 0 in (IY+d)", i => { _iy.TestBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x4E, 23, "BIT 1,(IY+d)", "Test bit 1 in (IY+d)", i => { _iy.TestBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x56, 23, "BIT 2,(IY+d)", "Test bit 2 in (IY+d)", i => { _iy.TestBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x5E, 23, "BIT 3,(IY+d)", "Test bit 3 in (IY+d)", i => { _iy.TestBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x66, 23, "BIT 4,(IY+d)", "Test bit 4 in (IY+d)", i => { _iy.TestBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x6E, 23, "BIT 5,(IY+d)", "Test bit 5 in (IY+d)", i => { _iy.TestBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x76, 23, "BIT 6,(IY+d)", "Test bit 6 in (IY+d)", i => { _iy.TestBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x7E, 23, "BIT 7,(IY+d)", "Test bit 7 in (IY+d)", i => { _iy.TestBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); AddDoubleByteInstructionWithMask(0xCB, 0xC0, 0x3F, 8, "SET b,r", "Set bit b in Register r", i => { SetBitByOpCode(i.OpCode); }); - AddSpecialCbInstruction(0xDD, 0xC6, 23, "SET 0,(IX+d)", "Set bit 0 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xCE, 23, "SET 1,(IX+d)", "Set bit 1 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xD6, 23, "SET 2,(IX+d)", "Set bit 2 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xDE, 23, "SET 3,(IX+d)", "Set bit 3 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xE6, 23, "SET 4,(IX+d)", "Set bit 4 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xEE, 23, "SET 5,(IX+d)", "Set bit 5 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xF6, 23, "SET 6,(IX+d)", "Set bit 6 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xDD, 0xFE, 23, "SET 7,(IX+d)", "Set bit 7 in (IX+d)", i => { SetBitByRegisterLocation(_ix, 7, ((SpecialCbInstruction)i).DataByte); }); - - AddSpecialCbInstruction(0xFD, 0xC6, 23, "SET 0,(IY+d)", "Set bit 0 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 0, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xCE, 23, "SET 1,(IY+d)", "Set bit 1 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 1, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xD6, 23, "SET 2,(IY+d)", "Set bit 2 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 2, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xDE, 23, "SET 3,(IY+d)", "Set bit 3 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 3, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xE6, 23, "SET 4,(IY+d)", "Set bit 4 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 4, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xEE, 23, "SET 5,(IY+d)", "Set bit 5 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 5, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xF6, 23, "SET 6,(IY+d)", "Set bit 6 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 6, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0xFE, 23, "SET 7,(IY+d)", "Set bit 7 in (IY+d)", i => { SetBitByRegisterLocation(_iy, 7, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xC6, 23, "SET 0,(IX+d)", "Set bit 0 in (IX+d)", i => { _ix.SetBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xCE, 23, "SET 1,(IX+d)", "Set bit 1 in (IX+d)", i => { _ix.SetBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xD6, 23, "SET 2,(IX+d)", "Set bit 2 in (IX+d)", i => { _ix.SetBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xDE, 23, "SET 3,(IX+d)", "Set bit 3 in (IX+d)", i => { _ix.SetBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xE6, 23, "SET 4,(IX+d)", "Set bit 4 in (IX+d)", i => { _ix.SetBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xEE, 23, "SET 5,(IX+d)", "Set bit 5 in (IX+d)", i => { _ix.SetBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xF6, 23, "SET 6,(IX+d)", "Set bit 6 in (IX+d)", i => { _ix.SetBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xDD, 0xFE, 23, "SET 7,(IX+d)", "Set bit 7 in (IX+d)", i => { _ix.SetBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); + + AddSpecialCbInstruction(0xFD, 0xC6, 23, "SET 0,(IY+d)", "Set bit 0 in (IY+d)", i => { _iy.SetBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xCE, 23, "SET 1,(IY+d)", "Set bit 1 in (IY+d)", i => { _iy.SetBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xD6, 23, "SET 2,(IY+d)", "Set bit 2 in (IY+d)", i => { _iy.SetBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xDE, 23, "SET 3,(IY+d)", "Set bit 3 in (IY+d)", i => { _iy.SetBitByRegisterLocation(3, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xE6, 23, "SET 4,(IY+d)", "Set bit 4 in (IY+d)", i => { _iy.SetBitByRegisterLocation(4, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xEE, 23, "SET 5,(IY+d)", "Set bit 5 in (IY+d)", i => { _iy.SetBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xF6, 23, "SET 6,(IY+d)", "Set bit 6 in (IY+d)", i => { _iy.SetBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0xFE, 23, "SET 7,(IY+d)", "Set bit 7 in (IY+d)", i => { _iy.SetBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); } private void PopulateRotateAndShiftInstructions() { - AddStandardInstruction(0x17, 4, "RLA", "Rotate Left Accumulator", _ => { RotateLeftAccumulator();}); - AddStandardInstruction(0x07, 4, "RLCA", "Rotate Left Circular Accumulator", _ => { RotateLeftCircularAccumulator(); }); - AddStandardInstruction(0x1F, 4, "RRA", "Rotate Right Accumulator", _ => { RotateRightAccumulator(); }); - AddStandardInstruction(0x0F, 4, "RRCA", "Rotate Right Circular Accumulator", _ => { RotateRightCircularAccumulator(); }); - - AddDoubleByteInstruction(0xCB, 0x17, 8, "RL A", "Rotate Left A 1 bit", _ => { RotateLeft(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x10, 8, "RL B", "Rotate Left B 1 bit", _ => { RotateLeft(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x11, 8, "RL C", "Rotate Left C 1 bit", _ => { RotateLeft(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x12, 8, "RL D", "Rotate Left D 1 bit", _ => { RotateLeft(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x13, 8, "RL E", "Rotate Left E 1 bit", _ => { RotateLeft(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x14, 8, "RL H", "Rotate Left H 1 bit", _ => { RotateLeft(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x15, 8, "RL L", "Rotate Left L 1 bit", _ => { RotateLeft(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x16, 8, "RL (HL)", "Rotate Left (HL) 1 bit", _ => { RotateLeft16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x16, 23, "RL (IX+d)", "Rotate Left (IX+d) 1 bit", i => { RotateLeft16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x16, 23, "RL (IY+d)", "Rotate Left (IY+d) 1 bit", i => { RotateLeft16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x07, 8, "RLC A", "Rotate Left Circular A 1 bit", _ => { RotateLeftCircular(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x00, 8, "RLC B", "Rotate Left Circular B 1 bit", _ => { RotateLeftCircular(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x01, 8, "RLC C", "Rotate Left Circular C 1 bit", _ => { RotateLeftCircular(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x02, 8, "RLC D", "Rotate Left Circular D 1 bit", _ => { RotateLeftCircular(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x03, 8, "RLC E", "Rotate Left Circular E 1 bit", _ => { RotateLeftCircular(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x04, 8, "RLC H", "Rotate Left Circular H 1 bit", _ => { RotateLeftCircular(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x05, 8, "RLC L", "Rotate Left Circular L 1 bit", _ => { RotateLeftCircular(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x06, 8, "RLC (HL)", "Rotate Left Circular (HL) 1 bit", _ => { RotateLeftCircular16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x06, 23, "RLC (IX+d)", "Rotate Left Circular (IX+d) 1 bit", i => { RotateLeftCircular16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x06, 23, "RLC (IY+d)", "Rotate Left Circular (IY+d) 1 bit", i => { RotateLeftCircular16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x1F, 8, "RR A", "Rotate Right A 1 bit", _ => { RotateRight(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x18, 8, "RR B", "Rotate Right B 1 bit", _ => { RotateRight(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x19, 8, "RR C", "Rotate Right C 1 bit", _ => { RotateRight(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x1A, 8, "RR D", "Rotate Right D 1 bit", _ => { RotateRight(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x1B, 8, "RR E", "Rotate Right E 1 bit", _ => { RotateRight(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x1C, 8, "RR H", "Rotate Right H 1 bit", _ => { RotateRight(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x1D, 8, "RR L", "Rotate Right L 1 bit", _ => { RotateRight(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x1E, 8, "RR (HL)", "Rotate Right (HL) 1 bit", _ => { RotateRight16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x1E, 23, "RR (IX+d)", "Rotate Right (IX+d) 1 bit", i => { RotateRight16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x1E, 23, "RR (IY+d)", "Rotate Right (IY+d) 1 bit", i => { RotateRight16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x0F, 8, "RRC A", "Rotate Right Circular A 1 bit", _ => { RotateRightCircular(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x08, 8, "RRC B", "Rotate Right Circular B 1 bit", _ => { RotateRightCircular(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x09, 8, "RRC C", "Rotate Right Circular C 1 bit", _ => { RotateRightCircular(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x0A, 8, "RRC D", "Rotate Right Circular D 1 bit", _ => { RotateRightCircular(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x0B, 8, "RRC E", "Rotate Right Circular E 1 bit", _ => { RotateRightCircular(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x0C, 8, "RRC H", "Rotate Right Circular H 1 bit", _ => { RotateRightCircular(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x0D, 8, "RRC L", "Rotate Right Circular L 1 bit", _ => { RotateRightCircular(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x0E, 8, "RRC (HL)", "Rotate Right Circular (HL) 1 bit", _ => { RotateRightCircular16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x0E, 23, "RRC (IX+d)", "Rotate Right Circular (IX+d) 1 bit", i => { RotateRightCircular16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x0E, 23, "RRC (IY+d)", "Rotate Right Circular (IY+d) 1 bit", i => { RotateRightCircular16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x27, 8, "SLA A", "Shift Left Arithmetic A 1 bit", _ => { ShiftLeftArithmetic(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x20, 8, "SLA B", "Shift Left Arithmetic B 1 bit", _ => { ShiftLeftArithmetic(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x21, 8, "SLA C", "Shift Left Arithmetic C 1 bit", _ => { ShiftLeftArithmetic(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x22, 8, "SLA D", "Shift Left Arithmetic D 1 bit", _ => { ShiftLeftArithmetic(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x23, 8, "SLA E", "Shift Left Arithmetic E 1 bit", _ => { ShiftLeftArithmetic(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x24, 8, "SLA H", "Shift Left Arithmetic H 1 bit", _ => { ShiftLeftArithmetic(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x25, 8, "SLA L", "Shift Left Arithmetic L 1 bit", _ => { ShiftLeftArithmetic(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x26, 8, "SLA (HL)", "Shift Left Arithmetic (HL) 1 bit", _ => { ShiftLeftArithmetic16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x26, 23, "SLA (IX+d)", "Shift Left Arithmetic (IX+d) 1 bit", i => { ShiftLeftArithmetic16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x26, 23, "SLA (IY+d)", "Shift Left Arithmetic (IY+d) 1 bit", i => { ShiftLeftArithmetic16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x2F, 8, "SRA A", "Shift Right Arithmetic A 1 bit", _ => { ShiftRightArithmetic(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x28, 8, "SRA B", "Shift Right Arithmetic B 1 bit", _ => { ShiftRightArithmetic(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x29, 8, "SRA C", "Shift Right Arithmetic C 1 bit", _ => { ShiftRightArithmetic(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x2A, 8, "SRA D", "Shift Right Arithmetic D 1 bit", _ => { ShiftRightArithmetic(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x2B, 8, "SRA E", "Shift Right Arithmetic E 1 bit", _ => { ShiftRightArithmetic(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x2C, 8, "SRA H", "Shift Right Arithmetic H 1 bit", _ => { ShiftRightArithmetic(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x2D, 8, "SRA L", "Shift Right Arithmetic L 1 bit", _ => { ShiftRightArithmetic(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x2E, 8, "SRA (HL)", "Shift Right Arithmetic (HL) 1 bit", _ => { ShiftRightArithmetic16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x2E, 23, "SRA (IX+d)", "Shift Right Arithmetic (IX+d) 1 bit", i => { ShiftRightArithmetic16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x2E, 23, "SRA (IY+d)", "Shift Right Arithmetic (IY+d) 1 bit", i => { ShiftRightArithmetic16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); + AddStandardInstruction(0x17, 4, "RLA", "Rotate Left Accumulator", _ => { _accumulator.RotateLeftAccumulator();}); + AddStandardInstruction(0x07, 4, "RLCA", "Rotate Left Circular Accumulator", _ => { _accumulator.RotateLeftCircularAccumulator(); }); + AddStandardInstruction(0x1F, 4, "RRA", "Rotate Right Accumulator", _ => { _accumulator.RotateRightAccumulator(); }); + AddStandardInstruction(0x0F, 4, "RRCA", "Rotate Right Circular Accumulator", _ => { _accumulator.RotateRightCircularAccumulator(); }); + + AddDoubleByteInstruction(0xCB, 0x17, 8, "RL A", "Rotate Left A 1 bit", _ => { _accumulator.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x10, 8, "RL B", "Rotate Left B 1 bit", _ => { _b.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x11, 8, "RL C", "Rotate Left C 1 bit", _ => { _c.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x12, 8, "RL D", "Rotate Left D 1 bit", _ => { _d.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x13, 8, "RL E", "Rotate Left E 1 bit", _ => { _e.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x14, 8, "RL H", "Rotate Left H 1 bit", _ => { _h.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x15, 8, "RL L", "Rotate Left L 1 bit", _ => { _l.RotateLeft(); }); + AddDoubleByteInstruction(0xCB, 0x16, 8, "RL (HL)", "Rotate Left (HL) 1 bit", _ => { _hl.RotateLeft(0); }); + AddSpecialCbInstruction(0xDD, 0x16, 23, "RL (IX+d)", "Rotate Left (IX+d) 1 bit", i => { _ix.RotateLeft(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x16, 23, "RL (IY+d)", "Rotate Left (IY+d) 1 bit", i => { _iy.RotateLeft(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x07, 8, "RLC A", "Rotate Left Circular A 1 bit", _ => { _accumulator.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x00, 8, "RLC B", "Rotate Left Circular B 1 bit", _ => { _b.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x01, 8, "RLC C", "Rotate Left Circular C 1 bit", _ => { _c.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x02, 8, "RLC D", "Rotate Left Circular D 1 bit", _ => { _d.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x03, 8, "RLC E", "Rotate Left Circular E 1 bit", _ => { _e.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x04, 8, "RLC H", "Rotate Left Circular H 1 bit", _ => { _h.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x05, 8, "RLC L", "Rotate Left Circular L 1 bit", _ => { _l.RotateLeftCircular(); }); + AddDoubleByteInstruction(0xCB, 0x06, 8, "RLC (HL)", "Rotate Left Circular (HL) 1 bit", _ => { _hl.RotateLeftCircular(0); }); + AddSpecialCbInstruction(0xDD, 0x06, 23, "RLC (IX+d)", "Rotate Left Circular (IX+d) 1 bit", i => { _ix.RotateLeftCircular(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x06, 23, "RLC (IY+d)", "Rotate Left Circular (IY+d) 1 bit", i => { _iy.RotateLeftCircular(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x1F, 8, "RR A", "Rotate Right A 1 bit", _ => { _accumulator.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x18, 8, "RR B", "Rotate Right B 1 bit", _ => { _b.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x19, 8, "RR C", "Rotate Right C 1 bit", _ => { _c.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x1A, 8, "RR D", "Rotate Right D 1 bit", _ => { _d.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x1B, 8, "RR E", "Rotate Right E 1 bit", _ => { _e.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x1C, 8, "RR H", "Rotate Right H 1 bit", _ => { _h.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x1D, 8, "RR L", "Rotate Right L 1 bit", _ => { _l.RotateRight(); }); + AddDoubleByteInstruction(0xCB, 0x1E, 8, "RR (HL)", "Rotate Right (HL) 1 bit", _ => { _hl.RotateRight(0); }); + AddSpecialCbInstruction(0xDD, 0x1E, 23, "RR (IX+d)", "Rotate Right (IX+d) 1 bit", i => { _ix.RotateRight(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x1E, 23, "RR (IY+d)", "Rotate Right (IY+d) 1 bit", i => { _iy.RotateRight(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x0F, 8, "RRC A", "Rotate Right Circular A 1 bit", _ => { _accumulator.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x08, 8, "RRC B", "Rotate Right Circular B 1 bit", _ => { _b.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x09, 8, "RRC C", "Rotate Right Circular C 1 bit", _ => { _c.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x0A, 8, "RRC D", "Rotate Right Circular D 1 bit", _ => { _d.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x0B, 8, "RRC E", "Rotate Right Circular E 1 bit", _ => { _e.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x0C, 8, "RRC H", "Rotate Right Circular H 1 bit", _ => { _h.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x0D, 8, "RRC L", "Rotate Right Circular L 1 bit", _ => { _l.RotateRightCircular(); }); + AddDoubleByteInstruction(0xCB, 0x0E, 8, "RRC (HL)", "Rotate Right Circular (HL) 1 bit", _ => { _hl.RotateRightCircular(0); }); + AddSpecialCbInstruction(0xDD, 0x0E, 23, "RRC (IX+d)", "Rotate Right Circular (IX+d) 1 bit", i => { _ix.RotateRightCircular(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x0E, 23, "RRC (IY+d)", "Rotate Right Circular (IY+d) 1 bit", i => { _iy.RotateRightCircular(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x27, 8, "SLA A", "Shift Left Arithmetic A 1 bit", _ => { _accumulator.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x20, 8, "SLA B", "Shift Left Arithmetic B 1 bit", _ => { _b.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x21, 8, "SLA C", "Shift Left Arithmetic C 1 bit", _ => { _c.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x22, 8, "SLA D", "Shift Left Arithmetic D 1 bit", _ => { _d.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x23, 8, "SLA E", "Shift Left Arithmetic E 1 bit", _ => { _e.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x24, 8, "SLA H", "Shift Left Arithmetic H 1 bit", _ => { _h.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x25, 8, "SLA L", "Shift Left Arithmetic L 1 bit", _ => { _l.ShiftLeftArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x26, 8, "SLA (HL)", "Shift Left Arithmetic (HL) 1 bit", _ => { _hl.ShiftLeftArithmetic(0); }); + AddSpecialCbInstruction(0xDD, 0x26, 23, "SLA (IX+d)", "Shift Left Arithmetic (IX+d) 1 bit", i => { _ix.ShiftLeftArithmetic(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x26, 23, "SLA (IY+d)", "Shift Left Arithmetic (IY+d) 1 bit", i => { _iy.ShiftLeftArithmetic(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x2F, 8, "SRA A", "Shift Right Arithmetic A 1 bit", _ => { _accumulator.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x28, 8, "SRA B", "Shift Right Arithmetic B 1 bit", _ => { _b.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x29, 8, "SRA C", "Shift Right Arithmetic C 1 bit", _ => { _c.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x2A, 8, "SRA D", "Shift Right Arithmetic D 1 bit", _ => { _d.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x2B, 8, "SRA E", "Shift Right Arithmetic E 1 bit", _ => { _e.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x2C, 8, "SRA H", "Shift Right Arithmetic H 1 bit", _ => { _h.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x2D, 8, "SRA L", "Shift Right Arithmetic L 1 bit", _ => { _l.ShiftRightArithmetic(); }); + AddDoubleByteInstruction(0xCB, 0x2E, 8, "SRA (HL)", "Shift Right Arithmetic (HL) 1 bit", _ => { _hl.ShiftRightArithmetic(0); }); + AddSpecialCbInstruction(0xDD, 0x2E, 23, "SRA (IX+d)", "Shift Right Arithmetic (IX+d) 1 bit", i => { _ix.ShiftRightArithmetic(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x2E, 23, "SRA (IY+d)", "Shift Right Arithmetic (IY+d) 1 bit", i => { _iy.ShiftRightArithmetic(((SpecialCbInstruction)i).DataByte); }); // NOTE: SLL is an undocumented instruction - AddDoubleByteInstruction(0xCB, 0x37, 8, "SLL A", "Shift Left LogicalA 1 bit", _ => { ShiftLeftLogical(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x30, 8, "SLL B", "Shift Left LogicalB 1 bit", _ => { ShiftLeftLogical(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x31, 8, "SLL C", "Shift Left LogicalC 1 bit", _ => { ShiftLeftLogical(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x32, 8, "SLL D", "Shift Left LogicalD 1 bit", _ => { ShiftLeftLogical(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x33, 8, "SLL E", "Shift Left LogicalE 1 bit", _ => { ShiftLeftLogical(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x34, 8, "SLL H", "Shift Left LogicalH 1 bit", _ => { ShiftLeftLogical(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x35, 8, "SLL L", "Shift Left LogicalL 1 bit", _ => { ShiftLeftLogical(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x36, 8, "SLL (HL)", "Shift Left Logical (HL) 1 bit", _ => { ShiftLeftLogical16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x36, 23, "SLL (IX+d)", "Shift Left Logical (IX+d) 1 bit", i => { ShiftLeftLogical16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x36, 23, "SLL (IY+d)", "Shift Left Logical (IY+d) 1 bit", i => { ShiftLeftLogical16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xCB, 0x3F, 8, "SRL A", "Shift Right Logical A 1 bit", _ => { ShiftRightLogical(ref _af.High); }); - AddDoubleByteInstruction(0xCB, 0x38, 8, "SRL B", "Shift Right Logical B 1 bit", _ => { ShiftRightLogical(ref _bc.High); }); - AddDoubleByteInstruction(0xCB, 0x39, 8, "SRL C", "Shift Right Logical C 1 bit", _ => { ShiftRightLogical(ref _bc.Low); }); - AddDoubleByteInstruction(0xCB, 0x3A, 8, "SRL D", "Shift Right Logical D 1 bit", _ => { ShiftRightLogical(ref _de.High); }); - AddDoubleByteInstruction(0xCB, 0x3B, 8, "SRL E", "Shift Right Logical E 1 bit", _ => { ShiftRightLogical(ref _de.Low); }); - AddDoubleByteInstruction(0xCB, 0x3C, 8, "SRL H", "Shift Right Logical H 1 bit", _ => { ShiftRightLogical(ref _hl.High); }); - AddDoubleByteInstruction(0xCB, 0x3D, 8, "SRL L", "Shift Right Logical L 1 bit", _ => { ShiftRightLogical(ref _hl.Low); }); - AddDoubleByteInstruction(0xCB, 0x3E, 8, "SRL (HL)", "Shift Right Logical (HL) 1 bit", _ => { ShiftRightLogical16BitRegisterMemoryLocation(_hl, 0); }); - AddSpecialCbInstruction(0xDD, 0x3E, 23, "SRL (IX+d)", "Shift Right Logical (IX+d) 1 bit", i => { ShiftRightLogical16BitRegisterMemoryLocation(_ix, ((SpecialCbInstruction)i).DataByte); }); - AddSpecialCbInstruction(0xFD, 0x3E, 23, "SRL (IY+d)", "Shift Right Logical (IY+d) 1 bit", i => { ShiftRightLogical16BitRegisterMemoryLocation(_iy, ((SpecialCbInstruction)i).DataByte); }); - - AddDoubleByteInstruction(0xED, 0x6F, 18, "RLD", "Rotate Left 4 bits", _ => { RotateLeftDigit(); }); - AddDoubleByteInstruction(0xED, 0x67, 18, "RRD", "Rotate Right 4 bits", _ => { RotateRightDigit(); }); + AddDoubleByteInstruction(0xCB, 0x37, 8, "SLL A", "Shift Left LogicalA 1 bit", _ => { _accumulator.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x30, 8, "SLL B", "Shift Left LogicalB 1 bit", _ => { _b.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x31, 8, "SLL C", "Shift Left LogicalC 1 bit", _ => { _c.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x32, 8, "SLL D", "Shift Left LogicalD 1 bit", _ => { _d.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x33, 8, "SLL E", "Shift Left LogicalE 1 bit", _ => { _e.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x34, 8, "SLL H", "Shift Left LogicalH 1 bit", _ => { _h.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x35, 8, "SLL L", "Shift Left LogicalL 1 bit", _ => { _l.ShiftLeftLogical(); }); + AddDoubleByteInstruction(0xCB, 0x36, 8, "SLL (HL)", "Shift Left Logical (HL) 1 bit", _ => { _hl.ShiftLeftLogical(0); }); + AddSpecialCbInstruction(0xDD, 0x36, 23, "SLL (IX+d)", "Shift Left Logical (IX+d) 1 bit", i => { _ix.ShiftLeftLogical(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x36, 23, "SLL (IY+d)", "Shift Left Logical (IY+d) 1 bit", i => { _iy.ShiftLeftLogical(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xCB, 0x3F, 8, "SRL A", "Shift Right Logical A 1 bit", _ => { _accumulator.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x38, 8, "SRL B", "Shift Right Logical B 1 bit", _ => { _b.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x39, 8, "SRL C", "Shift Right Logical C 1 bit", _ => { _c.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x3A, 8, "SRL D", "Shift Right Logical D 1 bit", _ => { _d.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x3B, 8, "SRL E", "Shift Right Logical E 1 bit", _ => { _e.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x3C, 8, "SRL H", "Shift Right Logical H 1 bit", _ => { _h.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x3D, 8, "SRL L", "Shift Right Logical L 1 bit", _ => { _l.ShiftRightLogical(); }); + AddDoubleByteInstruction(0xCB, 0x3E, 8, "SRL (HL)", "Shift Right Logical (HL) 1 bit", _ => { _hl.ShiftRightLogical(0); }); + AddSpecialCbInstruction(0xDD, 0x3E, 23, "SRL (IX+d)", "Shift Right Logical (IX+d) 1 bit", i => { _ix.ShiftRightLogical(((SpecialCbInstruction)i).DataByte); }); + AddSpecialCbInstruction(0xFD, 0x3E, 23, "SRL (IY+d)", "Shift Right Logical (IY+d) 1 bit", i => { _iy.ShiftRightLogical(((SpecialCbInstruction)i).DataByte); }); + + AddDoubleByteInstruction(0xED, 0x6F, 18, "RLD", "Rotate Left 4 bits", _ => { _accumulator.RotateLeftDigit(_hl); }); + AddDoubleByteInstruction(0xED, 0x67, 18, "RRD", "Rotate Right 4 bits", _ => { _accumulator.RotateRightDigit(_hl); }); } private void PopulateLoadAndExchangeInstructions() @@ -715,64 +714,64 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x5F, 4, "LD A,E", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); // These are undocumented instructions which operate on the IX/IY high and low nibbles - AddDoubleByteInstruction(0xDD, 0x26, 7, "LD IXH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _ix.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x2E, 7, "LD IXL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _ix.Low, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x26, 7, "LD IYH,N", "Load n into IX high", _ => { LoadValueInto8BitRegister(ref _iy.High, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x2E, 7, "LD IYL,N", "Load n into IX low", _ => { LoadValueInto8BitRegister(ref _iy.Low, _pc.GetNextDataByte()); }); - - AddDoubleByteInstruction(0xDD, 0x7C, 9, "LD A, IXH", "Load IXH into A", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x44, 9, "LD B, IXH", "Load IXH into B", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _bc.High); }); - AddDoubleByteInstruction(0xDD, 0x4C, 9, "LD C, IXH", "Load IXH into C", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _bc.Low); }); - AddDoubleByteInstruction(0xDD, 0x54, 9, "LD D, IXH", "Load IXH into D", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _de.High); }); - AddDoubleByteInstruction(0xDD, 0x5C, 9, "LD E, IXH", "Load IXH into E", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _de.Low); }); - AddDoubleByteInstruction(0xDD, 0x7D, 9, "LD A, IXL", "Load IXL into A", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _af.High); }); - AddDoubleByteInstruction(0xDD, 0x45, 9, "LD B, IXL", "Load IXL into B", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _bc.High); }); - AddDoubleByteInstruction(0xDD, 0x4D, 9, "LD C, IXL", "Load IXL into C", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _bc.Low); }); - AddDoubleByteInstruction(0xDD, 0x55, 9, "LD D, IXL", "Load IXL into D", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _de.High); }); - AddDoubleByteInstruction(0xDD, 0x5D, 9, "LD E, IXL", "Load IXL into E", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _de.Low); }); - - AddDoubleByteInstruction(0xFD, 0x7C, 9, "LD A, IYH", "Load IYH into A", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x44, 9, "LD B, IYH", "Load IYH into B", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _bc.High); }); - AddDoubleByteInstruction(0xFD, 0x4C, 9, "LD C, IYH", "Load IYH into C", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _bc.Low); }); - AddDoubleByteInstruction(0xFD, 0x54, 9, "LD D, IYH", "Load IYH into D", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _de.High); }); - AddDoubleByteInstruction(0xFD, 0x5C, 9, "LD E, IYH", "Load IYH into E", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _de.Low); }); - AddDoubleByteInstruction(0xFD, 0x7D, 9, "LD A, IYL", "Load IYL into A", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _af.High); }); - AddDoubleByteInstruction(0xFD, 0x45, 9, "LD B, IYL", "Load IYL into B", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _bc.High); }); - AddDoubleByteInstruction(0xFD, 0x4D, 9, "LD C, IYL", "Load IYL into C", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _bc.Low); }); - AddDoubleByteInstruction(0xFD, 0x55, 9, "LD D, IYL", "Load IYL into D", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _de.High); }); - AddDoubleByteInstruction(0xFD, 0x5D, 9, "LD E, IYL", "Load IYL into E", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _de.Low); }); - - AddDoubleByteInstruction(0xDD, 0x67, 9, "LD IXH, A", "Load A into IXH", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x60, 9, "LD IXH, B", "Load B into IXH", _ => { Load8BitRegisterFrom8BitRegister(_bc.High, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x61, 9, "LD IXH, C", "Load C into IXH", _ => { Load8BitRegisterFrom8BitRegister(_bc.Low, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x62, 9, "LD IXH, D", "Load D into IXH", _ => { Load8BitRegisterFrom8BitRegister(_de.High, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x63, 9, "LD IXH, E", "Load E into IXH", _ => { Load8BitRegisterFrom8BitRegister(_de.Low, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x64, 9, "LD IXH, IXH", "Load IXH into IXH", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _ix.High); }); - AddDoubleByteInstruction(0xDD, 0x65, 9, "LD IXH, IXL", "Load IXL into IXH", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _ix.High); }); - - AddDoubleByteInstruction(0xDD, 0x6F, 9, "LD IXL, A", "Load A into IXL", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x68, 9, "LD IXL, B", "Load B into IXL", _ => { Load8BitRegisterFrom8BitRegister(_bc.High, ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x69, 9, "LD IXL, C", "Load C into IXL", _ => { Load8BitRegisterFrom8BitRegister(_bc.Low , ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x6A, 9, "LD IXL, D", "Load D into IXL", _ => { Load8BitRegisterFrom8BitRegister(_de.High, ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x6B, 9, "LD IXL, E", "Load E into IXL", _ => { Load8BitRegisterFrom8BitRegister(_de.Low , ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x6C, 9, "LD IXL, IXH", "Load IXH into IXL", _ => { Load8BitRegisterFrom8BitRegister(_ix.High, ref _ix.Low); }); - AddDoubleByteInstruction(0xDD, 0x6D, 9, "LD IXL, IXL", "Load IXL into IXL", _ => { Load8BitRegisterFrom8BitRegister(_ix.Low, ref _ix.Low); }); - - AddDoubleByteInstruction(0xFD, 0x67, 9, "LD IYH, A", "Load A into IYH", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x60, 9, "LD IYH, B", "Load B into IYH", _ => { Load8BitRegisterFrom8BitRegister(_bc.High, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x61, 9, "LD IYH, C", "Load C into IYH", _ => { Load8BitRegisterFrom8BitRegister(_bc.Low, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x62, 9, "LD IYH, D", "Load D into IYH", _ => { Load8BitRegisterFrom8BitRegister(_de.High, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x63, 9, "LD IYH, E", "Load E into IYH", _ => { Load8BitRegisterFrom8BitRegister(_de.Low, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x64, 9, "LD IYH, IYH", "Load IYH into IYH", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _iy.High); }); - AddDoubleByteInstruction(0xFD, 0x65, 9, "LD IYH, IYL", "Load IYL into IYH", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _iy.High); }); - - AddDoubleByteInstruction(0xFD, 0x6F, 9, "LD IYL, A", "Load A into IYL", _ => { Load8BitRegisterFrom8BitRegister(_af.High, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x68, 9, "LD IYL, B", "Load B into IYL", _ => { Load8BitRegisterFrom8BitRegister(_bc.High, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x69, 9, "LD IYL, C", "Load C into IYL", _ => { Load8BitRegisterFrom8BitRegister(_bc.Low, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x6A, 9, "LD IYL, D", "Load D into IYL", _ => { Load8BitRegisterFrom8BitRegister(_de.High, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x6B, 9, "LD IYL, E", "Load E into IYL", _ => { Load8BitRegisterFrom8BitRegister(_de.Low, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x6C, 9, "LD IYL, IYH", "Load IYH into IYL", _ => { Load8BitRegisterFrom8BitRegister(_iy.High, ref _iy.Low); }); - AddDoubleByteInstruction(0xFD, 0x6D, 9, "LD IYL, IYL", "Load IYL into IYL", _ => { Load8BitRegisterFrom8BitRegister(_iy.Low, ref _iy.Low); }); + AddDoubleByteInstruction(0xDD, 0x26, 7, "LD IXH,N", "Load n into IX high", _ => { _ix.SetHigh(_pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x2E, 7, "LD IXL,N", "Load n into IX low", _ => { _ix.SetLow(_pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x26, 7, "LD IYH,N", "Load n into IX high", _ => { _iy.SetHigh(_pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x2E, 7, "LD IYL,N", "Load n into IX low", _ => { _iy.SetLow(_pc.GetNextDataByte()); }); + + AddDoubleByteInstruction(0xDD, 0x7C, 9, "LD A, IXH", "Load IXH into A", _ => { _accumulator.Set(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x44, 9, "LD B, IXH", "Load IXH into B", _ => { _b.Set(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x4C, 9, "LD C, IXH", "Load IXH into C", _ => { _c.Set(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x54, 9, "LD D, IXH", "Load IXH into D", _ => { _d.Set(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x5C, 9, "LD E, IXH", "Load IXH into E", _ => { _e.Set(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x7D, 9, "LD A, IXL", "Load IXL into A", _ => { _accumulator.Set(_ix.Low); }); + AddDoubleByteInstruction(0xDD, 0x45, 9, "LD B, IXL", "Load IXL into B", _ => { _b.Set(_ix.Low); }); + AddDoubleByteInstruction(0xDD, 0x4D, 9, "LD C, IXL", "Load IXL into C", _ => { _c.Set(_ix.Low); }); + AddDoubleByteInstruction(0xDD, 0x55, 9, "LD D, IXL", "Load IXL into D", _ => { _d.Set(_ix.Low); }); + AddDoubleByteInstruction(0xDD, 0x5D, 9, "LD E, IXL", "Load IXL into E", _ => { _e.Set(_ix.Low); }); + + AddDoubleByteInstruction(0xFD, 0x7C, 9, "LD A, IYH", "Load IYH into A", _ => { _accumulator.Set(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x44, 9, "LD B, IYH", "Load IYH into B", _ => { _b.Set(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x4C, 9, "LD C, IYH", "Load IYH into C", _ => { _c.Set(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x54, 9, "LD D, IYH", "Load IYH into D", _ => { _d.Set(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x5C, 9, "LD E, IYH", "Load IYH into E", _ => { _e.Set(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x7D, 9, "LD A, IYL", "Load IYL into A", _ => { _accumulator.Set(_iy.Low); }); + AddDoubleByteInstruction(0xFD, 0x45, 9, "LD B, IYL", "Load IYL into B", _ => { _b.Set(_iy.Low); }); + AddDoubleByteInstruction(0xFD, 0x4D, 9, "LD C, IYL", "Load IYL into C", _ => { _c.Set(_iy.Low); }); + AddDoubleByteInstruction(0xFD, 0x55, 9, "LD D, IYL", "Load IYL into D", _ => { _d.Set(_iy.Low); }); + AddDoubleByteInstruction(0xFD, 0x5D, 9, "LD E, IYL", "Load IYL into E", _ => { _e.Set(_iy.Low); }); + + AddDoubleByteInstruction(0xDD, 0x67, 9, "LD IXH, A", "Load A into IXH", _ => { _ix.SetHigh(_accumulator.Value); }); + AddDoubleByteInstruction(0xDD, 0x60, 9, "LD IXH, B", "Load B into IXH", _ => { _ix.SetHigh(_b.Value); }); + AddDoubleByteInstruction(0xDD, 0x61, 9, "LD IXH, C", "Load C into IXH", _ => { _ix.SetHigh(_c.Value); }); + AddDoubleByteInstruction(0xDD, 0x62, 9, "LD IXH, D", "Load D into IXH", _ => { _ix.SetHigh(_d.Value); }); + AddDoubleByteInstruction(0xDD, 0x63, 9, "LD IXH, E", "Load E into IXH", _ => { _ix.SetHigh(_e.Value); }); + AddDoubleByteInstruction(0xDD, 0x64, 9, "LD IXH, IXH", "Load IXH into IXH", _ => { _ix.SetHigh(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x65, 9, "LD IXH, IXL", "Load IXL into IXH", _ => { _ix.SetHigh(_ix.Low); }); + + AddDoubleByteInstruction(0xDD, 0x6F, 9, "LD IXL, A", "Load A into IXL", _ => { _ix.SetLow(_accumulator.Value); }); + AddDoubleByteInstruction(0xDD, 0x68, 9, "LD IXL, B", "Load B into IXL", _ => { _ix.SetLow(_b.Value); }); + AddDoubleByteInstruction(0xDD, 0x69, 9, "LD IXL, C", "Load C into IXL", _ => { _ix.SetLow(_c.Value); }); + AddDoubleByteInstruction(0xDD, 0x6A, 9, "LD IXL, D", "Load D into IXL", _ => { _ix.SetLow(_d.Value); }); + AddDoubleByteInstruction(0xDD, 0x6B, 9, "LD IXL, E", "Load E into IXL", _ => { _ix.SetLow(_e.Value); }); + AddDoubleByteInstruction(0xDD, 0x6C, 9, "LD IXL, IXH", "Load IXH into IXL", _ => { _ix.SetLow(_ix.High); }); + AddDoubleByteInstruction(0xDD, 0x6D, 9, "LD IXL, IXL", "Load IXL into IXL", _ => { _ix.SetLow(_ix.Low); }); + + AddDoubleByteInstruction(0xFD, 0x67, 9, "LD IYH, A", "Load A into IYH", _ => { _iy.SetHigh(_accumulator.Value); }); + AddDoubleByteInstruction(0xFD, 0x60, 9, "LD IYH, B", "Load B into IYH", _ => { _iy.SetHigh(_b.Value); }); + AddDoubleByteInstruction(0xFD, 0x61, 9, "LD IYH, C", "Load C into IYH", _ => { _iy.SetHigh(_c.Value); }); + AddDoubleByteInstruction(0xFD, 0x62, 9, "LD IYH, D", "Load D into IYH", _ => { _iy.SetHigh(_d.Value); }); + AddDoubleByteInstruction(0xFD, 0x63, 9, "LD IYH, E", "Load E into IYH", _ => { _iy.SetHigh(_e.Value); }); + AddDoubleByteInstruction(0xFD, 0x64, 9, "LD IYH, IYH", "Load IYH into IYH", _ => { _iy.SetHigh(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x65, 9, "LD IYH, IYL", "Load IYL into IYH", _ => { _iy.SetHigh(_iy.Low); }); + + AddDoubleByteInstruction(0xFD, 0x6F, 9, "LD IYL, A", "Load A into IYL", _ => { _iy.SetLow(_accumulator.Value); }); + AddDoubleByteInstruction(0xFD, 0x68, 9, "LD IYL, B", "Load B into IYL", _ => { _iy.SetLow(_b.Value); }); + AddDoubleByteInstruction(0xFD, 0x69, 9, "LD IYL, C", "Load C into IYL", _ => { _iy.SetLow(_c.Value); }); + AddDoubleByteInstruction(0xFD, 0x6A, 9, "LD IYL, D", "Load D into IYL", _ => { _iy.SetLow(_d.Value); }); + AddDoubleByteInstruction(0xFD, 0x6B, 9, "LD IYL, E", "Load E into IYL", _ => { _iy.SetLow(_e.Value); }); + AddDoubleByteInstruction(0xFD, 0x6C, 9, "LD IYL, IYH", "Load IYH into IYL", _ => { _iy.SetLow(_iy.High); }); + AddDoubleByteInstruction(0xFD, 0x6D, 9, "LD IYL, IYL", "Load IYL into IYL", _ => { _iy.SetLow(_iy.Low); }); // end undocumented instructions AddDoubleByteInstruction(0xDD, 0x7E, 19, "LD A,(IX+d)", "Load memory at IX + d into A", _ => { _accumulator.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); @@ -791,9 +790,9 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x66, 19, "LD H,(IY+d)", "Load memory at IY + d into H", _ => { _h.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { _l.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); - AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { WriteToMemory(_hl, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); WriteToMemory(_ix, value, offset); }); - AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); WriteToMemory(_iy, value, offset); }); + AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { _memoryManagement.WriteToMemory(_hl, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); _memoryManagement.WriteToMemory(_ix, value, offset); }); + AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); _memoryManagement.WriteToMemory(_iy, value, offset); }); AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { _accumulator.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x2A, 16, "LD HL,(NN)", "Load value at memory location NN into HL", _ => { _hl.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); @@ -820,10 +819,10 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => { - CopyMemory(_hl, _de); - Increment16Bit(ref _hl); - Increment16Bit(ref _de); - Decrement16Bit(ref _bc); + _memoryManagement.CopyMemory(_hl, _de); + _hl.Increment(); + _de.Increment(); + _bc.Decrement(); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); @@ -839,10 +838,10 @@ private void PopulateLoadAndExchangeInstructions() return; } - CopyMemory(_hl, _de); - Increment16Bit(ref _hl); - Increment16Bit(ref _de); - Decrement16Bit(ref _bc); + _memoryManagement.CopyMemory(_hl, _de); + _hl.Increment(); + _de.Increment(); + _bc.Decrement(); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); @@ -862,10 +861,10 @@ private void PopulateLoadAndExchangeInstructions() }); AddDoubleByteInstruction(0xED, 0xA8, 16, "LDD", "Load and Decrement", _ => { - CopyMemory(_hl, _de); - Decrement16Bit(ref _hl); - Decrement16Bit(ref _de); - Decrement16Bit(ref _bc); + _memoryManagement.CopyMemory(_hl, _de); + _hl.Decrement(); + _de.Decrement(); + _bc.Decrement(); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); @@ -881,10 +880,10 @@ private void PopulateLoadAndExchangeInstructions() return; } - CopyMemory(_hl, _de); - Decrement16Bit(ref _hl); - Decrement16Bit(ref _de); - Decrement16Bit(ref _bc); + _memoryManagement.CopyMemory(_hl, _de); + _hl.Decrement(); + _de.Decrement(); + _bc.Decrement(); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); // This is not a typo, for some reason in LDDR the PV flag is reset unlike the other LDx instructions @@ -911,28 +910,28 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xDD, 0xE5, 15, "PUSH IX", "Push IX", _ => { _stack.PushRegisterToStack(_ix); }); AddDoubleByteInstruction(0xFD, 0xE5, 15, "PUSH IY", "Push IY", _ => { _stack.PushRegisterToStack(_iy); }); - AddStandardInstruction(0xF1, 10, "POP AF", "Pop AF from Stack", _ => { _stack.PopRegisterFromStack(ref _af); }); - AddStandardInstruction(0xC1, 10, "POP BC", "Pop BC from Stack", _ => { _stack.PopRegisterFromStack(ref _bc); }); - AddStandardInstruction(0xD1, 10, "POP DE", "Pop DE from Stack", _ => { _stack.PopRegisterFromStack(ref _de); }); - AddStandardInstruction(0xE1, 10, "POP HL", "Pop HL from Stack", _ => { _stack.PopRegisterFromStack(ref _hl); }); - AddDoubleByteInstruction(0xDD, 0xE1, 14, "POP IX", "Pop IX from Stack", _ => { _stack.PopRegisterFromStack(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0xE1, 14, "POP IY", "Pop IY from Stack", _ => { _stack.PopRegisterFromStack(ref _iy); }); + AddStandardInstruction(0xF1, 10, "POP AF", "Pop AF from Stack", _ => { _stack.PopRegisterFromStack(_af); }); + AddStandardInstruction(0xC1, 10, "POP BC", "Pop BC from Stack", _ => { _stack.PopRegisterFromStack(_bc); }); + AddStandardInstruction(0xD1, 10, "POP DE", "Pop DE from Stack", _ => { _stack.PopRegisterFromStack(_de); }); + AddStandardInstruction(0xE1, 10, "POP HL", "Pop HL from Stack", _ => { _stack.PopRegisterFromStack(_hl); }); + AddDoubleByteInstruction(0xDD, 0xE1, 14, "POP IX", "Pop IX from Stack", _ => { _stack.PopRegisterFromStack(_ix); }); + AddDoubleByteInstruction(0xFD, 0xE1, 14, "POP IY", "Pop IY from Stack", _ => { _stack.PopRegisterFromStack(_iy); }); } private void PopulateExchangeBlockTransferAndSearchInstructions() { - AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _hl); }); - AddStandardInstruction(0x8, 4, "EX AF,AF'", "Exchange AF and AF Shadow", _ => { SwapRegisters(ref _af, ref _afShadow); }); + AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(_hl); }); + AddStandardInstruction(0x8, 4, "EX AF,AF'", "Exchange AF and AF Shadow", _ => { _af.SwapWithShadow(); }); AddStandardInstruction(0xEB, 4, "EX DE,HL", "Exchange DE and HL", _ => { _hl.SwapWithDeRegister(_de); }); - AddStandardInstruction(0xD9, 4, "EXX", "Exchange BC, DE, HL with Shadow Registers", _ => { SwapRegisters(ref _bc, ref _bcShadow); SwapRegisters(ref _de, ref _deShadow); SwapRegisters(ref _hl, ref _hlShadow); }); - AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _ix); }); - AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(ref _iy); }); + AddStandardInstruction(0xD9, 4, "EXX", "Exchange BC, DE, HL with Shadow Registers", _ => { _bc.SwapWithShadow(); _de.SwapWithShadow(); _hl.SwapWithShadow(); }); + AddDoubleByteInstruction(0xDD, 0xE3, 23, "EX (SP),IX", "Exchange IX with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(_ix); }); + AddDoubleByteInstruction(0xFD, 0xE3, 23, "EX (SP),IY", "Exchange IY with Data from Memory Address in SP", _ => { _stack.SwapRegisterWithDataAtStackPointerAddress(_iy); }); } private void PopulateInputOutputInstructions() { - AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _accumulator.Set(_ioManagement.Read(_af.High, _pc.GetNextDataByte(), false)); }); - AddDoubleByteInstruction(0xED, 0x70, 12, "IN (C)", "Read I/O at B/C But Only Set Flags", _ => { _ioManagement.Read(_bc.High, _bc.Low, true); }); + AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _accumulator.Set(_ioManagement.Read(_accumulator.Value, _pc.GetNextDataByte(), false)); }); + AddDoubleByteInstruction(0xED, 0x70, 12, "IN (C)", "Read I/O at B/C But Only Set Flags", _ => { _ioManagement.Read(_b.Value, _c.Value, true); }); AddDoubleByteInstruction(0xED, 0x78, 12, "IN A,(C)", "Read I/O at B/C into A with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _accumulator); }); AddDoubleByteInstruction(0xED, 0x40, 12, "IN B,(C)", "Read I/O at B/C into B with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _b); }); AddDoubleByteInstruction(0xED, 0x48, 12, "IN C,(C)", "Read I/O at B/C into C with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _c); }); @@ -943,17 +942,17 @@ private void PopulateInputOutputInstructions() AddDoubleByteInstruction(0xED, 0xA2, 16, "INI", "Input and Increment", _ => { - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); var data = _io.ReadPort(portAddress); _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); _hl.Increment(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB2, DynamicCycleHandling, "INIR", "Input, Increment, Repeat", _ => { - if (_bc.High == 0) + if (_b.Value == 0) { // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; @@ -962,7 +961,7 @@ private void PopulateInputOutputInstructions() return; } - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); var data = _io.ReadPort(portAddress); _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); @@ -970,7 +969,7 @@ private void PopulateInputOutputInstructions() _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); - if (_bc.High != 0) + if (_b.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -986,17 +985,17 @@ private void PopulateInputOutputInstructions() AddDoubleByteInstruction(0xED, 0xAA, 16, "IND", "Input and Decrement", _ => { - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); var data = _io.ReadPort(portAddress); _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); _hl.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBA, DynamicCycleHandling, "INDR", "Input, Decrement, Repeat", _ => { - if (_bc.High == 0) + if (_b.Value == 0) { // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; @@ -1005,7 +1004,7 @@ private void PopulateInputOutputInstructions() return; } - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); var data = _io.ReadPort(portAddress); _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); @@ -1013,7 +1012,7 @@ private void PopulateInputOutputInstructions() _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); - if (_bc.High != 0) + if (_b.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -1028,29 +1027,29 @@ private void PopulateInputOutputInstructions() }); - AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { _ioManagement.Write(_af.High, _pc.GetNextDataByte(), _accumulator); }); - AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { _ioManagement.Write(_b.Value, _bc.Low, _accumulator); }); - AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { _ioManagement.Write(_b.Value, _bc.Low, _b); }); - AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { _ioManagement.Write(_b.Value, _bc.Low, _c); }); - AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { _ioManagement.Write(_b.Value, _bc.Low, _d); }); - AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { _ioManagement.Write(_b.Value, _bc.Low, _e); }); - AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { _ioManagement.Write(_b.Value, _bc.Low, _h); }); - AddDoubleByteInstruction(0xED, 0x69, 12, "OUT (C),L", "Write I/O at B/C from L", _ => { _ioManagement.Write(_b.Value, _bc.Low, _l); }); + AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { _ioManagement.Write(_accumulator.Value, _pc.GetNextDataByte(), _accumulator); }); + AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { _ioManagement.Write(_b.Value, _c.Value, _accumulator); }); + AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { _ioManagement.Write(_b.Value, _c.Value, _b); }); + AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { _ioManagement.Write(_b.Value, _c.Value, _c); }); + AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { _ioManagement.Write(_b.Value, _c.Value, _d); }); + AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { _ioManagement.Write(_b.Value, _c.Value, _e); }); + AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { _ioManagement.Write(_b.Value, _c.Value, _h); }); + AddDoubleByteInstruction(0xED, 0x69, 12, "OUT (C),L", "Write I/O at B/C from L", _ => { _ioManagement.Write(_b.Value, _c.Value, _l); }); AddDoubleByteInstruction(0xED, 0xA3, 16, "OUTI", "Output and Increment", _ => { var data = _memoryManagement.ReadFromMemory(_hl); _b.Decrement(); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); _io.WritePort(portAddress, data); _hl.Increment(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB3, DynamicCycleHandling, "OTIR", "Output, Increment, Repeat", _ => { - if (_bc.High == 0) + if (_b.Value == 0) { // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; @@ -1061,14 +1060,14 @@ private void PopulateInputOutputInstructions() var data = _memoryManagement.ReadFromMemory(_hl); _b.Decrement(); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); _io.WritePort(portAddress, data); _hl.Increment(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); - if (_bc.High != 0) + if (_b.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts @@ -1086,16 +1085,16 @@ private void PopulateInputOutputInstructions() { var data = _memoryManagement.ReadFromMemory(_hl); _b.Decrement(); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); _io.WritePort(portAddress, data); _hl.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => { - if (_bc.High == 0) + if (_b.Value == 0) { // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; @@ -1106,14 +1105,14 @@ private void PopulateInputOutputInstructions() var data = _memoryManagement.ReadFromMemory(_hl); _b.Decrement(); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); _io.WritePort(portAddress, data); _hl.Decrement(); _flags.SetFlag(Z80StatusFlags.ZeroZ); _flags.SetFlag(Z80StatusFlags.AddSubtractN); - if (_bc.High != 0) + if (_b.Value != 0) { // If not zero, set PC back by 2 so instruction is repeated // Note that this is not a loop here since we still need to process interrupts From 0ab3ce8aa7b064c9591e21157acd10f3d924060f Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:03:59 +1000 Subject: [PATCH 08/21] Fix issue where save to memory for 16 bit general purpose registers was saving in wrong order Fix issue where index register class was setting high/low values incorrectly when incrementing and decrementing the IXH/IXL, IYH/IYL values Fix issues where check added to fail if stack pointer went below or above min/max possible breaks zexdoc so changed this to an error since this may be valid to do in practice, if not then this will still flag a error so it is clear. Add new unit test which validates every instruction against a pregenerated hash of CPU, memory and I/O state. The hashes were generated from the main branch (with some fixes backported) which passes zexdoc. These tests don't test for invalid implementations that current exist in the main branch but will detect any new regressions (ie. from this refactoring). This essentially provides a similar testing mechanism to zexdoc except validated against a working implementation and runs far faster. It shouldn't be relied on to validate current implementation but ensure no new regressions are introduced in the future. This also only tests CPU code with memory and I/O mocked out. --- .../Kmse.Core.UnitTests.csproj | 8 + .../Data/TestInstructions.json | 5945 +++++++++++++++++ .../TestInstructionsFixture.cs | 215 + .../Z80CpuTests/InstructionTests/TestIo.cs | 65 + .../InstructionTests/TestMemory.cs | 60 + .../Z80CpuTests/Z80StackManagementFixture.cs | 14 +- .../SpecialPurpose/Z80IndexRegisterXy.cs | 16 +- .../SpecialPurpose/Z80ProgramCounter.cs | 4 - .../SpecialPurpose/Z80StackManager.cs | 9 +- .../Z8016BitGeneralPurposeRegisterBase.cs | 4 +- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 16 +- 11 files changed, 6324 insertions(+), 32 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs diff --git a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj index de3a706..5319e64 100644 --- a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj +++ b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj @@ -7,6 +7,14 @@ disable + + + + + + + + diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json new file mode 100644 index 0000000..55f19dc --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json @@ -0,0 +1,5945 @@ +[ + { + "Name": "NOP", + "HexInstructions": "00", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D4A0627B93D3F34ADBCD0D12E765BA41A8220C39938B8F4C40E09331928AF9EC" + }, + { + "Name": "HALT", + "HexInstructions": "76", + "CpuHash": "2E630E500CD52608A41E7669C09738C0BE321F20084BABFA7383AC4A918FDD78", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BBEAB1E4688169E4B29273A83548EE3C5F7140AB16932293B2C50724DB64B7AA" + }, + { + "Name": "DI", + "HexInstructions": "F3", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "31DAE06C7E402AF14D779D2924F195D11503D8217FDD6C278CD7CBD1A5C004C0" + }, + { + "Name": "EI", + "HexInstructions": "FB", + "CpuHash": "6DA8909143A5BC6E967812ADA551BCA8ED8F7C4F2816363B587F4062D6B7CDA7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "717A7A5D379925910BC93B2C2A0DF5DB074FD4C38083AE863C315AD81B2EF803" + }, + { + "Name": "JP (HL)", + "HexInstructions": "E9", + "CpuHash": "7F81FE3393754B1F204BD9DA077391671C86AC32A9C9E7226161429212260B89", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A339DC296104C818493B93FE7B671EDE06B1A810B511E068B99BFBE98E364D8D" + }, + { + "Name": "JP $NN", + "HexInstructions": "C3", + "CpuHash": "649A428532A96295E959B95DA5096186E5117930AC072D460992205BE9B69166", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B6174320B2BE1F4AAE9468E79882F991746A4660ADC378AC61881739C2AE7A59" + }, + { + "Name": "JP C,$NN", + "HexInstructions": "DA", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DD7596664DF6E57C9E7471F53E404EF5575819303EC00079D94B5359DB7FFABE" + }, + { + "Name": "JP NC,$NN", + "HexInstructions": "D2", + "CpuHash": "649A428532A96295E959B95DA5096186E5117930AC072D460992205BE9B69166", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C85AB447475BB0639600C4FFD481213353655C3AF3B111ED5103CBEE0729E8D3" + }, + { + "Name": "JP M,$NN", + "HexInstructions": "FA", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "526F334450D59F3D8917654BA52EB652FD592B58E91C9AE6C1A40FFCD794DC9F" + }, + { + "Name": "JP P,$NN", + "HexInstructions": "F2", + "CpuHash": "649A428532A96295E959B95DA5096186E5117930AC072D460992205BE9B69166", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C9BDB6CF5964EFD43EF7976A8243A8E6E72FDD6AD4EE5F1FA1143CC95E5729B3" + }, + { + "Name": "JP Z,$NN", + "HexInstructions": "CA", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8FB9453151ED661CE28F023E934A88CA7404452FE788AF3D6A444DC442D65FA5" + }, + { + "Name": "JP NZ,$NN", + "HexInstructions": "C2", + "CpuHash": "649A428532A96295E959B95DA5096186E5117930AC072D460992205BE9B69166", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "157DE36B0F4FF8A8754D58C49C3A4A86DAAB5BD78688F7100C6FFCE3CD89150C" + }, + { + "Name": "JP PE,$NN", + "HexInstructions": "EA", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "46FD1F794D68C2C74142D51E4815B44D2A6FCABBA96CF14533A0187C2C330920" + }, + { + "Name": "JP PO,$NN", + "HexInstructions": "E2", + "CpuHash": "649A428532A96295E959B95DA5096186E5117930AC072D460992205BE9B69166", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CA8E9982B4DEE1900AFCC6D14A7CD1DFBAC88A6714E520E2FE7EE600E6ED4833" + }, + { + "Name": "JR $N\u002B2", + "HexInstructions": "18", + "CpuHash": "BBB0BF015EAC2657C647BFA739904D46D22B3AF7E422295ADE48D66AB777D70A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4158684B6539968B5A3A6170A806A90CD90D6D6CF9647FB0397B33C465B45EE5" + }, + { + "Name": "JR C,$N\u002B2", + "HexInstructions": "38", + "CpuHash": "596C683EA7F9CF806D15E79AAA515FC57DE8DFE46A038E13500AE3E493122493", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3D1BFF79E19B348F0A7E56A7319C3CC2969296F3B61C6C44F0AFCB347285AC85" + }, + { + "Name": "JR NC,$N\u002B2", + "HexInstructions": "30", + "CpuHash": "BBB0BF015EAC2657C647BFA739904D46D22B3AF7E422295ADE48D66AB777D70A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5A6FA446DC2702B697F8C22028E0E251F3600A757A132A3C6CFCF33C5B53DA54" + }, + { + "Name": "JR Z,$N\u002B2", + "HexInstructions": "28", + "CpuHash": "596C683EA7F9CF806D15E79AAA515FC57DE8DFE46A038E13500AE3E493122493", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DAC6BB0AFA66C10846F6B62D78565D077E1E28A2EDD392DBC6AC45F7043A378A" + }, + { + "Name": "JR NZ,$N\u002B2", + "HexInstructions": "20", + "CpuHash": "BBB0BF015EAC2657C647BFA739904D46D22B3AF7E422295ADE48D66AB777D70A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B40CF010F8BC1A85E054E7C81A351D5E10443BBF67B6B34BE28A767D2EA923E9" + }, + { + "Name": "DJNZ $\u002B2", + "HexInstructions": "10", + "CpuHash": "F8794FC458AB01EAD894F2A31C2855C3CABD7CE49F4A33081D93CF6F875CE16B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FB234833E8C069CDC61FAAAD8EC8E88F313DC9425AAE0E7CDA3C85FF232B7300" + }, + { + "Name": "CALL NN", + "HexInstructions": "CD", + "CpuHash": "E2802140AC558BB47DCBEAE4898654B4100316F56A7AAACCF11C0587E2A1F56E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0C47C20BCB7B42AC9CD2CEF4D6EC8E081813C340AC692657F08D7A6E0224D09B" + }, + { + "Name": "CALL C,NN", + "HexInstructions": "DC", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6C9EF717142A77B189FEE42DDBB805410B294EE040ECF2C3756CB663B767F5FF" + }, + { + "Name": "CALL NC,NN", + "HexInstructions": "D4", + "CpuHash": "E2802140AC558BB47DCBEAE4898654B4100316F56A7AAACCF11C0587E2A1F56E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1851F94C96E8CD5D9A9E2C1B9BD71B89126F152674D94753A17930DD55634CC5" + }, + { + "Name": "CALL M,NN", + "HexInstructions": "FC", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "367E0D6BC96A7FF62FA696FE3AB8F11CF4A14F26A6F2E7B844BC861BFDA40516" + }, + { + "Name": "CALL P,NN", + "HexInstructions": "F4", + "CpuHash": "E2802140AC558BB47DCBEAE4898654B4100316F56A7AAACCF11C0587E2A1F56E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "44512E80B9C83A65468C515602F33B1E0C181A3A0DB98D95A4FC4B9AD2593E77" + }, + { + "Name": "CALL Z,NN", + "HexInstructions": "CC", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AE02C42E809D79C666EAA20F542A7923D73AF87A4BF300B299237999AEB93B01" + }, + { + "Name": "CALL NZ,NN", + "HexInstructions": "C4", + "CpuHash": "E2802140AC558BB47DCBEAE4898654B4100316F56A7AAACCF11C0587E2A1F56E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "867B0FB6B27FCCD956ED7DF63715BB32CF87D9B3A64A825EC4A1EFEC0AA4AA3C" + }, + { + "Name": "CALL PE,NN", + "HexInstructions": "EC", + "CpuHash": "C605E0FF823FEF927A820BC0E32D43FC63C7ACFE0E11C90E7A6674ED87F21718", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A41C2D37009699EB943D9AEC4162FCE3ED9B7CE7CFDBC80292424EB3729A3776" + }, + { + "Name": "CALL PO,NN", + "HexInstructions": "E4", + "CpuHash": "E2802140AC558BB47DCBEAE4898654B4100316F56A7AAACCF11C0587E2A1F56E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "85234055978807585A1AEFD4D3BE15184CC833DBF2EFA20F80881A403FFB9A01" + }, + { + "Name": "RET", + "HexInstructions": "C9", + "CpuHash": "D4BA388D0A022A4A0CBAA71A54C337286E3B576538C3A9F0C412A6AF816A80EC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FCC6DEE647714C7A57DF32449A202E25DC104761C8645E7212E45FC4C8951BA5" + }, + { + "Name": "RET C", + "HexInstructions": "D8", + "CpuHash": "00657D8A3ABDAB1F29DEB9E5A55459BED80E98E75977BE60698FDDCF13909066", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3BA3FCBE3AF86144F0838E94FA54A00EB571CD9CD484924E2979FE3E4379E770" + }, + { + "Name": "RET NC", + "HexInstructions": "D0", + "CpuHash": "C4E371B993BFBF8872FE66DA4F8D2B7CF7088EAECD144F235B8F9552E3343318", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "71B151EE5BDC81FF14088790149F1829E8BC5846318917E5F65901048BC78CF5" + }, + { + "Name": "RET M", + "HexInstructions": "F8", + "CpuHash": "00657D8A3ABDAB1F29DEB9E5A55459BED80E98E75977BE60698FDDCF13909066", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0FA134935BDE6D10614A4DCBC5400307F49B0AB10CB922EFC331F9FFD7823AB3" + }, + { + "Name": "RET P", + "HexInstructions": "F0", + "CpuHash": "C4E371B993BFBF8872FE66DA4F8D2B7CF7088EAECD144F235B8F9552E3343318", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4929CC35DE031BD7736A76E3BD6CE7F1C342CE6186FB60FFA8AD0071B6EAFBB6" + }, + { + "Name": "RET Z", + "HexInstructions": "C8", + "CpuHash": "00657D8A3ABDAB1F29DEB9E5A55459BED80E98E75977BE60698FDDCF13909066", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "79EFDC3499AE375196CACD32D3BC46E431CEDC96DF3F2C9DF9792233A725EFBD" + }, + { + "Name": "RET NZ", + "HexInstructions": "C0", + "CpuHash": "C4E371B993BFBF8872FE66DA4F8D2B7CF7088EAECD144F235B8F9552E3343318", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CA96CA151534CF55A4E8B98E9DB5BBC99ED0DE18C7F093099C10DD06287E81DB" + }, + { + "Name": "RET PE", + "HexInstructions": "E8", + "CpuHash": "00657D8A3ABDAB1F29DEB9E5A55459BED80E98E75977BE60698FDDCF13909066", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A2A7504A0A52F40E4932BEBE0F875D1780C6A631E4F06FCA851F63DC21037A71" + }, + { + "Name": "RET PO", + "HexInstructions": "E0", + "CpuHash": "C4E371B993BFBF8872FE66DA4F8D2B7CF7088EAECD144F235B8F9552E3343318", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E06107593EA019723C838509308F9BA6AEA9FD340EFF49F15D2C6ADE0789FCDD" + }, + { + "Name": "RST 0", + "HexInstructions": "C7", + "CpuHash": "49D7EFEA1D4E4770F31F658343BA326907B69EFFA99ADACAE5E67BAB85C9E0B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4457C995657A8B0E9794D5FC5C79E053071AB0ED058142564D532230DDE986C3" + }, + { + "Name": "RST 08H", + "HexInstructions": "CF", + "CpuHash": "31468C5D7FA4FD7C264423A0FC1ABE8609007D6B13CFFF992C912A0B8B8039DA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A363A61B7EA0EF482D6B0BE131B899C24DE6AD06CB53982242A49F27A68B7F8D" + }, + { + "Name": "RST 10H", + "HexInstructions": "D7", + "CpuHash": "A3426FE0B2CDE32582FAE5E8D673ADE03F4BB9421E27DE80D0F1764EF73EE5C9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BD2FA60DC7E59D4A29850D0BCBB711A7B0E0ED5B4D4C7FE8672CFA6D11461BB1" + }, + { + "Name": "RST 18H", + "HexInstructions": "DF", + "CpuHash": "0B8FF7901D58E3E291D87D7C3BA2CD6046DCE0CFA4E0FEB0CDFB8657FD398179", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "57CEFED3610C79C2E23DD7DFF1613D5EFFCD3905C5250833F69BE62B22878100" + }, + { + "Name": "RST 20H", + "HexInstructions": "E7", + "CpuHash": "31EB293759C1CE11BEA844A91EA0B5B0A5204EBC06A5F3506716FF23CF980496", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3D591745EB6946C04385BB4775D44ACF32137DB1645A0AD9BE0EBBC174D3EAFB" + }, + { + "Name": "RST 28H", + "HexInstructions": "EF", + "CpuHash": "6B5B00FBB6CBD984E772C34C82192D0A0998EF60CDFEEABA18E3F2053DB67AE8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "012CCE72CD7D5F653FA7FDC9C09644FF4CFCC4126D2D1729F8D3DFAD8AD81866" + }, + { + "Name": "RST 30H", + "HexInstructions": "F7", + "CpuHash": "68836B5812413F483C40FB08DE103308BBC50F70BB2850CB9CE2EDD70357C7F5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E7E692FFA7799EF1C7A1048C38393DCE58F3DB0D4317C9D12C9C62B28631FB4" + }, + { + "Name": "RST 38H", + "HexInstructions": "FF", + "CpuHash": "28608FD876E55D7B0E167320F09B18F93215132323DFE0D82E201809934558F5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DDCBD7681A20C0343B1FDF893FA4C234D708ADDC0B936FD5C64E106526B797E2" + }, + { + "Name": "ADD A, A", + "HexInstructions": "87", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BE2E5D4DB22A9E5A5BB534D64F96F01075E1A5ABC69AAAF4976863AD427AC9DB" + }, + { + "Name": "ADD A, B", + "HexInstructions": "80", + "CpuHash": "6EE9F45E4B20C569A8E2A27E10D2E3A8A579A443DD30826322891C7FEEDFCC9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9F1917441A918FED114AECD588CB1D8440630CC87F099846622980DB8E54F575" + }, + { + "Name": "ADD A, C", + "HexInstructions": "81", + "CpuHash": "D25477B8D029F97DBE7BAA68D00E35C78A87EC4D2CC560D833EF5F6D7BCBCB40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7C13469C8F9B348E7D8FCFEF34EB75D0D7BA9B7D7DB8FCFC926087158FA82724" + }, + { + "Name": "ADD A, D", + "HexInstructions": "82", + "CpuHash": "6FBE8DFB94C9D80551AF2D6ED5299C1413B125A9EB2CAE093272EA3DE82B322D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B644DE1B4CF925CE914EC21A4D77B40D68AFA0DADADCBE953D6BB411E5A83DB4" + }, + { + "Name": "ADD A, E", + "HexInstructions": "83", + "CpuHash": "1C86F933012876E9122B20CB0D4CF474C36289693F808117D7A347C520D92B26", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "814BB0BD29D0A41BA9A7CBB1CF7AF90C36454658D035D089443A750A5267CB4E" + }, + { + "Name": "ADD A, H", + "HexInstructions": "84", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "50470BF2D6E5F24E8F8C421DD4D1D00669A5A82FE34A008AC3287FA1DA4F941F" + }, + { + "Name": "ADD A, L", + "HexInstructions": "85", + "CpuHash": "4D440CE8692A45352068453180BBF8F8D3D9F90077498957AEB6AC465DB51872", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8520EAF22D932F8B7455617BF02B3C5AD5396B8FEB8468A48F266215F652D373" + }, + { + "Name": "ADD A, N", + "HexInstructions": "C6", + "CpuHash": "8A3E6702E215D45438BF9A4FA65DB20DE2B237F5BA4434D45992B0B152C2604D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6A81E0AD24B6F1785F116770B9BF103B19341E3862CA8668962CACF4F7E119F3" + }, + { + "Name": "ADD HL,BC", + "HexInstructions": "09", + "CpuHash": "4AD331405055BC6F28F3DC38E9E01F2B58B1214C852C7FEA842BCBF768FFF2B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "589EE62AF36E18C28089B01E4BD58645232D64B54BD76D00B255C62D42444C32" + }, + { + "Name": "ADD HL,DE", + "HexInstructions": "19", + "CpuHash": "EE19EDD61EE51CF11C3635D860D91D109D68EE8361B6CB1F488FA19638CD382B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "96D17C49B796AFA9987CF3285A7645D563B0C198D9994300B00880B7DD66405F" + }, + { + "Name": "ADD HL,HL", + "HexInstructions": "29", + "CpuHash": "D344316DDC5BBA008A003473FD8079CEC0D78FFFD7111043A95993CFDAF5DF42", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C32BF0485DC0429D17499C62C9EA72F67E81FD12104AB15B28AFBA24E844CE9C" + }, + { + "Name": "ADD HL,SP", + "HexInstructions": "39", + "CpuHash": "10627B3EB50A2302D1D31DFB8CD5B79F2FC206CB8EFF1D6615063A6E2359241A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E8B70A147433B0181B3FE770FA00E63DED2E08676E3EB4ECD0A54B17AEAE26CA" + }, + { + "Name": "ADD A,(HL)", + "HexInstructions": "86", + "CpuHash": "76B2995BFC1D5ABBEFAB68961796363D8F8DCBFB0F12A923E8D0EE11CE34ED40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0B5A0A50F44301B346270AA5BFB6E82BC2976AAD7D6575D730883EEB6CE5466B" + }, + { + "Name": "ADC A, A", + "HexInstructions": "8F", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "654229BED2FE4968705F67EFFA986C45ABFDB16067F27E20EBED5CED8D6CD611" + }, + { + "Name": "ADC B, B", + "HexInstructions": "88", + "CpuHash": "6EE9F45E4B20C569A8E2A27E10D2E3A8A579A443DD30826322891C7FEEDFCC9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "450047D7C25E1107E551DCE230BFA0835C26D7822D5468C7A21B3E9FB90111E7" + }, + { + "Name": "ADC C, C", + "HexInstructions": "89", + "CpuHash": "D25477B8D029F97DBE7BAA68D00E35C78A87EC4D2CC560D833EF5F6D7BCBCB40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "40CC79F7B493E21CBA38DB18E209981CAAEA8670A44FA15E7C9C4633F8BBB501" + }, + { + "Name": "ADC D, D", + "HexInstructions": "8A", + "CpuHash": "6FBE8DFB94C9D80551AF2D6ED5299C1413B125A9EB2CAE093272EA3DE82B322D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A8BDE9A02CE84B1A4BABD568A4440A5A626E598577B907B23135AB5F633A5187" + }, + { + "Name": "ADC E, E", + "HexInstructions": "8B", + "CpuHash": "1C86F933012876E9122B20CB0D4CF474C36289693F808117D7A347C520D92B26", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7C9682C938D773263F6983B4533DCBCF8BDCD3ED2A5E4562B7F96BB2F7D76DDA" + }, + { + "Name": "ADC H, H", + "HexInstructions": "8C", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "21E4F8A40629DF2DF698EA7D4900F3B68F99032BBA8305C109879FC058336D3A" + }, + { + "Name": "ADC L, L", + "HexInstructions": "8D", + "CpuHash": "4D440CE8692A45352068453180BBF8F8D3D9F90077498957AEB6AC465DB51872", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A03C28D07ADB23F76272AE150508E1DC0DFB995CE98C82F6F124E9BDDC7839DF" + }, + { + "Name": "ADC A, N", + "HexInstructions": "CE", + "CpuHash": "8A3E6702E215D45438BF9A4FA65DB20DE2B237F5BA4434D45992B0B152C2604D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "955426BD9C2A224DE3C2D696FEEF5F4445D4BF3565240729CBCFBD91CA38B068" + }, + { + "Name": "ADC (HL)", + "HexInstructions": "8E", + "CpuHash": "76B2995BFC1D5ABBEFAB68961796363D8F8DCBFB0F12A923E8D0EE11CE34ED40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1E49C0984FCC4FE38736730176DBA3BDD997A0A1BB0F929CDE8FFDA269F42D0A" + }, + { + "Name": "SUB A, A", + "HexInstructions": "97", + "CpuHash": "06D51CF9D409D83ED31EE44D1BDCDB71D172F3311B5C8BA7F75D5EF72F453058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0D87E9490069CDBD1C74E7EE4245DDEC39623A77417A23D7D348608FB175F951" + }, + { + "Name": "SUB A, B", + "HexInstructions": "90", + "CpuHash": "E273F2E708CF52994DE2FB4E4980FD30E9FC495EA6FD0CB02021A55209CBDC5C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FDD358F9CD15DE502BC5C62153635C53748D20CF5AEEAE277B27769D8B161AF9" + }, + { + "Name": "SUB A, C", + "HexInstructions": "91", + "CpuHash": "E497943288E15B33E82FD7065A9B62D5B907389B14D8AECD19AC93606EE17FE7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2708F83A92BA1F0D89ED8D0C37E1B9898D064CC232F5E608D3E79B4C831CFC35" + }, + { + "Name": "SUB A, D", + "HexInstructions": "92", + "CpuHash": "FD73CA351A15FCF7421DA1BA1F1948715F6473A333D1D4252237601CCD690184", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CF0AF5554352B06E56003DE3AE2010EC552297B354E23D28B9CCDA8DFB144661" + }, + { + "Name": "SUB A, E", + "HexInstructions": "93", + "CpuHash": "FBBDEAACEE5DC09B0AB382FCEAEA94FA6834E9C71A3217FEBD8E3EB10FC29B8E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C23DB5956AB5213FF6C080B45619AA53A7D13C5957631F18E24DDACDC30E3815" + }, + { + "Name": "SUB A, H", + "HexInstructions": "94", + "CpuHash": "EF05BE4020F9225F22A190398F268AD4CD95D3D5931733D82F932D8786532B9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E93564B966715FBD96169999013CB990B25C0C4A00ECF5703070CCCF9CBEF8B2" + }, + { + "Name": "SUB A, L", + "HexInstructions": "95", + "CpuHash": "374E14E0A2B2BC0E8E70847D4EFA25E53F62A3B05CDF9D433DF208E3299CD75E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8E036D181DBE5CE2A96B5303BD32EB2B3302E5562336C78C38FF1FF385CB93FA" + }, + { + "Name": "SUB A, N", + "HexInstructions": "D6", + "CpuHash": "88541F60384DCC81F5E9E9B219735ADEF8CAAF48650AE60F6D06651118641D78", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0D501622888E46462F2009F9AB65313D33778C86F9769EB9FAFC3A77356698D3" + }, + { + "Name": "SUB (HL)", + "HexInstructions": "96", + "CpuHash": "7191664FEEF42C8642CCB1F197EC35CBD2242886616550C639B91942BA2A239D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4173E7F9DF019C0DE2532EC138FE605031334D0749CBA1815B02E92524702FCD" + }, + { + "Name": "SBC A, A", + "HexInstructions": "9F", + "CpuHash": "06D51CF9D409D83ED31EE44D1BDCDB71D172F3311B5C8BA7F75D5EF72F453058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A37D31E03CB575B6772756C8D392DF658F556BC6DE959CA75EF6B0A2AB8F7FDF" + }, + { + "Name": "SBC A, B", + "HexInstructions": "98", + "CpuHash": "E273F2E708CF52994DE2FB4E4980FD30E9FC495EA6FD0CB02021A55209CBDC5C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2C36E229B877924D31749202411DEBE1040715FA88FE520A3E5B3177641F96CE" + }, + { + "Name": "SBC A, C", + "HexInstructions": "99", + "CpuHash": "E497943288E15B33E82FD7065A9B62D5B907389B14D8AECD19AC93606EE17FE7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "20372BDD649146169F3A1E18DBF6657ABC819C7B42832C65EB465F165C57A4DE" + }, + { + "Name": "SBC A, D", + "HexInstructions": "9A", + "CpuHash": "FD73CA351A15FCF7421DA1BA1F1948715F6473A333D1D4252237601CCD690184", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9F0324B4EA0AEB885C40A6EE4407A31752B96C4FB6099285EF34A352F5C425F3" + }, + { + "Name": "SBC A, E", + "HexInstructions": "9B", + "CpuHash": "FBBDEAACEE5DC09B0AB382FCEAEA94FA6834E9C71A3217FEBD8E3EB10FC29B8E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "690570525A4DE0B267A761988AB56F930418274E6EB5F374F2709EDE12066EF7" + }, + { + "Name": "SBC A, H", + "HexInstructions": "9C", + "CpuHash": "EF05BE4020F9225F22A190398F268AD4CD95D3D5931733D82F932D8786532B9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "586F8DFC4326AD63F0DE4A64CA600F957B4EDF02CBC64862D8BAFEEEF2EF7441" + }, + { + "Name": "SBC A, L", + "HexInstructions": "9D", + "CpuHash": "374E14E0A2B2BC0E8E70847D4EFA25E53F62A3B05CDF9D433DF208E3299CD75E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EB05C868F5A21A2262E7B651FFD7DCF06000D7A7B3DF3EC3B8DB676BD8C0E9A4" + }, + { + "Name": "SBC A,N", + "HexInstructions": "DE", + "CpuHash": "88541F60384DCC81F5E9E9B219735ADEF8CAAF48650AE60F6D06651118641D78", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BBBFB51ACB601C330DF57BB9A07B42CCBAF2BC883EE97F075D4FBF125FC2A6E9" + }, + { + "Name": "SBC (HL)", + "HexInstructions": "9E", + "CpuHash": "BC97E357BB23E2D3083362B058A2C2437AF67588EC69A93F560F064C6BF2428D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "69202512CACDD2193C55EC131800ACC61D82A93DD2BBA5D3E35990BA62A66F67" + }, + { + "Name": "CP A", + "HexInstructions": "BF", + "CpuHash": "791803C6C3DD8F815900C9870DCB60A1B0CD869422152EC9CD1AF83EB3A2F26A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0AE15F8C70E715198D5C6CE097D2EF9EBF5264CA6E4BA431405775E9E686C696" + }, + { + "Name": "CP B", + "HexInstructions": "B8", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "62E3CCBB27F15E45ACA1D245F02BE43D52DE16B2C4701E357D5FEE295C139747" + }, + { + "Name": "CP C", + "HexInstructions": "B9", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AD625969E992DE9743F3CF387C36B139F7DCDA01ECA8A842B687C7D0C5BBA7BB" + }, + { + "Name": "CP D", + "HexInstructions": "BA", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "214B31C11B1D0ACC942CE80C5D9C96C7547E2A07E801FCCF5A97D480A3567BF4" + }, + { + "Name": "CP E", + "HexInstructions": "BB", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "81D3484C217ACB132CEF29CB849FA214F3CB92B2403F45A5286D1678CDD41C85" + }, + { + "Name": "CP H", + "HexInstructions": "BC", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "86B1365787156EB846BC5E723A69E0C8F6C00445528968ABEC21D6CFA7B523F8" + }, + { + "Name": "CP L", + "HexInstructions": "BD", + "CpuHash": "AD22887E39790EBE2AF9C02D67E6F71233D80861D0FEE2A88E36D1E4BAF00A82", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F06F0B2D5C15B0D441F84E0B25AAC3EE284EED5F941EB4A4324F694ED38A44B2" + }, + { + "Name": "CP (HL)", + "HexInstructions": "BE", + "CpuHash": "1BE9385BCEAFA7E38923ED425D1AC43F6B1AEA17AD576E4880945F284DADABFD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8F88B43FDC56D776C60577859D8B59FB0E657F0A65CE634E4DBC160374B0FD91" + }, + { + "Name": "CP N", + "HexInstructions": "FE", + "CpuHash": "F9E9B579A9A4776FDC6148EE0C50776814CCABFAF79DFBF9A4D90E5579444EC0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BF4A360A9D58525FE0540B610A08B4388E61E74DF9467B34003699215ABE7BA8" + }, + { + "Name": "AND A", + "HexInstructions": "A7", + "CpuHash": "4576B54747DC2451D4CEA695FE4AAAEEEB62631DFCA6CE1790B6A1669CEB0CFD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "79F8A4A2BB6DCA6FEFA42185D931931221EEB2839EB9D0D34D8DD46CDFCC284C" + }, + { + "Name": "AND B", + "HexInstructions": "A0", + "CpuHash": "B414CCD10D15AD1E89CCE30D73683E2F269B751D7C642D6D8C641CC96A7B7E9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DFCE73E35D8013303867B41290D20CC4D83AA2D232CC63BF519D737D0E58779D" + }, + { + "Name": "AND C", + "HexInstructions": "A1", + "CpuHash": "4576B54747DC2451D4CEA695FE4AAAEEEB62631DFCA6CE1790B6A1669CEB0CFD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "234C7BDA7EF222F59E82AE5C4DB7D5E4355E9701B90A92AC88696C8CC782CEAF" + }, + { + "Name": "AND D", + "HexInstructions": "A2", + "CpuHash": "B414CCD10D15AD1E89CCE30D73683E2F269B751D7C642D6D8C641CC96A7B7E9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "139748CF4AFB0026E3A8C03C4C8B2447807EBF58DEBD6841348E7A54983FA053" + }, + { + "Name": "AND E", + "HexInstructions": "A3", + "CpuHash": "4576B54747DC2451D4CEA695FE4AAAEEEB62631DFCA6CE1790B6A1669CEB0CFD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9BDE8C7C809971F58D472D2C240C9C1818A56B8BC8B3E89588DA5E2E4B74B1EF" + }, + { + "Name": "AND H", + "HexInstructions": "A4", + "CpuHash": "B414CCD10D15AD1E89CCE30D73683E2F269B751D7C642D6D8C641CC96A7B7E9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FFE81AA56CAD0C759BFFD803846541FA78EF7E43617491E88647ADE1D6BB7AC9" + }, + { + "Name": "AND L", + "HexInstructions": "A5", + "CpuHash": "4576B54747DC2451D4CEA695FE4AAAEEEB62631DFCA6CE1790B6A1669CEB0CFD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "39F624AD3997C12FA9A4434FC7C4A11C5066665BA43EFE4F38CAEA2942C67B73" + }, + { + "Name": "AND N", + "HexInstructions": "E6", + "CpuHash": "C2EF35002C8527CADE72D4D95B329B6C90207D142564A82D600D45D8231FC666", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D6B54216E5015E8EDA865F07548CBF1DBE2597609E04E82B2C33352AD94CE6BB" + }, + { + "Name": "AND (HL)", + "HexInstructions": "A6", + "CpuHash": "A383577D50E9CF3625254FB1BEAB02A258AD7AB8E9321D1810C5749F2DCED126", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DDAAE0125FC4AE7D2C8316D17B260FDFD767E6788B6A7A17C348EDED665DE728" + }, + { + "Name": "OR A", + "HexInstructions": "B7", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E131DB156A26E342BED21AE424B7C424AC2EEE5C002971DD53AFB9AFF9A9D284" + }, + { + "Name": "OR B", + "HexInstructions": "B0", + "CpuHash": "66BB4FA1E05032D2A9B4AEBAE282D5A2EAB75DEE6B0A521DB675E6471F23F18B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EF3BDCAAB6CAC0CF5A333FCF7F6740E43F822058ED4C843158DBBF01B463F5CC" + }, + { + "Name": "OR C", + "HexInstructions": "B1", + "CpuHash": "66BB4FA1E05032D2A9B4AEBAE282D5A2EAB75DEE6B0A521DB675E6471F23F18B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1DD6BF57F0CD1420ADBC3327C7001A8719A059AA6877205F0CBBAB0E3A748A5A" + }, + { + "Name": "OR D", + "HexInstructions": "B2", + "CpuHash": "6EC38C958F712DE274D17CE918A63A6FEDD48AF7AE24A77560FBD42FF499726C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "28925602F7A5D42D32CB1B2F39EC41A4DC3D0DE93DF4A0760EA4D6B2E00C5900" + }, + { + "Name": "OR E", + "HexInstructions": "B3", + "CpuHash": "6EC38C958F712DE274D17CE918A63A6FEDD48AF7AE24A77560FBD42FF499726C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "113B960D9819329CE34783C501308891EC01AEEFB072FD297E13B363593C7BD4" + }, + { + "Name": "OR H", + "HexInstructions": "B4", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "726F99E712B53A844496FE43B4114BF2DD0EF6803EE39D6B779BDFA7DA23EC88" + }, + { + "Name": "OR L", + "HexInstructions": "B5", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B257CEF0385D80BF6C65E84403C0C96B68EADAF15C4FBA1C4FE62C1C9223F301" + }, + { + "Name": "OR N", + "HexInstructions": "F6", + "CpuHash": "8A3E6702E215D45438BF9A4FA65DB20DE2B237F5BA4434D45992B0B152C2604D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A2F33D2C2054E28F4C722F81056B0623B7D4DD547CAC74AFB6EBD8D989679649" + }, + { + "Name": "OR (HL)", + "HexInstructions": "B6", + "CpuHash": "449A3E6E3000AAFCAF05A2ED214BC579DA4E51F25144347E570FB033D73BEE25", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C1BB4A709E9E96A3EB5C99BDD8268ECAE7BD44380908B71E3D787BD697092212" + }, + { + "Name": "XOR A", + "HexInstructions": "AF", + "CpuHash": "D64AC90077FA10BED5BCEB02ABFC8542564FA0BCFE8A38C977F8FBCC4405856C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E1A47C1D6334CCD22F002C7EA1509448A0B34E173487CF7DA22B82D8CAE873E5" + }, + { + "Name": "XOR B", + "HexInstructions": "A8", + "CpuHash": "66BB4FA1E05032D2A9B4AEBAE282D5A2EAB75DEE6B0A521DB675E6471F23F18B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8DCB32D8D66DD2E397B511800E44B4B748D7EF73B8071D6551301F6117120D34" + }, + { + "Name": "XOR C", + "HexInstructions": "A9", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "233EF9E6D6C43525B88C5B5901A62DD1C190622804EEDAB1F5E0EDF7B9C9DC76" + }, + { + "Name": "XOR D", + "HexInstructions": "AA", + "CpuHash": "6EC38C958F712DE274D17CE918A63A6FEDD48AF7AE24A77560FBD42FF499726C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2CB76E34713AB005BA05D833EAD446BB158F60B5205FD76B15E21D124AB42BA3" + }, + { + "Name": "XOR E", + "HexInstructions": "AB", + "CpuHash": "D25477B8D029F97DBE7BAA68D00E35C78A87EC4D2CC560D833EF5F6D7BCBCB40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "51D2B034BF449F84E425419C93FAA7C02BEBE62354478166D18F07B3BF563153" + }, + { + "Name": "XOR H", + "HexInstructions": "AC", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2F57C4E4E3269C2FF2C31C3DF237C7F259AE835771CC280B1A205305D74BB6F4" + }, + { + "Name": "XOR L", + "HexInstructions": "AD", + "CpuHash": "C12AD3C58985871B152C9ACFA71C6FDAF163186D798AFDA3386DDE0CCA77ACF7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BA3C9F1906B75E9E42CE7385962BE23E3301296B58C4AA4EC657C7406230BA4B" + }, + { + "Name": "XOR N", + "HexInstructions": "EE", + "CpuHash": "8A3E6702E215D45438BF9A4FA65DB20DE2B237F5BA4434D45992B0B152C2604D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4F1B4DE9772C0239E2DECA22436A8F93CB0D41E3AE14C788C891D57807E52931" + }, + { + "Name": "XOR (HL)", + "HexInstructions": "AE", + "CpuHash": "449A3E6E3000AAFCAF05A2ED214BC579DA4E51F25144347E570FB033D73BEE25", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "33645618D120A447FC71D737198597C0F322E31165C009A30A7594ABB754385B" + }, + { + "Name": "INC A", + "HexInstructions": "3C", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BD9575496626053F817C211557E71FFA7E54FFFECDD2D98CF4E3DA57A5A522B1" + }, + { + "Name": "INC B", + "HexInstructions": "04", + "CpuHash": "B5171B4DD56B04BFEABE8B9E7AA7643F64EF157EC49E6678DA83D3C43A4137B5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1503878CBC3520CDF896C0C9EED416374FB5AD23067B8BAF2400D1D7694E9ED3" + }, + { + "Name": "INC C", + "HexInstructions": "0C", + "CpuHash": "E821EF2D4A0D2847C0434EFB1E1DAFF90096203D73D47526A2AD59DA16FA7938", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "99D7229E451C7C4C6E02494BBF90070A2CECB3282B41BAF87B819CBA6391F329" + }, + { + "Name": "INC D", + "HexInstructions": "14", + "CpuHash": "B0F7B337604A68C908805839B8D837709C6D1FD445EE9A06485038FA8900EAFE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D9E3B63774BE5D6620C997388D8CB69B4F22E70DD530F4BADBA7528943EB991F" + }, + { + "Name": "INC E", + "HexInstructions": "1C", + "CpuHash": "F268915394FE8F3D6B55351D77F60AB202C14B8197A0C8C21C03A0A9C295135D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CDF375B609C9CEAB4AEA78B182DDC68D4C04FA6904A898B8B12650B492A9B81F" + }, + { + "Name": "INC H", + "HexInstructions": "24", + "CpuHash": "B1160C20075856E189D14FB65012DF779BD9BC456E7DA8EFE5B3E8A2854BE42A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3D92F7AD4E1D0058DB92414AB6343EDDBCAAA1592C63056B1AA9814AE7364E80" + }, + { + "Name": "INC L", + "HexInstructions": "2C", + "CpuHash": "3FD0F577140BDEF026A595C36F172E3A6BFCC54B2D6954A13A40914A54C79A45", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9304AF53242F84F9EF6B20F7176E18F240BABA554CA925CA27CF929BC6A335AC" + }, + { + "Name": "INC BC", + "HexInstructions": "03", + "CpuHash": "5F8FB3498F50F814A9B1007094F0BA33077BBA91196F7325656D8ED8635BDBDB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FF45164BBA734093B1281F1C68ACA2AFE86BC777C6F9C32F4A6179BF55090A7A" + }, + { + "Name": "INC DE", + "HexInstructions": "13", + "CpuHash": "4792F2B3F2CE659DEE040DFD8D8AE153F2B9D71D8149285BA007A8B8620DB5F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C86FD25666DCA65E92F3678800745FBF9CF4D55098FD50B508B0CE05CA5E1D11" + }, + { + "Name": "INC HL", + "HexInstructions": "23", + "CpuHash": "7300C9138CABB1308FED3E6D3B8F129D6DF666684EE59CDED4E098E89AD1EFDA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A3BCA41227E2E6EDF77FCC55F3C71B0D959A1B87EBDDB7AF57BF2DA2BF3D4C8E" + }, + { + "Name": "INC SP", + "HexInstructions": "33", + "CpuHash": "2356D55643A6B8937B2005979BE13F2EE51FED426F3023D3BC3DF76A52529571", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "111D06F5EB22FB69E67BA266340E11F43BE1F54EBCA379F18062DF578002CB0A" + }, + { + "Name": "INC (HL)", + "HexInstructions": "34", + "CpuHash": "1F85C2F7A8563024688D89D7BBB5A33964F70FBD836CCFD421A8F68BB3A7A30E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D9AE6959D6213511D063526653B4F4FC0FD1FBDA9932E11E749DAE89212FBCB0" + }, + { + "Name": "DEC A", + "HexInstructions": "3D", + "CpuHash": "06D51CF9D409D83ED31EE44D1BDCDB71D172F3311B5C8BA7F75D5EF72F453058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EAC56B44FC12D47DF4B1112674996FDBDFFF259FF7C27DD712D91AE888797380" + }, + { + "Name": "DEC B", + "HexInstructions": "05", + "CpuHash": "6B2E131C244A5ACB8531FEE380E8AEEB02F2F925600563F63B7BB7A69CE15DF2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "51F415C1C3C2D10A4B799648CFB8B9DD516DA75BEE4ACBB2D95E9F922EC8E328" + }, + { + "Name": "DEC C", + "HexInstructions": "0D", + "CpuHash": "48E4AABF7AA555B69F2CD811287795F4479EC80BCB8BB5FD84437B80BAD8A5E1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0115525BEDF21791CD8688965756D4F50D18506A578F38799B1BB050DCF98306" + }, + { + "Name": "DEC D", + "HexInstructions": "15", + "CpuHash": "56688F45B7ED763CEF1A290CCD29363D648C14E339D57DE339F22EE2F8C7BBB5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BABDBE28BD91D7373DA301904D718CC86937D3BD4C9C40C23E042A10E588B317" + }, + { + "Name": "DEC E", + "HexInstructions": "1D", + "CpuHash": "23535D0F62DCDB8096BB9328CEB3375DDBD5A6F3BAEEC2E491284E3384751DE7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "648B0991C01E099999DF051B7DF9E9A06FE19BA6CE2C2F7B949BB364C894808D" + }, + { + "Name": "DEC H", + "HexInstructions": "25", + "CpuHash": "F99948A7965CA956DE4881B047F8A90E1AE0D598A9A48CED30604FC4011327F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6C8ED4009ED4701640FC0F688F2B061F3DED89695CBCC7F9F6D983BBE7255716" + }, + { + "Name": "DEC L", + "HexInstructions": "2D", + "CpuHash": "250BD83F76AD9485BA917EB71A98E81F62B3A8E7F69EF87ED725EF0FD85B9620", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F68685B40C9B6881404FC03EE3B28293695A17F7B18C112D07B6201E5B872A11" + }, + { + "Name": "DEC BC", + "HexInstructions": "0B", + "CpuHash": "01941BE2FC4ABB07C81CA91189DDD8BAA4B5322F1C97102D8EF380EB8B1B9F0D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BF209C12A01A252C272FB52329162F93F1088CF94798A69ADCDA7E422A61ACB0" + }, + { + "Name": "DEC DE", + "HexInstructions": "1B", + "CpuHash": "0F48F5900EF73C22DA2A4A05EB34932D68026D5BFB109E473E8B7B8D5634026E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9C17DB49F9595C4588160904C48CE48C9FB40D48E54A3BD8B2FC179B92D575A3" + }, + { + "Name": "DEC HL", + "HexInstructions": "2B", + "CpuHash": "081EC08E58C891D17F6F942D6A66545E49FE6CB93FF23B799064E7D87671C0AC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6F2D5034F7A97750645414D0991F4835F32A4E442D5C3BF07098ED297153DAF8" + }, + { + "Name": "DEC SP", + "HexInstructions": "3B", + "CpuHash": "6D17ED59FA435B55D70111EA6172F419B4392033897710827CEA649421B6F0DD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C8BAEB8935C229DABFBF02E072B4F8CFB702AA4BF30E29B3B91D4BE1257E36A7" + }, + { + "Name": "DEC (HL)", + "HexInstructions": "35", + "CpuHash": "EDCE6C5A390BDE3209EB83DAF636D91A4D2424711CC9A970292C8555568AEC4C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "76ADB99571FC9C67B0580299D883A65484090011E2B0AAFA85F2FC1C21DF3E19" + }, + { + "Name": "SCF", + "HexInstructions": "37", + "CpuHash": "847B5D7E1014627F6F3A45125DE0C66BB96E3CF38300C4EE336531AC8970DA0A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E1FBAC37738A788F3C5ED94F2E89CC672410BE5E20DE7571790882234C69B3F4" + }, + { + "Name": "CCF", + "HexInstructions": "3F", + "CpuHash": "847B5D7E1014627F6F3A45125DE0C66BB96E3CF38300C4EE336531AC8970DA0A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1684C785C0B81FF99C8A7EF8E0B11554B8F716A8A0320FFA9A5458D7A50F2B8B" + }, + { + "Name": "DAA", + "HexInstructions": "27", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "449A1701FA8195B46504317295BD5C5705DDD47E176EBF692F6F31B228163A26" + }, + { + "Name": "CPL", + "HexInstructions": "2F", + "CpuHash": "DB1133BD9A41CAB8789AA3FED908974C98C7186FEA2FA18BE518E0CF575571C5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B774F114CC67E77A9F0D4D387F7355E07CB592CB3EF8350549EC6940B6396FB0" + }, + { + "Name": "RLA", + "HexInstructions": "17", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7F5A54BC7EC011737DF55FE972894E46EEF865335AE5866CB5F8A3719F6B8881" + }, + { + "Name": "RLCA", + "HexInstructions": "07", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "47F37E81DF379DB41E92D9862C035B71B5A63EEFFE0CB05E93927904D72CDBB3" + }, + { + "Name": "RRA", + "HexInstructions": "1F", + "CpuHash": "BE5A48CCEA979042E41FB0523E0110E98982514071FF4C933771822ABC6D45AA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F554E3E603AE81C82407A787616CC97F172B7E27E56AE55ADA39EE66E86DC581" + }, + { + "Name": "RRCA", + "HexInstructions": "0F", + "CpuHash": "1C73D147560EE6805755A15AE0D78E6C615B84E78C72DF2BBFD4DB17A8433B14", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5782629991A102CE83B244835AF44C4B7BA5DC3C9565216E0352230BD263AA32" + }, + { + "Name": "LD A,r", + "HexInstructions": "78", + "CpuHash": "42EAB082F0030B7E89CB62BD84AF91CB63CD670665AF40E479330D415B22E517", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AB9D5B529EF355F54CA15DA4D4E72A6F84D3EC1EE92CA58A7F0AAB7B7D22BCBC" + }, + { + "Name": "LD A,r", + "HexInstructions": "79", + "CpuHash": "6EE9F45E4B20C569A8E2A27E10D2E3A8A579A443DD30826322891C7FEEDFCC9C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "627A7149B13C765310AF84B5737D4BAC3ECE414FF4DA4B10AFC2B42837FA6EC3" + }, + { + "Name": "LD A,r", + "HexInstructions": "7A", + "CpuHash": "D25477B8D029F97DBE7BAA68D00E35C78A87EC4D2CC560D833EF5F6D7BCBCB40", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ED3C18895D6F8CB60F6FB9AE3DD72DD596BC5C8C55E0EE625EB59C7C3BA1A1AC" + }, + { + "Name": "LD A,r", + "HexInstructions": "7B", + "CpuHash": "6FBE8DFB94C9D80551AF2D6ED5299C1413B125A9EB2CAE093272EA3DE82B322D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EF07FED46E51A3AC145EFA7D89EBE792232EA7AB4F77F6A578612D7E9FBA7EAC" + }, + { + "Name": "LD A,r", + "HexInstructions": "7C", + "CpuHash": "1C86F933012876E9122B20CB0D4CF474C36289693F808117D7A347C520D92B26", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "237397AAE9D070DF7FD48D8508D26AFFFF5B89E06347FCE832B43D49D8F99F74" + }, + { + "Name": "LD A,r", + "HexInstructions": "7D", + "CpuHash": "DDAA4C0D62FF66BB6ED15BC2302AB199304B36A533218986AE70457B85B0A73D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6F00A5C010DBFDF039E432696C965315105AE0512E08F044B74D977657C0F782" + }, + { + "Name": "LD A,r", + "HexInstructions": "7E", + "CpuHash": "81A1F2B9D8943F06984DB1A7E2503ABF663998D5D35ECE23A1AB61FE948DFDCB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C17F9F1E2EBB8D6661352AC25A16AAB8D5C53554B2D704BDFF5C4174543E2185" + }, + { + "Name": "LD A,r", + "HexInstructions": "7F", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "557F19BCC84C2D373855344BA1F3D14239C98F36B223AC96F6A88D2F1489B624" + }, + { + "Name": "LD B,r", + "HexInstructions": "40", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EF3115559B6272F17D843B0D43C889B30E5A8111791F16FF1A4769BBBC920BE5" + }, + { + "Name": "LD B,r", + "HexInstructions": "41", + "CpuHash": "B5171B4DD56B04BFEABE8B9E7AA7643F64EF157EC49E6678DA83D3C43A4137B5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9B12502783277C9EF47CB23D0704D4C6DC2C3ECEF30FEA11162DCAE8D0FAC2C7" + }, + { + "Name": "LD B,r", + "HexInstructions": "42", + "CpuHash": "F6DD492140A055310C14551D89954A82DACD7663BA9DB0CE6C5FCBE250192038", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AFA548D83C23F6EF639817FBD45788FDFFF7E1F7288FF17CEA1F8FA8064C2022" + }, + { + "Name": "LD B,r", + "HexInstructions": "43", + "CpuHash": "44FFED5DAD2176D6F65F7B9704D5941E950527C94B4A0C907208EF5ED0BA28E7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F45B1D8DA7314E2F92C781EE1F634FC7D43D841C58D2F5A1472991DED03672D3" + }, + { + "Name": "LD B,r", + "HexInstructions": "44", + "CpuHash": "373E10DA6F04D7B33E970D8D85ABAD12513B3EFFE9A254653FB37AED3BBF4C3E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0E4D9052CF42FC031B9EB8771916B2C87B303008A2F3267A026C6CC26CC46F54" + }, + { + "Name": "LD B,r", + "HexInstructions": "45", + "CpuHash": "AE38E8722969CE8485EBC8ABF2782959794F04BD9BF5DF12C1321EA605E2EF70", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B86A5C73FACD85E9473CE429695949483E0499BAFE57A8864CF7ECB483CB0494" + }, + { + "Name": "LD B,r", + "HexInstructions": "46", + "CpuHash": "C79705BA7CAA8E3A34A5316D97D23945E8E5CE82DBA66376FEE8F2F7FF97C43D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0443F6CC36C3958F696B97A4A36A4D3197485BEF635983DEE12E3753BC133435" + }, + { + "Name": "LD B,r", + "HexInstructions": "47", + "CpuHash": "921876FFB7B4F7672AB669E8F60E61BE68D8E5CD060177D6A1D902BA3A56D69F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "56C163CAD72ED9CBCAAAF4B392229B3F23A1CC9D355351CFA8F5E7ACA6D79C41" + }, + { + "Name": "LD C,r", + "HexInstructions": "48", + "CpuHash": "EF90EAB030CF920A6B6A5ABD6109FADAEABA6D83D3DA15672B9CCF3BA319D244", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B11125C8D7798BBA19F425870853C61E0017783298C66FFE338018C8DCB9D988" + }, + { + "Name": "LD C,r", + "HexInstructions": "49", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "58751CC5D9F5DA53D7DD0731719F87C41F3EF441F5264E538E7D3AD8B33A6781" + }, + { + "Name": "LD C,r", + "HexInstructions": "4A", + "CpuHash": "E821EF2D4A0D2847C0434EFB1E1DAFF90096203D73D47526A2AD59DA16FA7938", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AA97776FE607892585DBA0618DEFE038DF635335E9EEE988FAB5A377F5A05513" + }, + { + "Name": "LD C,r", + "HexInstructions": "4B", + "CpuHash": "DE743BC0709BC356DA2B5CE5C9A3B20B4A1F7DD8B3C02B205FB0DFBC6CC2A46F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "853B671DE23B32C1D70D7765796163BFBFD16B0430D3C152A601F84144EDDCFB" + }, + { + "Name": "LD C,r", + "HexInstructions": "4C", + "CpuHash": "74436664127722451A610870B6FA5E70D4A56179E4FBEA7551667A0B06985BEB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5C828D43FD43D22841738349FB33CBD780B1F625824316C0A21B556326F9D1CA" + }, + { + "Name": "LD C,r", + "HexInstructions": "4D", + "CpuHash": "BD0EEC404ABF1FB4856B9E848376348CA5E23E9A6BA79647592FE53F0C564A8C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "569313A6C1A449591902113A8A95C843060BAC7C9E35B1D7AD264BE30719C4D3" + }, + { + "Name": "LD C,r", + "HexInstructions": "4E", + "CpuHash": "C9672B2A739B424DF2D96F158EDDE809DC104C0360960273506112239E0596C7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FBDA9F88D2E9B83ECD3B0B98BC788E04DE22EDA2BE91642AC01DC512C76E94F8" + }, + { + "Name": "LD C,r", + "HexInstructions": "4F", + "CpuHash": "5DFBB4966CDF038D9076492CF0B1E6DE3F1A7BDE2F221E6472E7EA8D866AA675", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "11E60EAE380BE48CDB8E645D22B3998B412BB3B45D0BD536350BC5AEBAB43426" + }, + { + "Name": "LD D,r", + "HexInstructions": "50", + "CpuHash": "1DE77F74C594D042FEB147F9A7E606641D6023E4F057368ABEA00CBBFD0BAE0F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5B5975DF1C66BCDABCD88A57FCAEC0B1DF5086F6F8146917C5FD71E9C90863A7" + }, + { + "Name": "LD D,r", + "HexInstructions": "51", + "CpuHash": "DA1BB6E80983311F5B92E3F1286BD9EBF6A2572DBCF5250CB8938D0BB24D3023", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7A15FFC3B6215F040901C1D443373CA5CD60E6ECD0EF611A4FF671D4F509A3CC" + }, + { + "Name": "LD D,r", + "HexInstructions": "52", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E11C7BF8D8FB4527F2812E1715A46492241C3D784D15B5D73006FA1483495BF2" + }, + { + "Name": "LD D,r", + "HexInstructions": "53", + "CpuHash": "B0F7B337604A68C908805839B8D837709C6D1FD445EE9A06485038FA8900EAFE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FD9186E0120242A13E6DEDDBA3C577186AAC3E5922801B0DE78B73CA1328E587" + }, + { + "Name": "LD D,r", + "HexInstructions": "54", + "CpuHash": "078A60A945C3C1CA2F88086DEB5003406C5FBB38217F0B7BB0D28D2B7DF8B942", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C34EF17439AD942D3F1DE8A0A3054B2C2113B382F13B9F6816E0AC7F4885C039" + }, + { + "Name": "LD D,r", + "HexInstructions": "55", + "CpuHash": "6CD346D28B08184F411DBF3E5A17DF104CF6F2E01EE3491A11F7582E9A7B0BC8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4BE5AD33AD47B0599C31A300CDE423FC786755A72F2AFBB1A0534AD50A5A3761" + }, + { + "Name": "LD D,r", + "HexInstructions": "56", + "CpuHash": "C58BA58DDBCBFBC35F541ED4FEC4A954DCCB510F624D0418F202A1ADCCCA6945", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "161552B33ABFBC61888CB7C095DAD509F21CCE929153B2CF9CA591F9107016F2" + }, + { + "Name": "LD D,r", + "HexInstructions": "57", + "CpuHash": "627FAB5C6882BC16E46528F118DC76B7C9E6DE3BF729E67AC04AAB9D92DBB7A8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "01763BBB8EFE26CEB28F1612BC54C5E4A6A616A6694BBB3330D43A8C1DBD96B5" + }, + { + "Name": "LD E,r", + "HexInstructions": "58", + "CpuHash": "42577E288B1EA92056EDD2A8419B70DB9FC39883835830A4A355C09A6666CB0B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DF2CE8585C183A8CB90F880AC103B044BD8448CF8F7CBD356D6CE801BB37804B" + }, + { + "Name": "LD E,r", + "HexInstructions": "59", + "CpuHash": "BB5DE19421AC0D5F6FDF0623727DAAB884A91A10922D69DD6115CA7DC4622717", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "409DDDB9F13F5ED9FC39C0EE363C8C2B3A4EE27D7CF754368344DD57928DC9E2" + }, + { + "Name": "LD E,r", + "HexInstructions": "5A", + "CpuHash": "30D35795688ABDD0C5906E23F850E2F6AF1447CAE59C636B07E07DCE5ADFBA89", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C29EC4D572472D20482FE55CECF677DF444822D9FCA3872D336E32FDD86AFA50" + }, + { + "Name": "LD E,r", + "HexInstructions": "5B", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "50BF9DFEDE74EF161DA04381EEBD760AE0A0C75E3F609C2825977D79BFDE4DEB" + }, + { + "Name": "LD E,r", + "HexInstructions": "5C", + "CpuHash": "F268915394FE8F3D6B55351D77F60AB202C14B8197A0C8C21C03A0A9C295135D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "177239CF929A67D4BCEBEA0A531489443C592B5A7BB090ED1BF15F08E015EC37" + }, + { + "Name": "LD E,r", + "HexInstructions": "5D", + "CpuHash": "D4BD0371417B1DB9B91C4F15F9DC23D51CD64F717FE482352C5FDC3A010F8EBB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4C32E0ED95F8C6AC2142F1A6426214274F734C24EDE36CF5992ADBE9FC3674A9" + }, + { + "Name": "LD E,r", + "HexInstructions": "5E", + "CpuHash": "2931B874CF1BDA6CB003DD8A47F0DC2F09791C3CD6BD7EA4B7A9239102049D00", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "47883749A483F5B51B99EDE6D00977316AD649474221F8403FDB4DAA5B2A80AD" + }, + { + "Name": "LD E,r", + "HexInstructions": "5F", + "CpuHash": "BE58C25EFC178D795F9B01BAA888CD5459F208804323F80390BE519F972D8476", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9A0781DB1ADBC20A15CDFCA857295ACED22F181B5207BB24D77FAB55D6712EBF" + }, + { + "Name": "LD H,r", + "HexInstructions": "60", + "CpuHash": "98ED4B9493DB28717ECE02FE96FE2D58496359E2CF3D19D46CEDEF36C1EA9B5F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C700823CBFC7AFCC43909C2A73DF63C4B888BCFF2C0D0F67B08AA7A5F5DA9304" + }, + { + "Name": "LD H,r", + "HexInstructions": "61", + "CpuHash": "1B602136785B2361EE890D0C7F80FAAA42BAC4AD8536456F4DA078F438BD3F84", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FE334D6D7FA0BA1FB7BCB2AAB39169DB2CB2596AF6545C0AAAC21D20E52C2630" + }, + { + "Name": "LD H,r", + "HexInstructions": "62", + "CpuHash": "46ED32D09A8CD7CB38A17C8793A54561622430A39A8C4B819C53B3935D214F1C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "28E27AF567FB6A04E580C381A87C21247B9B2685102C221BD565461C31922D28" + }, + { + "Name": "LD H,r", + "HexInstructions": "63", + "CpuHash": "C2112D4875837896D2472B7E6F11B57861A29FB5258BFF998D5B8D99FF1681BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C4A869A5A4FB95F1A996275B93A6B0B76C5EC7A5E02CCF85824823A8D8730111" + }, + { + "Name": "LD H,r", + "HexInstructions": "64", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CC5B9E71D3DAF01362E340A4EB6DED3C6C3D628B6E509214E7916B0AD3C89256" + }, + { + "Name": "LD H,r", + "HexInstructions": "65", + "CpuHash": "B1160C20075856E189D14FB65012DF779BD9BC456E7DA8EFE5B3E8A2854BE42A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E55D113D95144EAC0D345ACEAB267837EBCDB2DBE6326A59244BBF1FD5A1454A" + }, + { + "Name": "LD H,r", + "HexInstructions": "66", + "CpuHash": "3047DD139AF06084DB3411FB719D5721C0A55614D7C3AFB2D449CBD2C88BCF6D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "72CDF0072B02E316A1D7C534D1A00F77EBA5A02214E0F8275F4157E5DA2C08AB" + }, + { + "Name": "LD H,r", + "HexInstructions": "67", + "CpuHash": "75E351C3882C884799AB8FC9957132631D9EC437A1B012B8BF687E8999C8DF9F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1BC42331DFED308587B216F2E89A2F853C990A13535B6485BF721DD9A38936A7" + }, + { + "Name": "LD L,r", + "HexInstructions": "68", + "CpuHash": "7F4BB6E6B511CBBE0908CC72D950B32404DCD95B20564DF60643492B77969B71", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A3D3F036C282FD2A5E4E7880184082D010A429E29B56464ACF504701DADA3381" + }, + { + "Name": "LD L,r", + "HexInstructions": "69", + "CpuHash": "15A4935A2F98462A7893F0E7FAF70817C5E9EEEAC4AF614406F0FC6B5A1E425E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C1BCD9E326D893BE77E99E59FA6BF33150B83FBA7D42250DB35873DE95C599CD" + }, + { + "Name": "LD L,r", + "HexInstructions": "6A", + "CpuHash": "7C9A57A252F6A87312A09B505A336039C6913F2002C3664254AB9140517CBB2A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B9FA126A5C25EE08C55A9F0B4547C20A5A05BA21CEE11CB7A4CD7284D6BB0093" + }, + { + "Name": "LD L,r", + "HexInstructions": "6B", + "CpuHash": "34F3663DA4822D2C979E567E801B9227DC136ED5DADD4D33D5F585C9687ED916", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F8D97A24F9539893240D7B18F54AC57A082540514FCBAF77D544A20CB957E960" + }, + { + "Name": "LD L,r", + "HexInstructions": "6C", + "CpuHash": "2CADA83784EC61E874C3B4665CA03BCAFA3CA8AE199B6599B2FA9AFB0CDA0F4C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AC8B23C734026709CF23240C67A7C9478FBA1B42143958203C313119546DCCAB" + }, + { + "Name": "LD L,r", + "HexInstructions": "6D", + "CpuHash": "BD7FB413508DB6D3C02EB42452A78035C6562AF09419C7B124728098C9158B23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F4F709F515B750096B1EA548CF5D528D27F3E3168D2EF1A5981D017D4D1EA66E" + }, + { + "Name": "LD L,r", + "HexInstructions": "6E", + "CpuHash": "EB61F491FC47522E71B0A5F6234CD04CB10B6A939D670776F9540A1046BA05DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2E41F429CF942192BBE38B35637C4CCAA5FC027C5EE6CBF63BBAE063B570E409" + }, + { + "Name": "LD L,r", + "HexInstructions": "6F", + "CpuHash": "47E831610EEBD1BFD1552CF1FFF5E15B60C07C088E443DFC20415388BF876B93", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9C99E77E23A5EDDD89F49FDA19AF5398C2D74E55D433C1E4CA7A08C739EF9127" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "70", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "23F48EED41E164D8B85F91DDA52D9840870DC6D81837B8407E9B6376879D1AE0" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "71", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A0D45DCBC41C47D3B252B9D127FD044566C32FB670C1EDF4A7BBB8E344DD2419" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "72", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "74786F9B54ADB7E5AA7E6FCE779EFFA420CB70CC4FE1E394F0C8B89A35B4FA61" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "73", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A7C670528752B9C1540B8DAEDAE34C41D75AF73CFDDEB941216A5F4A77FE082C" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "74", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "48EFF7B1BC22A4EE64FBCE4ED1D400189DE27E39D50C10717D429DD1ABEB0375" + }, + { + "Name": "LD (HL),r", + "HexInstructions": "75", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0D33E9D265B85AFE3D315AE98B0A000F16BF4993C3F8E40C237298E362A435CA" + }, + { + "Name": "LD (HL),A", + "HexInstructions": "77", + "CpuHash": "17805344436BF57D6F729B28F29A4BA39556BC3A694BE6EBBB06F851C74BE2DB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EB0791F68087652D253242631F65C03C2FF7F62B60998ADCE095A4288FCED47D" + }, + { + "Name": "LD A,(BC)", + "HexInstructions": "0A", + "CpuHash": "81A1F2B9D8943F06984DB1A7E2503ABF663998D5D35ECE23A1AB61FE948DFDCB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BBA88B3E66197B4B2CCB3ED62C15F7041EE5CEBF265C001C2FFB71D82405D132" + }, + { + "Name": "LD A,(DE)", + "HexInstructions": "1A", + "CpuHash": "81A1F2B9D8943F06984DB1A7E2503ABF663998D5D35ECE23A1AB61FE948DFDCB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "21E52F72DF69489EF1C431A01A980BCD6189CCB6F7EB15300FF09A8C7AAF12AD" + }, + { + "Name": "LD (BC),A", + "HexInstructions": "02", + "CpuHash": "E3F4F1D42AFC04573019DCA6C489460A1E78B288B58F5093CD875EC08DA32FB3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "23A9D076CD8C9D2BA265B74B03151EE4B5962F9A390EE9FC7B2DAFCE96C57AD0" + }, + { + "Name": "LD (DE),A", + "HexInstructions": "12", + "CpuHash": "E3F4F1D42AFC04573019DCA6C489460A1E78B288B58F5093CD875EC08DA32FB3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "41B45B3A242D700CFCA2BCAEB2E962C15ED475834645E6DDBBC0E5622739C9D3" + }, + { + "Name": "LD SP,HL", + "HexInstructions": "F9", + "CpuHash": "CFFCC3AEEBE3AA1E16E2BF4595AB050D80C1034E052EB82653A40BBC9567B37C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C0E6C758E223B6BFA2EA6123F06794B27AC14435E99BD56D2D2D47FAE51BD5E8" + }, + { + "Name": "LD A,N", + "HexInstructions": "3E", + "CpuHash": "B6C25249E024554512799B7E3F28F69B84F6C6C438B384FAB555B3AB7FBE122B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A0E52AF093AEEF8B73B59D06C2E0F7415B5B73D2745367E88C1226CA3090217C" + }, + { + "Name": "LD B,N", + "HexInstructions": "06", + "CpuHash": "D41435744222A06D776E908ADB57CFC3D94C2F94675413109FCE1CE047E66ADE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "65ECB6574CDE8B11A6DCC8FAB60F1E5ECC9900F7679DD1AD163D9F91DF5DD757" + }, + { + "Name": "LD C,N", + "HexInstructions": "0E", + "CpuHash": "171B68BB9FA7DC5CD853A3346F12C476F447F0E9ECE73C3FE72B97CDEBDBD400", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "68F3F3E463E54CF3585F7DB8F1ADAF9F7FBF900214C190724ADD1E9AA112F430" + }, + { + "Name": "LD D,N", + "HexInstructions": "16", + "CpuHash": "61330212E5B7731B43F1B979B08D0D2DF02F10F9997E25943BA8955E5C2CB677", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6AEAD025F16B1D603A54EA5CDEDDA6D2CBB90BCF5E0D182A3BA529772B7BC882" + }, + { + "Name": "LD E,N", + "HexInstructions": "1E", + "CpuHash": "3F7DE89E8C4D3E285A15FBB753D0F4611A3661F32560D5A3207A78FD798DF62A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9C937A8E53495BBE41024DA4EBF3333F731C82EC0DA1670EFF3701FF424FA0B5" + }, + { + "Name": "LD H,N", + "HexInstructions": "26", + "CpuHash": "EA8FA15426F74CEC1CCC197B5EBB8C8E944596F333279AA9B09E6879E377365B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D51EFA87818193178ACB60B7B0C7326082DFCC1AA6A99B5D9B10C5313CE3D2B2" + }, + { + "Name": "LD L,N", + "HexInstructions": "2E", + "CpuHash": "CF7F6A51E5B2D68E2EB051201A80746078E5B65B773A2F39DB60724FC2C4C40D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "02CF2335A148ABAD7289239F9272372C80C54B7F97F9B57D574A4BEC56BA28AF" + }, + { + "Name": "LD (HL),N", + "HexInstructions": "36", + "CpuHash": "4CEBA6BCF6A25B154C42CA482474FFF2ABBB10221B64590618E5C447563F1759", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "66BC7B2B54CC3778A38CD8D0447BC8662DA30FB7C1C3D0B640D741DCA1A9E14B" + }, + { + "Name": "LD A,(NN)", + "HexInstructions": "3A", + "CpuHash": "56F1C25C55ADB1F12472742EAF450DBFAE8F979FC35156ABB4DE4BFFB1B07BB9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DFC255A54EF042B00CC0F799C52B3322053E744B7D56FE53AB0F583881B5506B" + }, + { + "Name": "LD HL,(NN)", + "HexInstructions": "2A", + "CpuHash": "C2A41AAF6085A0D9D5D7E9E571B381FB8221375E2E0EB0A72165CC5153BDF1A6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DF327920E4FCA08658F6D3C00394A606BC3C0F3CCD6DF569D2B25F52DA36683A" + }, + { + "Name": "LD BC,NN", + "HexInstructions": "01", + "CpuHash": "4EAB4C4B5D4EA3798BBA331506B486F2EF9A0E9AB5873285AAE753BF17CF6160", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DAF9614730795A22DA5A18FC2609B23AFB99B3C45FECB6C73649D07092F6A79C" + }, + { + "Name": "LD DE,NN", + "HexInstructions": "11", + "CpuHash": "29DDBF5B53CBA58655684411613FA2CACC75E64B4BAFD46CAC393A42377DE12C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "435CE0E3EB8836691FB4ACFDE50DC818A1743F2C613EAA29C9FBB8F143A4735D" + }, + { + "Name": "LD HL,NN", + "HexInstructions": "21", + "CpuHash": "4AF21F38245B2C850D647A7C5C5F6139BEAF5659702D36E14C172A0420103F49", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3C6B7F5EDAF745BDCCDE3AAFF15AA9B0E4D5030F7E09AEE205E3F43B7679A601" + }, + { + "Name": "LD SP,NN", + "HexInstructions": "31", + "CpuHash": "823323F0C0D32FD8C5FB9E8A7A05AFE8B12D4CA964DDEAAF96C9A7A008A61342", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "99262101E1DC05BD542F4BC64C74538EBBBB369E07A13D4886B01EF34F9C2CBB" + }, + { + "Name": "LD (NN),A", + "HexInstructions": "32", + "CpuHash": "EE8E6A641623671BD7533851BEF9DFA884D9810BB206DB99C776B95C7AE404FD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9CA5AE8DC21DF10FFAA330A435E9B8F4179ABCFD229FC4524124522F92464998" + }, + { + "Name": "LD (NN),HL", + "HexInstructions": "22", + "CpuHash": "E0C1223E5A540EE16575711C2A73186215809658E7AD137C3BA2772730450BF8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5CBDEF6538FCA50BC6D4F83F796934A2C6C29F541588446D0D8BF534EFD6F840" + }, + { + "Name": "PUSH AF", + "HexInstructions": "F5", + "CpuHash": "92D6883CC3EA80C01DBF356B7F4F9C1E68DB614A4648E45719ECC5F63FCA0A91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "891D378A4F9853CA7140CE78A7DD6A9ABFB4F8271F1ED8ECF0A688B443C77A7C" + }, + { + "Name": "PUSH BC", + "HexInstructions": "C5", + "CpuHash": "92D6883CC3EA80C01DBF356B7F4F9C1E68DB614A4648E45719ECC5F63FCA0A91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2D51707AE7DC25904A39282D0491900DA6B33101B43A42D5BE6FF89A03D44CC7" + }, + { + "Name": "PUSH DE", + "HexInstructions": "D5", + "CpuHash": "92D6883CC3EA80C01DBF356B7F4F9C1E68DB614A4648E45719ECC5F63FCA0A91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A722010B5E0407BF7B5A338E7500B65DAFBD2453862C07E3FFD5C0DE24FD8CDD" + }, + { + "Name": "PUSH HL", + "HexInstructions": "E5", + "CpuHash": "92D6883CC3EA80C01DBF356B7F4F9C1E68DB614A4648E45719ECC5F63FCA0A91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2289FE520BB5DF02C3A88D4538F8F193DD94D52EF13A75B0CDF6EFDE5EEEDE93" + }, + { + "Name": "POP AF", + "HexInstructions": "F1", + "CpuHash": "1E2EB76D85012FACCB940ABA0ED436342091F8B91DBDA98607067E4DCBC86FC3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FB87CB9C472BA098742CCB103A0CD228923982C3E92F53EF83F7E5D5AECE8C4C" + }, + { + "Name": "POP BC", + "HexInstructions": "C1", + "CpuHash": "F06F295DFB8E9672980F2AF58E91D2036F06A5FB88E991242E3BFBDCD9218461", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E8CE60F2A39DD1D8C2E270F9F653CCA5A6E684ED71DECD6B7E4ED31FBD723B5C" + }, + { + "Name": "POP DE", + "HexInstructions": "D1", + "CpuHash": "E272F39AD097446268D0E4A71D8576004DA667FE07877C8C8769AFD2FD7DC906", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0A22DA9E58FD3D427EADE37DD706139E197B9A30F828A5FC28875FD2CBA92772" + }, + { + "Name": "POP HL", + "HexInstructions": "E1", + "CpuHash": "F8DF9D7B9DB0DB8F39129ED99038210A413480D09C4BB189C88F7DB42804830D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DA94073DF6A0905188E61150B76C577B0BF81BE1F6148B08D83A5032895D53DE" + }, + { + "Name": "EX (SP),HL", + "HexInstructions": "E3", + "CpuHash": "84EEDFD88029E44D3B1206E977B1DF5AC5E2289491DFC226F74A3BCEB0BB9CF7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A6FA386B1E5ADFEC0938B3AC461558E064BD358C636C9892BD683EB499A2AAD9" + }, + { + "Name": "EX AF,AF\u0027", + "HexInstructions": "08", + "CpuHash": "B1E10436026395B2C00FDBD1869E5A4C74A3CC4FDEF8AFBB0577C9668C7174B9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A89E64261BD584097586478EFECF1267AF53C2775D0617BEE5E1F3C66BE1F070" + }, + { + "Name": "EX DE,HL", + "HexInstructions": "EB", + "CpuHash": "5B37465C0C9C4E65074A687E3F38C9EF25D93084605BCC01523EC504467FCE4B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "288F2A105F61199DA2E23E04C353CC84511EEAFBDA4E0D10C1F182F0FB04026F" + }, + { + "Name": "EXX", + "HexInstructions": "D9", + "CpuHash": "382ED9B3A2A63923FE2873751F6C7AB6F2320296D772DC7D6F555CE673F50CA7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "83068AEF770E4B31128547F359296C4F07E69333BD5C0911B641A2D15CFE41F7" + }, + { + "Name": "IN A,(N)", + "HexInstructions": "DB", + "CpuHash": "FA033ED9E297D3C0F05CD04027600871806B52F8CD2DF95952911BB0F06E8CD5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "409291FEF52B46F47E2C89C646BEAF5BAC5B7B731E8BF6CB81B5855D9C2CADA3" + }, + { + "Name": "OUT (N),A", + "HexInstructions": "D3", + "CpuHash": "7C3388A23082B65ADE920DDD3260AF7B974346EC16FDD9A121146A2AFE2F914F", + "IoHash": "2E5A8F1A98BC52B0EEAE6AB06A29F9A7D9DB1C9E578A2AD15A314397DE48A0EE", + "MemoryHash": "3D809CA57BFF8930E0046C4522420BBD20788FC1F4F5279B3106404BD3AED5F8" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,80", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "962B138690301C244428FE5755DA16DBB5B631D8256473D0F3713B3A4B654166" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,81", + "CpuHash": "C517560AE114B95A2C628B765141D0F61B20E8B9D20876CD12C770706794C13B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "83926998E1E2C1B5C34F00ADCCF658D9489A5B7303BE440C898DE477C177DCC2" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,82", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6A2EBDB66CED6ECEFBF3D122E482673A322FDF9BF83C1A1DC04A167A1325BF16" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,83", + "CpuHash": "DD90A7CFFBDFB28EB2258EB6D463A3AFAD95A01E34BF60A7D3AD782C4F12E399", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9158B08FAB487E15973DA5044BAAF75CFE1410F6A082488E580984D468901B0E" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,84", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "90291697A65089E59881643F6BD1D04B34BCD2061D8DD64F5081E1281F5AFF2E" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,85", + "CpuHash": "6682B5B9B32F58E6B607E64D0185EC1386F07486DAE7AA78ACD0EC9300F97BF8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BC161AE130A7CEAA36825D21F3A03A1298A8184FB20E168F18AFB828D6A31FAB" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,86", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "31C2A8029CF2BAF5D75B3E60ED1FED341220F7E97DE7D1D18D8D038645FBBFBB" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,87", + "CpuHash": "8A0D5FD7CD38647E0712226C5B034E732AB9484F50F8B69A23D12E951E6C01F7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "33EBD90F74D202B228999AC6688EA18D89CC311B27BB5F3BB9D57DC181175916" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,88", + "CpuHash": "22E76A1CDD846944576DD96CB49403642B11560EF60BD45E1ACD3C67D51B8CAE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8A015F14E3C96180B2D463504E4E1A7866B434F9EF7BC65BD531610CE74496F1" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,89", + "CpuHash": "89CCACC043AEDD06F952512FA65F3F53D4DE75085DBDEDD2F6C049FAB66EA8BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DF50C9121FDFFC7EE6C72AB50860F5AB90ED360D44E9C88654030BF33525C17F" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8A", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D8EEA671FE3C4BE3BE086ACC2668C8098BA2F18B249353BF05123D315B44D57A" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8B", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E1F2F8F00FD448A2211FE153FEC5769D9D474A08EBB506970A906C78EBD19985" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8C", + "CpuHash": "40BE8AF71D430DDD4AA194AAD3161C5A35071A236DB5868537B092C7D7DB773E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "083526DB8B2124A50C206C3183D2BF31918301B42AB8EA5F7517ADC2E680B7B4" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8D", + "CpuHash": "F89D2E352AAD500536392A587040D04AFC8CC9500E018E5CC59B701A71270C64", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2C2553CFA7DFC36FBF3C2B072EB77F544DD9ED90C21B20EE43DC05271C7344E3" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8E", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9EAD9CC8324E6187D58B37BE7BB03F44AE6141CC9107F146A9715850E810C703" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,8F", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1420CEE9197EE03BB115E95C0A275B1FF59D6A5703F011CDB46CC6E71808BF09" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,90", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3C28F215F3C1B3CBA5E507970A855B58BCDBC9CF6F9870A829985A73934EB32A" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,91", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9468270235FED9732F6AB3B5593220771ED9529C47A225C88C17A1BD1B45F6B3" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,92", + "CpuHash": "0A621C8AD3DDC17E23B1257B3C02DDF75E74F316ED09E0DBC2DE04DD3016C928", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "630C7ADF8F1F26D540DD1B19D1D91FEBDFD8D3693E8BA6EAFA1292D4F8E16E85" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,93", + "CpuHash": "AC47FDAEED24487A70635F0ACA3F5DC55CE5A34AE26030D8DF3DE5F4F705BD2C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E229FBEEA3E5239D1DBE39B01BF3F30C71D64D4676C3B4E0519962CC8847AE27" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,94", + "CpuHash": "2B14AEDA3841F0520DBF5088FBBDAD9D7A49ACC45BDA45F6C72722BFAA18D72C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "48CA1D1B6466EC2C5EF09BA1E06AE1D308019C6255A6A6CA8522AE73C50503B9" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,95", + "CpuHash": "0CDFF22A6852031D34A80989FC6DA8C3D3B5947CE7DF83CDDAA10CCA3CDA6C00", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "867B65222DBD492021E89E01AC8C36025DD7349E018E858921728CDD71A0D861" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,96", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B65AF45D37D509D722809B0D84743DA44CAA349626D3BD4B24A677D7882129EC" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,97", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A4E9DB70CC7E69673AFA564FE46107FBB0F0A89CA347D034B4047ABCC0125E23" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,98", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "540102A00E6BBA54A5B2A6B784BF95D3DA512C85C46D38D1DD8CB8B356AF321E" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,99", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "735F9E8FBAB4D4DB22FA8053ED48E56C6BB6FC9238C4F82761BC93B3DD1E46A5" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9A", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9533A3545103A31CB7E3A16F4B896F7DC7FFD96EA68D70BBB9CF1F30F6F0659D" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9B", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "121A8F43FBAD245B039248F5117E565505D5E7EBE0F1012F322FE4CE7BB0E34F" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9C", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7E255BC6B85C1D6C54861800AC836F2CEDD06B665D9E6CD0EB2C521FFB8852A3" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9D", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3FC5A11AE201C13AF0B7C36772EC318BA4618F569D81FBAD1E39C387B7963FFE" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9E", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7FE5F0B574602D50D804BEEDF8CB8A05A1FE0DE311E3F54AB76709BFA4F1753B" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,9F", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C46595EA2F61CFE77045F5933946038184BFF9440A2889AEFD45D3F8F9B5BAD0" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A0", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "44C3A0368B7C800DF3615D7F24BCC040F6F811D96474555D708A290492BD4AD5" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A1", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F314ABA32DCD93467AD878F3A8FD10AD2A3C79133146BF7D49584659C2FB4208" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A2", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CA9712F5C4E1D6553DFD8B06808D72E30B1F3ED66DF5ABEC85A55E069BC253BB" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A3", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EF9235457DA90CDD04EA6451D1ED9142B8D11FB05BEB43136096CD5DD40D7449" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A4", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "71EBD863587D1E73B029AE607F2EDAD55ECB195BD56911F3E68BEEF7C2C67654" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A5", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AB48D24530E3CF4F1BB925D415550467C4D06056A7C7E4B50A132E0EA00958F2" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "93CC4C68BB6A4182AD874E774736D203821B2A48B498A7984EA060D706123C29" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A7", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DD4159B11B54F86B568AD5D40B89BDCED60DE833C5A9D9160DBEB73EA43E94F3" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A8", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2C93EBF3286B5A0F0817D7DD0FDEB2FCB25DB3D33BD496EE04ABFB29506B261D" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,A9", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "505F353FBCAA4A6FB69C7D7C37578D518587731804EC51B34FAAFBE2FBCBD7B2" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AA", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CC865235ADB9800DA8CC425273132FB2F7273CB0CFBBE7F9233A7B83347C13A6" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AB", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "83615C247267DA69180745A9AD15150A85F17388FA09CAE076F4711C6B19A794" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AC", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "04383D26C54ED0C83880BB9DB0D7C3CF8A7DFB5F536A7F3CC7E65B4BDBE71C55" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AD", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B1765ADAD2BCC621730EF44F34AB3424EF075DF14A6BB8D8A9F3ADC0A73D2879" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DF4D8EE5D62171BEE5A753931217CA747F87FDA0355F4B417507E94B79900EE4" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,AF", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "03283A8615D6F63D9A2FCC45ACA0BE9589F9BC49867C04DA085ECE1D97BEBB2B" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B0", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E5BDA064B8E3F4ED812F874BB34E2EBE2AD60E365BAD74F18B6521E89E94CBFD" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B1", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EB488D47727E1CEE6650D80076F7B7E17B8A2301356D3646A303709A16E4AF96" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B2", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "96B3ED56617A9521CEBDCEEE03560A3EB450D700DDD9FEFE2B2E67D047A0F85E" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B3", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "23ED4C639CBA60D9735030597A210C9A6E6AD7BC9849DE9B1A863B0039ACFC0D" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B4", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "682ADF070C77C1B0298F971F4FDF127E93ABE4B9C767548A0B9BF1A42361F713" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B5", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "26E8D44EDB83842F76AF582025C1D900863515BA75A183FC6F155ABFD6A4DDA6" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CCF9D369876344DE685A0AE337D0F98CB4D2239F2CBABA178702BAAB34085DB5" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B7", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AE61FD2EF7E14AFE68ED02A4D893463D757BD7980FDAFAE42B0A192DB4752855" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B8", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FC4845C629971CC7875A47CD37E6EFC3E31922C0F6A42837D85EA20C94AF3935" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,B9", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D4ABA92592C385058E26212FD01A67988F60D7DA61DD2C80F0E704382F427126" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BA", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E3AFBEC8B7214A8C5631407841EDEA195096D770CED72DED3CEBB604413D6413" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BB", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "41217F2E8AF4E8DD308523BE016E8B184A400347F82D78ED2FE8F8DFCA49F282" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BC", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3A5EC94521CCEBA2389A66557C7E17757434F53570E60446C6A0510F02AB766B" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BD", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F17C0E5E3BDEB8FDA6059AABFF33004E6EEE2F64E6D0F96B28CD2A2D2F80BCEC" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8797DA14A0A07458356EC814D5091DE57B0B8F7E7652D34F20167C4E6724B731" + }, + { + "Name": "RES b,r", + "HexInstructions": "CB,BF", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "641BE96FA366348651D196CB4D91A7870ABA9C3DF62B96DC9DDC20EF5EF7CC53" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,40", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BCC815F8F296A0DFC66D919AFE92F5576F70CAE6C6D70D0E1D9945842BEF6196" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,41", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CD39D3D5C0718F3277047131110F5C1FA52580FEA34F5E7996B195570420D2E4" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,42", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "030850C388A3B918371E7FEAEC7A9564AD19D3EA9A7465FBD62658D694401D70" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,43", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C4914B63F8596163190C38661DDB15E1812F93DCC0A76BE1697D0BF7594E4230" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,44", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "078740B3494FE7C189A78559703A7D63102F47DA04575E89C45EFB99B9C94D57" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,45", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "78B17A8DF4085EF0216A413F047802C7B645B482AE3F1835F8D71650E3A498E1" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,46", + "CpuHash": "E59166B24297D48DE82DE2C993F96ECD3D19ADADC4850D68E7A374041F8548F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9B6039475E88BC48666597B22E02C512B98A20FEFF88F6EA52C3591BA44FD84D" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,47", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E5CC70192597523459BF3F258E0F04A86BF49E5AC94DFBEB552B8AE2F05929A4" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,48", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D64B302009E0C8C199A5BF6752D51B8D1F4C5EB34A19F05593CF1E28C6011A98" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,49", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "724DB56D3BCC155A06B7636429B7FCBF559D60667ED0508D7C670A0D0E646BD6" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4A", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4DCE7A2218468C2129D7D2DFF63706BF48EA6A9EF7C9EEA9CA4AFA4D3884E642" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4B", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "60B7A4981646D04793C23255D7ED10E6453DBFD1BE44A9D3A0EDB308C52AD2C6" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4C", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8A8802EA5CDA30B243B4C30C42DD73984792C9EC17B0A561114900DB1C316AAF" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4D", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F55224171940857B5EBDF4FA6A496E002574A1720672FE414E5D51665705C845" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4E", + "CpuHash": "4C0FF7B0D07955D22CF940AE019A6923F773ED4A56A1507A377469D8556C533F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "47EBB459216E6E4E299D8978D2F2A0B622276080BE1043B8FCD48493BDB5181D" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,4F", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F14FDC1724623C5296C114735C01EA1BCBBB48F3174F816B1801F5BB5193D345" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,50", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "836EA7E9C60B1EB096018FB69F5E9A0E53824F2C2A51343FBDDB81D0877CD366" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,51", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1404E5591044406DDA71F7CE88EB9F2C74B0E3914947F7DDE092F25F71CEF687" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,52", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "33349584209EEDB24149A806B23E5FDFB400C38A2DD7782AE2B4E18BB4F0E27B" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,53", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BB31D3A374914FA4D7FD85AC77405EA0C8E31D49BDBB75BB609B9666A255617C" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,54", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9748F7325ACC89D642958FA624C7B61745C5D11C7C4AD7F9E16D01877B417FA3" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,55", + "CpuHash": "3DFD2DCCB1F323B39100B5AE664DD5D7B3F1B860069564B6D6937039545436D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "916C306E459B1098F175F49FCD2B86913DE1FA6870224FC9297C2AC7F3F14ACF" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,56", + "CpuHash": "E59166B24297D48DE82DE2C993F96ECD3D19ADADC4850D68E7A374041F8548F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "96E77480617799F41645FADDAAA9A45FC80F9BD5D600ECFB20888880935306CB" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,57", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "28557863CB635E1B045C8CB1160BBD72910AE90DAF025A3CE03E502678146B89" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,58", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9BB81AB7033D92EB2C9FEF08A2C63BEA883490199A44D27A62847C96A54885D4" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,59", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B0059F2A4591381AE656E51034277160AD04574579163F663C315A58D2661CDB" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5A", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "98FD343628A35F0B040A3A39F69E5FE49B3D85A22174EE3B4FF3829C78E1F97A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5B", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E39B63B3E63BD68F663CFA8B74BA1B93D24120341D306D77F8CC2308BED89E84" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5C", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B749E8F573A5A9C0682FDE74E36955CDB7BC7DD43C80165806AD8F205155E60A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5D", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "90937FCB22A60CFA1A9F6FE07FF85A0F4F7E878E615BE220C4E74D3C969C3405" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5E", + "CpuHash": "4C0FF7B0D07955D22CF940AE019A6923F773ED4A56A1507A377469D8556C533F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "204A8844CF9A9D77DF202FC2FE7120DC281624D6E7F202ECAF2CC008939DF979" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,5F", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B0DF8D5EBAF11D213FD82FAEBB1634811822909C30CDE0456F2BBB419AE8FAED" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,60", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "908AA7ECC794FFCEE318D9134B465DA6B767EF4787C9568058C3C93A8E068027" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,61", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "42C2101A648B91FF772987B95B04F6B78365B1F40C7EF21AA99FDA5A929EC32A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,62", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "44A7AD6791B9B8EDA9C95016B8ED906F1B1411B0B9B6D2091D5281AB68FAE73D" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,63", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ED83D0FA10CFAD3C9E9FFD190C4C5B92CD9FDBD151361CE962514142EE1F2CA7" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,64", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D81F15477F3A243FB3FD657D66B696101B9AD2725F6C786E7979374F0F4C2F4A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,65", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E641596280FCE66308B8D4F2D9E4E03E51F94A68FFC7C27228FFAD383EC35F7" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,66", + "CpuHash": "E59166B24297D48DE82DE2C993F96ECD3D19ADADC4850D68E7A374041F8548F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "39D687C7E97CE4346E58235062CC98AC75E65B64DB5581D9ED84FB43050DA97A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,67", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7FA302E14CF4F6E11FA6C094159FD67B2A3140EC9E7BD2BF2662C0A730173743" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,68", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B6338E6E946962F44B737252D2E1386ECB10C8C301BDDDFB89A7C06A304FE8EE" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,69", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6F01E8BE9A0FED8ACB64A29ACD8B2D8F8B727120559FB8575EE92A684FF20AE1" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6A", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E1E7F5F483E37CCCC4C9D2301D2D5C5E6B7AEE295FD4F565E052BA985620D10E" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6B", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9D2FF5FBB9568BBB7FC8B214BB514FE7C4A46D08DFE1C2CE38693981FF8B1CFE" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6C", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A045FE5B6008FE1A78EAADE6B96BF083983CF6D9DC0AA371F8214995E47D588" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6D", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BE0DAF25C70F2E15A6B2025DEF5C65A4626D7F7AEFBCFC0DB4953575B13D4837" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6E", + "CpuHash": "4C0FF7B0D07955D22CF940AE019A6923F773ED4A56A1507A377469D8556C533F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "860BF27B4A7FC664646C19BC0EEF095D9EE8CDF6601C70E66EDF5D0250236115" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,6F", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8134CC7581B998E4CF6FDB944F635F1A489B95784CB8CEF3807F411A37E27B50" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,70", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5C0C3ED4AA73DC40CE7621AA51FF58D58E769A3A56D849BFEBA6E32AEA0DB3FA" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,71", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "35CBA99588D609C9A6E7D887F7E2E0C454B84D160D6A25DC99F66C2C421686FC" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,72", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "688A6FD8B743E98D70080FC240F740D24B249AF03A279A3D3402041DA703D905" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,73", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "16E1898BB1131AA55FE158A7D2643015801C7E4C2C10C748A173E7ED4723CB74" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,74", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F91BB1FD7802DEDF713F56628D2565787046819E57417AA96DC993704DC05B00" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,75", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "16A38DFF14D64881D758A8B261B9CFBA3A012CF67D9BD7FF900E0899A39FAA27" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,76", + "CpuHash": "E59166B24297D48DE82DE2C993F96ECD3D19ADADC4850D68E7A374041F8548F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8988F1DEF0713872BA31AC36175E93D96C6045E3D096C1503311DAD5E5EB81D1" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,77", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "09B53716EF121B2748F5FEBD387894F0CE5FB495C2D93FCB8A7D7BC0D3360F41" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,78", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "39829443CDE7326D7839541285FDBAC56D1F063559ACAD07D6164D13B5A7E3CB" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,79", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6471EA7228E433D3B1E90D402D150632C85C331D69EBA98C4ABB0B8D22FE0466" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7A", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EF00DDC5738CCC2A140C841A26FB0BDF18BEBDCE1256D3122CC17D2F30C292B1" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7B", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3125292274B065E0C71755E41B72CA61BC78AFBD16194F14B27F8BFDFD6CD61F" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7C", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4954EF0610F1AB1375CE77B8F115F0979C622A12A5BA45F33642E54BFA007F23" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7D", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F56C76D37575F96C42D7A24BDC0395F4534F0090B63FE269F009012E27FB4B14" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7E", + "CpuHash": "9FEAE78FF876CDFB9D81C00B8AE707334890174772F029BB3D700292D03CF577", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "128AE5ABACF7D108B8E6FD331FE43786727BEDE30DC19E92ADD72DB7F83BB43A" + }, + { + "Name": "BIT b,r", + "HexInstructions": "CB,7F", + "CpuHash": "2E7957BADD6E081601DED64F0B4347A993E9CB27856CA600542309AA5AEC3BDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4F3B032DF13C568166A12287D9A51887F11C42865B990F699571AF2AC8A46175" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C0", + "CpuHash": "9629857DA2ADF6D2B3F920A5CB2A7472A1F1CAC7989D49BEDE5E97000DE3812F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "586E366813C26B09B792250BFE5030D53BB74C16BEA2D92302CDB4849734AAF5" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C1", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F68EC812B3C9E4AE450E5307DF1A6317B1963347016460144B8EDDB097EF7D6D" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C2", + "CpuHash": "8F2780A7C2F26EFCA3C04EB18C5C3BD45418F8EE4C6D6D2570A5E6547E61F25B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "904751DD7599A5955B1CACBE9B5C0C19E8F99FB9B90530FC90FD6BC2BA5809D0" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C3", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "420C18F4E646787AF3E97EB2B667AA6DD20F05F616343DBA127568570BBCD24C" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C4", + "CpuHash": "511B175B72E204A9E6AC0498A85DDB5F5473976BB9E1EE2793EE7247077157B5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "43FE2D79F449D1250ACC1F1501A8ABA0BD5DA8137A909E94FE5CFA13FEFE8AB9" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C5", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5CF07348D8D8829ABEAA9B8D251C83B8BF3255EFD3620DDAEA4EFBCAB9240FE8" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "181572651981955A84DB85C0DD143B28335B99F88C41ADD1344F6F53B0FA1F00" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C7", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8E59E8FFBFA794EA289300438AD7662723E9BDF6272EAFCF53FAAB996A02BDD2" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C8", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2881E22E794FCF74C6A17494FAAD12CCB8FCC838DA5B143198B857AB8DBC3A41" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,C9", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1EB35A780E7334388BF2C78277F9897B1C3F2E7737D69EEEED7144AB60ED60AD" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CA", + "CpuHash": "250369C92BCBF2E64387A9D98F582ACDE36B06E6F6DF36E250F929DBC23B1A2A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F6D68EE68807939B59DEB7AD24C91DEDE12BB5D4A7D880D027DABAEA19A2CE41" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CB", + "CpuHash": "62410DDDB7E016DAF8F6EBD3CA6FDD04E2A9FA062A2B8546833B885EE52C7399", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C0C72BF95FDC5CBF8463539C068D5877BCA54532D66E45FBB6869B70ADFB603F" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CC", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "62FA5D384A69CC85183C2F3B1BB56ED60390EA30C5C1C3ACC734C427105CA875" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CD", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "36DAF69F7D2F5CE11E1FE49F294817DBA8047D7C5E4E11B6E02AC3E41BFF4771" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "386E96056B9284D713A42FA620E7EB50068FC3D4428358B51B97F4D9A782EDE7" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,CF", + "CpuHash": "F304C522A3996D6C937B5FFF1C0BC9E17A97AFA2545CEE2D2F39E60CDFC129F6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F8F67FB79799DDF8296C8A4BB9995823AEED072B5E3F6BA24395A8540CFD3D75" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D0", + "CpuHash": "07B071BBE230C3A25A9D48265F11705B6CF45FC0E0E75104ED215883B6725852", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "370E32D5D8CF601C53DD3E8E1450750CA87EC8AC16B8CAFE9C801E6BAA67B142" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D1", + "CpuHash": "2FC78B7F76003D7843B24F8DE4D8521C3763385E2487F2C1B1D3EADD76279056", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "21B1262DE4234FE7E5E80C1BC58DD86D0D1D61086E29E0B5B4362F4067ABE8A9" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D2", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9A5C3547A8C6AB4242EECAA094F287C0FFF9A20FA4BCA6C8D6418B44ED534C6F" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D3", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "602C90CEDE4AF9308B50A59F001FFD1D141065DF7509F249E5F114E47605C12B" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D4", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B850F10C7D22EAB7DE8EC431E5D5A2E7FB3457BD1AE75AB350130BA2FF787735" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D5", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E64794A0B983B54EE8E46E6703A20F37C10A7A3B0CD4C7BD0B005374654A3CC1" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B01D96B0BCC9A80FF40255BFCBB9CAC9DBCCE22C02D174BD505A6CB88D2B0E54" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D7", + "CpuHash": "627111ED937CCAB578688A478217FF87CB75E326FE2CD9293B22E23E23940EA9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1B2AE507E1F132B509CFE3FCED4F95340004C721C644C550E4BD69D003307999" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D8", + "CpuHash": "428BEC97062DAAF0473B66F00F8C9C48FA1D1BE51070F9D0C20BF78572A3E4B9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "095A7D9819A0CEAEFE1EC3754C1FF812EA3C2B5EA2C801E9B6C459FC99831B7E" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,D9", + "CpuHash": "689F482E6ABA11897E32C00E7F44AD9BE370D946039C277261FA632EEBCCF07B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "77012DCCF44DF56F851D67CF93766E007ECA6236A361D451734251D2516B2D96" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DA", + "CpuHash": "1B8D7925F95267118182330628EFAC679BEF2DD0A8F3F9D54B3354825342B029", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5E98B3991E90EB8B17066B7609D4AEFCF7C264663EE55A3F561718422A566274" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DB", + "CpuHash": "93D7838F53EBE885DF3EBC2B467A80D4E97E28636D4A9A1E99C02C6086D802BB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "70A3BF50DF98E94A28DE7DBE6D49C9FADBEA1C7421D7CDC6A7FA70C0A03AFBB7" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DC", + "CpuHash": "60215F3940FA599B44294044765848A40D93649926AF3C7D804448D968EE02AA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9ADC0BBC528F23D53E2AFDDEF6CDFE56366352BA7C6FF5852C66072B07012BE4" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DD", + "CpuHash": "8CF03183452E52261A1CFE3248AFBF81AC3BAA0C4AACC5163E4589704B056BD6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3380D011B5D07506D6BBDE13322543102317D5D6D565ECD45DD8CBA1EDA48441" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A257CD9F2D8CCB3C705A434AFEC006E9753E87F97FE8AFA3C411B29E46B34EE1" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,DF", + "CpuHash": "697F75D417A3F83308B0EB5FC3738CFF718B979A948F78946D8D6CD003445BE9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "391A252BE4BC5A7944E864741E2EF6158297B6CB0D928F1F63A7EC15B92A857B" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E0", + "CpuHash": "BF025FB412AA4171A5859ABF882D51E8EE967701F425D551AEEBE754C670F6CE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0D534FA2563644CAC84781C0C730F5CC9C86D5A979DA142FAE8D9833DF8C905F" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E1", + "CpuHash": "4F90898D6D80FFBE4686F77918D20EBAC84601EB7DD3B40EA158FE84A19AD173", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "316920844F6CD8C4D0B45BF5FDDF5170BEB402A8BE721B448745730636DB78FC" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E2", + "CpuHash": "36F29DA34C57526A191399CC22B24C38DA99404D7BAC8EB6ED86FF9FA16ABCB0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E0B3248C07DF06B705173867CC05B1F31C48442A0F229432204B69B3D0777C9" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E3", + "CpuHash": "0A1AF3E6231F48685FB0E6E38BDFB041509BA66D03A91B1A8DB996645848FBA6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "74E3A0571D0E169DEEF22ED53E3B88E38928CC526C9563A9435AFA3D1E6498D7" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E4", + "CpuHash": "51600243048B1A5DB46CE18A2912107C2CCFCD8612C432BCF4C604C504B84FCA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AEDB83A8179D5E94FC5B2D88F4EFE8C09B778A6598C8E9EF767432999FEABEC5" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E5", + "CpuHash": "E970E0AB696811236CEBADE275B2762B2A3D2AE9E0F354DD2E8A3EE7E83E3759", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "14F0EF267477DA1E67EFBAD9083D5717E832CB3331AF116A61BDEF9BB4E26B89" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B70F4274CDC3C202397DD250194D52F7EF5C420C6A0AEA91088565D933BFD78C" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E7", + "CpuHash": "95B6E0615302F8558965CFC641C5AA05A8C17209FE87FD8DFB4F394D042CD3CA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "085997601351EDAC27431530286AF6EEF5A00B625C62832EE97F906787025AEC" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E8", + "CpuHash": "FD8B2D639A883887818FD1745D402E230E5D3AD107B09F2BC790E6935763C911", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6BBA19F43333ADF91492112BECAF632D034D8890CA711BF59C14F5C426F541BF" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,E9", + "CpuHash": "F652668F96CAA724547E01B1B2B1041F4251F8902FF8C70AC7D0E47680FF568D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0440DA5C7C28A1902CB78ADDC16A9A82EA5F9E2177460DB2DA8DD3FCA5C79B9F" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,EA", + "CpuHash": "A5EBAC9297D5E384DD156F259EB54DD8A8763024D8B78947C7D538BA061D6C42", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FAB234CB182975332A988EE414503998090A6E9524261BA0595474C3C43E83E7" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,EB", + "CpuHash": "132810E0477CB78697A4F6BC033562F72B69282F95AA8F449667DEE8A5484AEC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "26CD5A60A59A27B25033366970552C4541908134B7DD5009C9993C38D7AED860" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,EC", + "CpuHash": "0F87A41DC8C356DA20A89F5143840C55611F40FBFE9A28D9D00A83DE10A0AE97", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EEA10A97B66DD2DA18A7356B106DE7ACA8BBE4E4C3F7F9B5CFDF75AF1CB790E1" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,ED", + "CpuHash": "B16EAB16B70F11AE3DF3E20766F7F32F99FDE481E58599A0698386EEA39AB713", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6CAB287D6D469086BE62320742A660B91FA8135077041D50A651059D88A92F3E" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,EE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E4D9BCAA5C04380DB865D7532C78A323004447A5E81DD3055FD9F3CFEF045A54" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,EF", + "CpuHash": "2062590C1376B3D7222B170A9C0BE966F44220ED7CB00DECDB673982C3148015", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C2333237B9F6F5975E23B20FEFDC6447CB1A19E9270E065B5ACD1121DDF509C3" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F0", + "CpuHash": "2BF060599870484314E8334AFECF778889C792009565F68FDA220F9F8B3A9950", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A4DDE05463CAEAC16B58879808F6C3C305A5539178D8DC6105E877172D35D667" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F1", + "CpuHash": "F53B44F451A0E58C6ACA9FA51B07B692FD03F63AD2EC48F079E8D2CBA4165FA3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6A788CDD2A4B9BE299A66EF554B1FBAACA5D09DCF08814D9FF5120245DCD1BED" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F2", + "CpuHash": "6711796F06E077001F33F14C4FD9C5053E94A3021E54F2AFC5D1303965F8FB02", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "72EE8A704D294839D39302B98870609FDABA31D13066DC9EEE05E8C5A8860014" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F3", + "CpuHash": "848457302F01DC09717B5C280207DAD0CA6CB333E0FBFD940BB16276C3BA0421", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0973FD8C6823A257D8CEDF61E330DF21F721F1C114430197ECD93B9AA0744583" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F4", + "CpuHash": "6EA7813EE5F15C40D85E2459E5F5A0C1DB7D52B1F6F2204EEC34EA35153A00F3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "65C7FB30549554D11C6E671DEBD376DD51B59602E13251B9C1C127E4125D4489" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F5", + "CpuHash": "DEC46755E42C40C6205353E127F096809AE5E2E62616690C562BCCC1DA2CA752", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A792B4B9ABFE604EC9DE81BE7547F0E2FA7144A04287CDFC8E22F7B85C005589" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F6", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "01400FD80CF3498B2CF2FDA228B90543816AE963CCA2C2A2D1721D9B2DA6FBF6" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F7", + "CpuHash": "152649A89B9CD3CDB3B3542FCEF5AE9D418D40F047F96A8FECC6CE0A8C2568D7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "13ECF4DA8388A867161ABDDF709A4BB1BA8CC741E55D47087E62E92A7804A4E5" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F8", + "CpuHash": "4779D80C8FD25C69D5BAA65BB3878429EF50BE3889ABAEF6D1C8C35D8538834C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "898891BE74EF5AE20369BD418F248373409B63B17338DF7832B9D34EB79E6557" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,F9", + "CpuHash": "B81ED64F0F5C254EDE6AEAF64348926C4927EAD478125CA1E3EE9C70C950A922", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "63893804E1A38A6BF76AA0BC890CEFD3852A639FB6EBD4E434670A7A4FE0A354" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FA", + "CpuHash": "9FEA381EBF7E03F1680C7934FC8118C58A9143C8C000A1451886DA7496B771C1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CE92AFD4040B581241DCFA4636A250E22D5B54272D9670AAB8073542B38362DC" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FB", + "CpuHash": "EBC22F21AC0B1CA49CD14FBFA9A23361518E7490B2E87E8FA3C0633A57ECBDF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1B9F06196B1CEA3CC529E47A461C01F3E78CE83FBF83EB4973D770014F7A8707" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FC", + "CpuHash": "EC5A3DB46308ACAB5F530ADC9A88F2C302CC0FD7F2B851AF17C0EFAE150D9634", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4C64109004C903BEECF1FC713C650E640ADC37A92AE46B1662B4EF1F4C6C1838" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FD", + "CpuHash": "EE165E63D97AD2A632580D5EBA2EF9FC8F80EE9F09C97AFBE64E4DB2506DE0C4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E4065159FA179170756A71ED913E5E020B9A6F5F066854F535CB95DCDB529FE9" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FE", + "CpuHash": "006AF9591D548BA4DF820617225BA44849EF1B919F70340589FA93A53A86A058", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B49DB3075A8166906399A230B3972B8749E4A37641800869BE20F65A95E29352" + }, + { + "Name": "SET b,r", + "HexInstructions": "CB,FF", + "CpuHash": "8D0121AF02B7F3A7B037498DBFDB62E604F8C95648DBFEFAB1FF9DCD97E27CF5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3BE0FD2A12D2C246CC581A89B67CE681B69B9A9882424B1198F094BE7253D647" + }, + { + "Name": "RL A", + "HexInstructions": "CB,17", + "CpuHash": "0F761B0BF3D9972FCEF15A2EE953B05121AD705E089809FD1D381EE4ADE6BB23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8E1218CD74F753928F8F3AFD38C1DE2F5839D739E01E9015E2C7D98043E1B245" + }, + { + "Name": "RL B", + "HexInstructions": "CB,10", + "CpuHash": "40DB0BDA43EC8B026CDFBDA3252331AED49E85D72A412BFF8553414FB09F0E73", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "78792C5A3BBB39979D4724EE2C32CD4D1B42715A4860BA2F92DD54196B95427A" + }, + { + "Name": "RL C", + "HexInstructions": "CB,11", + "CpuHash": "9CDDC67A8E9184D1B5C160E3CAFA3443036B3EB26940031EA216FE2182F4E216", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FE72A2241C436B221F7E28F09446182DD3575D3AF60142BD4B54EEF7AF1DF476" + }, + { + "Name": "RL D", + "HexInstructions": "CB,12", + "CpuHash": "A2F52F84DD5AC874033AE5AA88A19560537EA987BCAF72C2C2F9D98AB9EE37BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "42C666208B3BC1888D38BE806842F7C8B427467E064D24845BD24F17D4711BD1" + }, + { + "Name": "RL E", + "HexInstructions": "CB,13", + "CpuHash": "CF98E612F608F24CAC3202F3E1C7B80A0B041ED22D271B083C5D721EF805C474", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A6AAC10ACFA75BA949C3A75918F9E7894026D4A23C62FF5F253E88898946F479" + }, + { + "Name": "RL H", + "HexInstructions": "CB,14", + "CpuHash": "CF754C0AE784BC681ED9BB5F17D1773938F3CF873CD8176F136880BFA975C9CD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D82A92DCB9E90A7A7F65AEFBA6638B7FB0BE8935DBCBCAFE1D724A07539FA242" + }, + { + "Name": "RL L", + "HexInstructions": "CB,15", + "CpuHash": "37EF4699CE0ED9C9AA805919A88A03869760E8AC338BF12DA3EECCA1B3B7E2DA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BA4B2AA4DA14AB039C7CF0BB5E3DB41942AA8D1ABEBC898ED583C31E99691144" + }, + { + "Name": "RL (HL)", + "HexInstructions": "CB,16", + "CpuHash": "E7F74DCF7924FBA2158C69C7198F98CEFF7D45AC6F567A801362E1A850784C7D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6CC51A4BBEE7BCEB0B854A5E6BB116E2D6C3FE0DD4B74820F089904E0DD054F5" + }, + { + "Name": "RLC A", + "HexInstructions": "CB,07", + "CpuHash": "0F761B0BF3D9972FCEF15A2EE953B05121AD705E089809FD1D381EE4ADE6BB23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "32482AD603D317785B94E246B6B2C4991B963DC1124FAB56970B20964FCF5F0E" + }, + { + "Name": "RLC B", + "HexInstructions": "CB,00", + "CpuHash": "40DB0BDA43EC8B026CDFBDA3252331AED49E85D72A412BFF8553414FB09F0E73", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0C58A9B5B34170CC64EF517B83C8FEC2D10C2434B86B1AAFE0530C22B80A06C9" + }, + { + "Name": "RLC C", + "HexInstructions": "CB,01", + "CpuHash": "9CDDC67A8E9184D1B5C160E3CAFA3443036B3EB26940031EA216FE2182F4E216", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D29B21BEFD3177B5387350052401D96FA64D1CA97DADB96C2150FA869CD4827A" + }, + { + "Name": "RLC D", + "HexInstructions": "CB,02", + "CpuHash": "A2F52F84DD5AC874033AE5AA88A19560537EA987BCAF72C2C2F9D98AB9EE37BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8AC3A9B18B3EFB80B0B8749A3D33F329653B23C19CC532670B0105E26EDC2256" + }, + { + "Name": "RLC E", + "HexInstructions": "CB,03", + "CpuHash": "CF98E612F608F24CAC3202F3E1C7B80A0B041ED22D271B083C5D721EF805C474", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E9352E9F3AB57F91D1B097B27BE7FD4210E750BD632094E06D7801C941315766" + }, + { + "Name": "RLC H", + "HexInstructions": "CB,04", + "CpuHash": "CF754C0AE784BC681ED9BB5F17D1773938F3CF873CD8176F136880BFA975C9CD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "48C4EF897211F8B00AE86C55CA089780B7B6FCFB512B31A63DEB1884B79D554B" + }, + { + "Name": "RLC L", + "HexInstructions": "CB,05", + "CpuHash": "37EF4699CE0ED9C9AA805919A88A03869760E8AC338BF12DA3EECCA1B3B7E2DA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EC5A5B7838A8C00E537579B44657789B12BBA249B583A4D24F8B62CE9A3E8133" + }, + { + "Name": "RLC (HL)", + "HexInstructions": "CB,06", + "CpuHash": "6916FD4E1D8503E10313C4C1BFC049F8DDF3A3287A235A996AADBBD1A6FDAAF5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ABE46FCAC2E762B4A46BF7AA240479B77B9C40E0B968B23FE1C5AB86F80F0FC5" + }, + { + "Name": "RR A", + "HexInstructions": "CB,1F", + "CpuHash": "E0689FCBCDE88CC8BA3AADFE73A46DD68EB7CF3431449CE1E210554240DE5D02", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4121DB3A9C3E8BA817758D5BF6F22AD13D196E7AEF1A2AA587AB6A187D2C8B13" + }, + { + "Name": "RR B", + "HexInstructions": "CB,18", + "CpuHash": "4B2884F778ABA6E7EE5741C64D31239E66DC4112C538E6B0EFA69C0302D12AF6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9C5764FFBC91FDE4E4307473CBB3E3E8512D702E5B4561F34BA0DAC32924B4FD" + }, + { + "Name": "RR C", + "HexInstructions": "CB,19", + "CpuHash": "4EBA50176ADE29E6826FCB41DC3C622DBA0EBA466D1DE1449605830A87FD4A16", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "46E91AA6A21ADA78C3903532ADEFD5596D96FB71084C17FC7D28A7442A5AEB60" + }, + { + "Name": "RR D", + "HexInstructions": "CB,1A", + "CpuHash": "9429C243818A31E66C805CD38E788A3D2C96578C7BD489D7B254186AFF25E8B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7721ABDFD9A21649E438A3D77C770500345FB7DA58935158922928E900364617" + }, + { + "Name": "RR E", + "HexInstructions": "CB,1B", + "CpuHash": "50A373CC03A408EAF9C315297F121362B5AE3D0024C6250A4ED724E06EA9C00E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CD0A0EBCAA4B1A380C3B8BDD1A9B64BC3A546025335A51DA307F2E5D81A53451" + }, + { + "Name": "RR H", + "HexInstructions": "CB,1C", + "CpuHash": "4637E0F58D345D7479550F5912F1C20F8310E4D29184555083A7D094FDEAEEE5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D97722C3D6B4C307706697C3CD3F4C7FBAFBD692D85DBE000CE430A7504DBC66" + }, + { + "Name": "RR L", + "HexInstructions": "CB,1D", + "CpuHash": "C3BB327FF238600C685FA7CFF6554F21FFA6FB6A6A6677C1DC0A8D29A18CC4BD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3917161E3B5699D51D499C8D9B643F1007683B25546B53B494336A0515315ECA" + }, + { + "Name": "RR (HL)", + "HexInstructions": "CB,1E", + "CpuHash": "225A80DB82FFF04729F29860B1C5337F7A348071805CF38B3C499581A8CCA579", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0CFFD106A95882348BB677B4000BB6E227CCFEF39E6B64063BD61209C6327C14" + }, + { + "Name": "RRC A", + "HexInstructions": "CB,0F", + "CpuHash": "8B7A86303528EE27A24A825BC4E093D3A00170291781BDD6761C7D9DD6F276B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "434846E9365B20A52AF909A127082663F1B6C6A9EC41C7CA0EBCF5BE93FE09D3" + }, + { + "Name": "RRC B", + "HexInstructions": "CB,08", + "CpuHash": "4B2884F778ABA6E7EE5741C64D31239E66DC4112C538E6B0EFA69C0302D12AF6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C47600C661F9C60FEA9B0E9BDE9643CB080A2FD364DDB27A47F83544E5DF7FBD" + }, + { + "Name": "RRC C", + "HexInstructions": "CB,09", + "CpuHash": "5BACCD134A9607E426FB302C6B95BB47D76C9C003674EED3234A2E3333B96620", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1D8055FF9738AAF8769794DCCE4E3337D0C7E6127B1FE751EB13BFC9B310E938" + }, + { + "Name": "RRC D", + "HexInstructions": "CB,0A", + "CpuHash": "9429C243818A31E66C805CD38E788A3D2C96578C7BD489D7B254186AFF25E8B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AFEED3CA16247DAD59D753F6A93A1D28A832242FF513B1AC2E251CCC924F786B" + }, + { + "Name": "RRC E", + "HexInstructions": "CB,0B", + "CpuHash": "B7D668271C16B030F89A271E967E31466C00838AE4F0C573C37CD53018E41FCC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "27B8AC08F884D491159B8241C747EE7DAEEBA85C6471DB584A631AAD7C824DF5" + }, + { + "Name": "RRC H", + "HexInstructions": "CB,0C", + "CpuHash": "4637E0F58D345D7479550F5912F1C20F8310E4D29184555083A7D094FDEAEEE5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "294DC0DC57F95E2E00DEAE146634FF7C3AEC8E84DEFB0A2C8708FA5F0297ADB6" + }, + { + "Name": "RRC L", + "HexInstructions": "CB,0D", + "CpuHash": "1683FED70C51C2051DA81AF6C4B070C59F304E10787CAE8B059606FA90889338", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4739813884886B491924224224F40D1B9FB3910FEB352D495D435ECAE1A709C0" + }, + { + "Name": "RRC (HL)", + "HexInstructions": "CB,0E", + "CpuHash": "225A80DB82FFF04729F29860B1C5337F7A348071805CF38B3C499581A8CCA579", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4E5AB256A7630417A4E4595FC36A362B00BBFC912A35B8863D613FCBB09C0175" + }, + { + "Name": "SLA A", + "HexInstructions": "CB,27", + "CpuHash": "0F761B0BF3D9972FCEF15A2EE953B05121AD705E089809FD1D381EE4ADE6BB23", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9FD9B110B8248EC8F5F393F3CD32683DDEE9376B97E4FD889B9CD77E9E5655DF" + }, + { + "Name": "SLA B", + "HexInstructions": "CB,20", + "CpuHash": "40DB0BDA43EC8B026CDFBDA3252331AED49E85D72A412BFF8553414FB09F0E73", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ABD6960FB5CFCAC8FC41F0367DA0D250AA0B2BA5D4A7826A758266B488C1DF5F" + }, + { + "Name": "SLA C", + "HexInstructions": "CB,21", + "CpuHash": "9CDDC67A8E9184D1B5C160E3CAFA3443036B3EB26940031EA216FE2182F4E216", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B60969EDDD6479BCB9C7092EE2FFF595BC48A40864C540EBFCA12F6E694E082C" + }, + { + "Name": "SLA D", + "HexInstructions": "CB,22", + "CpuHash": "A2F52F84DD5AC874033AE5AA88A19560537EA987BCAF72C2C2F9D98AB9EE37BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C81948C0550F892D5C858774CBA11F4E00989AE382FECBD7FB8DD61124C35A4A" + }, + { + "Name": "SLA E", + "HexInstructions": "CB,23", + "CpuHash": "CF98E612F608F24CAC3202F3E1C7B80A0B041ED22D271B083C5D721EF805C474", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CA15A97A2A1250E501153E0D4112D775192F2C261D22F6FA29D3D366B7DD4688" + }, + { + "Name": "SLA H", + "HexInstructions": "CB,24", + "CpuHash": "CF754C0AE784BC681ED9BB5F17D1773938F3CF873CD8176F136880BFA975C9CD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8C52547C6D3FE37733D8253640DD47EB8B2A9188DD6DE71B20EF66B3DE824364" + }, + { + "Name": "SLA L", + "HexInstructions": "CB,25", + "CpuHash": "37EF4699CE0ED9C9AA805919A88A03869760E8AC338BF12DA3EECCA1B3B7E2DA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6BD2A60E0DC3AF078B18E6946D6F0C52B0E421FF7695345B7E2BBBD78176F944" + }, + { + "Name": "SLA (HL)", + "HexInstructions": "CB,26", + "CpuHash": "E7F74DCF7924FBA2158C69C7198F98CEFF7D45AC6F567A801362E1A850784C7D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D790CF795CBFEE76F9C2B2EB3F846B8967D0F48B047823A82DDB2D866136BE4B" + }, + { + "Name": "SRA A", + "HexInstructions": "CB,2F", + "CpuHash": "E0689FCBCDE88CC8BA3AADFE73A46DD68EB7CF3431449CE1E210554240DE5D02", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3D51E1A60E6795F98740E6167FA27F11B5E0FD4BBC443599DE33A3ABFCE387A8" + }, + { + "Name": "SRA B", + "HexInstructions": "CB,28", + "CpuHash": "4B2884F778ABA6E7EE5741C64D31239E66DC4112C538E6B0EFA69C0302D12AF6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E42A11CDE1009249E5279260C07A64D5A51E38A7758EBE5A0BD0C3F0FA01968" + }, + { + "Name": "SRA C", + "HexInstructions": "CB,29", + "CpuHash": "4EBA50176ADE29E6826FCB41DC3C622DBA0EBA466D1DE1449605830A87FD4A16", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B75B16BAF3F65ECE72141E2B889DB2F969BB9D672189E472041BC55CC7CEE98E" + }, + { + "Name": "SRA D", + "HexInstructions": "CB,2A", + "CpuHash": "9429C243818A31E66C805CD38E788A3D2C96578C7BD489D7B254186AFF25E8B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1F8CB84B768061606D6818C9ED1D99566FF5366AD614C312C3A2BE26D395B15C" + }, + { + "Name": "SRA E", + "HexInstructions": "CB,2B", + "CpuHash": "50A373CC03A408EAF9C315297F121362B5AE3D0024C6250A4ED724E06EA9C00E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B84D7BF04BD350D9E4B858EE5738CC36B1A1D51474A1DA8D8F054CCE3643B7F1" + }, + { + "Name": "SRA H", + "HexInstructions": "CB,2C", + "CpuHash": "4637E0F58D345D7479550F5912F1C20F8310E4D29184555083A7D094FDEAEEE5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "644A77C4343599ABA6B6EE4F24A1AA154FA4223DE1463163EAA18241AFDC4168" + }, + { + "Name": "SRA L", + "HexInstructions": "CB,2D", + "CpuHash": "C3BB327FF238600C685FA7CFF6554F21FFA6FB6A6A6677C1DC0A8D29A18CC4BD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E997C17C5B0497BF19AF0A672B4483B46A7971EDAF2CCC4D2FD78D4C81556EE2" + }, + { + "Name": "SRA (HL)", + "HexInstructions": "CB,2E", + "CpuHash": "BEC8529AD2AB202461B8483572F98792767B7F7871DBDF90BB87FCA63A464B42", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D7BF91EF183D2C9C17D41501D43D7AC79097BF7A7131E9988D0646D68D5BE9DB" + }, + { + "Name": "SLL A", + "HexInstructions": "CB,37", + "CpuHash": "0C1929E41312670A2F511BB4D8DC5C07F1988006444A15A3E109A481C38BC31B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BB95A58FF1F85D7D3B24BA73E228377FE01055024C4E51885C8B8FFB5E0D7A9D" + }, + { + "Name": "SLL B", + "HexInstructions": "CB,30", + "CpuHash": "851822FD4C339C28C873E672A34B65040A008F650531123EC7798C90CE0B1DB3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3F76B081044F7EFE059354FDC0EE19A139D0FA852B555F4E8014428165D67FC8" + }, + { + "Name": "SLL C", + "HexInstructions": "CB,31", + "CpuHash": "2FC78B7F76003D7843B24F8DE4D8521C3763385E2487F2C1B1D3EADD76279056", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CC72532DD1288990CE23B37FF6129C39071CE17BC419322E61326BD422FD9338" + }, + { + "Name": "SLL D", + "HexInstructions": "CB,32", + "CpuHash": "404E5F63253CDC0BD75264C3BE8158AF97B6B225B9BC590F69F03F655CCF8774", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E5CBB84D02C100DB2E1EC0001B51343515924B004433325AA973CCC4A552BCFD" + }, + { + "Name": "SLL E", + "HexInstructions": "CB,33", + "CpuHash": "0C81781D4E8F21BDE16F5A0C33EB9BDBE3AB7070E2EBB750AC5C9C51F447B866", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CAE6DEC3BBE972E4AA4457C4DBFB0DBD6945D4B8D999CBCC540C37C3F24D7CC3" + }, + { + "Name": "SLL H", + "HexInstructions": "CB,34", + "CpuHash": "A509E83FBC5ACA5FE73D6662DF16B7C4CB9193C4A707463533E40749DC8251E7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "36FA15922447783F1671147DDA5521D27BB51D4C282BA8D441807F2414268E63" + }, + { + "Name": "SLL L", + "HexInstructions": "CB,35", + "CpuHash": "CD5F7783BDBFCD941A8A696ED222D5D8612894B9D842A1E79392A31F766156A3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CFD06E506D7C2B203E03E350886C718F5807862E6CAD3FE1743C5642E27963C7" + }, + { + "Name": "SLL (HL)", + "HexInstructions": "CB,36", + "CpuHash": "6916FD4E1D8503E10313C4C1BFC049F8DDF3A3287A235A996AADBBD1A6FDAAF5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "89EF5878250E6E7F93CA3D53EDBB59B9EE680ED4F5DA0EEA0AF033B4565ED4E6" + }, + { + "Name": "SRL A", + "HexInstructions": "CB,3F", + "CpuHash": "E0689FCBCDE88CC8BA3AADFE73A46DD68EB7CF3431449CE1E210554240DE5D02", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B4B5117008206CD848EFBC2211F66C21C6A7A39E31EFCF58FF89D276A292171B" + }, + { + "Name": "SRL B", + "HexInstructions": "CB,38", + "CpuHash": "4B2884F778ABA6E7EE5741C64D31239E66DC4112C538E6B0EFA69C0302D12AF6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D7FF77937722CBF250722AEC69DD2311C1B55E8A91FC908A7F2F893799D2DF34" + }, + { + "Name": "SRL C", + "HexInstructions": "CB,39", + "CpuHash": "4EBA50176ADE29E6826FCB41DC3C622DBA0EBA466D1DE1449605830A87FD4A16", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7C89C1EBC63CA7596894A19C2CE950AE88639D8FEB7BB5895CDFA9F3B1CDD5EE" + }, + { + "Name": "SRL D", + "HexInstructions": "CB,3A", + "CpuHash": "9429C243818A31E66C805CD38E788A3D2C96578C7BD489D7B254186AFF25E8B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3979DF2A67507D24441D205A5721A6B57B9E4C37230F99D3BFB99080AE967C27" + }, + { + "Name": "SRL E", + "HexInstructions": "CB,3B", + "CpuHash": "50A373CC03A408EAF9C315297F121362B5AE3D0024C6250A4ED724E06EA9C00E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "05E8491A065108A194C7640ED2E1BE3265D3D1C68BE2CD029CF9E53AB883801F" + }, + { + "Name": "SRL H", + "HexInstructions": "CB,3C", + "CpuHash": "4637E0F58D345D7479550F5912F1C20F8310E4D29184555083A7D094FDEAEEE5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "817088EACD9BC3C5CD04C0614CBC82D7305B1A2779F34F24DC456D754250CD48" + }, + { + "Name": "SRL L", + "HexInstructions": "CB,3D", + "CpuHash": "C3BB327FF238600C685FA7CFF6554F21FFA6FB6A6A6677C1DC0A8D29A18CC4BD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C023FC3590B18A7719E8D73C10A6EA75F3629418935394AFBD8C8D9D76685571" + }, + { + "Name": "SRL (HL)", + "HexInstructions": "CB,3E", + "CpuHash": "225A80DB82FFF04729F29860B1C5337F7A348071805CF38B3C499581A8CCA579", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E0211B15C503F223827E77222451F1D2F636019C61BF694FAD37CF9EBA91275C" + }, + { + "Name": "JP (IX)", + "HexInstructions": "DD,E9", + "CpuHash": "63840A92DF75E909D3C57DA0A1487065B3313C9979F2C727BE2B26A2ED3AF4EB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "300295ABD2B371DBDC40EA5C116992585706981F51BB46AA2050AC91BBC0C81A" + }, + { + "Name": "ADD IX,DE", + "HexInstructions": "DD,19", + "CpuHash": "43EE450341EDD9D3E7D970E3705746A9E328717487D9BF0D1CE92E3F07033F38", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1664F6D96E153B6BD98050E2CDD87E55689F9E71458F4688825449086BB62941" + }, + { + "Name": "ADD IX,IX", + "HexInstructions": "DD,29", + "CpuHash": "5640BBDC78867B90602876356C3A7DFBE66B94D83EA0C933053DAA64DEE1AC45", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FC08EE21B347544FDD7C37077205534D426A179A3FB646ED2C89F3F371B6C23F" + }, + { + "Name": "ADD IX,SP", + "HexInstructions": "DD,39", + "CpuHash": "0DAC9816A6B9C9B5109F9D14C5AB6669867578F8F531C4FF6B8DD9193DBEBBDB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7FAE9467A8931EFBEE148C77A0DD09628C4DC1CA14B2E05673A8881362520C24" + }, + { + "Name": "ADD IX,BC", + "HexInstructions": "DD,09", + "CpuHash": "059556E78DABC29BD0D23B0F575CAB9E7699DD0E0F5E73E00CD6AAB136F59654", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D186CE6A6E35937597ACDCE8F5AC4F73650CA0634EBDE89F06768750C0DFF8CA" + }, + { + "Name": "ADD A,(IX\u002Bd)", + "HexInstructions": "DD,86", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "867FEA98FBC33AFE0E51C6846483920AD6403FD741EA0475F4FDC455E2C1F3BD" + }, + { + "Name": "ADC A,(IX\u002Bd)", + "HexInstructions": "DD,8E", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2758791DAA77CB2F12307647F5D89561FBF5B830FDCF9DCBE3D730872EA23E1F" + }, + { + "Name": "SUB (IX\u002Bd)", + "HexInstructions": "DD,96", + "CpuHash": "F75A3C247900FFDDE8BD98D3EA21EC64743CAB3DE596EA437ACE13A5119B8320", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BC8EF7AA64F008899C2C6F18F649BB8971BA8619DCD7F2CE76C815ABE2B9F502" + }, + { + "Name": "SBC A,(IX\u002Bd)", + "HexInstructions": "DD,9E", + "CpuHash": "F75A3C247900FFDDE8BD98D3EA21EC64743CAB3DE596EA437ACE13A5119B8320", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A3018758B64307BA53BA62A2B2DF7608B14C22A1609BB96295BF29DB3B7E337B" + }, + { + "Name": "CP (IX\u002Bd)", + "HexInstructions": "DD,BE", + "CpuHash": "3D64DAD112225D0AE062484F789F7DC9FB993D2357B6E91FC64F033770694BA6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CBBA766D6A5BB5015826596A5B6E6BFCFCCCA94E21BF8851B99B5C1C18550510" + }, + { + "Name": "AND (IX\u002Bd)", + "HexInstructions": "DD,A6", + "CpuHash": "D9BD49B0DCB9D852EE95639CD44C88798B38AB529F3563E499FA2A4A31C90487", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9ED569C148A664BD29CBA117A6603B6FDDD1FD14EF44E59DCFE311ED8CBF0566" + }, + { + "Name": "OR (IX\u002Bd)", + "HexInstructions": "DD,B6", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "76EF548AB1FBAA25F035D2B8C6BFCCF7D83AFDBCAD9E31E3922E499D71B1A751" + }, + { + "Name": "XOR (IX\u002Bd)", + "HexInstructions": "DD,AE", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A7D21C384B4195B0D18763E9A40A39B8307519D450C3C653CC8373498DD8B6FB" + }, + { + "Name": "INC IX", + "HexInstructions": "DD,23", + "CpuHash": "A45A8486935025D7DE4725A58C71D997CD1EE9ED60EEE3DF10069497DE349A8D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8C8B3E9D0E9B7CC4B30CCD7E9C3C5EABF2F20B45D4109E19877E85B051EA80B7" + }, + { + "Name": "INC (IX\u002Bd)", + "HexInstructions": "DD,34", + "CpuHash": "9889E3581018E5EC6D999F3B88FFBD09E85489B599C674C4D4B02994195F0CB8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E648E7E0FEF93D9E1DF4F61895535DB13E7F83869EEE30BE84210050A6D2CC15" + }, + { + "Name": "DEC IX", + "HexInstructions": "DD,2B", + "CpuHash": "8BA110867F652F7D496987C50AD074D061ACA9CB0B42A9C5E8022D52300A6DA4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0570F1047AF09F15C77905492A85D0FC05CC8C35D56DC8E0A533D1D29F6BD4E0" + }, + { + "Name": "DEC (IX\u002Bd)", + "HexInstructions": "DD,35", + "CpuHash": "22C7092287CAAFF8147290D3FA766B08CFDF3B7C8A585BCAAB153FA7286CC50E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9481DA6CD21DCEBBDCD4AAF195DC259D55ED7DA51268FF858C4DE099F6B97142" + }, + { + "Name": "ADD A, IXH", + "HexInstructions": "DD,84", + "CpuHash": "D0D0B2356CF79059DCDC215DDED3147C9B78D20DB11E4EC3710E357E4AF8D586", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "91D2B31C0183327E9029F7B80F56537F733AD50DD7A4D35B8F705B2DAEFD5A3D" + }, + { + "Name": "ADD A, IXL", + "HexInstructions": "DD,85", + "CpuHash": "7FC259F7426E87825AA645108F4EFAC488A33E7CFB216BB21FE667C777B49AB6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B3B4933161D07C06DF659257C56E16688C773914A386A15E0CCE87DF30DBE3AC" + }, + { + "Name": "ADC H, IXH", + "HexInstructions": "DD,8C", + "CpuHash": "D0D0B2356CF79059DCDC215DDED3147C9B78D20DB11E4EC3710E357E4AF8D586", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "76A6D685420983DDFF46585549AC1B033269EF39506B7658034B700178A6580E" + }, + { + "Name": "ADC L, IXL", + "HexInstructions": "DD,8D", + "CpuHash": "7FC259F7426E87825AA645108F4EFAC488A33E7CFB216BB21FE667C777B49AB6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DC85BDD2D5B384176204902F2DEE79D5684FD965C1957498994352B871E0EBFE" + }, + { + "Name": "SUB A, IXH", + "HexInstructions": "DD,94", + "CpuHash": "06078B27FF37302C5B015052A53B69B370DCC80EB21ED2F3D34C93DCA3B87F28", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0D9BAEE80C89719748E6F716A6C124EE06A643E7BF802140D83841A96154940D" + }, + { + "Name": "SUB A, IXH", + "HexInstructions": "DD,95", + "CpuHash": "76E3D227DDA725627AE69EDCDD76650E470446F8983746878F757A6C0BB5363F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9ABECAF046091EDDFBE3A166C7C3C7A466E267F21B028CBE5833014DF372EBFC" + }, + { + "Name": "SBC A, IXH", + "HexInstructions": "DD,9C", + "CpuHash": "06078B27FF37302C5B015052A53B69B370DCC80EB21ED2F3D34C93DCA3B87F28", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2EA2D800884F40224E384D8891A29D8CC2609F2BF33FC8E62EADFDE510F99633" + }, + { + "Name": "SBC A, IXL", + "HexInstructions": "DD,9D", + "CpuHash": "76E3D227DDA725627AE69EDCDD76650E470446F8983746878F757A6C0BB5363F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F80136F474A9824378DED593030A1EB7FBA822EE5843445B24BC14BD4CAFE840" + }, + { + "Name": "AND IXH", + "HexInstructions": "DD,A4", + "CpuHash": "8A41F8767E3DDD1DB789161C9E520CD8B243EFD4799D48142DA00639C4E29C21", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CB757302EE722A8B701D84B9AA354893DC28AB5167C3AF9706D3C0F0088B0D2E" + }, + { + "Name": "AND IXL", + "HexInstructions": "DD,A5", + "CpuHash": "C73BB87834AC9BE060B7646B2B6C798AAE87759B9FF32FD7BF7926CF3D7082B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "755258AFCA1DF1BA5A47FFEBDFB08051409272666795E1BC53BF3FA778B14151" + }, + { + "Name": "XOR IXH", + "HexInstructions": "DD,AC", + "CpuHash": "EBC1A6AC8CD0A1D0F00C87F052F19DE2E3626EF3EC49AA963BECA2A440403FA3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E0B49543717E4EAFBD88F833D655C069B129207B825A0C0B6A4C58485674CC2B" + }, + { + "Name": "XOR IXL", + "HexInstructions": "DD,AD", + "CpuHash": "70A5FAB6D56096982EBC26B8140EF260718C3D0B639F7C2C05675224C5FF0E77", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "41313E5154FC3395C894D397A20F669EFE0B0086D050F45C1CD3EDDE40B182E4" + }, + { + "Name": "OR IXH", + "HexInstructions": "DD,B4", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4818A792F50CE739E6A1259BD81119B8778BFC321D4B76E0433EAF28DDEAA734" + }, + { + "Name": "OR IXL", + "HexInstructions": "DD,B5", + "CpuHash": "70A5FAB6D56096982EBC26B8140EF260718C3D0B639F7C2C05675224C5FF0E77", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "00B0FFE94DC27C22B5D3B2EA9BF74097B98BD816198FF922DD6757B82C776734" + }, + { + "Name": "CP IXH", + "HexInstructions": "DD,BC", + "CpuHash": "8DAE199D9A7B6D0456A03239EE41BBA2EC5BC803363DFACB6713E63DD94B7CDC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "490C9D2F301B1ADDE6E7278759C7C186B7AFBBDE3EB1FB65D96A10146D4AB05B" + }, + { + "Name": "CP IXL", + "HexInstructions": "DD,BD", + "CpuHash": "2BF6CC7176A4DD653CC21EAA9E3409ED53C2E5E6160E64D71D9475145F1DD765", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "250611ABC6C2D10D3EF9FDA35B20C6120D3734D5D17E882E1F693A440585948D" + }, + { + "Name": "INC IXH", + "HexInstructions": "DD,24", + "CpuHash": "F8DE32DE080EAEB415085CCC7534C4B9014864A61419CD74E10C9CF87649EB6B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "68F13D61855A0F7799D74567CB9C24CB5398A38121B201221241B22CD2AAA444" + }, + { + "Name": "INC IXL", + "HexInstructions": "DD,2C", + "CpuHash": "42908DA9F1365977316068179E429C5611EE5AB3707DCA4AAD8E665874D4438D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D796044F215AB53E5F82C259E60522CD77A6F670EEBB8D066222AF2EE4FCAFE8" + }, + { + "Name": "DEC IXH", + "HexInstructions": "DD,25", + "CpuHash": "6E53C8BA58061CB575D242AE5F97722D1720A854715D7D56E45039E277B197E8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C542B19BE9E1FC172498EA729AED731004F9B1319DACA4EF3D2DDD192DA6AC5C" + }, + { + "Name": "DEC IXL", + "HexInstructions": "DD,2D", + "CpuHash": "72EE71287173AB8D3E6ADBF4F5F0DB2469FCECE305913258559A11F55EA4E138", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "98ED3382EC64D60E1D09D505AFCFB1598E19050141B65423CE253A9D0C13E5C4" + }, + { + "Name": "LD (IX\u002Bd),A", + "HexInstructions": "DD,77", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F54AB706911994672910647DCFA98626D00E439E23BA1B13E7AE2E000F7E558E" + }, + { + "Name": "LD (IX\u002Bd),B", + "HexInstructions": "DD,70", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4D08247BE938826C0DCDBF7AC28900EF9C5853371EFA874B48A5391E937534A7" + }, + { + "Name": "LD (IX\u002Bd),C", + "HexInstructions": "DD,71", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "86A4EFAB16F43F2C27CF8D90CE290FF427C249F582B93EF33420789FEBFC79B1" + }, + { + "Name": "LD (IX\u002Bd),D", + "HexInstructions": "DD,72", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1C8C46BF61559F90F794BCA4D68180D4118B71C0F7D61D7CACB55D9D0365112A" + }, + { + "Name": "LD (IX\u002Bd),E", + "HexInstructions": "DD,73", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AE22FBB5632925950F2ACDABB96E8EEAB2BA3152237B441D9966A2C91E0FA05A" + }, + { + "Name": "LD (IX\u002Bd),H", + "HexInstructions": "DD,74", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8B791E478B15ABA1D5ACFFE0A8A6E9B8DACDBD333D369D93E1A8716263837AF2" + }, + { + "Name": "LD (IX\u002Bd),L", + "HexInstructions": "DD,75", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "48CC81892D271F66C99BA092E51ED926E0EAC784A296EE95FC9D4C7E285AED4F" + }, + { + "Name": "LD SP,IX", + "HexInstructions": "DD,F9", + "CpuHash": "CD3690CB6D5D2185A3F7D729B08811F20C47F5D0FCE755D826932F0F2AA1BBBC", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "103658903AFEF5E1352A3F7016BDF95B688C7C45B9AF62579E5BA63F33216123" + }, + { + "Name": "LD A,r", + "HexInstructions": "DD,78", + "CpuHash": "D0D0B2356CF79059DCDC215DDED3147C9B78D20DB11E4EC3710E357E4AF8D586", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1D50FFFAB5FA58880284AFA798A30D0C3C3B793D9FF2337A5A2933E97E98BC33" + }, + { + "Name": "LD A,r", + "HexInstructions": "DD,79", + "CpuHash": "BBBC35A5AF17BEF4481BB2A64EB937F3B09CC3F3A5765E4DF676333F07F0A6B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "44DF6A3ECF86938C2D69480D545085FB063C5D3E8F46748D1666EEAF7771B489" + }, + { + "Name": "LD A,r", + "HexInstructions": "DD,7A", + "CpuHash": "519F4DA559E5873719635DB08CE23722EEEFA49D2E5D2B82FDBA6D36A4F1D8BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EAB02E0D7AE696A4530732541C4048DE5A8A5E1003E1DD9A85A23D4A1A150BCB" + }, + { + "Name": "LD A,r", + "HexInstructions": "DD,7B", + "CpuHash": "0320E8A8360FC87CAFF3364642D22CD588D845C03F4A440E4D0F66CD53AD5942", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5B7676668D7F98CD3D9EC58CD998BCD61D6303A2F3BF31909876AD73FA355042" + }, + { + "Name": "LD B,r", + "HexInstructions": "DD,40", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "EABE512C6CBC440B055E12CCB0220F077161FE9E9C0EB887FA3AB677C21485AD" + }, + { + "Name": "LD B,r", + "HexInstructions": "DD,41", + "CpuHash": "2D75FAA4FD72B2BF40A51994B7494314A9AAC0C84ADB3CFB4C64BC3B3CAEB0CE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "323B25501771B4F914A835A89729D8062AF1EC95105D3F791C18A8F2AAE82A03" + }, + { + "Name": "LD B,r", + "HexInstructions": "DD,42", + "CpuHash": "7703F30F8785AEEE3C42D8591D94AA96169CF73523E657AEFB9852CB81FB1FAD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F7FDA04E09BE2B95EDE1619AE427A70DB3C6150A3E57CDAD1015CD94105D5E3E" + }, + { + "Name": "LD B,r", + "HexInstructions": "DD,43", + "CpuHash": "909AAB0D259DAF28660DEC7969C3FB644CC0B869F7FDF6BA0B047D6F9E6DBA9F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AE9B36E53EA32342C7038CF7AC095C244C41446F5C1639E8FDC16FCE3910EA3C" + }, + { + "Name": "LD C,r", + "HexInstructions": "DD,48", + "CpuHash": "A6BCF0A77ED806CD7972DAFAEE535B8984F4F18E5B8731BEAB50898C57FE160F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2C426026E33B15FA6D9D785BFD981C21790817D74898AE188A58C5AB92FAF596" + }, + { + "Name": "LD C,r", + "HexInstructions": "DD,49", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "760B7A29B11E8A2ABA8783F54DD94C918857692904EDCDEA05B4574BBB4A92DC" + }, + { + "Name": "LD C,r", + "HexInstructions": "DD,4A", + "CpuHash": "CA93AD06604B4FC09DD596726B4F8CAC40C1485A38422BB3AD49DC3FDE39777B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "540DD853AB8685B3A419B1D7D31371E36E374826BD9BA27F86863A07EBA0830C" + }, + { + "Name": "LD C,r", + "HexInstructions": "DD,4B", + "CpuHash": "77A200CCAC438F8EED7519BFFEACA49B43E4530219A336FB546F957C5F443B15", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9A443F0D2E7104E8493175B572057C5298C244B980890361E3A8EA90FE031E4B" + }, + { + "Name": "LD D,r", + "HexInstructions": "DD,50", + "CpuHash": "C4BC994D587BE09E0EAB35205E4664E840EC2771B9230F68F4BFB2EC9EF6AD65", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BEDC5623CE212C070ABE00FEA7B5D9E0D5C782E7051C8EE516F95A1EB6474D9E" + }, + { + "Name": "LD D,r", + "HexInstructions": "DD,51", + "CpuHash": "699185250E2270B8555589A1B037575C740FCF5A3F1B201CAB394A8E13DE916C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "449A3E6F9DF788085B4C809CD39DB3F83FC93F54644B2CAE102E8E22D4D850B0" + }, + { + "Name": "LD D,r", + "HexInstructions": "DD,52", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4CFA96431FEE8FB976262AFE681BE9E7492426742C4BDF97D587F3347405400B" + }, + { + "Name": "LD D,r", + "HexInstructions": "DD,53", + "CpuHash": "29853618EDB5E139E6496859B552E33D38D2502E81A9725C844533F916424404", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E18AC0826201CE68B180855563A6D320BB92648DF07CFA5A39B561BC9F3F514" + }, + { + "Name": "LD E,r", + "HexInstructions": "DD,58", + "CpuHash": "A10D89062C32231FEC0444702FEF885D1342EA7A7299A9EAF18069518612E822", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "507331822A01F5AEE7D34AE94C706F4003A3B020934817418C21CFFDA55CFD32" + }, + { + "Name": "LD E,r", + "HexInstructions": "DD,59", + "CpuHash": "DF056B0A37C8841FFFDCFD09148514DD24D08416A603795EE4F211D540548E80", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F7E725F4598237E72B497760A9B787BA7CDCB6E86FD6F5D75E58D3E435A3CC06" + }, + { + "Name": "LD E,r", + "HexInstructions": "DD,5A", + "CpuHash": "571F775CCF4557BE5ACA6DAA4191533B73433A07F1DFDA43141817A8FC4997F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "91E94EA537D0B8C7BED1907060FCD24EBDF622B1F37B4952B071FCBAD3A99F6F" + }, + { + "Name": "LD E,r", + "HexInstructions": "DD,5B", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DA886D2308A53D9CCCF0F6DA19446AF0461A7F721D7B975B4AA08C93E4D106EC" + }, + { + "Name": "LD A,A", + "HexInstructions": "DD,7F", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D5B5E59DD6B7237E46D651A242829D8B2836F4059A57ACB2D52DF284412E2F40" + }, + { + "Name": "LD A,B", + "HexInstructions": "DD,47", + "CpuHash": "C690C684A3DEB33446FB7DC9F4458548D18CA11F961A0C59537896B31571365F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "47C4C657E0C34B1AF1C634CB6C1216BC946FEA52A260BF9DF7FB9F74D5507CB0" + }, + { + "Name": "LD A,C", + "HexInstructions": "DD,4F", + "CpuHash": "5945A6697F86D59F984CFD72A2C2539C4A90BDA0ECB2F6239FC9F71C87C2680C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "37B3E5A2B412606C4EB51C6B8D5E26A8766D35CFDBDA60D45642D4D699475B57" + }, + { + "Name": "LD A,D", + "HexInstructions": "DD,57", + "CpuHash": "057EA7D7B9EB8E09F87EF845796EFAE916AF468E0D8408921E4FE30D0F5F7EFE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CFE5B6B62B9EF6F10A413D6CFC809081ECB52A1FA08148C663BFD8B43A53E2D3" + }, + { + "Name": "LD A,E", + "HexInstructions": "DD,5F", + "CpuHash": "6CC1858460107F780440B063C2BD99982551CEBF3AA38687495654278F8A6911", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5809237B66A20F56A73B962196064C5EBF55976AD55306DB2D11CE0691B09806" + }, + { + "Name": "LD IXH,N", + "HexInstructions": "DD,26", + "CpuHash": "39CEC6057A2FA0A233211E66C7E1DEEB9E10D4F0C201DC61DA49901B7B374B41", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "72A6C37AD5E30D177EFFDEF5E2E1AB88ED37F41C7828910DC8F020024EA652EB" + }, + { + "Name": "LD IXL,N", + "HexInstructions": "DD,2E", + "CpuHash": "652A379ADCB8CE98A1D4F03036673982EFE6312CF67B48AA8CC722832A3CAA11", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CB7701D1BEAEB6F6CDC031CA1C38EFE116FEF7A708E33584EDFE0186B24FCDB3" + }, + { + "Name": "LD A, IXH", + "HexInstructions": "DD,7C", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FBFD4AB214F5DC62876AD5E01DDFC2BA2A8998C315782E06293843F3F875549C" + }, + { + "Name": "LD B, IXH", + "HexInstructions": "DD,44", + "CpuHash": "214DFBE947345BB6A9B1268891DC31FA09EA55A4E9242906BC97D0A26D1B434B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C6553DB71C833BAE55FF5C93A6B85F98D0B43CA7D7FB6E09D7EC91AC1FE4BE01" + }, + { + "Name": "LD C, IXH", + "HexInstructions": "DD,4C", + "CpuHash": "5F70B026DDD27B4B7C64B3FEBF9DB27DFB342F504410392CA15079F0596160E2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A7DFFBAFDFD0D280143BA272DDAD631EFC3BB87ED08B0E4FF8A6222009E7F1F2" + }, + { + "Name": "LD D, IXH", + "HexInstructions": "DD,54", + "CpuHash": "E880AC0B43F7537B6595B9108B38A3F07A28E9941CAD14B77CFDD65B99250939", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2D539C277D41F27A189624F18335EF16927417E554544D658B880EF1EA1168CF" + }, + { + "Name": "LD E, IXH", + "HexInstructions": "DD,5C", + "CpuHash": "274BB3BC78D624A46334AFC7E47999E1F4317CC801B878D5D775B30747B7EBD7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F416ABD1AE534261A4F8CC47423D139FAF2DEA50483B1223BCF68918BB797808" + }, + { + "Name": "LD A, IXL", + "HexInstructions": "DD,7D", + "CpuHash": "5EF8CD8A3BDB826CA94120A312E71526B3E02024D62B30D086DBDD80757845AF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C995E8CDC6DEAF0E21440C713D783005A3E7E25741EDF7AB7D24FFE8B41633B1" + }, + { + "Name": "LD B, IXL", + "HexInstructions": "DD,45", + "CpuHash": "DE2D94B05CAE48C906EBB92F4926BE384850B34E5A6735C144997BAA8B8DF557", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A11D86EE1EC3A2B055067D4EE17D4AC67C82A110FFD4596ABB47CD4518185605" + }, + { + "Name": "LD C, IXL", + "HexInstructions": "DD,4D", + "CpuHash": "AF93D8ACF68DF1E38AA2240B169764744CC9B7101657B1280EC4649D935DCDA4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8A70F862A3DC8248671FAE6BF201D941C46BF89329E9D02765EA6D4FB97B261E" + }, + { + "Name": "LD D, IXL", + "HexInstructions": "DD,55", + "CpuHash": "A4ECD1AAD3615E96F63F59B519D098E0763680990D6BB9651B9B5E936E6961D6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5CD6F422AA74463BDCCD63DA53F44A68D860D6F1AF11ACC799CEF4D35BCB9C0B" + }, + { + "Name": "LD E, IXL", + "HexInstructions": "DD,5D", + "CpuHash": "B2AAEDE9D504D00C6F452AE2C3C416DFD2AA21F1E4B7E1694CF18DC04907D9B7", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8B10EE02B5225B11D94C48D29ADFAE843B8220EA306407567E31587E744D004A" + }, + { + "Name": "LD IXH, A", + "HexInstructions": "DD,67", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1624C81DE44BFF5BE991ADC84C1FAD2895F5E9C37A24CD1C8BEE86B47FEACECD" + }, + { + "Name": "LD IXH, B", + "HexInstructions": "DD,60", + "CpuHash": "1BB83444B22DF11EF5CB084451976D14EE010B701E34652E446C94D015DBE55B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BB15E1E53E6E65601776AC264055A6D2CEE791CA0DB0BCFEC42E805AE52907C7" + }, + { + "Name": "LD IXH, C", + "HexInstructions": "DD,61", + "CpuHash": "F3FC2175DF2FB7C22ADD364CA3057A102416C6F871D4DA83040EAEA5B7D1841F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F7872FE39C51C849BA91ECA07EEC00928FFE1DA8310F5A44DA2C1FB75ACC1600" + }, + { + "Name": "LD IXH, D", + "HexInstructions": "DD,62", + "CpuHash": "7142B28D4A41AD2C7650C99F0CB3E8E20C0573BF069BF9DA4FC3B394C087BC48", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F95E2756CA9EC3FF85098D5A17D19C10F2E1D84EFED4181E247EE26AF009544F" + }, + { + "Name": "LD IXH, E", + "HexInstructions": "DD,63", + "CpuHash": "9B8E1CE76D84AB1852F059D338E60E2291839021DA20C8876BE6B6230E8C411F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E3B898E3F4E62A64A90BBB3226292FA4FF4A3D764647E82472F3E504255A8CC7" + }, + { + "Name": "LD IXH, IXH", + "HexInstructions": "DD,64", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "618F22AA1F6AA419DD8B4DBBC288B5B316BB0D4E9DA8B7324D45CE8C0284939B" + }, + { + "Name": "LD IXH, IXL", + "HexInstructions": "DD,65", + "CpuHash": "EAE500BBB5D5FC1F456B9A06B0333DBBE8F14B801739642DAC646211376B96F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7A3CC78AC292508C2108F41FEE4AED59B125DC939F7BE5078090345FBF1432A4" + }, + { + "Name": "LD IXL, A", + "HexInstructions": "DD,6F", + "CpuHash": "D5AC74D8997CC4913091160F49868A2529C087A00D808A8094222AA5AB34E7F0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8B3DD11C2825CC98FF11FE5FB5E0F87277E00E2BACD531C47511FD82C282FDAA" + }, + { + "Name": "LD IXL, B", + "HexInstructions": "DD,68", + "CpuHash": "BA9523DD3C6F97399A406E0758E90CFF7CE6DCE7E23074BBFEF03E7CB7EABF1E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5439B3535E387C93B62E9E42F3116FE20DBFA25641AC0A1EDF857FA9A29C0ED4" + }, + { + "Name": "LD IXL, C", + "HexInstructions": "DD,69", + "CpuHash": "01F59AEF1515E66CB5ACC00A23AB7C7E88A3494B61F38B0DB03594A8BD219051", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CE8B69F46DB72C5A98BF9BBBC5BE1804E004A8B5B6B6BDAED4CA85C48752545B" + }, + { + "Name": "LD IXL, D", + "HexInstructions": "DD,6A", + "CpuHash": "C9532124CFC0438B957D75C57AC261166C7CD66AC0933CE4FA56728355BB979F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7A5EFF52291D915ECA8114A92D577C728C2468C37DED71848A699FC28C58A9D3" + }, + { + "Name": "LD IXL, E", + "HexInstructions": "DD,6B", + "CpuHash": "E840684219A3775F029FABB4289613ADC04A76AB21E1D93D0088C1F1B3552964", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8E94B25C656A8C1EC341C27D3AB65097F7162F4CA0861D0583004B09364151F4" + }, + { + "Name": "LD IXL, IXH", + "HexInstructions": "DD,6C", + "CpuHash": "D5AC74D8997CC4913091160F49868A2529C087A00D808A8094222AA5AB34E7F0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0B8DA513EACD7E8762172CD8BB84E95B35DF1BFC68FE2C6C3C5CEFF2A14518EF" + }, + { + "Name": "LD IXL, IXL", + "HexInstructions": "DD,6D", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C14F97AAB1936B343235682A2EA70A5657DD01F9D4AC00B117EE899286B5E5D7" + }, + { + "Name": "LD A,(IX\u002Bd)", + "HexInstructions": "DD,7E", + "CpuHash": "BC4C3D9B5216B9383F1A7E7BBEAAECE3DFC4982035CA7847F52956D7C24DB73C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "55433E807028630BE1871777B11C84991843D92F96186B3ADD20CB7627EDBEC7" + }, + { + "Name": "LD B,(IX\u002Bd)", + "HexInstructions": "DD,46", + "CpuHash": "8C09D3051B3BC691F6497F8A46AEA0D636BC719F8DF7A5802A3250D4209462D5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "24C31F2FC13BAED15E6194B17A59B73037444105B6C72CC6F0A33CB433EF9450" + }, + { + "Name": "LD C,(IX\u002Bd)", + "HexInstructions": "DD,4E", + "CpuHash": "4B5ED864429F5927D480BD618669A8515C015A6D85E76B1D7766366A6377BB41", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2FFBCAF0D3C89EE8DC752A3E021ECAF3C446AF469B14C750F85B788B1DDC658E" + }, + { + "Name": "LD D,(IX\u002Bd)", + "HexInstructions": "DD,56", + "CpuHash": "DBB0BE2AFAED113960D79F7FA70ED17AE9B0A439D0D418C976A9556DB6E2E4EE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C147549CA9F3B52C4B5E584E2CC31E47535CCCE4CE71E907DB06C3A6AA997A42" + }, + { + "Name": "LD E,(IX\u002Bd)", + "HexInstructions": "DD,5E", + "CpuHash": "14B62C7B056220972E5B8AB1C75C91098BD604987A6C1E8B8B769E292B8F4E6C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B7BEFA0B41F9DDE5E9C83F5F41D061B4F85712B72B368900965E351D28458B3B" + }, + { + "Name": "LD H,(IX\u002Bd)", + "HexInstructions": "DD,66", + "CpuHash": "73263552DFCD81878066DCF8A6260261DADC05B718A1F214D044CA27FD8A1AA4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "99DEEB5231B45863FAB30F388324F42ECF10B1ED5161C5E737C682F28E74F0B9" + }, + { + "Name": "LD L,(IX\u002Bd)", + "HexInstructions": "DD,6E", + "CpuHash": "29CD277148714BBFE810D41FFC14C8C19F45D17C89E33A51678E42334F90FD9A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9C36F718EA30A7A2712956B5D440DC68CBC7264FC9373D5EA3417692DFF4F0AA" + }, + { + "Name": "LD(IX \u002B d), N", + "HexInstructions": "DD,36", + "CpuHash": "D7EDDBA5276F3F63EC9C49FEC1D269BB04451160EFF55E76305CFAD35FCA2D7F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0633CC75C5F58D00A3CBDEEC2D15FA3FAEE7F1D650257AEFB13486BD163C8F0A" + }, + { + "Name": "LD IX, (NN)", + "HexInstructions": "DD,2A", + "CpuHash": "A22272E3D170D2EA4C8F700B0D4F7C8ACD045F204AE1ADC2B598076FC41814B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9CBEE552C032778B03BA6CAA2A65F0DF191FF0A81DAD356A47B6E7A9C5F7C3F2" + }, + { + "Name": "LD IX, NN", + "HexInstructions": "DD,21", + "CpuHash": "E5DAA49D6A7B86FA7D97FD0D753415EA1389FD3662913C404F702792E1F8B523", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6D8B564EB7446DCFC2BC890A0088D739A9FFD833A43290E165DF3868B3CDBA65" + }, + { + "Name": "LD(NN), IX", + "HexInstructions": "DD,22", + "CpuHash": "03DB8D1286CFAFA5C0E6B41BA881070E56C97515CEF0B4AFAD2E9FDAB69D7A44", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0822B6DD0AEB3B330E2423104CE019892A7D2A8442510BFB88768900E98B768C" + }, + { + "Name": "PUSH IX", + "HexInstructions": "DD,E5", + "CpuHash": "8DDBDB748ED7B5CB16DD2C53C3D55721BC63BBBACF758B8311A374C9DF015EB2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9FF7D16FB2D554B2B1D4EDB4F40E665EE7F6B889598A8485A28045ED354B8EDF" + }, + { + "Name": "POP IX", + "HexInstructions": "DD,E1", + "CpuHash": "5E0B4DF729F32B0DA0C74F6BE521667F750CBD792FACD7B7B5C8EC5112D57107", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A05E219DA786B9B2C267FACB01206BD55DFA13E944EDC35B7ECF2BFB5A95CA64" + }, + { + "Name": "EX (SP),IX", + "HexInstructions": "DD,E3", + "CpuHash": "19DD2AF1318E65557AF64F4AB551C27DC5AA7BEE3F1DABDBB0D6FBCFCC4ACE1E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6C7AE0607A786640F96B27BFDFC4B09EEE54D68FDE23F03CB1615E1EFE1F20E0" + }, + { + "Name": "IM 0", + "HexInstructions": "ED,46", + "CpuHash": "5307502123032621767FA73E24FDB9DC6388A8428457EE2310243FD2C76429F8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0C5C3CBFE28486524B41307EBA503623121167A35AF3FA2FA4CAF287A81493EE" + }, + { + "Name": "IM 1", + "HexInstructions": "ED,56", + "CpuHash": "76895E7BFE6FAE02DCB0A9685130F89CF5F078FCAF000B6C85A8786815364C48", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5235AFA81417367D83A367A209F3851CBCE7E34B707FE74D4D603FE403CE3915" + }, + { + "Name": "IM 2", + "HexInstructions": "ED,5E", + "CpuHash": "8623FC31C7F9C88D23325E71FAC2C47D5F986F97C36119EAACB34C2D11C8C58C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "36CE58C8EC8B8A88D98BAE483DD6FF04731EA0428B6C91D8DDC5B3E608B82675" + }, + { + "Name": "RETI", + "HexInstructions": "ED,4D", + "CpuHash": "9A41221CEDDB03911C075B2F85D85FA6068BB283EEB6D0340169F41217699559", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "366A621A69C63C02DA14D5FE616EF4C958B5E97380395219BD65B1C8954FB7D1" + }, + { + "Name": "RETN", + "HexInstructions": "ED,45", + "CpuHash": "9A41221CEDDB03911C075B2F85D85FA6068BB283EEB6D0340169F41217699559", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7EDA03B5A453716786D2F704AE3093D4D547E695FC1D6F3079E1541A914E9310" + }, + { + "Name": "ADC HL,BC", + "HexInstructions": "ED,4A", + "CpuHash": "F8C63070BD31BA7FB6869FAA64B2ED2A1D326B39344ADACD83DFC20F82C9A1D9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "15609812FA13A78550331A240A3CFF8EADAE5EB6B25F98E52E2866E661B2D042" + }, + { + "Name": "ADC HL,DE", + "HexInstructions": "ED,5A", + "CpuHash": "DCC8FA52CC6AE48546EAD864FE47857F99E2F1BFE436B64273C9C586745BA136", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "55DF2F5CB30D9E8E43A2CE4C9CCFB2EE9A9BD7CA2F3DC896CAD16DC9033EDEEA" + }, + { + "Name": "ADC HL,HL", + "HexInstructions": "ED,6A", + "CpuHash": "A20C39F2C04504DE363F031C4F89B757E28B47070D33B605B67A71840CD20211", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B246E9D75FADEA3E35BB32A6138DB9B1B5F4AA787E2EEEF7F822920607B86237" + }, + { + "Name": "ADC HL,SP", + "HexInstructions": "ED,7A", + "CpuHash": "2242B5BAD91281AFE26F7C85B7FE6443F6FCF4C7EA068CDFD79C4994B381047A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "50C7014FE8C9A0DD965915BBA648B4F32FC0765AC5E30A9FCBF0D61968651682" + }, + { + "Name": "SBC HL,BC", + "HexInstructions": "ED,42", + "CpuHash": "DB59FF715B75688D1DADA89DFB89476A01F10B8A531DEEDCF499E17C6165387A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "15C39BFDBA24AC8A9868DE9994252EE0951161D4BD78DBA4A403E94459DD89C1" + }, + { + "Name": "SBC HL,DE", + "HexInstructions": "ED,52", + "CpuHash": "CD11EECEF1390600CB6D8D8FABB320E3C5EF7B76F44B7F1A5927E2EB2A292E91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7FA009B8835E6D18E7EE80B3AF8D01D8F7244937D57923496FB39D093013AA1D" + }, + { + "Name": "SBC HL,HL", + "HexInstructions": "ED,62", + "CpuHash": "293EC9C9E47D58475E608DB1A59E89D1D1711326D2B16862E73DF23563D52E8F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C96F4049918ED8898539EE3BBC4D292E003D26EBBC173F3EB1FC7BDD43A7B987" + }, + { + "Name": "SBC HL,SP", + "HexInstructions": "ED,72", + "CpuHash": "E2F1311710A9C183B98DB32BC60FB2B4809C75A5876CB516D8DD5B632EB43C8E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D19F2EA6D1AD2E814517355AFA589A09CD57525F2716ED163A1D2993057B5CCF" + }, + { + "Name": "CPI", + "HexInstructions": "ED,A1", + "CpuHash": "F68FE1A9C4B841BAB6CC6CB0B46A60A6BA797EDFAA4C9D4AEBE9C429471A19BB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6981E9A7EE79FA40D0237CE5C359B9FFAB82E89403516F8FD3338299A91FD95D" + }, + { + "Name": "CPIR", + "HexInstructions": "ED,B1", + "CpuHash": "49066EB6E1F2B653D43D8DA2DF698AB7AC0E1B99BCB86BBECC3056412FE905B3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3352B73CE802C8BD847116C19748396AF2D03A007E1A18FBA3EF261CBCEA733C" + }, + { + "Name": "CPD", + "HexInstructions": "ED,A9", + "CpuHash": "AEEB6E64A43BD8B05AD464D8F295E72A5BF090B137C85F3F87E206BB60B2E050", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A4E907015305FE10EDD31D3FC6B65B6228BB99FE2ABA2EA5810128A7FF101CE6" + }, + { + "Name": "CPDR", + "HexInstructions": "ED,B9", + "CpuHash": "F6BF855CFC40A590CF6FA5C1B42F1055426663311AA976AC85FF78D474195363", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ED8CD41135E4B7146867303E10BDF85A5CCB2E63FE698F43A888F65E40F57D64" + }, + { + "Name": "NEG", + "HexInstructions": "ED,44", + "CpuHash": "1F72C49626A56933082BFDC1D095D90387AB6A3F2901F55E00ACDDACEBC7513A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "24345EB1DF065CD7593B8E7A0766B4136D60A76DF55BD623C44B64CBC51AD955" + }, + { + "Name": "RLD", + "HexInstructions": "ED,6F", + "CpuHash": "E06074107BD4C5EB3A01EEF428977E5D326C1447CA2B7FB8C02CE8FEEBAF6A83", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B78246F90131EFA5CC562DD4EA2CB94DC0909DC2D3C8D455E0619BC53467CC91" + }, + { + "Name": "RRD", + "HexInstructions": "ED,67", + "CpuHash": "E06074107BD4C5EB3A01EEF428977E5D326C1447CA2B7FB8C02CE8FEEBAF6A83", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "52B9E49299E769F4D45C038EF117BF1AB922ACC3D88A3031CF26F830577417AD" + }, + { + "Name": "LD I,A", + "HexInstructions": "ED,47", + "CpuHash": "6FF52673BB68DB98D805354D1A2B9B457EC3EF9C042628B8A64C9DEED283BCE9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D4017C0807487BE9526217DEF911D102DB4B2F068487BA84D1206305E0B02480" + }, + { + "Name": "LD R,A", + "HexInstructions": "ED,4F", + "CpuHash": "3C8798728EF0A6388E257C2E01E46810D23BF788D073E787226151989EBCE411", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F601AEDF93782102D3BE31131BAE1760FF4C6665C8FED49BDFC8F0411F6DFD4B" + }, + { + "Name": "LD A,I", + "HexInstructions": "ED,57", + "CpuHash": "7DBA97B956F6A73E0200464511D5627B5B631A70E2E88931B31FE597B1D89104", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FC0FE3D2D663F23D92E659536E49DFB2ACD05E7B2A0D8F37BAF0284E95EE1380" + }, + { + "Name": "LD A,R", + "HexInstructions": "ED,5F", + "CpuHash": "7DBA97B956F6A73E0200464511D5627B5B631A70E2E88931B31FE597B1D89104", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7EEE0CA2B8ACBDC4F46F016C39B7541D13274B8F62C3ECC5737D9E4F022268BD" + }, + { + "Name": "LD BC, (NN)", + "HexInstructions": "ED,4B", + "CpuHash": "220EAC064099255CFAF446D0D99F12C8B04B1B27561548409F1DC062F257B917", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8B2BC0620B1775994F8A0033F5FD6679DD582EA093FE6A93B938EA8AAFA59E65" + }, + { + "Name": "LD DE, (NN)", + "HexInstructions": "ED,5B", + "CpuHash": "1299B688C211FC666CF1262D0C5A6B5BB7861E06804BA4A8419556CE82ED85F5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "85EE4CD5EC3CBA4E8A6586321E82520F86469ADD5F6F4AD3A5AE35978A3CD6C8" + }, + { + "Name": "LD SP, (NN)", + "HexInstructions": "ED,7B", + "CpuHash": "71D649B2BD688FC7E4925DC18EC53E5F4F5C03B86BDB7166C4703431A776622A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2F66CEEE4B2D6534DE54AB1A5F6EB50A22CCD6C0A5E3309351CCEEE9F97BF280" + }, + { + "Name": "LD(NN), BC", + "HexInstructions": "ED,43", + "CpuHash": "03DB8D1286CFAFA5C0E6B41BA881070E56C97515CEF0B4AFAD2E9FDAB69D7A44", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "56AAC8FAE5771E359534231AF6338AF4B5114F7C7FC3C043C3029A048ECB3341" + }, + { + "Name": "LD(NN), DE", + "HexInstructions": "ED,53", + "CpuHash": "03DB8D1286CFAFA5C0E6B41BA881070E56C97515CEF0B4AFAD2E9FDAB69D7A44", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "321B7BDEA36F060132D737BB00B0C6BAD8F686098C363C4BC82A09D048ED085D" + }, + { + "Name": "LD(NN), SP", + "HexInstructions": "ED,73", + "CpuHash": "03DB8D1286CFAFA5C0E6B41BA881070E56C97515CEF0B4AFAD2E9FDAB69D7A44", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "49C0016014CC4464F4B49EA4177C8F1DE0F80A397A110C722A1A13A325B5F3C2" + }, + { + "Name": "LDI", + "HexInstructions": "ED,A0", + "CpuHash": "E27694200D4B05B1FE68211C89F65ABA2286D43DE9B524986553D236AC90E995", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9D0DDAB190A7F8BA92F983547C22DEF2A122F421F72F8AAA57B0D837B423653A" + }, + { + "Name": "LDIR", + "HexInstructions": "ED,B0", + "CpuHash": "76BDB38396C3777998EF507551D6EBE6C186AEE44C54201156407BFD7A7CE189", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C91CC1D33CAD72A27CBE6C5BA6FCF3AFEE3E2E71AFB6342E5EB68E5E59C04D2C" + }, + { + "Name": "LDD", + "HexInstructions": "ED,A8", + "CpuHash": "444690690DC7F91042B7AA37A1A103E99E987EADADEBAE8843393D55D0D6346C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "10B84FFEE6D0EDF499ADC1248B1255F707259DCC4C310F9C2774D0987EA3F7D9" + }, + { + "Name": "LDDR", + "HexInstructions": "ED,B8", + "CpuHash": "D3AC6544F09E4801AB17FE4B935E6CCA650677CA95CC5AC6A650387C0745C969", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1AE1EEDB47131E9F6D6F1319A523BDF85561F390D05DE5CEA218233418462DFD" + }, + { + "Name": "IN (C)", + "HexInstructions": "ED,70", + "CpuHash": "D2031FFBFB76FE6D51EB476B1502EF3770EE149001D677C194D36C7458D57C25", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "78603DEA4E67A2941447A4CDC94A73544F30E0721463EF92557F044F62810FA5" + }, + { + "Name": "IN A,(C)", + "HexInstructions": "ED,78", + "CpuHash": "E97C6839864BBF54FC64D35A71218BDCD3BF16C6B98771A92BD2DF26EB9D0387", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9106B016E777CF9C053F16D51370035653A6889B80F1630E12113CBD8EDC94A5" + }, + { + "Name": "IN B,(C)", + "HexInstructions": "ED,40", + "CpuHash": "7E21D56609D48C44F41D67D007E14709A02902A1E3B622C99FCF518F71E00587", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9607D5AD881414E9D024C07E268D1BC003B2ABFE83E9889F99488695D547FCE5" + }, + { + "Name": "IN C,(C)", + "HexInstructions": "ED,48", + "CpuHash": "C7CB7792B59CC48E64A1DDF9D1AC7CF11B31173FE440A2F3AE593974C2350E12", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F9DC87857789085EB3D59F7AEAB1F15499CCAC51D71926E645FAF2126DD81259" + }, + { + "Name": "IN D,(C)", + "HexInstructions": "ED,50", + "CpuHash": "8BE1E2C1A0BC46EAC73969378F76FF1677043A9EA411C4CFE752BE8C569A4088", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2ABBF95E6AC6A2DB96B81185093EFC28833A0FB526B63621754AA4C9E8B91F88" + }, + { + "Name": "IN E,(C)", + "HexInstructions": "ED,58", + "CpuHash": "AC09652B841FC1362BBF0D54CBCC8C62ACF21B7B2F217CC08461A25F25B46860", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B76AF15BD87A630A3EB13283A948ECB433140FF98CB86AEB760FC860D1C4A144" + }, + { + "Name": "IN H,(C)", + "HexInstructions": "ED,60", + "CpuHash": "EEBEB1BF055927C48D39BD718ED7CFA3F452B0AD010049720111E3BF4CA84791", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D5D025620102D75BD173CA4A2A6852D6D62A27EC5DD321F80B92FE501F34CE10" + }, + { + "Name": "IN L,(C)", + "HexInstructions": "ED,68", + "CpuHash": "1F95AC7E6F73990DB7043395D8D1FB33D3D2A2792AC93A4451DCD25494AE9D4A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2E675B9BA75ED63F33800A8B7A285C3EB1527C2A32B6C273DBC9ACA568F0832D" + }, + { + "Name": "INI", + "HexInstructions": "ED,A2", + "CpuHash": "D3CC7F196BEAF66A8C27F55D86B8318A3C5F0099E854F4EB8CBE8A230C167C1A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "50361D782C689DDD03342651B9BE70DA8A448203593585888EF8F218C7C1554E" + }, + { + "Name": "INIR", + "HexInstructions": "ED,B2", + "CpuHash": "A39597E3FF2B22DF489BED17D41ECF4BA7259F03A6A37C0FA3370A65C871ED88", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "40A9A27ED815A74BA40A31875289EBCCD7DB0D66F6F802896D3ACF10A4AB7C1B" + }, + { + "Name": "IND", + "HexInstructions": "ED,AA", + "CpuHash": "35E6688F57BFC79A7C976BF72B84F7D91886A59E2F5325C5DB21F11632C1EDE9", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "64AEF707EB833424BBE986221C64A1152007DF40B8B2B07473BE17D1CC927DDB" + }, + { + "Name": "INDR", + "HexInstructions": "ED,BA", + "CpuHash": "40850C0A3A4916BBC0EFB236C18C4FE8849CB117AE9F65C169121AE132D31A05", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "947475BFCD97A462F094ABC5B1C84587A69B6E30BF40511A9B208566F89CBC0D" + }, + { + "Name": "OUT (C),A", + "HexInstructions": "ED,79", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "1366D2108CA5759FFCBF95459C92B2EAA2394AE16FED3B1565668A3797E6DA7C", + "MemoryHash": "0BB885B5EC083EED54A83907B21E16D6E4C6D9D922A8B346B14F56087583ADAD" + }, + { + "Name": "OUT (C),B", + "HexInstructions": "ED,41", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "051C5163807F0080331A83DE530F9DE92513EF5F36D589F38514F2915801435A", + "MemoryHash": "DAC66C04ED81922EACE02070EDEA4A9E5104B7341ECF23330CE215742C602CC4" + }, + { + "Name": "OUT (C),C", + "HexInstructions": "ED,49", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "408DB520055CE941E506DFEDB5AB6D57862ED3C6F68221BAEE6A7FD940547339", + "MemoryHash": "D59FFB5AC51F8835C1B32910955E847450F36D97A5B2956E5BBA64BA4D59E79C" + }, + { + "Name": "OUT (C),D", + "HexInstructions": "ED,51", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "ADD15790FAB670E879A1B81A0A24B1E5AF660E6A205E26C5BB3F6DE11D39905D", + "MemoryHash": "E3CC87E3495B6E362C70E56F36E41EE2E5EC013BEC34ACEEAA8FB81EDD2F33E6" + }, + { + "Name": "OUT (C),E", + "HexInstructions": "ED,59", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "7143CB756C6F601FB512EEF351F06B0417BEB79D3A04F01B65CB9F75423A6572", + "MemoryHash": "38A54E6199EC84BB2C27EA7F99893503C2A0CFF24CF93628BF29D4D2BF368E50" + }, + { + "Name": "OUT (C),H", + "HexInstructions": "ED,61", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "CC813CD9935D08792BE2438A03A0CA3E3023999E9C594B7EF0A790E20912DFD7", + "MemoryHash": "A88150BBBD44667706F46684E794B6DD78C8C90D0FDD5BAD2E4819CECA178F44" + }, + { + "Name": "OUT (C),L", + "HexInstructions": "ED,69", + "CpuHash": "DDFE31B138CCA1FEB6C2BFDAC4EB3EAC0ECAF7FAA209B78815F2B0433A2B88F8", + "IoHash": "4B579B182173E1CAED6FB69409761A712AD9052AB7E54E18B798845394F94B84", + "MemoryHash": "0F9B6C38630A618CC1E2811B5DCD2081993E9931F00A25BEB46FBFF9011371D9" + }, + { + "Name": "OUTI", + "HexInstructions": "ED,A3", + "CpuHash": "D3CC7F196BEAF66A8C27F55D86B8318A3C5F0099E854F4EB8CBE8A230C167C1A", + "IoHash": "202138E7BD38D534601A66A4424861998454210BE8715DB3545A337D873D074E", + "MemoryHash": "EBD5F3FAFA55AA9A5406ED201153B3D58F36AA59E14A48234D97AD87763CAA03" + }, + { + "Name": "OTIR", + "HexInstructions": "ED,B3", + "CpuHash": "A39597E3FF2B22DF489BED17D41ECF4BA7259F03A6A37C0FA3370A65C871ED88", + "IoHash": "202138E7BD38D534601A66A4424861998454210BE8715DB3545A337D873D074E", + "MemoryHash": "8CD9EB98274610223562247BA2FC51ADDF67A30BA569614B5F4066442F1AB585" + }, + { + "Name": "OUTD", + "HexInstructions": "ED,AB", + "CpuHash": "35E6688F57BFC79A7C976BF72B84F7D91886A59E2F5325C5DB21F11632C1EDE9", + "IoHash": "202138E7BD38D534601A66A4424861998454210BE8715DB3545A337D873D074E", + "MemoryHash": "07276B744F25E7D2F1BD6017F1B838F23CF026E9EA1D1CA9823D6F70D8549FD3" + }, + { + "Name": "OTDR", + "HexInstructions": "ED,BB", + "CpuHash": "40850C0A3A4916BBC0EFB236C18C4FE8849CB117AE9F65C169121AE132D31A05", + "IoHash": "202138E7BD38D534601A66A4424861998454210BE8715DB3545A337D873D074E", + "MemoryHash": "4EEFEA15A9136341E3D7DEBB85C985F3E6AFE34C3915C5F90C2513532AB154FD" + }, + { + "Name": "JP (IY)", + "HexInstructions": "FD,E9", + "CpuHash": "55D773E0A2C744F2EA9D84CA350BAFA8178A7790B0F1B3FCADF60347BFC8EF10", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1A48120701E973933EA4D387E643BCDEE73424FA722B82D5551F7D2591AEE11A" + }, + { + "Name": "ADD IY,BC", + "HexInstructions": "FD,09", + "CpuHash": "7B9EF0529193B8BC37BBF32CAEA0261B2473EE145BCFB1662FA929817C0658BB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5F5CAD344E7B2B782E812A781C26D454063AC9D2893F5EA26307E9C695A84D01" + }, + { + "Name": "ADD IY,DE", + "HexInstructions": "FD,19", + "CpuHash": "1F73536E10B1AE9F22F14618BD0F3076A966BBF7D4CDFB62C9485FD8467D248F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FD25DC42AFE22983FA9D854FF1A5C4D351F3B4932A451E4825561E842BE0638A" + }, + { + "Name": "ADD IY,IY", + "HexInstructions": "FD,29", + "CpuHash": "A0B473734E8EB362A560326858E4ACD0106A2B95ADBB2DAF55A18945D1BEE6EF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9570D2084B726AAC306F72B6B12A1EE36D01332A4FFFA0134D49F2ACDC02B1A9" + }, + { + "Name": "ADD IY,SP", + "HexInstructions": "FD,39", + "CpuHash": "17BC1BEBF8985F84AD093ACABB9C7C56CECD9484CF9E5BE47189E3C564A580D5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "16A3C02A248DABC6159DE848ABE918C70C6A920D170AF1466E55CCC16294957E" + }, + { + "Name": "ADD A,(IY\u002Bd)", + "HexInstructions": "FD,86", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "82FBE52B611A21870D1549EFF8615D5A552C9DF03FEB4FECB5608E9012FE646E" + }, + { + "Name": "ADC A,(IY\u002Bd)", + "HexInstructions": "FD,8E", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "CAC3943C6FDD7844D6EC7A941AA1F991110B4AF0F9FD423EF6CC2F9B7B37013A" + }, + { + "Name": "SUB (IY\u002Bd)", + "HexInstructions": "FD,96", + "CpuHash": "F75A3C247900FFDDE8BD98D3EA21EC64743CAB3DE596EA437ACE13A5119B8320", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D027CBD07832285A41CC444837C883DC7A8C40060F26D5251E5B07A441AB4670" + }, + { + "Name": "SBC A,(IY\u002Bd)", + "HexInstructions": "FD,9E", + "CpuHash": "F75A3C247900FFDDE8BD98D3EA21EC64743CAB3DE596EA437ACE13A5119B8320", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8694B7C2559A1B9F6A2BA57E5C1891C38EB3598636730B0B43F3811E9B17F50E" + }, + { + "Name": "CP (IY\u002Bd)", + "HexInstructions": "FD,BE", + "CpuHash": "3D64DAD112225D0AE062484F789F7DC9FB993D2357B6E91FC64F033770694BA6", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FCA7DBC407F2C74EF196197A2DFACC1F62C81123816CBFD9A7668B95526216F3" + }, + { + "Name": "AND (IY\u002Bd)", + "HexInstructions": "FD,A6", + "CpuHash": "D9BD49B0DCB9D852EE95639CD44C88798B38AB529F3563E499FA2A4A31C90487", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "412F1BFBC94593E1302B2F2FB23A5FD9CFFEF916AAFB77468FA36C84DEFFBED9" + }, + { + "Name": "OR (IY\u002Bd)", + "HexInstructions": "FD,B6", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DB49B07400554F56AFDB2D13DFB503BA8A39DDDCB9C71E29ED42B8A79E3B1CDA" + }, + { + "Name": "XOR (IY\u002Bd)", + "HexInstructions": "FD,AE", + "CpuHash": "F83CE5D81D0EA06E54149D7C36D9B3B349E560D856B57099F1FF282FAE944B9B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A4B9D5E84D97133368CFD8A50A120C84A12900CEB87C52194C9AB0DEE8A19613" + }, + { + "Name": "INC IY", + "HexInstructions": "FD,23", + "CpuHash": "8126CFE15099DA12D4047F97D8BB73EDD8B891FC4B55244115B18E54CAA7EA0F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8622A78B1A012D38ACBBC0DAF235A7B80F7E4171FEC7CF66B6DD900B20CFAF25" + }, + { + "Name": "INC (IY\u002Bd)", + "HexInstructions": "FD,34", + "CpuHash": "9889E3581018E5EC6D999F3B88FFBD09E85489B599C674C4D4B02994195F0CB8", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "87D7988ADA82ECA6CC28747BF28576B6CF0BBE64C08819B0FA460C3E923AFA0D" + }, + { + "Name": "DEC IY", + "HexInstructions": "FD,2B", + "CpuHash": "017933FB2AB39C57E70F3354216E8B500A3E507655970987093A023AC64E2503", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A0B641168E90C696DD03F26C0A935D5931218F75BCEF55F487F15976E9CDDFA8" + }, + { + "Name": "DEC (IY\u002Bd)", + "HexInstructions": "FD,35", + "CpuHash": "22C7092287CAAFF8147290D3FA766B08CFDF3B7C8A585BCAAB153FA7286CC50E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E85683DB6A6E352616B6C6A140DDF1460D53379C5C4649EA3BF0207E79BBCA0D" + }, + { + "Name": "ADD A, IYH", + "HexInstructions": "FD,84", + "CpuHash": "BBBC35A5AF17BEF4481BB2A64EB937F3B09CC3F3A5765E4DF676333F07F0A6B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "288B60136B318BC3F176C98594603629E0048EBC45CD7F79486E1511CA114F55" + }, + { + "Name": "ADD A, IYL", + "HexInstructions": "FD,85", + "CpuHash": "30088E507F93FBBD3D70CB2DA104CD2EF9CDB60B97D0682FC302A080B38E9C13", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "69ED07DCB1AD35E9A953F59AF8606F70667886F35ED16E42639AC6388A751579" + }, + { + "Name": "ADC H, IYH", + "HexInstructions": "FD,8C", + "CpuHash": "BBBC35A5AF17BEF4481BB2A64EB937F3B09CC3F3A5765E4DF676333F07F0A6B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7670638E3E23AC42B139079A6C455B01A34B3EE95CE4A66E37021A01EB6C1F0F" + }, + { + "Name": "ADC L, IYL", + "HexInstructions": "FD,8D", + "CpuHash": "30088E507F93FBBD3D70CB2DA104CD2EF9CDB60B97D0682FC302A080B38E9C13", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "958541D3294CED797C31AE67FDC9448A0E7714F48F134B81F5B8B2C73B7B3A70" + }, + { + "Name": "SUB A, IYH", + "HexInstructions": "FD,94", + "CpuHash": "6DFA067F1A87E0CAADDC7079E0D361C6D2BC6E0B8DC2BE2E55DAEB2F6CB876A3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4F394685A6381B614013197EC5081FF03D214C6826284DA9FA86517C27CC76B7" + }, + { + "Name": "SUB A, IYH", + "HexInstructions": "FD,95", + "CpuHash": "60CD02064276D0655AD73FE7FA803CE1AC096A32061B98B6AD421EE80C12D2D4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FCB78385702178AD85774EF9B3C5182CF68B7508FB0ABA75F8AB3A4148B30028" + }, + { + "Name": "SBC A, IYH", + "HexInstructions": "FD,9C", + "CpuHash": "6DFA067F1A87E0CAADDC7079E0D361C6D2BC6E0B8DC2BE2E55DAEB2F6CB876A3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "987A59A1572A62924D958E22A758C2182EB03CED042DBC62AE0287959B93D369" + }, + { + "Name": "SBC A, IYL", + "HexInstructions": "FD,9D", + "CpuHash": "60CD02064276D0655AD73FE7FA803CE1AC096A32061B98B6AD421EE80C12D2D4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7D1EA189F7B711496B36223F20EC8C9BCEC34F2710593AADC0158B39A47A7CFB" + }, + { + "Name": "AND IYH", + "HexInstructions": "FD,A4", + "CpuHash": "C73BB87834AC9BE060B7646B2B6C798AAE87759B9FF32FD7BF7926CF3D7082B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8162F95937720CCA8014C9CDB0E39AA506C57D310738803F850B17D50AAC924F" + }, + { + "Name": "AND IYL", + "HexInstructions": "FD,A5", + "CpuHash": "8A41F8767E3DDD1DB789161C9E520CD8B243EFD4799D48142DA00639C4E29C21", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FAA185608D0BDB7DFD99C9800E1D32318DC7EDF1047925A3397367971A9B6FC1" + }, + { + "Name": "XOR IYH", + "HexInstructions": "FD,AC", + "CpuHash": "914E26AFF480A87C5629529E54E6F97937A5428B4D5A1250F31D30D623CA3222", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C9319BCAE763C422958DBDB08E7EFE32EFF8F7D7A3EED9848905E94FB2A82392" + }, + { + "Name": "XOR IYL", + "HexInstructions": "FD,AD", + "CpuHash": "6611664399F7D6E13339827E4B240D67E7B29CDF594C2D0465C1D7BD278E0FA2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F6A26CA5CB62808F95B9CB479366D361ABA7F5DB6D8AB7A71BF8BF3D5E1F316F" + }, + { + "Name": "OR IYH", + "HexInstructions": "FD,B4", + "CpuHash": "914E26AFF480A87C5629529E54E6F97937A5428B4D5A1250F31D30D623CA3222", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "687B6343F52364FCF96E293EF2D02637A5A047FD91659523492CD17A10F725B1" + }, + { + "Name": "OR IYL", + "HexInstructions": "FD,B5", + "CpuHash": "70A5FAB6D56096982EBC26B8140EF260718C3D0B639F7C2C05675224C5FF0E77", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A7F4C372AC9707E36A0BA5D22BC10AE0599D74894B35586860FCC98A943D732" + }, + { + "Name": "CP IYH", + "HexInstructions": "FD,BC", + "CpuHash": "2BF6CC7176A4DD653CC21EAA9E3409ED53C2E5E6160E64D71D9475145F1DD765", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E58FCE431D313D399F938D69BC2D661269B6F5BEA3DE4EFFF6A8D515E3AC8B5A" + }, + { + "Name": "CP IYL", + "HexInstructions": "FD,BD", + "CpuHash": "2BF6CC7176A4DD653CC21EAA9E3409ED53C2E5E6160E64D71D9475145F1DD765", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E71C7C146F31A40A228D0AD50672C5014051A93FBA42BCC49D0A4481BB6454E6" + }, + { + "Name": "INC IYH", + "HexInstructions": "FD,24", + "CpuHash": "DA48CD67FA07F2C6D58834C07DFA472730B91005D98C85690C55E4D8BD99FF91", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AD1278F0D6966E0E7389A704AFB11DBC1E35C305F530F4E6CD0C7A0356A47AB2" + }, + { + "Name": "INC IYL", + "HexInstructions": "FD,2C", + "CpuHash": "F8046ECC2533FBAE66111BBE6182F86BB4307EADD36657AF106DFFA1904C9287", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B82DF3515636098B710300DF17F45BDEB771703E07BF5CDC91277970B9D2DF2D" + }, + { + "Name": "DEC IYH", + "HexInstructions": "FD,25", + "CpuHash": "7E32CDDB76ABCA7B4B8CA6B1383BFAF82F7B6F82DA3F913DBACC38C89D994EA1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1CD0170A7B677572D3A4C4B8D719CA7A6236A6B3E0DD2C1D696C4D85B58B95CD" + }, + { + "Name": "DEC IYL", + "HexInstructions": "FD,2D", + "CpuHash": "3446124CE735431D6A0C9C777D51CEBE2BD4C20CAB97DC7CE2570B970B82EC8D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D9C1CE2144454CFC8A1BC3A0A13F8849CD0C9238FBFE3913981E5DADC82E4FA5" + }, + { + "Name": "LD (IY\u002Bd),A", + "HexInstructions": "FD,77", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5D534EF41E498C8CEA55EAEC970B68DA29DBA689F5995916B62EA4DD3C6C3B86" + }, + { + "Name": "LD (IY\u002Bd),B", + "HexInstructions": "FD,70", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C0D4A4C9E8B6DD517AFF7F219C75A05D5C879FEDC6FFFCE646205160F8CEA688" + }, + { + "Name": "LD (IY\u002Bd),C", + "HexInstructions": "FD,71", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "DF0E3069B01D51F4804384BFEEFF763528E280E1EB430CB95EFE20496FCCD73F" + }, + { + "Name": "LD (IY\u002Bd),D", + "HexInstructions": "FD,72", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A6057CBB090DA6C649ED920B93F4A85FDF8D624747565AB477BCD9C8272F633" + }, + { + "Name": "LD (IY\u002Bd),E", + "HexInstructions": "FD,73", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "51109221A8E5C0EC5FCCC79CF6062E5B0C3597903F055B76DFFD5297055E8DBE" + }, + { + "Name": "LD (IY\u002Bd),H", + "HexInstructions": "FD,74", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6587F70A17A04BCF5FB8F81212F7694599457166F03F3A070D216331FFF54268" + }, + { + "Name": "LD (IY\u002Bd),L", + "HexInstructions": "FD,75", + "CpuHash": "750D1230DC062CA4FD8A72125561D6AB06468ABE1036B358040440638D881FDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2DE2E0DD444A7342FE7423849533FFFA3B423F493C7AAEED4F3FA93291C8FFE6" + }, + { + "Name": "LD SP,IY", + "HexInstructions": "FD,F9", + "CpuHash": "B91E1FBD7C6036DF85EBFCBF64809D7D0DAE01EAAD34FED827BD336E4ECD1310", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "00DB803E0973170822D21AFB83F6D4E80162F35831C2B25E12FC203247ECCCB7" + }, + { + "Name": "LD A,r", + "HexInstructions": "FD,78", + "CpuHash": "D0D0B2356CF79059DCDC215DDED3147C9B78D20DB11E4EC3710E357E4AF8D586", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3734BE2D71F5CF8A8F42A89D2D196F8CD31D6C540E38281C1553CC09CEFEF643" + }, + { + "Name": "LD A,r", + "HexInstructions": "FD,79", + "CpuHash": "BBBC35A5AF17BEF4481BB2A64EB937F3B09CC3F3A5765E4DF676333F07F0A6B0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "527E1DDB924936C0F4F6DC2C8F629CAC219EBED5EDFD20201F4C6EA03B5AC206" + }, + { + "Name": "LD A,r", + "HexInstructions": "FD,7A", + "CpuHash": "519F4DA559E5873719635DB08CE23722EEEFA49D2E5D2B82FDBA6D36A4F1D8BE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "48899791A985FFAAC4DD0B08E3BBE5FD6DBE353865BAFD6D45E27666705B094F" + }, + { + "Name": "LD A,r", + "HexInstructions": "FD,7B", + "CpuHash": "0320E8A8360FC87CAFF3364642D22CD588D845C03F4A440E4D0F66CD53AD5942", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2F385791046B419C4DAE87B9A272F9F8F938A1552B32FB23E18B0111ECB01696" + }, + { + "Name": "LD B,r", + "HexInstructions": "FD,40", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "494E6EE0DCAAD85410DCB1610F404D484F704941AF717BE42CF4ABE7688F6CED" + }, + { + "Name": "LD B,r", + "HexInstructions": "FD,41", + "CpuHash": "2D75FAA4FD72B2BF40A51994B7494314A9AAC0C84ADB3CFB4C64BC3B3CAEB0CE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5B76FF573AACD347ADD89578B0F7D7C2F20C69952797B7BF1EBF0486BE069F43" + }, + { + "Name": "LD B,r", + "HexInstructions": "FD,42", + "CpuHash": "7703F30F8785AEEE3C42D8591D94AA96169CF73523E657AEFB9852CB81FB1FAD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9EFEA8C1F0AAEEF12B62969A11A9DBC90B7FFCE3F0D1306060CB03B5ED111202" + }, + { + "Name": "LD B,r", + "HexInstructions": "FD,43", + "CpuHash": "909AAB0D259DAF28660DEC7969C3FB644CC0B869F7FDF6BA0B047D6F9E6DBA9F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A46A28587AB7EF661EF9BBDA735CE692037ADCC6BE7BB31ED68F72DACB29A93" + }, + { + "Name": "LD C,r", + "HexInstructions": "FD,48", + "CpuHash": "A6BCF0A77ED806CD7972DAFAEE535B8984F4F18E5B8731BEAB50898C57FE160F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "52727A79A34E60BC26E27506F394082906A5279DEEE2B15FB44DB46C68E9254D" + }, + { + "Name": "LD C,r", + "HexInstructions": "FD,49", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FF45785D3C545A74FDE4D9760472BC1D807C3EC0CA63C06CEBAB18846170C56E" + }, + { + "Name": "LD C,r", + "HexInstructions": "FD,4A", + "CpuHash": "CA93AD06604B4FC09DD596726B4F8CAC40C1485A38422BB3AD49DC3FDE39777B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A4439631FE922317C90264BDC728EDDAD5A6E39B0E57CEC36A64B7AC00E19AFC" + }, + { + "Name": "LD C,r", + "HexInstructions": "FD,4B", + "CpuHash": "77A200CCAC438F8EED7519BFFEACA49B43E4530219A336FB546F957C5F443B15", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0A38EFE321CA1F808CBB36804A1CA311BA912155607F77DBF3A6E5F3D2A257FE" + }, + { + "Name": "LD D,r", + "HexInstructions": "FD,50", + "CpuHash": "C4BC994D587BE09E0EAB35205E4664E840EC2771B9230F68F4BFB2EC9EF6AD65", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C85A55BDBA8BEC99CF8BFB2F175CA7B9BC58A142A08E6569AF8C823BDCA239D1" + }, + { + "Name": "LD D,r", + "HexInstructions": "FD,51", + "CpuHash": "699185250E2270B8555589A1B037575C740FCF5A3F1B201CAB394A8E13DE916C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "32580AF86BCBF56C1096B1CB5D8D3BA48949875064C46652217EC204BFBAC846" + }, + { + "Name": "LD D,r", + "HexInstructions": "FD,52", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FCAFF70667525DF034ACF10C3C58CCC50439599151194533355070C3E429D93B" + }, + { + "Name": "LD D,r", + "HexInstructions": "FD,53", + "CpuHash": "29853618EDB5E139E6496859B552E33D38D2502E81A9725C844533F916424404", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E854F29558BF17FF8BBF5D0E28DFF50C311CE8A532CC1AD2637AB1E4EA3C8D00" + }, + { + "Name": "LD E,r", + "HexInstructions": "FD,58", + "CpuHash": "A10D89062C32231FEC0444702FEF885D1342EA7A7299A9EAF18069518612E822", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A703BC46D2D68E220FF7FCE8F3860DF1ABF96AB4324DBA6BF45810142D6D5865" + }, + { + "Name": "LD E,r", + "HexInstructions": "FD,59", + "CpuHash": "DF056B0A37C8841FFFDCFD09148514DD24D08416A603795EE4F211D540548E80", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "37BB2AD5588EA0421B30E470142B653B94B6EC6EBB729EF782D2C6077B720973" + }, + { + "Name": "LD E,r", + "HexInstructions": "FD,5A", + "CpuHash": "571F775CCF4557BE5ACA6DAA4191533B73433A07F1DFDA43141817A8FC4997F1", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3E7E2F7C3E13CE7AC8B83D8D80CD6AB0F06F3FCA9140DCD5DF577BC5FA86EF99" + }, + { + "Name": "LD E,r", + "HexInstructions": "FD,5B", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5170E88DBDD77ACEDF8BE4428CAB40187B91D59638CAE873CEAAE926345D229F" + }, + { + "Name": "LD A,A", + "HexInstructions": "FD,7F", + "CpuHash": "478246FE4B69CE54DA6779439F5A814092384007C6E1C0EA9830D83A05809BF4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1E36E36A6596C54A516FB8C0FC6589C1900D9C340F8C5EFDF0EE70EE998A6C5F" + }, + { + "Name": "LD A,B", + "HexInstructions": "FD,47", + "CpuHash": "C690C684A3DEB33446FB7DC9F4458548D18CA11F961A0C59537896B31571365F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6C060C502F873B415E64735614CC93D0201A21E4AEF1A37F5CB5F0885C9A2400" + }, + { + "Name": "LD A,C", + "HexInstructions": "FD,4F", + "CpuHash": "5945A6697F86D59F984CFD72A2C2539C4A90BDA0ECB2F6239FC9F71C87C2680C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C8E3E44DCD11604D5E70FB595D9DCDCF24ED59355BA389553000FC44568C2C2F" + }, + { + "Name": "LD A,D", + "HexInstructions": "FD,57", + "CpuHash": "057EA7D7B9EB8E09F87EF845796EFAE916AF468E0D8408921E4FE30D0F5F7EFE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1E95607191CF6DDC7BA2F62F0A1C0FC8E203193D38DB459877C918886E473F5F" + }, + { + "Name": "LD A,E", + "HexInstructions": "FD,5F", + "CpuHash": "6CC1858460107F780440B063C2BD99982551CEBF3AA38687495654278F8A6911", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "67DA778F4544CD1DFA1512B0F22709F5E2329AEFD8709C89DEBDBE53C8E02E78" + }, + { + "Name": "LD IYH,N", + "HexInstructions": "FD,26", + "CpuHash": "795B87B41AA71CB51346159D4B657A544AFA7A0AD667FF6BBBE0E6F041F222D0", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "334EA40DEC07ABCB6D9FA6054D1F3F32365ED6AFDC9F0A42E9CF24A07D3E7F8D" + }, + { + "Name": "LD IYL,N", + "HexInstructions": "FD,2E", + "CpuHash": "D52C46F67569007DE42C1D248431DBF86C7C531272A984E637FD74257F27BEFB", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1CB79DFE87010845A04AA9DF0506C6D4EDFEE86F8E9372D36A536C4F6F723F22" + }, + { + "Name": "LD A, IYH", + "HexInstructions": "FD,7C", + "CpuHash": "E5AE13DA6A811E5530E9B6B353D308B6D1FC731E19C9BEBC309046D78671FDAD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5630C7A3180EECBB76E13D4FEEC38439197AF94457FFC6C6F011BDD6644FA713" + }, + { + "Name": "LD B, IYH", + "HexInstructions": "FD,44", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C156F07FF24EEA8FF4E2F76B294FB0DCC0F4AD6FE6ABE8D98D10F9923AA9BD86" + }, + { + "Name": "LD C, IYH", + "HexInstructions": "FD,4C", + "CpuHash": "FC51A91F5E95A4DB0C4EE314BC5F94B93372A420BC36ABF28DA8326B35DFC8E3", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "32F1B624ED3459B54FDCF892D3ECA41D3337F94DC01BD7D24222D7C4698DF4F2" + }, + { + "Name": "LD D, IYH", + "HexInstructions": "FD,54", + "CpuHash": "1A3907B7FF6D093BE9DD41542F84DA20C02F5DFFB75B2784F8266540B189C720", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "32BEBC254355966E4319E20A5CD9FB424F88A89420923DA91C4140172EF3DD07" + }, + { + "Name": "LD E, IYH", + "HexInstructions": "FD,5C", + "CpuHash": "D585AC823EC6E8736BE7A2D3478BD4EBBC483790D39DD151AB154176BC2EFC54", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6970AF9E6DCE77BECAAD0CAA21A1438F65E8E0050A3AB494D45995605F1F5559" + }, + { + "Name": "LD A, IYL", + "HexInstructions": "FD,7D", + "CpuHash": "4B14384E807E0B7E3FEB292C6223DE5B4C5D9EEC1A7414ECE78AEAA49B8E643D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "8C474FDD013B4227357E592DDB067B5CD7D1818EAEB6421D0D32251080AC8335" + }, + { + "Name": "LD B, IYL", + "HexInstructions": "FD,45", + "CpuHash": "BA9A897DFEF0EC546C7ACE9AEC41728309AE578E1D362A4681A696B4066AE167", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "55702BEC8BCD6EC0D76BAB9409883A0F9C29A7E7C6BBC4FBE74387127C7D9963" + }, + { + "Name": "LD C, IYL", + "HexInstructions": "FD,4D", + "CpuHash": "166E4E8A45D5787AA9D5DB1257431EEBB8A6BC453E6DD7AD74DE046303D0D0DA", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C3C85D32DC438BECDDAA60789E1AC25DE68DC867E8D399F701221352491B4DB3" + }, + { + "Name": "LD D, IYL", + "HexInstructions": "FD,55", + "CpuHash": "6717F194FA9E80F29D8EE5DE9FC3E8C986FB35C4178B02D96F300FA4CB727D74", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "02C591B0BBDFA0AEAD054EE26B82722828B4618D7E5A64593293FD02BF013B70" + }, + { + "Name": "LD E, IYL", + "HexInstructions": "FD,5D", + "CpuHash": "72267F09B665E2C8F2F52BFB77DF9B491A62DEC219FFE9238B98E8FA7B5B754D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AC7D11C290D0DABF6F2E76D745E7968BC2A3AD241BDF57E74BD05D1A9E2D8CA4" + }, + { + "Name": "LD IYH, A", + "HexInstructions": "FD,67", + "CpuHash": "99184555EE7954D5955FAA32DB503C18B3118525F1EE4E0A2C27C7280A018F99", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F78BF7A686EC8C8D7B65A6CD523656F7372DF7368CC9311FEEE9C10D995DFBE6" + }, + { + "Name": "LD IYH, B", + "HexInstructions": "FD,60", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4AF8ED1DDE85BF2D7BA8286CE3EA426EAB65672A2A719457DAEA4E345ECC727E" + }, + { + "Name": "LD IYH, C", + "HexInstructions": "FD,61", + "CpuHash": "5BE62927B8735476DE7269132842E26E792D1590E3358916AE98BBAF753A5EAF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B33B28078B937E38F79DAD392E1B1BE818760F675EFCCE08ADD338BE37025216" + }, + { + "Name": "LD IYH, D", + "HexInstructions": "FD,62", + "CpuHash": "B3CF96AE5E5540AE59C18239757A1EEF1C625360A1BA95B5CE32B88DCF98A876", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2A352AD27B791ECEC0EAF618AB5EA7DAF5C6CBED81875FD80AF1D628F0D848E5" + }, + { + "Name": "LD IYH, E", + "HexInstructions": "FD,63", + "CpuHash": "4CAD32B1E7B1F00881CE5C8EDAFC03A1685BE2F3140B4D7B77673DAE2B2C6F18", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "239AC6EFF38EAD0FB7D84EF5A410BD786FB6956C692628B25979404ADEF7043F" + }, + { + "Name": "LD IYH, IYH", + "HexInstructions": "FD,64", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "06F1FCCB173CEA0F0FDCF4FF6D62285CD0AFB206AA4629D20B5AED7D086F5A72" + }, + { + "Name": "LD IYH, IYL", + "HexInstructions": "FD,65", + "CpuHash": "9AB1F736C7FA05D6F3DCF8D3C5D3CD9DC716E4D7BCBF5980FC13C7B9E2046CDF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "34FA5F0204F010FE6416C19B86B010AC3D14CA8F9A48A6BE01C0D40269343DA8" + }, + { + "Name": "LD IYL, A", + "HexInstructions": "FD,6F", + "CpuHash": "946528C1570346915DF519FB8543C388FB22F2A36FF8A3B2E46A9EE6A7167A6D", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "44C81D5AD71C50C73A51738B5B34ACFE758E0EBFD0D3ED5785FC6A109B9B18E9" + }, + { + "Name": "LD IYL, B", + "HexInstructions": "FD,68", + "CpuHash": "1ED784E0124AA12484928A44570E45FA90DEF62699FFF7B376CEA7DA3621F4CD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D271B512E6A30C4AE652420F8D351CDF619A027D392F68D1C0E69D504CBE0954" + }, + { + "Name": "LD IYL, C", + "HexInstructions": "FD,69", + "CpuHash": "073228394C76B802A5E70A6D50A2A4B92D858EB0B193A97025EC0F46023A430B", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A0716B50E0A3B426AE59E7B4AAB1CE5CB9C25306D0879A913F3247A53CA86F05" + }, + { + "Name": "LD IYL, D", + "HexInstructions": "FD,6A", + "CpuHash": "B91DBD134EAE78C1FE9D6C2E85F32E6D645673271F00FE77A8BE0508C56884A2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4FA2B36BDF8195185622180EBCD1EFFEE6CFD1C69B62237524CF06A8C024E8BF" + }, + { + "Name": "LD IYL, E", + "HexInstructions": "FD,6B", + "CpuHash": "AC1136264F4BEF094D6F4ED17CAD441CAC6A9020D6409D5E1013A29C56275533", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BC4D3348D28707F7A85AB51D954F5EEDD4B5396FC6740BD4B4FA08A18F294B61" + }, + { + "Name": "LD IYL, IYH", + "HexInstructions": "FD,6C", + "CpuHash": "1ED784E0124AA12484928A44570E45FA90DEF62699FFF7B376CEA7DA3621F4CD", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4EB7B98FF8B2FE7C9DBE67C5055691C63E1C840E9F3EDFB99D00E99BAF9B90F4" + }, + { + "Name": "LD IYL, IYL", + "HexInstructions": "FD,6D", + "CpuHash": "AED1754DEDECC87C75328477FAE3874B8F46C162F46E1EB3FD3B110D23FA35F4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "52E283AC1B5B772E274F9EDD480BC251C6D115E90FB4C2138A87BA1756BFE652" + }, + { + "Name": "LD A,(IY\u002Bd)", + "HexInstructions": "FD,7E", + "CpuHash": "BC4C3D9B5216B9383F1A7E7BBEAAECE3DFC4982035CA7847F52956D7C24DB73C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E125CFE26075AE4EA9A80C24FC3E9BCCE7ABE5805A7C00C64A0068D9D7AB0795" + }, + { + "Name": "LD B,(IY\u002Bd)", + "HexInstructions": "FD,46", + "CpuHash": "8C09D3051B3BC691F6497F8A46AEA0D636BC719F8DF7A5802A3250D4209462D5", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3F8280E244B0F42D0B0F559C7A6699017D1F4D28A7C26AC39CE82CF3AAA25FA8" + }, + { + "Name": "LD C,(IY\u002Bd)", + "HexInstructions": "FD,4E", + "CpuHash": "4B5ED864429F5927D480BD618669A8515C015A6D85E76B1D7766366A6377BB41", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "859E829917BD8A6C14215B091083A8DEBC6A78EBBE1EC63CBE06219A6147A324" + }, + { + "Name": "LD D,(IY\u002Bd)", + "HexInstructions": "FD,56", + "CpuHash": "DBB0BE2AFAED113960D79F7FA70ED17AE9B0A439D0D418C976A9556DB6E2E4EE", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "898144088E6E434C16AD216D1596BB86EB6CD116285773ECE825269FA5F82D5D" + }, + { + "Name": "LD E,(IY\u002Bd)", + "HexInstructions": "FD,5E", + "CpuHash": "14B62C7B056220972E5B8AB1C75C91098BD604987A6C1E8B8B769E292B8F4E6C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "883906DF7C9955E57559FA904F3389D31F0B8ED5A7C11C11F4B6FF0E42208A3F" + }, + { + "Name": "LD H,(IY\u002Bd)", + "HexInstructions": "FD,66", + "CpuHash": "73263552DFCD81878066DCF8A6260261DADC05B718A1F214D044CA27FD8A1AA4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5DF239A7FE36565D9AA5A23B64FF06756FDF7F082A74136D7F00066876F45357" + }, + { + "Name": "LD L,(IY\u002Bd)", + "HexInstructions": "FD,6E", + "CpuHash": "29CD277148714BBFE810D41FFC14C8C19F45D17C89E33A51678E42334F90FD9A", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A0DF7F97A2E2AA9D26D6342A99D5C7796E4548EA1FF578FF4AD9D999A798412" + }, + { + "Name": "LD(IY \u002B d), N", + "HexInstructions": "FD,36", + "CpuHash": "D7EDDBA5276F3F63EC9C49FEC1D269BB04451160EFF55E76305CFAD35FCA2D7F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E9380CA585C106D3FBC1183C5F24510AB4DA9DD67F34651E8F5F15AB54BEE848" + }, + { + "Name": "LD IY, (NN)", + "HexInstructions": "FD,2A", + "CpuHash": "89E96D0C7DDA1E0F24B061A38BC325A11AD5043111F38EBF9F3DAD0286410D59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E26DF7836DB8244DCC1105833C7B355D87D72DA95E18B6A25F837701BF2A9E9D" + }, + { + "Name": "LD IY, NN", + "HexInstructions": "FD,21", + "CpuHash": "77FEABD8461BFE07FF2F8C62DCC4ED61DC82DA7F20FB369CA56C58276FF71D5C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B2F17C1F521BFF5D4E701D2C8D069CBC008F78FE6E7BE23F5F11B8A575BC0A3D" + }, + { + "Name": "LD(NN), IY", + "HexInstructions": "FD,22", + "CpuHash": "03DB8D1286CFAFA5C0E6B41BA881070E56C97515CEF0B4AFAD2E9FDAB69D7A44", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9F401FC9D4B3F235600112B6A55BD939D446479B743FB0FC81CD0D6B7A0E355D" + }, + { + "Name": "PUSH IY", + "HexInstructions": "FD,E5", + "CpuHash": "8DDBDB748ED7B5CB16DD2C53C3D55721BC63BBBACF758B8311A374C9DF015EB2", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7DB9B15B9B4CFA4685064CB8D7AB9992F65DFFAE5F9EBE6A1EEF6507477DF035" + }, + { + "Name": "POP IY", + "HexInstructions": "FD,E1", + "CpuHash": "CC3163F36D69FDB65CEEE5EEBA12D3B34C4FCD55399A12C6698F9F3A8DD9BA78", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "39B33BF8C71EB5918327648A95DFEA308A4B60E304C06C2E08C1E6974DE130BE" + }, + { + "Name": "EX (SP),IY", + "HexInstructions": "FD,E3", + "CpuHash": "FC4D8D8FFCBAA23184224203FEFFBEA973D21D054795129471A667BB9859D37F", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B95766807B903837F041E599F928DDD34481809879594ECD32A1F92576EB4371" + }, + { + "Name": "RES 0,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,86", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "69CB083FF66643835405204BF7FCCC405AD016EDBBB60559B9F720B3B11B7D03" + }, + { + "Name": "RES 1,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,8E", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "A1DA4F82E2B0F18B2E346112A681BCE7B71F6B577340DEB1929C942CB695160A" + }, + { + "Name": "RES 2,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,96", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "21B25374CB12726869F730F58C5A3B8C74008E519BF7184CCE2D013D630DD474" + }, + { + "Name": "RES 3,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,9E", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2DA587088314A1A776F6631B19E4E77F0775E639FD708C0C50D33233257BF5FC" + }, + { + "Name": "RES 4,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,A6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "808100C8C892521E80E1309752A8B75DE16A030FEEE0754CDDDB2330FB4638A2" + }, + { + "Name": "RES 5,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,AE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "9E82B62AC6C7A66D79597B494C00385DE6A111B09A6D212450FB33D2558739D4" + }, + { + "Name": "RES 6,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,B6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B0A20A7F1965F821DD70BE0A99A4E8AB519A36D74FBC1AB9127E6242187E7936" + }, + { + "Name": "RES 7,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,BE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0DEB7B3251CFBE104C1A5140B351DB5B56B672A13AEA5700BBEB4CF527AEB695" + }, + { + "Name": "BIT 0,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,46", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E7063B12038E7F244829B2166C6C00C103EE1CA74E57481863A5908E05D5E59F" + }, + { + "Name": "BIT 1,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,4E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "54B0B7FAAB6D842FED8E305A98C81495D39C361256E47AAA01EBE29B9AB7663C" + }, + { + "Name": "BIT 2,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,56", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "589724C9A6283E157BB613A8406C35015693FD7CF1081AC52FF16BF87208DBB5" + }, + { + "Name": "BIT 3,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,5E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7220BFEA90BC83F4CC76389A478400B6B3C94C57CDBCEBFC98F7153C66A8F29E" + }, + { + "Name": "BIT 4,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,66", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2E4F58D81119ED68CA6E55FEBC3464A8A22D6144922780FBAC0F4D655479131F" + }, + { + "Name": "BIT 5,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,6E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3B37E147EDED5A23228ECD5737ED9BC3DE4B24D2F2F4DF2DBAFD2F6886D38EC0" + }, + { + "Name": "BIT 6,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,76", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "93C20C67FF4AE260C25E21CC363519CF424F542BCCA998E3519AB3AB6B6E0B58" + }, + { + "Name": "BIT 7,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,7E", + "CpuHash": "BFCEB1204DCE1F7FDD555D901E2780F095E5311AEC26946634FA824E9471D8CF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D5364E5A9C7FCDF83256D5CC34DAD37F74D52CEE522B38764816555F988BDA01" + }, + { + "Name": "SET 0,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,C6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E6EAEE49172F26C4476099DF37CABAB9351E1C1CA0AA5E879D0722C8B99099E3" + }, + { + "Name": "SET 1,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,CE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "997F937C3D09855EA6B4A9A845621798515A6E772211F8569CFB4AA92DF8A8CB" + }, + { + "Name": "SET 2,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,D6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C27F640CA0254F2CD0BCF58DBE23A7C4BBF6FB5722D641386E9B6F432AF0AAC0" + }, + { + "Name": "SET 3,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,DE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4E2816E3DF54738309E755A077700F2DECFF9805EBEE9998C68BA94D25C082C5" + }, + { + "Name": "SET 4,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,E6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "7D178FABAC2DBAD8728DC2ED96AE9D17FC796CFB0CD0B03435C48C26194AA107" + }, + { + "Name": "SET 5,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,EE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "D431141E0622B9BD652851E7F1705BEA75ECC3037392C62E414A276A2DF657C2" + }, + { + "Name": "SET 6,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,F6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "471824F5FD41B0BBD5EA9000EA86E81ED538EC8FAF0787CCE090F0B9D51FCF09" + }, + { + "Name": "SET 7,(IX\u002Bd)", + "HexInstructions": "DD,CB,12,FE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "80659A5ABA0851E59E376085D4B7672040D66565E9B9D0714A943E915F64825D" + }, + { + "Name": "RL (IX\u002Bd)", + "HexInstructions": "DD,CB,12,16", + "CpuHash": "69166C104B73EB8FFF239963D5413CB158F369AB9AFCE51DBDC9CC7260730040", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "93142E066EC7A9B1E18BF3A0F946374DFE65F337CEEAB4B7B1AF263F26E7B402" + }, + { + "Name": "RLC (IX\u002Bd)", + "HexInstructions": "DD,CB,12,06", + "CpuHash": "589F47394A85B0555ADA282ACD15333FBCCA73F77345977331E5E7EE6B92D06C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "422F19285B348F1951E5637DE5C75D87E25DFF6434F49A3FDD1380531050BDD5" + }, + { + "Name": "RR (IX\u002Bd)", + "HexInstructions": "DD,CB,12,1E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "6665469E0305B0F6C234F291C0CE7E58E67EA9793670B393104922093C92E543" + }, + { + "Name": "RRC (IX\u002Bd)", + "HexInstructions": "DD,CB,12,0E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "30475301EA5B248127EA6DA0204B30F357B5307162D56DB40B86316EB37C7B0B" + }, + { + "Name": "SLA (IX\u002Bd)", + "HexInstructions": "DD,CB,12,26", + "CpuHash": "69166C104B73EB8FFF239963D5413CB158F369AB9AFCE51DBDC9CC7260730040", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3FFEF3EAFC7FD93F41BB03D505560772499BEB05E3B4E1FF585D125E9C3C1BFA" + }, + { + "Name": "SRA (IX\u002Bd)", + "HexInstructions": "DD,CB,12,2E", + "CpuHash": "3EA2CEAE0586222A859CEC29E28AE36EDBD6000DC5ABC5F9DF753CBF43751892", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "E37D1DFF6BE0DB20C9230D42317C22954CA7ADB28B8A7F97113773F2C74AF059" + }, + { + "Name": "SLL (IX\u002Bd)", + "HexInstructions": "DD,CB,12,36", + "CpuHash": "589F47394A85B0555ADA282ACD15333FBCCA73F77345977331E5E7EE6B92D06C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AF26AB24E43E892D77B875060088EAA43D2A36A67297D9DB29F6DF59D4B9594C" + }, + { + "Name": "SRL (IX\u002Bd)", + "HexInstructions": "DD,CB,12,3E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FA5FC529711205EA0E3961FA6DDA7457B7B2B5B7A119ACEDC5C97C1266B103BE" + }, + { + "Name": "RES 0,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,86", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "1C875690484FA26C571E63366BA806C97A510B8698E410C57708C68CD8935DC3" + }, + { + "Name": "RES 1,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,8E", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "C17AA52A55FC559538E3288F0218EBC0C79A3C169A0B0C5697C415108EC652E6" + }, + { + "Name": "RES 2,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,96", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "00E047E0A1CEA0F2005DE5A8D97EE1B8DE9C4275D9850A1767AFECD8128C09DC" + }, + { + "Name": "RES 3,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,9E", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "296FF7CD1BBA7DE3648CD1CC4076743A0FC4440CC5AF7C859BAA1FC1627772D7" + }, + { + "Name": "RES 4,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,A6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "F22ED14945E01F0F3B23C84E9A64CA69CF4845E173A18F48DF844D6762974D9E" + }, + { + "Name": "RES 5,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,AE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "34ED26857A3806AD45B96B7BD7B1FED48CD225A3EA65F2DF8D137A6490B7B19B" + }, + { + "Name": "RES 6,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,B6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "4A3514A552F90040EB8EC73977A06210989CB119EB6F56D54DFDAD661C0EE181" + }, + { + "Name": "RES 7,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,BE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "738B32C26DD0B27410BDEFF929DB08A3E30F85BB04668D3D79FF096DB2F5C37A" + }, + { + "Name": "BIT 0,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,46", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "657B387963B47824839E48CF5260A1E660EB01EB220E41DEC49EE565FA052046" + }, + { + "Name": "BIT 1,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,4E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "73C4C7C72624F4F6FAF343111DE803FF20A1E0F33FDEDB1DD499C01AECD2DB8C" + }, + { + "Name": "BIT 2,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,56", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "5BF09D244ED0684C7706E4D0BB7E708F43950762E74E35991660BF5679CB3C20" + }, + { + "Name": "BIT 3,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,5E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "3B690AFA4690D0C176CF6C76DBC82EF987EC625FD69508BE568E8B979E88946A" + }, + { + "Name": "BIT 4,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,66", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "15462E0B968F0A8FF891697A6688152BF75E7C3A885958DDE77D9A7F4486BF9F" + }, + { + "Name": "BIT 5,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,6E", + "CpuHash": "250E5B74210E7F65778AC60C66984916AF2A1C9449562F2DC94C1EAB598763B4", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "06ADD6012835133792586ADA2A942BF11B2897A6DFBBADE8CC722AB815F163C7" + }, + { + "Name": "BIT 6,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,76", + "CpuHash": "2D70919E953D2880E20B637FAD024EAFB938DAD1091647842A1612ED53B5401E", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "B3503D00BB80791E43B44E5C0DF1AC8841A2679E7F3FC6F40F99A66A36892161" + }, + { + "Name": "BIT 7,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,7E", + "CpuHash": "BFCEB1204DCE1F7FDD555D901E2780F095E5311AEC26946634FA824E9471D8CF", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FAF0406CC8CB415DF9E2722EBF0B329D949961B0972E4A3F54EDEE265314EF61" + }, + { + "Name": "SET 0,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,C6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "685DA0BA904EF8D4434CFC6758EE4EDF3CC3D1FDE968C39FEC3D8CABD70D874F" + }, + { + "Name": "SET 1,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,CE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "01DDFEDA63558F0CB1DCDFE5584B8E3E48F1FF85553F84DF600EDE1B211B1349" + }, + { + "Name": "SET 2,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,D6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "01F637268F90C1E8E255624D86A57432F58F67AC8EF15AAD653762477A145059" + }, + { + "Name": "SET 3,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,DE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AB3958EE66989694B9E3541F99C1689B1A0E9AE9E3EC5AC4131BF6520A16AB34" + }, + { + "Name": "SET 4,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,E6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FA70BC6B5E8369BE56B1EAB05D7BCA736CB8AC566410E834172D4DEC7F1996ED" + }, + { + "Name": "SET 5,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,EE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "2855AB5A57E5F855C76665F420F3C1C393B625D2438246041EE25DC0BFFC494B" + }, + { + "Name": "SET 6,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,F6", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "675B2BAAEFED845FA2B3C5706E76E9983D95D23A4FDE61EAD070D46B61244A84" + }, + { + "Name": "SET 7,(IY\u002Bd)", + "HexInstructions": "FD,CB,13,FE", + "CpuHash": "E4C2D501DF7749B85B5160C85CDCBE9E275FA615208352EBFCA7F7FCF3657761", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "234AF78A57CB9BAD2DD769106430D3EE58FF8AEA86781B40DB7593C0821330E9" + }, + { + "Name": "RL (IY\u002Bd)", + "HexInstructions": "FD,CB,13,16", + "CpuHash": "69166C104B73EB8FFF239963D5413CB158F369AB9AFCE51DBDC9CC7260730040", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "03A6279C88BCA1D4C8A1AF978C33678CA93B68507F81B46D7C165B495D324D9D" + }, + { + "Name": "RLC (IY\u002Bd)", + "HexInstructions": "FD,CB,13,06", + "CpuHash": "589F47394A85B0555ADA282ACD15333FBCCA73F77345977331E5E7EE6B92D06C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "28979C164C9391848D195936E8585EC6BB8DFDF8B82821985CEC2CC632CC04BF" + }, + { + "Name": "RR (IY\u002Bd)", + "HexInstructions": "FD,CB,13,1E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "BE4BE6F9E5D09C6E51081F5CC157B3B0ADEC5FA7435F949E76DC40DD9106ED3D" + }, + { + "Name": "RRC (IY\u002Bd)", + "HexInstructions": "FD,CB,13,0E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "ADDB77DEF022B8D18C0B67DD0A00E3C0F336A4AD190B1A7B50A653880CA6A29B" + }, + { + "Name": "SLA (IY\u002Bd)", + "HexInstructions": "FD,CB,13,26", + "CpuHash": "69166C104B73EB8FFF239963D5413CB158F369AB9AFCE51DBDC9CC7260730040", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "0C86270742027CB6199B106321990BAFDE9A00AC229A49AB5546C23BC876B6B3" + }, + { + "Name": "SRA (IY\u002Bd)", + "HexInstructions": "FD,CB,13,2E", + "CpuHash": "3EA2CEAE0586222A859CEC29E28AE36EDBD6000DC5ABC5F9DF753CBF43751892", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "232B3762C87890AB7C1075AB77083E193A9AB3C0AFF6D06369F55E9C89251787" + }, + { + "Name": "SLL (IY\u002Bd)", + "HexInstructions": "FD,CB,13,36", + "CpuHash": "589F47394A85B0555ADA282ACD15333FBCCA73F77345977331E5E7EE6B92D06C", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "AFAFB4C548426B5CA65306BC1593A975CEE6AC53B5CB19948370649329BED33D" + }, + { + "Name": "SRL (IY\u002Bd)", + "HexInstructions": "FD,CB,13,3E", + "CpuHash": "2A3E0754355C0084ABD9C9E55BAF04C46D7C20AEA9E8C7DFD1AA7C3BF9E92B59", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "144A52571A429B443EEF71D322F6E291A41D6B57AD0C7A5005AF6EF0F71C49A4" + } +] \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs new file mode 100644 index 0000000..9934b34 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs @@ -0,0 +1,215 @@ +using System.Collections; +using System.Globalization; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using FluentAssertions; +using Kmse.Core.Z80; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Support; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; + +/// +/// Test all the instructions by executing each possible instruction and validating that a hash of the current CPU, IO and Memory state matches expected +/// This follows a very similar concept to zexall where we take a known good execution and generate test data from that including hashes of state +/// We save that to a JSON file and run this as a unit test to validate no regressions in any instruction implementations +/// This does not cover tests for register or other support classes but primarily ensure that the mapping of instruction op codes to operations is maintained and not broken +/// These tests are really designed to stop new regressions more than testing the current implementation and this relies on testing with zexdoc and other unit tests to validate the implementation is correct +/// So it is important to remember that if any instruction is broken when the hashes are generated, it will then be testing against a incorrect implementation +/// so care must be taken that when tests fail, it may be because an issue is actually fixed and the hashes need to be regenerated +/// +[TestFixture] +public class TestInstructionsFixture +{ + private Z80Cpu _cpu; + private ICpuLogger _cpuLogger; + private TestMemory _memory; + private TestIo _io; + + [SetUp] + public void Setup() + { + _cpuLogger = Substitute.For(); + _memory = new TestMemory(); + _io = new TestIo(); + _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); + _cpu.Initialize(_memory, _io); + } + + protected static T DeserializeTestFile(string filename) + where T : class + { + var assembly = Assembly.GetExecutingAssembly(); + using var stream = assembly.GetManifestResourceStream(filename); + if (stream != null) + { + return JsonSerializer.Deserialize(stream); + } + + throw new ArgumentException($"Test file {filename} not found"); + } + + private class TestInstructionData + { + public string Name { get; set; } + public string HexInstructions { get; set; } + public string CpuHash { get; set; } + public string IoHash { get; set; } + public string MemoryHash { get; set; } + } + + private class InstructionTestCases : IEnumerable + { + public IEnumerator GetEnumerator() + { + var testInstructions = DeserializeTestFile>("Kmse.Core.UnitTests.Z80CpuTests.InstructionTests.Data.TestInstructions.json"); + foreach (var instruction in testInstructions) + { + var instructions = instruction.HexInstructions.Split(',').Select(x => byte.Parse(x, NumberStyles.AllowHexSpecifier)).ToArray(); + yield return new object[] { instruction.Name, instructions, instruction.CpuHash, instruction.IoHash, instruction.MemoryHash }; + } + } + } + + [Test] + [TestCaseSource(typeof(InstructionTestCases))] + public void ValidateInstructionsAgainstPrecalculatedHashes(string name, byte[] instructions, string expectedCpuHash, string expectedIoHash, string expectedMemoryHash) + { + _cpu.Reset(); + _memory.Reset(); + _io.Reset(); + + // Load some data into the registers - a,b,c,d,e,h,l + _memory.AddInstruction(new[] { (byte)0x3E, (byte)0x01 }); + _memory.AddInstruction(new[] { (byte)0x06, (byte)0x02 }); + _memory.AddInstruction(new[] { (byte)0x0E, (byte)0x03 }); + _memory.AddInstruction(new[] { (byte)0x16, (byte)0x04 }); + _memory.AddInstruction(new[] { (byte)0x1E, (byte)0x05 }); + _memory.AddInstruction(new[] { (byte)0x26, (byte)0x06 }); + _memory.AddInstruction(new[] { (byte)0x2E, (byte)0x07 }); + // 16 bit registers sp, ix, iy + // Note we don't set the stack pointer since already defaults to a sensible value + _memory.AddInstruction(new[] { (byte)0xDD, (byte)0x21, (byte)0x08, (byte)0x01 }); + _memory.AddInstruction(new[] { (byte)0xFD, (byte)0x21, (byte)0x09, (byte)0x02 }); + + _memory.AddInstruction(instructions); + for (var i = 0; i < _memory.InstructionCount; i++) + { + _cpu.ExecuteNextCycle(); + } + + var status = _cpu.GetStatus(); + var cpuHash = HashCpuStatus(status); + var ioHash = _io.GetHash(); + var memoryHash = _memory.GetHash(); + + cpuHash.Should().Be(expectedCpuHash); + ioHash.Should().Be(expectedIoHash); + memoryHash.Should().Be(expectedMemoryHash); + } + + // Uncomment to regenerate hashes from known good instructions + // Note this requires the instruction lists and associated classes to be made public + //[Test] + //public void GenerateTestData() + //{ + // // Generate a list of instruction bytes + // var instructions = new List(); + // instructions.AddRange(_cpu._genericInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._cbInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"CB,{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._ddInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"DD,{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._edInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"ED,{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._fdInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"FD,{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._specialDdcbInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"DD,CB,12,{instruction.Key:X2}" })); + // instructions.AddRange(_cpu._specialFdcbInstructions.Select(instruction => new TestInstructionData { Name = instruction.Value.Name, HexInstructions = $"FD,CB,13,{instruction.Key:X2}" })); + + // foreach (var instruction in instructions) + // { + // _cpu.Reset(); + // _memory.Reset(); + // _io.Reset(); + + // //AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { LoadValueInto8BitRegister(ref _af.High, GetNextDataByte()); }); + // //AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { LoadValueInto8BitRegister(ref _bc.High, GetNextDataByte()); }); + // //AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { LoadValueInto8BitRegister(ref _bc.Low, GetNextDataByte()); }); + // //AddStandardInstruction(0x16, 7, "LD D,N", "Load n into D", _ => { LoadValueInto8BitRegister(ref _de.High, GetNextDataByte()); }); + // //AddStandardInstruction(0x1E, 7, "LD E,N", "Load n into E", _ => { LoadValueInto8BitRegister(ref _de.Low, GetNextDataByte()); }); + // //AddStandardInstruction(0x26, 7, "LD H,N", "Load n into H", _ => { LoadValueInto8BitRegister(ref _hl.High, GetNextDataByte()); }); + // //AddStandardInstruction(0x2E, 7, "LD L,N", "Load n into L", _ => { LoadValueInto8BitRegister(ref _hl.Low, GetNextDataByte()); }); + // //AddStandardInstruction(0x01, 10, "LD BC,NN", "Load nn value into BC", _ => { LoadValueInto16BitRegister(ref _bc, GetNextTwoDataBytes()); }); + // //AddStandardInstruction(0x11, 10, "LD DE,NN", "Load nn value into DE", _ => { LoadValueInto16BitRegister(ref _de, GetNextTwoDataBytes()); }); + // //AddStandardInstruction(0x21, 10, "LD HL,NN", "Load nn value into HL", _ => { LoadValueInto16BitRegister(ref _hl, GetNextTwoDataBytes()); }); + // //AddStandardInstruction(0x31, 10, "LD SP,NN", "Load nn value into SP", _ => { LoadValueInto16BitRegister(ref _stackPointer, GetNextTwoDataBytes()); }); + // //AddDoubleByteInstruction(0xDD, 0x21, 14, "LD IX, NN", "Load nn value into IX", _ => { LoadValueInto16BitRegister(ref _ix, GetNextTwoDataBytes()); }); + // //AddDoubleByteInstruction(0xFD, 0x21, 14, "LD IY, NN", "Load nn value into IY", _ => { LoadValueInto16BitRegister(ref _iy, GetNextTwoDataBytes()); }); + + // // Load some data into the registers - a,b,c,d,e,h,l + // _memory.AddInstruction(new[] { (byte)0x3E, (byte)0x01 }); + // _memory.AddInstruction(new[] { (byte)0x06, (byte)0x02 }); + // _memory.AddInstruction(new[] { (byte)0x0E, (byte)0x03 }); + // _memory.AddInstruction(new[] { (byte)0x16, (byte)0x04 }); + // _memory.AddInstruction(new[] { (byte)0x1E, (byte)0x05 }); + // _memory.AddInstruction(new[] { (byte)0x26, (byte)0x06 }); + // _memory.AddInstruction(new[] { (byte)0x2E, (byte)0x07 }); + // // 16 bit registers sp, ix, iy + // // Note we don't set the stack pointer since already defaults to a sensible value + // _memory.AddInstruction(new[] { (byte)0xDD, (byte)0x21, (byte)0x08 }); + // _memory.AddInstruction(new[] { (byte)0xFD, (byte)0x21, (byte)0x09 }); + + // _memory.AddInstruction(instruction.HexInstructions.Split(',').Select(x => byte.Parse(x, NumberStyles.AllowHexSpecifier)).ToArray()); + // for (var i = 0; i < _memory.InstructionCount; i++) + // { + // _cpu.ExecuteNextCycle(); + // } + + // var status = _cpu.GetStatus(); + // var cpuHash = HashCpuStatus(status); + // var ioHash = _io.GetHash(); + // var memoryHash = _memory.GetHash(); + + // instruction.CpuHash = cpuHash; + // instruction.IoHash = ioHash; + // instruction.MemoryHash = memoryHash; + // } + + // var json = JsonSerializer.Serialize(instructions); + //} + + private string HashCpuStatus(CpuStatus status) + { + using var sha256Hash = SHA256.Create(); + + var sb = new StringBuilder(); + sb.Append(status.CurrentCycleCount.ToString()); + sb.Append(status.Halted.ToString()); + sb.Append(status.Af.Word.ToString()); + sb.Append(status.Bc.Word.ToString()); + sb.Append(status.De.Word.ToString()); + sb.Append(status.Hl.Word.ToString()); + sb.Append(status.AfShadow.Word.ToString()); + sb.Append(status.BcShadow.Word.ToString()); + sb.Append(status.DeShadow.Word.ToString()); + sb.Append(status.HlShadow.Word.ToString()); + sb.Append(status.Ix.Word.ToString()); + sb.Append(status.Iy.Word.ToString()); + sb.Append(status.Pc.ToString()); + sb.Append(status.StackPointer.ToString()); + sb.Append(status.IRegister.ToString()); + sb.Append(status.RRegister.ToString()); + sb.Append(status.InterruptFlipFlop1.ToString()); + + sb.Append(status.InterruptFlipFlop2.ToString()); + sb.Append(status.InterruptMode.ToString()); + + sb.Append(status.NonMaskableInterruptStatus.ToString()); + sb.Append(status.MaskableInterruptStatus.ToString()); + + var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString())); + + return BitConverter.ToString(bytes).Replace("-", ""); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs new file mode 100644 index 0000000..4e726fc --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs @@ -0,0 +1,65 @@ +using System.Security.Cryptography; +using Kmse.Core.IO; +using Kmse.Core.IO.Controllers; +using Kmse.Core.IO.DebugConsole; +using Kmse.Core.IO.Sound; +using Kmse.Core.IO.Vdp; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; + +public class TestIo : IMasterSystemIoManager +{ + private readonly Memory _portData = new(new byte[0xFF + 1]); + + public void Initialize(IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort soundPort, + IDebugConsolePort debugConsolePort) + { + _portData.Span.Fill(0xBB); + } + + public void Reset() + { + _portData.Span.Fill(0xBB); + ClearMaskableInterrupt(); + ClearNonMaskableInterrupt(); + } + + public bool NonMaskableInterrupt { get; private set; } + public bool MaskableInterrupt { get; private set; } + public void SetMaskableInterrupt() + { + MaskableInterrupt = true; + } + + public void ClearMaskableInterrupt() + { + MaskableInterrupt = false; + } + + public void SetNonMaskableInterrupt() + { + NonMaskableInterrupt = true; + } + + public void ClearNonMaskableInterrupt() + { + NonMaskableInterrupt = false; + } + + public byte ReadPort(ushort port) + { + return _portData.Span[port & 0xFF]; + } + + public void WritePort(ushort port, byte value) + { + _portData.Span[port & 0xFF] = value; + } + + public string GetHash() + { + using var sha256Hash = SHA256.Create(); + var bytes = sha256Hash.ComputeHash(_portData.Span.ToArray()); + return BitConverter.ToString(bytes).Replace("-", ""); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs new file mode 100644 index 0000000..0b4e4ba --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs @@ -0,0 +1,60 @@ +using System.Security.Cryptography; +using Kmse.Core.Cartridge; +using Kmse.Core.Memory; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; + +public class TestMemory : IMasterSystemMemory +{ + private readonly Memory _internalRam = new(new byte[0xFFFF + 1]); + private int _instructionPointer; + + public TestMemory() + { + _internalRam.Span.Fill(0xAA); + } + + public int InstructionCount { get; private set; } + + public void LoadCartridge(IMasterSystemCartridge masterSystemCartridge) { } + + public byte this[ushort address] + { + get => _internalRam.Span[address]; + set => _internalRam.Span[address] = value; + } + + public int GetMaximumAvailableMemorySize() + { + return 0xFFFF + 1; + } + + public int GetMinimumAvailableMemorySize() + { + return 0; + } + + public void Reset() + { + _internalRam.Span.Fill(0xAA); + _instructionPointer = 0; + InstructionCount = 0; + } + + public void AddInstruction(byte[] instructionBytes) + { + foreach (var value in instructionBytes) + { + _internalRam.Span[_instructionPointer++] = value; + } + + InstructionCount++; + } + + public string GetHash() + { + using var sha256Hash = SHA256.Create(); + var bytes = sha256Hash.ComputeHash(_internalRam.Span.ToArray()); + return BitConverter.ToString(bytes).Replace("-", ""); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs index 21401e0..bdb6702 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -75,23 +75,25 @@ public void WhenStackPointerDecrementedThenValueIsUpdated() } [Test] - public void WhenStackPointerIncrementedWhichOverflowsThenThrowsException() + public void WhenStackPointerIncrementedWhichOverflowsThenWrapsAround() { _memory.GetMaximumAvailableMemorySize().Returns(1000); _stackManager.Reset(); _stackManager.Set(1000); - var action = () => _stackManager.IncrementStackPointer(); - action.Should().Throw(); + _stackManager.IncrementStackPointer(); + _stackManager.Value.Should().Be(0); + _cpuLogger.Received(1).Error(Arg.Is(x => x.Contains("Stack Pointer has been incremented", StringComparison.InvariantCultureIgnoreCase))); } [Test] - public void WhenStackPointerDecrementedWhichOverflowsThenThrowsException() + public void WhenStackPointerDecrementedWhichOverflowsThenLogsError() { _memory.GetMinimumAvailableMemorySize().Returns(100); _stackManager.Reset(); _stackManager.Set(100); - var action = () => _stackManager.DecrementStackPointer(); - action.Should().Throw(); + _stackManager.DecrementStackPointer(); + _stackManager.Value.Should().Be(99); + _cpuLogger.Received(1).Error(Arg.Is(x => x.Contains("Stack Pointer has been decremented", StringComparison.InvariantCultureIgnoreCase))); } [Test] diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs index 748d140..a040a68 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -13,7 +13,7 @@ public void IncrementHigh() { var oldValue = Register.High; var newValue = (byte)(Register.High + 1); - Set(newValue); + SetHigh(newValue); CheckIncrementFlags(newValue, oldValue); } @@ -21,23 +21,23 @@ public void DecrementHigh() { var oldValue = Register.High; var newValue = (byte)(Register.High - 1); - Set(newValue); + SetHigh(newValue); CheckDecrementFlags(newValue, oldValue); } public void IncrementLow() { - var oldValue = Register.High; - var newValue = (byte)(Register.High + 1); - Set(newValue); + var oldValue = Register.Low; + var newValue = (byte)(Register.Low + 1); + SetLow(newValue); CheckIncrementFlags(newValue, oldValue); } public void DecrementLow() { - var oldValue = Register.High; - var newValue = (byte)(Register.High - 1); - Set(newValue); + var oldValue = Register.Low; + var newValue = (byte)(Register.Low - 1); + SetLow(newValue); CheckDecrementFlags(newValue, oldValue); } diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index a9b91d9..ac11494 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -127,10 +127,8 @@ public bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool s { JumpByOffset(offset); return true; - //_currentCycleCount += 12; } - //_currentCycleCount += 7; return false; } @@ -172,10 +170,8 @@ public bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) { SetFromStack(); return true; - //_currentCycleCount += 11; } - //_currentCycleCount += 5; return false; } diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs index 86f4683..a261040 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs @@ -31,8 +31,9 @@ public void IncrementStackPointer() { if (Register.Word >= _maximumMemorySize) { - throw new InvalidOperationException( - $"Cannot increment Stack Pointer higher than available RAM - {_maximumMemorySize} bytes"); + _cpuLogger.Error($"Warning: Stack Pointer has been incremented beyond maximum RAM size and will rollover to 0, current stack pointer '{Register.Word:X4}'"); + Register.Word = 0x00; + return; } Register.Word++; @@ -44,8 +45,8 @@ public void DecrementStackPointer() // Although it is unlikely any ROM is going to use so much stack that it basically uses all the RAM if (Register.Word <= _memory.GetMinimumAvailableMemorySize()) { - throw new InvalidOperationException( - $"Cannot decrement Stack Pointer lower than available RAM - {_memory.GetMinimumAvailableMemorySize()} bytes"); + // We use this as a warning since zexdoc will decrement this down pass ROM space as part of testing so this may be a legitimate, if unusual, manipulation of the stack + _cpuLogger.Error($"Warning: Stack Pointer has been decremented into ROM memory slots, current stack pointer '{Register.Word:X4}'"); } Register.Word--; diff --git a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs index 76bd445..773ccc2 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs @@ -68,8 +68,8 @@ public void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0) public void SaveToMemory(ushort address, byte offset = 0) { var location = (ushort)(address + offset); - Memory[location] = HighRegister.Value; - Memory[(ushort)(location + 1)] = LowRegister.Value; + Memory[location] = LowRegister.Value; + Memory[(ushort)(location + 1)] = HighRegister.Value; } public void SwapWithShadow() diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index 6bae530..90ade16 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -467,9 +467,9 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xFD, 0x24, 4, "INC IYH", "Increment IYX", _ => { _iy.IncrementHigh(); }); AddDoubleByteInstruction(0xFD, 0x2C, 4, "INC IYL", "Increment IYL", _ => { _iy.IncrementLow(); }); - AddDoubleByteInstruction(0xDD, 0x25, 4, "DEC IXH", "Decrement IHX", _ => { _ix.DecrementHigh(); }); - AddDoubleByteInstruction(0xDD, 0x2D, 4, "DEC IXL", "Decrement IHL", _ => { _ix.DecrementLow(); }); - AddDoubleByteInstruction(0xFD, 0x25, 4, "DEC IYH", "Decrement IYX", _ => { _iy.DecrementHigh(); }); + AddDoubleByteInstruction(0xDD, 0x25, 4, "DEC IXH", "Decrement IXH", _ => { _ix.DecrementHigh(); }); + AddDoubleByteInstruction(0xDD, 0x2D, 4, "DEC IXL", "Decrement IXL", _ => { _ix.DecrementLow(); }); + AddDoubleByteInstruction(0xFD, 0x25, 4, "DEC IYH", "Decrement IYH", _ => { _iy.DecrementHigh(); }); AddDoubleByteInstruction(0xFD, 0x2D, 4, "DEC IYL", "Decrement IYL", _ => { _iy.DecrementLow(); }); } @@ -811,11 +811,11 @@ private void PopulateLoadAndExchangeInstructions() AddStandardInstruction(0x32, 13, "LD (NN),A", "Load A into memory location NN", _ => { _accumulator.SaveToMemory(_pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x22, 16, "LD (NN),HL", "Load HL into memory location NN", _ => { _hl.SaveToMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x43, 20, "LD(NN), BC", "Load BC into memory location NN", _ => { _bc.SaveToMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x53, 20, "LD(NN), DE", "Load DE into memory location NN", _ => { _de.SaveToMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xDD, 0x22, 20, "LD(NN), IX", "Load IX into memory location NN", _ => { _ix.SaveToMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xFD, 0x22, 20, "LD(NN), IY", "Load IY into memory location NN", _ => { _iy.SaveToMemory(_pc.GetNextTwoDataBytes()); }); - AddDoubleByteInstruction(0xED, 0x73, 20, "LD(NN), SP", "Load SP into memory location NN", _ => { _stack.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x43, 20, "LD (NN), BC", "Load BC into memory location NN", _ => { _bc.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x53, 20, "LD (NN), DE", "Load DE into memory location NN", _ => { _de.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xDD, 0x22, 20, "LD (NN), IX", "Load IX into memory location NN", _ => { _ix.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xFD, 0x22, 20, "LD (NN), IY", "Load IY into memory location NN", _ => { _iy.SaveToMemory(_pc.GetNextTwoDataBytes()); }); + AddDoubleByteInstruction(0xED, 0x73, 20, "LD (NN), SP", "Load SP into memory location NN", _ => { _stack.SaveToMemory(_pc.GetNextTwoDataBytes()); }); AddDoubleByteInstruction(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => { From f572676be3203c05fbbc70aca0a96df814b05d7a Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:12:16 +1000 Subject: [PATCH 09/21] Split out interrupt management from CPU and I/O into own class. This allows an other class to trigger an interrupt whereas previously it was tied directly to I/O which was not the best place for it. This does create some dependency issues since the interrupt currently has to be created inside the CPU class but is needed outside of it. A method is added to the interface to get access to this but how the seperate registers are created needs to be tidied up to avoid this somewhat clunkly handling. Changed the VDP handling so the Execute does not return a bool when executed since ideally refresh would be handled as part of interrupt handling --- Kmse.Console/Logging/SerilogCpuLogger.cs | 20 ++ Kmse.Console/Logging/SerilogIoLogger.cs | 20 -- .../Z80CpuTests/CpuExecutionFixture.cs | 42 ++-- .../Z80CpuTests/CpuInstructionsFixture.cs | 3 +- Kmse.Core/IO/IMasterSystemIoManager.cs | 8 - Kmse.Core/IO/Logging/IIoPortLogger.cs | 3 - Kmse.Core/IO/MasterSystemIoManager.cs | 31 +-- Kmse.Core/IO/Vdp/IVdpPort.cs | 2 +- Kmse.Core/IO/Vdp/VdpPort.cs | 4 +- Kmse.Core/MasterSystemMk2.cs | 12 +- Kmse.Core/Z80/IZ80Cpu.cs | 2 + .../Z80/Interrupts/IZ80InterruptManagement.cs | 25 +++ .../Z80/Interrupts/Z80InterruptManagement.cs | 180 ++++++++++++++++++ Kmse.Core/Z80/Logging/ICpuLogger.cs | 2 + Kmse.Core/Z80/Z80Cpu.Instructions.cs | 18 +- Kmse.Core/Z80/Z80Cpu.cs | 92 ++------- 16 files changed, 289 insertions(+), 175 deletions(-) create mode 100644 Kmse.Core/Z80/Interrupts/IZ80InterruptManagement.cs create mode 100644 Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs diff --git a/Kmse.Console/Logging/SerilogCpuLogger.cs b/Kmse.Console/Logging/SerilogCpuLogger.cs index 6b6392b..76a87c9 100644 --- a/Kmse.Console/Logging/SerilogCpuLogger.cs +++ b/Kmse.Console/Logging/SerilogCpuLogger.cs @@ -42,6 +42,26 @@ public void LogInstruction(ushort baseAddress, string opCode, string operationNa : $"0x{baseAddress:X4}: {opCode} ({operationName} - {operationDescription})"); } + public void SetMaskableInterruptStatus(bool status) + { + if (!_verboseLoggingEnabled) + { + return; + } + + _log.Debug("Set MI to {Status}", status); + } + + public void SetNonMaskableInterruptStatus(bool status) + { + if (!_verboseLoggingEnabled) + { + return; + } + + _log.Debug("Set NMI to {Status}", status); + } + public void EnableVerboseLogging() { _verboseLoggingEnabled = true; diff --git a/Kmse.Console/Logging/SerilogIoLogger.cs b/Kmse.Console/Logging/SerilogIoLogger.cs index c970049..2446e92 100644 --- a/Kmse.Console/Logging/SerilogIoLogger.cs +++ b/Kmse.Console/Logging/SerilogIoLogger.cs @@ -53,26 +53,6 @@ public void PortWrite(ushort address, byte newData) _logger.Debug("Wrote to I/O port at address {Address:X4} to {NewData:X2}", address, newData); } - public void SetMaskableInterruptStatus(bool status) - { - if (!_verboseLoggingEnabled) - { - return; - } - - _logger.Debug("Set MI to {Status}", status); - } - - public void SetNonMaskableInterruptStatus(bool status) - { - if (!_verboseLoggingEnabled) - { - return; - } - - _logger.Debug("Set NMI to {Status}", status); - } - public void EnableVerboseLogging() { _verboseLoggingEnabled = true; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index 29d2e4b..ad9555a 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -2,6 +2,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.Logging; using NSubstitute; using NUnit.Framework; @@ -19,6 +20,7 @@ public void Setup() _io = Substitute.For(); _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); _cpu.Initialize(_memory, _io); + _interruptManagement = _cpu.GetInterruptManagementInterface(); } private void PrepareForTest() @@ -26,21 +28,26 @@ private void PrepareForTest() _cpu.Reset(); _memory.ClearReceivedCalls(); _io.ClearReceivedCalls(); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(false); + _interruptManagement.ClearMaskableInterrupt(); + _interruptManagement.ClearNonMaskableInterrupt(); } private Z80Cpu _cpu; private ICpuLogger _cpuLogger; private IMasterSystemMemory _memory; private IMasterSystemIoManager _io; + private IZ80InterruptManagement _interruptManagement; [Test] public void WhenResettingCpu() { + _interruptManagement.SetMaskableInterrupt(); + _interruptManagement.SetNonMaskableInterrupt(); + _cpu.Reset(); - _io.Received(1).ClearMaskableInterrupt(); - _io.Received(1).ClearNonMaskableInterrupt(); + _interruptManagement.MaskableInterrupt.Should().BeFalse(); + _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + var status = _cpu.GetStatus(); status.CurrentCycleCount.Should().Be(0); @@ -115,8 +122,8 @@ public void WhenExecutingAndNmiSet() // This allows us to better test handling since Pc is non zero and interrupts are on _cpu.ExecuteNextCycle().Should().Be(4); - _io.NonMaskableInterrupt.Returns(true); - _io.MaskableInterrupt.Returns(false); + _interruptManagement.ClearMaskableInterrupt(); + _interruptManagement.SetNonMaskableInterrupt(); var cycleCount = _cpu.ExecuteNextCycle(); cycleCount.Should().Be(11); @@ -158,15 +165,16 @@ public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) _cpu.ExecuteNextCycle().Should().Be(4); _cpu.ExecuteNextCycle().Should().Be(8); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(true); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeTrue(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); + _interruptManagement.SetMaskableInterrupt(); ; var cycleCount = _cpu.ExecuteNextCycle(); var status = _cpu.GetStatus(); status.Halted.Should().Be(false); - status.InterruptFlipFlop1.Should().BeFalse(); - status.InterruptFlipFlop2.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); // Jumps to 0x38 when in mode 0 or 1 if (mode is 0 or 1) @@ -174,7 +182,7 @@ public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) cycleCount.Should().Be(11); // Interrupt is not cleared in these modes until a RETI or DI is called - _io.DidNotReceive().ClearMaskableInterrupt(); + _interruptManagement.MaskableInterrupt.Should().BeTrue(); status.Pc.Should().Be(0x38); @@ -190,7 +198,7 @@ public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) cycleCount.Should().Be(4); // Interrupt is cleared in this modes since this mode is not supported - _io.Received(1).ClearMaskableInterrupt(); + _interruptManagement.MaskableInterrupt.Should().BeFalse(); } } @@ -205,14 +213,12 @@ public void WhenEnablingAndDisablingMaskableInterrupts() // Execute EI first _cpu.ExecuteNextCycle().Should().Be(4); - var status = _cpu.GetStatus(); - status.InterruptFlipFlop1.Should().BeTrue(); - status.InterruptFlipFlop2.Should().BeTrue(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeTrue(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); // Execute disable interrupt _cpu.ExecuteNextCycle().Should().Be(4); - status = _cpu.GetStatus(); - status.InterruptFlipFlop1.Should().BeFalse(); - status.InterruptFlipFlop2.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index f5b0c57..26c166c 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs @@ -2,6 +2,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Support; using NSubstitute; @@ -29,8 +30,6 @@ private void PrepareForTest() _memory.ClearReceivedCalls(); _memory.ClearSubstitute(); _io.ClearReceivedCalls(); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(false); } private Z80Cpu _cpu; diff --git a/Kmse.Core/IO/IMasterSystemIoManager.cs b/Kmse.Core/IO/IMasterSystemIoManager.cs index 3fbfa2d..7a912ef 100644 --- a/Kmse.Core/IO/IMasterSystemIoManager.cs +++ b/Kmse.Core/IO/IMasterSystemIoManager.cs @@ -11,14 +11,6 @@ void Initialize(IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort sou IDebugConsolePort debugConsolePort); void Reset(); - bool NonMaskableInterrupt { get; } - bool MaskableInterrupt { get; } - - void SetMaskableInterrupt(); - void ClearMaskableInterrupt(); - void SetNonMaskableInterrupt(); - void ClearNonMaskableInterrupt(); - byte ReadPort(ushort port); void WritePort(ushort port, byte value); } \ No newline at end of file diff --git a/Kmse.Core/IO/Logging/IIoPortLogger.cs b/Kmse.Core/IO/Logging/IIoPortLogger.cs index 8f3c9b0..fd41368 100644 --- a/Kmse.Core/IO/Logging/IIoPortLogger.cs +++ b/Kmse.Core/IO/Logging/IIoPortLogger.cs @@ -8,8 +8,5 @@ public interface IIoPortLogger void PortRead(ushort address, byte data); void PortWrite(ushort address, byte newData); - - void SetMaskableInterruptStatus(bool status); - void SetNonMaskableInterruptStatus(bool status); } } diff --git a/Kmse.Core/IO/MasterSystemIoManager.cs b/Kmse.Core/IO/MasterSystemIoManager.cs index 5811bf5..5bb02b2 100644 --- a/Kmse.Core/IO/MasterSystemIoManager.cs +++ b/Kmse.Core/IO/MasterSystemIoManager.cs @@ -3,6 +3,7 @@ using Kmse.Core.IO.Logging; using Kmse.Core.IO.Sound; using Kmse.Core.IO.Vdp; +using Kmse.Core.Z80.Interrupts; namespace Kmse.Core.IO; @@ -53,36 +54,6 @@ public void Reset() { _memoryControlRegister = 0x00; _ioControlRegister = 0x00; - - ClearMaskableInterrupt(); - ClearNonMaskableInterrupt(); - } - - public bool NonMaskableInterrupt { get; private set; } - public bool MaskableInterrupt { get; private set; } - - public void SetMaskableInterrupt() - { - MaskableInterrupt = true; - _logger.SetMaskableInterruptStatus(MaskableInterrupt); - } - - public void ClearMaskableInterrupt() - { - MaskableInterrupt = false; - _logger.SetMaskableInterruptStatus(MaskableInterrupt); - } - - public void SetNonMaskableInterrupt() - { - NonMaskableInterrupt = true; - _logger.SetNonMaskableInterruptStatus(NonMaskableInterrupt); - } - - public void ClearNonMaskableInterrupt() - { - NonMaskableInterrupt = false; - _logger.SetNonMaskableInterruptStatus(NonMaskableInterrupt); } /* SMS MkII Port Mapping diff --git a/Kmse.Core/IO/Vdp/IVdpPort.cs b/Kmse.Core/IO/Vdp/IVdpPort.cs index d8d5700..e930d76 100644 --- a/Kmse.Core/IO/Vdp/IVdpPort.cs +++ b/Kmse.Core/IO/Vdp/IVdpPort.cs @@ -5,7 +5,7 @@ public interface IVdpPort void Reset(); byte ReadPort(byte port); void WritePort(byte port, byte value); - bool Execute(int cycles); + void Execute(int cycles); VdpPortStatus GetStatus(); byte[] DumpVideoRam(); diff --git a/Kmse.Core/IO/Vdp/VdpPort.cs b/Kmse.Core/IO/Vdp/VdpPort.cs index 0028a29..3fb9807 100644 --- a/Kmse.Core/IO/Vdp/VdpPort.cs +++ b/Kmse.Core/IO/Vdp/VdpPort.cs @@ -84,10 +84,8 @@ public void WritePort(byte port, byte value) /// Execute update of VDP based on the number of cycles that have been executed by the CPU /// /// Number of cycles executed since last update - /// True if display can be written/refreshed or false if not ready to be written/refreshed - public bool Execute(int cycles) + public void Execute(int cycles) { - return false; } public VdpPortStatus GetStatus() diff --git a/Kmse.Core/MasterSystemMk2.cs b/Kmse.Core/MasterSystemMk2.cs index ea60ce0..e8ed6bd 100644 --- a/Kmse.Core/MasterSystemMk2.cs +++ b/Kmse.Core/MasterSystemMk2.cs @@ -7,6 +7,7 @@ using Kmse.Core.IO.Vdp; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Support; @@ -37,6 +38,7 @@ public class MasterSystemMk2 : IMasterSystemConsole private readonly IMasterSystemIoManager _io; private readonly ISoundPort _sound; private readonly IVdpPort _vdp; + private readonly IZ80InterruptManagement _cpuInterruptManagement; private bool _paused; private bool _running; @@ -54,6 +56,7 @@ public MasterSystemMk2(IZ80Cpu cpu, IMasterSystemIoManager io, IVdpPort vdp, ICo _memory = memory; _cpuLogger = cpuLogger; _cpu.Initialize(_memory, _io); + _cpuInterruptManagement = _cpu.GetInterruptManagementInterface(); _io.Initialize(_vdp, _controllers, _sound, _debugConsole); } @@ -135,11 +138,8 @@ public void Run() while (totalCycles < cpuCyclesPerFrame) { var cpuCycles = _cpu.ExecuteNextCycle(); - var canRefresh = _vdp.Execute(cpuCycles); - if (canRefresh) - { - // TODO: Display current frame on screen - } + _vdp.Execute(cpuCycles); + // TODO: Check if an interrupt has been generated by the VDP and if so, set the maskable interrupt in CPU via _cpuInterruptManagement _sound.Execute(cpuCycles); totalCycles += cpuCycles; @@ -171,7 +171,7 @@ public void TriggerPauseButton() // The Z80's NMI pin is connected to the PAUSE button. When this button is // pressed an NMI is generated, causing the PC to change to $0066.Releasing // the button does nothing. - _io.SetNonMaskableInterrupt(); + _cpuInterruptManagement.SetNonMaskableInterrupt(); } private double GetDisplayFrameRate() diff --git a/Kmse.Core/Z80/IZ80Cpu.cs b/Kmse.Core/Z80/IZ80Cpu.cs index 883d7f7..668eb3e 100644 --- a/Kmse.Core/Z80/IZ80Cpu.cs +++ b/Kmse.Core/Z80/IZ80Cpu.cs @@ -1,5 +1,6 @@ using Kmse.Core.IO; using Kmse.Core.Memory; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80; @@ -7,6 +8,7 @@ namespace Kmse.Core.Z80; public interface IZ80Cpu { void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io); + IZ80InterruptManagement GetInterruptManagementInterface(); CpuStatus GetStatus(); void Reset(); int ExecuteNextCycle(); diff --git a/Kmse.Core/Z80/Interrupts/IZ80InterruptManagement.cs b/Kmse.Core/Z80/Interrupts/IZ80InterruptManagement.cs new file mode 100644 index 0000000..c7f80f9 --- /dev/null +++ b/Kmse.Core/Z80/Interrupts/IZ80InterruptManagement.cs @@ -0,0 +1,25 @@ +namespace Kmse.Core.Z80.Interrupts; + +public interface IZ80InterruptManagement +{ + bool InterruptEnableFlipFlopStatus { get; } + bool InterruptEnableFlipFlopTempStorageStatus { get; } + byte InterruptMode { get; } + bool NonMaskableInterrupt { get; } + bool MaskableInterrupt { get; } + + void Reset(); + bool InterruptWaiting(); + + void SetMaskableInterrupt(); + void ClearMaskableInterrupt(); + void SetNonMaskableInterrupt(); + void ClearNonMaskableInterrupt(); + + void EnableMaskableInterrupts(); + void DisableMaskableInterrupts(); + void SetInterruptMode(byte mode); + void ResetInterruptEnableFlipFlopFromTemporaryStorage(); + + int ProcessInterrupts(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs new file mode 100644 index 0000000..2e98efe --- /dev/null +++ b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs @@ -0,0 +1,180 @@ +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80.Interrupts; + +public class Z80InterruptManagement : IZ80InterruptManagement +{ + private readonly ICpuLogger _cpuLogger; + private readonly IZ80ProgramCounter _programCounter; + + public Z80InterruptManagement(IZ80ProgramCounter programCounter, ICpuLogger cpuLogger) + { + _programCounter = programCounter; + _cpuLogger = cpuLogger; + } + + /// + /// Disables interrupts from being accepted if set to False + /// IFF1 + /// + public bool InterruptEnableFlipFlopStatus { get; private set; } + + /// + /// Temporary storage location for interrupt enable flip flip (IFF1) above + /// IFF2 + /// + public bool InterruptEnableFlipFlopTempStorageStatus { get; private set; } + + /// + /// Current Interrupt mode, used for maskable interrupts + /// + public byte InterruptMode { get; private set; } + + /// + /// Status of Non Maskable Interrupt, this is set to true if a non-maskable interrupt has been triggered + /// + public bool NonMaskableInterrupt { get; private set; } + + /// + /// Status of a Maskable Interrupt, this is set to true if a maskable interrupt has been triggered + /// + public bool MaskableInterrupt { get; private set; } + + public bool InterruptWaiting() + { + return NonMaskableInterrupt || (InterruptEnableFlipFlopStatus && MaskableInterrupt); + } + + public void Reset() + { + ClearMaskableInterrupt(); + ClearNonMaskableInterrupt(); + + InterruptEnableFlipFlopStatus = false; + InterruptEnableFlipFlopTempStorageStatus = false; + InterruptMode = 0; + } + + public void SetMaskableInterrupt() + { + MaskableInterrupt = true; + _cpuLogger.SetMaskableInterruptStatus(MaskableInterrupt); + } + + public void ClearMaskableInterrupt() + { + MaskableInterrupt = false; + _cpuLogger.SetMaskableInterruptStatus(MaskableInterrupt); + } + + public void SetNonMaskableInterrupt() + { + NonMaskableInterrupt = true; + _cpuLogger.SetNonMaskableInterruptStatus(NonMaskableInterrupt); + } + + public void ClearNonMaskableInterrupt() + { + NonMaskableInterrupt = false; + _cpuLogger.SetNonMaskableInterruptStatus(NonMaskableInterrupt); + } + + public void EnableMaskableInterrupts() + { + InterruptEnableFlipFlopStatus = true; + InterruptEnableFlipFlopTempStorageStatus = true; + } + + public void DisableMaskableInterrupts() + { + InterruptEnableFlipFlopStatus = false; + InterruptEnableFlipFlopTempStorageStatus = false; + } + + public void SetInterruptMode(byte mode) + { + if (mode > 2) + { + throw new InvalidOperationException("Z80 CPU interrupt mode must be set to 0, 1 or 2"); + } + + InterruptMode = mode; + } + + public void ResetInterruptEnableFlipFlopFromTemporaryStorage() + { + InterruptEnableFlipFlopStatus = InterruptEnableFlipFlopTempStorageStatus; + } + + public int ProcessInterrupts() + { + if (NonMaskableInterrupt) + { + return ProcessNonMaskableInterrupt(); + } + + if (InterruptEnableFlipFlopStatus && MaskableInterrupt) + { + return ProcessMaskableInterrupt(); + } + + return 0; + } + + private int ProcessNonMaskableInterrupt() + { + _cpuLogger.LogInstruction(_programCounter.Value, "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", + string.Empty); + + // Copy state of IFF1 into IFF2 to keep a copy and reset IFF1 so processing can continue without a masked interrupt occuring + // This gets copied back with a RETN occurs + InterruptEnableFlipFlopTempStorageStatus = InterruptEnableFlipFlopStatus; + InterruptEnableFlipFlopStatus = false; + + // We have to clear this here to avoid this triggering in every cycle + // but not sure this is accurate since if another NMI is triggered this could end up in an endless loop instead + ClearNonMaskableInterrupt(); + + // Handle NMI by jumping to 0x66 + _programCounter.SetAndSaveExisting(0x66); + + return 11; + } + + private int ProcessMaskableInterrupt() + { + _cpuLogger.LogInstruction(_programCounter.Value, "MI", "Maskable Interrupt", "Maskable Interrupt", + $"Mode {InterruptMode}"); + + InterruptEnableFlipFlopStatus = false; + InterruptEnableFlipFlopTempStorageStatus = false; + + if (InterruptMode is 0 or 1) + { + // The SMS hardware generates two types of interrupts: IRQs and NMIs. + // An IRQ is a maskable interrupt which may be generated by: + // * the VSync impulse which occurs when a frame has been rasterised, or: + // * a scanline counter falling below zero(see the VDP Register 10 description + // for details) + + // For the SMS 2, Game Gear, and Genesis, the value $FF is always read from + // the data bus, which corresponds to the instruction 'RST 38H'. + // Basically mode 0 is the same as mode 1 + + // Mode 1, jump to address 0x0038h + _programCounter.SetAndSaveExisting(0x0038); + return 11; + } + + // Mode 2 is not used in SMS since ports don't set a byte on data bus + //https://www.smspower.org/uploads/Development/richard.txt + // Maybe it is used but with random values returned? + //https://www.smspower.org/uploads/Development/smstech-20021112.txt + _cpuLogger.Error("Maskable Interrupt while in mode 2 which is not supported"); + ClearMaskableInterrupt(); + + // Treat this as a NOP which is 4 cycles + return 4; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Logging/ICpuLogger.cs b/Kmse.Core/Z80/Logging/ICpuLogger.cs index c4cd438..262deb0 100644 --- a/Kmse.Core/Z80/Logging/ICpuLogger.cs +++ b/Kmse.Core/Z80/Logging/ICpuLogger.cs @@ -5,4 +5,6 @@ public interface ICpuLogger void Debug(string message); void Error(string message); void LogInstruction(ushort baseAddress, string opCode, string operationName, string operationDescription, string data); + void SetMaskableInterruptStatus(bool status); + void SetNonMaskableInterruptStatus(bool status); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index 90ade16..a05cc25 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -111,11 +111,11 @@ private void PopulateCpuControlOperations() AddStandardInstruction(0x00, 4, "NOP", "No Operation", (_) => { }); AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { Halt(); }); - AddStandardInstruction(0xF3, 4, "DI", "Disable Interrupts", (_) => { _interruptFlipFlop1 = false; _interruptFlipFlop2 = false; }); - AddStandardInstruction(0xFB, 4, "EI", "Enable Interrupts", (_) => { _interruptFlipFlop1 = true; _interruptFlipFlop2 = true; }); - AddDoubleByteInstruction(0xED, 0x46, 8, "IM 0", "Set Maskable Interrupt to Mode 0", (_) => { _interruptMode = 0; }); - AddDoubleByteInstruction(0xED, 0x56, 8, "IM 1", "Set Maskable Interrupt to Mode 1", (_) => { _interruptMode = 1; }); - AddDoubleByteInstruction(0xED, 0x5E, 8, "IM 2", "Set Maskable Interrupt to Mode 2", (_) => { _interruptMode = 2; }); + AddStandardInstruction(0xF3, 4, "DI", "Disable Interrupts", (_) => { _interruptManagement.DisableMaskableInterrupts(); }); + AddStandardInstruction(0xFB, 4, "EI", "Enable Interrupts", (_) => { _interruptManagement.EnableMaskableInterrupts(); }); + AddDoubleByteInstruction(0xED, 0x46, 8, "IM 0", "Set Maskable Interrupt to Mode 0", (_) => { _interruptManagement.SetInterruptMode(0); }); + AddDoubleByteInstruction(0xED, 0x56, 8, "IM 1", "Set Maskable Interrupt to Mode 1", (_) => { _interruptManagement.SetInterruptMode(1); }); + AddDoubleByteInstruction(0xED, 0x5E, 8, "IM 2", "Set Maskable Interrupt to Mode 2", (_) => { _interruptManagement.SetInterruptMode(2); }); } private void PopulateJumpCallAndReturnOperations() @@ -183,8 +183,8 @@ private void PopulateJumpCallAndReturnOperations() AddStandardInstruction(0xF7, 11, "RST 30H", "Restart at 30h", _ => { _pc.SetAndSaveExisting(0x30); }); AddStandardInstruction(0xFF, 11, "RST 38H", "Restart at 38h", _ => { _pc.SetAndSaveExisting(0x38); }); - AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { _pc.SetFromStack(); _io.ClearMaskableInterrupt(); }); - AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { _pc.SetFromStack(); _interruptFlipFlop1 = _interruptFlipFlop2; }); + AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { _pc.SetFromStack(); _interruptManagement.ClearMaskableInterrupt(); }); + AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { _pc.SetFromStack(); _interruptManagement.ResetInterruptEnableFlipFlopFromTemporaryStorage(); }); } private void PopulateArthmeticAndLogicalInstructions() @@ -675,8 +675,8 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xED, 0x47, 9, "LD I,A", "Load A into I", _ => { _iRegister.Set(_accumulator); }); AddDoubleByteInstruction(0xED, 0x4F, 9, "LD R,A", "Load A into R", _ => { _rRegister.Set(_accumulator); }); - AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { _accumulator.SetFromInterruptRegister(_iRegister, _interruptFlipFlop2); }); - AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { _accumulator.SetFromMemoryRefreshRegister(_rRegister, _interruptFlipFlop2); }); + AddDoubleByteInstruction(0xED, 0x57, 9, "LD A,I", "Load I into A", _ => { _accumulator.SetFromInterruptRegister(_iRegister, _interruptManagement.InterruptEnableFlipFlopTempStorageStatus); }); + AddDoubleByteInstruction(0xED, 0x5F, 9, "LD A,R", "Load R into A", _ => { _accumulator.SetFromMemoryRefreshRegister(_rRegister, _interruptManagement.InterruptEnableFlipFlopTempStorageStatus); }); AddStandardInstruction(0x3E, 7, "LD A,N", "Load n into A", _ => { _accumulator.Set(_pc.GetNextDataByte()); }); AddStandardInstruction(0x06, 7, "LD B,N", "Load n into B", _ => { _b.Set(_pc.GetNextDataByte()); }); AddStandardInstruction(0x0E, 7, "LD C,N", "Load n into C", _ => { _c.Set(_pc.GetNextDataByte()); }); diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 895e134..999176c 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,5 +1,6 @@ using Kmse.Core.IO; using Kmse.Core.Memory; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Memory; @@ -42,16 +43,7 @@ public partial class Z80Cpu : IZ80Cpu private IZ80InterruptPageAddressRegister _iRegister; private IZ80CpuInputOutput _ioManagement; private IZ80CpuMemoryManagement _memoryManagement; - - /// - /// Disables interrupts from being accepted if set to False - /// - private bool _interruptFlipFlop1; - /// - /// Temporary storage location for FF1 above - /// - private bool _interruptFlipFlop2; - private byte _interruptMode; + private IZ80InterruptManagement _interruptManagement; public Z80Cpu(ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger) { @@ -90,6 +82,12 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _ioManagement = new Z80CpuInputOutput(_io, _flags); _memoryManagement = new Z80CpuMemoryManagement(_memory, _flags); + _interruptManagement = new Z80InterruptManagement(_pc, _cpuLogger); + } + + public IZ80InterruptManagement GetInterruptManagementInterface() + { + return _interruptManagement; } public CpuStatus GetStatus() @@ -113,13 +111,13 @@ public CpuStatus GetStatus() StackPointer = _stack.Value, IRegister = _iRegister.Value, RRegister = _rRegister.Value, - InterruptFlipFlop1 = _interruptFlipFlop1, + InterruptFlipFlop1 = _interruptManagement.InterruptEnableFlipFlopStatus, - InterruptFlipFlop2 = _interruptFlipFlop2, - InterruptMode = _interruptMode, + InterruptFlipFlop2 = _interruptManagement.InterruptEnableFlipFlopTempStorageStatus, + InterruptMode = _interruptManagement.InterruptMode, - NonMaskableInterruptStatus = _io.NonMaskableInterrupt, - MaskableInterruptStatus = _io.MaskableInterrupt + NonMaskableInterruptStatus = _interruptManagement.NonMaskableInterrupt, + MaskableInterruptStatus = _interruptManagement.MaskableInterrupt }; } @@ -130,11 +128,9 @@ public void Reset() _pc.Reset(); _stack.Reset(); + _interruptManagement.Reset(); _halted = false; - _interruptFlipFlop1 = false; - _interruptFlipFlop2 = false; - _interruptMode = 0; _af.Reset(); _bc.Reset(); @@ -145,9 +141,6 @@ public void Reset() _iRegister.Reset(); _rRegister.Reset(); - _io.ClearNonMaskableInterrupt(); - _io.ClearMaskableInterrupt(); - _instructionLogger.StartNewInstruction(0x00); } @@ -156,62 +149,11 @@ public int ExecuteNextCycle() _currentCycleCount = 0; _instructionLogger.StartNewInstruction(_pc.Value); - if (_io.NonMaskableInterrupt) + if (_interruptManagement.InterruptWaiting()) { - _cpuLogger.LogInstruction(_pc.Value, "NMI", "Non Maskable Interrupt", "Non Maskable Interrupt", string.Empty); - - // Copy state of IFF1 into IFF2 to keep a copy and reset IFF1 so processing can continue without a masked interrupt occuring - // This gets copied back with a RETN occurs - _interruptFlipFlop2 = _interruptFlipFlop1; - _interruptFlipFlop1 = false; - - // We have to clear this here to avoid this triggering in every cycle - // but not sure this is accurate since if another NMI is triggered this could end up in an endless loop instead - _io.ClearNonMaskableInterrupt(); - - // Handle NMI by jumping to 0x66 - _pc.SetAndSaveExisting(0x66); - - // If halted then NMI starts it up again + _currentCycleCount += _interruptManagement.ProcessInterrupts(); + // If halted and an interrupt occurs, then resume ResumeIfHalted(); - - _currentCycleCount += 11; - return _currentCycleCount; - } - - if (_interruptFlipFlop1 && _io.MaskableInterrupt) - { - _cpuLogger.LogInstruction(_pc.Value, "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {_interruptMode}"); - - _interruptFlipFlop1 = false; - _interruptFlipFlop2 = false; - - if (_interruptMode is 0 or 1) - { - // The SMS hardware generates two types of interrupts: IRQs and NMIs. - // An IRQ is a maskable interrupt which may be generated by: - // * the VSync impulse which occurs when a frame has been rasterised, or: - // * a scanline counter falling below zero(see the VDP Register 10 description - // for details) - - // For the SMS 2, Game Gear, and Genesis, the value $FF is always read from - // the data bus, which corresponds to the instruction 'RST 38H'. - // Basically mode 0 is the same as mode 1 - - // Mode 1, jump to address 0x0038h - _pc.SetAndSaveExisting(0x0038); - _currentCycleCount += 11; - - return _currentCycleCount; - } - - // Mode 2 is not used in SMS since ports don't set a byte on data bus - //https://www.smspower.org/uploads/Development/richard.txt - // Maybe it is used but with random values returned? - //https://www.smspower.org/uploads/Development/smstech-20021112.txt - _cpuLogger.Error("Maskable Interrupt while in mode 2 which is not supported"); - _currentCycleCount += NopCycleCount; - _io.ClearMaskableInterrupt(); return _currentCycleCount; } From d8020e1a9801fdec2b813e9307d4127e8e7d5420 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Wed, 21 Sep 2022 18:27:21 +1000 Subject: [PATCH 10/21] Add common helper methods to Z80FlagsManager to avoid having duplicate code checking for sign, zero and some other common cases. Note many different instructions handle flags differently so this only moves anything used multiple times into the flags manager class. --- Kmse.Core/Z80/IO/Z80CpuInputOutput.cs | 4 +- .../Z80/Memory/Z80CpuMemoryManagement.cs | 13 ++-- .../Z80/Registers/General/IZ80FlagsManager.cs | 12 ++++ .../Z80/Registers/General/Z80Accumulator.cs | 64 ++++++++----------- .../Z80/Registers/General/Z80FlagsManager.cs | 57 ++++++++++++++++- .../SpecialPurpose/Z80IndexRegisterXy.cs | 12 ++-- .../Z80/Registers/Z8016BitRegisterBase.cs | 15 ++--- .../Z808BitGeneralPurposeRegister.cs | 12 ++-- Kmse.Core/Z80/Registers/Z80RegisterBase.cs | 33 +++++----- Kmse.Core/Z80/Z80Cpu.Instructions.cs | 8 +-- Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs | 15 +++-- 11 files changed, 150 insertions(+), 95 deletions(-) diff --git a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs index f12e71f..a5fafde 100644 --- a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs +++ b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs @@ -28,8 +28,8 @@ public byte Read(byte high, byte low, bool setFlags) } // If high bit set, then negative so set sign flag - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); + _flags.SetIfNegative(data); + _flags.SetIfZero(data); _flags.ClearFlag(Z80StatusFlags.HalfCarryH); _flags.SetParityFromValue(data); diff --git a/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs index 7adb408..7886737 100644 --- a/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs +++ b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs @@ -40,13 +40,13 @@ public void IncrementMemory(IZ8016BitRegister register, byte offset = 0) _memory[address] = newValue; - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + _flags.SetIfNegative(newValue); + _flags.SetIfZero(newValue); // Set half carry is carry from bit 3 // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble // https://en.wikipedia.org/wiki/Half-carry_flag _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (value & 0x0F) == 0x0F); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x7F); + _flags.SetIfIncrementOverflow(value); _flags.ClearFlag(Z80StatusFlags.AddSubtractN); } @@ -58,15 +58,16 @@ public void DecrementMemory(IZ8016BitRegister register, byte offset = 0) _memory[address] = newValue; - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + _flags.SetIfNegative(newValue); + _flags.SetIfZero(newValue); + // Set half carry is borrow from bit 4 // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits // ie. 0x20 - 1 = 0x1F // https://en.wikipedia.org/wiki/Half-carry_flag // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (value & 0x0F) == 0x00); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x80); + _flags.SetIfDecrementOverflow(value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs index 20d848b..e9fe7f3 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -27,4 +27,16 @@ public interface IZ80FlagsManager void SetClearFlagConditional(Z80StatusFlags flags, bool condition); bool IsFlagSet(Z80StatusFlags flags); void SetParityFromValue(byte value); + + void SetIfZero(byte value); + void SetIfZero(ushort value); + void SetIfNegative(byte value); + void SetIfNegative(ushort value); + void SetIfNegative(int twosComplementValue); + void SetIfZero(int value); + + void SetIfIncrementOverflow(byte value); + void SetIfDecrementOverflow(byte value); + + void SetIfHalfCarry(byte currentValue, byte operand, int changedValue); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index 1c53868..41140ec 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -33,8 +33,8 @@ public void RotateLeftDigit(IZ80HlRegister hl) // A is higher bits of A left same + higher bits of hl var newAValue = (byte)((ah << 4) + hlh); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + Flags.SetIfNegative(newAValue); + Flags.SetIfZero(newAValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newAValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -56,8 +56,8 @@ public void RotateRightDigit(IZ80HlRegister hl) // A is higher bits of A left same + higher bits of hl var newAValue = (byte)((ah << 4) + hll); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newAValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newAValue == 0); + Flags.SetIfNegative(newAValue); + Flags.SetIfZero(newAValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newAValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -138,8 +138,8 @@ from bit 3 to bit 4. } Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, Bitwise.IsSet(currentValue ^ Value, 4)); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(Value, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, Value == 0); + Flags.SetIfNegative(Value); + Flags.SetIfZero(Value); Flags.SetParityFromValue(Value); } @@ -157,8 +157,9 @@ public void NegateAccumulatorRegister() { var twosComplementValue = (0 - Value) & 0xFF; - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, twosComplementValue == 0); + Flags.SetIfNegative(twosComplementValue); + Flags.SetIfZero(twosComplementValue); + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (Value & 0x0F) > 0); Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, Value == 0x80); Flags.SetFlag(Z80StatusFlags.AddSubtractN); @@ -183,15 +184,10 @@ public void Add(byte value, bool withCarry = false) var newValue = Value + valueWithCarry; - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - // Half carry occurs if the result of adding the lower nibbles means it will set the next higher bit (basically overflows) - // This is since the adding is done in two 4 bit operations not 1 8 bit operation internally - // This is then combined with the DAA instruction which adjusts the result to get the valid value - //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ newValue ^ value) & 0x10) != 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((value ^ Value ^ 0x80) & (Value ^ newValue) & 0x80) != 0); + Flags.SetIfNegative((byte)newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); + Flags.SetIfHalfCarry(Value, value, newValue); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value ^ 0x80) & (Value ^ newValue) & 0x80) != 0); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); // A carry is same as half carry just on the overall value @@ -217,15 +213,10 @@ public void Subtract(byte value, bool withCarry = false) var newValue = Value - valueWithCarry; - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - // Half carry occurs if the result of subtracting the higher nibbles means it will set the next lower bit (basically underflows) - // We check if the subtraction means that adding higher nibbles sets bit 3 - // This is then combined with the DAA instruction which adjusts the result to get the valid value - //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit - Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ newValue ^ value) & 0x10) != 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((value ^ Value) & (Value ^ newValue) & 0x80) != 0); + Flags.SetIfNegative((byte)newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); + Flags.SetIfHalfCarry(Value, value, newValue); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value) & (Value ^ newValue) & 0x80) != 0); Flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit @@ -239,11 +230,10 @@ public void Compare(byte value) // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not var difference = Value - value; - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((Value ^ difference ^ value) & 0x10) != 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((value ^ Value) & (Value ^ difference) & 0x80) != 0); + Flags.SetIfNegative((byte)difference); + Flags.SetIfZero((byte)(difference & 0xFF)); + Flags.SetIfHalfCarry(Value, value, difference); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value) & (Value ^ difference) & 0x80) != 0); Flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit @@ -260,8 +250,8 @@ public void And(byte value, byte valueToAndAgainst) { var newValue = (byte)(value & valueToAndAgainst); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); Flags.SetFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -280,8 +270,8 @@ public void Or(byte value, byte valueToAndAgainst) { var newValue = (byte)(value | valueToAndAgainst); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -300,8 +290,8 @@ public void Xor(byte value, byte valueToXorAgainst) { var newValue = (byte)(value ^ valueToXorAgainst); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs index 6a80b87..7da1276 100644 --- a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -1,4 +1,5 @@ -using Kmse.Core.Utilities; +using System.Reflection.PortableExecutable; +using Kmse.Core.Utilities; using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; @@ -84,5 +85,57 @@ public void SetParityFromValue(byte value) SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, bitsSet == 0 || bitsSet % 2 == 0); } - // TODO: Add generic methods for setting flags for sign, zero, carry to cut down on duplication as much as possible etc + public void SetIfZero(byte value) + { + SetClearFlagConditional(Z80StatusFlags.ZeroZ, value == 0); + } + + public void SetIfZero(ushort value) + { + SetClearFlagConditional(Z80StatusFlags.ZeroZ, value == 0); + } + + public void SetIfZero(int value) + { + SetClearFlagConditional(Z80StatusFlags.ZeroZ, value == 0); + } + + public void SetIfNegative(byte value) + { + SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(value, 7)); + } + + public void SetIfNegative(ushort value) + { + SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(value, 15)); + } + + public void SetIfNegative(int twosComplementValue) + { + SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); + } + + public void SetIfIncrementOverflow(byte value) + { + SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x7F); + } + + public void SetIfDecrementOverflow(byte value) + { + SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, value == 0x80); + } + + public void SetIfHalfCarry(byte currentValue, byte operand, int changedValue) + { + // Half carry occurs if the result of subtracting the higher nibbles means it will set the next lower bit (basically underflows) + // We check if the subtraction means that adding higher nibbles sets bit 3 + // This is then combined with the DAA instruction which adjusts the result to get the valid value + //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit + SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((currentValue ^ changedValue ^ operand) & 0x10) != 0); + } + + public void SetIfHalfCarry(ushort currentValue, ushort operand) + { + SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (currentValue & 0x0FFF) + (operand & 0x0FFF) > 0x0FFF); + } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs index a040a68..f4f20bd 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -43,27 +43,27 @@ public void DecrementLow() private void CheckIncrementFlags(byte newValue, byte oldValue) { - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); // Set half carry is carry from bit 3 // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble // https://en.wikipedia.org/wiki/Half-carry_flag Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); + Flags.SetIfIncrementOverflow(oldValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); } private void CheckDecrementFlags(byte newValue, byte oldValue) { - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); // Set half carry is borrow from bit 4 // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits // ie. 0x20 - 1 = 0x1F // https://en.wikipedia.org/wiki/Half-carry_flag // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); + Flags.SetIfDecrementOverflow(oldValue); Flags.SetFlag(Z80StatusFlags.AddSubtractN); } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index bf4b5f9..55c2cc4 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -71,11 +71,9 @@ public void Add(ushort source, bool withCarry = false) if (withCarry) { - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((Value ^ valueWithCarry) & 0x8000) == 0 && - ((Value ^ (newValue & 0xFFFF)) & 0x8000) != 0); + Flags.SetIfNegative((ushort)newValue); + Flags.SetIfZero((ushort)(newValue & 0xFFFF)); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((Value ^ valueWithCarry) & 0x8000) == 0 && ((Value ^ (newValue & 0xFFFF)) & 0x8000) != 0); } Set((ushort)newValue); @@ -96,13 +94,12 @@ public void Subtract(ushort source, bool withCarry = false) var newValue = Value - valueWithCarry; - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFFFF) == 0); + Flags.SetIfNegative((ushort)newValue); + Flags.SetIfZero((ushort)(newValue & 0xFFFF)); // Half carry for 16 bit occurs if the result of adding the lower of the higher 8 bit value means it will set the next higher bit (13th bit and basically overflows) Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (((Value ^ newValue ^ source) >> 8) & 0x10) != 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, - ((source ^ Value) & (Value ^ newValue) & 0x8000) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((source ^ Value) & (Value ^ newValue) & 0x8000) != 0); Flags.SetFlag(Z80StatusFlags.AddSubtractN); // Carry occurs if the result of adding the higher nibbles means it will set the next higher bit (17th bit and basically overflows) diff --git a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs index 28d55a6..4523ffe 100644 --- a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs @@ -87,27 +87,27 @@ public void ShiftRightLogical() private void CheckIncrementFlags(byte newValue, byte oldValue) { - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); // Set half carry is carry from bit 3 // Basically if all 4 lower bits are set, then incrementing means it would set bit 5 which in the high nibble // https://en.wikipedia.org/wiki/Half-carry_flag Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x0F); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x7F); + Flags.SetIfIncrementOverflow(oldValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); } private void CheckDecrementFlags(byte newValue, byte oldValue) { - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, newValue == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); // Set half carry is borrow from bit 4 // Basically if all 4 lower bits are clear, then decrementing would essentially set all the lower bits // ie. 0x20 - 1 = 0x1F // https://en.wikipedia.org/wiki/Half-carry_flag // This could also check by seeing if the new value & 0x0F == 0x0F means all the lower bits were set Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (oldValue & 0x0F) == 0x00); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, oldValue == 0x80); + Flags.SetIfDecrementOverflow(oldValue); Flags.SetFlag(Z80StatusFlags.AddSubtractN); } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs index a7ec97f..9b6c5e6 100644 --- a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -24,8 +24,9 @@ protected void RotateLeftCircular(ref byte register) Bitwise.Set(ref newValue, 0); } - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -45,8 +46,8 @@ protected void RotateLeft(ref byte register) Bitwise.Set(ref newValue, 0); } - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -66,8 +67,8 @@ protected void RotateRightCircular(ref byte register) Bitwise.Set(ref newValue, 7); } - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -88,8 +89,8 @@ protected void RotateRight(ref byte register) Bitwise.Set(ref newValue, 7); } - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -103,8 +104,8 @@ protected void ShiftLeftArithmetic(ref byte register) // Shift register left by 1 bit, bit 7 is copied to carry flag var newValue = (byte)(register << 1); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -126,8 +127,8 @@ protected void ShiftRightArithmetic(ref byte register) Bitwise.Set(ref newValue, 7); } - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -144,8 +145,8 @@ protected void ShiftLeftLogical(ref byte register) // The difference between shift left logical and shift left arithmetic is this sets bit 0 Bitwise.Set(ref newValue, 0); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); @@ -163,8 +164,8 @@ protected void ShiftRightLogical(ref byte register) // The difference between shift right logical and shift right arithmetic is this does maintain bit 7 when shifting and just clears it Bitwise.Clear(ref newValue, 7); - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetParityFromValue(newValue); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index a05cc25..e5209fe 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -947,7 +947,7 @@ private void PopulateInputOutputInstructions() _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); _hl.Increment(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); + _flags.SetIfZero(_b.Value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB2, DynamicCycleHandling, "INIR", "Input, Increment, Repeat", _ => @@ -990,7 +990,7 @@ private void PopulateInputOutputInstructions() _memoryManagement.WriteToMemory(_hl, data); _b.Decrement(); _hl.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); + _flags.SetIfZero(_b.Value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBA, DynamicCycleHandling, "INDR", "Input, Decrement, Repeat", _ => @@ -1044,7 +1044,7 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); _hl.Increment(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); + _flags.SetIfZero(_b.Value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xB3, DynamicCycleHandling, "OTIR", "Output, Increment, Repeat", _ => @@ -1089,7 +1089,7 @@ private void PopulateInputOutputInstructions() _io.WritePort(portAddress, data); _hl.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _b.Value == 0); + _flags.SetIfZero(_b.Value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => diff --git a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs index 7603bd3..eda9152 100644 --- a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs @@ -134,14 +134,15 @@ private void CompareIncrement() { var value = _memory[_hl.Value]; // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _af.High - (sbyte)value; + var difference = _accumulator.Value - (sbyte)value; _hl.Increment(); _bc.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, (difference & 0xFF) == 0); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetIfNegative((byte)difference); + _flags.SetIfZero((byte)(difference & 0xFF)); + + _flags.SetIfHalfCarry(_accumulator.Value, value, difference); _flags.SetFlag(Z80StatusFlags.AddSubtractN); _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); } @@ -150,14 +151,14 @@ private void CompareDecrement() { var value = _memory[_hl.Value]; // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _af.High - (sbyte)value; + var difference = _accumulator.Value - (sbyte)value; _hl.Decrement(); _bc.Decrement(); - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)difference, 7)); + _flags.SetIfNegative((byte)difference); _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); - _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((_af.High ^ difference ^ value) & 0x10) != 0); + _flags.SetIfHalfCarry(_accumulator.Value, value, difference); _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); _flags.SetFlag(Z80StatusFlags.AddSubtractN); } From ed04cd62c0eb628f19a835d1b5d050b53c441994 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Wed, 21 Sep 2022 22:00:55 +1000 Subject: [PATCH 11/21] Refactor to use dependency injection for all CPU registers and management classes and for I/O and memory classes. This removes the need for the Initialize calls in places. Most classes are registered as instance per scope, allowing easier and cleaner usage given some of the cross dependencies but allowing it to be restricted it to a single scope. The CPU uses a simple set of facades to avoid the Z80CPU class constructor getting too large and brittle --- Kmse.Console/EmulatorService.cs | 83 ++++++++++-------- .../Z80CpuTests/CpuExecutionFixture.cs | 28 +----- .../Z80CpuTests/CpuInstructionsFixture.cs | 49 +++-------- .../Z80CpuTests/CpuTestFixtureBase.cs | 81 +++++++++++++++++ .../TestInstructionsFixture.cs | 50 ++++++++++- .../Z80CpuTests/InstructionTests/TestIo.cs | 32 +++---- .../Z80CpuTests/Z80ProgramCounterFixture.cs | 2 +- .../Z80CpuTests/Z80StackManagementFixture.cs | 2 +- Kmse.Core/IO/IMasterSystemIoManager.cs | 9 +- Kmse.Core/IO/IoPortsModule.cs | 10 +-- Kmse.Core/IO/MasterSystemIoManager.cs | 8 +- Kmse.Core/Infrastructure/EmulatorModule.cs | 17 ++-- Kmse.Core/MasterSystemMk2.cs | 6 +- Kmse.Core/Z80/IZ80Cpu.cs | 7 +- .../Z80/Interrupts/Z80InterruptManagement.cs | 12 +-- .../Z80/Registers/General/Z80AfRegister.cs | 8 +- .../Z80/Registers/General/Z80BcRegister.cs | 6 +- .../Z80/Registers/General/Z80DeRegister.cs | 7 +- .../Z80/Registers/General/Z80HlRegister.cs | 6 +- .../SpecialPurpose/IZ80StackManager.cs | 4 +- .../SpecialPurpose/Z80ProgramCounter.cs | 11 ++- .../SpecialPurpose/Z80StackManager.cs | 4 +- Kmse.Core/Z80/Z80Cpu.cs | 86 ++++++++----------- Kmse.Core/Z80/Z80CpuManagement.cs | 12 +++ Kmse.Core/Z80/Z80CpuModule.cs | 44 ++++++++++ Kmse.Core/Z80/Z80CpuRegisters.cs | 18 ++++ 26 files changed, 365 insertions(+), 237 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs create mode 100644 Kmse.Core/Z80/Z80CpuManagement.cs create mode 100644 Kmse.Core/Z80/Z80CpuModule.cs create mode 100644 Kmse.Core/Z80/Z80CpuRegisters.cs diff --git a/Kmse.Console/EmulatorService.cs b/Kmse.Console/EmulatorService.cs index c2fa92e..33886c5 100644 --- a/Kmse.Console/EmulatorService.cs +++ b/Kmse.Console/EmulatorService.cs @@ -1,4 +1,5 @@ using System.Text; +using Autofac; using Kmse.Console.Logging; using Kmse.Core; using Kmse.Core.IO.Controllers; @@ -14,19 +15,16 @@ internal class EmulatorService : BackgroundService private readonly IHostApplicationLifetime _applicationLifetime; private readonly SerilogCpuLogger _cpuLogger; private readonly SerilogIoLogger _ioLogger; + private readonly ILifetimeScope _scope; private readonly ILogger _log; - private readonly IMasterSystemConsole _masterSystemConsole; - private readonly IControllerPort _controllers; private readonly SerilogMemoryLogger _memoryLogger; private readonly string _romFilename; - public EmulatorService(ILogger log, Options options, IMasterSystemConsole masterSystemConsole, IControllerPort controllers, - IHostApplicationLifetime applicationLifetime, + public EmulatorService(ILifetimeScope scope, ILogger log, Options options, IHostApplicationLifetime applicationLifetime, SerilogCpuLogger cpuLogger, SerilogMemoryLogger memoryLogger, SerilogIoLogger ioLogger) { + _scope = scope; _log = log; - _masterSystemConsole = masterSystemConsole; - _controllers = controllers; _applicationLifetime = applicationLifetime; _cpuLogger = cpuLogger; _memoryLogger = memoryLogger; @@ -37,16 +35,25 @@ public EmulatorService(ILogger log, Options options, IMasterSystemConsole master protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Yield(); - stoppingToken.Register(() => _masterSystemConsole.PowerOff()); - await _masterSystemConsole.LoadCartridge(_romFilename, stoppingToken); - _masterSystemConsole.PowerOn(); + await using var emulationScope = _scope.BeginLifetimeScope(); + + var masterSystemConsole = emulationScope.Resolve(); + var controllers = emulationScope.Resolve(); + stoppingToken.Register(() => masterSystemConsole.PowerOff()); + + await masterSystemConsole.LoadCartridge(_romFilename, stoppingToken); + masterSystemConsole.PowerOn(); // Run two tasks, one to run the main CPU loop and one to capture key inputs // Key inputs is temporary until we wire up proper inputs - var task = Task.Run(() => RunEmulation(stoppingToken), stoppingToken); - await Task.Run(() => HandleInputs(stoppingToken), stoppingToken); + var emulationCancellationTokenSource = new CancellationTokenSource(); + var emulationRunTask = Task.Run(() => RunEmulation(masterSystemConsole, emulationCancellationTokenSource.Token, stoppingToken), stoppingToken); + await Task.Run(() => HandleInputs(masterSystemConsole, controllers, emulationCancellationTokenSource), stoppingToken); + await emulationRunTask.WaitAsync(stoppingToken); + + emulationScope.Dispose(); _log.Information("Emulation stopped"); System.Console.WriteLine("Press any key to exit"); @@ -54,14 +61,14 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _applicationLifetime.StopApplication(); } - private void RunEmulation(CancellationToken cancellationToken) + private void RunEmulation(IMasterSystemConsole masterSystemConsole, CancellationToken emulationCancellationToken, CancellationToken applicationCancellationToken) { - while (!cancellationToken.IsCancellationRequested) + while (!emulationCancellationToken.IsCancellationRequested && !applicationCancellationToken.IsCancellationRequested) { // This loop allows us to restart since if powered off then just stays here until we power it back on again - if (_masterSystemConsole.IsRunning()) + if (masterSystemConsole.IsRunning()) { - _masterSystemConsole.Run(); + masterSystemConsole.Run(); } else { @@ -70,21 +77,21 @@ private void RunEmulation(CancellationToken cancellationToken) } } - private void UpdateControllerEmulationForKey(ConsoleKey key, bool inputA, ControllerInputStatus button) + private void UpdateControllerEmulationForKey(IControllerPort controllerPort, ConsoleKey key, bool inputA, ControllerInputStatus button) { // TODO: Not particularly efficient since we are setting this to be pressed or not pressed each time var pressed = Keyboard.IsKeyDown(key); if (inputA) { - _controllers.ChangeInputAControlState(button, pressed); + controllerPort.ChangeInputAControlState(button, pressed); } else { - _controllers.ChangeInputBControlState(button, pressed); + controllerPort.ChangeInputBControlState(button, pressed); } } - private void HandleInputs(CancellationToken cancellationToken) + private void HandleInputs(IMasterSystemConsole masterSystemConsole, IControllerPort controllerPort, CancellationTokenSource emulationCancellationTokenSource) { System.Console.WriteLine("Esc to exit console app"); System.Console.WriteLine("TAB to stop/start emulation"); @@ -104,17 +111,17 @@ private void HandleInputs(CancellationToken cancellationToken) System.Console.WriteLine("Z to trigger controller A left button"); System.Console.WriteLine("X to trigger controller A right button"); - while (!cancellationToken.IsCancellationRequested) + while (!emulationCancellationTokenSource.IsCancellationRequested) { // Check the state of the input keys first and then handle regular commands like normal // This is so the controller emulation can get key up and down separately - UpdateControllerEmulationForKey(ConsoleKey.Z, true, ControllerInputStatus.LeftButton); - UpdateControllerEmulationForKey(ConsoleKey.X, true, ControllerInputStatus.RightButton); - UpdateControllerEmulationForKey(ConsoleKey.UpArrow, true, ControllerInputStatus.Up); - UpdateControllerEmulationForKey(ConsoleKey.DownArrow, true, ControllerInputStatus.Down); - UpdateControllerEmulationForKey(ConsoleKey.LeftArrow, true, ControllerInputStatus.Left); - UpdateControllerEmulationForKey(ConsoleKey.RightArrow, true, ControllerInputStatus.Right); - _controllers.ChangeResetButtonState(Keyboard.IsKeyDown(ConsoleKey.R)); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.Z, true, ControllerInputStatus.LeftButton); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.X, true, ControllerInputStatus.RightButton); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.UpArrow, true, ControllerInputStatus.Up); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.DownArrow, true, ControllerInputStatus.Down); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.LeftArrow, true, ControllerInputStatus.Left); + UpdateControllerEmulationForKey(controllerPort, ConsoleKey.RightArrow, true, ControllerInputStatus.Right); + controllerPort.ChangeResetButtonState(Keyboard.IsKeyDown(ConsoleKey.R)); if (!System.Console.KeyAvailable) { @@ -127,40 +134,42 @@ private void HandleInputs(CancellationToken cancellationToken) { case ConsoleKey.Escape: { - _log.Information("Existing emulation console app"); + _log.Information("Exiting emulation console app"); + masterSystemConsole.PowerOff(); + emulationCancellationTokenSource.Cancel(); return; } case ConsoleKey.Tab: { - if (_masterSystemConsole.IsRunning()) + if (masterSystemConsole.IsRunning()) { _log.Information("Stopping emulation"); - _masterSystemConsole.PowerOff(); + masterSystemConsole.PowerOff(); } else { _log.Information("Starting emulation"); - _masterSystemConsole.PowerOn(); + masterSystemConsole.PowerOn(); } } break; case ConsoleKey.P: { - if (_masterSystemConsole.IsPaused()) + if (masterSystemConsole.IsPaused()) { _log.Information("Unpausing emulation"); - _masterSystemConsole.Unpause(); + masterSystemConsole.Unpause(); } else { _log.Information("Pausing emulation"); - _masterSystemConsole.Pause(); + masterSystemConsole.Pause(); } } break; case ConsoleKey.S: { - var status = _masterSystemConsole.GetCpuStatus(); + var status = masterSystemConsole.GetCpuStatus(); _log.Information($"Flags: {GetStatusFlagsAsString(status)}"); _log.Information($"Registers: {GetRegistersAsString(status)}"); } @@ -186,13 +195,13 @@ private void HandleInputs(CancellationToken cancellationToken) case ConsoleKey.O: { _log.Information("Hitting Console Pause Button"); - _masterSystemConsole.TriggerPauseButton(); + masterSystemConsole.TriggerPauseButton(); } break; } } - _masterSystemConsole.PowerOff(); + masterSystemConsole.PowerOff(); } diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index ad9555a..18dcdf3 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -10,34 +10,8 @@ namespace Kmse.Core.UnitTests.Z80CpuTests; [TestFixture] -public class CpuExecutionFixture +public class CpuExecutionFixture : CpuTestFixtureBase { - [SetUp] - public void Setup() - { - _cpuLogger = Substitute.For(); - _memory = Substitute.For(); - _io = Substitute.For(); - _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); - _cpu.Initialize(_memory, _io); - _interruptManagement = _cpu.GetInterruptManagementInterface(); - } - - private void PrepareForTest() - { - _cpu.Reset(); - _memory.ClearReceivedCalls(); - _io.ClearReceivedCalls(); - _interruptManagement.ClearMaskableInterrupt(); - _interruptManagement.ClearNonMaskableInterrupt(); - } - - private Z80Cpu _cpu; - private ICpuLogger _cpuLogger; - private IMasterSystemMemory _memory; - private IMasterSystemIoManager _io; - private IZ80InterruptManagement _interruptManagement; - [Test] public void WhenResettingCpu() { diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index 26c166c..f62cdcc 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs @@ -1,42 +1,13 @@ using FluentAssertions; -using Kmse.Core.IO; -using Kmse.Core.Memory; -using Kmse.Core.Z80; -using Kmse.Core.Z80.Interrupts; -using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Support; using NSubstitute; -using NSubstitute.ClearExtensions; using NUnit.Framework; namespace Kmse.Core.UnitTests.Z80CpuTests; [TestFixture] -public class CpuInstructionsFixture +public class CpuInstructionsFixture : CpuTestFixtureBase { - [SetUp] - public void Setup() - { - _cpuLogger = Substitute.For(); - _memory = Substitute.For(); - _io = Substitute.For(); - _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); - _cpu.Initialize(_memory, _io); - } - - private void PrepareForTest() - { - _cpu.Reset(); - _memory.ClearReceivedCalls(); - _memory.ClearSubstitute(); - _io.ClearReceivedCalls(); - } - - private Z80Cpu _cpu; - private ICpuLogger _cpuLogger; - private IMasterSystemMemory _memory; - private IMasterSystemIoManager _io; - private static object[] _jrTestCases = { // This is 500 + 2 + offset (the 2 is for the instruction) @@ -48,7 +19,7 @@ private void PrepareForTest() // Since 130 is actually -126 here, we go backwards new object[] { (byte)130, (ushort)376 }, // Since 255 is actually -1 here, we go backwards by 1 - new object[] { (byte)255, (ushort) 501 }, + new object[] { (byte)255, (ushort)501 } }; [Test] @@ -97,16 +68,16 @@ public void WhenExecutingAddWithCarryWithNoCarry() var status = _cpu.GetStatus(); status.Pc.Should().Be(0x03); - + var expectedValue = (byte)(i + i); var result = originalValue + valueToAdd; var foundValue = _cpu.GetStatus().Af.High; foundValue.Should().Be(expectedValue); - var signFlagSet = (expectedValue >> 7) == 0x01; + var signFlagSet = expectedValue >> 7 == 0x01; var zeroFlagSet = expectedValue == 0; - var hFlagSet = (((originalValue ^ result ^ originalValue) & 0x10) != 0); - var pvFlagSet = (((originalValue ^ originalValue ^ 0x80) & (originalValue ^ result) & 0x80) != 0); + var hFlagSet = ((originalValue ^ result ^ originalValue) & 0x10) != 0; + var pvFlagSet = ((originalValue ^ originalValue ^ 0x80) & (originalValue ^ result) & 0x80) != 0; var carryFlagSet = result > 0xFF; IsFlagSet(signFlagSet, status.Af.Low, Z80StatusFlags.SignS); @@ -128,7 +99,7 @@ public void WhenExecutingAddWithCarryWithCarry() PrepareForTest(); var originalValue = (byte)i; - var valueToAdd = i+1; + var valueToAdd = i + 1; // scf to set carry flag _memory[0].Returns((byte)0x37); @@ -158,10 +129,10 @@ public void WhenExecutingAddWithCarryWithCarry() var foundValue = _cpu.GetStatus().Af.High; foundValue.Should().Be(expectedValue); - var signFlagSet = (expectedValue >> 7) == 0x01; + var signFlagSet = expectedValue >> 7 == 0x01; var zeroFlagSet = expectedValue == 0; - var hFlagSet = (((originalValue ^ result ^ originalValue) & 0x10) != 0); - var pvFlagSet = (((originalValue ^ originalValue ^ 0x80) & (originalValue ^ result) & 0x80) != 0); + var hFlagSet = ((originalValue ^ result ^ originalValue) & 0x10) != 0; + var pvFlagSet = ((originalValue ^ originalValue ^ 0x80) & (originalValue ^ result) & 0x80) != 0; var carryFlagSet = result > 0xFF; IsFlagSet(signFlagSet, status.Af.Low, Z80StatusFlags.SignS); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs new file mode 100644 index 0000000..e6a0fb3 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -0,0 +1,81 @@ +using Kmse.Core.IO; +using Kmse.Core.Memory; +using Kmse.Core.Z80; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using NSubstitute; +using NSubstitute.ClearExtensions; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests; + +public abstract class CpuTestFixtureBase +{ + protected Z80Cpu _cpu; + protected ICpuLogger _cpuLogger; + protected IZ80InterruptManagement _interruptManagement; + protected IMasterSystemIoManager _io; + protected IMasterSystemMemory _memory; + + protected void PrepareForTest() + { + _cpu.Reset(); + _memory.ClearReceivedCalls(); + _memory.ClearSubstitute(); + _io.ClearReceivedCalls(); + } + + [SetUp] + public void Setup() + { + _cpuLogger = Substitute.For(); + _memory = Substitute.For(); + _io = Substitute.For(); + var instructionLogger = new Z80InstructionLogger(_cpuLogger); + + var flags = new Z80FlagsManager(); + var accumulator = new Z80Accumulator(flags, _memory); + var af = new Z80AfRegister(_memory, flags, accumulator); + var bc = new Z80BcRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var de = new Z80DeRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var hl = new Z80HlRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var ix = new Z80IndexRegisterXy(_memory, af.Flags); + var iy = new Z80IndexRegisterXy(_memory, af.Flags); + var rRegister = new Z80MemoryRefreshRegister(_memory, af.Flags); + var iRegister = new Z80InterruptPageAddressRegister(_memory, af.Flags); + + var stack = new Z80StackManager(_memory, af.Flags, _cpuLogger); + var pc = new Z80ProgramCounter(_memory, af.Flags, stack, instructionLogger); + + var ioManagement = new Z80CpuInputOutput(_io, af.Flags); + var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); + _interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); + + var registers = new Z80CpuRegisters + { + Pc = pc, + Stack = stack, + Af = af, + Bc = bc, + De = de, + Hl = hl, + IX = ix, + IY = iy, + R = rRegister, + I = iRegister + }; + var cpuManagement = new Z80CpuManagement + { + IoManagement = ioManagement, + InterruptManagement = _interruptManagement, + MemoryManagement = memoryManagement + }; + + _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs index 9934b34..4bfcd5b 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs @@ -6,7 +6,13 @@ using System.Text.Json; using FluentAssertions; using Kmse.Core.Z80; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; using Kmse.Core.Z80.Support; using NSubstitute; using NUnit.Framework; @@ -36,8 +42,48 @@ public void Setup() _cpuLogger = Substitute.For(); _memory = new TestMemory(); _io = new TestIo(); - _cpu = new Z80Cpu(_cpuLogger, new Z80InstructionLogger(_cpuLogger)); - _cpu.Initialize(_memory, _io); + + var instructionLogger = new Z80InstructionLogger(_cpuLogger); + + var flags = new Z80FlagsManager(); + var accumulator = new Z80Accumulator(flags, _memory); + var af = new Z80AfRegister(_memory, flags, accumulator); + var bc = new Z80BcRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var de = new Z80DeRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var hl = new Z80HlRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); + var ix = new Z80IndexRegisterXy(_memory, af.Flags); + var iy = new Z80IndexRegisterXy(_memory, af.Flags); + var rRegister = new Z80MemoryRefreshRegister(_memory, af.Flags); + var iRegister = new Z80InterruptPageAddressRegister(_memory, af.Flags); + + var stack = new Z80StackManager(_memory, af.Flags, _cpuLogger); + var pc = new Z80ProgramCounter(_memory, af.Flags, stack, instructionLogger); + + var ioManagement = new Z80CpuInputOutput(_io, af.Flags); + var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); + var interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); + + var registers = new Z80CpuRegisters + { + Pc = pc, + Stack = stack, + Af = af, + Bc = bc, + De = de, + Hl = hl, + IX = ix, + IY = iy, + R = rRegister, + I = iRegister + }; + var cpuManagement = new Z80CpuManagement + { + IoManagement = ioManagement, + InterruptManagement = interruptManagement, + MemoryManagement = memoryManagement + }; + + _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); } protected static T DeserializeTestFile(string filename) diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs index 4e726fc..bfc726f 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs @@ -1,9 +1,5 @@ using System.Security.Cryptography; using Kmse.Core.IO; -using Kmse.Core.IO.Controllers; -using Kmse.Core.IO.DebugConsole; -using Kmse.Core.IO.Sound; -using Kmse.Core.IO.Vdp; namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; @@ -11,12 +7,14 @@ public class TestIo : IMasterSystemIoManager { private readonly Memory _portData = new(new byte[0xFF + 1]); - public void Initialize(IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort soundPort, - IDebugConsolePort debugConsolePort) + public TestIo() { _portData.Span.Fill(0xBB); } + public bool NonMaskableInterrupt { get; private set; } + public bool MaskableInterrupt { get; private set; } + public void Reset() { _portData.Span.Fill(0xBB); @@ -24,8 +22,16 @@ public void Reset() ClearNonMaskableInterrupt(); } - public bool NonMaskableInterrupt { get; private set; } - public bool MaskableInterrupt { get; private set; } + public byte ReadPort(ushort port) + { + return _portData.Span[port & 0xFF]; + } + + public void WritePort(ushort port, byte value) + { + _portData.Span[port & 0xFF] = value; + } + public void SetMaskableInterrupt() { MaskableInterrupt = true; @@ -46,16 +52,6 @@ public void ClearNonMaskableInterrupt() NonMaskableInterrupt = false; } - public byte ReadPort(ushort port) - { - return _portData.Span[port & 0xFF]; - } - - public void WritePort(ushort port, byte value) - { - _portData.Span[port & 0xFF] = value; - } - public string GetHash() { using var sha256Hash = SHA256.Create(); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs index 88452a4..acccbbd 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs @@ -23,7 +23,7 @@ public void Setup() _instructionLogger = Substitute.For(); _flags = Substitute.For(); _stack = Substitute.For(); - _programCounter = new Z80ProgramCounter(_memory, _instructionLogger, _flags, _stack); + _programCounter = new Z80ProgramCounter(_memory, _flags, _stack, _instructionLogger); } [Test] diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs index bdb6702..869e345 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -22,7 +22,7 @@ public void Setup() _memory = Substitute.For(); _cpuLogger = Substitute.For(); _flags = Substitute.For(); - _stackManager = new Z80StackManager(_memory, _cpuLogger, _flags); + _stackManager = new Z80StackManager(_memory, _flags, _cpuLogger); _memory.GetMinimumAvailableMemorySize().Returns(100); _memory.GetMaximumAvailableMemorySize().Returns(0x5000); diff --git a/Kmse.Core/IO/IMasterSystemIoManager.cs b/Kmse.Core/IO/IMasterSystemIoManager.cs index 7a912ef..c94d700 100644 --- a/Kmse.Core/IO/IMasterSystemIoManager.cs +++ b/Kmse.Core/IO/IMasterSystemIoManager.cs @@ -1,14 +1,7 @@ -using Kmse.Core.IO.Controllers; -using Kmse.Core.IO.DebugConsole; -using Kmse.Core.IO.Sound; -using Kmse.Core.IO.Vdp; - -namespace Kmse.Core.IO; +namespace Kmse.Core.IO; public interface IMasterSystemIoManager { - void Initialize(IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort soundPort, - IDebugConsolePort debugConsolePort); void Reset(); byte ReadPort(ushort port); diff --git a/Kmse.Core/IO/IoPortsModule.cs b/Kmse.Core/IO/IoPortsModule.cs index fa0b172..693658e 100644 --- a/Kmse.Core/IO/IoPortsModule.cs +++ b/Kmse.Core/IO/IoPortsModule.cs @@ -10,10 +10,10 @@ public class IoPortsModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As().InstancePerDependency(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); } } \ No newline at end of file diff --git a/Kmse.Core/IO/MasterSystemIoManager.cs b/Kmse.Core/IO/MasterSystemIoManager.cs index 5bb02b2..c5cc8e1 100644 --- a/Kmse.Core/IO/MasterSystemIoManager.cs +++ b/Kmse.Core/IO/MasterSystemIoManager.cs @@ -36,14 +36,10 @@ public class MasterSystemIoManager : IMasterSystemIoManager private byte _memoryControlRegister; private byte _ioControlRegister; - public MasterSystemIoManager(IIoPortLogger logger) - { - _logger = logger; - } - - public void Initialize(IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort soundPort, + public MasterSystemIoManager(IIoPortLogger logger, IVdpPort vdpPort, IControllerPort controllerPort, ISoundPort soundPort, IDebugConsolePort debugConsolePort) { + _logger = logger; _vdpPort = vdpPort; _controllerPort = controllerPort; _soundPort = soundPort; diff --git a/Kmse.Core/Infrastructure/EmulatorModule.cs b/Kmse.Core/Infrastructure/EmulatorModule.cs index a5d5969..8d13580 100644 --- a/Kmse.Core/Infrastructure/EmulatorModule.cs +++ b/Kmse.Core/Infrastructure/EmulatorModule.cs @@ -1,10 +1,15 @@ -using System.IO.Abstractions; +using System; +using System.IO.Abstractions; using Autofac; using Kmse.Core.Cartridge; using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; -using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; namespace Kmse.Core.Infrastructure; @@ -14,10 +19,10 @@ protected override void Load(ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterModule(); - builder.RegisterType().As(); + builder.RegisterModule(); } } \ No newline at end of file diff --git a/Kmse.Core/MasterSystemMk2.cs b/Kmse.Core/MasterSystemMk2.cs index e8ed6bd..e77c7db 100644 --- a/Kmse.Core/MasterSystemMk2.cs +++ b/Kmse.Core/MasterSystemMk2.cs @@ -44,7 +44,7 @@ public class MasterSystemMk2 : IMasterSystemConsole private bool _running; public MasterSystemMk2(IZ80Cpu cpu, IMasterSystemIoManager io, IVdpPort vdp, IControllerPort controllers, - ISoundPort sound, IDebugConsolePort debugConsole, IMasterSystemCartridge cartridge, IMasterSystemMemory memory, ICpuLogger cpuLogger) + ISoundPort sound, IDebugConsolePort debugConsole, IMasterSystemCartridge cartridge, IMasterSystemMemory memory, ICpuLogger cpuLogger, IZ80InterruptManagement cpuInterruptManagement) { _cpu = cpu; _io = io; @@ -55,9 +55,7 @@ public MasterSystemMk2(IZ80Cpu cpu, IMasterSystemIoManager io, IVdpPort vdp, ICo _cartridge = cartridge; _memory = memory; _cpuLogger = cpuLogger; - _cpu.Initialize(_memory, _io); - _cpuInterruptManagement = _cpu.GetInterruptManagementInterface(); - _io.Initialize(_vdp, _controllers, _sound, _debugConsole); + _cpuInterruptManagement = cpuInterruptManagement; } public async Task LoadCartridge(string filename, CancellationToken cancellationToken) diff --git a/Kmse.Core/Z80/IZ80Cpu.cs b/Kmse.Core/Z80/IZ80Cpu.cs index 668eb3e..62a46a9 100644 --- a/Kmse.Core/Z80/IZ80Cpu.cs +++ b/Kmse.Core/Z80/IZ80Cpu.cs @@ -1,14 +1,9 @@ -using Kmse.Core.IO; -using Kmse.Core.Memory; -using Kmse.Core.Z80.Interrupts; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80; public interface IZ80Cpu { - void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io); - IZ80InterruptManagement GetInterruptManagementInterface(); CpuStatus GetStatus(); void Reset(); int ExecuteNextCycle(); diff --git a/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs index 2e98efe..25f80ff 100644 --- a/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs +++ b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs @@ -5,7 +5,7 @@ namespace Kmse.Core.Z80.Interrupts; public class Z80InterruptManagement : IZ80InterruptManagement { - private readonly ICpuLogger _cpuLogger; + private ICpuLogger _cpuLogger; private readonly IZ80ProgramCounter _programCounter; public Z80InterruptManagement(IZ80ProgramCounter programCounter, ICpuLogger cpuLogger) @@ -41,11 +41,6 @@ public Z80InterruptManagement(IZ80ProgramCounter programCounter, ICpuLogger cpuL /// public bool MaskableInterrupt { get; private set; } - public bool InterruptWaiting() - { - return NonMaskableInterrupt || (InterruptEnableFlipFlopStatus && MaskableInterrupt); - } - public void Reset() { ClearMaskableInterrupt(); @@ -56,6 +51,11 @@ public void Reset() InterruptMode = 0; } + public bool InterruptWaiting() + { + return NonMaskableInterrupt || (InterruptEnableFlipFlopStatus && MaskableInterrupt); + } + public void SetMaskableInterrupt() { MaskableInterrupt = true; diff --git a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs index f162f96..c6b7070 100644 --- a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -8,11 +8,11 @@ public class Z80AfRegister : IZ80AfRegister { private readonly IMasterSystemMemory _memory; - public Z80AfRegister(IMasterSystemMemory memory) + public Z80AfRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, IZ80Accumulator accumulator) { _memory = memory; - Flags = new Z80FlagsManager(); - Accumulator = new Z80Accumulator(Flags, memory); + Flags = flags; + Accumulator = accumulator; } public IZ80Accumulator Accumulator { get; } @@ -29,8 +29,6 @@ public void Reset() Accumulator.Reset(); } - // TODO: Remove any methods not used, since mixed with flags, very few operations act on AF as a 16 bit register - public void Set(ushort value) { var (high, low) = Bitwise.ToBytes(value); diff --git a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs index 6b9e5c0..10ecbdc 100644 --- a/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80BcRegister.cs @@ -4,11 +4,11 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80BcRegister : Z8016BitGeneralPurposeRegisterBase, IZ80BcRegister { - public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) + public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, Func registerFactory) : base(memory, flags) { - B = new Z808BitGeneralPurposeRegister(memory, flags); - C = new Z808BitGeneralPurposeRegister(memory, flags); + B = registerFactory(); + C = registerFactory(); } protected override IZ808BitRegister HighRegister => B; diff --git a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs index 4963b75..19a3cef 100644 --- a/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs @@ -4,11 +4,12 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80DeRegister : Z8016BitGeneralPurposeRegisterBase, IZ80DeRegister { - public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) + public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, + Func registerFactory) : base(memory, flags) { - D = new Z808BitGeneralPurposeRegister(memory, flags); - E = new Z808BitGeneralPurposeRegister(memory, flags); + D = registerFactory(); + E = registerFactory(); } protected override IZ808BitRegister HighRegister => D; diff --git a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs index 3bd1416..8ebed6c 100644 --- a/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs @@ -4,11 +4,11 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80HlRegister : Z8016BitGeneralPurposeRegisterBase, IZ80HlRegister { - public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) + public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, Func registerFactory) : base(memory, flags) { - H = new Z808BitGeneralPurposeRegister(memory, flags); - L = new Z808BitGeneralPurposeRegister(memory, flags); + H = registerFactory(); + L = registerFactory(); } protected override IZ808BitRegister HighRegister => H; diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs index 520ef53..b5b3220 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs @@ -1,4 +1,6 @@ -namespace Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Logging; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; public interface IZ80StackManager : IZ8016BitSpecialRegister { diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index ac11494..57cbf20 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -8,14 +8,13 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; public class Z80ProgramCounter : Z8016BitSpecialRegisterBase, IZ80ProgramCounter { private readonly IZ80StackManager _stack; - private readonly IZ80InstructionLogger _z80InstructionLogger; + private readonly IZ80InstructionLogger _instructionLogger; - public Z80ProgramCounter(IMasterSystemMemory memory, IZ80InstructionLogger z80InstructionLogger, - IZ80FlagsManager flags, IZ80StackManager stack) + public Z80ProgramCounter(IMasterSystemMemory memory, IZ80FlagsManager flags, IZ80StackManager stack, IZ80InstructionLogger instructionLogger) : base(memory, flags) { - _z80InstructionLogger = z80InstructionLogger; _stack = stack; + _instructionLogger = instructionLogger; } public byte GetNextInstruction() @@ -26,7 +25,7 @@ public byte GetNextInstruction() public byte GetNextDataByte() { var data = GetNextByteByProgramCounter(); - _z80InstructionLogger.AddOperationData(data); + _instructionLogger.AddOperationData(data); return data; } @@ -34,7 +33,7 @@ public ushort GetNextTwoDataBytes() { ushort data = GetNextByteByProgramCounter(); data += (ushort)(GetNextByteByProgramCounter() << 8); - _z80InstructionLogger.AddOperationData(data); + _instructionLogger.AddOperationData(data); return data; } diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs index a261040..8b13265 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs @@ -7,11 +7,11 @@ namespace Kmse.Core.Z80.Registers.SpecialPurpose; public class Z80StackManager : Z8016BitSpecialRegisterBase, IZ80StackManager { private const ushort DefaultStackAddress = 0xDFF0; - private readonly ICpuLogger _cpuLogger; + private ICpuLogger _cpuLogger; private readonly IMasterSystemMemory _memory; private int _maximumMemorySize; - public Z80StackManager(IMasterSystemMemory memory, ICpuLogger cpuLogger, IZ80FlagsManager flags) + public Z80StackManager(IMasterSystemMemory memory, IZ80FlagsManager flags, ICpuLogger cpuLogger) : base(memory, flags) { _memory = memory; diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 999176c..b21e7b9 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -15,58 +15,51 @@ public partial class Z80Cpu : IZ80Cpu { private readonly ICpuLogger _cpuLogger; private readonly IZ80InstructionLogger _instructionLogger; + private readonly IMasterSystemIoManager _io; + private readonly IMasterSystemMemory _memory; + private readonly IZ80ProgramCounter _pc; + private readonly IZ80StackManager _stack; + private readonly IZ80FlagsManager _flags; + private readonly IZ80Accumulator _accumulator; + private readonly IZ80AfRegister _af; + private readonly IZ80BcRegister _bc; + private readonly IZ80DeRegister _de; + private readonly IZ80HlRegister _hl; + private readonly IZ808BitGeneralPurposeRegister _b; + private readonly IZ808BitGeneralPurposeRegister _c; + private readonly IZ808BitGeneralPurposeRegister _d; + private readonly IZ808BitGeneralPurposeRegister _e; + private readonly IZ808BitGeneralPurposeRegister _h; + private readonly IZ808BitGeneralPurposeRegister _l; + private readonly IZ80IndexRegisterXy _ix; + private readonly IZ80IndexRegisterXy _iy; + private readonly IZ80MemoryRefreshRegister _rRegister; + private readonly IZ80InterruptPageAddressRegister _iRegister; + private readonly IZ80CpuInputOutput _ioManagement; + private readonly IZ80CpuMemoryManagement _memoryManagement; + private readonly IZ80InterruptManagement _interruptManagement; private int _currentCycleCount; - private IMasterSystemIoManager _io; - private IMasterSystemMemory _memory; private bool _halted; - private const int NopCycleCount = 4; - private IZ80ProgramCounter _pc; - private IZ80StackManager _stack; - private IZ80FlagsManager _flags; - private IZ80Accumulator _accumulator; - private IZ80AfRegister _af; - private IZ80BcRegister _bc; - private IZ80DeRegister _de; - private IZ80HlRegister _hl; - private IZ808BitGeneralPurposeRegister _b; - private IZ808BitGeneralPurposeRegister _c; - private IZ808BitGeneralPurposeRegister _d; - private IZ808BitGeneralPurposeRegister _e; - private IZ808BitGeneralPurposeRegister _h; - private IZ808BitGeneralPurposeRegister _l; - private IZ80IndexRegisterXy _ix; - private IZ80IndexRegisterXy _iy; - private IZ80MemoryRefreshRegister _rRegister; - private IZ80InterruptPageAddressRegister _iRegister; - private IZ80CpuInputOutput _ioManagement; - private IZ80CpuMemoryManagement _memoryManagement; - private IZ80InterruptManagement _interruptManagement; - - public Z80Cpu(ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger) + public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger, Z80CpuRegisters registers, Z80CpuManagement cpuManagement) { _cpuLogger = cpuLogger; _instructionLogger = instructionLogger; - PopulateInstructions(); - } - public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) - { _cpuLogger.Debug("Initializing CPU"); _memory = memory; _io = io; - // TODO: Need to create these indirectly to allow mock interfaces to be injected for testing - _af = new Z80AfRegister(memory); - _bc = new Z80BcRegister(memory, _af.Flags); - _de = new Z80DeRegister(memory, _af.Flags); - _hl = new Z80HlRegister(memory, _af.Flags); - _ix = new Z80IndexRegisterXy(memory, _af.Flags); - _iy = new Z80IndexRegisterXy(memory, _af.Flags); - _rRegister = new Z80MemoryRefreshRegister(memory, _af.Flags); - _iRegister = new Z80InterruptPageAddressRegister(memory, _af.Flags); + _af = registers.Af; + _bc = registers.Bc; + _de = registers.De; + _hl = registers.Hl; + _ix = registers.IX; + _iy = registers.IY; + _rRegister = registers.R; + _iRegister = registers.I; _accumulator = _af.Accumulator; _flags = _af.Flags; @@ -77,17 +70,14 @@ public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) _h = _hl.H; _l = _hl.L; - _stack = new Z80StackManager(memory, _cpuLogger, _af.Flags); - _pc = new Z80ProgramCounter(memory, _instructionLogger, _flags, _stack); + _stack = registers.Stack; + _pc = registers.Pc; - _ioManagement = new Z80CpuInputOutput(_io, _flags); - _memoryManagement = new Z80CpuMemoryManagement(_memory, _flags); - _interruptManagement = new Z80InterruptManagement(_pc, _cpuLogger); - } + _ioManagement = cpuManagement.IoManagement; + _memoryManagement = cpuManagement.MemoryManagement; + _interruptManagement = cpuManagement.InterruptManagement; - public IZ80InterruptManagement GetInterruptManagementInterface() - { - return _interruptManagement; + PopulateInstructions(); } public CpuStatus GetStatus() diff --git a/Kmse.Core/Z80/Z80CpuManagement.cs b/Kmse.Core/Z80/Z80CpuManagement.cs new file mode 100644 index 0000000..511b3b9 --- /dev/null +++ b/Kmse.Core/Z80/Z80CpuManagement.cs @@ -0,0 +1,12 @@ +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Memory; + +namespace Kmse.Core.Z80; + +public class Z80CpuManagement +{ + public IZ80CpuInputOutput IoManagement { get; set; } + public IZ80CpuMemoryManagement MemoryManagement { get; set; } + public IZ80InterruptManagement InterruptManagement { get; set; } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuModule.cs b/Kmse.Core/Z80/Z80CpuModule.cs new file mode 100644 index 0000000..77edc36 --- /dev/null +++ b/Kmse.Core/Z80/Z80CpuModule.cs @@ -0,0 +1,44 @@ +using Autofac; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80; + +public class Z80CpuModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().AsSelf().PropertiesAutowired(); + builder.RegisterType().AsSelf().PropertiesAutowired(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerDependency(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerDependency(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuRegisters.cs b/Kmse.Core/Z80/Z80CpuRegisters.cs new file mode 100644 index 0000000..eeb3633 --- /dev/null +++ b/Kmse.Core/Z80/Z80CpuRegisters.cs @@ -0,0 +1,18 @@ +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80; + +public class Z80CpuRegisters +{ + public IZ80ProgramCounter Pc { get; set; } + public IZ80StackManager Stack { get; set; } + public IZ80AfRegister Af { get; set; } + public IZ80BcRegister Bc { get; set; } + public IZ80DeRegister De { get; set; } + public IZ80HlRegister Hl { get; set; } + public IZ80IndexRegisterXy IX { get; set; } + public IZ80IndexRegisterXy IY { get; set; } + public IZ80MemoryRefreshRegister R { get; set; } + public IZ80InterruptPageAddressRegister I { get; set; } +} \ No newline at end of file From 114a0edb88ec02e49a1811a16435a1db397204e1 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Wed, 21 Sep 2022 22:26:46 +1000 Subject: [PATCH 12/21] Refactor CPU cycle counting out of Z80Cpu class into own class --- .../Kmse.Core.UnitTests.csproj | 4 +- .../Z80CpuTests/CpuTestFixtureBase.cs | 5 +- .../Data/TestInstructions.json | 0 .../TestInstructionsFixture.cs | 9 +- .../TestIo.cs | 2 +- .../TestMemory.cs | 2 +- .../Z80CpuCycleCounterFixture.cs | 42 +++++++++ .../Z80/Instructions/IZ80CpuCycleCounter.cs | 11 +++ .../Z80/Instructions/Z80CpuCycleCounter.cs | 20 ++++ Kmse.Core/Z80/Z80Cpu.Instructions.cs | 92 +++++++++---------- Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs | 10 +- Kmse.Core/Z80/Z80Cpu.cs | 24 +++-- Kmse.Core/Z80/Z80CpuManagement.cs | 4 +- Kmse.Core/Z80/Z80CpuModule.cs | 2 + 14 files changed, 157 insertions(+), 70 deletions(-) rename Kmse.Core.UnitTests/Z80CpuTests/{InstructionTests => InstructionHashTests}/Data/TestInstructions.json (100%) rename Kmse.Core.UnitTests/Z80CpuTests/{InstructionTests => InstructionHashTests}/TestInstructionsFixture.cs (97%) rename Kmse.Core.UnitTests/Z80CpuTests/{InstructionTests => InstructionHashTests}/TestIo.cs (95%) rename Kmse.Core.UnitTests/Z80CpuTests/{InstructionTests => InstructionHashTests}/TestMemory.cs (95%) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Z80CpuCycleCounterFixture.cs create mode 100644 Kmse.Core/Z80/Instructions/IZ80CpuCycleCounter.cs create mode 100644 Kmse.Core/Z80/Instructions/Z80CpuCycleCounter.cs diff --git a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj index 5319e64..49ec504 100644 --- a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj +++ b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj @@ -8,11 +8,11 @@ - + - + diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs index e6a0fb3..9b2e303 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -1,6 +1,7 @@ using Kmse.Core.IO; using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Instructions; using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; @@ -55,6 +56,7 @@ public void Setup() var ioManagement = new Z80CpuInputOutput(_io, af.Flags); var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); _interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); + var cycleCounter = new Z80CpuCycleCounter(); var registers = new Z80CpuRegisters { @@ -73,7 +75,8 @@ public void Setup() { IoManagement = ioManagement, InterruptManagement = _interruptManagement, - MemoryManagement = memoryManagement + MemoryManagement = memoryManagement, + CycleCounter = cycleCounter }; _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json similarity index 100% rename from Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Data/TestInstructions.json rename to Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs similarity index 97% rename from Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs rename to Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs index 4bfcd5b..6458c95 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs @@ -6,6 +6,7 @@ using System.Text.Json; using FluentAssertions; using Kmse.Core.Z80; +using Kmse.Core.Z80.Instructions; using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; @@ -17,7 +18,7 @@ using NSubstitute; using NUnit.Framework; -namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests; /// /// Test all the instructions by executing each possible instruction and validating that a hash of the current CPU, IO and Memory state matches expected @@ -62,6 +63,7 @@ public void Setup() var ioManagement = new Z80CpuInputOutput(_io, af.Flags); var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); var interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); + var cycleCounter = new Z80CpuCycleCounter(); var registers = new Z80CpuRegisters { @@ -80,7 +82,8 @@ public void Setup() { IoManagement = ioManagement, InterruptManagement = interruptManagement, - MemoryManagement = memoryManagement + MemoryManagement = memoryManagement, + CycleCounter = cycleCounter }; _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); @@ -112,7 +115,7 @@ private class InstructionTestCases : IEnumerable { public IEnumerator GetEnumerator() { - var testInstructions = DeserializeTestFile>("Kmse.Core.UnitTests.Z80CpuTests.InstructionTests.Data.TestInstructions.json"); + var testInstructions = DeserializeTestFile>("Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests.Data.TestInstructions.json"); foreach (var instruction in testInstructions) { var instructions = instruction.HexInstructions.Split(',').Select(x => byte.Parse(x, NumberStyles.AllowHexSpecifier)).ToArray(); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestIo.cs similarity index 95% rename from Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs rename to Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestIo.cs index bfc726f..9dde75d 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestIo.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestIo.cs @@ -1,7 +1,7 @@ using System.Security.Cryptography; using Kmse.Core.IO; -namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests; public class TestIo : IMasterSystemIoManager { diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestMemory.cs similarity index 95% rename from Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs rename to Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestMemory.cs index 0b4e4ba..4b7f81e 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/TestMemory.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestMemory.cs @@ -2,7 +2,7 @@ using Kmse.Core.Cartridge; using Kmse.Core.Memory; -namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests; public class TestMemory : IMasterSystemMemory { diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Z80CpuCycleCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Z80CpuCycleCounterFixture.cs new file mode 100644 index 0000000..c421ec0 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionTests/Z80CpuCycleCounterFixture.cs @@ -0,0 +1,42 @@ +using FluentAssertions; +using Kmse.Core.Z80.Instructions; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionTests; + +public class Z80CpuCycleCounterFixture +{ + private Z80CpuCycleCounter _cycleCounter; + + [SetUp] + public void Setup() + { + _cycleCounter = new Z80CpuCycleCounter(); + } + + [Test] + public void WhenIncrementingValue() + { + _cycleCounter.Reset(); + _cycleCounter.Increment(1); + _cycleCounter.Increment(2); + _cycleCounter.Increment(3); + _cycleCounter.CurrentCycleCount.Should().Be(6); + } + + [Test] + public void WhenIncrementingValueByNegativeNumber() + { + _cycleCounter.Reset(); + var action = () => _cycleCounter.Increment(-1); + action.Should().Throw(); + } + + [Test] + public void WhenResetThenValueIsZero() + { + _cycleCounter.Increment(123); + _cycleCounter.Reset(); + _cycleCounter.CurrentCycleCount.Should().Be(0); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Instructions/IZ80CpuCycleCounter.cs b/Kmse.Core/Z80/Instructions/IZ80CpuCycleCounter.cs new file mode 100644 index 0000000..ae8d9e0 --- /dev/null +++ b/Kmse.Core/Z80/Instructions/IZ80CpuCycleCounter.cs @@ -0,0 +1,11 @@ +using System; + +namespace Kmse.Core.Z80.Instructions; + +public interface IZ80CpuCycleCounter +{ + int CurrentCycleCount { get; } + void Reset(); + void Increment(int value); + +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Instructions/Z80CpuCycleCounter.cs b/Kmse.Core/Z80/Instructions/Z80CpuCycleCounter.cs new file mode 100644 index 0000000..6b3f41b --- /dev/null +++ b/Kmse.Core/Z80/Instructions/Z80CpuCycleCounter.cs @@ -0,0 +1,20 @@ +namespace Kmse.Core.Z80.Instructions; + +public class Z80CpuCycleCounter : IZ80CpuCycleCounter +{ + public int CurrentCycleCount { get; private set; } + + public void Reset() + { + CurrentCycleCount = 0; + } + + public void Increment(int value) + { + if (value < 0) + { + throw new InvalidOperationException("Cannot increment cycle count by negative value"); + } + CurrentCycleCount += value; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs index e5209fe..d361725 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Z80Cpu.Instructions.cs @@ -135,10 +135,10 @@ private void PopulateJumpCallAndReturnOperations() AddStandardInstruction(0xE2, 10, "JP PO,$NN", "Conditional Jump If Parity Odd", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0x18, 12, "JR $N+2", "Relative Jump By Offset", _ => { _pc.JumpByOffset(_pc.GetNextDataByte()); }); - AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7; }); - AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7; }); - AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7; }); - AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { _currentCycleCount += _pc.JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7; }); + AddStandardInstruction(0x38, DynamicCycleHandling, "JR C,$N+2", "Cond. Relative Jump", _ => { _cycleCounter.Increment(_pc.JumpByOffsetIfFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7); }); + AddStandardInstruction(0x30, DynamicCycleHandling, "JR NC,$N+2", "Cond. Relative Jump", _ => { _cycleCounter.Increment(_pc.JumpByOffsetIfNotFlag(Z80StatusFlags.CarryC, _pc.GetNextDataByte()) ? 12 : 7); }); + AddStandardInstruction(0x28, DynamicCycleHandling, "JR Z,$N+2", "Cond. Relative Jump", _ => { _cycleCounter.Increment(_pc.JumpByOffsetIfFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7); }); + AddStandardInstruction(0x20, DynamicCycleHandling, "JR NZ,$N+2", "Cond. Relative Jump", _ => { _cycleCounter.Increment(_pc.JumpByOffsetIfNotFlag(Z80StatusFlags.ZeroZ, _pc.GetNextDataByte()) ? 12 : 7); }); AddStandardInstruction(0x10, DynamicCycleHandling, "DJNZ $+2", "Decrement, Jump if Non-Zero", _ => { @@ -147,32 +147,32 @@ private void PopulateJumpCallAndReturnOperations() if (_b.Value != 0) { _pc.JumpByOffset(offset); - _currentCycleCount += 13; + _cycleCounter.Increment(13); } // Not jumping, continue to next instruction - _currentCycleCount += 8; + _cycleCounter.Increment(8); }); AddStandardInstruction(0xCD, 17, "CALL NN", "Unconditional Call", _ => { _pc.SetAndSaveExisting(_pc.GetNextTwoDataBytes()); }); - AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { _currentCycleCount += _pc.CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); - AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { _currentCycleCount += _pc.CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10; }); + AddStandardInstruction(0xDC, DynamicCycleHandling, "CALL C,NN", "Conditional Call If Carry Set", _ => { _cycleCounter.Increment(_pc.CallIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xD4, DynamicCycleHandling, "CALL NC,NN", "Conditional Call If Carry Not Set", _ => { _cycleCounter.Increment(_pc.CallIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xFC, DynamicCycleHandling, "CALL M,NN", "Conditional Call If Negative", _ => { _cycleCounter.Increment(_pc.CallIfFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xF4, DynamicCycleHandling, "CALL P,NN", "Conditional Call If Negative", _ => { _cycleCounter.Increment(_pc.CallIfNotFlagCondition(Z80StatusFlags.SignS, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xCC, DynamicCycleHandling, "CALL Z,NN", "Conditional Call If Zero", _ => { _cycleCounter.Increment(_pc.CallIfFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xC4, DynamicCycleHandling, "CALL NZ,NN", "Conditional Call If Not Zero", _ => { _cycleCounter.Increment(_pc.CallIfNotFlagCondition(Z80StatusFlags.ZeroZ, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xEC, DynamicCycleHandling, "CALL PE,NN", "Conditional Call If Parity Even", _ => { _cycleCounter.Increment(_pc.CallIfFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); + AddStandardInstruction(0xE4, DynamicCycleHandling, "CALL PO,NN", "Conditional Call If Parity Odd", _ => { _cycleCounter.Increment(_pc.CallIfNotFlagCondition(Z80StatusFlags.ParityOverflowPV, _pc.GetNextTwoDataBytes()) ? 17 : 10); }); AddStandardInstruction(0xC9, 10, "RET", "Return", _ => { _pc.SetFromStack(); }); - AddStandardInstruction(0xD8, DynamicCycleHandling, "RET C", "Conditional Return If Carry Set", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.CarryC) ? 11 : 5; }); - AddStandardInstruction(0xD0, DynamicCycleHandling, "RET NC", "Conditional Return If Carry Not Set", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.CarryC) ? 11 : 5; }); - AddStandardInstruction(0xF8, DynamicCycleHandling, "RET M", "Conditional Return If Negative", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.SignS) ? 11 : 5; }); - AddStandardInstruction(0xF0, DynamicCycleHandling, "RET P", "Conditional Return If Positive", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.SignS) ? 11 : 5; }); - AddStandardInstruction(0xC8, DynamicCycleHandling, "RET Z", "Conditional Return If Zero", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.ZeroZ) ? 11 : 5; }); - AddStandardInstruction(0xC0, DynamicCycleHandling, "RET NZ", "Conditional Return If Not Zero", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.ZeroZ) ? 11 : 5; }); - AddStandardInstruction(0xE8, DynamicCycleHandling, "RET PE", "Conditional Return If Parity Even", _ => { _currentCycleCount += _pc.ReturnIfFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5; }); - AddStandardInstruction(0xE0, DynamicCycleHandling, "RET PO", "Conditional Return If Parity Odd", _ => { _currentCycleCount += _pc.ReturnIfNotFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5; }); + AddStandardInstruction(0xD8, DynamicCycleHandling, "RET C", "Conditional Return If Carry Set", _ => { _cycleCounter.Increment(_pc.ReturnIfFlag(Z80StatusFlags.CarryC) ? 11 : 5); }); + AddStandardInstruction(0xD0, DynamicCycleHandling, "RET NC", "Conditional Return If Carry Not Set", _ => { _cycleCounter.Increment(_pc.ReturnIfNotFlag(Z80StatusFlags.CarryC) ? 11 : 5); }); + AddStandardInstruction(0xF8, DynamicCycleHandling, "RET M", "Conditional Return If Negative", _ => { _cycleCounter.Increment(_pc.ReturnIfFlag(Z80StatusFlags.SignS) ? 11 : 5); }); + AddStandardInstruction(0xF0, DynamicCycleHandling, "RET P", "Conditional Return If Positive", _ => { _cycleCounter.Increment(_pc.ReturnIfNotFlag(Z80StatusFlags.SignS) ? 11 : 5); }); + AddStandardInstruction(0xC8, DynamicCycleHandling, "RET Z", "Conditional Return If Zero", _ => { _cycleCounter.Increment(_pc.ReturnIfFlag(Z80StatusFlags.ZeroZ) ? 11 : 5); }); + AddStandardInstruction(0xC0, DynamicCycleHandling, "RET NZ", "Conditional Return If Not Zero", _ => { _cycleCounter.Increment(_pc.ReturnIfNotFlag(Z80StatusFlags.ZeroZ) ? 11 : 5); }); + AddStandardInstruction(0xE8, DynamicCycleHandling, "RET PE", "Conditional Return If Parity Even", _ => { _cycleCounter.Increment(_pc.ReturnIfFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5); }); + AddStandardInstruction(0xE0, DynamicCycleHandling, "RET PO", "Conditional Return If Parity Odd", _ => { _cycleCounter.Increment(_pc.ReturnIfNotFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5); }); AddStandardInstruction(0xC7, 11, "RST 0", "Restart at 0h", _ => { _pc.SetAndSaveExisting(0x00); }); AddStandardInstruction(0xCF, 11, "RST 08H", "Restart at 08h", _ => { _pc.SetAndSaveExisting(0x08); }); @@ -286,7 +286,7 @@ private void PopulateArthmeticAndLogicalInstructions() // // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this // //_bc.Word = 64 * 1024; - // _currentCycleCount += 16; + // _cycleCounter.Increment(16; // return; //} @@ -298,11 +298,11 @@ private void PopulateArthmeticAndLogicalInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -314,7 +314,7 @@ private void PopulateArthmeticAndLogicalInstructions() // // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this // //_bc.Word = 64 * 1024; - // _currentCycleCount += 16; + // _cycleCounter.Increment(16; // return; //} @@ -326,11 +326,11 @@ private void PopulateArthmeticAndLogicalInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -834,7 +834,7 @@ private void PopulateLoadAndExchangeInstructions() // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this //_bc.Word = 64 * 1024; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -852,11 +852,11 @@ private void PopulateLoadAndExchangeInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); AddDoubleByteInstruction(0xED, 0xA8, 16, "LDD", "Load and Decrement", _ => @@ -876,7 +876,7 @@ private void PopulateLoadAndExchangeInstructions() // BC was set to 0 before instruction was executed, so set to 64kb accordingly to documentation but no emulator does this //_bc.Word = 64 * 1024; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -895,11 +895,11 @@ private void PopulateLoadAndExchangeInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -957,7 +957,7 @@ private void PopulateInputOutputInstructions() // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -975,11 +975,11 @@ private void PopulateInputOutputInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -1000,7 +1000,7 @@ private void PopulateInputOutputInstructions() // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -1018,11 +1018,11 @@ private void PopulateInputOutputInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -1054,7 +1054,7 @@ private void PopulateInputOutputInstructions() // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -1073,11 +1073,11 @@ private void PopulateInputOutputInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); @@ -1099,7 +1099,7 @@ private void PopulateInputOutputInstructions() // B was set to 0 before instruction was executed, so set to 256 bytes accordingly to documentation but no emulator does this //_bc.Word = 256; - _currentCycleCount += 16; + _cycleCounter.Increment(16); return; } @@ -1118,11 +1118,11 @@ private void PopulateInputOutputInstructions() // Note that this is not a loop here since we still need to process interrupts // hence running instruction again rather than doing a loop here _pc.MoveProgramCounterBackward(2); - _currentCycleCount += 21; + _cycleCounter.Increment(21); } else { - _currentCycleCount += 16; + _cycleCounter.Increment(16); } }); } diff --git a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs index eda9152..4005c7b 100644 --- a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs +++ b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs @@ -25,7 +25,7 @@ private void LoadRR(byte opCode) if (sourceRegisterId == 0x06) { Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); - _currentCycleCount += 3; + _cycleCounter.Increment(3); return; } @@ -33,7 +33,7 @@ private void LoadRR(byte opCode) { var register = Get8BitRegisterByRIdentifier(sourceRegisterId); _memoryManagement.WriteToMemory(_hl, register.Value); - _currentCycleCount += 3; + _cycleCounter.Increment(3); return; } @@ -72,7 +72,7 @@ private void ResetBitByOpCode(byte opCode) { _hl.ResetBitByRegisterLocation(bit, 0); // Accessing (HL) increases cycle count - _currentCycleCount += 7; + _cycleCounter.Increment(7); return; } @@ -93,7 +93,7 @@ private void SetBitByOpCode(byte opCode) { _hl.SetBitByRegisterLocation(bit, 0); // Accessing (HL) increases cycle count - _currentCycleCount += 7; + _cycleCounter.Increment(7); return; } @@ -114,7 +114,7 @@ private void TestBitByOpCode(byte opCode) { _hl.TestBitByRegisterLocation(bit, 0); // Testing bit via (HL) memory location increases cycle count - _currentCycleCount += 4; + _cycleCounter.Increment(4); return; } diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index b21e7b9..87dec85 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,5 +1,6 @@ using Kmse.Core.IO; using Kmse.Core.Memory; +using Kmse.Core.Z80.Instructions; using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; @@ -38,8 +39,8 @@ public partial class Z80Cpu : IZ80Cpu private readonly IZ80CpuInputOutput _ioManagement; private readonly IZ80CpuMemoryManagement _memoryManagement; private readonly IZ80InterruptManagement _interruptManagement; + private readonly IZ80CpuCycleCounter _cycleCounter; - private int _currentCycleCount; private bool _halted; private const int NopCycleCount = 4; @@ -76,6 +77,7 @@ public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger _ioManagement = cpuManagement.IoManagement; _memoryManagement = cpuManagement.MemoryManagement; _interruptManagement = cpuManagement.InterruptManagement; + _cycleCounter = cpuManagement.CycleCounter; PopulateInstructions(); } @@ -84,7 +86,7 @@ public CpuStatus GetStatus() { return new CpuStatus { - CurrentCycleCount = _currentCycleCount, + CurrentCycleCount = _cycleCounter.CurrentCycleCount, Halted = _halted, Af = _af.AsRegister(), @@ -114,7 +116,8 @@ public CpuStatus GetStatus() public void Reset() { _cpuLogger.Debug("Resetting CPU"); - _currentCycleCount = 0; + + _cycleCounter.Reset(); _pc.Reset(); _stack.Reset(); @@ -136,15 +139,16 @@ public void Reset() public int ExecuteNextCycle() { - _currentCycleCount = 0; + _cycleCounter.Reset(); _instructionLogger.StartNewInstruction(_pc.Value); if (_interruptManagement.InterruptWaiting()) { - _currentCycleCount += _interruptManagement.ProcessInterrupts(); + var cycles = _interruptManagement.ProcessInterrupts(); + _cycleCounter.Increment(cycles); // If halted and an interrupt occurs, then resume ResumeIfHalted(); - return _currentCycleCount; + return _cycleCounter.CurrentCycleCount; } if (_halted) @@ -170,22 +174,22 @@ public int ExecuteNextCycle() .SetOpCode(opCode.ToString("X2"), "Unimplemented Instruction", "Unimplemented Instruction") .Log(); - _currentCycleCount += NopCycleCount; - return _currentCycleCount; + _cycleCounter.Increment(NopCycleCount); + return _cycleCounter.CurrentCycleCount; } instruction.Execute(); // Note that -1 (DynamicCycleHandling) indicates the clock cycles change dynamically so handled inside the instruction handler if (instruction.ClockCycles > 0) { - _currentCycleCount += instruction.ClockCycles; + _cycleCounter.Increment(instruction.ClockCycles); } _instructionLogger .SetOpCode(instruction.GetOpCode(), instruction.Name, instruction.Description) .Log(); - return _currentCycleCount; + return _cycleCounter.CurrentCycleCount; } private void Halt() diff --git a/Kmse.Core/Z80/Z80CpuManagement.cs b/Kmse.Core/Z80/Z80CpuManagement.cs index 511b3b9..b3b4484 100644 --- a/Kmse.Core/Z80/Z80CpuManagement.cs +++ b/Kmse.Core/Z80/Z80CpuManagement.cs @@ -1,4 +1,5 @@ -using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.Instructions; +using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Memory; @@ -9,4 +10,5 @@ public class Z80CpuManagement public IZ80CpuInputOutput IoManagement { get; set; } public IZ80CpuMemoryManagement MemoryManagement { get; set; } public IZ80InterruptManagement InterruptManagement { get; set; } + public IZ80CpuCycleCounter CycleCounter { get; set; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuModule.cs b/Kmse.Core/Z80/Z80CpuModule.cs index 77edc36..5368d4c 100644 --- a/Kmse.Core/Z80/Z80CpuModule.cs +++ b/Kmse.Core/Z80/Z80CpuModule.cs @@ -1,4 +1,5 @@ using Autofac; +using Kmse.Core.Z80.Instructions; using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; @@ -40,5 +41,6 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); } } \ No newline at end of file From 7356150807ed746c0cb8d67b36239729e9d01208 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Wed, 21 Sep 2022 23:04:00 +1000 Subject: [PATCH 13/21] Move running/halt state out of Z80CPU class into own class. Move instructions into own class out of Z80Cpu class. This is enabled by the splitting up of the registers and other classes to allow instructions to operate on registers/I/O/memory etc directly without having to be in the main CPU class. The Z80CPU class now is clean and simple and only handles core instruction execution. The instructions class is still very large but in a much better position to be refactored and split up in the future than previously --- Kmse.Console/EmulatorService.cs | 2 +- .../Z80CpuTests/CpuInstructionsFixture.cs | 2 +- .../Z80CpuTests/CpuTestFixtureBase.cs | 9 +- .../TestInstructionsFixture.cs | 10 +- Kmse.Core/IMasterSystemConsole.cs | 2 +- Kmse.Core/IO/Vdp/VdpPort.cs | 4 +- Kmse.Core/MasterSystemMk2.cs | 2 +- Kmse.Core/Z80/IO/Z80CpuInputOutput.cs | 2 +- Kmse.Core/Z80/IZ80Cpu.cs | 2 +- .../Z80/Instructions/IZ80CpuInstructions.cs | 10 + .../Z80CpuInstructions.cs} | 449 ++++++++++++++---- .../Z80/Memory/Z80CpuMemoryManagement.cs | 2 +- Kmse.Core/Z80/Model/CbInstructionModes.cs | 8 + Kmse.Core/Z80/{Support => Model}/CpuStatus.cs | 2 +- Kmse.Core/Z80/Model/Instruction.cs | 42 ++ Kmse.Core/Z80/Model/SpecialCbInstruction.cs | 22 + .../Z80/{Support => Model}/Z80Register.cs | 2 +- .../Z80/{Support => Model}/Z80StatusFlags.cs | 2 +- .../Z80/Registers/General/IZ80Accumulator.cs | 1 - .../Z80/Registers/General/IZ80AfRegister.cs | 2 +- .../Z80/Registers/General/IZ80FlagsManager.cs | 2 +- .../Z80/Registers/General/Z80Accumulator.cs | 2 +- .../Z80/Registers/General/Z80AfRegister.cs | 2 +- .../Z80/Registers/General/Z80FlagsManager.cs | 5 +- .../IZ8016BitGeneralPurposeRegister.cs | 2 +- Kmse.Core/Z80/Registers/IZ8016BitRegister.cs | 2 +- .../SpecialPurpose/IZ80ProgramCounter.cs | 2 +- .../SpecialPurpose/Z80IndexRegisterXy.cs | 2 +- .../SpecialPurpose/Z80ProgramCounter.cs | 2 +- .../Z8016BitGeneralPurposeRegisterBase.cs | 2 +- .../Z80/Registers/Z8016BitRegisterBase.cs | 2 +- .../Registers/Z8016BitSpecialRegisterBase.cs | 2 +- .../Z808BitGeneralPurposeRegister.cs | 2 +- Kmse.Core/Z80/Registers/Z80RegisterBase.cs | 2 +- .../Z80/Running/IZ80CpuRunningStateManager.cs | 10 + .../Z80/Running/Z80CpuRunningStateManager.cs | 36 ++ Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs | 166 ------- Kmse.Core/Z80/Z80Cpu.cs | 198 +------- Kmse.Core/Z80/Z80CpuManagement.cs | 2 + Kmse.Core/Z80/Z80CpuModule.cs | 3 + 40 files changed, 550 insertions(+), 473 deletions(-) create mode 100644 Kmse.Core/Z80/Instructions/IZ80CpuInstructions.cs rename Kmse.Core/Z80/{Z80Cpu.Instructions.cs => Instructions/Z80CpuInstructions.cs} (87%) create mode 100644 Kmse.Core/Z80/Model/CbInstructionModes.cs rename Kmse.Core/Z80/{Support => Model}/CpuStatus.cs (96%) create mode 100644 Kmse.Core/Z80/Model/Instruction.cs create mode 100644 Kmse.Core/Z80/Model/SpecialCbInstruction.cs rename Kmse.Core/Z80/{Support => Model}/Z80Register.cs (88%) rename Kmse.Core/Z80/{Support => Model}/Z80StatusFlags.cs (91%) create mode 100644 Kmse.Core/Z80/Running/IZ80CpuRunningStateManager.cs create mode 100644 Kmse.Core/Z80/Running/Z80CpuRunningStateManager.cs delete mode 100644 Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs diff --git a/Kmse.Console/EmulatorService.cs b/Kmse.Console/EmulatorService.cs index 33886c5..b52924e 100644 --- a/Kmse.Console/EmulatorService.cs +++ b/Kmse.Console/EmulatorService.cs @@ -4,7 +4,7 @@ using Kmse.Core; using Kmse.Core.IO.Controllers; using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; using Microsoft.Extensions.Hosting; using Serilog; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index f62cdcc..8c5d508 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; using NSubstitute; using NUnit.Framework; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs index 9b2e303..d1f2860 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -9,6 +9,7 @@ using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Running; using NSubstitute; using NSubstitute.ClearExtensions; using NUnit.Framework; @@ -57,6 +58,7 @@ public void Setup() var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); _interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); var cycleCounter = new Z80CpuCycleCounter(); + var runningStateManager = new Z80CpuRunningStateManager(_cpuLogger); var registers = new Z80CpuRegisters { @@ -76,9 +78,10 @@ public void Setup() IoManagement = ioManagement, InterruptManagement = _interruptManagement, MemoryManagement = memoryManagement, - CycleCounter = cycleCounter + CycleCounter = cycleCounter, + RunningStateManager = runningStateManager, }; - - _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); + var cpuInstructions = new Z80CpuInstructions(_memory, _io, _cpuLogger, registers, cpuManagement); + _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, cpuInstructions, registers, cpuManagement); } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs index 6458c95..65fec4c 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs @@ -11,10 +11,11 @@ using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Running; using NSubstitute; using NUnit.Framework; @@ -64,6 +65,7 @@ public void Setup() var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); var interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); var cycleCounter = new Z80CpuCycleCounter(); + var runningStateManager = new Z80CpuRunningStateManager(_cpuLogger); var registers = new Z80CpuRegisters { @@ -83,10 +85,12 @@ public void Setup() IoManagement = ioManagement, InterruptManagement = interruptManagement, MemoryManagement = memoryManagement, - CycleCounter = cycleCounter + CycleCounter = cycleCounter, + RunningStateManager = runningStateManager }; - _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, registers, cpuManagement); + var cpuInstructions = new Z80CpuInstructions(_memory, _io, _cpuLogger, registers, cpuManagement); + _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, cpuInstructions, registers, cpuManagement); } protected static T DeserializeTestFile(string filename) diff --git a/Kmse.Core/IMasterSystemConsole.cs b/Kmse.Core/IMasterSystemConsole.cs index 046162e..41e6a87 100644 --- a/Kmse.Core/IMasterSystemConsole.cs +++ b/Kmse.Core/IMasterSystemConsole.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core; diff --git a/Kmse.Core/IO/Vdp/VdpPort.cs b/Kmse.Core/IO/Vdp/VdpPort.cs index 3fb9807..cac9f12 100644 --- a/Kmse.Core/IO/Vdp/VdpPort.cs +++ b/Kmse.Core/IO/Vdp/VdpPort.cs @@ -1,6 +1,4 @@ -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.IO.Vdp; +namespace Kmse.Core.IO.Vdp; /// /// Emulate the video display processor diff --git a/Kmse.Core/MasterSystemMk2.cs b/Kmse.Core/MasterSystemMk2.cs index e77c7db..476c5ae 100644 --- a/Kmse.Core/MasterSystemMk2.cs +++ b/Kmse.Core/MasterSystemMk2.cs @@ -9,7 +9,7 @@ using Kmse.Core.Z80; using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.Logging; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core; diff --git a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs index a5fafde..39551b8 100644 --- a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs +++ b/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs @@ -1,8 +1,8 @@ using Kmse.Core.IO; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.IO; diff --git a/Kmse.Core/Z80/IZ80Cpu.cs b/Kmse.Core/Z80/IZ80Cpu.cs index 62a46a9..b870c98 100644 --- a/Kmse.Core/Z80/IZ80Cpu.cs +++ b/Kmse.Core/Z80/IZ80Cpu.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80; diff --git a/Kmse.Core/Z80/Instructions/IZ80CpuInstructions.cs b/Kmse.Core/Z80/Instructions/IZ80CpuInstructions.cs new file mode 100644 index 0000000..62e0606 --- /dev/null +++ b/Kmse.Core/Z80/Instructions/IZ80CpuInstructions.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Instructions +{ + public interface IZ80CpuInstructions + { + Instruction GetInstruction(byte opCode); + } +} diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs similarity index 87% rename from Kmse.Core/Z80/Z80Cpu.Instructions.cs rename to Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs index d361725..cafb08c 100644 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs @@ -1,13 +1,83 @@ -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80; - -/// -/// Generate the list of instructions and how to handle them -/// Created using this amazing page which summaries all the instructions - https://www.smspower.org/Development/InstructionSet -/// -public partial class Z80Cpu +using Kmse.Core.IO; +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Running; + +namespace Kmse.Core.Z80.Instructions; + +public class Z80CpuInstructions : IZ80CpuInstructions { + private readonly ICpuLogger _cpuLogger; + private readonly IMasterSystemIoManager _io; + private readonly IMasterSystemMemory _memory; + private readonly IZ80ProgramCounter _pc; + private readonly IZ80StackManager _stack; + private readonly IZ80FlagsManager _flags; + private readonly IZ80Accumulator _accumulator; + private readonly IZ80AfRegister _af; + private readonly IZ80BcRegister _bc; + private readonly IZ80DeRegister _de; + private readonly IZ80HlRegister _hl; + private readonly IZ808BitGeneralPurposeRegister _b; + private readonly IZ808BitGeneralPurposeRegister _c; + private readonly IZ808BitGeneralPurposeRegister _d; + private readonly IZ808BitGeneralPurposeRegister _e; + private readonly IZ808BitGeneralPurposeRegister _h; + private readonly IZ808BitGeneralPurposeRegister _l; + private readonly IZ80IndexRegisterXy _ix; + private readonly IZ80IndexRegisterXy _iy; + private readonly IZ80MemoryRefreshRegister _rRegister; + private readonly IZ80InterruptPageAddressRegister _iRegister; + private readonly IZ80CpuInputOutput _ioManagement; + private readonly IZ80CpuMemoryManagement _memoryManagement; + private readonly IZ80InterruptManagement _interruptManagement; + private readonly IZ80CpuCycleCounter _cycleCounter; + private readonly IZ80CpuRunningStateManager _runningStateManager; + + public Z80CpuInstructions(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger cpuLogger, Z80CpuRegisters registers, Z80CpuManagement cpuManagement) + { + _cpuLogger = cpuLogger; + _memory = memory; + _io = io; + + _af = registers.Af; + _bc = registers.Bc; + _de = registers.De; + _hl = registers.Hl; + _ix = registers.IX; + _iy = registers.IY; + _rRegister = registers.R; + _iRegister = registers.I; + + _accumulator = _af.Accumulator; + _flags = _af.Flags; + _b = _bc.B; + _c = _bc.C; + _d = _de.D; + _e = _de.E; + _h = _hl.H; + _l = _hl.L; + + _stack = registers.Stack; + _pc = registers.Pc; + + _ioManagement = cpuManagement.IoManagement; + _memoryManagement = cpuManagement.MemoryManagement; + _interruptManagement = cpuManagement.InterruptManagement; + _cycleCounter = cpuManagement.CycleCounter; + _runningStateManager = cpuManagement.RunningStateManager; + + PopulateInstructions(); + } + // TODO: In future, maybe we can combine these into a single dictionary and lookup but need to evaluate how different the handling is first private readonly Dictionary _genericInstructions = new(); private readonly Dictionary _cbInstructions = new(); @@ -18,11 +88,129 @@ public partial class Z80Cpu private readonly Dictionary _specialFdcbInstructions = new(); private const int DynamicCycleHandling = -1; + public Instruction GetInstruction(byte opCode) + { + var instruction = opCode switch + { + 0xCB => ProcessCbOpCode(CbInstructionModes.Normal), + 0xDD => ProcessDdOpCode(), + 0xFD => ProcessFdOpCode(), + 0xED => ProcessEdOpCode(), + _ => ProcessGenericNonPrefixedOpCode(opCode) + }; + + return instruction; + } + + private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) + { + if (!_genericInstructions.TryGetValue(opCode, out var instruction)) + { + _cpuLogger.Error($"Unhandled instruction - {opCode:X2}"); + } + + return instruction; + } + + private Instruction ProcessCbOpCode(CbInstructionModes mode) + { + // Two byte op code, so get next part of instruction and use that to lookup instruction + var opCode = _pc.GetNextInstruction(); + + // Normal CB instruction, just do a lookup for the instruction + if (mode == CbInstructionModes.Normal) + { + if (!_cbInstructions.TryGetValue(opCode, out var instruction)) + { + _cpuLogger.Error($"Unhandled 0xCB instruction - {opCode:X2}"); + } + + return instruction; + } + + // Special instruction which is actually 4 bytes and has started with a different prefix op code + // FD/DD CB XX OpCode + + // We need to read another byte which is the actual op code we use to lookup since third byte is data + var fourthOpCode = _pc.GetNextInstruction(); + SpecialCbInstruction specialCbInstruction; + bool foundInstruction; + switch (mode) + { + case CbInstructionModes.DD: foundInstruction = _specialDdcbInstructions.TryGetValue(fourthOpCode, out specialCbInstruction); break; + case CbInstructionModes.FD: foundInstruction = _specialFdcbInstructions.TryGetValue(fourthOpCode, out specialCbInstruction); break; + default: + { + _cpuLogger.Error($"Unhandled CB instruction mode 0x{mode} - Second Op Code {opCode:X2}, Third Op Code {fourthOpCode:X2}"); + throw new ArgumentOutOfRangeException(nameof(mode), mode, null); + } + } + + if (!foundInstruction) + { + _cpuLogger.Error($"Unhandled 0x{mode} 0xCB instruction - {opCode:X2}"); + return null; + } + + // Add data byte which is byte read after the 0xCB + specialCbInstruction.SetDataByte(opCode); + return specialCbInstruction; + } + + private Instruction ProcessDdOpCode() + { + // Two byte op code, so get next part of instruction and use that to lookup instruction + var secondOpCode = _pc.GetNextInstruction(); + if (secondOpCode == 0xCB) + { + // Not a normal instruction, but a special instruction + // ie. DD CB data opcode + return ProcessCbOpCode(CbInstructionModes.DD); + } + + if (!_ddInstructions.TryGetValue(secondOpCode, out var instruction)) + { + _cpuLogger.Error($"Unhandled 0xDD instruction - {secondOpCode:X2}"); + } + + return instruction; + } + + private Instruction ProcessFdOpCode() + { + // Two byte op code, so get next part of instruction and use that to lookup instruction + var secondOpCode = _pc.GetNextInstruction(); + if (secondOpCode == 0xCB) + { + // Not a normal instruction, but a special instruction + // ie. FD CB data opcode + return ProcessCbOpCode(CbInstructionModes.FD); + } + if (!_fdInstructions.TryGetValue(secondOpCode, out var instruction)) + { + _cpuLogger.Error($"Unhandled 0xFD instruction - {secondOpCode:X2}"); + } + + return instruction; + } + + private Instruction ProcessEdOpCode() + { + // Two byte op code, so get next part of instruction and use that to lookup instruction + var secondOpCode = _pc.GetNextInstruction(); + if (!_edInstructions.TryGetValue(secondOpCode, out var instruction)) + { + _cpuLogger.Error($"Unhandled 0xED instruction - {secondOpCode:X2}"); + } + + return instruction; + } + private void AddStandardInstructionWithMask(byte opCode, byte mask, int cycles, string name, string description, Action handleFunc) { // These op codes do the same thing but generally have some information in the op code itself, but we can handle them with the same function // An example being ADD A, r where 80 is base r is low 3 bits (mask is 7) so op codes are 0x80 - 0x87 which all do the same add, just different registers - + // NOTE: We don't use i <= opCode+mask since the increment is before the check and this will wrap around (since byte type) and loop forever for (var i = opCode; i < opCode + mask; i++) { @@ -40,7 +228,7 @@ private void AddDoubleByteInstructionWithMask(byte prefix, byte opCode, byte mas { AddDoubleByteInstruction(prefix, i, cycles, name, description, handleFunc); } - AddDoubleByteInstruction(prefix, (byte)(opCode+mask), cycles, name, description, handleFunc); + AddDoubleByteInstruction(prefix, (byte)(opCode + mask), cycles, name, description, handleFunc); } private void AddStandardInstruction(byte opCode, int cycles, string name, string description, Action handleFunc) @@ -82,18 +270,6 @@ private void AddSpecialCbInstruction(byte prefix, byte opCode, int cycles, strin } } - private void AddSpecialCbInstructionWithMask(byte prefix, byte opCode, byte mask, int cycles, string name, string description, Action handleFunc) - { - // These op codes do the same thing but generally have some information in the op code itself, but we can handle them with the same function - // An example being ADD A, r where 80 is base r is low 3 bits (mask is 7) so op codes are 0x80 - 0x87 which all do the same add, just different registers - // NOTE: We don't use i <= opCode+mask since the increment is before the check and this will wrap around (since byte type) and loop forever - for (var i = opCode; i < opCode + mask; i++) - { - AddSpecialCbInstruction(prefix, i, cycles, name, description, handleFunc); - } - AddSpecialCbInstruction(prefix, (byte)(opCode + mask), cycles, name, description, handleFunc); - } - private void PopulateInstructions() { PopulateCpuControlOperations(); @@ -109,7 +285,7 @@ private void PopulateInstructions() private void PopulateCpuControlOperations() { AddStandardInstruction(0x00, 4, "NOP", "No Operation", (_) => { }); - AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { Halt(); }); + AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { _runningStateManager.Halt(); }); AddStandardInstruction(0xF3, 4, "DI", "Disable Interrupts", (_) => { _interruptManagement.DisableMaskableInterrupts(); }); AddStandardInstruction(0xFB, 4, "EI", "Enable Interrupts", (_) => { _interruptManagement.EnableMaskableInterrupts(); }); @@ -123,7 +299,7 @@ private void PopulateJumpCallAndReturnOperations() AddStandardInstruction(0xE9, 4, "JP (HL)", "Unconditional Jump", _ => { _pc.Set(_hl); }); AddDoubleByteInstruction(0xDD, 0xE9, 8, "JP (IX)", "Unconditional Jump", _ => { _pc.Set(_ix); }); AddDoubleByteInstruction(0xFD, 0xE9, 8, "JP (IY)", "Unconditional Jump", _ => { _pc.Set(_iy); }); - AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { _pc.Set(_pc.GetNextTwoDataBytes()); }); + AddStandardInstruction(0xC3, 10, "JP $NN", "Unconditional Jump", _ => { _pc.Set(_pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0xDA, 10, "JP C,$NN", "Conditional Jump If Carry Set", _ => { _pc.Jump16BitIfFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); AddStandardInstruction(0xD2, 10, "JP NC,$NN", "Conditional Jump If Carry Not Set", _ => { _pc.Jump16BitIfNotFlagCondition(Z80StatusFlags.CarryC, _pc.GetNextTwoDataBytes()); }); @@ -175,14 +351,14 @@ private void PopulateJumpCallAndReturnOperations() AddStandardInstruction(0xE0, DynamicCycleHandling, "RET PO", "Conditional Return If Parity Odd", _ => { _cycleCounter.Increment(_pc.ReturnIfNotFlag(Z80StatusFlags.ParityOverflowPV) ? 11 : 5); }); AddStandardInstruction(0xC7, 11, "RST 0", "Restart at 0h", _ => { _pc.SetAndSaveExisting(0x00); }); - AddStandardInstruction(0xCF, 11, "RST 08H", "Restart at 08h", _ => { _pc.SetAndSaveExisting(0x08); }); - AddStandardInstruction(0xD7, 11, "RST 10H", "Restart at 10h", _ => { _pc.SetAndSaveExisting(0x10); }); - AddStandardInstruction(0xDF, 11, "RST 18H", "Restart at 18h", _ => { _pc.SetAndSaveExisting(0x18); }); - AddStandardInstruction(0xE7, 11, "RST 20H", "Restart at 20h", _ => { _pc.SetAndSaveExisting(0x20); }); - AddStandardInstruction(0xEF, 11, "RST 28H", "Restart at 28h", _ => { _pc.SetAndSaveExisting(0x28); }); - AddStandardInstruction(0xF7, 11, "RST 30H", "Restart at 30h", _ => { _pc.SetAndSaveExisting(0x30); }); + AddStandardInstruction(0xCF, 11, "RST 08H", "Restart at 08h", _ => { _pc.SetAndSaveExisting(0x08); }); + AddStandardInstruction(0xD7, 11, "RST 10H", "Restart at 10h", _ => { _pc.SetAndSaveExisting(0x10); }); + AddStandardInstruction(0xDF, 11, "RST 18H", "Restart at 18h", _ => { _pc.SetAndSaveExisting(0x18); }); + AddStandardInstruction(0xE7, 11, "RST 20H", "Restart at 20h", _ => { _pc.SetAndSaveExisting(0x20); }); + AddStandardInstruction(0xEF, 11, "RST 28H", "Restart at 28h", _ => { _pc.SetAndSaveExisting(0x28); }); + AddStandardInstruction(0xF7, 11, "RST 30H", "Restart at 30h", _ => { _pc.SetAndSaveExisting(0x30); }); AddStandardInstruction(0xFF, 11, "RST 38H", "Restart at 38h", _ => { _pc.SetAndSaveExisting(0x38); }); - + AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { _pc.SetFromStack(); _interruptManagement.ClearMaskableInterrupt(); }); AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { _pc.SetFromStack(); _interruptManagement.ResetInterruptEnableFlipFlopFromTemporaryStorage(); }); } @@ -196,7 +372,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x83, 4, "ADD A, E", "Add E to A", _ => { _accumulator.Add(_e.Value); }); AddStandardInstruction(0x84, 4, "ADD A, H", "Add H to A", _ => { _accumulator.Add(_h.Value); }); AddStandardInstruction(0x85, 4, "ADD A, L", "Add L to A", _ => { _accumulator.Add(_l.Value); }); - AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { _accumulator.Add(_pc.GetNextDataByte());}); + AddStandardInstruction(0xC6, 7, "ADD A, N", "Add", _ => { _accumulator.Add(_pc.GetNextDataByte()); }); AddStandardInstruction(0x09, 11, "ADD HL,BC", "Add BC to HL", _ => { _hl.Add(_bc); }); AddStandardInstruction(0x19, 11, "ADD HL,DE", "Add DE to HL", _ => { _hl.Add(_de); }); @@ -228,7 +404,7 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x8E, 4, "ADC (HL)", "Add (HL) to A with Carry", _ => { _accumulator.AddFromMemory(_hl, 0, true); }); AddDoubleByteInstruction(0xDD, 0x8E, 19, "ADC A,(IX+d)", "Add (IX+d) to A with Carry", _ => { _accumulator.AddFromMemory(_ix, _pc.GetNextDataByte(), true); }); AddDoubleByteInstruction(0xFD, 0x8E, 19, "ADC A,(IY+d)", "Add (IX+d) to A with Carry", _ => { _accumulator.AddFromMemory(_iy, _pc.GetNextDataByte(), true); }); - + AddDoubleByteInstruction(0xED, 0x4A, 15, "ADC HL,BC", "Add BC to HL with Carry", _ => { _hl.Add(_bc, true); }); AddDoubleByteInstruction(0xED, 0x5A, 15, "ADC HL,DE", "Add DE to HL with Carry", _ => { _hl.Add(_de, true); }); AddDoubleByteInstruction(0xED, 0x6A, 15, "ADC HL,HL", "Add HL to HL with Carry", _ => { _hl.Add(_hl, true); }); @@ -414,8 +590,8 @@ private void PopulateArthmeticAndLogicalInstructions() AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => { - _flags.ClearFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); _flags.SetFlag(Z80StatusFlags.CarryC); }); AddStandardInstruction(0x3F, 4, "CCF", "Complement Carry Flag", _ => @@ -424,7 +600,7 @@ private void PopulateArthmeticAndLogicalInstructions() _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, _flags.IsFlagSet(Z80StatusFlags.CarryC)); _flags.InvertFlag(Z80StatusFlags.CarryC); }); - AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { _accumulator.DecimalAdjustAccumulator(); }); + AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { _accumulator.DecimalAdjustAccumulator(); }); AddStandardInstruction(0X2F, 4, "CPL", "Complement", _ => { _accumulator.InvertAccumulatorRegister(); }); AddDoubleByteInstruction(0xED, 0x44, 8, "NEG", "Negate", _ => { _accumulator.NegateAccumulatorRegister(); }); @@ -504,7 +680,7 @@ private void PopulateBitSetResetAndTestGroupInstructions() AddSpecialCbInstruction(0xDD, 0x6E, 23, "BIT 5,(IX+d)", "Test bit 5 in (IX+d)", i => { _ix.TestBitByRegisterLocation(5, ((SpecialCbInstruction)i).DataByte); }); AddSpecialCbInstruction(0xDD, 0x76, 23, "BIT 6,(IX+d)", "Test bit 6 in (IX+d)", i => { _ix.TestBitByRegisterLocation(6, ((SpecialCbInstruction)i).DataByte); }); AddSpecialCbInstruction(0xDD, 0x7E, 23, "BIT 7,(IX+d)", "Test bit 7 in (IX+d)", i => { _ix.TestBitByRegisterLocation(7, ((SpecialCbInstruction)i).DataByte); }); - + AddSpecialCbInstruction(0xFD, 0x46, 23, "BIT 0,(IY+d)", "Test bit 0 in (IY+d)", i => { _iy.TestBitByRegisterLocation(0, ((SpecialCbInstruction)i).DataByte); }); AddSpecialCbInstruction(0xFD, 0x4E, 23, "BIT 1,(IY+d)", "Test bit 1 in (IY+d)", i => { _iy.TestBitByRegisterLocation(1, ((SpecialCbInstruction)i).DataByte); }); AddSpecialCbInstruction(0xFD, 0x56, 23, "BIT 2,(IY+d)", "Test bit 2 in (IY+d)", i => { _iy.TestBitByRegisterLocation(2, ((SpecialCbInstruction)i).DataByte); }); @@ -537,7 +713,7 @@ private void PopulateBitSetResetAndTestGroupInstructions() private void PopulateRotateAndShiftInstructions() { - AddStandardInstruction(0x17, 4, "RLA", "Rotate Left Accumulator", _ => { _accumulator.RotateLeftAccumulator();}); + AddStandardInstruction(0x17, 4, "RLA", "Rotate Left Accumulator", _ => { _accumulator.RotateLeftAccumulator(); }); AddStandardInstruction(0x07, 4, "RLCA", "Rotate Left Circular Accumulator", _ => { _accumulator.RotateLeftCircularAccumulator(); }); AddStandardInstruction(0x1F, 4, "RRA", "Rotate Right Accumulator", _ => { _accumulator.RotateRightAccumulator(); }); AddStandardInstruction(0x0F, 4, "RRCA", "Rotate Right Circular Accumulator", _ => { _accumulator.RotateRightCircularAccumulator(); }); @@ -712,7 +888,7 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x4F, 4, "LD A,C", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); AddDoubleByteInstruction(0xFD, 0x57, 4, "LD A,D", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); AddDoubleByteInstruction(0xFD, 0x5F, 4, "LD A,E", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); - + // These are undocumented instructions which operate on the IX/IY high and low nibbles AddDoubleByteInstruction(0xDD, 0x26, 7, "LD IXH,N", "Load n into IX high", _ => { _ix.SetHigh(_pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x2E, 7, "LD IXL,N", "Load n into IX low", _ => { _ix.SetLow(_pc.GetNextDataByte()); }); @@ -782,8 +958,8 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xDD, 0x66, 19, "LD H,(IX+d)", "Load memory at IX + d into H", _ => { _h.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xDD, 0x6E, 19, "LD L,(IX+d)", "Load memory at IX + d into L", _ => { _l.SetFromDataInMemory(_ix.Value, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { _accumulator.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte());}); - AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { _b.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte());}); + AddDoubleByteInstruction(0xFD, 0x7E, 19, "LD A,(IY+d)", "Load memory at IY + d into A", _ => { _accumulator.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); + AddDoubleByteInstruction(0xFD, 0x46, 19, "LD B,(IY+d)", "Load memory at IY + d into B", _ => { _b.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x4E, 19, "LD C,(IY+d)", "Load memory at IY + d into C", _ => { _c.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x56, 19, "LD D,(IY+d)", "Load memory at IY + d into D", _ => { _d.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); AddDoubleByteInstruction(0xFD, 0x5E, 19, "LD E,(IY+d)", "Load memory at IY + d into E", _ => { _e.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); @@ -791,7 +967,7 @@ private void PopulateLoadAndExchangeInstructions() AddDoubleByteInstruction(0xFD, 0x6E, 19, "LD L,(IY+d)", "Load memory at IY + d into L", _ => { _l.SetFromDataInMemory(_iy.Value, _pc.GetNextDataByte()); }); AddStandardInstruction(0x36, 10, "LD (HL),N", "Load value n into memory at HL", _ => { _memoryManagement.WriteToMemory(_hl, _pc.GetNextDataByte()); }); - AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); _memoryManagement.WriteToMemory(_ix, value, offset); }); + AddDoubleByteInstruction(0xDD, 0x36, 19, "LD(IX + d), N", "Load value n into location at IX + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); _memoryManagement.WriteToMemory(_ix, value, offset); }); AddDoubleByteInstruction(0xFD, 0x36, 19, "LD(IY + d), N", "Load value n into location at IY + d", _ => { var offset = _pc.GetNextDataByte(); var value = _pc.GetNextDataByte(); _memoryManagement.WriteToMemory(_iy, value, offset); }); AddStandardInstruction(0x3A, 13, "LD A,(NN)", "Load value at memory location NN into A", _ => { _accumulator.SetFromDataInMemory(_pc.GetNextTwoDataBytes()); }); @@ -939,7 +1115,7 @@ private void PopulateInputOutputInstructions() AddDoubleByteInstruction(0xED, 0x58, 12, "IN E,(C)", "Read I/O at B/C into E with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _e); }); AddDoubleByteInstruction(0xED, 0x60, 12, "IN H,(C)", "Read I/O at B/C into H with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _h); }); AddDoubleByteInstruction(0xED, 0x68, 12, "IN L,(C)", "Read I/O at B/C into L with flags", _ => { _ioManagement.ReadAndSetRegister(_bc, _l); }); - + AddDoubleByteInstruction(0xED, 0xA2, 16, "INI", "Input and Increment", _ => { var portAddress = (ushort)((_b.Value << 8) + _c.Value); @@ -1028,12 +1204,12 @@ private void PopulateInputOutputInstructions() AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { _ioManagement.Write(_accumulator.Value, _pc.GetNextDataByte(), _accumulator); }); - AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { _ioManagement.Write(_b.Value, _c.Value, _accumulator); }); - AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { _ioManagement.Write(_b.Value, _c.Value, _b); }); - AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { _ioManagement.Write(_b.Value, _c.Value, _c); }); - AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { _ioManagement.Write(_b.Value, _c.Value, _d); }); - AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { _ioManagement.Write(_b.Value, _c.Value, _e); }); - AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { _ioManagement.Write(_b.Value, _c.Value, _h); }); + AddDoubleByteInstruction(0xED, 0x79, 12, "OUT (C),A", "Write I/O at B/C from A", _ => { _ioManagement.Write(_b.Value, _c.Value, _accumulator); }); + AddDoubleByteInstruction(0xED, 0x41, 12, "OUT (C),B", "Write I/O at B/C from B", _ => { _ioManagement.Write(_b.Value, _c.Value, _b); }); + AddDoubleByteInstruction(0xED, 0x49, 12, "OUT (C),C", "Write I/O at B/C from C", _ => { _ioManagement.Write(_b.Value, _c.Value, _c); }); + AddDoubleByteInstruction(0xED, 0x51, 12, "OUT (C),D", "Write I/O at B/C from D", _ => { _ioManagement.Write(_b.Value, _c.Value, _d); }); + AddDoubleByteInstruction(0xED, 0x59, 12, "OUT (C),E", "Write I/O at B/C from E", _ => { _ioManagement.Write(_b.Value, _c.Value, _e); }); + AddDoubleByteInstruction(0xED, 0x61, 12, "OUT (C),H", "Write I/O at B/C from H", _ => { _ioManagement.Write(_b.Value, _c.Value, _h); }); AddDoubleByteInstruction(0xED, 0x69, 12, "OUT (C),L", "Write I/O at B/C from L", _ => { _ioManagement.Write(_b.Value, _c.Value, _l); }); AddDoubleByteInstruction(0xED, 0xA3, 16, "OUTI", "Output and Increment", _ => @@ -1088,8 +1264,8 @@ private void PopulateInputOutputInstructions() var portAddress = (ushort)((_b.Value << 8) + _c.Value); _io.WritePort(portAddress, data); - _hl.Decrement(); - _flags.SetIfZero(_b.Value); + _hl.Decrement(); + _flags.SetIfZero(_b.Value); _flags.SetFlag(Z80StatusFlags.AddSubtractN); }); AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => @@ -1127,72 +1303,157 @@ private void PopulateInputOutputInstructions() }); } - private enum CbInstructionModes : byte + private void LoadRR(byte opCode) { - Normal = 0x00, - DD = 0xDD, - FD = 0xFD - } + // LD r,r' is 0 1 r r r r' r' r' + var sourceRegisterId = (byte)(opCode & 0x07); + var destinationRegisterId = (byte)((opCode & 0x38) >> 3); - private class Instruction - { - public Instruction(byte opCode, string name, string description, int cycles, Action handleMethod) + if (sourceRegisterId == 0x06 && destinationRegisterId == 0x06) + { + // 16 bit register load not supported in this method + throw new InvalidOperationException($"Invalid op code, 16-bit load to same register LoadRR - OP code {opCode:X2}"); + } + + // Special cases where we are loading from or into memory location referenced by HL register rather than actual register + if (sourceRegisterId == 0x06) { - PrefixOpCode = 0x00; - OpCode = opCode; - Name = name; - Description = description; - ClockCycles = cycles; - _handleMethod = handleMethod; + Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); + _cycleCounter.Increment(3); + return; } - public Instruction(byte prefixOpCode, byte opCode, string name, string description, int cycles, Action handleMethod) + if (destinationRegisterId == 0x06) { - PrefixOpCode = prefixOpCode; - OpCode = opCode; - Name = name; - Description = description; - ClockCycles = cycles; - _handleMethod = handleMethod; + var register = Get8BitRegisterByRIdentifier(sourceRegisterId); + _memoryManagement.WriteToMemory(_hl, register.Value); + _cycleCounter.Increment(3); + return; } - private readonly Action _handleMethod; + var sourceRegister = Get8BitRegisterByRIdentifier(sourceRegisterId); + var destinationRegister = Get8BitRegisterByRIdentifier(destinationRegisterId); - public byte PrefixOpCode { get; } - public byte OpCode { get; } - public string Name { get; } - public string Description { get; } - public int ClockCycles { get; } + destinationRegister.Set(sourceRegister); + } - public void Execute() + private IZ808BitGeneralPurposeRegister Get8BitRegisterByRIdentifier(byte identifier) + { + return identifier switch { - _handleMethod(this); + 0 => _b, + 1 => _c, + 2 => _d, + 3 => _e, + 4 => _h, + 5 => _l, + // 6 is HL so cannot return here since 16 bit register + 7 => _accumulator, + _ => throw new ArgumentOutOfRangeException() + }; + } + + private void ResetBitByOpCode(byte opCode) + { + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); } + var registerId = (byte)(opCode & 0x07); - public virtual string GetOpCode() + if (registerId == 0x06) { - return PrefixOpCode != 0x00 ? $"{PrefixOpCode:X2} {OpCode:X2}" : $"{OpCode:X2}"; + _hl.ResetBitByRegisterLocation(bit, 0); + // Accessing (HL) increases cycle count + _cycleCounter.Increment(7); + return; } + + var register = Get8BitRegisterByRIdentifier(registerId); + register.ClearBit(bit); } - /// - /// Special CB instructions where they are DD/FD CB XX - /// - private class SpecialCbInstruction : Instruction + private void SetBitByOpCode(byte opCode) { - public byte DataByte { get; private set; } + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) + { + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to set"); + } + var registerId = (byte)(opCode & 0x07); - public SpecialCbInstruction(byte prefixOpCode, byte opCode, string name, string description, int cycles, - Action handleMethod) : base(prefixOpCode, opCode, name, description, cycles, handleMethod) { } + if (registerId == 0x06) + { + _hl.SetBitByRegisterLocation(bit, 0); + // Accessing (HL) increases cycle count + _cycleCounter.Increment(7); + return; + } - public void SetDataByte(byte data) + var register = Get8BitRegisterByRIdentifier(registerId); + register.SetBit(bit); + } + + private void TestBitByOpCode(byte opCode) + { + var bit = (opCode & 0x38) >> 3; + if (bit is < 0 or > 7) { - DataByte = data; + throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to test"); } + var registerId = (byte)(opCode & 0x07); - public override string GetOpCode() + if (registerId == 0x06) { - return $"{PrefixOpCode:X2} CB {OpCode:X2}"; + _hl.TestBitByRegisterLocation(bit, 0); + // Testing bit via (HL) memory location increases cycle count + _cycleCounter.Increment(4); + return; } + + var register = Get8BitRegisterByRIdentifier(registerId); + var valueToCheck = register.Value; + var bitSet = Bitwise.IsSet(valueToCheck, bit); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); + _flags.SetFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + // This behaviour is not documented + _flags.SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); + } + + private void CompareIncrement() + { + var value = _memory[_hl.Value]; + // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not + var difference = _accumulator.Value - (sbyte)value; + + _hl.Increment(); + _bc.Decrement(); + + _flags.SetIfNegative((byte)difference); + _flags.SetIfZero((byte)(difference & 0xFF)); + + _flags.SetIfHalfCarry(_accumulator.Value, value, difference); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); + } + + private void CompareDecrement() + { + var value = _memory[_hl.Value]; + // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not + var difference = _accumulator.Value - (sbyte)value; + + _hl.Decrement(); + _bc.Decrement(); + + _flags.SetIfNegative((byte)difference); + _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); + _flags.SetIfHalfCarry(_accumulator.Value, value, difference); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs index 7886737..f53a9ae 100644 --- a/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs +++ b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs @@ -1,8 +1,8 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Memory; diff --git a/Kmse.Core/Z80/Model/CbInstructionModes.cs b/Kmse.Core/Z80/Model/CbInstructionModes.cs new file mode 100644 index 0000000..1ef1f74 --- /dev/null +++ b/Kmse.Core/Z80/Model/CbInstructionModes.cs @@ -0,0 +1,8 @@ +namespace Kmse.Core.Z80.Model; + +public enum CbInstructionModes : byte +{ + Normal = 0x00, + DD = 0xDD, + FD = 0xFD +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Support/CpuStatus.cs b/Kmse.Core/Z80/Model/CpuStatus.cs similarity index 96% rename from Kmse.Core/Z80/Support/CpuStatus.cs rename to Kmse.Core/Z80/Model/CpuStatus.cs index 59ed2db..a56e6aa 100644 --- a/Kmse.Core/Z80/Support/CpuStatus.cs +++ b/Kmse.Core/Z80/Model/CpuStatus.cs @@ -1,4 +1,4 @@ -namespace Kmse.Core.Z80.Support; +namespace Kmse.Core.Z80.Model; public class CpuStatus { diff --git a/Kmse.Core/Z80/Model/Instruction.cs b/Kmse.Core/Z80/Model/Instruction.cs new file mode 100644 index 0000000..33d1145 --- /dev/null +++ b/Kmse.Core/Z80/Model/Instruction.cs @@ -0,0 +1,42 @@ +namespace Kmse.Core.Z80.Model; + +public class Instruction +{ + public Instruction(byte opCode, string name, string description, int cycles, Action handleMethod) + { + PrefixOpCode = 0x00; + OpCode = opCode; + Name = name; + Description = description; + ClockCycles = cycles; + _handleMethod = handleMethod; + } + + public Instruction(byte prefixOpCode, byte opCode, string name, string description, int cycles, Action handleMethod) + { + PrefixOpCode = prefixOpCode; + OpCode = opCode; + Name = name; + Description = description; + ClockCycles = cycles; + _handleMethod = handleMethod; + } + + private readonly Action _handleMethod; + + public byte PrefixOpCode { get; } + public byte OpCode { get; } + public string Name { get; } + public string Description { get; } + public int ClockCycles { get; } + + public void Execute() + { + _handleMethod(this); + } + + public virtual string GetOpCode() + { + return PrefixOpCode != 0x00 ? $"{PrefixOpCode:X2} {OpCode:X2}" : $"{OpCode:X2}"; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Model/SpecialCbInstruction.cs b/Kmse.Core/Z80/Model/SpecialCbInstruction.cs new file mode 100644 index 0000000..c8eab21 --- /dev/null +++ b/Kmse.Core/Z80/Model/SpecialCbInstruction.cs @@ -0,0 +1,22 @@ +namespace Kmse.Core.Z80.Model; + +/// +/// Special CB instructions where they are DD/FD CB XX +/// +public class SpecialCbInstruction : Instruction +{ + public byte DataByte { get; private set; } + + public SpecialCbInstruction(byte prefixOpCode, byte opCode, string name, string description, int cycles, + Action handleMethod) : base(prefixOpCode, opCode, name, description, cycles, handleMethod) { } + + public void SetDataByte(byte data) + { + DataByte = data; + } + + public override string GetOpCode() + { + return $"{PrefixOpCode:X2} CB {OpCode:X2}"; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Support/Z80Register.cs b/Kmse.Core/Z80/Model/Z80Register.cs similarity index 88% rename from Kmse.Core/Z80/Support/Z80Register.cs rename to Kmse.Core/Z80/Model/Z80Register.cs index eaba6b7..fab8179 100644 --- a/Kmse.Core/Z80/Support/Z80Register.cs +++ b/Kmse.Core/Z80/Model/Z80Register.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Kmse.Core.Z80.Support; +namespace Kmse.Core.Z80.Model; // TODO: Rename to Unsigned16BitValue [StructLayout(LayoutKind.Explicit)] diff --git a/Kmse.Core/Z80/Support/Z80StatusFlags.cs b/Kmse.Core/Z80/Model/Z80StatusFlags.cs similarity index 91% rename from Kmse.Core/Z80/Support/Z80StatusFlags.cs rename to Kmse.Core/Z80/Model/Z80StatusFlags.cs index f5219a1..6d41a8b 100644 --- a/Kmse.Core/Z80/Support/Z80StatusFlags.cs +++ b/Kmse.Core/Z80/Model/Z80StatusFlags.cs @@ -1,4 +1,4 @@ -namespace Kmse.Core.Z80.Support; +namespace Kmse.Core.Z80.Model; [Flags] public enum Z80StatusFlags : byte diff --git a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs index ae1f22a..409b6fc 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs @@ -1,5 +1,4 @@ using Kmse.Core.Z80.Registers.SpecialPurpose; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs index ae00506..fb2b343 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs index e9fe7f3..c8d301c 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index 41140ec..b5cebe2 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.SpecialPurpose; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs index c6b7070..9fd1ae2 100644 --- a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -1,6 +1,6 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs index 7da1276..54e8240 100644 --- a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -1,6 +1,5 @@ -using System.Reflection.PortableExecutable; -using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs index 54c8256..c2f311f 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs index 39fa71e..687f2fd 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs index f51bafd..10c1558 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -1,4 +1,4 @@ -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80.Registers.SpecialPurpose; diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs index f4f20bd..1974b7c 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.SpecialPurpose; diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index 57cbf20..85506fd 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers.SpecialPurpose; diff --git a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs index 773ccc2..d98caa0 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index 55c2cc4..5b95350 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs index 74dbe5d..4184ed2 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs @@ -1,6 +1,6 @@ using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs index 4523ffe..9cee595 100644 --- a/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs @@ -1,7 +1,7 @@ using Kmse.Core.Memory; using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs index 9b6c5e6..e432b39 100644 --- a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -1,6 +1,6 @@ using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; -using Kmse.Core.Z80.Support; namespace Kmse.Core.Z80.Registers; diff --git a/Kmse.Core/Z80/Running/IZ80CpuRunningStateManager.cs b/Kmse.Core/Z80/Running/IZ80CpuRunningStateManager.cs new file mode 100644 index 0000000..bdd9832 --- /dev/null +++ b/Kmse.Core/Z80/Running/IZ80CpuRunningStateManager.cs @@ -0,0 +1,10 @@ +namespace Kmse.Core.Z80.Running; + +public interface IZ80CpuRunningStateManager +{ + bool Halted { get; } + void Reset(); + void Halt(); + + void ResumeIfHalted(); +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Running/Z80CpuRunningStateManager.cs b/Kmse.Core/Z80/Running/Z80CpuRunningStateManager.cs new file mode 100644 index 0000000..d143f9a --- /dev/null +++ b/Kmse.Core/Z80/Running/Z80CpuRunningStateManager.cs @@ -0,0 +1,36 @@ +using Kmse.Core.Z80.Logging; + +namespace Kmse.Core.Z80.Running; + +public class Z80CpuRunningStateManager : IZ80CpuRunningStateManager +{ + private readonly ICpuLogger _cpuLogger; + + public Z80CpuRunningStateManager(ICpuLogger cpuLogger) + { + _cpuLogger = cpuLogger; + } + + public bool Halted { get; private set; } + + public void Reset() + { + Halted = false; + } + + public void Halt() + { + _cpuLogger.Debug("Halting CPU"); + Halted = true; + } + + public void ResumeIfHalted() + { + if (Halted) + { + _cpuLogger.Debug("Resuming CPU"); + } + + Halted = false; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs b/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs deleted file mode 100644 index 4005c7b..0000000 --- a/Kmse.Core/Z80/Z80Cpu.SpecialOperations.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Kmse.Core.Utilities; -using Kmse.Core.Z80.Registers; -using Kmse.Core.Z80.Support; - -namespace Kmse.Core.Z80 -{ - /// - /// Core operations, memory operations, reset, flags, stack operations - /// - public partial class Z80Cpu - { - private void LoadRR(byte opCode) - { - // LD r,r' is 0 1 r r r r' r' r' - var sourceRegisterId = (byte)(opCode & 0x07); - var destinationRegisterId = (byte)((opCode & 0x38) >> 3); - - if (sourceRegisterId == 0x06 && destinationRegisterId == 0x06) - { - // 16 bit register load not supported in this method - throw new InvalidOperationException($"Invalid op code, 16-bit load to same register LoadRR - OP code {opCode:X2}"); - } - - // Special cases where we are loading from or into memory location referenced by HL register rather than actual register - if (sourceRegisterId == 0x06) - { - Get8BitRegisterByRIdentifier(destinationRegisterId).SetFromDataInMemory(_hl); - _cycleCounter.Increment(3); - return; - } - - if (destinationRegisterId == 0x06) - { - var register = Get8BitRegisterByRIdentifier(sourceRegisterId); - _memoryManagement.WriteToMemory(_hl, register.Value); - _cycleCounter.Increment(3); - return; - } - - var sourceRegister = Get8BitRegisterByRIdentifier(sourceRegisterId); - var destinationRegister = Get8BitRegisterByRIdentifier(destinationRegisterId); - - destinationRegister.Set(sourceRegister); - } - - private IZ808BitGeneralPurposeRegister Get8BitRegisterByRIdentifier(byte identifier) - { - return identifier switch - { - 0 => _b, - 1 => _c, - 2 => _d, - 3 => _e, - 4 => _h, - 5 => _l, - // 6 is HL so cannot return here since 16 bit register - 7 => _accumulator, - _ => throw new ArgumentOutOfRangeException() - }; - } - - private void ResetBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to reset"); - } - var registerId = (byte)(opCode & 0x07); - - if (registerId == 0x06) - { - _hl.ResetBitByRegisterLocation(bit, 0); - // Accessing (HL) increases cycle count - _cycleCounter.Increment(7); - return; - } - - var register = Get8BitRegisterByRIdentifier(registerId); - register.ClearBit(bit); - } - - private void SetBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to set"); - } - var registerId = (byte)(opCode & 0x07); - - if (registerId == 0x06) - { - _hl.SetBitByRegisterLocation(bit, 0); - // Accessing (HL) increases cycle count - _cycleCounter.Increment(7); - return; - } - - var register = Get8BitRegisterByRIdentifier(registerId); - register.SetBit(bit); - } - - private void TestBitByOpCode(byte opCode) - { - var bit = (opCode & 0x38) >> 3; - if (bit is < 0 or > 7) - { - throw new ArgumentOutOfRangeException($"Bit {bit} is not a valid bit to test"); - } - var registerId = (byte)(opCode & 0x07); - - if (registerId == 0x06) - { - _hl.TestBitByRegisterLocation(bit, 0); - // Testing bit via (HL) memory location increases cycle count - _cycleCounter.Increment(4); - return; - } - - var register = Get8BitRegisterByRIdentifier(registerId); - var valueToCheck = register.Value; - var bitSet = Bitwise.IsSet(valueToCheck, bit); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - _flags.SetFlag(Z80StatusFlags.HalfCarryH); - _flags.ClearFlag(Z80StatusFlags.AddSubtractN); - - // This behaviour is not documented - _flags.SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, !bitSet); - } - - private void CompareIncrement() - { - var value = _memory[_hl.Value]; - // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _accumulator.Value - (sbyte)value; - - _hl.Increment(); - _bc.Decrement(); - - _flags.SetIfNegative((byte)difference); - _flags.SetIfZero((byte)(difference & 0xFF)); - - _flags.SetIfHalfCarry(_accumulator.Value, value, difference); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); - } - - private void CompareDecrement() - { - var value = _memory[_hl.Value]; - // The compare is the difference and we do a subtract so we can tell if the comparison would be negative or not - var difference = _accumulator.Value - (sbyte)value; - - _hl.Decrement(); - _bc.Decrement(); - - _flags.SetIfNegative((byte)difference); - _flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, _af.High == value); - _flags.SetIfHalfCarry(_accumulator.Value, value, difference); - _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Value != 0); - _flags.SetFlag(Z80StatusFlags.AddSubtractN); - } - } -} diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 87dec85..9425e10 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -2,56 +2,41 @@ using Kmse.Core.Memory; using Kmse.Core.Z80.Instructions; using Kmse.Core.Z80.Interrupts; -using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Logging; -using Kmse.Core.Z80.Memory; -using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Running; namespace Kmse.Core.Z80; -public partial class Z80Cpu : IZ80Cpu +public class Z80Cpu : IZ80Cpu { - private readonly ICpuLogger _cpuLogger; - private readonly IZ80InstructionLogger _instructionLogger; - private readonly IMasterSystemIoManager _io; - private readonly IMasterSystemMemory _memory; - private readonly IZ80ProgramCounter _pc; - private readonly IZ80StackManager _stack; - private readonly IZ80FlagsManager _flags; - private readonly IZ80Accumulator _accumulator; + private const int NopCycleCount = 4; private readonly IZ80AfRegister _af; private readonly IZ80BcRegister _bc; + private readonly ICpuLogger _cpuLogger; + private readonly IZ80CpuCycleCounter _cycleCounter; private readonly IZ80DeRegister _de; private readonly IZ80HlRegister _hl; - private readonly IZ808BitGeneralPurposeRegister _b; - private readonly IZ808BitGeneralPurposeRegister _c; - private readonly IZ808BitGeneralPurposeRegister _d; - private readonly IZ808BitGeneralPurposeRegister _e; - private readonly IZ808BitGeneralPurposeRegister _h; - private readonly IZ808BitGeneralPurposeRegister _l; + private readonly IZ80InstructionLogger _instructionLogger; + private readonly IZ80CpuInstructions _instructions; + private readonly IZ80InterruptManagement _interruptManagement; + private readonly IZ80InterruptPageAddressRegister _iRegister; private readonly IZ80IndexRegisterXy _ix; private readonly IZ80IndexRegisterXy _iy; + private readonly IZ80ProgramCounter _pc; private readonly IZ80MemoryRefreshRegister _rRegister; - private readonly IZ80InterruptPageAddressRegister _iRegister; - private readonly IZ80CpuInputOutput _ioManagement; - private readonly IZ80CpuMemoryManagement _memoryManagement; - private readonly IZ80InterruptManagement _interruptManagement; - private readonly IZ80CpuCycleCounter _cycleCounter; - - private bool _halted; - private const int NopCycleCount = 4; + private readonly IZ80CpuRunningStateManager _runningStateManager; + private readonly IZ80StackManager _stack; - public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger cpuLogger, IZ80InstructionLogger instructionLogger, Z80CpuRegisters registers, Z80CpuManagement cpuManagement) + public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger cpuLogger, + IZ80InstructionLogger instructionLogger, IZ80CpuInstructions instructions, Z80CpuRegisters registers, Z80CpuManagement cpuManagement) { _cpuLogger = cpuLogger; _instructionLogger = instructionLogger; _cpuLogger.Debug("Initializing CPU"); - _memory = memory; - _io = io; _af = registers.Af; _bc = registers.Bc; @@ -62,24 +47,13 @@ public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger _rRegister = registers.R; _iRegister = registers.I; - _accumulator = _af.Accumulator; - _flags = _af.Flags; - _b = _bc.B; - _c = _bc.C; - _d = _de.D; - _e = _de.E; - _h = _hl.H; - _l = _hl.L; - _stack = registers.Stack; _pc = registers.Pc; - _ioManagement = cpuManagement.IoManagement; - _memoryManagement = cpuManagement.MemoryManagement; _interruptManagement = cpuManagement.InterruptManagement; _cycleCounter = cpuManagement.CycleCounter; - - PopulateInstructions(); + _runningStateManager = cpuManagement.RunningStateManager; + _instructions = instructions; } public CpuStatus GetStatus() @@ -87,7 +61,7 @@ public CpuStatus GetStatus() return new CpuStatus { CurrentCycleCount = _cycleCounter.CurrentCycleCount, - Halted = _halted, + Halted = _runningStateManager.Halted, Af = _af.AsRegister(), Bc = _bc.AsRegister(), @@ -122,8 +96,7 @@ public void Reset() _pc.Reset(); _stack.Reset(); _interruptManagement.Reset(); - - _halted = false; + _runningStateManager.Reset(); _af.Reset(); _bc.Reset(); @@ -147,26 +120,18 @@ public int ExecuteNextCycle() var cycles = _interruptManagement.ProcessInterrupts(); _cycleCounter.Increment(cycles); // If halted and an interrupt occurs, then resume - ResumeIfHalted(); + _runningStateManager.ResumeIfHalted(); return _cycleCounter.CurrentCycleCount; } - if (_halted) + if (_runningStateManager.Halted) { // NOP until interrupt return NopCycleCount; } var opCode = _pc.GetNextInstruction(); - var instruction = opCode switch - { - 0xCB => ProcessCbOpCode(CbInstructionModes.Normal), - 0xDD => ProcessDdOpCode(), - 0xFD => ProcessFdOpCode(), - 0xED => ProcessEdOpCode(), - _ => ProcessGenericNonPrefixedOpCode(opCode) - }; - + var instruction = _instructions.GetInstruction(opCode); if (instruction == null) { // Unhandled instruction, just do a NOP @@ -191,123 +156,4 @@ public int ExecuteNextCycle() return _cycleCounter.CurrentCycleCount; } - - private void Halt() - { - _cpuLogger.Debug("Halting CPU"); - _halted = true; - } - - private void ResumeIfHalted() - { - if (_halted) - { - _cpuLogger.Debug("Resuming CPU"); - } - _halted = false; - } - - private Instruction ProcessGenericNonPrefixedOpCode(byte opCode) - { - if (!_genericInstructions.TryGetValue(opCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled instruction - {opCode:X2}"); - } - - return instruction; - } - - private Instruction ProcessCbOpCode(CbInstructionModes mode) - { - // Two byte op code, so get next part of instruction and use that to lookup instruction - var opCode = _pc.GetNextInstruction(); - - // Normal CB instruction, just do a lookup for the instruction - if (mode == CbInstructionModes.Normal) - { - if (!_cbInstructions.TryGetValue(opCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled 0xCB instruction - {opCode:X2}"); - } - - return instruction; - } - - // Special instruction which is actually 4 bytes and has started with a different prefix op code - // FD/DD CB XX OpCode - - // We need to read another byte which is the actual op code we use to lookup since third byte is data - var fourthOpCode = _pc.GetNextInstruction(); - SpecialCbInstruction specialCbInstruction; - bool foundInstruction; - switch (mode) - { - case CbInstructionModes.DD: foundInstruction = _specialDdcbInstructions.TryGetValue(fourthOpCode, out specialCbInstruction); break; - case CbInstructionModes.FD: foundInstruction = _specialFdcbInstructions.TryGetValue(fourthOpCode, out specialCbInstruction); break; - default: - { - _cpuLogger.Error($"Unhandled CB instruction mode 0x{mode} - Second Op Code {opCode:X2}, Third Op Code {fourthOpCode:X2}"); - throw new ArgumentOutOfRangeException(nameof(mode), mode, null); - } - } - - if (!foundInstruction) - { - _cpuLogger.Error($"Unhandled 0x{mode} 0xCB instruction - {opCode:X2}"); - return null; - } - - // Add data byte which is byte read after the 0xCB - specialCbInstruction.SetDataByte(opCode); - return specialCbInstruction; - } - - private Instruction ProcessDdOpCode() - { - // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = _pc.GetNextInstruction(); - if (secondOpCode == 0xCB) - { - // Not a normal instruction, but a special instruction - // ie. DD CB data opcode - return ProcessCbOpCode(CbInstructionModes.DD); - } - - if (!_ddInstructions.TryGetValue(secondOpCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled 0xDD instruction - {secondOpCode:X2}"); - } - - return instruction; - } - - private Instruction ProcessFdOpCode() - { - // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = _pc.GetNextInstruction(); - if (secondOpCode == 0xCB) - { - // Not a normal instruction, but a special instruction - // ie. FD CB data opcode - return ProcessCbOpCode(CbInstructionModes.FD); - } - if (!_fdInstructions.TryGetValue(secondOpCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled 0xFD instruction - {secondOpCode:X2}"); - } - - return instruction; - } - - private Instruction ProcessEdOpCode() - { - // Two byte op code, so get next part of instruction and use that to lookup instruction - var secondOpCode = _pc.GetNextInstruction(); - if (!_edInstructions.TryGetValue(secondOpCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled 0xED instruction - {secondOpCode:X2}"); - } - - return instruction; - } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuManagement.cs b/Kmse.Core/Z80/Z80CpuManagement.cs index b3b4484..4b806b5 100644 --- a/Kmse.Core/Z80/Z80CpuManagement.cs +++ b/Kmse.Core/Z80/Z80CpuManagement.cs @@ -2,6 +2,7 @@ using Kmse.Core.Z80.Interrupts; using Kmse.Core.Z80.IO; using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Running; namespace Kmse.Core.Z80; @@ -11,4 +12,5 @@ public class Z80CpuManagement public IZ80CpuMemoryManagement MemoryManagement { get; set; } public IZ80InterruptManagement InterruptManagement { get; set; } public IZ80CpuCycleCounter CycleCounter { get; set; } + public IZ80CpuRunningStateManager RunningStateManager { get; set; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuModule.cs b/Kmse.Core/Z80/Z80CpuModule.cs index 5368d4c..be248ba 100644 --- a/Kmse.Core/Z80/Z80CpuModule.cs +++ b/Kmse.Core/Z80/Z80CpuModule.cs @@ -7,6 +7,7 @@ using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Running; namespace Kmse.Core.Z80; @@ -42,5 +43,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); } } \ No newline at end of file From 0c6462174174c4aa178367c90a692771250154d7 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Wed, 21 Sep 2022 23:10:00 +1000 Subject: [PATCH 14/21] Rename Z80Register struct to Unsigned16BitValue to avoid any confusion with the newer register classes --- .../Z80CpuTests/Z80ProgramCounterFixture.cs | 16 +++++++-------- .../Z80CpuTests/Z80StackManagementFixture.cs | 16 +++++++-------- Kmse.Core/Z80/Model/CpuStatus.cs | 20 +++++++++---------- .../{Z80Register.cs => Unsigned16BitValue.cs} | 3 +-- .../Z80/Registers/General/IZ80AfRegister.cs | 2 +- .../Z80/Registers/General/Z80AfRegister.cs | 8 ++++---- .../IZ8016BitGeneralPurposeRegister.cs | 2 +- Kmse.Core/Z80/Registers/IZ8016BitRegister.cs | 2 +- .../SpecialPurpose/IZ80ProgramCounter.cs | 2 +- .../SpecialPurpose/Z80ProgramCounter.cs | 2 +- .../Z8016BitGeneralPurposeRegisterBase.cs | 8 ++++---- .../Registers/Z8016BitSpecialRegisterBase.cs | 6 +++--- Kmse.Core/Z80/Z80Cpu.cs | 20 +++++++++---------- 13 files changed, 53 insertions(+), 54 deletions(-) rename Kmse.Core/Z80/Model/{Z80Register.cs => Unsigned16BitValue.cs} (78%) diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs index acccbbd..6b1b316 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs @@ -31,7 +31,7 @@ public void WhenResetThenValueIsZero() { _programCounter.Reset(); _programCounter.Value.Should().Be(0); - _programCounter.AsRegister().Word.Should().Be(0); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0); } [Test] @@ -39,7 +39,7 @@ public void WhenSettingProgramCounterThenValueIsUpdated() { _programCounter.Set(0x1234); _programCounter.Value.Should().Be(0x1234); - _programCounter.AsRegister().Word.Should().Be(0x1234); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x1234); } [Test] @@ -48,7 +48,7 @@ public void WhenProgramCounterMovedForwardOffsetWhichOverflowsThenWrapsAround() _programCounter.Set(ushort.MaxValue); _programCounter.MoveProgramCounterForward(3); _programCounter.Value.Should().Be(0x02); - _programCounter.AsRegister().Word.Should().Be(0x02); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x02); } [Test] @@ -57,7 +57,7 @@ public void WhenProgramCounterMovedForwardByOffsetThenValueIsUpdated() _programCounter.Set(0x1234); _programCounter.MoveProgramCounterForward(10); _programCounter.Value.Should().Be(0x123E); - _programCounter.AsRegister().Word.Should().Be(0x123E); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x123E); } [Test] @@ -66,7 +66,7 @@ public void WhenProgramCounterMovedBackwardByOffsetThenValueIsUpdated() _programCounter.Set(0x1234); _programCounter.MoveProgramCounterBackward(10); _programCounter.Value.Should().Be(0x122A); - _programCounter.AsRegister().Word.Should().Be(0x122A); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x122A); } [Test] @@ -75,7 +75,7 @@ public void WhenProgramCounterMovedBackwardByOffsetWhichOverflowsThenWrapsAround _programCounter.Set(0); _programCounter.MoveProgramCounterBackward(3); _programCounter.Value.Should().Be(ushort.MaxValue - 2); - _programCounter.AsRegister().Word.Should().Be(ushort.MaxValue - 2); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(ushort.MaxValue - 2); } [Test] @@ -87,7 +87,7 @@ public void WhenGettingNextDataByteFromMemoryUsingPcValueTheCorrectDataIsReturne data.Should().Be(0x12); _programCounter.Value.Should().Be(0x06); - _programCounter.AsRegister().Word.Should().Be(0x06); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x06); _instructionLogger.Received(1).AddOperationData(0x12); } @@ -103,7 +103,7 @@ public void WhenGettingNextTwoByteDataFromMemoryUsingPcValueTheCorrectDataIsRetu data.Should().Be(0x3412); _programCounter.Value.Should().Be(0x07); - _programCounter.AsRegister().Word.Should().Be(0x07); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x07); _instructionLogger.Received(1).AddOperationData(0x3412); } diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs index 869e345..59a0daf 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs @@ -34,7 +34,7 @@ public void WhenResetThenValueIsTopOfMemoryStack() { _stackManager.Reset(); _stackManager.Value.Should().Be(0xDFF0); - _stackManager.AsRegister().Word.Should().Be(0xDFF0); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0xDFF0); } [Test] @@ -42,7 +42,7 @@ public void WhenSettingStackPointerThenValueIsUpdated() { _stackManager.Set(0x1235); _stackManager.Value.Should().Be(0x1235); - _stackManager.AsRegister().Word.Should().Be(0x1235); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1235); } [Test] @@ -53,7 +53,7 @@ public void WhenSettingStackPointerFromDataInMemoryThenValueIsUpdated() _stackManager.SetFromDataInMemory(0x1122); _stackManager.Value.Should().Be(0x2756); - _stackManager.AsRegister().Word.Should().Be(0x2756); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x2756); } [Test] @@ -62,7 +62,7 @@ public void WhenStackPointerIncrementedThenValueIsUpdated() _stackManager.Set(0x1133); _stackManager.IncrementStackPointer(); _stackManager.Value.Should().Be(0x1134); - _stackManager.AsRegister().Word.Should().Be(0x1134); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1134); } [Test] @@ -71,7 +71,7 @@ public void WhenStackPointerDecrementedThenValueIsUpdated() _stackManager.Set(0x1133); _stackManager.DecrementStackPointer(); _stackManager.Value.Should().Be(0x1132); - _stackManager.AsRegister().Word.Should().Be(0x1132); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1132); } [Test] @@ -108,7 +108,7 @@ public void WhenPushingRegisterToStackThenValueIsWrittenToMemoryAtStackPointerAd _memory.Received()[998] = 0x34; _stackManager.Value.Should().Be(998); - _stackManager.AsRegister().Word.Should().Be(998); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(998); } [Test] @@ -125,7 +125,7 @@ public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointer register.Value.Should().Be(0x6723); _stackManager.Value.Should().Be(1002); - _stackManager.AsRegister().Word.Should().Be(1002); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(1002); } [Test] @@ -145,7 +145,7 @@ public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueI _memory.Received()[1001] = 0x12; _stackManager.Value.Should().Be(1000); - _stackManager.AsRegister().Word.Should().Be(1000); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(1000); } private class Test16BitClass : Z8016BitSpecialRegisterBase diff --git a/Kmse.Core/Z80/Model/CpuStatus.cs b/Kmse.Core/Z80/Model/CpuStatus.cs index a56e6aa..f073973 100644 --- a/Kmse.Core/Z80/Model/CpuStatus.cs +++ b/Kmse.Core/Z80/Model/CpuStatus.cs @@ -5,16 +5,16 @@ public class CpuStatus public int CurrentCycleCount { get; init; } public bool Halted { get; init; } - public Z80Register Af { get; init; } - public Z80Register Bc { get; init; } - public Z80Register De { get; init; } - public Z80Register Hl { get; init; } - public Z80Register AfShadow { get; init; } - public Z80Register BcShadow { get; init; } - public Z80Register DeShadow { get; init; } - public Z80Register HlShadow { get; init; } - public Z80Register Ix { get; init; } - public Z80Register Iy { get; init; } + public Unsigned16BitValue Af { get; init; } + public Unsigned16BitValue Bc { get; init; } + public Unsigned16BitValue De { get; init; } + public Unsigned16BitValue Hl { get; init; } + public Unsigned16BitValue AfShadow { get; init; } + public Unsigned16BitValue BcShadow { get; init; } + public Unsigned16BitValue DeShadow { get; init; } + public Unsigned16BitValue HlShadow { get; init; } + public Unsigned16BitValue Ix { get; init; } + public Unsigned16BitValue Iy { get; init; } public ushort Pc { get; init; } public ushort StackPointer { get; init; } public byte IRegister { get; init; } diff --git a/Kmse.Core/Z80/Model/Z80Register.cs b/Kmse.Core/Z80/Model/Unsigned16BitValue.cs similarity index 78% rename from Kmse.Core/Z80/Model/Z80Register.cs rename to Kmse.Core/Z80/Model/Unsigned16BitValue.cs index fab8179..e6600b3 100644 --- a/Kmse.Core/Z80/Model/Z80Register.cs +++ b/Kmse.Core/Z80/Model/Unsigned16BitValue.cs @@ -2,9 +2,8 @@ namespace Kmse.Core.Z80.Model; -// TODO: Rename to Unsigned16BitValue [StructLayout(LayoutKind.Explicit)] -public struct Z80Register +public struct Unsigned16BitValue { [FieldOffset(0)] public byte Low; [FieldOffset(1)] public byte High; diff --git a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs index fb2b343..df3ae61 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs @@ -17,5 +17,5 @@ public interface IZ80AfRegister : IZ8016BitRegister IZ80Accumulator Accumulator { get; } IZ80FlagsManager Flags { get; } void SwapWithShadow(); - Z80Register ShadowAsRegister(); + Unsigned16BitValue ShadowAsUnsigned16BitValue(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs index 9fd1ae2..4e28782 100644 --- a/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -80,18 +80,18 @@ public void SwapWithShadow() Accumulator.SwapWithShadow(); } - public Z80Register AsRegister() + public Unsigned16BitValue AsUnsigned16BitValue() { - return new Z80Register + return new Unsigned16BitValue { Low = Flags.Value, High = Accumulator.Value }; } - public Z80Register ShadowAsRegister() + public Unsigned16BitValue ShadowAsUnsigned16BitValue() { - return new Z80Register + return new Unsigned16BitValue { Low = Flags.ShadowValue, High = Accumulator.ShadowValue diff --git a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs index c2f311f..ed369c8 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -6,7 +6,7 @@ public interface IZ8016BitGeneralPurposeRegister : IZ8016BitRegister { ushort ShadowValue { get; } void SwapWithShadow(); - Z80Register ShadowAsRegister(); + Unsigned16BitValue ShadowAsUnsigned16BitValue(); void Increment(); void Decrement(); diff --git a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs index 687f2fd..73a1eec 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -16,5 +16,5 @@ public interface IZ8016BitRegister void SetFromDataInMemory(ushort address, byte offset = 0); void SetFromDataInMemory(IZ8016BitRegister register, byte offset = 0); void SaveToMemory(ushort address, byte offset = 0); - Z80Register AsRegister(); + Unsigned16BitValue AsUnsigned16BitValue(); } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs index 10c1558..f37fd96 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -30,7 +30,7 @@ public interface IZ80ProgramCounter : IZ8016BitSpecialRegister /// Set program counter value to register value /// /// Z80 register - void Set(Z80Register register); + void Set(Unsigned16BitValue register); /// /// Set the Program Counter by popping address from top of the stack diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs index 85506fd..ce72990 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80ProgramCounter.cs @@ -66,7 +66,7 @@ public void SetAndSaveExisting(ushort address) Set(address); } - public void Set(Z80Register register) + public void Set(Unsigned16BitValue register) { // Update PC to execute from the value of the register Set(register.Word); diff --git a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs index d98caa0..14eef92 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs @@ -78,18 +78,18 @@ public void SwapWithShadow() HighRegister.SwapWithShadow(); } - public Z80Register AsRegister() + public Unsigned16BitValue AsUnsigned16BitValue() { - return new Z80Register + return new Unsigned16BitValue { Low = LowRegister.Value, High = HighRegister.Value }; } - public Z80Register ShadowAsRegister() + public Unsigned16BitValue ShadowAsUnsigned16BitValue() { - return new Z80Register + return new Unsigned16BitValue { Low = LowRegister.ShadowValue, High = HighRegister.ShadowValue diff --git a/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs index 4184ed2..a37a51d 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs @@ -9,12 +9,12 @@ namespace Kmse.Core.Z80.Registers; /// public abstract class Z8016BitSpecialRegisterBase : Z8016BitRegisterBase, IZ8016BitRegister { - protected Z80Register Register; + protected Unsigned16BitValue Register; protected Z8016BitSpecialRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { - Register = new Z80Register(); + Register = new Unsigned16BitValue(); } public override ushort Value => Register.Word; @@ -70,7 +70,7 @@ public void SaveToMemory(ushort address, byte offset = 0) Memory[(ushort)(location + 1)] = Register.High; } - public Z80Register AsRegister() + public Unsigned16BitValue AsUnsigned16BitValue() { return Register; } diff --git a/Kmse.Core/Z80/Z80Cpu.cs b/Kmse.Core/Z80/Z80Cpu.cs index 9425e10..cf39c53 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -63,16 +63,16 @@ public CpuStatus GetStatus() CurrentCycleCount = _cycleCounter.CurrentCycleCount, Halted = _runningStateManager.Halted, - Af = _af.AsRegister(), - Bc = _bc.AsRegister(), - De = _de.AsRegister(), - Hl = _hl.AsRegister(), - AfShadow = _af.ShadowAsRegister(), - BcShadow = _bc.ShadowAsRegister(), - DeShadow = _de.ShadowAsRegister(), - HlShadow = _hl.ShadowAsRegister(), - Ix = _ix.AsRegister(), - Iy = _iy.AsRegister(), + Af = _af.AsUnsigned16BitValue(), + Bc = _bc.AsUnsigned16BitValue(), + De = _de.AsUnsigned16BitValue(), + Hl = _hl.AsUnsigned16BitValue(), + AfShadow = _af.ShadowAsUnsigned16BitValue(), + BcShadow = _bc.ShadowAsUnsigned16BitValue(), + DeShadow = _de.ShadowAsUnsigned16BitValue(), + HlShadow = _hl.ShadowAsUnsigned16BitValue(), + Ix = _ix.AsUnsigned16BitValue(), + Iy = _iy.AsUnsigned16BitValue(), Pc = _pc.Value, StackPointer = _stack.Value, IRegister = _iRegister.Value, From 5bd7bede922fb446c55fee897d1f84c4d25cac2c Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Thu, 22 Sep 2022 17:47:55 +1000 Subject: [PATCH 15/21] Tidied up handling in interrupts so if an interrupt is set but interrupts are not enabled (interrupt flip flop is not set) then maskable interrupt flag is not set. This should avoid issues where maskable interrupts are used in a routine triggered by a non-maskable interrupt but this behaviour needs to be validated Add unit tests for CPU I/O management, memory management, running state management, interrupt management --- .../Kmse.Core.UnitTests.csproj | 4 + .../Z80CpuTests/CpuExecutionFixture.cs | 172 ++++++----------- .../Z80CpuTests/CpuInstructionsFixture.cs | 48 ++--- .../Z80CpuTests/CpuTestFixtureBase.cs | 69 +++---- .../TestInstructionsFixture.cs | 2 +- .../Z80InterruptManagementFixture.cs | 178 ++++++++++++++++++ .../IoTests/Z80CpuInputOutputFixture.cs | 94 +++++++++ .../Z80CpuMemoryManagementFixture.cs | 110 +++++++++++ .../Z80IndexRegisterXyFixture.cs | 113 +++++++++++ .../Z80ProgramCounterFixture.cs | 2 +- .../Z80StackManagementFixture.cs | 2 +- .../Z80CpuRunningStateManagerFixture.cs | 42 +++++ ...Output.cs => IZ80CpuInputOutputManager.cs} | 2 +- ...tOutput.cs => Z80CpuInputOutputManager.cs} | 5 +- .../Z80/Instructions/Z80CpuInstructions.cs | 2 +- .../Z80/Interrupts/Z80InterruptManagement.cs | 11 +- .../Z80/Registers/General/Z80FlagsManager.cs | 5 - .../SpecialPurpose/Z80IndexRegisterXy.cs | 1 - Kmse.Core/Z80/Z80CpuManagement.cs | 2 +- Kmse.Core/Z80/Z80CpuModule.cs | 2 +- 20 files changed, 680 insertions(+), 186 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/InterruptManagementTests/Z80InterruptManagementFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/IoTests/Z80CpuInputOutputFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/MemoryTests/Z80CpuMemoryManagementFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80IndexRegisterXyFixture.cs rename Kmse.Core.UnitTests/Z80CpuTests/{ => RegisterTests/SpecialPurpose}/Z80ProgramCounterFixture.cs (98%) rename Kmse.Core.UnitTests/Z80CpuTests/{ => RegisterTests/SpecialPurpose}/Z80StackManagementFixture.cs (98%) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RunningStateTests/Z80CpuRunningStateManagerFixture.cs rename Kmse.Core/Z80/IO/{IZ80CpuInputOutput.cs => IZ80CpuInputOutputManager.cs} (89%) rename Kmse.Core/Z80/IO/{Z80CpuInputOutput.cs => Z80CpuInputOutputManager.cs} (87%) diff --git a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj index 49ec504..a0a8c9e 100644 --- a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj +++ b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj @@ -27,4 +27,8 @@ + + + + diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index 18dcdf3..86fc1ac 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -1,9 +1,7 @@ using FluentAssertions; -using Kmse.Core.IO; -using Kmse.Core.Memory; using Kmse.Core.Z80; -using Kmse.Core.Z80.Interrupts; -using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Instructions; +using Kmse.Core.Z80.Registers.SpecialPurpose; using NSubstitute; using NUnit.Framework; @@ -15,14 +13,14 @@ public class CpuExecutionFixture : CpuTestFixtureBase [Test] public void WhenResettingCpu() { - _interruptManagement.SetMaskableInterrupt(); - _interruptManagement.SetNonMaskableInterrupt(); + InterruptManagement.SetMaskableInterrupt(); + InterruptManagement.SetNonMaskableInterrupt(); - _cpu.Reset(); - _interruptManagement.MaskableInterrupt.Should().BeFalse(); - _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + Cpu.Reset(); + InterruptManagement.MaskableInterrupt.Should().BeFalse(); + InterruptManagement.NonMaskableInterrupt.Should().BeFalse(); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.CurrentCycleCount.Should().Be(0); status.Pc.Should().Be(0); @@ -50,10 +48,10 @@ public void WhenExecutingSimpleInstruction() PrepareForTest(); // Test using HALT since basic instruction with clear change - _memory[0x00].Returns((byte)0x76); - _cpu.ExecuteNextCycle().Should().Be(4); + Memory[0x00].Returns((byte)0x76); + Cpu.ExecuteNextCycle().Should().Be(4); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.Halted.Should().Be(true); status.Pc.Should().Be(0x01); } @@ -64,11 +62,11 @@ public void WhenExecutingDoubleByteInstruction() PrepareForTest(); // Test using setting MI mode to 2 since double byte instruction with clear change - _memory[0x00].Returns((byte)0xED); - _memory[0x01].Returns((byte)0x5E); - _cpu.ExecuteNextCycle().Should().Be(8); + Memory[0x00].Returns((byte)0xED); + Memory[0x01].Returns((byte)0x5E); + Cpu.ExecuteNextCycle().Should().Be(8); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.InterruptMode.Should().Be(2); status.Pc.Should().Be(0x02); } @@ -76,32 +74,68 @@ public void WhenExecutingDoubleByteInstruction() [Test] public void WhenExecutingDdCbSpecialInstruction() { - // TODO: Add once we implemented at least 1 DD CB XX XX instruction + Registers.IX = Substitute.For(); + var cpuInstructions = new Z80CpuInstructions(Memory, Io, CpuLogger, Registers, CpuManagement); + Cpu = new Z80Cpu(Memory, Io, CpuLogger, InstructionLogger, cpuInstructions, Registers, CpuManagement); + + PrepareForTest(); + + // RES 0,(IX+d) + Memory[0x00].Returns((byte)0xDD); + Memory[0x01].Returns((byte)0xCB); + Memory[0x02].Returns((byte)0x02); + Memory[0x03].Returns((byte)0x86); + Cpu.ExecuteNextCycle().Should().Be(23); + + var status = Cpu.GetStatus(); + status.Pc.Should().Be(0x04); + + Registers.IX.Received(1).ResetBitByRegisterLocation(0, 0x02); } [Test] public void WhenExecutingFdCbSpecialInstruction() { - // TODO: Add once we implemented at least 1 FD CB XX XX instruction + Registers.IY = Substitute.For(); + var cpuInstructions = new Z80CpuInstructions(Memory, Io, CpuLogger, Registers, CpuManagement); + Cpu = new Z80Cpu(Memory, Io, CpuLogger, InstructionLogger, cpuInstructions, Registers, CpuManagement); + + PrepareForTest(); + + // RES 0,(IY+d) + Memory[0x00].Returns((byte)0xFD); + Memory[0x01].Returns((byte)0xCB); + Memory[0x02].Returns((byte)0x02); + Memory[0x03].Returns((byte)0x86); + Cpu.ExecuteNextCycle().Should().Be(23); + + var status = Cpu.GetStatus(); + status.Pc.Should().Be(0x04); + + Registers.IY.Received(1).ResetBitByRegisterLocation(0, 0x02); } [Test] - public void WhenExecutingAndNmiSet() + public void WhenExecutingAndInterruptIsSet() { + // This validates that when executing, it checks for interrupts and processes them + PrepareForTest(); // Enable interrupts - _memory[0x00].Returns((byte)0xFB); + Memory[0x00].Returns((byte)0xFB); // Execute EI instruction first so we have maskable set but also moved the PC // This allows us to better test handling since Pc is non zero and interrupts are on - _cpu.ExecuteNextCycle().Should().Be(4); + Cpu.ExecuteNextCycle().Should().Be(4); - _interruptManagement.ClearMaskableInterrupt(); - _interruptManagement.SetNonMaskableInterrupt(); + InterruptManagement.ClearMaskableInterrupt(); + InterruptManagement.SetNonMaskableInterrupt(); + + var cycleCount = Cpu.ExecuteNextCycle(); - var cycleCount = _cpu.ExecuteNextCycle(); cycleCount.Should().Be(11); - var status = _cpu.GetStatus(); + + var status = Cpu.GetStatus(); status.Halted.Should().Be(false); status.InterruptFlipFlop1.Should().BeFalse(); status.InterruptFlipFlop2.Should().BeTrue(); @@ -110,89 +144,7 @@ public void WhenExecutingAndNmiSet() // Pc put onto stack status.StackPointer.Should().Be(0xDFEE); // Wrote current PC (0x00) onto stack - _memory.Received(1)[0xDFEF] = 0x00; - _memory.Received(1)[0xDFEE] = 0x01; - } - - private static int[] _maskedInterruptModes = { 0, 1, 2 }; - - [Test] - [TestCaseSource(nameof(_maskedInterruptModes))] - public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) - { - PrepareForTest(); - - // Enable interrupts - _memory[0x00].Returns((byte)0xFB); - _memory[0x01].Returns((byte)0xED); - - byte opCode = mode switch - { - 0 => 0x46, - 1 => 0x56, - 2 => 0x5E, - _ => throw new ArgumentException("Invalid mode") - }; - _memory[0x02].Returns(opCode); - - // Set EI and then set mode - _cpu.ExecuteNextCycle().Should().Be(4); - _cpu.ExecuteNextCycle().Should().Be(8); - - _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeTrue(); - _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); - _interruptManagement.SetMaskableInterrupt(); ; - - var cycleCount = _cpu.ExecuteNextCycle(); - var status = _cpu.GetStatus(); - - status.Halted.Should().Be(false); - _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); - _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); - - // Jumps to 0x38 when in mode 0 or 1 - if (mode is 0 or 1) - { - cycleCount.Should().Be(11); - - // Interrupt is not cleared in these modes until a RETI or DI is called - _interruptManagement.MaskableInterrupt.Should().BeTrue(); - - status.Pc.Should().Be(0x38); - - // Pc put onto stack - status.StackPointer.Should().Be(0xDFEE); - // Wrote current PC (0x00) onto stack - _memory.Received(1)[0xDFEF] = 0x00; - _memory.Received(1)[0xDFEE] = 0x03; - } - else - { - // NOP - cycleCount.Should().Be(4); - - // Interrupt is cleared in this modes since this mode is not supported - _interruptManagement.MaskableInterrupt.Should().BeFalse(); - } - } - - [Test] - public void WhenEnablingAndDisablingMaskableInterrupts() - { - PrepareForTest(); - - // Enable interrupts - _memory[0x00].Returns((byte)0xFB); - _memory[0x01].Returns((byte)0xF3); - - // Execute EI first - _cpu.ExecuteNextCycle().Should().Be(4); - _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeTrue(); - _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); - - // Execute disable interrupt - _cpu.ExecuteNextCycle().Should().Be(4); - _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); - _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); + Memory.Received(1)[0xDFEF] = 0x00; + Memory.Received(1)[0xDFEE] = 0x01; } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index 8c5d508..bd25265 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs @@ -31,15 +31,15 @@ public void WhenExecutingRelativeJump(byte offset, ushort expectedPc) // Execute at least 500 instructions for (var i = 0; i < 500; i++) { - _memory[(ushort)i].Returns((byte)0x00); - _cpu.ExecuteNextCycle().Should().Be(4); + Memory[(ushort)i].Returns((byte)0x00); + Cpu.ExecuteNextCycle().Should().Be(4); } - _memory[500].Returns((byte)0x18); - _memory[501].Returns(offset); - _cpu.ExecuteNextCycle().Should().Be(12); + Memory[500].Returns((byte)0x18); + Memory[501].Returns(offset); + Cpu.ExecuteNextCycle().Should().Be(12); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.Pc.Should().Be(expectedPc); } @@ -54,24 +54,24 @@ public void WhenExecutingAddWithCarryWithNoCarry() var valueToAdd = i + 1; //ld a, 127 - _memory[0].Returns((byte)0x3E); - _memory[1].Returns((byte)i); + Memory[0].Returns((byte)0x3E); + Memory[1].Returns((byte)i); //adc a, a - _memory[2].Returns((byte)0x8F); + Memory[2].Returns((byte)0x8F); // Execute ld - _cpu.ExecuteNextCycle().Should().Be(7); - _cpu.GetStatus().Af.High.Should().Be((byte)i); + Cpu.ExecuteNextCycle().Should().Be(7); + Cpu.GetStatus().Af.High.Should().Be((byte)i); // Execute adc - _cpu.ExecuteNextCycle().Should().Be(4); + Cpu.ExecuteNextCycle().Should().Be(4); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.Pc.Should().Be(0x03); var expectedValue = (byte)(i + i); var result = originalValue + valueToAdd; - var foundValue = _cpu.GetStatus().Af.High; + var foundValue = Cpu.GetStatus().Af.High; foundValue.Should().Be(expectedValue); var signFlagSet = expectedValue >> 7 == 0x01; @@ -102,31 +102,31 @@ public void WhenExecutingAddWithCarryWithCarry() var valueToAdd = i + 1; // scf to set carry flag - _memory[0].Returns((byte)0x37); + Memory[0].Returns((byte)0x37); //ld a, i - _memory[1].Returns((byte)0x3E); - _memory[2].Returns((byte)i); + Memory[1].Returns((byte)0x3E); + Memory[2].Returns((byte)i); //adc a, a - _memory[3].Returns((byte)0x8F); + Memory[3].Returns((byte)0x8F); // execute scf - _cpu.ExecuteNextCycle().Should().Be(4); + Cpu.ExecuteNextCycle().Should().Be(4); // Execute ld - _cpu.ExecuteNextCycle().Should().Be(7); - _cpu.GetStatus().Af.High.Should().Be((byte)i); + Cpu.ExecuteNextCycle().Should().Be(7); + Cpu.GetStatus().Af.High.Should().Be((byte)i); // Execute adc - _cpu.ExecuteNextCycle().Should().Be(4); + Cpu.ExecuteNextCycle().Should().Be(4); - var status = _cpu.GetStatus(); + var status = Cpu.GetStatus(); status.Pc.Should().Be(0x04); var expectedValue = (byte)(originalValue + valueToAdd); var result = originalValue + valueToAdd; - var foundValue = _cpu.GetStatus().Af.High; + var foundValue = Cpu.GetStatus().Af.High; foundValue.Should().Be(expectedValue); var signFlagSet = expectedValue >> 7 == 0x01; diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs index d1f2860..57e43e5 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -18,49 +18,52 @@ namespace Kmse.Core.UnitTests.Z80CpuTests; public abstract class CpuTestFixtureBase { - protected Z80Cpu _cpu; - protected ICpuLogger _cpuLogger; - protected IZ80InterruptManagement _interruptManagement; - protected IMasterSystemIoManager _io; - protected IMasterSystemMemory _memory; + protected Z80Cpu Cpu; + protected ICpuLogger CpuLogger; + protected IZ80InterruptManagement InterruptManagement; + protected IMasterSystemIoManager Io; + protected IMasterSystemMemory Memory; + protected IZ80InstructionLogger InstructionLogger; + protected Z80CpuRegisters Registers; + protected Z80CpuManagement CpuManagement; protected void PrepareForTest() { - _cpu.Reset(); - _memory.ClearReceivedCalls(); - _memory.ClearSubstitute(); - _io.ClearReceivedCalls(); + Cpu.Reset(); + Memory.ClearReceivedCalls(); + Memory.ClearSubstitute(); + Io.ClearReceivedCalls(); } [SetUp] public void Setup() { - _cpuLogger = Substitute.For(); - _memory = Substitute.For(); - _io = Substitute.For(); - var instructionLogger = new Z80InstructionLogger(_cpuLogger); + CpuLogger = Substitute.For(); + Memory = Substitute.For(); + Io = Substitute.For(); + InstructionLogger = new Z80InstructionLogger(CpuLogger); var flags = new Z80FlagsManager(); - var accumulator = new Z80Accumulator(flags, _memory); - var af = new Z80AfRegister(_memory, flags, accumulator); - var bc = new Z80BcRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); - var de = new Z80DeRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); - var hl = new Z80HlRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); - var ix = new Z80IndexRegisterXy(_memory, af.Flags); - var iy = new Z80IndexRegisterXy(_memory, af.Flags); - var rRegister = new Z80MemoryRefreshRegister(_memory, af.Flags); - var iRegister = new Z80InterruptPageAddressRegister(_memory, af.Flags); + var accumulator = new Z80Accumulator(flags, Memory); + var af = new Z80AfRegister(Memory, flags, accumulator); + var bc = new Z80BcRegister(Memory, af.Flags, () => new Z808BitGeneralPurposeRegister(Memory, flags)); + var de = new Z80DeRegister(Memory, af.Flags, () => new Z808BitGeneralPurposeRegister(Memory, flags)); + var hl = new Z80HlRegister(Memory, af.Flags, () => new Z808BitGeneralPurposeRegister(Memory, flags)); + var ix = new Z80IndexRegisterXy(Memory, af.Flags); + var iy = new Z80IndexRegisterXy(Memory, af.Flags); + var rRegister = new Z80MemoryRefreshRegister(Memory, af.Flags); + var iRegister = new Z80InterruptPageAddressRegister(Memory, af.Flags); - var stack = new Z80StackManager(_memory, af.Flags, _cpuLogger); - var pc = new Z80ProgramCounter(_memory, af.Flags, stack, instructionLogger); + var stack = new Z80StackManager(Memory, af.Flags, CpuLogger); + var pc = new Z80ProgramCounter(Memory, af.Flags, stack, InstructionLogger); - var ioManagement = new Z80CpuInputOutput(_io, af.Flags); - var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); - _interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); + var ioManagement = new Z80CpuInputOutputManager(Io, af.Flags); + var memoryManagement = new Z80CpuMemoryManagement(Memory, af.Flags); + InterruptManagement = new Z80InterruptManagement(pc, CpuLogger); var cycleCounter = new Z80CpuCycleCounter(); - var runningStateManager = new Z80CpuRunningStateManager(_cpuLogger); + var runningStateManager = new Z80CpuRunningStateManager(CpuLogger); - var registers = new Z80CpuRegisters + Registers = new Z80CpuRegisters { Pc = pc, Stack = stack, @@ -73,15 +76,15 @@ public void Setup() R = rRegister, I = iRegister }; - var cpuManagement = new Z80CpuManagement + CpuManagement = new Z80CpuManagement { IoManagement = ioManagement, - InterruptManagement = _interruptManagement, + InterruptManagement = InterruptManagement, MemoryManagement = memoryManagement, CycleCounter = cycleCounter, RunningStateManager = runningStateManager, }; - var cpuInstructions = new Z80CpuInstructions(_memory, _io, _cpuLogger, registers, cpuManagement); - _cpu = new Z80Cpu(_memory, _io, _cpuLogger, instructionLogger, cpuInstructions, registers, cpuManagement); + var cpuInstructions = new Z80CpuInstructions(Memory, Io, CpuLogger, Registers, CpuManagement); + Cpu = new Z80Cpu(Memory, Io, CpuLogger, InstructionLogger, cpuInstructions, Registers, CpuManagement); } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs index 65fec4c..1864f85 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs @@ -61,7 +61,7 @@ public void Setup() var stack = new Z80StackManager(_memory, af.Flags, _cpuLogger); var pc = new Z80ProgramCounter(_memory, af.Flags, stack, instructionLogger); - var ioManagement = new Z80CpuInputOutput(_io, af.Flags); + var ioManagement = new Z80CpuInputOutputManager(_io, af.Flags); var memoryManagement = new Z80CpuMemoryManagement(_memory, af.Flags); var interruptManagement = new Z80InterruptManagement(pc, _cpuLogger); var cycleCounter = new Z80CpuCycleCounter(); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InterruptManagementTests/Z80InterruptManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InterruptManagementTests/Z80InterruptManagementFixture.cs new file mode 100644 index 0000000..0d5c91d --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InterruptManagementTests/Z80InterruptManagementFixture.cs @@ -0,0 +1,178 @@ +using FluentAssertions; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InterruptManagementTests; + +public class Z80InterruptManagementFixture +{ + private Z80InterruptManagement _interruptManagement; + private IZ80ProgramCounter _programCounter; + + [SetUp] + public void Setup() + { + _programCounter = Substitute.For(); + _interruptManagement = new Z80InterruptManagement(_programCounter, Substitute.For()); + } + + [Test] + public void WhenSettingInterruptModeThenModeIsSet() + { + _interruptManagement.SetInterruptMode(2); + _interruptManagement.InterruptMode.Should().Be(2); + } + + [Test] + public void WhenSettingInvalidInterruptModeThenExceptionIsThrown() + { + var action = () => _interruptManagement.SetInterruptMode(3); + action.Should().Throw(); + } + + [Test] + public void WhenEnablingMaskableInterruptsThenFlipFlopsAreSetToTrue() + { + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeTrue(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); + } + + [Test] + public void WhenDisablingMaskableInterruptsThenFlipFlopsAreSetToFalse() + { + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.DisableMaskableInterrupts(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); + } + + [Test] + public void WhenSettingNonMaskableInterruptThenNonMaskableInterruptIsSet() + { + _interruptManagement.SetNonMaskableInterrupt(); + _interruptManagement.NonMaskableInterrupt.Should().BeTrue(); + _interruptManagement.InterruptWaiting().Should().BeTrue(); + } + + [Test] + public void WhenProcessingInterruptsWithNonMaskableInterruptThenJumpProgramCounterToMemoryAddress66H() + { + _interruptManagement.SetNonMaskableInterrupt(); + var cycleCount = _interruptManagement.ProcessInterrupts(); + + cycleCount.Should().Be(11); + _programCounter.Received(1).SetAndSaveExisting(0x66); + _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + } + + [Test] + public void WhenProcessingInterruptsWithNonMaskableInterruptThenFlipFlopStatusIsCopiedToTempStorage() + { + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetNonMaskableInterrupt(); + _interruptManagement.ProcessInterrupts(); + + _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); + } + + [Test] + public void WhenSettingMaskableInterruptWithInterruptsEnabledThenMaskableInterruptIsSet() + { + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetMaskableInterrupt(); + _interruptManagement.MaskableInterrupt.Should().BeTrue(); + _interruptManagement.InterruptWaiting().Should().BeTrue(); + } + + [Test] + public void WhenSettingMaskableInterruptWithInterruptsDisabledThenMaskableInterruptIsNotSet() + { + _interruptManagement.DisableMaskableInterrupts(); + _interruptManagement.SetMaskableInterrupt(); + _interruptManagement.MaskableInterrupt.Should().BeFalse(); + _interruptManagement.InterruptWaiting().Should().BeFalse(); + } + + [Test] + [TestCase(0)] + [TestCase(1)] + public void WhenProcessingMaskableInterruptMode0Or1ThenJumpTo38H(byte mode) + { + _interruptManagement.SetInterruptMode(mode); + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetMaskableInterrupt(); + + var cycleCount = _interruptManagement.ProcessInterrupts(); + + cycleCount.Should().Be(11); + + _interruptManagement.MaskableInterrupt.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); + + _programCounter.Received(1).SetAndSaveExisting(0x38); + } + + [Test] + public void WhenProcessingMaskableInterruptMode2ThenNoOperation() + { + _interruptManagement.SetInterruptMode(2); + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetMaskableInterrupt(); + + var cycleCount = _interruptManagement.ProcessInterrupts(); + + cycleCount.Should().Be(4); + + _interruptManagement.MaskableInterrupt.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); + + // Mode 2 does nothing so we check it did not make any calls to PC + _programCounter.DidNotReceiveWithAnyArgs().SetAndSaveExisting(0); + _programCounter.DidNotReceiveWithAnyArgs().Set(0); + } + + [Test] + public void WhenProcessingInterruptsWithBothNonMaskableAndMaskableInterruptsThenNonMaskableIsHandledFirst() + { + // NonMaskable interrupt is always processed first and Maskable interrupts disabled temporarily until a RET call then on the next processing cycle is the maskable interrupt processed + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetMaskableInterrupt(); + _interruptManagement.SetNonMaskableInterrupt(); + var cycleCount = _interruptManagement.ProcessInterrupts(); + + cycleCount.Should().Be(11); + _programCounter.Received(1).SetAndSaveExisting(0x66); + _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + _interruptManagement.MaskableInterrupt.Should().BeTrue(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeTrue(); + + cycleCount = _interruptManagement.ProcessInterrupts(); + cycleCount.Should().Be(0); + } + + [Test] + public void WhenResetThenInterruptsAndInterruptFlipFlopsAreReset() + { + _interruptManagement.SetInterruptMode(1); + _interruptManagement.EnableMaskableInterrupts(); + _interruptManagement.SetNonMaskableInterrupt(); + _interruptManagement.SetMaskableInterrupt(); + + _interruptManagement.Reset(); + _interruptManagement.MaskableInterrupt.Should().BeFalse(); + _interruptManagement.NonMaskableInterrupt.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopStatus.Should().BeFalse(); + _interruptManagement.InterruptEnableFlipFlopTempStorageStatus.Should().BeFalse(); + _interruptManagement.InterruptMode.Should().Be(0); + _interruptManagement.InterruptWaiting().Should().BeFalse(); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/IoTests/Z80CpuInputOutputFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/IoTests/Z80CpuInputOutputFixture.cs new file mode 100644 index 0000000..246581a --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/IoTests/Z80CpuInputOutputFixture.cs @@ -0,0 +1,94 @@ +using FluentAssertions; +using Kmse.Core.IO; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.IoTests; + +public class Z80CpuInputOutputFixture +{ + private Z80CpuInputOutputManager _cpuIoManager; + private IZ80FlagsManager _flags; + private IMasterSystemIoManager _io; + + [SetUp] + public void Setup() + { + _io = Substitute.For(); + _flags = Substitute.For(); + _cpuIoManager = new Z80CpuInputOutputManager(_io, _flags); + } + + [Test] + public void WhenReadFromIoWithoutFlagsThenValueReturnedAndNoFlagsChanged() + { + _io.ReadPort(0x1234).Returns((byte)0x21); + var value = _cpuIoManager.Read(0x12, 0x34, false); + value.Should().Be(0x21); + } + + [Test] + public void WhenReadFromIoWithFlagsThenFlagsAreUpdated() + { + const byte expectedValue = 0x81; + _io.ReadPort(0x1234).Returns(expectedValue); + var value = _cpuIoManager.Read(0x12, 0x34, true); + + value.Should().Be(expectedValue); + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + public void WhenReadFromIoAndSet8BitRegisterThen8BitRegisterIsSetWithValue() + { + const byte expectedValue = 0x81; + _io.ReadPort(0x1234).Returns(expectedValue); + var register = Substitute.For(); + _cpuIoManager.ReadAndSetRegister(0x12, 0x34, register); + + register.Received(1).Set(expectedValue); + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + public void WhenReadFromIoAndSetUsing16BitRegisterThen8BitRegisterIsSetWithValue() + { + const byte expectedValue = 0x81; + _io.ReadPort(0x1234).Returns(expectedValue); + var destinationRegister = Substitute.For(); + var sourceRegister = Substitute.For(); + sourceRegister.High.Returns((byte)0x12); + sourceRegister.Low.Returns((byte)0x34); + _cpuIoManager.ReadAndSetRegister(sourceRegister, destinationRegister); + + destinationRegister.Received(1).Set(expectedValue); + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + + + [Test] + public void WhenWriteToIoThenIoPortIsWrittenTo() + { + var register = Substitute.For(); + register.Value.Returns((byte)0x23); + _cpuIoManager.Write(0x22, 0x12, register); + + _io.Received(1).WritePort(0x2212, 0x23); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/MemoryTests/Z80CpuMemoryManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/MemoryTests/Z80CpuMemoryManagementFixture.cs new file mode 100644 index 0000000..4c9c046 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/MemoryTests/Z80CpuMemoryManagementFixture.cs @@ -0,0 +1,110 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.MemoryTests; + +public class Z80CpuMemoryManagementFixture +{ + private IZ80FlagsManager _flags; + private IMasterSystemMemory _memory; + private Z80CpuMemoryManagement _memoryManager; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _memoryManager = new Z80CpuMemoryManagement(_memory, _flags); + } + + [Test] + public void WhenCopyingMemoryThenMemoryIsCopiedFromSourceToDestination() + { + var source = Substitute.For(); + source.Value.Returns((ushort)0x1234); + + var destination = Substitute.For(); + destination.Value.Returns((ushort)0x0102); + + _memory[0x1234].Returns((byte)0x33); + + _memoryManager.CopyMemory(source, destination); + + _memory.Received()[0x0102] = 0x33; + } + + [Test] + public void WhenReadFromMemoryWithOffsetThenValueInMemoryAtAddressIsReturned() + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x1234); + _memory[0x1235].Returns((byte)0x31); + + var value = _memoryManager.ReadFromMemory(register, 1); + value.Should().Be(0x31); + } + + [Test] + public void WhenWriteToMemoryWithOffsetThenValueInMemoryAtAddressIsSet() + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x1122); + + _memoryManager.WriteToMemory(register, 0x53, 5); + _memory.Received()[0x1127] = 0x53; + } + + [Test] + [TestCase(0x01, 0x02, false)] + [TestCase(0x0F, 0x10, true)] + [TestCase(0x10, 0x11, false)] + [TestCase(0xFE, 0xFF, false)] + [TestCase(0xFF, 0x00, true)] + public void WhenIncrementDataInMemoryThenValueIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x1122); + + _memory[0x1123] = value; + + _memoryManager.IncrementMemory(register, 1); + _memory.Received()[0x1123] = expectedValue; + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, halfCarryStatus); + _flags.Received(1).SetIfIncrementOverflow(value); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + [TestCase(0x02, 0x01, false)] + [TestCase(0x10, 0x0F, true)] + [TestCase(0x11, 0x10, false)] + [TestCase(0xFF, 0xFE, false)] + [TestCase(0x00, 0xFF, true)] + public void WhenDecrementDataInMemoryThenValueIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x1122); + + _memory[0x1123] = value; + + _memoryManager.DecrementMemory(register, 1); + _memory.Received()[0x1123] = expectedValue; + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, halfCarryStatus); + _flags.Received(1).SetIfDecrementOverflow(value); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80IndexRegisterXyFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80IndexRegisterXyFixture.cs new file mode 100644 index 0000000..f25ab42 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80IndexRegisterXyFixture.cs @@ -0,0 +1,113 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +public class Z80IndexRegisterXyFixture +{ + private IZ80FlagsManager _flags; + private Z80IndexRegisterXy _indexRegister; + private IMasterSystemMemory _memory; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _indexRegister = new Z80IndexRegisterXy(_memory, _flags); + } + + [Test] + [TestCase(0x01, 0x02, false)] + [TestCase(0x0F, 0x10, true)] + [TestCase(0x10, 0x11, false)] + [TestCase(0xFE, 0xFF, false)] + [TestCase(0xFF, 0x00, true)] + public void WhenIncrementHighByteThenHighByteIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _indexRegister.SetHigh(value); + _indexRegister.SetLow(0x12); + _indexRegister.IncrementHigh(); + _indexRegister.High.Should().Be(expectedValue); + _indexRegister.Low.Should().Be(0x12); + + CheckFlags(true, halfCarryStatus, value, expectedValue); + } + + [Test] + [TestCase(0x01, 0x02, false)] + [TestCase(0x0F, 0x10, true)] + [TestCase(0x10, 0x11, false)] + [TestCase(0xFE, 0xFF, false)] + [TestCase(0xFF, 0x00, true)] + public void WhenIncrementLowByteThenLowByteIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _indexRegister.SetLow(value); + _indexRegister.SetHigh(0x33); + _indexRegister.IncrementLow(); + _indexRegister.Low.Should().Be(expectedValue); + _indexRegister.High.Should().Be(0x33); + + CheckFlags(true, halfCarryStatus, value, expectedValue); + } + + private void CheckFlags(bool increment, bool halfCarryStatus, byte value, byte expectedValue) + { + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, halfCarryStatus); + if (increment) + { + _flags.Received(1).SetIfIncrementOverflow(value); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + else + { + _flags.Received(1).SetIfDecrementOverflow(value); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + } + } + + [Test] + [TestCase(0x02, 0x01, false)] + [TestCase(0x10, 0x0F, true)] + [TestCase(0x11, 0x10, false)] + [TestCase(0xFF, 0xFE, false)] + [TestCase(0x00, 0xFF, true)] + public void WhenDecrementHighByteThenHighByteIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _indexRegister.SetHigh(value); + _indexRegister.SetLow(0x12); + _indexRegister.DecrementHigh(); + _indexRegister.High.Should().Be(expectedValue); + _indexRegister.Low.Should().Be(0x12); + + CheckFlags(false, halfCarryStatus, value, expectedValue); + } + + [Test] + [TestCase(0x02, 0x01, false)] + [TestCase(0x10, 0x0F, true)] + [TestCase(0x11, 0x10, false)] + [TestCase(0xFF, 0xFE, false)] + [TestCase(0x00, 0xFF, true)] + public void WhenDecrementLowByteThenLowByteIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _indexRegister.SetLow(value); + _indexRegister.SetHigh(0x33); + _indexRegister.DecrementLow(); + _indexRegister.Low.Should().Be(expectedValue); + _indexRegister.High.Should().Be(0x33); + + CheckFlags(false, halfCarryStatus, value, expectedValue); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs similarity index 98% rename from Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs rename to Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs index 6b1b316..e5589bf 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80ProgramCounterFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs @@ -6,7 +6,7 @@ using NSubstitute; using NUnit.Framework; -namespace Kmse.Core.UnitTests.Z80CpuTests; +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; public class Z80ProgramCounterFixture { diff --git a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs similarity index 98% rename from Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs rename to Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs index 59a0daf..39bd7b4 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs @@ -7,7 +7,7 @@ using NSubstitute; using NUnit.Framework; -namespace Kmse.Core.UnitTests.Z80CpuTests; +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; public class Z80StackManagementFixture { diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RunningStateTests/Z80CpuRunningStateManagerFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RunningStateTests/Z80CpuRunningStateManagerFixture.cs new file mode 100644 index 0000000..a481b15 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RunningStateTests/Z80CpuRunningStateManagerFixture.cs @@ -0,0 +1,42 @@ +using FluentAssertions; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Running; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RunningStateTests; + +public class Z80CpuRunningStateManagerFixture +{ + private Z80CpuRunningStateManager _cpuRunningState; + + [SetUp] + public void Setup() + { + _cpuRunningState = new Z80CpuRunningStateManager(Substitute.For()); + } + + [Test] + public void WhenHaltIsCalledThenRunningStateIsHalted() + { + _cpuRunningState.Reset(); + _cpuRunningState.Halt(); + _cpuRunningState.Halted.Should().BeTrue(); + } + + [Test] + public void WhenResetThenNotHalted() + { + _cpuRunningState.Halt(); + _cpuRunningState.Reset(); + _cpuRunningState.Halted.Should().BeFalse(); + } + + [Test] + public void WhenResumeIfHaltedThenNotHalted() + { + _cpuRunningState.Halt(); + _cpuRunningState.ResumeIfHalted(); + _cpuRunningState.Halted.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs b/Kmse.Core/Z80/IO/IZ80CpuInputOutputManager.cs similarity index 89% rename from Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs rename to Kmse.Core/Z80/IO/IZ80CpuInputOutputManager.cs index 28334b1..da54921 100644 --- a/Kmse.Core/Z80/IO/IZ80CpuInputOutput.cs +++ b/Kmse.Core/Z80/IO/IZ80CpuInputOutputManager.cs @@ -2,7 +2,7 @@ namespace Kmse.Core.Z80.IO; -public interface IZ80CpuInputOutput +public interface IZ80CpuInputOutputManager { byte Read(byte high, byte low, bool setFlags); void ReadAndSetRegister(byte high, byte low, IZ808BitRegister register); diff --git a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs b/Kmse.Core/Z80/IO/Z80CpuInputOutputManager.cs similarity index 87% rename from Kmse.Core/Z80/IO/Z80CpuInputOutput.cs rename to Kmse.Core/Z80/IO/Z80CpuInputOutputManager.cs index 39551b8..0d58dc0 100644 --- a/Kmse.Core/Z80/IO/Z80CpuInputOutput.cs +++ b/Kmse.Core/Z80/IO/Z80CpuInputOutputManager.cs @@ -6,12 +6,12 @@ namespace Kmse.Core.Z80.IO; -public class Z80CpuInputOutput : IZ80CpuInputOutput +public class Z80CpuInputOutputManager : IZ80CpuInputOutputManager { private readonly IZ80FlagsManager _flags; private readonly IMasterSystemIoManager _io; - public Z80CpuInputOutput(IMasterSystemIoManager io, IZ80FlagsManager flags) + public Z80CpuInputOutputManager(IMasterSystemIoManager io, IZ80FlagsManager flags) { _io = io; _flags = flags; @@ -27,7 +27,6 @@ public byte Read(byte high, byte low, bool setFlags) return data; } - // If high bit set, then negative so set sign flag _flags.SetIfNegative(data); _flags.SetIfZero(data); diff --git a/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs index cafb08c..5684da3 100644 --- a/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs +++ b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs @@ -36,7 +36,7 @@ public class Z80CpuInstructions : IZ80CpuInstructions private readonly IZ80IndexRegisterXy _iy; private readonly IZ80MemoryRefreshRegister _rRegister; private readonly IZ80InterruptPageAddressRegister _iRegister; - private readonly IZ80CpuInputOutput _ioManagement; + private readonly IZ80CpuInputOutputManager _ioManagement; private readonly IZ80CpuMemoryManagement _memoryManagement; private readonly IZ80InterruptManagement _interruptManagement; private readonly IZ80CpuCycleCounter _cycleCounter; diff --git a/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs index 25f80ff..6d4b3c2 100644 --- a/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs +++ b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs @@ -58,6 +58,11 @@ public bool InterruptWaiting() public void SetMaskableInterrupt() { + if (!InterruptEnableFlipFlopStatus) + { + // Can only set if interrupts are enabled + return; + } MaskableInterrupt = true; _cpuLogger.SetMaskableInterruptStatus(MaskableInterrupt); } @@ -84,12 +89,14 @@ public void EnableMaskableInterrupts() { InterruptEnableFlipFlopStatus = true; InterruptEnableFlipFlopTempStorageStatus = true; + ClearMaskableInterrupt(); } public void DisableMaskableInterrupts() { InterruptEnableFlipFlopStatus = false; InterruptEnableFlipFlopTempStorageStatus = false; + ClearMaskableInterrupt(); } public void SetInterruptMode(byte mode) @@ -147,8 +154,7 @@ private int ProcessMaskableInterrupt() _cpuLogger.LogInstruction(_programCounter.Value, "MI", "Maskable Interrupt", "Maskable Interrupt", $"Mode {InterruptMode}"); - InterruptEnableFlipFlopStatus = false; - InterruptEnableFlipFlopTempStorageStatus = false; + DisableMaskableInterrupts(); if (InterruptMode is 0 or 1) { @@ -172,7 +178,6 @@ private int ProcessMaskableInterrupt() // Maybe it is used but with random values returned? //https://www.smspower.org/uploads/Development/smstech-20021112.txt _cpuLogger.Error("Maskable Interrupt while in mode 2 which is not supported"); - ClearMaskableInterrupt(); // Treat this as a NOP which is 4 cycles return 4; diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs index 54e8240..6826dee 100644 --- a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -132,9 +132,4 @@ public void SetIfHalfCarry(byte currentValue, byte operand, int changedValue) //https://retrocomputing.stackexchange.com/questions/4693/why-does-the-z80-have-a-half-carry-bit SetClearFlagConditional(Z80StatusFlags.HalfCarryH, ((currentValue ^ changedValue ^ operand) & 0x10) != 0); } - - public void SetIfHalfCarry(ushort currentValue, ushort operand) - { - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (currentValue & 0x0FFF) + (operand & 0x0FFF) > 0x0FFF); - } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs index 1974b7c..5a49e63 100644 --- a/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -1,5 +1,4 @@ using Kmse.Core.Memory; -using Kmse.Core.Utilities; using Kmse.Core.Z80.Model; using Kmse.Core.Z80.Registers.General; diff --git a/Kmse.Core/Z80/Z80CpuManagement.cs b/Kmse.Core/Z80/Z80CpuManagement.cs index 4b806b5..d13e6b4 100644 --- a/Kmse.Core/Z80/Z80CpuManagement.cs +++ b/Kmse.Core/Z80/Z80CpuManagement.cs @@ -8,7 +8,7 @@ namespace Kmse.Core.Z80; public class Z80CpuManagement { - public IZ80CpuInputOutput IoManagement { get; set; } + public IZ80CpuInputOutputManager IoManagement { get; set; } public IZ80CpuMemoryManagement MemoryManagement { get; set; } public IZ80InterruptManagement InterruptManagement { get; set; } public IZ80CpuCycleCounter CycleCounter { get; set; } diff --git a/Kmse.Core/Z80/Z80CpuModule.cs b/Kmse.Core/Z80/Z80CpuModule.cs index be248ba..74563f2 100644 --- a/Kmse.Core/Z80/Z80CpuModule.cs +++ b/Kmse.Core/Z80/Z80CpuModule.cs @@ -39,7 +39,7 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); From 4583f1dc6c584611cc415fd9d9ed764278085a11 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 24 Sep 2022 09:49:02 +1000 Subject: [PATCH 16/21] Add unit test for Z808BitGeneralPurposeRegister --- .../Kmse.Core.UnitTests.csproj | 4 - .../Z808BitGeneralPurposeRegisterFixture.cs | 261 ++++++++++++++++++ Kmse.Core/Z80/Registers/Z80RegisterBase.cs | 5 +- 3 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs diff --git a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj index a0a8c9e..49ec504 100644 --- a/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj +++ b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj @@ -27,8 +27,4 @@ - - - - diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs new file mode 100644 index 0000000..260f8fa --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs @@ -0,0 +1,261 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class Z808BitGeneralPurposeRegisterFixture +{ + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _register = new Z808BitGeneralPurposeRegister(_memory, _flags); + } + + private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; + private Z808BitGeneralPurposeRegister _register; + + [Test] + [TestCase(0x01, 0x02, false)] + [TestCase(0x0F, 0x10, true)] + [TestCase(0x10, 0x11, false)] + [TestCase(0xFE, 0xFF, false)] + [TestCase(0xFF, 0x00, true)] + public void WhenIncrementThenValueIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _register.Set(value); + _register.Increment(); + _register.Value.Should().Be(expectedValue); + + CheckFlags(true, halfCarryStatus, value, expectedValue); + } + + [Test] + [TestCase(0x02, 0x01, false)] + [TestCase(0x10, 0x0F, true)] + [TestCase(0x11, 0x10, false)] + [TestCase(0xFF, 0xFE, false)] + [TestCase(0x00, 0xFF, true)] + public void WhenDecrementHighByteThenHighByteIncrementedAndFlagsSet(byte value, byte expectedValue, + bool halfCarryStatus) + { + _register.Set(value); + _register.Decrement(); + _register.Value.Should().Be(expectedValue); + + CheckFlags(false, halfCarryStatus, value, expectedValue); + } + + private void CheckFlags(bool increment, bool halfCarryStatus, byte value, byte expectedValue) + { + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, halfCarryStatus); + if (increment) + { + _flags.Received(1).SetIfIncrementOverflow(value); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + } + else + { + _flags.Received(1).SetIfDecrementOverflow(value); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + } + } + + [Test] + public void WhenClearingBitThenBitCleared() + { + for (var i = 0; i < 8; i++) + { + _register.Set(0xFF); + _register.ClearBit(i); + // Set the bit manually and then invert to get the cleared bit + _register.Value.Should().Be((byte)~(1 << i)); + } + } + + [Test] + [TestCase(-1)] + [TestCase(8)] + public void WhenClearingBitOutsideRangeThenExceptionThrown(int bit) + { + var action = () => _register.ClearBit(bit); + action.Should().Throw(); + } + + [Test] + public void WhenSettingBitThenBitSet() + { + for (var i = 0; i < 8; i++) + { + _register.Set(0x00); + _register.SetBit(i); + // Set the bit manually and then invert to get the cleared bit + _register.Value.Should().Be((byte)(1 << i)); + } + } + + [Test] + [TestCase(-1)] + [TestCase(8)] + public void WhenSettingBitOutsideRangeThenExceptionThrown(int bit) + { + var action = () => _register.SetBit(bit); + action.Should().Throw(); + } + + [Test] + [TestCase(0, 0, false)] + [TestCase(0xAA, (byte)(((0xAA << 1) & 0xFF) + 1), true)] + [TestCase(0x55, (byte)((0x55 << 1) & 0xFF), false)] + public void WhenRotatingLeftCircularThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.RotateLeftCircular(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0, false, false)] + [TestCase(0x55, (byte)((0x55 << 1) & 0xFF), false, false)] + [TestCase(0x55, (byte)(((0x55 << 1) & 0xFF) + 1), true, false)] + [TestCase(0xAA, (byte)((0xAA << 1) & 0xFF), false, true)] + [TestCase(0xAA, (byte)(((0xAA << 1) & 0xFF) + 1), true, true)] + [TestCase(0xFF, 0xFE, false, true)] + [TestCase(0xFF, 0xFF, true, true)] + public void WhenRotatingLeftThenMatchesExpectedValue(byte value, byte expectedValue, bool currentCarryFlagStatus, + bool expectedCarryFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(currentCarryFlagStatus); + _register.Set(value); + _register.RotateLeft(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0, false)] + [TestCase(0x01, 0x80, true)] + [TestCase(0xAA, (byte)((0xAA >> 1) & 0xFF), false)] + [TestCase(0x55, (byte)(((0x55 >> 1) & 0xFF) + 0x80), true)] + [TestCase(0xFF, 0xFF, true)] + public void WhenRotatingRightCircularThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.RotateRightCircular(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0, false, false)] + [TestCase(0, 0x80, true, false)] + [TestCase(0x01, 0x00, false, true)] + [TestCase(0x01, 0x80, true, true)] + [TestCase(0x55, (byte)((0x55 >> 1) & 0xFF), false, true)] + [TestCase(0x55, (byte)(((0x55 >> 1) & 0xFF) + 0x80), true, true)] + [TestCase(0xAA, (byte)((0xAA >> 1) & 0xFF), false, false)] + [TestCase(0xAA, (byte)(((0xAA >> 1) & 0xFF) + 0x80), true, false)] + [TestCase(0xFF, 0x7F, false, true)] + [TestCase(0xFF, 0xFF, true, true)] + public void WhenRotatingRightThenMatchesExpectedValue(byte value, byte expectedValue, bool currentCarryFlagStatus, + bool expectedCarryFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(currentCarryFlagStatus); + _register.Set(value); + _register.RotateRight(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + + [Test] + [TestCase(0, 0, false)] + [TestCase(0x55, (byte)((0x55 << 1) & 0xFF), false)] + [TestCase(0xAA, (byte)((0xAA << 1) & 0xFF), true)] + [TestCase(0xFF, 0xFE, true)] + public void WhenShiftingLeftArithmeticThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.ShiftLeftArithmetic(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0, false)] + [TestCase(0x55, (byte)((0x55 >> 1) & 0xFF), true)] + [TestCase(0xAA, (byte)(((0xAA >> 1) & 0xFF) + 0x80), false)] + [TestCase(0xFF, 0xFF, true)] + public void WhenShiftingRightArithmeticThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.ShiftRightArithmetic(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0x01, false)] + [TestCase(0x01, 0x03, false)] + [TestCase(0x55, (byte)(((0x55 << 1) & 0xFF) + 1), false)] + [TestCase(0xAA, (byte)(((0xAA << 1) & 0xFF) + 1), true)] + [TestCase(0xFF, 0xFF, true)] + public void WhenShiftingLeftLogicalThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.ShiftLeftLogical(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + [Test] + [TestCase(0, 0, false)] + [TestCase(0x01, 0x00, true)] + [TestCase(0x80, 0x40, false)] + [TestCase(0x55, (byte)((0x55 >> 1) & 0xFF), true)] + [TestCase(0xAA, (byte)((0xAA >> 1) & 0xFF), false)] + [TestCase(0xFF, 0x7F, true)] + public void WhenShiftingRightLogicalThenMatchesExpectedValue(byte value, byte expectedValue, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.ShiftRightLogical(); + + _register.Value.Should().Be(expectedValue); + CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); + } + + private void CheckFlagsForBitShifting(byte expectedValue, bool expectedCarryFlagStatus) + { + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs index e432b39..287ae54 100644 --- a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -117,13 +117,12 @@ protected void ShiftLeftArithmetic(ref byte register) protected void ShiftRightArithmetic(ref byte register) { // Rotate register right by 1 bit, bit 0 is copied to carry flag - // This is special method since RRA flags are set differently to RR r instruction var newValue = (byte)(register >> 1); var bit7Set = Bitwise.IsSet(register, 7); + // If bit 7 is set in original then leave as set even as we shift right if (bit7Set) { - // If bit 7 is set in original then leave as set even as we shift right Bitwise.Set(ref newValue, 7); } @@ -161,7 +160,7 @@ protected void ShiftRightLogical(ref byte register) // This is special method since RRA flags are set differently to RR r instruction var newValue = (byte)(register >> 1); - // The difference between shift right logical and shift right arithmetic is this does maintain bit 7 when shifting and just clears it + // The difference between shift right logical and shift right arithmetic is arithmetic always maintains bit 7 when shifting and logical shifting always clears bit 7 Bitwise.Clear(ref newValue, 7); Flags.SetIfNegative(newValue); From 824828a2993a5dce6b11aa265c5256c42dc606e8 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 24 Sep 2022 11:12:37 +1000 Subject: [PATCH 17/21] Add unit tests for 16 bit general purpose registers, 16 bit special purpose register base, 8 bit register base class, flags register and 16 bit Fixed issues where shadow value for 8 bit registers was not being cleared when a reset was called Renamed method for set if negative for twos complement to be clearer since special case --- Kmse.Console/Properties/launchSettings.json | 2 + ...BcZ8016BitGeneralPurposeRegisterFixture.cs | 16 + ...DeZ8016BitGeneralPurposeRegisterFixture.cs | 16 + ...HlZ8016BitGeneralPurposeRegisterFixture.cs | 32 ++ .../GeneralPurpose/TestZ808BitRegister.cs | 10 + .../Z8016BitGeneralPurposeRegisterBaseTest.cs | 135 ++++++++ .../GeneralPurpose/Z808BitRegisterFixture.cs | 112 +++++++ .../GeneralPurpose/Z80FlagsManagerFixture.cs | 311 ++++++++++++++++++ .../TestZ8016BitSpecialRegisterBase.cs | 10 + .../Z8016BitSpecialRegisterFixture.cs | 108 ++++++ .../Z80StackManagementFixture.cs | 8 +- .../Z80/Registers/General/IZ80FlagsManager.cs | 2 +- .../Z80/Registers/General/Z80Accumulator.cs | 2 +- .../Z80/Registers/General/Z80FlagsManager.cs | 6 +- Kmse.Core/Z80/Registers/Z808BitRegister.cs | 1 + 15 files changed, 765 insertions(+), 6 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/BcZ8016BitGeneralPurposeRegisterFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/DeZ8016BitGeneralPurposeRegisterFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/HlZ8016BitGeneralPurposeRegisterFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/TestZ808BitRegister.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z8016BitGeneralPurposeRegisterBaseTest.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitRegisterFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80FlagsManagerFixture.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitSpecialRegisterBase.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitSpecialRegisterFixture.cs diff --git a/Kmse.Console/Properties/launchSettings.json b/Kmse.Console/Properties/launchSettings.json index 7fac0d6..11c2703 100644 --- a/Kmse.Console/Properties/launchSettings.json +++ b/Kmse.Console/Properties/launchSettings.json @@ -3,6 +3,8 @@ "Kmse.Console": { "commandName": "Project", "commandLineArgs": "-f ..\\..\\..\\Validation\\zexdoc.sms" + //"commandLineArgs": "-f ..\\..\\..\\..\\..\\zexall-sms\\source\\zexdoc.sms" + //"commandLineArgs": "-f ..\\..\\..\\..\\..\\sonic.sms" } } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/BcZ8016BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/BcZ8016BitGeneralPurposeRegisterFixture.cs new file mode 100644 index 0000000..f0c1659 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/BcZ8016BitGeneralPurposeRegisterFixture.cs @@ -0,0 +1,16 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class BcZ8016BitGeneralPurposeRegisterFixture : Z8016BitGeneralPurposeRegisterBaseTest +{ + protected override Z8016BitGeneralPurposeRegisterBase CreateRegister(IMasterSystemMemory memory, + IZ80FlagsManager flags) + { + return new Z80BcRegister(memory, flags, () => new Z808BitGeneralPurposeRegister(memory, flags)); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/DeZ8016BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/DeZ8016BitGeneralPurposeRegisterFixture.cs new file mode 100644 index 0000000..70636a1 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/DeZ8016BitGeneralPurposeRegisterFixture.cs @@ -0,0 +1,16 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class DeZ8016BitGeneralPurposeRegisterFixture : Z8016BitGeneralPurposeRegisterBaseTest +{ + protected override Z8016BitGeneralPurposeRegisterBase CreateRegister(IMasterSystemMemory memory, + IZ80FlagsManager flags) + { + return new Z80DeRegister(memory, flags, () => new Z808BitGeneralPurposeRegister(memory, flags)); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/HlZ8016BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/HlZ8016BitGeneralPurposeRegisterFixture.cs new file mode 100644 index 0000000..ef4090c --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/HlZ8016BitGeneralPurposeRegisterFixture.cs @@ -0,0 +1,32 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class HlZ8016BitGeneralPurposeRegisterFixture : Z8016BitGeneralPurposeRegisterBaseTest +{ + protected override Z8016BitGeneralPurposeRegisterBase CreateRegister(IMasterSystemMemory memory, + IZ80FlagsManager flags) + { + return new Z80HlRegister(memory, flags, () => new Z808BitGeneralPurposeRegister(memory, flags)); + } + + [Test] + public void WhenSwappingWithDeRegister() + { + var deRegister = Substitute.For(); + deRegister.Value.Returns((ushort)0x1234); + + Register.Set(0x3426); + + ((Z80HlRegister)Register).SwapWithDeRegister(deRegister); + + Register.Value.Should().Be(0x1234); + deRegister.Received(1).Set(0x3426); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/TestZ808BitRegister.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/TestZ808BitRegister.cs new file mode 100644 index 0000000..cafed47 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/TestZ808BitRegister.cs @@ -0,0 +1,10 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +public class TestZ808BitRegister : Z808BitRegister +{ + public TestZ808BitRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z8016BitGeneralPurposeRegisterBaseTest.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z8016BitGeneralPurposeRegisterBaseTest.cs new file mode 100644 index 0000000..ba72ed6 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z8016BitGeneralPurposeRegisterBaseTest.cs @@ -0,0 +1,135 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +public abstract class Z8016BitGeneralPurposeRegisterBaseTest +{ + private IZ80FlagsManager _flags; + + private IMasterSystemMemory _memory; + protected Z8016BitGeneralPurposeRegisterBase Register; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + Register = CreateRegister(_memory, _flags); + } + + protected abstract Z8016BitGeneralPurposeRegisterBase CreateRegister(IMasterSystemMemory memory, + IZ80FlagsManager flags); + + [Test] + public void WhenSettingValueThenValueIsUpdated() + { + Register.Set(0x0522); + Register.Value.Should().Be(0x0522); + } + + [Test] + public void WhenSettingValueFromRegisterThenValueIsUpdated() + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x0412); + Register.Set(register); + Register.Value.Should().Be(0x0412); + } + + [Test] + public void WhenGetValueAsUnsigned16BitValue() + { + Register.Set(0x0522); + var value = Register.AsUnsigned16BitValue(); + value.Word.Should().Be(0x0522); + value.Low.Should().Be(0x22); + value.High.Should().Be(0x05); + } + + [Test] + public void WhenSettingValueFromDataInMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x1235].Returns((byte)0x34); + _memory[0x1236].Returns((byte)0x12); + Register.Set(0x0011); + Register.SetFromDataInMemory(0x1234, 1); + Register.Value.Should().Be(0x1234); + } + + [Test] + public void WhenSettingValueFrom16RegisterPointerToMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x2233].Returns((byte)0x13); + _memory[0x2234].Returns((byte)0x03); + var register = Substitute.For(); + register.Value.Returns((ushort)0x2231); + + Register.Set(0x0511); + Register.SetFromDataInMemory(register, 2); + Register.Value.Should().Be(0x0313); + } + + [Test] + public void WhenSavingToMemoryLocationByAddressThenMemoryIsWritten() + { + Register.Set(0x1234); + Register.SaveToMemory(0x1235, 5); + _memory.Received()[0x123A] = 0x34; + _memory.Received()[0x123B] = 0x12; + } + + [Test] + public void WhenResettingRegisterThenValueIsZero() + { + Register.Set(0x3457); + Register.SwapWithShadow(); + Register.Set(0x1234); + Register.Reset(); + Register.Value.Should().Be(0x0000); + Register.ShadowValue.Should().Be(0x0000); + } + + [Test] + public void WhenSwappingWithShadowRegister() + { + Register.Set(0x3457); + Register.SwapWithShadow(); + Register.Set(0x1234); + Register.Value.Should().Be(0x1234); + Register.ShadowValue.Should().Be(0x3457); + } + + [Test] + public void WhenSwappingBackWithShadowRegister() + { + Register.Set(0x3457); + Register.SwapWithShadow(); + Register.Set(0x1234); + Register.SwapWithShadow(); + Register.Value.Should().Be(0x3457); + Register.ShadowValue.Should().Be(0x1234); + } + + [Test] + public void WhenIncrementingValueThenValueIsIncrementedAndFlagsUnchanged() + { + Register.Set(0x1234); + Register.Increment(); + Register.Value.Should().Be(0x1235); + _flags.ReceivedCalls().Should().BeEmpty(); + } + + [Test] + public void WhenDecrementingValueThenValueIsDecrementedAndFlagsUnchanged() + { + Register.Set(0x1234); + Register.Decrement(); + Register.Value.Should().Be(0x1233); + _flags.ReceivedCalls().Should().BeEmpty(); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitRegisterFixture.cs new file mode 100644 index 0000000..8abbcd0 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitRegisterFixture.cs @@ -0,0 +1,112 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class Z808BitRegisterFixture +{ + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _register = new TestZ808BitRegister(_memory, _flags); + } + + private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; + private TestZ808BitRegister _register; + + [Test] + public void WhenSettingValueThenValueIsUpdated() + { + _register.Set(0x05); + _register.Value.Should().Be(0x05); + } + + [Test] + public void WhenSettingValueFromRegisterThenValueIsUpdated() + { + var register = Substitute.For(); + register.Value.Returns((byte)0x04); + _register.Set(register); + _register.Value.Should().Be(0x04); + } + + [Test] + public void WhenSettingValueFromDataInMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x1235].Returns((byte)0x12); + _register.Set(0x05); + _register.SetFromDataInMemory(0x1234, 1); + _register.Value.Should().Be(0x12); + } + + [Test] + public void WhenSettingValueFrom16RegisterPointerToMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x2233].Returns((byte)0x13); + var register = Substitute.For(); + register.Value.Returns((ushort)0x2231); + + _register.Set(0x05); + _register.SetFromDataInMemory(register, 2); + _register.Value.Should().Be(0x13); + } + + [Test] + public void WhenSavingToMemoryLocationByAddressThenMemoryIsWritten() + { + _register.Set(0x23); + _register.SaveToMemory(0x1235, 5); + _memory.Received()[0x123A] = 0x23; + } + + [Test] + public void WhenSavingToMemoryLocationBy16BitRegisterAddressThenMemoryIsWritten() + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x2231); + + _register.Set(0x43); + _register.SaveToMemory(register, 2); + _memory.Received()[0x2233] = 0x43; + } + + [Test] + public void WhenResettingRegisterThenValueIsZero() + { + _register.Set(0x05); + _register.SwapWithShadow(); + _register.Set(0x01); + _register.Reset(); + _register.Value.Should().Be(0x00); + _register.ShadowValue.Should().Be(0x00); + } + + [Test] + public void WhenSwappingValueWithShadow() + { + _register.Set(0x05); + _register.SwapWithShadow(); + _register.Value.Should().Be(0x00); + _register.ShadowValue.Should().Be(0x05); + } + + [Test] + public void WhenSwappingValueBackFromShadow() + { + _register.Set(0x12); + _register.SwapWithShadow(); + _register.Set(0x33); + _register.SwapWithShadow(); + + _register.Value.Should().Be(0x12); + _register.ShadowValue.Should().Be(0x33); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80FlagsManagerFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80FlagsManagerFixture.cs new file mode 100644 index 0000000..7d0a67b --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80FlagsManagerFixture.cs @@ -0,0 +1,311 @@ +using FluentAssertions; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class Z80FlagsManagerFixture +{ + [SetUp] + public void Setup() + { + _register = new Z80FlagsManager(); + } + + private Z80FlagsManager _register; + + [Test] + public void WhenSettingValueThenValueIsUpdated() + { + _register.Set(0x05); + _register.Value.Should().Be(0x05); + } + + [Test] + public void WhenResettingRegisterThenValueIsZero() + { + _register.Set(0x05); + _register.SwapWithShadow(); + _register.Set(0x01); + _register.Reset(); + _register.Value.Should().Be(0x00); + _register.ShadowValue.Should().Be(0x00); + } + + [Test] + public void WhenSwappingValueWithShadow() + { + _register.Set(0x05); + _register.SwapWithShadow(); + _register.Value.Should().Be(0x00); + _register.ShadowValue.Should().Be(0x05); + } + + [Test] + public void WhenSwappingValueBackFromShadow() + { + _register.Set(0x12); + _register.SwapWithShadow(); + _register.Set(0x33); + _register.SwapWithShadow(); + + _register.Value.Should().Be(0x12); + _register.ShadowValue.Should().Be(0x33); + } + + [Test] + [TestCase(1 << 0, Z80StatusFlags.CarryC)] + [TestCase(1 << 1, Z80StatusFlags.AddSubtractN)] + [TestCase(1 << 2, Z80StatusFlags.ParityOverflowPV)] + [TestCase(1 << 3, Z80StatusFlags.NotUsedX3)] + [TestCase(1 << 4, Z80StatusFlags.HalfCarryH)] + [TestCase(1 << 5, Z80StatusFlags.NotUsedX5)] + [TestCase(1 << 6, Z80StatusFlags.ZeroZ)] + [TestCase(1 << 7, Z80StatusFlags.SignS)] + [TestCase(0xAA, + Z80StatusFlags.AddSubtractN | Z80StatusFlags.NotUsedX3 | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.SignS)] + [TestCase(0xFF, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS)] + public void WhenSettingFlagThenValueIsUpdatedWithExpectedValue(byte expectedValue, Z80StatusFlags flags) + { + _register.SetFlag(flags); + _register.Value.Should().Be(expectedValue); + } + + [Test] + // We test by setting all flags and then validating the correct bit was cleared + // To do this we take the bit, bit shift it up to the correct spot + // and then do a NOT (~) to set all bits except the one we want to ensure cleared + [TestCase(unchecked((byte)~(1 << 0)), Z80StatusFlags.CarryC)] + [TestCase(unchecked((byte)~(1 << 1)), Z80StatusFlags.AddSubtractN)] + [TestCase(unchecked((byte)~(1 << 2)), Z80StatusFlags.ParityOverflowPV)] + [TestCase(unchecked((byte)~(1 << 3)), Z80StatusFlags.NotUsedX3)] + [TestCase(unchecked((byte)~(1 << 4)), Z80StatusFlags.HalfCarryH)] + [TestCase(unchecked((byte)~(1 << 5)), Z80StatusFlags.NotUsedX5)] + [TestCase(unchecked((byte)~(1 << 6)), Z80StatusFlags.ZeroZ)] + [TestCase(unchecked((byte)~(1 << 7)), Z80StatusFlags.SignS)] + [TestCase(unchecked((byte)~0xAA), + Z80StatusFlags.AddSubtractN | Z80StatusFlags.NotUsedX3 | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.SignS)] + [TestCase(0x00, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS)] + public void WhenClearingFlagThenValueIsUpdatedWithExpectedValue(byte expectedValue, Z80StatusFlags flags) + { + _register.Set(0xFF); + _register.ClearFlag(flags); + _register.Value.Should().Be(expectedValue); + } + + [Test] + [TestCase(unchecked((byte)~(1 << 0)), Z80StatusFlags.CarryC)] + [TestCase(unchecked((byte)~(1 << 1)), Z80StatusFlags.AddSubtractN)] + [TestCase(unchecked((byte)~(1 << 2)), Z80StatusFlags.ParityOverflowPV)] + [TestCase(unchecked((byte)~(1 << 3)), Z80StatusFlags.NotUsedX3)] + [TestCase(unchecked((byte)~(1 << 4)), Z80StatusFlags.HalfCarryH)] + [TestCase(unchecked((byte)~(1 << 5)), Z80StatusFlags.NotUsedX5)] + [TestCase(unchecked((byte)~(1 << 6)), Z80StatusFlags.ZeroZ)] + [TestCase(unchecked((byte)~(1 << 7)), Z80StatusFlags.SignS)] + [TestCase(unchecked((byte)~0xAA), + Z80StatusFlags.AddSubtractN | Z80StatusFlags.NotUsedX3 | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.SignS)] + [TestCase(0x00, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS)] + public void WhenInvertingSetFlagThenValueIsUpdatedWithExpectedValue(byte expectedValue, Z80StatusFlags flags) + { + // In this case, if we set all the flags and then invert a specific flag it's the same as clearing the flag + _register.Set(0xFF); + _register.InvertFlag(flags); + _register.Value.Should().Be(expectedValue); + } + + [Test] + [TestCase(1 << 0, Z80StatusFlags.CarryC)] + [TestCase(1 << 1, Z80StatusFlags.AddSubtractN)] + [TestCase(1 << 2, Z80StatusFlags.ParityOverflowPV)] + [TestCase(1 << 3, Z80StatusFlags.NotUsedX3)] + [TestCase(1 << 4, Z80StatusFlags.HalfCarryH)] + [TestCase(1 << 5, Z80StatusFlags.NotUsedX5)] + [TestCase(1 << 6, Z80StatusFlags.ZeroZ)] + [TestCase(1 << 7, Z80StatusFlags.SignS)] + [TestCase(0xAA, + Z80StatusFlags.AddSubtractN | Z80StatusFlags.NotUsedX3 | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.SignS)] + [TestCase(0xFF, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS)] + public void WhenInvertingClearedFlagThenValueIsUpdatedWithExpectedValue(byte expectedValue, Z80StatusFlags flags) + { + // In this case, if we clear all the flags and then invert a specific flag it's the same as setting the flag + _register.Set(0x00); + _register.InvertFlag(flags); + _register.Value.Should().Be(expectedValue); + } + + [Test] + public void WhenSettingOrClearingFlagOnConditionalTrueThenFlagIsSet() + { + _register.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, true); + _register.Value.Should().Be(1 << 2); + } + + [Test] + public void WhenSettingOrClearingFlagOnConditionalFalseThenFlagIsCleared() + { + _register.Set(0xFF); + _register.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, false); + _register.Value.Should().Be(unchecked((byte)~(1 << 2))); + } + + [Test] + [TestCase(0, Z80StatusFlags.CarryC | Z80StatusFlags.NotUsedX5, false)] + [TestCase(0x01, Z80StatusFlags.CarryC, true)] + // False since only 1 flag is set not both + [TestCase(0x01, Z80StatusFlags.CarryC | Z80StatusFlags.HalfCarryH, false)] + [TestCase(0x81, Z80StatusFlags.CarryC | Z80StatusFlags.SignS, true)] + [TestCase(0xFE, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS, false)] + [TestCase(0xFF, + Z80StatusFlags.CarryC | Z80StatusFlags.AddSubtractN | Z80StatusFlags.ParityOverflowPV | + Z80StatusFlags.NotUsedX3 | Z80StatusFlags.HalfCarryH | Z80StatusFlags.NotUsedX5 | Z80StatusFlags.ZeroZ | + Z80StatusFlags.SignS, true)] + public void WhenCheckingIfSetFlagThenReturnExpectedResult(byte value, Z80StatusFlags flags, bool expectedResult) + { + _register.Set(value); + _register.IsFlagSet(flags).Should().Be(expectedResult); + } + + [Test] + [TestCase(0x00, true)] + [TestCase(0x01, false)] + [TestCase(0x03, true)] + [TestCase(0xAB, false)] + [TestCase(0xFE, false)] + [TestCase(0xFF, true)] + public void WhenSettingParityFromValueThenFlagIsSetIfBitsEven(byte value, bool parityFlagSet) + { + _register.SetParityFromValue(value); + _register.IsFlagSet(Z80StatusFlags.ParityOverflowPV).Should().Be(parityFlagSet); + } + + [Test] + [TestCase(0x00, true)] + [TestCase(0x01, false)] + [TestCase(0xFF, false)] + public void WhenSettingZeroFlagFromByteValueThenFlagIsSetIfValueZero(byte value, bool flagSet) + { + _register.SetIfZero(value); + _register.IsFlagSet(Z80StatusFlags.ZeroZ).Should().Be(flagSet); + } + + [Test] + [TestCase((ushort)0x0000, true)] + [TestCase((ushort)0x0001, false)] + [TestCase((ushort)0x00FF, false)] + [TestCase((ushort)0xFFFF, false)] + public void WhenSettingZeroFlagFromUnsignedShortValueThenFlagIsSetIfValueZero(ushort value, bool flagSet) + { + _register.SetIfZero(value); + _register.IsFlagSet(Z80StatusFlags.ZeroZ).Should().Be(flagSet); + } + + [Test] + [TestCase(0x0000, true)] + [TestCase(0x0001, false)] + [TestCase(0x0000FF, false)] + [TestCase(0xFFFFFF, false)] + public void WhenSettingZeroFlagFromIntValueThenFlagIsSetIfValueZero(int value, bool flagSet) + { + _register.SetIfZero(value); + _register.IsFlagSet(Z80StatusFlags.ZeroZ).Should().Be(flagSet); + } + + [Test] + [TestCase(0x00, false)] + [TestCase(0x01, false)] + [TestCase(0x80, true)] + [TestCase(0x81, true)] + [TestCase(0xFF, true)] + public void WhenSettingSignFlagFromByteValueThenFlagIsSetIfValueNegative(byte value, bool flagSet) + { + _register.SetIfNegative(value); + _register.IsFlagSet(Z80StatusFlags.SignS).Should().Be(flagSet); + } + + [Test] + [TestCase((ushort)0x0000, false)] + [TestCase((ushort)0x0001, false)] + [TestCase((ushort)0x0080, false)] + [TestCase((ushort)0x00FF, false)] + [TestCase((ushort)0x8000, true)] + [TestCase((ushort)0x8001, true)] + [TestCase((ushort)0xFFFF, true)] + public void WhenSettingSignFlagFromUnsignedShortValueThenFlagIsSetIfValueNegative(ushort value, bool flagSet) + { + _register.SetIfNegative(value); + _register.IsFlagSet(Z80StatusFlags.SignS).Should().Be(flagSet); + } + + [Test] + [TestCase(0x0000, false)] + [TestCase(0x0001, false)] + [TestCase(0x0080, true)] + [TestCase(0x0081, true)] + [TestCase(0x00FF, true)] + [TestCase(0x8000, false)] + [TestCase(0x8080, true)] + [TestCase(0xFFFF, true)] + public void WhenSettingSignFlagFromTwosComplementIntValueThenFlagIsSetIfValueNegative(int value, bool flagSet) + { + _register.SetIfTwosComplementNegative(value); + _register.IsFlagSet(Z80StatusFlags.SignS).Should().Be(flagSet); + } + + [Test] + [TestCase(0x00, false)] + [TestCase(0x01, false)] + [TestCase(0x7E, false)] + [TestCase(0x7F, true)] + [TestCase(0x80, false)] + [TestCase(0xFF, false)] + public void WhenSettingOverflowFlagFromIncrementedValueThenFlagIsSetIfValueIs7F(byte value, bool flagSet) + { + _register.SetIfIncrementOverflow(value); + _register.IsFlagSet(Z80StatusFlags.ParityOverflowPV).Should().Be(flagSet); + } + + [Test] + [TestCase(0x00, false)] + [TestCase(0x01, false)] + [TestCase(0x7F, false)] + [TestCase(0x80, true)] + [TestCase(0x81, false)] + [TestCase(0xFF, false)] + public void WhenSettingOverflowFlagFromDecrementedValueThenFlagIsSetIfValueIs80(byte value, bool flagSet) + { + _register.SetIfDecrementOverflow(value); + _register.IsFlagSet(Z80StatusFlags.ParityOverflowPV).Should().Be(flagSet); + } + + [Test] + [TestCase(0x00, 0x00, 0x00, false)] + [TestCase(0x07, 0x07, 0x0E, false)] + [TestCase(0x07, 0x08, 0x0F, false)] + [TestCase(0x0F, 0x00, 0x0F, false)] + [TestCase(0x08, 0x08, 0x10, true)] + [TestCase(0x0F, 0x01, 0x10, true)] + [TestCase(0xFF, 0xFF, 0x1FE, true)] + public void WhenSettingHalfCarryFlagFromChangedValueThenFlagIsSetIfHalfCarry(byte currentValue, byte operand, + int changedValue, bool flagSet) + { + _register.SetIfHalfCarry(currentValue, operand, changedValue); + _register.IsFlagSet(Z80StatusFlags.HalfCarryH).Should().Be(flagSet); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitSpecialRegisterBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitSpecialRegisterBase.cs new file mode 100644 index 0000000..b9f21ec --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitSpecialRegisterBase.cs @@ -0,0 +1,10 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +public class TestZ8016BitSpecialRegisterBase : Z8016BitSpecialRegisterBase +{ + public TestZ8016BitSpecialRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitSpecialRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitSpecialRegisterFixture.cs new file mode 100644 index 0000000..d7bafae --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitSpecialRegisterFixture.cs @@ -0,0 +1,108 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +[TestFixture] +public class Z8016BitSpecialRegisterFixture +{ + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _register = new TestZ8016BitSpecialRegisterBase(_memory, _flags); + } + + private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; + private TestZ8016BitSpecialRegisterBase _register; + + [Test] + public void WhenSettingValueThenValueIsUpdated() + { + _register.Set(0x0522); + _register.Value.Should().Be(0x0522); + } + + [Test] + public void WhenSettingValueFromRegisterThenValueIsUpdated() + { + var register = Substitute.For(); + register.Value.Returns((ushort)0x0412); + _register.Set(register); + _register.Value.Should().Be(0x0412); + } + + [Test] + public void WhenGetValueAsUnsigned16BitValue() + { + _register.Set(0x0522); + var value = _register.AsUnsigned16BitValue(); + value.Word.Should().Be(0x0522); + value.Low.Should().Be(0x22); + value.High.Should().Be(0x05); + } + + [Test] + public void WhenSettingValueFromDataInMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x1235].Returns((byte)0x34); + _memory[0x1236].Returns((byte)0x12); + _register.Set(0x0011); + _register.SetFromDataInMemory(0x1234, 1); + _register.Value.Should().Be(0x1234); + } + + [Test] + public void WhenSettingValueFrom16RegisterPointerToMemoryThenValueIsUpdatedFromDataAtMemoryAddress() + { + _memory[0x2233].Returns((byte)0x13); + _memory[0x2234].Returns((byte)0x03); + var register = Substitute.For(); + register.Value.Returns((ushort)0x2231); + + _register.Set(0x0511); + _register.SetFromDataInMemory(register, 2); + _register.Value.Should().Be(0x0313); + } + + [Test] + public void WhenSavingToMemoryLocationByAddressThenMemoryIsWritten() + { + _register.Set(0x1234); + _register.SaveToMemory(0x1235, 5); + _memory.Received()[0x123A] = 0x34; + _memory.Received()[0x123B] = 0x12; + } + + [Test] + public void WhenResettingRegisterThenValueIsZero() + { + _register.Set(0x3457); + _register.Reset(); + _register.Value.Should().Be(0x0000); + } + + [Test] + public void WhenIncrementingValueThenValueIsIncrementedAndFlagsUnchanged() + { + _register.Set(0x1234); + _register.Increment(); + _register.Value.Should().Be(0x1235); + _flags.ReceivedCalls().Should().BeEmpty(); + } + + [Test] + public void WhenDecrementingValueThenValueIsDecrementedAndFlagsUnchanged() + { + _register.Set(0x1234); + _register.Decrement(); + _register.Value.Should().Be(0x1233); + _flags.ReceivedCalls().Should().BeEmpty(); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs index 39bd7b4..d4cf050 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs @@ -12,8 +12,8 @@ namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; public class Z80StackManagementFixture { private ICpuLogger _cpuLogger; - private IMasterSystemMemory _memory; private IZ80FlagsManager _flags; + private IMasterSystemMemory _memory; private Z80StackManager _stackManager; [SetUp] @@ -82,7 +82,8 @@ public void WhenStackPointerIncrementedWhichOverflowsThenWrapsAround() _stackManager.Set(1000); _stackManager.IncrementStackPointer(); _stackManager.Value.Should().Be(0); - _cpuLogger.Received(1).Error(Arg.Is(x => x.Contains("Stack Pointer has been incremented", StringComparison.InvariantCultureIgnoreCase))); + _cpuLogger.Received(1).Error(Arg.Is(x => + x.Contains("Stack Pointer has been incremented", StringComparison.InvariantCultureIgnoreCase))); } [Test] @@ -93,7 +94,8 @@ public void WhenStackPointerDecrementedWhichOverflowsThenLogsError() _stackManager.Set(100); _stackManager.DecrementStackPointer(); _stackManager.Value.Should().Be(99); - _cpuLogger.Received(1).Error(Arg.Is(x => x.Contains("Stack Pointer has been decremented", StringComparison.InvariantCultureIgnoreCase))); + _cpuLogger.Received(1).Error(Arg.Is(x => + x.Contains("Stack Pointer has been decremented", StringComparison.InvariantCultureIgnoreCase))); } [Test] diff --git a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs index c8d301c..137c6a0 100644 --- a/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -32,7 +32,7 @@ public interface IZ80FlagsManager void SetIfZero(ushort value); void SetIfNegative(byte value); void SetIfNegative(ushort value); - void SetIfNegative(int twosComplementValue); + void SetIfTwosComplementNegative(int twosComplementValue); void SetIfZero(int value); void SetIfIncrementOverflow(byte value); diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index b5cebe2..a1667e1 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -157,7 +157,7 @@ public void NegateAccumulatorRegister() { var twosComplementValue = (0 - Value) & 0xFF; - Flags.SetIfNegative(twosComplementValue); + Flags.SetIfTwosComplementNegative(twosComplementValue); Flags.SetIfZero(twosComplementValue); Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (Value & 0x0F) > 0); diff --git a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs index 6826dee..7fa3d84 100644 --- a/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -23,6 +23,7 @@ public byte ShadowValue public void Reset() { Value = 0; + ShadowValue = 0; } public void Set(byte value) @@ -70,8 +71,11 @@ public bool IsFlagSet(Z80StatusFlags flags) public void SetParityFromValue(byte value) { + // If required, can implement a faster implementation from here http://www.graphics.stanford.edu/~seander/bithacks.html + // Count the number of 1 bits in the value // If odd, then clear flag and if even, then set flag + // No bits set counts as even parity var bitsSet = 0; for (var i = 0; i < 8; i++) { @@ -109,7 +113,7 @@ public void SetIfNegative(ushort value) SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(value, 15)); } - public void SetIfNegative(int twosComplementValue) + public void SetIfTwosComplementNegative(int twosComplementValue) { SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(twosComplementValue, 7)); } diff --git a/Kmse.Core/Z80/Registers/Z808BitRegister.cs b/Kmse.Core/Z80/Registers/Z808BitRegister.cs index cfff472..ecc105c 100644 --- a/Kmse.Core/Z80/Registers/Z808BitRegister.cs +++ b/Kmse.Core/Z80/Registers/Z808BitRegister.cs @@ -30,6 +30,7 @@ public byte ShadowValue public void Reset() { Value = 0; + ShadowValue = 0; } public void Set(byte value) From a7cbc9fc41630d49f9d8c23f01cda5d1bb78a1ab Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 24 Sep 2022 14:05:34 +1000 Subject: [PATCH 18/21] Add unit tests for Z8016BitRegisterBase Tidied up Substract for Z8016BitRegisterBase where it does not support subtract without carry so removed that as an option Revert accidental commit of launchSettings for console app with local file references --- Kmse.Console/Properties/launchSettings.json | 2 - .../Z808BitGeneralPurposeRegisterFixture.cs | 1 - .../TestZ8016BitRegisterBase.cs | 26 ++ .../Z8016BitRegisterBaseFixture.cs | 299 ++++++++++++++++++ .../Z80/Instructions/Z80CpuInstructions.cs | 8 +- .../IZ8016BitGeneralPurposeRegister.cs | 4 +- .../Z80/Registers/IZ8016BitSpecialRegister.cs | 4 +- .../Z80/Registers/Z8016BitRegisterBase.cs | 8 +- Kmse.Core/Z80/Registers/Z80RegisterBase.cs | 3 +- 9 files changed, 338 insertions(+), 17 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitRegisterBase.cs create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitRegisterBaseFixture.cs diff --git a/Kmse.Console/Properties/launchSettings.json b/Kmse.Console/Properties/launchSettings.json index 11c2703..7fac0d6 100644 --- a/Kmse.Console/Properties/launchSettings.json +++ b/Kmse.Console/Properties/launchSettings.json @@ -3,8 +3,6 @@ "Kmse.Console": { "commandName": "Project", "commandLineArgs": "-f ..\\..\\..\\Validation\\zexdoc.sms" - //"commandLineArgs": "-f ..\\..\\..\\..\\..\\zexall-sms\\source\\zexdoc.sms" - //"commandLineArgs": "-f ..\\..\\..\\..\\..\\sonic.sms" } } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs index 260f8fa..27f77d6 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs @@ -185,7 +185,6 @@ public void WhenRotatingRightThenMatchesExpectedValue(byte value, byte expectedV CheckFlagsForBitShifting(expectedValue, expectedCarryFlagStatus); } - [Test] [TestCase(0, 0, false)] [TestCase(0x55, (byte)((0x55 << 1) & 0xFF), false)] diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitRegisterBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitRegisterBase.cs new file mode 100644 index 0000000..35fe4b4 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/TestZ8016BitRegisterBase.cs @@ -0,0 +1,26 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +public class TestZ8016BitRegisterBase : Z8016BitRegisterBase +{ + private Unsigned16BitValue _register; + + public TestZ8016BitRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(memory, flags) + { + _register = new Unsigned16BitValue(); + } + + public override ushort Value => _register.Word; + public override byte High => _register.High; + public override byte Low => _register.Low; + + public override void Set(ushort value) + { + _register.Word = value; + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitRegisterBaseFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitRegisterBaseFixture.cs new file mode 100644 index 0000000..d8b41da --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z8016BitRegisterBaseFixture.cs @@ -0,0 +1,299 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +[TestFixture] +public class Z8016BitRegisterBaseFixture +{ + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _register = new TestZ8016BitRegisterBase(_memory, _flags); + + _memory[0x00].Returns((byte)0x01); + _memory[0x01].Returns((byte)0x02); + _memory[0x02].Returns((byte)0xFF); + _memory[0x03].Returns((byte)0x04); + } + + private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; + private TestZ8016BitRegisterBase _register; + + [Test] + public void WhenResetBitByRegisterLocationThenBitInDataIsResetAtMemoryLocation() + { + _register.Set(0x01); + _register.ResetBitByRegisterLocation(3, 1); + _memory.Received()[0x02] = 0xF7; + } + + [Test] + public void WhenSetBitByRegisterLocationThenBitInDataIsSetAtMemoryLocation() + { + _register.Set(0x02); + _register.SetBitByRegisterLocation(3, 1); + _memory.Received()[0x03] = 0x0C; + } + + [Test] + public void WhenTestBitByRegisterLocationAndBitSetThenFlagsAreSetAsExpected() + { + _register.Set(0x02); + _register.TestBitByRegisterLocation(2, 1); + _memory.DidNotReceive()[0x03] = 0x04; + + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ZeroZ, false); + _flags.Received(1).SetFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.SignS, false); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, false); + } + + [Test] + public void WhenTestBitByRegisterLocationAndBitNotSetThenFlagsAreSetAsExpected() + { + _register.Set(0x02); + _register.TestBitByRegisterLocation(3, 1); + _memory.DidNotReceive()[0x03] = 0x04; + + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ZeroZ, true); + _flags.Received(1).SetFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.SignS, false); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, true); + } + + [Test] + public void WhenTestBitByRegisterLocationAndBit7SetThenSignFlagSet() + { + _register.Set(0x02); + _register.TestBitByRegisterLocation(7, 0); + _memory.DidNotReceive()[0x02] = 0xFF; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.SignS, true); + } + + [Test] + [TestCase((ushort)0, (ushort)0, (ushort)0, false, false)] + [TestCase((ushort)0x0001, (ushort)0x01, (ushort)0x0002, false, false)] + [TestCase((ushort)0x00FF, (ushort)0x01, (ushort)0x0100, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x01, (ushort)0x1000, true, false)] + [TestCase((ushort)0x0FFF, (ushort)0x00FF, (ushort)0x10FE, true, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0x0000, true, true)] + [TestCase((ushort)0x8000, (ushort)0x8000, (ushort)0x0000, false, true)] + public void WhenAddWithoutCarry(ushort value, ushort operand, ushort newValue, bool expectedHalfCarryFlagStatus, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.Add(operand); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryFlagStatus); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + } + + [Test] + [TestCase((ushort)0, (ushort)0, (ushort)1, false, false, false)] + [TestCase((ushort)0x0001, (ushort)0x01, (ushort)0x0003, false, false, false)] + [TestCase((ushort)0x00FF, (ushort)0x01, (ushort)0x0101, false, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x01, (ushort)0x1001, true, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x00FF, (ushort)0x10FF, true, false, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0x0001, true, true, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0x0001, true, true, false)] + [TestCase((ushort)0x8000, (ushort)0x9AAA, (ushort)0x1AAB, false, true, true)] + public void WhenAddWithCarryAndCarrySet(ushort value, ushort operand, ushort newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(value); + _register.Add(operand, true); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryFlagStatus); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((ushort)0, (ushort)0, (ushort)0, false, false, false)] + [TestCase((ushort)0x0001, (ushort)0x01, (ushort)0x0002, false, false, false)] + [TestCase((ushort)0x00FF, (ushort)0x01, (ushort)0x0100, false, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x01, (ushort)0x1000, true, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x00FF, (ushort)0x10FE, true, false, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0x0000, true, true, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0x0000, true, true, false)] + [TestCase((ushort)0x8000, (ushort)0x9AAA, (ushort)0x1AAA, false, true, true)] + public void WhenAddWithCarryAndCarryNotSet(ushort value, ushort operand, ushort newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(false); + _register.Set(value); + _register.Add(operand, true); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryFlagStatus); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((ushort)0, (ushort)0, (ushort)0xFFFF, true, true, false)] + [TestCase((ushort)1, (ushort)1, (ushort)0xFFFF, true, true, false)] + [TestCase((ushort)0x0002, (ushort)0x01, (ushort)0x0000, false, false, false)] + [TestCase((ushort)0x00FF, (ushort)0x01, (ushort)0x00FD, false, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x01, (ushort)0x0FFD, false, false, false)] + [TestCase((ushort)0x1000, (ushort)0x01, (ushort)0x0FFE, true, false, false)] + [TestCase((ushort)0xFFFF, (ushort)0x0001, (ushort)0xFFFD, false, false, false)] + [TestCase((ushort)0xFFFF, (ushort)0xFFFF, (ushort)0xFFFF, true, true, false)] + [TestCase((ushort)0x8000, (ushort)0x7F00, (ushort)0x00FF, true, false, true)] + public void WhenSubtractWithCarryAndCarrySet(ushort value, ushort operand, ushort newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(value); + _register.Subtract(operand); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryFlagStatus); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((ushort)0, (ushort)0, (ushort)0, false, false, false)] + [TestCase((ushort)0, (ushort)1, (ushort)0xFFFF, true, true, false)] + [TestCase((ushort)0x0001, (ushort)0x01, (ushort)0x0000, false, false, false)] + [TestCase((ushort)0x00FF, (ushort)0x01, (ushort)0x00FE, false, false, false)] + [TestCase((ushort)0x0FFF, (ushort)0x01, (ushort)0x0FFE, false, false, false)] + [TestCase((ushort)0x1000, (ushort)0x01, (ushort)0x0FFF, true, false, false)] + [TestCase((ushort)0x8000, (ushort)0x7F00, (ushort)0x0100, true, false, true)] + public void WhenSubtractWithCarryAndCarryNotSet(ushort value, ushort operand, ushort newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(false); + _register.Set(value); + _register.Subtract(operand); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryFlagStatus); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + // These are tested extensively else where, we just need to validate it is reading and writing the rotated/shifted values from/to memory + + [Test] + public void WhenRotatingLeftCircularThenRotatedLeftWithCircularHandling() + { + _memory[0x1235].Returns((byte)0x87); + _register.Set(0x1234); + _register.RotateLeftCircular(1); + _memory.Received()[0x1235] = 0x0F; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingRightCircularThenRotatedRightWithCircularHandling() + { + _memory[0x1235].Returns((byte)0x07); + _register.Set(0x1234); + _register.RotateRightCircular(1); + _memory.Received()[0x1235] = 0x83; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingLeftThenRotatedLeft() + { + _memory[0x1235].Returns((byte)0x87); + _register.Set(0x1234); + _register.RotateLeft(1); + _memory.Received()[0x1235] = 0x0E; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingRightThenRotatedRight() + { + _memory[0x1235].Returns((byte)0x07); + _register.Set(0x1234); + _register.RotateRight(1); + _memory.Received()[0x1235] = 0x03; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftLeftArithmeticThenMemoryDataShiftedLeft() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _memory[0x1235].Returns((byte)0x87); + _register.Set(0x1234); + _register.ShiftLeftArithmetic(1); + _memory.Received()[0x1235] = 0x0E; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftRightArithmeticThenMemoryDataShiftedRight() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _memory[0x1235].Returns((byte)0x07); + _register.Set(0x1234); + _register.ShiftRightArithmetic(1); + _memory.Received()[0x1235] = 0x03; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftLeftLogicalThenMemoryDataShiftedLeft() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _memory[0x1235].Returns((byte)0x87); + _register.Set(0x1234); + _register.ShiftLeftLogical(1); + _memory.Received()[0x1235] = 0x0F; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftRightLogicalThenMemoryDataShiftedRight() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _memory[0x1235].Returns((byte)0x87); + _register.Set(0x1234); + _register.ShiftRightLogical(1); + _memory.Received()[0x1235] = 0x43; + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs index 5684da3..e2e22b3 100644 --- a/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs +++ b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs @@ -436,10 +436,10 @@ private void PopulateArthmeticAndLogicalInstructions() AddDoubleByteInstruction(0xDD, 0x9E, 19, "SBC A,(IX+d)", "Subtract (IX+d) from A with Carry", _ => { _accumulator.SubtractFromMemory(_ix, _pc.GetNextDataByte(), true); }); AddDoubleByteInstruction(0xFD, 0x9E, 19, "SBC A,(IY+d)", "Subtract (Iy+d) from A with Carry", _ => { _accumulator.SubtractFromMemory(_iy, _pc.GetNextDataByte(), true); }); - AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { _hl.Subtract(_bc, true); }); - AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { _hl.Subtract(_de, true); }); - AddDoubleByteInstruction(0xED, 0x62, 15, "SBC HL,HL", "Subtract with Carry", _ => { _hl.Subtract(_hl, true); }); - AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { _hl.Subtract(_stack, true); }); + AddDoubleByteInstruction(0xED, 0x42, 15, "SBC HL,BC", "Subtract with Carry", _ => { _hl.Subtract(_bc); }); + AddDoubleByteInstruction(0xED, 0x52, 15, "SBC HL,DE", "Subtract with Carry", _ => { _hl.Subtract(_de); }); + AddDoubleByteInstruction(0xED, 0x62, 15, "SBC HL,HL", "Subtract with Carry", _ => { _hl.Subtract(_hl); }); + AddDoubleByteInstruction(0xED, 0x72, 15, "SBC HL,SP", "Subtract with Carry", _ => { _hl.Subtract(_stack); }); AddStandardInstruction(0xBF, 4, "CP A", "Compare A to A", _ => { _accumulator.Compare(_accumulator.Value); }); AddStandardInstruction(0xB8, 4, "CP B", "Compare B to A", _ => { _accumulator.Compare(_b.Value); }); diff --git a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs index ed369c8..361c973 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -16,8 +16,8 @@ public interface IZ8016BitGeneralPurposeRegister : IZ8016BitRegister void TestBitByRegisterLocation(int bit, int offset); void Add(ushort source, bool withCarry = false); void Add(IZ8016BitRegister register, bool withCarry = false); - void Subtract(ushort source, bool withCarry = false); - void Subtract(IZ8016BitRegister register, bool withCarry = false); + void Subtract(ushort source); + void Subtract(IZ8016BitRegister register); void RotateLeftCircular(int offset); void RotateLeft(int offset); void RotateRightCircular(int offset); diff --git a/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs index f3a21ad..f864627 100644 --- a/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs +++ b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs @@ -9,8 +9,8 @@ public interface IZ8016BitSpecialRegister : IZ8016BitRegister void TestBitByRegisterLocation(int bit, int offset); void Add(ushort source, bool withCarry = false); void Add(IZ8016BitRegister register, bool withCarry = false); - void Subtract(ushort source, bool withCarry = false); - void Subtract(IZ8016BitRegister register, bool withCarry = false); + void Subtract(ushort source); + void Subtract(IZ8016BitRegister register); void RotateLeftCircular(int offset); void RotateLeft(int offset); void RotateRightCircular(int offset); diff --git a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs index 5b95350..3391426 100644 --- a/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -84,10 +84,10 @@ public void Add(IZ8016BitRegister register, bool withCarry = false) Add(register.Value, withCarry); } - public void Subtract(ushort source, bool withCarry = false) + public void Subtract(ushort source) { int valueWithCarry = source; - if (withCarry && Flags.IsFlagSet(Z80StatusFlags.CarryC)) + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) { valueWithCarry += 0x01; } @@ -108,9 +108,9 @@ public void Subtract(ushort source, bool withCarry = false) Set((ushort)newValue); } - public void Subtract(IZ8016BitRegister register, bool withCarry = false) + public void Subtract(IZ8016BitRegister register) { - Subtract(register.Value, withCarry); + Subtract(register.Value); } public void RotateLeftCircular(int offset) diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs index 287ae54..762e936 100644 --- a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -156,8 +156,7 @@ protected void ShiftLeftLogical(ref byte register) protected void ShiftRightLogical(ref byte register) { - // Rotate register right by 1 bit, bit 0 is copied to carry flag - // This is special method since RRA flags are set differently to RR r instruction + // Shift register right by 1 bit, bit 0 is copied to carry flag var newValue = (byte)(register >> 1); // The difference between shift right logical and shift right arithmetic is arithmetic always maintains bit 7 when shifting and logical shifting always clears bit 7 From aef2352e656dd6989fbae7ef33cd4afd12d57f62 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 24 Sep 2022 18:05:11 +1000 Subject: [PATCH 19/21] Fixed bug when copying I or R register to accumulator it was not setting sign flag properly Add unit tests for accumulator register --- .../Z80CpuTests/CpuTestFixtureBase.cs | 2 +- .../Data/TestInstructions.json | 4 +- .../TestInstructionsFixture.cs | 2 +- .../GeneralPurpose/Z80AccumulatorFixture.cs | 434 ++++++++++++++++++ .../Z80/Registers/General/Z80Accumulator.cs | 9 +- 5 files changed, 443 insertions(+), 8 deletions(-) create mode 100644 Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs index 57e43e5..941b838 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -44,7 +44,7 @@ public void Setup() InstructionLogger = new Z80InstructionLogger(CpuLogger); var flags = new Z80FlagsManager(); - var accumulator = new Z80Accumulator(flags, Memory); + var accumulator = new Z80Accumulator(Memory, flags); var af = new Z80AfRegister(Memory, flags, accumulator); var bc = new Z80BcRegister(Memory, af.Flags, () => new Z808BitGeneralPurposeRegister(Memory, flags)); var de = new Z80DeRegister(Memory, af.Flags, () => new Z808BitGeneralPurposeRegister(Memory, flags)); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json index 55f19dc..365f74c 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json @@ -4482,14 +4482,14 @@ { "Name": "LD A,I", "HexInstructions": "ED,57", - "CpuHash": "7DBA97B956F6A73E0200464511D5627B5B631A70E2E88931B31FE597B1D89104", + "CpuHash": "E9EC57FC9F2C14881B58885A68921F0F8C724C8FF59DF1542FD3646E0D21E240", "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", "MemoryHash": "FC0FE3D2D663F23D92E659536E49DFB2ACD05E7B2A0D8F37BAF0284E95EE1380" }, { "Name": "LD A,R", "HexInstructions": "ED,5F", - "CpuHash": "7DBA97B956F6A73E0200464511D5627B5B631A70E2E88931B31FE597B1D89104", + "CpuHash": "E9EC57FC9F2C14881B58885A68921F0F8C724C8FF59DF1542FD3646E0D21E240", "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", "MemoryHash": "7EEE0CA2B8ACBDC4F46F016C39B7541D13274B8F62C3ECC5737D9E4F022268BD" }, diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs index 1864f85..1605245 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs @@ -48,7 +48,7 @@ public void Setup() var instructionLogger = new Z80InstructionLogger(_cpuLogger); var flags = new Z80FlagsManager(); - var accumulator = new Z80Accumulator(flags, _memory); + var accumulator = new Z80Accumulator(_memory, flags); var af = new Z80AfRegister(_memory, flags, accumulator); var bc = new Z80BcRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); var de = new Z80DeRegister(_memory, af.Flags, () => new Z808BitGeneralPurposeRegister(_memory, flags)); diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs new file mode 100644 index 0000000..5f1577e --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs @@ -0,0 +1,434 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.GeneralPurpose; + +[TestFixture] +public class Z80AccumulatorFixture +{ + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _flags = Substitute.For(); + _register = new Z80Accumulator(_memory, _flags); + } + + private IMasterSystemMemory _memory; + private IZ80FlagsManager _flags; + private Z80Accumulator _register; + + [Test] + [TestCase(false)] + [TestCase(true)] + public void WhenSetFromInterruptRegisterAndIff2SetThenValueAndFlagsAreSet(bool iff2Status) + { + var interruptRegister = Substitute.For(); + const byte value = 0x12; + interruptRegister.Value.Returns(value); + _register.SetFromInterruptRegister(interruptRegister, iff2Status); + _register.Value.Should().Be(value); + + _flags.Received(1).SetIfNegative(value); + _flags.Received(1).SetIfZero(value); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, iff2Status); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + [TestCase(false)] + [TestCase(true)] + public void WhenSetFromMemoryRefreshRegisterAndIff2SetThenValueAndFlagsAreSet(bool iff2Status) + { + var interruptRegister = Substitute.For(); + const byte value = 0x12; + interruptRegister.Value.Returns(value); + _register.SetFromMemoryRefreshRegister(interruptRegister, iff2Status); + _register.Value.Should().Be(value); + + _flags.Received(1).SetIfNegative(value); + _flags.Received(1).SetIfZero(value); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, iff2Status); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + public void WhenRotateLeftDigitFromHlRegister() + { + const byte expectedAValue = 0x73; + var hlRegister = Substitute.For(); + hlRegister.Value.Returns((ushort)0x1234); + _memory[0x1234].Returns((byte)0x31); + _register.Set(0x7A); + + _register.RotateLeftDigit(hlRegister); + + _memory.Received()[0x1234] = 0x1A; + _register.Value.Should().Be(expectedAValue); + + _flags.Received(1).SetIfNegative(expectedAValue); + _flags.Received(1).SetIfZero(expectedAValue); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(expectedAValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + public void WhenRotateRightDigitFromHlRegister() + { + const byte expectedAValue = 0x80; + var hlRegister = Substitute.For(); + hlRegister.Value.Returns((ushort)0x1234); + _memory[0x1234].Returns((byte)0x20); + _register.Set(0x84); + + _register.RotateRightDigit(hlRegister); + + _memory.Received()[0x1234] = 0x42; + _register.Value.Should().Be(expectedAValue); + + _flags.Received(1).SetIfNegative(expectedAValue); + _flags.Received(1).SetIfZero(expectedAValue); + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.SetParityFromValue(expectedAValue); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + [TestCase(0, 0, false, false, false, false)] + [TestCase(1, 1, false, false, false, false)] + [TestCase(0xA0, 0x00, false, false, false, true)] + [TestCase(0xAA, 0x10, false, false, true, true)] + + [TestCase(0x11, 0x17, true, false, false, false)] + [TestCase(0x01, 0x61, false, true, false, true)] + + [TestCase(0xFF, 0x65, false, false, true, true)] + public void WhenDoingDecAdjustAccumulatorAfterAdd(byte currentValue, byte expectedValue, bool halfCarrySet, bool carrySet, bool expectedHalfCarryStatus, bool expectedCarryStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(carrySet); + _flags.IsFlagSet(Z80StatusFlags.HalfCarryH).Returns(halfCarrySet); + _flags.IsFlagSet(Z80StatusFlags.AddSubtractN).Returns(false); + + _register.Set(currentValue); + _register.DecimalAdjustAccumulator(); + + _register.Value.Should().Be(expectedValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryStatus); + if (expectedCarryStatus) + { + _flags.Received(1).SetFlag(Z80StatusFlags.CarryC); + } + else + { + _flags.Received(1).ClearFlag(Z80StatusFlags.CarryC); + } + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetParityFromValue(expectedValue); + } + + [Test] + public void WhenInvertAccumulatorRegisterThenValueIsInverted() + { + _register.Set(0x12); + _register.InvertAccumulatorRegister(); + _register.Value.Should().Be(0xED); + + _flags.Received(1).SetFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + } + + [Test] + [TestCase(0, 0, false, false)] + [TestCase(1, 0xFF, true, true)] + [TestCase(0x12, 0xEE, true, true)] + [TestCase(0xFF, 0x01, true, true)] + [TestCase(0x10, 0xF0, false, true)] + public void WhenNegateAccumulatorRegisterThenValueIsInverted(byte currentValue, byte expectedValue, bool expectedHalfCarryStatus, bool expectedCarryStatus) + { + _register.Set(currentValue); + _register.NegateAccumulatorRegister(); + _register.Value.Should().Be(expectedValue); + + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.HalfCarryH, expectedHalfCarryStatus); + _flags.Received(1).SetIfDecrementOverflow(currentValue); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryStatus); + } + + [Test] + [TestCase((byte)0, (byte)0, (byte)0, false, false)] + [TestCase((byte)0x01, (byte)0x01, (byte)0x0002, false, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x00, false, true)] + [TestCase((byte)0xFF, (byte)0xFF, (byte)0xFE, true, true)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x00, true, true)] + [TestCase((byte)0x80, (byte)0x80, (byte)0x00, false, true)] + public void WhenAddWithoutCarry(byte value, byte operand, byte newValue, bool expectedHalfCarryFlagStatus, + bool expectedCarryFlagStatus) + { + _register.Set(value); + _register.Add(operand); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetIfHalfCarry(value, operand, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + } + + [Test] + [TestCase((byte)0, (byte)0, (byte)1, false, false, false)] + [TestCase((byte)0x01, (byte)0x01, (byte)0x0003, false, false, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x01, false, true, false)] + [TestCase((byte)0x0F, (byte)0x01, (byte)0x11, true, false, false)] + [TestCase((byte)0x0F, (byte)0xFF, (byte)0x0F, true, true, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x01, true, true, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x01, true, true, false)] + [TestCase((byte)0x80, (byte)0xAA, (byte)0x2B, false, true, true)] + public void WhenAddWithCarryAndCarrySet(byte value, byte operand, byte newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(value); + _register.Add(operand, true); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetIfHalfCarry(value, operand, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((byte)0, (byte)0, (byte)0, false, false, false)] + [TestCase((byte)0x01, (byte)0x01, (byte)0x0002, false, false, false)] + [TestCase((byte)0x0F, (byte)0x01, (byte)0x10, true, false, false)] + [TestCase((byte)0x0F, (byte)0xFF, (byte)0x0E, true, true, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0x00, true, true, false)] + [TestCase((byte)0x80, (byte)0xAA, (byte)0x2A, false, true, true)] + public void WhenAddWithCarryAndCarryNotSet(byte value, byte operand, byte newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(false); + _register.Set(value); + _register.Add(operand, true); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetIfHalfCarry(value, operand, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((byte)0, (byte)0, (byte)0xFF, true, true, false)] + [TestCase((byte)1, (byte)1, (byte)0xFF, true, true, false)] + [TestCase((byte)0x02, (byte)0x01, (byte)0x00, false, false, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0xFD, false, false, false)] + [TestCase((byte)0x0F, (byte)0x01, (byte)0x0D, false, false, false)] + [TestCase((byte)0x10, (byte)0x01, (byte)0x0E, true, false, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0xFD, false, false, false)] + [TestCase((byte)0xFF, (byte)0xFF, (byte)0xFF, true, true, false)] + [TestCase((byte)0x80, (byte)0x7F, (byte)0x00, true, false, true)] + public void WhenSubtractWithCarryAndCarrySet(byte value, byte operand, byte newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(value); + _register.Subtract(operand, true); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetIfHalfCarry(value, operand, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + [Test] + [TestCase((byte)0, (byte)0, (byte)0, false, false, false)] + [TestCase((byte)0, (byte)1, (byte)0xFF, true, true, false)] + [TestCase((byte)0x01, (byte)0x01, (byte)0x00, false, false, false)] + [TestCase((byte)0xFF, (byte)0x01, (byte)0xFE, false, false, false)] + [TestCase((byte)0x0F, (byte)0x01, (byte)0x0E, false, false, false)] + [TestCase((byte)0x10, (byte)0x01, (byte)0x0F, true, false, false)] + [TestCase((byte)0x80, (byte)0x7F, (byte)0x01, true, false, true)] + public void WhenSubtractWithCarryAndCarryNotSet(byte value, byte operand, byte newValue, + bool expectedHalfCarryFlagStatus, bool expectedCarryFlagStatus, bool expectedParityFlagStatus) + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(false); + _register.Set(value); + _register.Subtract(operand); + + _register.Value.Should().Be(newValue); + + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetIfHalfCarry(value, operand, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, expectedCarryFlagStatus); + + _flags.Received(1).SetIfNegative(newValue); + _flags.Received(1).SetIfZero(newValue); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, expectedParityFlagStatus); + } + + // These are tested extensively else where, we just need to validate it is reading and writing the rotated/shifted values from/to memory + + [Test] + public void WhenRotatingLeftCircularThenRotatedLeftWithCircularHandling() + { + _register.Set(0x87); + _register.RotateLeftCircular(); + _register.Value.Should().Be(0x0F); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingRightCircularThenRotatedRightWithCircularHandling() + { + _register.Set(0x07); + _register.RotateRightCircular(); + _register.Value.Should().Be(0x83); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingLeftThenRotatedLeft() + { + _register.Set(0x87); + _register.RotateLeft(); + _register.Value.Should().Be(0x0E); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenRotatingRightThenRotatedRight() + { + _register.Set(0x07); + _register.RotateRight(); + _register.Value.Should().Be(0x03); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftLeftArithmeticThenShiftedLeft() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(0x87); + _register.ShiftLeftArithmetic(); + _register.Value.Should().Be(0x0E); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftRightArithmeticThenShiftedRight() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(0x07); + _register.ShiftRightArithmetic(); + _register.Value.Should().Be(0x03); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftLeftLogicalThenShiftedLeft() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(0x87); + _register.ShiftLeftLogical(); + _register.Value.Should().Be(0x0F); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenShiftRightLogicalThenShiftedRight() + { + _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(true); + _register.Set(0x87); + _register.ShiftRightLogical(); + _register.Value.Should().Be(0x43); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + } + + [Test] + public void WhenComparingValue() + { + const byte expectedValue = 0xDE; + _register.Set(0x12); + _register.Compare(0x34); + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetIfHalfCarry(0x12, 0x34, Arg.Any()); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.CarryC, true); + _flags.Received(1).SetFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, false); + } + + [Test] + public void WhenAndValue() + { + const byte expectedValue = 0x10; + _register.And(0x12, 0x34); + _register.Value.Should().Be(expectedValue); + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).SetFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).ClearFlag(Z80StatusFlags.CarryC); + } + + [Test] + public void WhenOrValue() + { + const byte expectedValue = 0x36; + _register.Or(0x12, 0x34); + _register.Value.Should().Be(expectedValue); + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).ClearFlag(Z80StatusFlags.CarryC); + } + + [Test] + public void WhenXorValue() + { + const byte expectedValue = 0x26; + _register.Xor(0x12, 0x34); + _register.Value.Should().Be(expectedValue); + + _flags.Received(1).SetIfNegative(expectedValue); + _flags.Received(1).SetIfZero(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.Received(1).SetParityFromValue(expectedValue); + _flags.Received(1).ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.Received(1).ClearFlag(Z80StatusFlags.CarryC); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index a1667e1..56dfd39 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -7,7 +7,7 @@ namespace Kmse.Core.Z80.Registers.General; public class Z80Accumulator : Z808BitGeneralPurposeRegister, IZ80Accumulator { - public Z80Accumulator(IZ80FlagsManager flags, IMasterSystemMemory memory) + public Z80Accumulator(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } public void SetFromInterruptRegister(IZ80InterruptPageAddressRegister register, bool interruptFlipFlop2Status) @@ -161,7 +161,7 @@ public void NegateAccumulatorRegister() Flags.SetIfZero(twosComplementValue); Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (Value & 0x0F) > 0); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, Value == 0x80); + Flags.SetIfDecrementOverflow(Value); Flags.SetFlag(Z80StatusFlags.AddSubtractN); Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value != 0x00); @@ -387,8 +387,9 @@ private void LoadSpecial8BitRegisterToAccumulator(byte sourceData, bool interrup Value = sourceData; // Check flags since copying from special register into accumulator - Flags.SetClearFlagConditional(Z80StatusFlags.SignS, !Bitwise.IsSet(sourceData, 7)); - Flags.SetClearFlagConditional(Z80StatusFlags.ZeroZ, sourceData == 0); + Flags.SetIfNegative(sourceData); + Flags.SetIfZero(sourceData); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, interruptFlipFlop2Status); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); From 8c7f5397368a56db9b2831b8ed7dfea0a37b63e6 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sat, 24 Sep 2022 18:05:45 +1000 Subject: [PATCH 20/21] Tidy up accumulator and accumulator test code --- .../GeneralPurpose/Z80AccumulatorFixture.cs | 13 +++++-------- Kmse.Core/Z80/Registers/General/Z80Accumulator.cs | 9 ++++++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs index 5f1577e..ff4c749 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs @@ -1,11 +1,8 @@ using FluentAssertions; using Kmse.Core.Memory; -using Kmse.Core.Utilities; using Kmse.Core.Z80.Model; -using Kmse.Core.Z80.Registers; using Kmse.Core.Z80.Registers.General; using Kmse.Core.Z80.Registers.SpecialPurpose; -using Newtonsoft.Json.Linq; using NSubstitute; using NUnit.Framework; @@ -109,12 +106,11 @@ public void WhenRotateRightDigitFromHlRegister() [TestCase(1, 1, false, false, false, false)] [TestCase(0xA0, 0x00, false, false, false, true)] [TestCase(0xAA, 0x10, false, false, true, true)] - [TestCase(0x11, 0x17, true, false, false, false)] [TestCase(0x01, 0x61, false, true, false, true)] - [TestCase(0xFF, 0x65, false, false, true, true)] - public void WhenDoingDecAdjustAccumulatorAfterAdd(byte currentValue, byte expectedValue, bool halfCarrySet, bool carrySet, bool expectedHalfCarryStatus, bool expectedCarryStatus) + public void WhenDoingDecAdjustAccumulatorAfterAdd(byte currentValue, byte expectedValue, bool halfCarrySet, + bool carrySet, bool expectedHalfCarryStatus, bool expectedCarryStatus) { _flags.IsFlagSet(Z80StatusFlags.CarryC).Returns(carrySet); _flags.IsFlagSet(Z80StatusFlags.HalfCarryH).Returns(halfCarrySet); @@ -156,7 +152,8 @@ public void WhenInvertAccumulatorRegisterThenValueIsInverted() [TestCase(0x12, 0xEE, true, true)] [TestCase(0xFF, 0x01, true, true)] [TestCase(0x10, 0xF0, false, true)] - public void WhenNegateAccumulatorRegisterThenValueIsInverted(byte currentValue, byte expectedValue, bool expectedHalfCarryStatus, bool expectedCarryStatus) + public void WhenNegateAccumulatorRegisterThenValueIsInverted(byte currentValue, byte expectedValue, + bool expectedHalfCarryStatus, bool expectedCarryStatus) { _register.Set(currentValue); _register.NegateAccumulatorRegister(); @@ -176,7 +173,7 @@ public void WhenNegateAccumulatorRegisterThenValueIsInverted(byte currentValue, [TestCase((byte)0xFF, (byte)0x01, (byte)0x00, true, true)] [TestCase((byte)0x80, (byte)0x80, (byte)0x00, false, true)] public void WhenAddWithoutCarry(byte value, byte operand, byte newValue, bool expectedHalfCarryFlagStatus, - bool expectedCarryFlagStatus) + bool expectedCarryFlagStatus) { _register.Set(value); _register.Add(operand); diff --git a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs index 56dfd39..9532453 100644 --- a/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -187,7 +187,8 @@ public void Add(byte value, bool withCarry = false) Flags.SetIfNegative((byte)newValue); Flags.SetIfZero((byte)(newValue & 0xFF)); Flags.SetIfHalfCarry(Value, value, newValue); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value ^ 0x80) & (Value ^ newValue) & 0x80) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value ^ 0x80) & (Value ^ newValue) & 0x80) != 0); Flags.ClearFlag(Z80StatusFlags.AddSubtractN); // A carry is same as half carry just on the overall value @@ -216,7 +217,8 @@ public void Subtract(byte value, bool withCarry = false) Flags.SetIfNegative((byte)newValue); Flags.SetIfZero((byte)(newValue & 0xFF)); Flags.SetIfHalfCarry(Value, value, newValue); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value) & (Value ^ newValue) & 0x80) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value) & (Value ^ newValue) & 0x80) != 0); Flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit @@ -233,7 +235,8 @@ public void Compare(byte value) Flags.SetIfNegative((byte)difference); Flags.SetIfZero((byte)(difference & 0xFF)); Flags.SetIfHalfCarry(Value, value, difference); - Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((value ^ Value) & (Value ^ difference) & 0x80) != 0); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, + ((value ^ Value) & (Value ^ difference) & 0x80) != 0); Flags.SetFlag(Z80StatusFlags.AddSubtractN); // Subtraction went negative, so carried over into next bit From d75d7b0ec340d1644cc3a453c34cce42f9cb9384 Mon Sep 17 00:00:00 2001 From: Tim Pike <17920409+KeyDecoder@users.noreply.github.com> Date: Sun, 25 Sep 2022 10:41:15 +1000 Subject: [PATCH 21/21] Fixed issue where master system memory was not being cleared when new cartridge was loaded Added unit tests for master system memory handling --- .../MemoryTests/MasterSystemMemoryFixture.cs | 265 ++++++++++++++++++ Kmse.Core/Memory/MasterSystemMemory.cs | 15 +- 2 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 Kmse.Core.UnitTests/MemoryTests/MasterSystemMemoryFixture.cs diff --git a/Kmse.Core.UnitTests/MemoryTests/MasterSystemMemoryFixture.cs b/Kmse.Core.UnitTests/MemoryTests/MasterSystemMemoryFixture.cs new file mode 100644 index 0000000..fca178c --- /dev/null +++ b/Kmse.Core.UnitTests/MemoryTests/MasterSystemMemoryFixture.cs @@ -0,0 +1,265 @@ +using FluentAssertions; +using Kmse.Core.Cartridge; +using Kmse.Core.Memory; +using NSubstitute; +using NUnit.Framework; +using Serilog; + +namespace Kmse.Core.UnitTests.MemoryTests; + +[TestFixture] +public class MasterSystemMemoryFixture +{ + [SetUp] + public void Setup() + { + _memoryLogger = Substitute.For(); + _cartridge = Substitute.For(); + _cartridge.Length.Returns(0xFFFF); + _cartridge.HasDumpHeader.Returns(false); + + _memory = new MasterSystemMemory(Substitute.For(), _memoryLogger); + } + + private IMasterSystemMemory _memory; + private IMemoryLogger _memoryLogger; + private IMasterSystemCartridge _cartridge; + + [Test] + public void WhenLoadingANewCartridgeThenMemoryIsReset() + { + _memory[0x00] = 0x01; + _memory[0x01] = 0x02; + _memory[0x1234] = 0x03; + + + _cartridge.Length.Returns(0x1000); + _memory.LoadCartridge(_cartridge); + _memory[0x00].Should().Be(0x00); + _memory[0x01].Should().Be(0x00); + _memory[0x1234].Should().Be(0x00); + } + + [Test] + public void WhenGettingMinAndMaxAvailableMemoryWithPagingDisabled() + { + _memory.GetMinimumAvailableMemorySize().Should().Be(0xC000); + _memory.GetMaximumAvailableMemorySize().Should().Be(0x10000); + } + + [Test] + public void WhenGettingMinAndMaxAvailableMemoryWithPagingEnabled() + { + // Set paging on (bit 3 of memory control register) + _memory[0xFFFC] = 0x08; + _memory.GetMinimumAvailableMemorySize().Should().Be(0x8000); + _memory.GetMaximumAvailableMemorySize().Should().Be(0x10000); + } + + [Test] + [TestCase(0x0000)] + [TestCase(0x3FFF)] + [TestCase(0x4000)] + [TestCase(0x7FFF)] + [TestCase(0x8000)] + [TestCase(0xBFFF)] + public void WhenAttemptingToWriteToRomSpaceWithRamBankDisabledThenShouldNotChangeMemory(int address) + { + _memory.LoadCartridge(_cartridge); + + _memory[(ushort)address] = 0x12; + + _memoryLogger.Received(1).Error(Arg.Any()); + _memory[(ushort)address].Should().Be(0x00); + } + + [Test] + [TestCase(0x0000, false)] + [TestCase(0x3FFF, false)] + [TestCase(0x4000, false)] + [TestCase(0x7FFF, false)] + [TestCase(0x8000, true)] + [TestCase(0xBFFF, true)] + public void WhenAttemptingToWriteToRomSpaceWithRamBankEnabledThenShouldOnlyChangeMemoryInPage3(int address, + bool canWrite) + { + _memory.LoadCartridge(_cartridge); + + _memory[0xFFFC] = 0x08; + _memory[(ushort)address] = 0x12; + + if (canWrite) + { + _memoryLogger.DidNotReceiveWithAnyArgs().Error(null); + _memory[(ushort)address].Should().Be(0x12); + } + else + { + _memoryLogger.Received(1).Error(Arg.Any()); + _memory[(ushort)address].Should().Be(0x00); + } + } + + [Test] + [TestCase(0x0000)] + [TestCase(0x3FFF)] + [TestCase(0x4000)] + [TestCase(0x7FFF)] + [TestCase(0x8000)] + [TestCase(0xBFFF)] + public void WhenReadingFromCartridgeMemoryWithNoPagesSetThenFirst3SlotsAreReturned(int address) + { + _cartridge[(ushort)address].Returns((byte)0x01); + _memory.LoadCartridge(_cartridge); + + var value = _memory[(ushort)address]; + value.Should().Be(0x01); + } + + [Test] + [TestCase(0x0000, 0, 0x0000)] + [TestCase(0x03FF, 1, 0x03FF)] + [TestCase(0x0400, 1, 0x4400)] + [TestCase(0x0400, 2, 0x8400)] + [TestCase(0x3FFF, 1, 0x7FFF)] + [TestCase(0x3FFF, 3, 0xFFFF)] + [TestCase(0x4000, 1, 0x4000)] + [TestCase(0x7FFF, 1, 0x7FFF)] + [TestCase(0x8000, 1, 0x8000)] + [TestCase(0xBFFF, 1, 0xBFFF)] + public void WhenReadingFromCartridgeMemoryWithFirstBankPageSet(int memoryAddress, byte firstBankPage, + int pagedRomAddress) + { + _cartridge[(ushort)pagedRomAddress].Returns((byte)0x01); + _memory.LoadCartridge(_cartridge); + _memory[0xFFFD] = firstBankPage; + var value = _memory[(ushort)memoryAddress]; + value.Should().Be(0x01); + } + + [Test] + [TestCase(0x0000, 0, 0x0000)] + [TestCase(0x03FF, 1, 0x03FF)] + [TestCase(0x0400, 1, 0x0400)] + [TestCase(0x3FFF, 1, 0x3FFF)] + [TestCase(0x4000, 1, 0x4000)] + [TestCase(0x7FFF, 2, 0xBFFF)] + [TestCase(0x4001, 3, 0xC001)] + [TestCase(0x8000, 1, 0x8000)] + [TestCase(0xBFFF, 1, 0xBFFF)] + public void WhenReadingFromCartridgeMemoryWithSecondBankPageSet(int memoryAddress, byte secondBankPage, + int pagedRomAddress) + { + _cartridge[(ushort)pagedRomAddress].Returns((byte)0x01); + _memory.LoadCartridge(_cartridge); + _memory[0xFFFE] = secondBankPage; + var value = _memory[(ushort)memoryAddress]; + value.Should().Be(0x01); + } + + [Test] + [TestCase(0x0000, 0, 0x0000)] + [TestCase(0x03FF, 1, 0x03FF)] + [TestCase(0x0400, 1, 0x0400)] + [TestCase(0x3FFF, 1, 0x3FFF)] + [TestCase(0x4000, 1, 0x4000)] + [TestCase(0x7FFF, 2, 0x7FFF)] + [TestCase(0x8000, 1, 0x4000)] + [TestCase(0x8001, 1, 0x4001)] + [TestCase(0x8001, 2, 0x8001)] + [TestCase(0x8001, 3, 0xC001)] + [TestCase(0xBFFF, 2, 0xBFFF)] + [TestCase(0xBFFF, 3, 0xFFFF)] + public void WhenReadingFromCartridgeMemoryWithThirdBankPageSet(int memoryAddress, byte thirdBankPage, + int pagedRomAddress) + { + _cartridge[(ushort)pagedRomAddress].Returns((byte)0x01); + _memory.LoadCartridge(_cartridge); + _memory[0xFFFF] = thirdBankPage; + var value = _memory[(ushort)memoryAddress]; + value.Should().Be(0x01); + } + + [Test] + [TestCase(0xC000)] + [TestCase(0xFFFF)] + public void WhenWritingAndReadingNormalRamThenDataIsReturned(int address) + { + _memory[(ushort)address] = 0x01; + var value = _memory[(ushort)address]; + value.Should().Be(0x01); + } + + [Test] + public void WhenReadingWritingUsingFirstRamBankThenDataIsStoredInRamBank0() + { + _cartridge[0x8001].Returns((byte)0x55); + _memory.LoadCartridge(_cartridge); + + // enable RAM bank 0 + _memory[0xFFFC] = 0x08; + + _memory[0x8001] = 0x03; + _memory[0x8001].Should().Be(0x03, "if value is 0x55 then reading from ROM not RAM bank 0"); + } + + [Test] + public void WhenReadingWritingUsingSecondRamBankThenDataIsStoredInRamBank1() + { + _cartridge[0x8001].Returns((byte)0x55); + _memory.LoadCartridge(_cartridge); + + // enable RAM bank 1 + _memory[0xFFFC] = 0x0C; + + _memory[0x8001] = 0x04; + _memory[0x8001].Should().Be(0x04, "if value is 0x55 then reading from ROM not RAM bank 1"); + } + + [Test] + public void WhenSwitchingBetweenRamBanks() + { + // This test has data in ROM and both RAM banks and ensures can switch between them without affecting any other data + // and tests that switching and disabling RAM bank is working properly + _cartridge[0x8001].Returns((byte)0x55); + _memory.LoadCartridge(_cartridge); + + // enable RAM bank 0 + _memory[0xFFFC] = 0x08; + _memory[0x8001] = 0x03; + + // enable RAM bank 1 + _memory[0xFFFC] = 0x0C; + _memory[0x8001] = 0x04; + + // Disable RAM bank + _memory[0xFFFC] = 0x00; + _memory[0x8001].Should().Be(0x55, "if value is 0x04 then disabling RAM bank not working"); + + // Enable RAM bank 0 + _memory[0xFFFC] = 0x08; + _memory[0x8001].Should().Be(0x03, + "if value is 0x55 then reading from ROM not RAM bank 0, if 0x04 then did not switch to RAM bank 0 since reading from RAM bank 1"); + + // Enable RAM bank 1 + _memory[0xFFFC] = 0x0C; + _memory[0x8001].Should().Be(0x04, + "if value is 0x55 then reading from ROM not RAM bank 1, if 0x03 then did not switch to RAM bank 1"); + } + + [Test] + public void WhenWritingNormalRamThenDataIsMirrored() + { + _memory[0xC000] = 0x01; + _memory[0xC000].Should().Be(0x01); + _memory[0xE000].Should().Be(0x01); + } + + [Test] + public void WhenWritingMirroredRamThenRegularDataIsUpdated() + { + _memory[0xE000] = 0x01; + _memory[0xE000].Should().Be(0x01); + _memory[0xC000].Should().Be(0x01); + } +} \ No newline at end of file diff --git a/Kmse.Core/Memory/MasterSystemMemory.cs b/Kmse.Core/Memory/MasterSystemMemory.cs index 3050179..49f26a9 100644 --- a/Kmse.Core/Memory/MasterSystemMemory.cs +++ b/Kmse.Core/Memory/MasterSystemMemory.cs @@ -118,7 +118,10 @@ private void Reset() _firstBankPage = 0; _secondBankPage = 1; _thirdBankPage = 2; - } + _internalRAM.Span.Fill(0x00); + _ramBank0.Span.Fill(0x00); + _ramBank1.Span.Fill(0x00); +} private bool IsPagingEnabled() { @@ -191,6 +194,7 @@ private void WriteMemory(ushort address, byte data) return; // Cannot write to slot 3 since RAM banking not enabled so this is used for ROM pages only case < MemorySlot3 + MemoryPageSize: + _memoryLogger.Error("Attempted to write to ROM Memory Slot 3 with RAM banking disabled"); return; } @@ -239,7 +243,8 @@ private byte ReadMemory(ushort address) // 0 - 0x4000 // Reading data from slot 1, use page to lookup ROM data // We just add since accessing inside the first slot - var bankAddress = (ushort)((readAddress & 0x3FFF) + MemoryPageSize * _firstBankPage); + var offsetInPage = (ushort)(readAddress & 0x3FFF); + var bankAddress = (ushort)(offsetInPage + MemoryPageSize * _firstBankPage); return ReadRom(bankAddress); } @@ -247,7 +252,8 @@ private byte ReadMemory(ushort address) { // 0x4000 - 0x8000 // Reading data from slot 2, use page to lookup ROM data - var bankAddress = (ushort)((readAddress & 0x3FFF) + MemoryPageSize * _secondBankPage); + var offsetInPage = (ushort)(readAddress & 0x3FFF); + var bankAddress = (ushort)(offsetInPage + MemoryPageSize * _secondBankPage); return ReadRom(bankAddress); } @@ -268,7 +274,8 @@ private byte ReadMemory(ushort address) // Reading data from slot 2, use page to lookup ROM data // Adjust address to offset page * 2 since slot 3 - var bankAddress = (ushort)((readAddress & 0x3FFF) + MemoryPageSize * _thirdBankPage); + var offsetInPage = (ushort)(readAddress & 0x3FFF); + var bankAddress = (ushort)(offsetInPage + MemoryPageSize * _thirdBankPage); return ReadRom(bankAddress); }