mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2025-01-10 00:29:23 +00:00
63ef445a78
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
219 lines
7.1 KiB
C#
219 lines
7.1 KiB
C#
// <copyright file="TestRunner.cs" company="Adrian Conlon">
|
|
// Copyright (c) Adrian Conlon. All rights reserved.
|
|
// </copyright>
|
|
|
|
namespace Fuse
|
|
{
|
|
public enum Register
|
|
{
|
|
AF,
|
|
BC,
|
|
DE,
|
|
HL,
|
|
SP,
|
|
PC,
|
|
}
|
|
|
|
public class TestRunner<T> : EightBit.GameBoy.Bus
|
|
where T : Fuse.IRegisterState, new()
|
|
{
|
|
private readonly Test<T> test;
|
|
private readonly Result<T> result;
|
|
private readonly EightBit.Ram ram = new EightBit.Ram(0x10000);
|
|
|
|
public TestRunner(Test<T> test, Result<T> result)
|
|
{
|
|
this.test = test;
|
|
this.result = result;
|
|
}
|
|
|
|
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.Mask16, 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 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);
|
|
}
|
|
}
|
|
|
|
private void InitialiseRegisters()
|
|
{
|
|
var testState = this.test.RegisterState;
|
|
var inputRegisters = testState.Registers;
|
|
|
|
this.CPU.AF.Word = inputRegisters[(int)Register.AF].Word;
|
|
this.CPU.BC.Word = inputRegisters[(int)Register.BC].Word;
|
|
this.CPU.DE.Word = inputRegisters[(int)Register.DE].Word;
|
|
this.CPU.HL.Word = inputRegisters[(int)Register.HL].Word;
|
|
|
|
this.CPU.SP.Word = inputRegisters[(int)Register.SP].Word;
|
|
this.CPU.PC.Word = inputRegisters[(int)Register.PC].Word;
|
|
}
|
|
|
|
private void InitialiseMemory()
|
|
{
|
|
foreach (var memoryDatum in this.test.MemoryData)
|
|
{
|
|
var address = memoryDatum.Address;
|
|
foreach (var seed in memoryDatum.Bytes)
|
|
{
|
|
this.Poke(address++, seed);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Check()
|
|
{
|
|
this.Checkregisters();
|
|
this.CheckMemory();
|
|
}
|
|
|
|
private void Checkregisters()
|
|
{
|
|
var expectedState = this.result.RegisterState;
|
|
var expectedRegisters = expectedState.Registers;
|
|
|
|
var af = this.CPU.AF.Word == expectedRegisters[(int)Register.AF].Word;
|
|
var bc = this.CPU.BC.Word == expectedRegisters[(int)Register.BC].Word;
|
|
var de = this.CPU.DE.Word == expectedRegisters[(int)Register.DE].Word;
|
|
var hl = this.CPU.HL.Word == expectedRegisters[(int)Register.HL].Word;
|
|
|
|
var sp = this.CPU.SP.Word == expectedRegisters[(int)Register.SP].Word;
|
|
var pc = this.CPU.PC.Word == expectedRegisters[(int)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)Register.AF];
|
|
var actualWord = this.CPU.AF;
|
|
DumpDifference("A", "F", expectedWord, actualWord);
|
|
}
|
|
|
|
if (!bc)
|
|
{
|
|
var expectedWord = expectedRegisters[(int)Register.BC];
|
|
var actualWord = this.CPU.BC;
|
|
DumpDifference("B", "C", expectedWord, actualWord);
|
|
}
|
|
|
|
if (!de)
|
|
{
|
|
var expectedWord = expectedRegisters[(int)Register.DE];
|
|
var actualWord = this.CPU.DE;
|
|
DumpDifference("D", "E", expectedWord, actualWord);
|
|
}
|
|
|
|
if (!hl)
|
|
{
|
|
var expectedWord = expectedRegisters[(int)Register.HL];
|
|
var actualWord = this.CPU.HL;
|
|
DumpDifference("H", "L", expectedWord, actualWord);
|
|
}
|
|
|
|
if (!sp)
|
|
{
|
|
var expectedWord = expectedRegisters[(int)Register.SP];
|
|
var actualWord = this.CPU.SP;
|
|
DumpDifference("SPH", "SPL", expectedWord, actualWord);
|
|
}
|
|
|
|
if (!pc)
|
|
{
|
|
var expectedWord = expectedRegisters[(int)Register.PC];
|
|
var actualWord = this.CPU.PC;
|
|
DumpDifference("PCH", "PCL", expectedWord, actualWord);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckMemory()
|
|
{
|
|
var first = true;
|
|
|
|
foreach (var memoryDatum in this.result.MemoryData)
|
|
{
|
|
var address = memoryDatum.Address;
|
|
foreach (var expected in memoryDatum.Bytes)
|
|
{
|
|
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}");
|
|
}
|
|
|
|
++address;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|