// This file is part of the Noddybox.Emulation C# suite.
//
// Noddybox.Emulation is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Noddybox.Emulation is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Noddybox.Emulation. If not, see .
//
// Copyright (c) 2012 Ian Cowburn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Noddybox.Emulation.EightBit.Z80.Disassembler;
namespace Noddybox.Emulation.EightBit.Z80.Test
{
class TestMachine : IMemory, IDevice
{
private readonly byte[] mem = new byte[0x10000];
private readonly Z80Cpu z80 = new Z80Cpu();
private readonly Z80Disassembler disassembler = new Z80Disassembler();
private readonly Clock clock = new Clock(uint.MaxValue, uint.MaxValue);
private void Output(ConsoleColor pen, ConsoleColor paper, string format, params object[] p)
{
Console.ForegroundColor = pen;
Console.BackgroundColor = paper;
Console.Write(format, p);
Console.ResetColor();
Console.WriteLine();
}
private Queue Decode(string i)
{
Queue l = new Queue();
foreach (string s in i.Split(new char[] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries))
{
if (s != "-1")
{
l.Enqueue(new Register16(UInt16.Parse(s, System.Globalization.NumberStyles.HexNumber)));
}
}
return l;
}
byte IMemory.Read(ushort address)
{
// Output(ConsoleColor.Green, ConsoleColor.Black, "Reading {0:X2} from {1:X4}", mem[address], address);
return mem[address];
}
void IMemory.Write(ushort address, byte value)
{
// Output(ConsoleColor.Red, ConsoleColor.Black, "Writing {0:X2} to {1:X4}", value, address);
mem[address] = value;
}
byte IDevice.Read(ushort device)
{
// Output(ConsoleColor.Green, ConsoleColor.DarkGray, "Reading 00 from device {1:X4}", device);
Register16 r = new Register16(device);
return r.high;
}
void IDevice.Write(ushort device, byte value)
{
// Output(ConsoleColor.Red, ConsoleColor.DarkGray, "Writing {0:X2} to device {1:X4}", value, device);
}
public bool Run(string name, Queue input, Queue expected, bool stop)
{
Output(ConsoleColor.Black, ConsoleColor.White, "Running test {0}{1}", name, stop ? " - STOPPING FOR DEBUG THIS PASS" : String.Empty);
Queue line = Decode(input.Dequeue());
Register16 r = line.Dequeue();
z80.Acummulator = r.high;
z80.StatusFlags = (Z80Flags)r.low;
z80.BC_Register = line.Dequeue();
z80.DE_Register = line.Dequeue();
z80.HL_Register = line.Dequeue();
z80.AF_Alternate = line.Dequeue();
z80.BC_Alternate = line.Dequeue();
z80.DE_Alternate = line.Dequeue();
z80.HL_Alternate = line.Dequeue();
z80.IX_Register = line.Dequeue();
z80.IY_Register = line.Dequeue();
z80.StackPointer = line.Dequeue().reg;
z80.ProgramCounter = line.Dequeue().reg;
line = Decode(input.Dequeue());
z80.InterruptVector = line.Dequeue().low;
z80.Refresh = line.Dequeue().low;
z80.IFF1_Register = (line.Dequeue().reg != 0);
z80.IFF2_Register = (line.Dequeue().reg != 0);
z80.IM_Register = line.Dequeue().low;
z80.HaltLine = (line.Dequeue().reg != 0);
Output(ConsoleColor.Gray, ConsoleColor.Black, z80.ToString());
int cyclesToRun = Convert.ToInt32(line.Dequeue().reg.ToString("X"));
while(input.Count > 0)
{
line = Decode(input.Dequeue());
if (line.Count > 1)
{
ushort addr = line.Dequeue().reg;
ushort start = addr;
foreach (Register16 b in line)
{
mem[addr++] = b.low;
}
while(start < addr)
{
string a, b, c;
start = disassembler.Disassemble(start, out a, out b, out c);
Output(ConsoleColor.Yellow, ConsoleColor.Blue, "{0}: {1,-20} ; {2}", a, b, c);
}
}
}
if (stop)
{
Debugger.Break();
}
while (clock.Ticks < cyclesToRun)
{
z80.Step();
}
Output(ConsoleColor.White, ConsoleColor.Black, z80.ToString());
bool ok = true;
while(expected.Peek().StartsWith(" "))
{
// Output(ConsoleColor.DarkGray, ConsoleColor.Black, "Not yet handled - {0}", expected.Dequeue());
expected.Dequeue();
}
line = Decode(expected.Dequeue());
r = line.Dequeue();
if (r.high != z80.Acummulator)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in A, got {1:X}", r.high, z80.Acummulator);
ok = false;
}
if ((Z80Flags)r.low != z80.StatusFlags)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0} in F, got {1}", (Z80Flags)r.low, z80.StatusFlags);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.BC_Register.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in BC, got {1:X}", r.reg, z80.BC_Register.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.DE_Register.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in DE, got {1:X}", r.reg, z80.DE_Register.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.HL_Register.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in HL, got {1:X}", r.reg, z80.HL_Register.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.AF_Alternate.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in AF', got {1:X}", r.reg, z80.AF_Alternate.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.BC_Alternate.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in BC', got {1:X}", r.reg, z80.BC_Alternate.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.DE_Alternate.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in DE', got {1:X}", r.reg, z80.DE_Alternate.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.HL_Alternate.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in HL', got {1:X}", r.reg, z80.HL_Alternate.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.IX_Register.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in IX, got {1:X}", r.reg, z80.IX_Register.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.IY_Register.reg)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in IY, got {1:X}", r.reg, z80.IY_Register.reg);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.StackPointer)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in SP, got {1:X}", r.reg, z80.StackPointer);
ok = false;
}
r = line.Dequeue();
if (r.reg != z80.ProgramCounter)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in PC, got {1:X}", r.reg, z80.ProgramCounter);
ok = false;
}
line = Decode(expected.Dequeue());
r = line.Dequeue();
if (r.low != z80.InterruptVector)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in I, got {1:X}", r.low, z80.InterruptVector);
ok = false;
}
r = line.Dequeue();
if (r.low != z80.Refresh)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in R, got {1:X}", r.low, z80.Refresh);
ok = false;
}
r = line.Dequeue();
if ((r.reg != 0) != z80.IFF1_Register)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0} in IFF1, got {1}", (r.reg != 0), z80.IFF1_Register);
ok = false;
}
r = line.Dequeue();
if ((r.reg != 0) != z80.IFF2_Register)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0} in IFF2, got {1}", (r.reg != 0), z80.IFF2_Register);
ok = false;
}
r = line.Dequeue();
if (r.low != z80.IM_Register)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} in IM, got {1:X}", r.low, z80.IM_Register);
ok = false;
}
r = line.Dequeue();
if ((r.reg != 0) != z80.HaltLine)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0} in HALT, got {1}", (r.reg != 0), z80.HaltLine);
ok = false;
}
int cyclesToTest = Convert.ToInt32(line.Dequeue().reg.ToString("X"));
if (cyclesToTest != clock.Ticks)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0} cycles, got {1}", cyclesToTest, clock.Ticks);
ok = false;
}
while (expected.Count > 0)
{
line = Decode(expected.Dequeue());
if (line.Count > 1)
{
ushort addr = line.Dequeue().reg;
foreach (Register16 b in line)
{
if (mem[addr] != b.low)
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Expected {0:X} at location {1:X}, got {2:X}", b.low, addr, mem[addr]);
ok = false;
}
addr++;
}
}
}
if (ok)
{
Output(ConsoleColor.Yellow, ConsoleColor.Black, "Test passed");
}
else
{
Output(ConsoleColor.Red, ConsoleColor.Black, "Test FAILED");
}
Console.WriteLine();
return ok;
}
public TestMachine()
{
z80.Initialise(this, this, clock);
z80.Reset();
disassembler.Initialise(this);
}
}
}