From ed92ce33f63271fb8816316c4281b9ef77b6bf8b Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Fri, 19 Jul 2019 23:59:32 +0100 Subject: [PATCH] Starting to port Fuse test runner for LR35902 emulator. Not running yet. Signed-off-by: Adrian Conlon --- EightBit.sln | 28 + LR35902/LR35902.FuseTest/App.config | 6 + .../LR35902.FuseTest/ExpectedTestResult.cs | 58 + .../LR35902.FuseTest/ExpectedTestResults.cs | 33 + .../LR35902.FuseTest/LR35902.FuseTest.csproj | 73 + LR35902/LR35902.FuseTest/MemoryDatum.cs | 34 + LR35902/LR35902.FuseTest/Program.cs | 11 + .../Properties/AssemblyInfo.cs | 36 + LR35902/LR35902.FuseTest/RegisterState.cs | 44 + LR35902/LR35902.FuseTest/Test.cs | 45 + LR35902/LR35902.FuseTest/TestEvent.cs | 65 + LR35902/LR35902.FuseTest/TestEvents.cs | 26 + LR35902/LR35902.FuseTest/TestRunner.cs | 204 + LR35902/LR35902.FuseTest/TestSuite.cs | 42 + LR35902/LR35902.FuseTest/Tests.cs | 31 + LR35902/LR35902.FuseTest/fuse-tests/README | 79 + .../fuse-tests/tests.expected | 5011 +++++++++++++++++ LR35902/LR35902.FuseTest/fuse-tests/tests.in | 3653 ++++++++++++ 18 files changed, 9479 insertions(+) create mode 100644 LR35902/LR35902.FuseTest/App.config create mode 100644 LR35902/LR35902.FuseTest/ExpectedTestResult.cs create mode 100644 LR35902/LR35902.FuseTest/ExpectedTestResults.cs create mode 100644 LR35902/LR35902.FuseTest/LR35902.FuseTest.csproj create mode 100644 LR35902/LR35902.FuseTest/MemoryDatum.cs create mode 100644 LR35902/LR35902.FuseTest/Program.cs create mode 100644 LR35902/LR35902.FuseTest/Properties/AssemblyInfo.cs create mode 100644 LR35902/LR35902.FuseTest/RegisterState.cs create mode 100644 LR35902/LR35902.FuseTest/Test.cs create mode 100644 LR35902/LR35902.FuseTest/TestEvent.cs create mode 100644 LR35902/LR35902.FuseTest/TestEvents.cs create mode 100644 LR35902/LR35902.FuseTest/TestRunner.cs create mode 100644 LR35902/LR35902.FuseTest/TestSuite.cs create mode 100644 LR35902/LR35902.FuseTest/Tests.cs create mode 100644 LR35902/LR35902.FuseTest/fuse-tests/README create mode 100644 LR35902/LR35902.FuseTest/fuse-tests/tests.expected create mode 100644 LR35902/LR35902.FuseTest/fuse-tests/tests.in diff --git a/EightBit.sln b/EightBit.sln index dad215d..6376d7a 100644 --- a/EightBit.sln +++ b/EightBit.sln @@ -27,6 +27,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MC6850", "MC6850\MC6850.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MC6809.Test", "MC6809\MC6809.Test\MC6809.Test.csproj", "{5ADB4727-2F5F-4A41-979B-6734501E34BC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LR35902", "LR35902\LR35902.csproj", "{01F61A1D-CB4A-4EA3-96EF-222F831DF483}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LR35902.FuseTest", "LR35902\LR35902.FuseTest\LR35902.FuseTest.csproj", "{CC24B08D-1C51-43FD-961D-7C9A49253D69}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -181,6 +185,30 @@ Global {5ADB4727-2F5F-4A41-979B-6734501E34BC}.Release|x64.Build.0 = Release|Any CPU {5ADB4727-2F5F-4A41-979B-6734501E34BC}.Release|x86.ActiveCfg = Release|Any CPU {5ADB4727-2F5F-4A41-979B-6734501E34BC}.Release|x86.Build.0 = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|x64.ActiveCfg = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|x64.Build.0 = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|x86.ActiveCfg = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Debug|x86.Build.0 = Debug|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|Any CPU.Build.0 = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|x64.ActiveCfg = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|x64.Build.0 = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|x86.ActiveCfg = Release|Any CPU + {01F61A1D-CB4A-4EA3-96EF-222F831DF483}.Release|x86.Build.0 = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|x64.Build.0 = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Debug|x86.Build.0 = Debug|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|Any CPU.Build.0 = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|x64.ActiveCfg = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|x64.Build.0 = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|x86.ActiveCfg = Release|Any CPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LR35902/LR35902.FuseTest/App.config b/LR35902/LR35902.FuseTest/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/LR35902/LR35902.FuseTest/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LR35902/LR35902.FuseTest/ExpectedTestResult.cs b/LR35902/LR35902.FuseTest/ExpectedTestResult.cs new file mode 100644 index 0000000..de18608 --- /dev/null +++ b/LR35902/LR35902.FuseTest/ExpectedTestResult.cs @@ -0,0 +1,58 @@ +namespace Fuse +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class ExpectedTestResult + { + private readonly TestEvents events = new TestEvents(); + + public bool Finish { get; private set; } = false; + + public string Description { get; private set; } + + public RegisterState RegisterState { get; } = new RegisterState(); + + public List MemoryData { get; } = new List(); + + public void Read(StreamReader file) + { + this.Finish = false; + do + { + this.Description = file.ReadLine(); + this.Finish = file.EndOfStream; + } + while (string.IsNullOrEmpty(this.Description) && !this.Finish); + + if (this.Finish) + { + return; + } + + this.events.Read(file); + this.RegisterState.Read(file); + + var line = file.ReadLine(); + if (line.Length > 0) + { + throw new InvalidOperationException("EOL swallow failure!!"); + } + + var finished = false; + do + { + line = file.ReadLine(); + finished = string.IsNullOrEmpty(line); + if (!finished) + { + var datum = new MemoryDatum(); + datum.Parse(line); + this.MemoryData.Add(datum); + } + } + while (!finished); + } + } +} diff --git a/LR35902/LR35902.FuseTest/ExpectedTestResults.cs b/LR35902/LR35902.FuseTest/ExpectedTestResults.cs new file mode 100644 index 0000000..1c8b5dc --- /dev/null +++ b/LR35902/LR35902.FuseTest/ExpectedTestResults.cs @@ -0,0 +1,33 @@ +namespace Fuse +{ + using System.Collections.Generic; + using System.IO; + + public class ExpectedTestResults + { + public Dictionary Results { get; } = new Dictionary(); + + public void Read(string path) + { + using (var file = new StreamReader(path)) + { + this.Read(file); + } + } + + private void Read(StreamReader file) + { + var finished = false; + while (!file.EndOfStream) + { + var result = new ExpectedTestResult(); + result.Read(file); + finished = result.Finish; + if (!finished) + { + this.Results[result.Description] = result; + } + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/LR35902.FuseTest.csproj b/LR35902/LR35902.FuseTest/LR35902.FuseTest.csproj new file mode 100644 index 0000000..7c3a94a --- /dev/null +++ b/LR35902/LR35902.FuseTest/LR35902.FuseTest.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {CC24B08D-1C51-43FD-961D-7C9A49253D69} + Exe + Fuse + LR35902.FuseTest + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {6ebf8857-62a3-4ef4-af21-c1844031d7e4} + EightBit + + + {01f61a1d-cb4a-4ea3-96ef-222f831df483} + LR35902 + + + + \ No newline at end of file diff --git a/LR35902/LR35902.FuseTest/MemoryDatum.cs b/LR35902/LR35902.FuseTest/MemoryDatum.cs new file mode 100644 index 0000000..7cd836c --- /dev/null +++ b/LR35902/LR35902.FuseTest/MemoryDatum.cs @@ -0,0 +1,34 @@ +namespace Fuse +{ + using System; + using System.Collections.Generic; + + public class MemoryDatum + { + public ushort Address { get; private set; } = (ushort)EightBit.Mask.Mask16; + + public List Bytes { get; } = new List(); + + public void Parse(string line) + { + var tokens = line.Split(new char[] { ' ', '\t' }); + this.Parse(tokens); + } + + public void Parse(string[] tokens) + { + this.Address = Convert.ToUInt16(tokens[0], 16); + + var finished = false; + for (var i = 1; !finished && (i < tokens.Length); ++i) + { + var token = tokens[i]; + finished = token == "-1"; + if (!finished) + { + this.Bytes.Add(Convert.ToByte(token, 16)); + } + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/Program.cs b/LR35902/LR35902.FuseTest/Program.cs new file mode 100644 index 0000000..5ce7567 --- /dev/null +++ b/LR35902/LR35902.FuseTest/Program.cs @@ -0,0 +1,11 @@ +namespace Fuse +{ + public static class Program + { + public static void Main(string[] args) + { + TestSuite testSuite = new TestSuite("fuse-tests\\tests"); + testSuite.Run(); + } + } +} diff --git a/LR35902/LR35902.FuseTest/Properties/AssemblyInfo.cs b/LR35902/LR35902.FuseTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b13b70b --- /dev/null +++ b/LR35902/LR35902.FuseTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LR35902.FuseTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LR35902.FuseTest")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cc24b08d-1c51-43fd-961d-7c9a49253d69")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LR35902/LR35902.FuseTest/RegisterState.cs b/LR35902/LR35902.FuseTest/RegisterState.cs new file mode 100644 index 0000000..2e3fd71 --- /dev/null +++ b/LR35902/LR35902.FuseTest/RegisterState.cs @@ -0,0 +1,44 @@ +namespace Fuse +{ + using System; + using System.Collections.Generic; + using System.IO; + + using EightBit; + + public class RegisterState + { + public enum Register + { + AF, BC, DE, HL, SP, PC + }; + + public List Registers { get; } = new List(); + public bool Halted { get; private set; } = false; + public int TStates { get; private set; } = -1; + + public void Read(StreamReader file) + { + this.ReadExternalState(file); + this.ReadInternalState(file); + } + + private void ReadInternalState(StreamReader file) + { + var line = file.ReadLine(); + var tokens = line.Split(new char[] { ' ', '\t' }); + + this.Halted = Convert.ToInt32(tokens[0]) == 1; + this.TStates = Convert.ToInt32(tokens[1]); + } + + private void ReadExternalState(StreamReader file) + { + var line = file.ReadLine(); + foreach (var token in line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)) + { + this.Registers.Add(new Register16(Convert.ToUInt16(token, 16))); + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/Test.cs b/LR35902/LR35902.FuseTest/Test.cs new file mode 100644 index 0000000..ea91dc2 --- /dev/null +++ b/LR35902/LR35902.FuseTest/Test.cs @@ -0,0 +1,45 @@ +namespace Fuse +{ + using System.Collections.Generic; + using System.IO; + + public class Test + { + public bool Valid => !string.IsNullOrEmpty(this.Description); + + public string Description { get; private set; } + + public RegisterState RegisterState { get; } = new RegisterState(); + + public List MemoryData { get; } = new List(); + + public void Read(StreamReader file) + { + while ((string.IsNullOrWhiteSpace(this.Description) || this.Description.StartsWith(";")) && !file.EndOfStream) + { + this.Description = file.ReadLine(); + } + + if (!this.Valid) + { + return; + } + + this.RegisterState.Read(file); + + var finished = false; + do + { + var line = file.ReadLine(); + finished = line == "-1"; + if (!finished) + { + var datum = new MemoryDatum(); + datum.Parse(line); + this.MemoryData.Add(datum); + } + } + while (!finished); + } + } +} diff --git a/LR35902/LR35902.FuseTest/TestEvent.cs b/LR35902/LR35902.FuseTest/TestEvent.cs new file mode 100644 index 0000000..dc59cdf --- /dev/null +++ b/LR35902/LR35902.FuseTest/TestEvent.cs @@ -0,0 +1,65 @@ +namespace Fuse +{ + using System; + using System.IO; + + public class TestEvent + { + public bool Valid { get; private set; } = false; + + public int Cycles { get; private set; } = -1; + + public string Specifier { get; private set; } + + public ushort Address { get; private set; } = (ushort)EightBit.Mask.Mask16; + + public ushort Value { get; private set; } = (byte)EightBit.Mask.Mask8; + + public void Read(StreamReader file) + { + var prior = file.BaseStream.Position; + this.ParseLine(file.ReadLine()); + if (!this.Valid) + { + file.BaseStream.Seek(prior, SeekOrigin.Begin); + } + } + + private void ParseLine(string line) + { + var split = line.Split(new char[] { ' ', '\t' }); + this.ParseLine(split); + } + + private void ParseLine(string[] tokens) + { + this.Cycles = int.Parse(tokens[0]); + this.Specifier = tokens[1]; + + this.Valid = true; + switch (this.Specifier) + { + case "MR": + case "MW": + this.Address = Convert.ToUInt16(tokens[2], 16); + this.Value = Convert.ToByte(tokens[3], 16); + break; + + case "MC": + case "PC": + this.Address = Convert.ToUInt16(tokens[2], 16); + break; + + case "PR": + case "PW": + this.Address = Convert.ToUInt16(tokens[2], 16); + this.Value = Convert.ToByte(tokens[3], 16); + break; + + default: + this.Valid = false; + break; + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/TestEvents.cs b/LR35902/LR35902.FuseTest/TestEvents.cs new file mode 100644 index 0000000..0f9decd --- /dev/null +++ b/LR35902/LR35902.FuseTest/TestEvents.cs @@ -0,0 +1,26 @@ +namespace Fuse +{ + using System.Collections.Generic; + using System.IO; + + public class TestEvents + { + public List Events { get; } = new List(); + + public void Read(StreamReader file) + { + var complete = false; + do + { + var e = new TestEvent(); + e.Read(file); + complete = !e.Valid; + if (!complete) + { + this.Events.Add(e); + } + } + while (!complete); + } + } +} diff --git a/LR35902/LR35902.FuseTest/TestRunner.cs b/LR35902/LR35902.FuseTest/TestRunner.cs new file mode 100644 index 0000000..0114c5c --- /dev/null +++ b/LR35902/LR35902.FuseTest/TestRunner.cs @@ -0,0 +1,204 @@ +namespace Fuse +{ + public class TestRunner : EightBit.GameBoy.Bus + { + private readonly Test test; + private readonly ExpectedTestResult expected; + private readonly EightBit.Ram ram = new EightBit.Ram(0x10000); + + public TestRunner(Test test, ExpectedTestResult expected) + { + this.test = test; + this.expected = expected; + } + + public bool Failed { get; private set; } = false; + + public bool Unimplemented { get; private set; } = false; + + public override EightBit.MemoryMapping Mapping(ushort address) => new EightBit.MemoryMapping(this.ram, 0, EightBit.Mask.Mask8, EightBit.AccessLevel.ReadWrite); + + public void Run() + { + this.RaisePOWER(); + this.Initialize(); + var allowedCycles = this.test.RegisterState.TStates; + try + { + this.CPU.Run(allowedCycles); + this.Check(); + } + catch (System.InvalidOperationException error) + { + this.Unimplemented = true; + System.Console.Error.WriteLine($"**** Error: {error.Message}"); + } + } + + public override void RaisePOWER() + { + base.RaisePOWER(); + this.CPU.RaisePOWER(); + this.CPU.RaiseRESET(); + this.CPU.RaiseINT(); + this.InitialiseRegisters(); + this.InitialiseMemory(); + } + + public override void LowerPOWER() + { + this.CPU.LowerPOWER(); + base.LowerPOWER(); + } + + public override void Initialize() => this.DisableGameRom(); + + private void InitialiseRegisters() + { + var testState = this.test.RegisterState; + var inputRegisters = testState.Registers; + + this.CPU.AF.Word = inputRegisters[(int)RegisterState.Register.AF].Word; + this.CPU.BC.Word = inputRegisters[(int)RegisterState.Register.BC].Word; + this.CPU.DE.Word = inputRegisters[(int)RegisterState.Register.DE].Word; + this.CPU.HL.Word = inputRegisters[(int)RegisterState.Register.HL].Word; + + this.CPU.SP.Word = inputRegisters[(int)RegisterState.Register.SP].Word; + this.CPU.PC.Word = inputRegisters[(int)RegisterState.Register.PC].Word; + } + + private void InitialiseMemory() + { + foreach (var memoryDatum in this.test.MemoryData) + { + var address = memoryDatum.Address; + var bytes = memoryDatum.Bytes; + for (var i = 0; i < bytes.Count; ++i) + { + this.Poke((ushort)(address + i), bytes[i]); + } + } + } + + private void Check() + { + this.Checkregisters(); + this.CheckMemory(); + } + + private void Checkregisters() + { + var expectedState = this.expected.RegisterState; + var expectedRegisters = expectedState.Registers; + + var af = this.CPU.AF.Word == expectedRegisters[(int)RegisterState.Register.AF].Word; + var bc = this.CPU.BC.Word == expectedRegisters[(int)RegisterState.Register.BC].Word; + var de = this.CPU.DE.Word == expectedRegisters[(int)RegisterState.Register.DE].Word; + var hl = this.CPU.HL.Word == expectedRegisters[(int)RegisterState.Register.HL].Word; + + var sp = this.CPU.SP.Word == expectedRegisters[(int)RegisterState.Register.SP].Word; + var pc = this.CPU.PC.Word == expectedRegisters[(int)RegisterState.Register.PC].Word; + + var success = af && bc && de && hl && sp && pc; + if (!success) + { + this.Failed = true; + System.Console.Error.WriteLine($"**** Failed test (Register): {this.test.Description}"); + + if (!af) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.AF]; + var actualWord = this.CPU.AF; + DumpDifference("A", "F", expectedWord, actualWord); + } + + if (!bc) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.BC]; + var actualWord = this.CPU.BC; + DumpDifference("B", "C", expectedWord, actualWord); + } + + if (!de) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.DE]; + var actualWord = this.CPU.DE; + DumpDifference("D", "E", expectedWord, actualWord); + } + + if (!hl) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.HL]; + var actualWord = this.CPU.HL; + DumpDifference("H", "L", expectedWord, actualWord); + } + + if (!sp) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.SP]; + var actualWord = this.CPU.SP; + DumpDifference("SPH", "SPL", expectedWord, actualWord); + } + + if (!pc) + { + var expectedWord = expectedRegisters[(int)RegisterState.Register.PC]; + var actualWord = this.CPU.PC; + DumpDifference("PCH", "PCL", expectedWord, actualWord); + } + } + } + + private void CheckMemory() + { + var first = true; + + foreach (var memoryDatum in this.expected.MemoryData) + { + var bytes = memoryDatum.Bytes; + for (var i = 0; i < bytes.Count; ++i) + { + var expected = bytes[i]; + var address = (ushort)(memoryDatum.Address + i); + var actual = this.Peek(address); + if (expected != actual) + { + this.Failed = true; + if (first) + { + first = false; + System.Console.Error.WriteLine($"**** Failed test (Memory): {this.test.Description}"); + } + + System.Console.Error.WriteLine($"**** Difference: Address: {address:x4} Expected: {expected:x2} Actual: {actual:x2}"); + } + } + } + } + + private static void DumpDifference(string description, byte expected, byte actual) + { + var output = $"**** {description}, Expected: {expected:x2}, Got {actual:x2}"; + System.Console.Error.WriteLine(output); + } + + private static void DumpDifference(string highDescription, string lowDescription, EightBit.Register16 expected, EightBit.Register16 actual) + { + var expectedHigh = expected.High; + var expectedLow = expected.Low; + + var actualHigh = actual.High; + var actualLow = actual.Low; + + if (expectedHigh != actualHigh) + { + DumpDifference(highDescription, actualHigh, expectedHigh); + } + + if (expectedLow != actualLow) + { + DumpDifference(lowDescription, actualLow, expectedLow); + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/TestSuite.cs b/LR35902/LR35902.FuseTest/TestSuite.cs new file mode 100644 index 0000000..a485abd --- /dev/null +++ b/LR35902/LR35902.FuseTest/TestSuite.cs @@ -0,0 +1,42 @@ +namespace Fuse +{ + public class TestSuite + { + private readonly Tests tests = new Tests(); + private readonly ExpectedTestResults results = new ExpectedTestResults(); + + public TestSuite(string path) + { + this.tests.Read(path + ".in"); + this.results.Read(path + ".expected"); + } + + public void Run() + { + var failedCount = 0; + var unimplementedCount = 0; + foreach (var test in this.tests.Container) + { + var key = test.Key; + System.Console.Out.WriteLine($"** Checking: {key}"); + + var input = test.Value; + var result = this.results.Results[key]; + var runner = new TestRunner(input, result); + + runner.Run(); + if (runner.Failed) + { + ++failedCount; + } + + if (runner.Unimplemented) + { + ++unimplementedCount; + } + } + System.Console.Out.WriteLine($"+++ Failed test count: {failedCount}"); + System.Console.Out.WriteLine($"+++ Unimplemented test count: {unimplementedCount}"); + } + } +} diff --git a/LR35902/LR35902.FuseTest/Tests.cs b/LR35902/LR35902.FuseTest/Tests.cs new file mode 100644 index 0000000..3acb5f0 --- /dev/null +++ b/LR35902/LR35902.FuseTest/Tests.cs @@ -0,0 +1,31 @@ +namespace Fuse +{ + using System.Collections.Generic; + using System.IO; + + public class Tests + { + public void Read(string path) + { + using (var file = new StreamReader(path)) + { + this.Read(file); + } + } + + public Dictionary Container { get; } = new Dictionary(); + + public void Read(StreamReader file) + { + while (!file.EndOfStream) + { + var test = new Test(); + test.Read(file); + if (test.Valid) + { + this.Container.Add(test.Description, test); + } + } + } + } +} diff --git a/LR35902/LR35902.FuseTest/fuse-tests/README b/LR35902/LR35902.FuseTest/fuse-tests/README new file mode 100644 index 0000000..005c7ee --- /dev/null +++ b/LR35902/LR35902.FuseTest/fuse-tests/README @@ -0,0 +1,79 @@ +File formats +============ + +tests.in +-------- + +Each test has the format: + + +AF BC DE HL SP PC + + + specifies whether the Z80 is halted. + specifies the number of tstates to run the test for, in + decimal; the number actually executed may be higher, as the final + instruction is allowed to complete. + +Then followed by lines specifying the initial memory setup. Each has +the format: + + ... -1 + +eg + +1234 56 78 9a -1 + +says to put 0x56 at 0x1234, 0x78 at 0x1235 and 0x9a at 0x1236. + +Finally, -1 to end the test. Blank lines may follow before the next test. + +tests.expected +-------------- + +Each test output starts with the test description, followed by a list +of 'events': each has the format + +