diff --git a/Kmse.Console/EmulatorService.cs b/Kmse.Console/EmulatorService.cs index 02d5084..b52924e 100644 --- a/Kmse.Console/EmulatorService.cs +++ b/Kmse.Console/EmulatorService.cs @@ -1,9 +1,10 @@ using System.Text; +using Autofac; using Kmse.Console.Logging; 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; @@ -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(); } @@ -217,8 +226,8 @@ 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 += $"SP: {status.StackPointer.Word:X4} "; + data += $"PC: {status.Pc:X4} "; + data += $"SP: {status.StackPointer:X4} "; data += $"I: {status.IRegister:X2} "; data += $"R: {status.RRegister:X2} "; data += $"Halt: {status.Halted}"; diff --git a/Kmse.Console/Logging/SerilogCpuLogger.cs b/Kmse.Console/Logging/SerilogCpuLogger.cs index ff583f0..76a87c9 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; @@ -41,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.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/BitWiseTests/BitWiseTestFixture.cs b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs index 57dd29a..e6fe4eb 100644 --- a/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs +++ b/Kmse.Core.UnitTests/BitWiseTests/BitWiseTestFixture.cs @@ -322,4 +322,40 @@ 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 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.UnitTests/Kmse.Core.UnitTests.csproj b/Kmse.Core.UnitTests/Kmse.Core.UnitTests.csproj index de3a706..49ec504 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/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.UnitTests/Z80CpuTests/CpuExecutionFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs index d4b76d7..86fc1ac 100644 --- a/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuExecutionFixture.cs @@ -1,49 +1,29 @@ using FluentAssertions; -using Kmse.Core.IO; -using Kmse.Core.Memory; using Kmse.Core.Z80; +using Kmse.Core.Z80.Instructions; +using Kmse.Core.Z80.Registers.SpecialPurpose; using NSubstitute; using NUnit.Framework; 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); - _cpu.Initialize(_memory, _io); - } - - private void PrepareForTest() - { - _cpu.Reset(); - _memory.ClearReceivedCalls(); - _io.ClearReceivedCalls(); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(false); - } - - private Z80Cpu _cpu; - private ICpuLogger _cpuLogger; - private IMasterSystemMemory _memory; - private IMasterSystemIoManager _io; - [Test] public void WhenResettingCpu() { - _cpu.Reset(); - _io.Received(1).ClearMaskableInterrupt(); - _io.Received(1).ClearNonMaskableInterrupt(); - var status = _cpu.GetStatus(); + InterruptManagement.SetMaskableInterrupt(); + InterruptManagement.SetNonMaskableInterrupt(); + + Cpu.Reset(); + InterruptManagement.MaskableInterrupt.Should().BeFalse(); + InterruptManagement.NonMaskableInterrupt.Should().BeFalse(); + + 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(); @@ -51,7 +31,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); @@ -68,12 +48,12 @@ 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.Word.Should().Be(0x01); + status.Pc.Should().Be(0x01); } [Test] @@ -82,136 +62,89 @@ 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.Word.Should().Be(0x02); + status.Pc.Should().Be(0x02); } [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); - [Test] - public void WhenExecutingAndNmiSet() - { PrepareForTest(); - // Enable interrupts - _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); + // 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); - _io.NonMaskableInterrupt.Returns(true); - _io.MaskableInterrupt.Returns(false); + var status = Cpu.GetStatus(); + status.Pc.Should().Be(0x04); - var cycleCount = _cpu.ExecuteNextCycle(); - cycleCount.Should().Be(11); - var status = _cpu.GetStatus(); - status.Halted.Should().Be(false); - status.InterruptFlipFlop1.Should().BeFalse(); - status.InterruptFlipFlop2.Should().BeTrue(); - status.Pc.Word.Should().Be(0x66); - - // Pc put onto stack - status.StackPointer.Word.Should().Be(0xDFEE); - // Wrote current PC (0x00) onto stack - _memory.Received(1)[0xDFEF] = 0x00; - _memory.Received(1)[0xDFEE] = 0x01; + Registers.IY.Received(1).ResetBitByRegisterLocation(0, 0x02); } - private static int[] _maskedInterruptModes = { 0, 1, 2 }; - [Test] - [TestCaseSource(nameof(_maskedInterruptModes))] - public void WhenExecutingAndMaskedInterruptSetUsingMode0(int mode) + public void WhenExecutingAndInterruptIsSet() { + // This validates that when executing, it checks for interrupts and processes them + 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); + 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); - // Set EI and then set mode - _cpu.ExecuteNextCycle().Should().Be(4); - _cpu.ExecuteNextCycle().Should().Be(8); + InterruptManagement.ClearMaskableInterrupt(); + InterruptManagement.SetNonMaskableInterrupt(); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(true); + var cycleCount = Cpu.ExecuteNextCycle(); - var cycleCount = _cpu.ExecuteNextCycle(); - var status = _cpu.GetStatus(); + cycleCount.Should().Be(11); + var status = Cpu.GetStatus(); status.Halted.Should().Be(false); status.InterruptFlipFlop1.Should().BeFalse(); - status.InterruptFlipFlop2.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 - _io.DidNotReceive().ClearMaskableInterrupt(); - - status.Pc.Word.Should().Be(0x38); - - // Pc put onto stack - status.StackPointer.Word.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 - _io.Received(1).ClearMaskableInterrupt(); - } - } - - [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); - var status = _cpu.GetStatus(); - status.InterruptFlipFlop1.Should().BeTrue(); status.InterruptFlipFlop2.Should().BeTrue(); + status.Pc.Should().Be(0x66); - // Execute disable interrupt - _cpu.ExecuteNextCycle().Should().Be(4); - status = _cpu.GetStatus(); - status.InterruptFlipFlop1.Should().BeFalse(); - status.InterruptFlipFlop2.Should().BeFalse(); + // 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; } } \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/CpuInstructionsFixture.cs index 0fc8116..bd25265 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.Support; +using Kmse.Core.Z80.Model; 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); - _cpu.Initialize(_memory, _io); - } - - private void PrepareForTest() - { - _cpu.Reset(); - _memory.ClearReceivedCalls(); - _memory.ClearSubstitute(); - _io.ClearReceivedCalls(); - _io.NonMaskableInterrupt.Returns(false); - _io.MaskableInterrupt.Returns(false); - } - - 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] @@ -60,16 +31,16 @@ 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(); - status.Pc.Word.Should().Be(expectedPc); + var status = Cpu.GetStatus(); + status.Pc.Should().Be(expectedPc); } [Test] @@ -83,30 +54,30 @@ 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(); + status.Pc.Should().Be(0x03); - var status = _cpu.GetStatus(); - status.Pc.Word.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; + 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,40 +99,40 @@ 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); + 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(); - status.Pc.Word.Should().Be(0x04); + 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; + 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..941b838 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/CpuTestFixtureBase.cs @@ -0,0 +1,90 @@ +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; +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.Running; +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 IZ80InstructionLogger InstructionLogger; + protected Z80CpuRegisters Registers; + protected Z80CpuManagement CpuManagement; + + protected void PrepareForTest() + { + Cpu.Reset(); + Memory.ClearReceivedCalls(); + Memory.ClearSubstitute(); + Io.ClearReceivedCalls(); + } + + [SetUp] + public void Setup() + { + CpuLogger = Substitute.For(); + Memory = Substitute.For(); + Io = Substitute.For(); + InstructionLogger = new Z80InstructionLogger(CpuLogger); + + var flags = new Z80FlagsManager(); + 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)); + 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 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); + + Registers = new Z80CpuRegisters + { + Pc = pc, + Stack = stack, + Af = af, + Bc = bc, + De = de, + Hl = hl, + IX = ix, + IY = iy, + R = rRegister, + I = iRegister + }; + CpuManagement = new Z80CpuManagement + { + IoManagement = ioManagement, + 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); + } +} \ No newline at end of file diff --git a/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/Data/TestInstructions.json new file mode 100644 index 0000000..365f74c --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/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": "E9EC57FC9F2C14881B58885A68921F0F8C724C8FF59DF1542FD3646E0D21E240", + "IoHash": "CC0450F7D844D75BF5C1F9A43AA9DD3D1DFAE49D2A10115C351E0483E30B8CE3", + "MemoryHash": "FC0FE3D2D663F23D92E659536E49DFB2ACD05E7B2A0D8F37BAF0284E95EE1380" + }, + { + "Name": "LD A,R", + "HexInstructions": "ED,5F", + "CpuHash": "E9EC57FC9F2C14881B58885A68921F0F8C724C8FF59DF1542FD3646E0D21E240", + "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/InstructionHashTests/TestInstructionsFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs new file mode 100644 index 0000000..1605245 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestInstructionsFixture.cs @@ -0,0 +1,268 @@ +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.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.Model; +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 NUnit.Framework; + +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 +/// 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(); + + var instructionLogger = new Z80InstructionLogger(_cpuLogger); + + var flags = new Z80FlagsManager(); + 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)); + 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 Z80CpuInputOutputManager(_io, af.Flags); + 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 + { + 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, + CycleCounter = cycleCounter, + RunningStateManager = runningStateManager + }; + + 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) + 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.InstructionHashTests.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/InstructionHashTests/TestIo.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestIo.cs new file mode 100644 index 0000000..9dde75d --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestIo.cs @@ -0,0 +1,61 @@ +using System.Security.Cryptography; +using Kmse.Core.IO; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests; + +public class TestIo : IMasterSystemIoManager +{ + private readonly Memory _portData = new(new byte[0xFF + 1]); + + 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); + ClearMaskableInterrupt(); + ClearNonMaskableInterrupt(); + } + + 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; + } + + public void ClearMaskableInterrupt() + { + MaskableInterrupt = false; + } + + public void SetNonMaskableInterrupt() + { + NonMaskableInterrupt = true; + } + + public void ClearNonMaskableInterrupt() + { + NonMaskableInterrupt = false; + } + + 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/InstructionHashTests/TestMemory.cs b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestMemory.cs new file mode 100644 index 0000000..4b7f81e --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/InstructionHashTests/TestMemory.cs @@ -0,0 +1,60 @@ +using System.Security.Cryptography; +using Kmse.Core.Cartridge; +using Kmse.Core.Memory; + +namespace Kmse.Core.UnitTests.Z80CpuTests.InstructionHashTests; + +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/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.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/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/Z808BitGeneralPurposeRegisterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs new file mode 100644 index 0000000..27f77d6 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z808BitGeneralPurposeRegisterFixture.cs @@ -0,0 +1,260 @@ +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.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/Z80AccumulatorFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs new file mode 100644 index 0000000..ff4c749 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/GeneralPurpose/Z80AccumulatorFixture.cs @@ -0,0 +1,431 @@ +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.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.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/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/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/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.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/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/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs new file mode 100644 index 0000000..e5589bf --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80ProgramCounterFixture.cs @@ -0,0 +1,110 @@ +using FluentAssertions; +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +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 Z80ProgramCounterFixture +{ + private IZ80FlagsManager _flags; + private IZ80InstructionLogger _instructionLogger; + private IMasterSystemMemory _memory; + private Z80ProgramCounter _programCounter; + private IZ80StackManager _stack; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _instructionLogger = Substitute.For(); + _flags = Substitute.For(); + _stack = Substitute.For(); + _programCounter = new Z80ProgramCounter(_memory, _flags, _stack, _instructionLogger); + } + + [Test] + public void WhenResetThenValueIsZero() + { + _programCounter.Reset(); + _programCounter.Value.Should().Be(0); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0); + } + + [Test] + public void WhenSettingProgramCounterThenValueIsUpdated() + { + _programCounter.Set(0x1234); + _programCounter.Value.Should().Be(0x1234); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x1234); + } + + [Test] + public void WhenProgramCounterMovedForwardOffsetWhichOverflowsThenWrapsAround() + { + _programCounter.Set(ushort.MaxValue); + _programCounter.MoveProgramCounterForward(3); + _programCounter.Value.Should().Be(0x02); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x02); + } + + [Test] + public void WhenProgramCounterMovedForwardByOffsetThenValueIsUpdated() + { + _programCounter.Set(0x1234); + _programCounter.MoveProgramCounterForward(10); + _programCounter.Value.Should().Be(0x123E); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x123E); + } + + [Test] + public void WhenProgramCounterMovedBackwardByOffsetThenValueIsUpdated() + { + _programCounter.Set(0x1234); + _programCounter.MoveProgramCounterBackward(10); + _programCounter.Value.Should().Be(0x122A); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x122A); + } + + [Test] + public void WhenProgramCounterMovedBackwardByOffsetWhichOverflowsThenWrapsAround() + { + _programCounter.Set(0); + _programCounter.MoveProgramCounterBackward(3); + _programCounter.Value.Should().Be(ushort.MaxValue - 2); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(ushort.MaxValue - 2); + } + + [Test] + public void WhenGettingNextDataByteFromMemoryUsingPcValueTheCorrectDataIsReturnedAndPcIncremented() + { + _memory[0x05].Returns((byte)0x12); + _programCounter.Set(0x05); + var data = _programCounter.GetNextDataByte(); + data.Should().Be(0x12); + + _programCounter.Value.Should().Be(0x06); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x06); + + _instructionLogger.Received(1).AddOperationData(0x12); + } + + [Test] + public void WhenGettingNextTwoByteDataFromMemoryUsingPcValueTheCorrectDataIsReturnedAndPcIncrementedByTwo() + { + _memory[0x05].Returns((byte)0x12); + _memory[0x06].Returns((byte)0x34); + _programCounter.Set(0x05); + var data = _programCounter.GetNextTwoDataBytes(); + // Loading byte order is low and then high byte hence swapped order + data.Should().Be(0x3412); + + _programCounter.Value.Should().Be(0x07); + _programCounter.AsUnsigned16BitValue().Word.Should().Be(0x07); + + _instructionLogger.Received(1).AddOperationData(0x3412); + } +} \ 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 new file mode 100644 index 0000000..d4cf050 --- /dev/null +++ b/Kmse.Core.UnitTests/Z80CpuTests/RegisterTests/SpecialPurpose/Z80StackManagementFixture.cs @@ -0,0 +1,157 @@ +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; + +namespace Kmse.Core.UnitTests.Z80CpuTests.RegisterTests.SpecialPurpose; + +public class Z80StackManagementFixture +{ + private ICpuLogger _cpuLogger; + private IZ80FlagsManager _flags; + private IMasterSystemMemory _memory; + private Z80StackManager _stackManager; + + [SetUp] + public void Setup() + { + _memory = Substitute.For(); + _cpuLogger = Substitute.For(); + _flags = Substitute.For(); + _stackManager = new Z80StackManager(_memory, _flags, _cpuLogger); + + _memory.GetMinimumAvailableMemorySize().Returns(100); + _memory.GetMaximumAvailableMemorySize().Returns(0x5000); + _stackManager.Reset(); + } + + [Test] + public void WhenResetThenValueIsTopOfMemoryStack() + { + _stackManager.Reset(); + _stackManager.Value.Should().Be(0xDFF0); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0xDFF0); + } + + [Test] + public void WhenSettingStackPointerThenValueIsUpdated() + { + _stackManager.Set(0x1235); + _stackManager.Value.Should().Be(0x1235); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1235); + } + + [Test] + public void WhenSettingStackPointerFromDataInMemoryThenValueIsUpdated() + { + _memory[0x1122].Returns((byte)0x56); + _memory[0x1123].Returns((byte)0x27); + _stackManager.SetFromDataInMemory(0x1122); + + _stackManager.Value.Should().Be(0x2756); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x2756); + } + + [Test] + public void WhenStackPointerIncrementedThenValueIsUpdated() + { + _stackManager.Set(0x1133); + _stackManager.IncrementStackPointer(); + _stackManager.Value.Should().Be(0x1134); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1134); + } + + [Test] + public void WhenStackPointerDecrementedThenValueIsUpdated() + { + _stackManager.Set(0x1133); + _stackManager.DecrementStackPointer(); + _stackManager.Value.Should().Be(0x1132); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(0x1132); + } + + [Test] + public void WhenStackPointerIncrementedWhichOverflowsThenWrapsAround() + { + _memory.GetMaximumAvailableMemorySize().Returns(1000); + _stackManager.Reset(); + _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))); + } + + [Test] + public void WhenStackPointerDecrementedWhichOverflowsThenLogsError() + { + _memory.GetMinimumAvailableMemorySize().Returns(100); + _stackManager.Reset(); + _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))); + } + + [Test] + public void WhenPushingRegisterToStackThenValueIsWrittenToMemoryAtStackPointerAddressAndPointerDecremented() + { + var register = new Test16BitClass(_memory, _flags); + register.Set(0x1234); + _stackManager.Set(1000); + _stackManager.PushRegisterToStack(register); + + _memory.Received()[999] = 0x12; + _memory.Received()[998] = 0x34; + + _stackManager.Value.Should().Be(998); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(998); + } + + [Test] + public void WhenPoppingRegisterFromStackThenValueIsReadFromMemoryAndStackPointerIncremented() + { + _memory[1000].Returns((byte)0x23); + _memory[1001].Returns((byte)0x67); + + var register = new Test16BitClass(_memory, _flags); + register.Set(0x00); + _stackManager.Set(1000); + _stackManager.PopRegisterFromStack(register); + + register.Value.Should().Be(0x6723); + + _stackManager.Value.Should().Be(1002); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(1002); + } + + [Test] + public void WhenSwappingRegisterWithDataInMemoryAtStackPointerLocationThenValueIsReturnedAndStackPointerUnchanged() + { + _memory[1000].Returns((byte)0x23); + _memory[1001].Returns((byte)0x47); + + var register = new Test16BitClass(_memory, _flags); + register.Set(0x1234); + _stackManager.Set(1000); + _stackManager.SwapRegisterWithDataAtStackPointerAddress(register); + + register.Value.Should().Be(0x4723); + + _memory.Received()[1000] = 0x34; + _memory.Received()[1001] = 0x12; + + _stackManager.Value.Should().Be(1000); + _stackManager.AsUnsigned16BitValue().Word.Should().Be(1000); + } + + private class Test16BitClass : Z8016BitSpecialRegisterBase + { + public Test16BitClass(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } + } +} \ No newline at end of file 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.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/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/IMasterSystemIoManager.cs b/Kmse.Core/IO/IMasterSystemIoManager.cs index 3fbfa2d..c94d700 100644 --- a/Kmse.Core/IO/IMasterSystemIoManager.cs +++ b/Kmse.Core/IO/IMasterSystemIoManager.cs @@ -1,24 +1,9 @@ -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(); - 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/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/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..c5cc8e1 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; @@ -35,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; @@ -53,36 +50,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..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 @@ -84,10 +82,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/Infrastructure/EmulatorModule.cs b/Kmse.Core/Infrastructure/EmulatorModule.cs index 7aca787..8d13580 100644 --- a/Kmse.Core/Infrastructure/EmulatorModule.cs +++ b/Kmse.Core/Infrastructure/EmulatorModule.cs @@ -1,9 +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.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; @@ -13,9 +19,10 @@ protected override void Load(ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As().InstancePerDependency(); - 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 adc6248..476c5ae 100644 --- a/Kmse.Core/MasterSystemMk2.cs +++ b/Kmse.Core/MasterSystemMk2.cs @@ -7,7 +7,9 @@ using Kmse.Core.IO.Vdp; using Kmse.Core.Memory; using Kmse.Core.Z80; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Model; namespace Kmse.Core; @@ -36,12 +38,13 @@ 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; 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; @@ -52,8 +55,7 @@ public MasterSystemMk2(IZ80Cpu cpu, IMasterSystemIoManager io, IVdpPort vdp, ICo _cartridge = cartridge; _memory = memory; _cpuLogger = cpuLogger; - _cpu.Initialize(_memory, _io); - _io.Initialize(_vdp, _controllers, _sound, _debugConsole); + _cpuInterruptManagement = cpuInterruptManagement; } public async Task LoadCartridge(string filename, CancellationToken cancellationToken) @@ -134,11 +136,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; @@ -170,7 +169,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/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..49f26a9 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; @@ -98,6 +118,14 @@ private void Reset() _firstBankPage = 0; _secondBankPage = 1; _thirdBankPage = 2; + _internalRAM.Span.Fill(0x00); + _ramBank0.Span.Fill(0x00); + _ramBank1.Span.Fill(0x00); +} + + private bool IsPagingEnabled() + { + return Bitwise.IsSet(_pagingControl, 3); } private byte ReadRam(ushort address) @@ -152,7 +180,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); @@ -166,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; } @@ -176,8 +205,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 +217,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) @@ -208,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); } @@ -216,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); } @@ -237,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); } diff --git a/Kmse.Core/Utilities/Bitwise.cs b/Kmse.Core/Utilities/Bitwise.cs index 3dbf311..6d0377e 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 High, byte Low) 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/IZ80CpuInputOutputManager.cs b/Kmse.Core/Z80/IO/IZ80CpuInputOutputManager.cs new file mode 100644 index 0000000..da54921 --- /dev/null +++ b/Kmse.Core/Z80/IO/IZ80CpuInputOutputManager.cs @@ -0,0 +1,11 @@ +using Kmse.Core.Z80.Registers; + +namespace Kmse.Core.Z80.IO; + +public interface IZ80CpuInputOutputManager +{ + 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/Z80CpuInputOutputManager.cs b/Kmse.Core/Z80/IO/Z80CpuInputOutputManager.cs new file mode 100644 index 0000000..0d58dc0 --- /dev/null +++ b/Kmse.Core/Z80/IO/Z80CpuInputOutputManager.cs @@ -0,0 +1,56 @@ +using Kmse.Core.IO; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.IO; + +public class Z80CpuInputOutputManager : IZ80CpuInputOutputManager +{ + private readonly IZ80FlagsManager _flags; + private readonly IMasterSystemIoManager _io; + + public Z80CpuInputOutputManager(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; + } + + _flags.SetIfNegative(data); + _flags.SetIfZero(data); + + _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/IZ80Cpu.cs b/Kmse.Core/Z80/IZ80Cpu.cs index 883d7f7..b870c98 100644 --- a/Kmse.Core/Z80/IZ80Cpu.cs +++ b/Kmse.Core/Z80/IZ80Cpu.cs @@ -1,12 +1,9 @@ -using Kmse.Core.IO; -using Kmse.Core.Memory; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Model; namespace Kmse.Core.Z80; public interface IZ80Cpu { - void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io); CpuStatus GetStatus(); void Reset(); int ExecuteNextCycle(); 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/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/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/Instructions/Z80CpuInstructions.cs b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs new file mode 100644 index 0000000..e2e22b3 --- /dev/null +++ b/Kmse.Core/Z80/Instructions/Z80CpuInstructions.cs @@ -0,0 +1,1459 @@ +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 IZ80CpuInputOutputManager _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(); + private readonly Dictionary _ddInstructions = new(); + private readonly Dictionary _edInstructions = new(); + private readonly Dictionary _fdInstructions = new(); + private readonly Dictionary _specialDdcbInstructions = new(); + 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++) + { + AddStandardInstruction(i, cycles, name, description, handleFunc); + } + AddStandardInstruction((byte)(opCode + mask), cycles, name, description, handleFunc); + } + + private void AddDoubleByteInstructionWithMask(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++) + { + AddDoubleByteInstruction(prefix, i, 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) + { + _genericInstructions.Add(opCode, new Instruction(opCode, name, description, cycles, handleFunc)); + } + + private void AddDoubleByteInstruction(byte prefix, byte opCode, int cycles, string name, string description, Action handleFunc) + { + switch (prefix) + { + case 0xCB: _cbInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; + case 0xDD: _ddInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; + case 0xED: _edInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; + case 0xFD: _fdInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; + default: + throw new ArgumentException("Only CB/DD/ED/FD double byte instructions supported"); + } + } + + /// + /// Add special instructions which are a double byte start (ie. FD/DD) prefix, then CB and then a value and then normal op code + /// These require special handling since not a normal CB XX instruction + /// + /// First op code, usually 0xFD or 0xDD + /// Actual op code, after CB and value byte + /// Number of cycles + /// Name of instruction + /// Description of instruction + /// Action to call when op code executed + private void AddSpecialCbInstruction(byte prefix, byte opCode, int cycles, string name, string description, Action handleFunc) + { + switch (prefix) + { + case 0xDD: _specialDdcbInstructions.Add(opCode, new SpecialCbInstruction(prefix, opCode, name, description, cycles, handleFunc)); break; + case 0xFD: _specialFdcbInstructions.Add(opCode, new SpecialCbInstruction(prefix, opCode, name, description, cycles, handleFunc)); break; + default: + throw new ArgumentException("Only FD/DD special CB instructions supported"); + } + } + + private void PopulateInstructions() + { + PopulateCpuControlOperations(); + PopulateJumpCallAndReturnOperations(); + PopulateArthmeticAndLogicalInstructions(); + PopulateBitSetResetAndTestGroupInstructions(); + PopulateRotateAndShiftInstructions(); + PopulateLoadAndExchangeInstructions(); + PopulateExchangeBlockTransferAndSearchInstructions(); + PopulateInputOutputInstructions(); + } + + private void PopulateCpuControlOperations() + { + AddStandardInstruction(0x00, 4, "NOP", "No Operation", (_) => { }); + AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { _runningStateManager.Halt(); }); + + 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() + { + 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", _ => { _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", _ => + { + _b.Decrement(); + var offset = _pc.GetNextDataByte(); + if (_b.Value != 0) + { + _pc.JumpByOffset(offset); + _cycleCounter.Increment(13); + } + + // Not jumping, continue to next instruction + _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", _ => { _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", _ => { _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); }); + 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(); }); + } + + private void PopulateArthmeticAndLogicalInstructions() + { + 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", _ => { _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); }); + 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); }); + 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", _ => + { + //if (_bc.Word == 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; + + // _cycleCounter.Increment(16; + // return; + //} + + CompareIncrement(); + + 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 + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + AddDoubleByteInstruction(0xED, 0xA9, 16, "CPD", "Compare and Decrement", _ => { CompareDecrement(); }); + AddDoubleByteInstruction(0xED, 0xB9, 21 / 16, "CPDR", "Compare, Decrement, Repeat", _ => + { + //if (_bc.Word == 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; + + // _cycleCounter.Increment(16; + // return; + //} + + CompareDecrement(); + + 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 + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + 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", _ => { _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", _ => { _ix.Decrement(); }); + AddDoubleByteInstruction(0xFD, 0x2B, 10, "DEC IY", "Decrement IY", _ => { _iy.Decrement(); }); + + 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", _ => + { + _flags.ClearFlag(Z80StatusFlags.HalfCarryH); + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetFlag(Z80StatusFlags.CarryC); + }); + AddStandardInstruction(0x3F, 4, "CCF", "Complement Carry Flag", _ => + { + _flags.ClearFlag(Z80StatusFlags.AddSubtractN); + _flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, _flags.IsFlagSet(Z80StatusFlags.CarryC)); + _flags.InvertFlag(Z80StatusFlags.CarryC); + }); + 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", _ => { _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 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(); }); + } + + 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 => { _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 => { _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 => { _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 => { _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", _ => { _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", _ => { _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() + { + AddStandardInstructionWithMask(0x78, 7, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x40, 7, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x48, 7, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x50, 7, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x58, 7, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x60, 7, 4, "LD H,r", "Load 8-bit register into H", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x68, 7, 4, "LD L,r", "Load 8-bit register into L", i => { LoadRR(i.OpCode); }); + AddStandardInstructionWithMask(0x70, 5, 7, "LD (HL),r", "Load 8 bit register into (HL)", i => { LoadRR(i.OpCode); }); + // 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.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, _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()); }); + 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/ + // DD & FD + // Officially the 0xDD and 0xFD prefixes cause any instruction that references(HL) to instead work against the IX &IY registers with a displacement, 0xDD for IX and 0xFD for IY. + // The undocumented instructions allows for instructions that refer to just H or L can also be used to access the upper or lower 8 - bit components of IX and IY themselves. + + // These first ones are copies of existing instructions but with a DD/FD prefix + AddDoubleByteInstructionWithMask(0xDD, 0x78, 3, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xDD, 0x40, 3, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xDD, 0x48, 3, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xDD, 0x50, 3, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xDD, 0x58, 3, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xFD, 0x78, 3, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xFD, 0x40, 3, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xFD, 0x48, 3, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xFD, 0x50, 3, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstructionWithMask(0xFD, 0x58, 3, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xDD, 0x7F, 4, "LD A,A", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xDD, 0x47, 4, "LD A,B", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xDD, 0x4F, 4, "LD A,C", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xDD, 0x57, 4, "LD A,D", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xDD, 0x5F, 4, "LD A,E", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xFD, 0x7F, 4, "LD A,A", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); + AddDoubleByteInstruction(0xFD, 0x47, 4, "LD A,B", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); + 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()); }); + 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()); }); + 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", _ => { _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()); }); + 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", _ => + { + _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); + }); + AddDoubleByteInstruction(0xED, 0xB0, DynamicCycleHandling, "LDIR", "Load, Increment, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + _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); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + AddDoubleByteInstruction(0xED, 0xA8, 16, "LDD", "Load and Decrement", _ => + { + _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); + }); + AddDoubleByteInstruction(0xED, 0xB8, DynamicCycleHandling, "LDDR", "Load, Decrement, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + _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 + _flags.ClearFlag(Z80StatusFlags.ParityOverflowPV); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + 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(_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(_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", _ => { _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(_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); }); + 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)((_b.Value << 8) + _c.Value); + var data = _io.ReadPort(portAddress); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Increment(); + _flags.SetIfZero(_b.Value); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + }); + AddDoubleByteInstruction(0xED, 0xB2, DynamicCycleHandling, "INIR", "Input, Increment, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + var data = _io.ReadPort(portAddress); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Increment(); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + AddDoubleByteInstruction(0xED, 0xAA, 16, "IND", "Input and Decrement", _ => + { + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + var data = _io.ReadPort(portAddress); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Decrement(); + _flags.SetIfZero(_b.Value); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + }); + AddDoubleByteInstruction(0xED, 0xBA, DynamicCycleHandling, "INDR", "Input, Decrement, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + var data = _io.ReadPort(portAddress); + _memoryManagement.WriteToMemory(_hl, data); + _b.Decrement(); + _hl.Decrement(); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + + 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)((_b.Value << 8) + _c.Value); + _io.WritePort(portAddress, data); + + _hl.Increment(); + _flags.SetIfZero(_b.Value); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + }); + AddDoubleByteInstruction(0xED, 0xB3, DynamicCycleHandling, "OTIR", "Output, Increment, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + _io.WritePort(portAddress, data); + + _hl.Increment(); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + + AddDoubleByteInstruction(0xED, 0xAB, 16, "OUTD", "Output and Decrement", _ => + { + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + _io.WritePort(portAddress, data); + + _hl.Decrement(); + _flags.SetIfZero(_b.Value); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + }); + AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => + { + 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; + + _cycleCounter.Increment(16); + return; + } + + var data = _memoryManagement.ReadFromMemory(_hl); + _b.Decrement(); + var portAddress = (ushort)((_b.Value << 8) + _c.Value); + _io.WritePort(portAddress, data); + + _hl.Decrement(); + _flags.SetFlag(Z80StatusFlags.ZeroZ); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + + 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 + // hence running instruction again rather than doing a loop here + _pc.MoveProgramCounterBackward(2); + _cycleCounter.Increment(21); + } + else + { + _cycleCounter.Increment(16); + } + }); + } + + 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); + } +} \ No newline at end of file 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..6d4b3c2 --- /dev/null +++ b/Kmse.Core/Z80/Interrupts/Z80InterruptManagement.cs @@ -0,0 +1,185 @@ +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80.Interrupts; + +public class Z80InterruptManagement : IZ80InterruptManagement +{ + private 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 void Reset() + { + ClearMaskableInterrupt(); + ClearNonMaskableInterrupt(); + + InterruptEnableFlipFlopStatus = false; + InterruptEnableFlipFlopTempStorageStatus = false; + InterruptMode = 0; + } + + public bool InterruptWaiting() + { + return NonMaskableInterrupt || (InterruptEnableFlipFlopStatus && MaskableInterrupt); + } + + public void SetMaskableInterrupt() + { + if (!InterruptEnableFlipFlopStatus) + { + // Can only set if interrupts are enabled + return; + } + 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; + ClearMaskableInterrupt(); + } + + public void DisableMaskableInterrupts() + { + InterruptEnableFlipFlopStatus = false; + InterruptEnableFlipFlopTempStorageStatus = false; + ClearMaskableInterrupt(); + } + + 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}"); + + DisableMaskableInterrupts(); + + 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"); + + // Treat this as a NOP which is 4 cycles + return 4; + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/ICpuLogger.cs b/Kmse.Core/Z80/Logging/ICpuLogger.cs similarity index 61% rename from Kmse.Core/Z80/ICpuLogger.cs rename to Kmse.Core/Z80/Logging/ICpuLogger.cs index 6d73ee5..262deb0 100644 --- a/Kmse.Core/Z80/ICpuLogger.cs +++ b/Kmse.Core/Z80/Logging/ICpuLogger.cs @@ -1,8 +1,10 @@ -namespace Kmse.Core.Z80; +namespace Kmse.Core.Z80.Logging; 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/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/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..f53a9ae --- /dev/null +++ b/Kmse.Core/Z80/Memory/Z80CpuMemoryManagement.cs @@ -0,0 +1,73 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers; +using Kmse.Core.Z80.Registers.General; + +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.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.SetIfIncrementOverflow(value); + _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.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.SetIfDecrementOverflow(value); + _flags.SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ No newline at end of file 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/Model/CpuStatus.cs b/Kmse.Core/Z80/Model/CpuStatus.cs new file mode 100644 index 0000000..f073973 --- /dev/null +++ b/Kmse.Core/Z80/Model/CpuStatus.cs @@ -0,0 +1,30 @@ +namespace Kmse.Core.Z80.Model; + +public class CpuStatus +{ + public int CurrentCycleCount { get; init; } + public bool Halted { 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; } + public byte RRegister { get; init; } + public bool InterruptFlipFlop1 { get; init; } + + public bool InterruptFlipFlop2 { get; init; } + public byte InterruptMode { get; init; } + + public bool NonMaskableInterruptStatus { get; init; } + public bool MaskableInterruptStatus { get; init; } + +} \ No newline at end of file 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/Unsigned16BitValue.cs similarity index 75% rename from Kmse.Core/Z80/Support/Z80Register.cs rename to Kmse.Core/Z80/Model/Unsigned16BitValue.cs index 4bab802..e6600b3 100644 --- a/Kmse.Core/Z80/Support/Z80Register.cs +++ b/Kmse.Core/Z80/Model/Unsigned16BitValue.cs @@ -1,9 +1,9 @@ using System.Runtime.InteropServices; -namespace Kmse.Core.Z80.Support; +namespace Kmse.Core.Z80.Model; [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/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 new file mode 100644 index 0000000..409b6fc --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80Accumulator.cs @@ -0,0 +1,32 @@ +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80.Registers.General; + +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(IZ8016BitRegister register, int offset, bool withCarry = false); + void Add(byte value, 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(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(IZ8016BitRegister register, int offset, byte valueToOrAgainst); + void Xor(byte value, byte valueToXorAgainst); + void XorFromMemory(IZ8016BitRegister 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 new file mode 100644 index 0000000..df3ae61 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80AfRegister.cs @@ -0,0 +1,21 @@ +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers.General; + +/// +/// Provides a 16 bit interface to the combined Accumulator and Flag (AF) register +/// +/// +/// 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(); + Unsigned16BitValue ShadowAsUnsigned16BitValue(); +} \ 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..8821b28 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80BcRegister.cs @@ -0,0 +1,7 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80BcRegister : IZ8016BitGeneralPurposeRegister +{ + 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 new file mode 100644 index 0000000..a62e91e --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80DeRegister.cs @@ -0,0 +1,7 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80DeRegister : IZ8016BitGeneralPurposeRegister +{ + 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 new file mode 100644 index 0000000..137c6a0 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80FlagsManager.cs @@ -0,0 +1,42 @@ +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers.General; + +/// +/// 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); + 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 SetIfTwosComplementNegative(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/IZ80HlRegister.cs b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs new file mode 100644 index 0000000..bc3579d --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/IZ80HlRegister.cs @@ -0,0 +1,9 @@ +namespace Kmse.Core.Z80.Registers.General; + +public interface IZ80HlRegister : IZ8016BitGeneralPurposeRegister +{ + 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 new file mode 100644 index 0000000..9532453 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80Accumulator.cs @@ -0,0 +1,400 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.SpecialPurpose; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80Accumulator : Z808BitGeneralPurposeRegister, IZ80Accumulator +{ + public Z80Accumulator(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(memory, 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.SetIfNegative(newAValue); + Flags.SetIfZero(newAValue); + 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.SetIfNegative(newAValue); + Flags.SetIfZero(newAValue); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newAValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + + Memory[hl.Value] = newHlValue; + 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.SetIfNegative(Value); + Flags.SetIfZero(Value); + 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.SetIfTwosComplementNegative(twosComplementValue); + Flags.SetIfZero(twosComplementValue); + + Flags.SetClearFlagConditional(Z80StatusFlags.HalfCarryH, (Value & 0x0F) > 0); + Flags.SetIfDecrementOverflow(Value); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value != 0x00); + + Value = (byte)twosComplementValue; + } + + public void AddFromMemory(IZ8016BitRegister register, int offset, bool withCarry = false) + { + var value = Memory[(ushort)(register.Value + 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.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 + // 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(IZ8016BitRegister register, int offset, bool withCarry = false) + { + var value = Memory[(ushort)(register.Value + 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.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 + 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.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 + Flags.SetClearFlagConditional(Z80StatusFlags.CarryC, Value < value); + } + + public void CompareFromMemory(IZ8016BitRegister register, int offset) + { + var value = Memory[(ushort)(register.Value + offset)]; + Compare(value); + } + + public void And(byte value, byte valueToAndAgainst) + { + var newValue = (byte)(value & valueToAndAgainst); + + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); + Flags.SetFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void AndFromMemory(IZ8016BitRegister register, int offset, byte valueToAndAgainst) + { + var value = Memory[(ushort)(register.Value + offset)]; + And(value, valueToAndAgainst); + } + + public void Or(byte value, byte valueToAndAgainst) + { + var newValue = (byte)(value | valueToAndAgainst); + + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void OrFromMemory(IZ8016BitRegister register, int offset, byte valueToOrAgainst) + { + var value = Memory[(ushort)(register.Value + offset)]; + Or(value, valueToOrAgainst); + } + + public void Xor(byte value, byte valueToXorAgainst) + { + var newValue = (byte)(value ^ valueToXorAgainst); + + Flags.SetIfNegative(newValue); + Flags.SetIfZero((byte)(newValue & 0xFF)); + Flags.ClearFlag(Z80StatusFlags.HalfCarryH); + Flags.SetParityFromValue(newValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + Flags.ClearFlag(Z80StatusFlags.CarryC); + + Set(newValue); + } + + public void XorFromMemory(IZ8016BitRegister register, int offset, byte valueToXorAgainst) + { + var value = Memory[(ushort)(register.Value + 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; + + // Check flags since copying from special register into accumulator + Flags.SetIfNegative(sourceData); + Flags.SetIfZero(sourceData); + + 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..4e28782 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80AfRegister.cs @@ -0,0 +1,100 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80AfRegister : IZ80AfRegister +{ + private readonly IMasterSystemMemory _memory; + + public Z80AfRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, IZ80Accumulator accumulator) + { + _memory = memory; + Flags = flags; + Accumulator = accumulator; + } + + 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(); + } + + 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 Unsigned16BitValue AsUnsigned16BitValue() + { + return new Unsigned16BitValue + { + Low = Flags.Value, + High = Accumulator.Value + }; + } + + public Unsigned16BitValue ShadowAsUnsigned16BitValue() + { + return new Unsigned16BitValue + { + 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 new file mode 100644 index 0000000..10ecbdc --- /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 : Z8016BitGeneralPurposeRegisterBase, IZ80BcRegister +{ + public Z80BcRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, Func registerFactory) + : base(memory, flags) + { + B = registerFactory(); + C = registerFactory(); + } + + protected override IZ808BitRegister HighRegister => B; + protected override IZ808BitRegister LowRegister => C; + 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 new file mode 100644 index 0000000..19a3cef --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80DeRegister.cs @@ -0,0 +1,19 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80DeRegister : Z8016BitGeneralPurposeRegisterBase, IZ80DeRegister +{ + public Z80DeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, + Func registerFactory) + : base(memory, flags) + { + D = registerFactory(); + E = registerFactory(); + } + + protected override IZ808BitRegister HighRegister => D; + protected override IZ808BitRegister LowRegister => E; + 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 new file mode 100644 index 0000000..7fa3d84 --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80FlagsManager.cs @@ -0,0 +1,139 @@ +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80FlagsManager : IZ80FlagsManager +{ + 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; + ShadowValue = 0; + } + + public void Set(byte value) + { + Value = value; + } + + public void SwapWithShadow() + { + (Value, ShadowValue) = (ShadowValue, Value); + } + + 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) + { + // 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++) + { + if (Bitwise.IsSet(value, i)) + { + bitsSet++; + } + } + + SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, bitsSet == 0 || bitsSet % 2 == 0); + } + + 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 SetIfTwosComplementNegative(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); + } +} \ 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..8ebed6c --- /dev/null +++ b/Kmse.Core/Z80/Registers/General/Z80HlRegister.cs @@ -0,0 +1,26 @@ +using Kmse.Core.Memory; + +namespace Kmse.Core.Z80.Registers.General; + +public class Z80HlRegister : Z8016BitGeneralPurposeRegisterBase, IZ80HlRegister +{ + public Z80HlRegister(IMasterSystemMemory memory, IZ80FlagsManager flags, Func registerFactory) + : base(memory, flags) + { + H = registerFactory(); + L = registerFactory(); + } + + protected override IZ808BitRegister HighRegister => H; + protected override IZ808BitRegister LowRegister => L; + public IZ808BitGeneralPurposeRegister H { get; } + public IZ808BitGeneralPurposeRegister 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/IZ8016BitGeneralPurposeRegister.cs b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs new file mode 100644 index 0000000..361c973 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitGeneralPurposeRegister.cs @@ -0,0 +1,29 @@ +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitGeneralPurposeRegister : IZ8016BitRegister +{ + ushort ShadowValue { get; } + void SwapWithShadow(); + Unsigned16BitValue ShadowAsUnsigned16BitValue(); + + 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 Add(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); + 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 new file mode 100644 index 0000000..73a1eec --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitRegister.cs @@ -0,0 +1,20 @@ +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers; + +public interface IZ8016BitRegister +{ + ushort Value { get; } + byte High { get; } + byte Low { get; } + + 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); + Unsigned16BitValue AsUnsigned16BitValue(); +} \ 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..f864627 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ8016BitSpecialRegister.cs @@ -0,0 +1,22 @@ +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 Add(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); + 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 new file mode 100644 index 0000000..3b84fb8 --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ808BitGeneralPurposeRegister.cs @@ -0,0 +1,21 @@ +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 +{ + 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/IZ808BitRegister.cs b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs new file mode 100644 index 0000000..af6688c --- /dev/null +++ b/Kmse.Core/Z80/Registers/IZ808BitRegister.cs @@ -0,0 +1,19 @@ +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; } + public byte ShadowValue { get; } + + 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/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/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..f37fd96 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80ProgramCounter.cs @@ -0,0 +1,52 @@ +using Kmse.Core.Z80.Model; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80ProgramCounter : IZ8016BitSpecialRegister +{ + byte GetNextInstruction(); + byte GetNextDataByte(); + ushort GetNextTwoDataBytes(); + + /// + /// 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); + + /// + /// 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(Unsigned16BitValue 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(); + + bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address); + bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address); + void JumpByOffset(byte offset); + bool JumpByOffsetIfFlagHasStatus(Z80StatusFlags flag, byte offset, bool status); + 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); +} \ 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..b5b3220 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/IZ80StackManager.cs @@ -0,0 +1,13 @@ +using Kmse.Core.Z80.Logging; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public interface IZ80StackManager : IZ8016BitSpecialRegister +{ + 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/Z80IndexRegisterXy.cs b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs new file mode 100644 index 0000000..5a49e63 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80IndexRegisterXy.cs @@ -0,0 +1,68 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +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); + SetHigh(newValue); + CheckIncrementFlags(newValue, oldValue); + } + + public void DecrementHigh() + { + var oldValue = Register.High; + var newValue = (byte)(Register.High - 1); + SetHigh(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + public void IncrementLow() + { + var oldValue = Register.Low; + var newValue = (byte)(Register.Low + 1); + SetLow(newValue); + CheckIncrementFlags(newValue, oldValue); + } + + public void DecrementLow() + { + var oldValue = Register.Low; + var newValue = (byte)(Register.Low - 1); + SetLow(newValue); + CheckDecrementFlags(newValue, oldValue); + } + + private void CheckIncrementFlags(byte newValue, byte oldValue) + { + 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.SetIfIncrementOverflow(oldValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + private void CheckDecrementFlags(byte newValue, byte oldValue) + { + 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.SetIfDecrementOverflow(oldValue); + Flags.SetFlag(Z80StatusFlags.AddSubtractN); + } +} \ 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..3a91e47 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80InterruptPageAddressRegister.cs @@ -0,0 +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, 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 new file mode 100644 index 0000000..a5f98fb --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80MemoryRefreshRegister.cs @@ -0,0 +1,9 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80MemoryRefreshRegister : Z808BitRegister, IZ80MemoryRefreshRegister +{ + 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 new file mode 100644 index 0000000..ce72990 --- /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.Model; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80ProgramCounter : Z8016BitSpecialRegisterBase, IZ80ProgramCounter +{ + private readonly IZ80StackManager _stack; + private readonly IZ80InstructionLogger _instructionLogger; + + public Z80ProgramCounter(IMasterSystemMemory memory, IZ80FlagsManager flags, IZ80StackManager stack, IZ80InstructionLogger instructionLogger) + : base(memory, flags) + { + _stack = stack; + _instructionLogger = instructionLogger; + } + + public byte GetNextInstruction() + { + return GetNextByteByProgramCounter(); + } + + public byte GetNextDataByte() + { + var data = GetNextByteByProgramCounter(); + _instructionLogger.AddOperationData(data); + return data; + } + + public ushort GetNextTwoDataBytes() + { + ushort data = GetNextByteByProgramCounter(); + data += (ushort)(GetNextByteByProgramCounter() << 8); + _instructionLogger.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 + Register.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 + Register.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(Unsigned16BitValue register) + { + // Update PC to execute from the value of the register + Set(register.Word); + } + + public void SetFromStack() + { + _stack.PopRegisterFromStack(this); + } + + public bool Jump16BitIfFlagCondition(Z80StatusFlags flag, ushort address) + { + if (Flags.IsFlagSet(flag)) + { + Set(address); + return true; + } + + return false; + } + + public bool Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) + { + if (!Flags.IsFlagSet(flag)) + { + Set(address); + return true; + } + + return false; + } + + public void JumpByOffset(byte offset) + { + 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 + 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; + } + + return false; + } + + public bool JumpByOffsetIfFlag(Z80StatusFlags flag, byte offset) + { + return JumpByOffsetIfFlagHasStatus(flag, offset, true); + } + + public bool JumpByOffsetIfNotFlag(Z80StatusFlags flag, byte offset) + { + return JumpByOffsetIfFlagHasStatus(flag, offset, false); + } + + public bool CallIfFlagCondition(Z80StatusFlags flag, ushort address) + { + if (Flags.IsFlagSet(flag)) + { + SetAndSaveExisting(address); + return true; + } + + return false; + } + + public bool CallIfNotFlagCondition(Z80StatusFlags flag, ushort address) + { + if (!Flags.IsFlagSet(flag)) + { + SetAndSaveExisting(address); + return true; + } + + return false; + } + + public bool ReturnIfFlagHasStatus(Z80StatusFlags flag, bool status) + { + if (Flags.IsFlagSet(flag) == status) + { + SetFromStack(); + return true; + } + + 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[Register.Word]; + Register.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..8b13265 --- /dev/null +++ b/Kmse.Core/Z80/Registers/SpecialPurpose/Z80StackManager.cs @@ -0,0 +1,85 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers.SpecialPurpose; + +public class Z80StackManager : Z8016BitSpecialRegisterBase, IZ80StackManager +{ + private const ushort DefaultStackAddress = 0xDFF0; + private ICpuLogger _cpuLogger; + private readonly IMasterSystemMemory _memory; + private int _maximumMemorySize; + + public Z80StackManager(IMasterSystemMemory memory, IZ80FlagsManager flags, ICpuLogger cpuLogger) + : base(memory, flags) + { + _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) + { + _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++; + } + + 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()) + { + // 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--; + } + + 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/Z8016BitGeneralPurposeRegisterBase.cs b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs new file mode 100644 index 0000000..14eef92 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitGeneralPurposeRegisterBase.cs @@ -0,0 +1,112 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers; + +/// +/// Base class for a 16 bit register which is composed of two 8 bit registers +/// +public abstract class Z8016BitGeneralPurposeRegisterBase : Z8016BitRegisterBase, IZ8016BitGeneralPurposeRegister +{ + protected Z8016BitGeneralPurposeRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(memory, flags) { } + + protected abstract IZ808BitRegister HighRegister { get; } + protected abstract IZ808BitRegister LowRegister { get; } + + 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() + { + LowRegister.Reset(); + HighRegister.Reset(); + } + + public override void Set(ushort value) + { + 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) + { + 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 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] = LowRegister.Value; + Memory[(ushort)(location + 1)] = HighRegister.Value; + } + + public void SwapWithShadow() + { + LowRegister.SwapWithShadow(); + HighRegister.SwapWithShadow(); + } + + public Unsigned16BitValue AsUnsigned16BitValue() + { + return new Unsigned16BitValue + { + Low = LowRegister.Value, + High = HighRegister.Value + }; + } + + public Unsigned16BitValue ShadowAsUnsigned16BitValue() + { + return new Unsigned16BitValue + { + Low = LowRegister.ShadowValue, + High = HighRegister.ShadowValue + }; + } + + public void Increment() + { + var currentValue = Value; + currentValue++; + Set(currentValue); + } + + public void Decrement() + { + var currentValue = Value; + currentValue--; + Set(currentValue); + } +} \ 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..3391426 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitRegisterBase.cs @@ -0,0 +1,179 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers; + +public abstract class Z8016BitRegisterBase : Z80RegisterBase +{ + protected readonly IMasterSystemMemory Memory; + + protected Z8016BitRegisterBase(IMasterSystemMemory memory, IZ80FlagsManager flags) + : base(flags) + { + Memory = memory; + } + + public abstract ushort Value { get; } + public abstract byte High { get; } + public abstract byte Low { get; } + public abstract void Set(ushort value); + + public void ResetBitByRegisterLocation(int bit, int offset) + { + // 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; + } + + public void SetBitByRegisterLocation(int bit, int offset) + { + // 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 TestBitByRegisterLocation(int bit, int offset) + { + 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.SetIfNegative((ushort)newValue); + Flags.SetIfZero((ushort)(newValue & 0xFFFF)); + Flags.SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, ((Value ^ valueWithCarry) & 0x8000) == 0 && ((Value ^ (newValue & 0xFFFF)) & 0x8000) != 0); + } + + Set((ushort)newValue); + } + + public void Add(IZ8016BitRegister register, bool withCarry = false) + { + Add(register.Value, withCarry); + } + + public void Subtract(ushort source) + { + int valueWithCarry = source; + if (Flags.IsFlagSet(Z80StatusFlags.CarryC)) + { + valueWithCarry += 0x01; + } + + var newValue = Value - valueWithCarry; + + 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.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 Subtract(IZ8016BitRegister register) + { + Subtract(register.Value); + } + + public void RotateLeftCircular(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateLeftCircular(ref value); + Memory[location] = value; + } + + public void RotateLeft(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateLeft(ref value); + Memory[location] = value; + } + + public void RotateRightCircular(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateRightCircular(ref value); + Memory[location] = value; + } + + public void RotateRight(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + RotateRight(ref value); + Memory[location] = value; + } + + public void ShiftLeftArithmetic(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftLeftArithmetic(ref value); + Memory[location] = value; + } + + public void ShiftRightArithmetic(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftRightArithmetic(ref value); + Memory[location] = value; + } + + public void ShiftLeftLogical(int offset) + { + var location = (ushort)(Value + offset); + var value = Memory[location]; + ShiftLeftLogical(ref value); + Memory[location] = value; + } + + public void ShiftRightLogical(int offset) + { + 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..a37a51d --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z8016BitSpecialRegisterBase.cs @@ -0,0 +1,87 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers; + +/// +/// Base class for a true 16 bit register +/// +public abstract class Z8016BitSpecialRegisterBase : Z8016BitRegisterBase, IZ8016BitRegister +{ + protected Unsigned16BitValue Register; + + protected Z8016BitSpecialRegisterBase(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 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 Unsigned16BitValue AsUnsigned16BitValue() + { + 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..9cee595 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z808BitGeneralPurposeRegister.cs @@ -0,0 +1,113 @@ +using Kmse.Core.Memory; +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers; + +public class Z808BitGeneralPurposeRegister : Z808BitRegister, IZ808BitGeneralPurposeRegister +{ + public Z808BitGeneralPurposeRegister(IMasterSystemMemory memory, IZ80FlagsManager flags) : base(memory, flags) { } + + public void Increment() + { + var oldValue = Value; + var newValue = (byte)(Value + 1); + Set(newValue); + 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.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.SetIfIncrementOverflow(oldValue); + Flags.ClearFlag(Z80StatusFlags.AddSubtractN); + } + + private void CheckDecrementFlags(byte newValue, byte oldValue) + { + 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.SetIfDecrementOverflow(oldValue); + 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 new file mode 100644 index 0000000..ecc105c --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z808BitRegister.cs @@ -0,0 +1,71 @@ +using Kmse.Core.Memory; +using Kmse.Core.Z80.Registers.General; + +namespace Kmse.Core.Z80.Registers; + +public abstract class Z808BitRegister : Z80RegisterBase, IZ808BitRegister +{ + protected readonly IMasterSystemMemory Memory; + protected byte InternalShadowValue; + protected byte InternalValue; + + 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; + ShadowValue = 0; + } + + 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); + } +} \ No newline at end of file diff --git a/Kmse.Core/Z80/Registers/Z80RegisterBase.cs b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs new file mode 100644 index 0000000..762e936 --- /dev/null +++ b/Kmse.Core/Z80/Registers/Z80RegisterBase.cs @@ -0,0 +1,174 @@ +using Kmse.Core.Utilities; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; + +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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + + 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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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 + 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) + { + Bitwise.Set(ref newValue, 7); + } + + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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) + { + // 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 + Bitwise.Clear(ref newValue, 7); + + Flags.SetIfNegative(newValue); + Flags.SetIfZero(newValue); + 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/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/Support/CpuStatus.cs b/Kmse.Core/Z80/Support/CpuStatus.cs deleted file mode 100644 index c987bb5..0000000 --- a/Kmse.Core/Z80/Support/CpuStatus.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Kmse.Core.Z80.Support; - -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 Z80Register Pc { get; init; } - public Z80Register StackPointer { get; init; } - public byte IRegister { get; init; } - public byte RRegister { get; init; } - public bool InterruptFlipFlop1 { get; init; } - - public bool InterruptFlipFlop2 { get; init; } - public byte InterruptMode { get; init; } - - public bool NonMaskableInterruptStatus { get; init; } - public bool MaskableInterruptStatus { get; init; } - -} \ 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 3d4ca67..0000000 --- a/Kmse.Core/Z80/Z80Cpu.CoreOperations.cs +++ /dev/null @@ -1,478 +0,0 @@ -using Kmse.Core.Utilities; -using Kmse.Core.Z80.Support; -using Microsoft.Win32; - -namespace Kmse.Core.Z80 -{ - /// - /// Core operations, memory operations, reset, flags, stack operations - /// - 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; - } - - 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; - } - - /// - /// 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); - - // Update PC to execute from new address - _pc.Word = address; - } - - private void SetProgramCounterFromRegister(Z80Register register) - { - // Update PC to execute from the value of the register - _pc.Word = register.Word; - } - - private void ResetProgramCounterFromStack() - { - PopRegisterFromStack(ref _pc); - } - - 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)) - { - SetProgramCounter(address); - } - } - - private void Jump16BitIfNotFlagCondition(Z80StatusFlags flag, ushort address) - { - if (!IsFlagSet(flag)) - { - SetProgramCounter(address); - } - } - - private 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 - 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); - 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 - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(data, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, data == 0); - - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(data); - 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 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); - } - - 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' - 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) - { - LoadFrom16BitRegisterMemoryLocationInto8BitRegisterById(_hl, destinationRegisterId); - _currentCycleCount += 3; - return; - } - - if (destinationRegisterId == 0x06) - { - SaveTo16BitRegisterMemoryLocationFrom8BitRegisterById(_hl, sourceRegisterId); - _currentCycleCount += 3; - return; - } - - ref byte sourceRegister = ref Get8BitRegisterByRIdentifier(sourceRegisterId); - ref byte destinationRegister = ref Get8BitRegisterByRIdentifier(destinationRegisterId); - - destinationRegister = sourceRegister; - } - - private void LoadFrom16BitRegisterMemoryLocationInto8BitRegisterById(Z80Register sourceRegister, byte destinationRegisterId) - { - ref var destinationRegister = ref Get8BitRegisterByRIdentifier(destinationRegisterId); - LoadInto8BitRegisterFromMemory(ref destinationRegister, sourceRegister.Word); - } - - private void SaveTo16BitRegisterMemoryLocationFrom8BitRegisterById(Z80Register destinationRegister, byte sourceRegisterId, byte offset = 0) - { - 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) - { - 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 Load16BitRegisterFrom16BitRegister(ref Z80Register source, Z80Register destination) - { - destination.Word = source.Word; - } - - private void Load8BitRegisterFrom8BitRegister(byte sourceData, ref byte destination) - { - 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]; - } - - 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) - { - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - 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); - } - - 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) - { - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - 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); - } - - private void Increment16Bit(ref Z80Register register) - { - register.Word++; - } - - private void Decrement16Bit(ref Z80Register register) - { - register.Word--; - } - } -} diff --git a/Kmse.Core/Z80/Z80Cpu.Instructions.cs b/Kmse.Core/Z80/Z80Cpu.Instructions.cs deleted file mode 100644 index 9b5588d..0000000 --- a/Kmse.Core/Z80/Z80Cpu.Instructions.cs +++ /dev/null @@ -1,1198 +0,0 @@ -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 -{ - // 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(); - private readonly Dictionary _ddInstructions = new(); - private readonly Dictionary _edInstructions = new(); - private readonly Dictionary _fdInstructions = new(); - private readonly Dictionary _specialDdcbInstructions = new(); - private readonly Dictionary _specialFdcbInstructions = new(); - private const int DynamicCycleHandling = -1; - - 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++) - { - AddStandardInstruction(i, cycles, name, description, handleFunc); - } - AddStandardInstruction((byte)(opCode + mask), cycles, name, description, handleFunc); - } - - private void AddDoubleByteInstructionWithMask(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++) - { - AddDoubleByteInstruction(prefix, i, 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) - { - _genericInstructions.Add(opCode, new Instruction(opCode, name, description, cycles, handleFunc)); - } - - private void AddDoubleByteInstruction(byte prefix, byte opCode, int cycles, string name, string description, Action handleFunc) - { - switch (prefix) - { - case 0xCB: _cbInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; - case 0xDD: _ddInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; - case 0xED: _edInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; - case 0xFD: _fdInstructions.Add(opCode, new Instruction(prefix, opCode, name, description, cycles, handleFunc)); break; - default: - throw new ArgumentException("Only CB/DD/ED/FD double byte instructions supported"); - } - } - - /// - /// Add special instructions which are a double byte start (ie. FD/DD) prefix, then CB and then a value and then normal op code - /// These require special handling since not a normal CB XX instruction - /// - /// First op code, usually 0xFD or 0xDD - /// Actual op code, after CB and value byte - /// Number of cycles - /// Name of instruction - /// Description of instruction - /// Action to call when op code executed - private void AddSpecialCbInstruction(byte prefix, byte opCode, int cycles, string name, string description, Action handleFunc) - { - switch (prefix) - { - case 0xDD: _specialDdcbInstructions.Add(opCode, new SpecialCbInstruction(prefix, opCode, name, description, cycles, handleFunc)); break; - case 0xFD: _specialFdcbInstructions.Add(opCode, new SpecialCbInstruction(prefix, opCode, name, description, cycles, handleFunc)); break; - default: - throw new ArgumentException("Only FD/DD special CB instructions supported"); - } - } - - 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(); - PopulateJumpCallAndReturnOperations(); - PopulateArthmeticAndLogicalInstructions(); - PopulateBitSetResetAndTestGroupInstructions(); - PopulateRotateAndShiftInstructions(); - PopulateLoadAndExchangeInstructions(); - PopulateExchangeBlockTransferAndSearchInstructions(); - PopulateInputOutputInstructions(); - } - - private void PopulateCpuControlOperations() - { - AddStandardInstruction(0x00, 4, "NOP", "No Operation", (_) => { }); - AddStandardInstruction(0x76, 4, "HALT", "Halt", (_) => { _halted = true; }); - - 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; }); - } - - 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(0x10, DynamicCycleHandling, "DJNZ $+2", "Decrement, Jump if Non-Zero", _ => - { - Decrement8Bit(ref _bc.High); - var offset = GetNextDataByte(); - if (_bc.High != 0) - { - JumpByOffset(offset); - _currentCycleCount += 13; - } - - // Not jumping, continue to next instruction - _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(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); }); - - AddDoubleByteInstruction(0xED, 0x4D, 14, "RETI", "Return from Interrupt", _ => { ResetProgramCounterFromStack(); _io.ClearMaskableInterrupt(); }); - AddDoubleByteInstruction(0xED, 0x45, 14, "RETN", "Return from NMI", _ => { ResetProgramCounterFromStack(); _interruptFlipFlop1 = _interruptFlipFlop2; }); - } - - 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(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(_stackPointer, 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, 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); }); - - 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); }); - - 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(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(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); }); - - 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(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); }); - - 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(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(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); }); - - 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(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); }); - - AddDoubleByteInstruction(0xED, 0xA1, 16, "CPI", "Compare and Increment", _ => { CompareIncrement(); }); - AddDoubleByteInstruction(0xED, 0xB1, DynamicCycleHandling, "CPIR", "Compare, Increment, Repeat", _ => - { - //if (_bc.Word == 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; - - // _currentCycleCount += 16; - // return; - //} - - CompareIncrement(); - - if (_bc.Word != 0 && !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 - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - AddDoubleByteInstruction(0xED, 0xA9, 16, "CPD", "Compare and Decrement", _ => { CompareDecrement(); }); - AddDoubleByteInstruction(0xED, 0xB9, 21 / 16, "CPDR", "Compare, Decrement, Repeat", _ => - { - //if (_bc.Word == 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; - - // _currentCycleCount += 16; - // return; - //} - - CompareDecrement(); - - if (_bc.Word != 0 && !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 - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - 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, 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); }); - - 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, 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); }); - - 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, 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); }); - - 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(0x33, 6, "INC SP", "Increment SP", _ => { Increment16Bit(ref _stackPointer); }); - 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, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x34, 23, "INC (IY+d)", "Increment", _ => { IncrementAtRegisterMemoryLocation(_iy, 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); }); - AddStandardInstruction(0x3B, 6, "DEC SP", "Decrement SP", _ => { Decrement16Bit(ref _stackPointer); }); - 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, GetNextDataByte()); }); - AddDoubleByteInstruction(0xFD, 0x35, 23, "DEC (IY+d)", "Decrement", _ => { DecrementAtRegisterMemoryLocation(_iy, GetNextDataByte()); }); - - AddStandardInstruction(0x37, 4, "SCF", "Set Carry Flag", _ => - { - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetFlag(Z80StatusFlags.CarryC); - }); - AddStandardInstruction(0x3F, 4, "CCF", "Complement Carry Flag", _ => - { - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.HalfCarryH, IsFlagSet(Z80StatusFlags.CarryC)); - InvertFlag(Z80StatusFlags.CarryC); - }); - AddStandardInstruction(0x27, 4, "DAA", "Decimal Adjust Accumulator", _ => { DecimalAdjustAccumulator(); }); - AddStandardInstruction(0X2F, 4, "CPL", "Complement", _ => { InvertAccumulatorRegister(); }); - AddDoubleByteInstruction(0xED, 0x44, 8, "NEG", "Negate", _ => { 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 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, 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); }); - } - - 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); }); - - 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(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); }); - - 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); }); - } - - 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); }); - - // 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(); }); - } - - private void PopulateLoadAndExchangeInstructions() - { - AddStandardInstructionWithMask(0x78, 7, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x40, 7, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x48, 7, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x50, 7, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x58, 7, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x60, 7, 4, "LD H,r", "Load 8-bit register into H", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x68, 7, 4, "LD L,r", "Load 8-bit register into L", i => { LoadRR(i.OpCode); }); - AddStandardInstructionWithMask(0x70, 5, 7, "LD (HL),r", "Load 8 bit register into (HL)", i => { LoadRR(i.OpCode); }); - // 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); }); - - 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()); }); - - 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); }); - - 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, 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()); }); - - // These are undocumented but zxdoc still runs them - // https://iot.onl/asm/z80/opcodes/undocumented/ - // DD & FD - // Officially the 0xDD and 0xFD prefixes cause any instruction that references(HL) to instead work against the IX &IY registers with a displacement, 0xDD for IX and 0xFD for IY. - // The undocumented instructions allows for instructions that refer to just H or L can also be used to access the upper or lower 8 - bit components of IX and IY themselves. - - // These first ones are copies of existing instructions but with a DD/FD prefix - AddDoubleByteInstructionWithMask(0xDD, 0x78, 3, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xDD, 0x40, 3, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xDD, 0x48, 3, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xDD, 0x50, 3, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xDD, 0x58, 3, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xFD, 0x78, 3, 4, "LD A,r", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xFD, 0x40, 3, 4, "LD B,r", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xFD, 0x48, 3, 4, "LD C,r", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xFD, 0x50, 3, 4, "LD D,r", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstructionWithMask(0xFD, 0x58, 3, 4, "LD E,r", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xDD, 0x7F, 4, "LD A,A", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xDD, 0x47, 4, "LD A,B", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xDD, 0x4F, 4, "LD A,C", "Load 8-bit register into C", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xDD, 0x57, 4, "LD A,D", "Load 8-bit register into D", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xDD, 0x5F, 4, "LD A,E", "Load 8-bit register into E", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xFD, 0x7F, 4, "LD A,A", "Load 8-bit register into A", i => { LoadRR(i.OpCode); }); - AddDoubleByteInstruction(0xFD, 0x47, 4, "LD A,B", "Load 8-bit register into B", i => { LoadRR(i.OpCode); }); - 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", _ => { 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, 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); }); - // 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(0xED, 0xA0, 16, "LDI", "Load and Increment", _ => - { - CopyMemoryByRegisterLocations(_hl, _de); - Increment16Bit(ref _hl); - Increment16Bit(ref _de); - Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - }); - AddDoubleByteInstruction(0xED, 0xB0, DynamicCycleHandling, "LDIR", "Load, Increment, Repeat", _ => - { - if (_bc.Word == 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; - - _currentCycleCount += 16; - return; - } - - CopyMemoryByRegisterLocations(_hl, _de); - Increment16Bit(ref _hl); - Increment16Bit(ref _de); - Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - - if (_bc.Word != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - AddDoubleByteInstruction(0xED, 0xA8, 16, "LDD", "Load and Decrement", _ => - { - CopyMemoryByRegisterLocations(_hl, _de); - Decrement16Bit(ref _hl); - Decrement16Bit(ref _de); - Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - SetClearFlagConditional(Z80StatusFlags.ParityOverflowPV, _bc.Word != 0); - }); - AddDoubleByteInstruction(0xED, 0xB8, DynamicCycleHandling, "LDDR", "Load, Decrement, Repeat", _ => - { - if (_bc.Word == 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; - - _currentCycleCount += 16; - return; - } - - CopyMemoryByRegisterLocations(_hl, _de); - Decrement16Bit(ref _hl); - Decrement16Bit(ref _de); - Decrement16Bit(ref _bc); - ClearFlag(Z80StatusFlags.HalfCarryH); - 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); - - if (_bc.Word != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - 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); }); - } - - private void PopulateExchangeBlockTransferAndSearchInstructions() - { - AddStandardInstruction(0xE3, 19, "EX (SP),HL", "Exchange HL with Data from Memory Address in SP", _ => { 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); }); - } - - private void PopulateInputOutputInstructions() - { - AddStandardInstruction(0xDB, 11, "IN A,(N)", "Read I/O at N into A", _ => { _af.High = ReadFromIo(_af.High, 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); }); - - 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); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); - }); - AddDoubleByteInstruction(0xED, 0xB2, DynamicCycleHandling, "INIR", "Input, Increment, Repeat", _ => - { - if (_bc.High == 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; - - _currentCycleCount += 16; - return; - } - - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Increment16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); - - if (_bc.High != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - AddDoubleByteInstruction(0xED, 0xAA, 16, "IND", "Input and Decrement", _ => - { - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Decrement16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); - }); - AddDoubleByteInstruction(0xED, 0xBA, DynamicCycleHandling, "INDR", "Input, Decrement, Repeat", _ => - { - if (_bc.High == 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; - - _currentCycleCount += 16; - return; - } - - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - var data = _io.ReadPort(portAddress); - Save8BitRegisterValueToMemory(data, _hl.Word); - Decrement8Bit(ref _bc.High); - Decrement16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); - - if (_bc.High != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - - AddStandardInstruction(0xD3, 11, "OUT (N),A", "Write I/O at n from A", _ => { WriteToIo(_af.High, 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); }); - - AddDoubleByteInstruction(0xED, 0xA3, 16, "OUTI", "Output and Increment", _ => - { - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - _io.WritePort(portAddress, data); - - Increment16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); - }); - AddDoubleByteInstruction(0xED, 0xB3, DynamicCycleHandling, "OTIR", "Output, Increment, Repeat", _ => - { - if (_bc.High == 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; - - _currentCycleCount += 16; - return; - } - - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - _io.WritePort(portAddress, data); - - Increment16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); - - if (_bc.High != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - - AddDoubleByteInstruction(0xED, 0xAB, 16, "OUTD", "Output and Decrement", _ => - { - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - _io.WritePort(portAddress, data); - - Decrement16Bit(ref _hl); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, _bc.High == 0); - SetFlag(Z80StatusFlags.AddSubtractN); - }); - AddDoubleByteInstruction(0xED, 0xBB, DynamicCycleHandling, "OTDR", "Output, Decrement, Repeat", _ => - { - if (_bc.High == 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; - - _currentCycleCount += 16; - return; - } - - var data = GetValueFromMemoryByRegisterLocation(_hl); - Decrement8Bit(ref _bc.High); - var portAddress = (ushort)((_bc.High << 8) + _bc.Low); - _io.WritePort(portAddress, data); - - Decrement16Bit(ref _hl); - SetFlag(Z80StatusFlags.ZeroZ); - SetFlag(Z80StatusFlags.AddSubtractN); - - if (_bc.High != 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 - // hence running instruction again rather than doing a loop here - SetProgramCounter((ushort)(_pc.Word - 2)); - _currentCycleCount += 21; - } - else - { - _currentCycleCount += 16; - } - }); - } - - private enum CbInstructionModes : byte - { - Normal = 0x00, - DD = 0xDD, - FD = 0xFD - } - - private 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}"; - } - } - - /// - /// Special CB instructions where they are DD/FD CB XX - /// - private 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/Z80Cpu.MathAndLogicOperations.cs b/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs deleted file mode 100644 index 2e83c10..0000000 --- a/Kmse.Core/Z80/Z80Cpu.MathAndLogicOperations.cs +++ /dev/null @@ -1,848 +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 || IsFlagSet(Z80StatusFlags.CarryC)) - { - factor |= (0x06 << 4); - SetFlag(Z80StatusFlags.CarryC); - } - else - { - factor &= 0x0F; - ClearFlag(Z80StatusFlags.CarryC); - } - - if ((_af.High & 0x0F) > 9 || IsFlagSet(Z80StatusFlags.HalfCarryH)) - { - factor |= 0x06; - } - else - { - factor &= 0xF0; - } - - if (!IsFlagSet(Z80StatusFlags.AddSubtractN)) - { - _af.High += factor; - } - else - { - _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); - SetParityFromValue(_af.High); - } - - private void InvertAccumulatorRegister() - { - var onesComplementValue = ~_af.High & 0xFF; - - SetFlag(Z80StatusFlags.HalfCarryH); - SetFlag(Z80StatusFlags.AddSubtractN); - - _af.High = (byte)onesComplementValue; - } - - 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); - - _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); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - SetFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - - // This behaviour is not documented - SetClearFlagConditional(Z80StatusFlags.SignS, (bit == 7 && bitSet)); - 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); - - SetClearFlagConditional(Z80StatusFlags.ZeroZ, !bitSet); - SetFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - - // This behaviour is not documented - SetClearFlagConditional(Z80StatusFlags.SignS, bit == 7 && bitSet); - 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 && IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry = value + 0x01; - } - int newValue = destination + valueWithCarry; - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - 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); - - 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); - - destination = (byte)newValue; - } - - private void Add16BitRegisterTo16BitRegister(Z80Register source, ref Z80Register destination, bool withCarry = false) - { - int valueWithCarry = source.Word; - if (withCarry && 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, - (destination.Word & 0x0FFF) + (valueWithCarry & 0x0FFF) > 0x0FFF); - - 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, - (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, - ((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 && IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry += 0x01; - } - int newValue = destination - valueWithCarry; - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((byte)newValue, 7)); - 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); - - // Subtraction went negative, so carried over into next bit - 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 && IsFlagSet(Z80StatusFlags.CarryC)) - { - valueWithCarry += 0x01; - } - int newValue = destination.Word - valueWithCarry; - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet((ushort)newValue, 15)); - 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); - - 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)); - - 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; - - 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); - // Subtraction went negative, so carried over into next bit - //SetClearFlagConditional(Z80StatusFlags.CarryC, difference < 0); - 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); - - 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); - } - - 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); - - 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); - } - - 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); - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - SetFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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); - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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); - - SetClearFlagConditional(Z80StatusFlags.SignS, Bitwise.IsSet(newValue, 7)); - SetClearFlagConditional(Z80StatusFlags.ZeroZ, (newValue & 0xFF) == 0); - ClearFlag(Z80StatusFlags.HalfCarryH); - SetParityFromValue(newValue); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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); - } - - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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 (IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 0 - Bitwise.Set(ref newValue, 0); - } - - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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); - } - - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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 (IsFlagSet(Z80StatusFlags.CarryC)) - { - // Copy carry flag to bit 7 - Bitwise.Set(ref newValue, 7); - } - - ClearFlag(Z80StatusFlags.HalfCarryH); - ClearFlag(Z80StatusFlags.AddSubtractN); - 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); - } - - 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); - - 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 (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); - - 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); - } - - 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); - - 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 (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); - - 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); - - 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)); - - 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); - } - - 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)); - - 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); - - 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)); - - 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); - - 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)); - - 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; - } - - 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 95b7e50..cf39c53 100644 --- a/Kmse.Core/Z80/Z80Cpu.cs +++ b/Kmse.Core/Z80/Z80Cpu.cs @@ -1,348 +1,159 @@ -using System.Text; -using Kmse.Core.IO; +using Kmse.Core.IO; using Kmse.Core.Memory; -using Kmse.Core.Z80.Support; +using Kmse.Core.Z80.Instructions; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.Logging; +using Kmse.Core.Z80.Model; +using Kmse.Core.Z80.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Running; namespace Kmse.Core.Z80; -public partial class Z80Cpu : IZ80Cpu +public class Z80Cpu : IZ80Cpu { - private readonly ICpuLogger _cpuLogger; - private int _currentCycleCount; - private IMasterSystemIoManager _io; - private IMasterSystemMemory _memory; - private bool _halted; - private const int NopCycleCount = 4; - - private Z80Register _af, _bc, _de, _hl; - private Z80Register _afShadow, _bcShadow, _deShadow, _hlShadow; - private Z80Register _ix, _iy; - private Z80Register _pc, _stackPointer; - private byte _iRegister, _rRegister; - - /// - /// Disables interrupts from being accepted if set to False - /// - private bool _interruptFlipFlop1 = false; - /// - /// 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(); - - public Z80Cpu(ICpuLogger cpuLogger) + 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 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 IZ80CpuRunningStateManager _runningStateManager; + private readonly IZ80StackManager _stack; + + public Z80Cpu(IMasterSystemMemory memory, IMasterSystemIoManager io, ICpuLogger cpuLogger, + IZ80InstructionLogger instructionLogger, IZ80CpuInstructions instructions, Z80CpuRegisters registers, Z80CpuManagement cpuManagement) { _cpuLogger = cpuLogger; - _pc = new Z80Register(); - _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(); - _stackPointer = new Z80Register(); - _iRegister = 0; - _rRegister = 0; - PopulateInstructions(); - } + _instructionLogger = instructionLogger; - public void Initialize(IMasterSystemMemory memory, IMasterSystemIoManager io) - { _cpuLogger.Debug("Initializing CPU"); - _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; + + _stack = registers.Stack; + _pc = registers.Pc; + + _interruptManagement = cpuManagement.InterruptManagement; + _cycleCounter = cpuManagement.CycleCounter; + _runningStateManager = cpuManagement.RunningStateManager; + _instructions = instructions; } public CpuStatus GetStatus() { return new CpuStatus { - 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, - StackPointer = _stackPointer, - IRegister = _iRegister, - RRegister = _rRegister, - InterruptFlipFlop1 = _interruptFlipFlop1, - - InterruptFlipFlop2 = _interruptFlipFlop2, - InterruptMode = _interruptMode, - - NonMaskableInterruptStatus = _io.NonMaskableInterrupt, - MaskableInterruptStatus = _io.MaskableInterrupt + CurrentCycleCount = _cycleCounter.CurrentCycleCount, + Halted = _runningStateManager.Halted, + + 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, + RRegister = _rRegister.Value, + InterruptFlipFlop1 = _interruptManagement.InterruptEnableFlipFlopStatus, + + InterruptFlipFlop2 = _interruptManagement.InterruptEnableFlipFlopTempStorageStatus, + InterruptMode = _interruptManagement.InterruptMode, + + NonMaskableInterruptStatus = _interruptManagement.NonMaskableInterrupt, + MaskableInterruptStatus = _interruptManagement.MaskableInterrupt }; } public void Reset() { _cpuLogger.Debug("Resetting CPU"); - _currentCycleCount = 0; - - // Reset program counter back to start - _pc.Word = 0x00; - - _halted = false; - _interruptFlipFlop1 = false; - _interruptFlipFlop2 = false; - _interruptMode = 0; - - _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; + _cycleCounter.Reset(); - _af.Word = 0x00; - _bc.Word = 0x00; - _de.Word = 0x00; - _hl.Word = 0x00; + _pc.Reset(); + _stack.Reset(); + _interruptManagement.Reset(); + _runningStateManager.Reset(); - _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(); + _instructionLogger.StartNewInstruction(0x00); } public int ExecuteNextCycle() { - _currentCycleCount = 0; - _instructionMemoryAddressStart = _pc.Word; - _currentData.Clear(); - - if (_io.NonMaskableInterrupt) - { - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, "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 - SaveAndUpdateProgramCounter(0x66); + _cycleCounter.Reset(); + _instructionLogger.StartNewInstruction(_pc.Value); - // If halted then NMI starts it up again - _halted = false; - - _currentCycleCount += 11; - return _currentCycleCount; - } - - if (_interruptFlipFlop1 && _io.MaskableInterrupt) + if (_interruptManagement.InterruptWaiting()) { - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, "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 - SaveAndUpdateProgramCounter(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; + var cycles = _interruptManagement.ProcessInterrupts(); + _cycleCounter.Increment(cycles); + // If halted and an interrupt occurs, then resume + _runningStateManager.ResumeIfHalted(); + return _cycleCounter.CurrentCycleCount; } - if (_halted) + if (_runningStateManager.Halted) { // NOP until interrupt return NopCycleCount; } - var opCode = GetNextInstruction(); - var instruction = opCode switch - { - 0xCB => ProcessCBOpCode(CbInstructionModes.Normal), - 0xDD => ProcessDDOpCode(), - 0xFD => ProcessFDOpCode(), - 0xED => ProcessEDOpCode(), - _ => ProcessGenericNonPrefixedOpCode(opCode) - }; - + var opCode = _pc.GetNextInstruction(); + var instruction = _instructions.GetInstruction(opCode); if (instruction == null) { // Unhandled instruction, just do a NOP - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, opCode.ToString("X2"), "Unimplemented Instruction", "Unimplemented Instruction", string.Empty); - _currentCycleCount += NopCycleCount; - return _currentCycleCount; + _instructionLogger + .SetOpCode(opCode.ToString("X2"), "Unimplemented Instruction", "Unimplemented Instruction") + .Log(); + + _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); } - _cpuLogger.LogInstruction(_instructionMemoryAddressStart, instruction.GetOpCode(), instruction.Name, instruction.Description, _currentData.ToString()); - - 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)) - { - _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 = 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 = 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 = 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 = 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 = GetNextInstruction(); - if (!_edInstructions.TryGetValue(secondOpCode, out var instruction)) - { - _cpuLogger.Error($"Unhandled 0xED instruction - {secondOpCode:X2}"); - } + _instructionLogger + .SetOpCode(instruction.GetOpCode(), instruction.Name, instruction.Description) + .Log(); - return instruction; + return _cycleCounter.CurrentCycleCount; } } \ No newline at end of file diff --git a/Kmse.Core/Z80/Z80CpuManagement.cs b/Kmse.Core/Z80/Z80CpuManagement.cs new file mode 100644 index 0000000..d13e6b4 --- /dev/null +++ b/Kmse.Core/Z80/Z80CpuManagement.cs @@ -0,0 +1,16 @@ +using Kmse.Core.Z80.Instructions; +using Kmse.Core.Z80.Interrupts; +using Kmse.Core.Z80.IO; +using Kmse.Core.Z80.Memory; +using Kmse.Core.Z80.Running; + +namespace Kmse.Core.Z80; + +public class Z80CpuManagement +{ + public IZ80CpuInputOutputManager IoManagement { get; set; } + 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 new file mode 100644 index 0000000..74563f2 --- /dev/null +++ b/Kmse.Core/Z80/Z80CpuModule.cs @@ -0,0 +1,49 @@ +using Autofac; +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.Registers.General; +using Kmse.Core.Z80.Registers.SpecialPurpose; +using Kmse.Core.Z80.Running; + +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(); + 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