mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2026-03-11 04:41:57 +00:00
Add (non-passing) Z80 HarteTest
This commit is contained in:
184
Z80/HarteTest_Z80/HarteTest_Z80.vcxproj
Normal file
184
Z80/HarteTest_Z80/HarteTest_Z80.vcxproj
Normal file
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>18.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{93f0d716-a83d-4afd-af9d-4f947d1c9f02}</ProjectGuid>
|
||||
<RootNamespace>HarteTestZ80</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>..\inc;..\..\inc;C:\Libraries\boost_1_88_0;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>..\inc;..\..\inc;C:\Libraries\boost_1_88_0;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>..\inc;..\..\inc;C:\Libraries\boost_1_88_0;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>..\inc;..\..\inc;C:\Libraries\boost_1_88_0;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="array_t.h" />
|
||||
<ClInclude Include="byte_t.h" />
|
||||
<ClInclude Include="checker_t.h" />
|
||||
<ClInclude Include="cycles_t.h" />
|
||||
<ClInclude Include="cycle_t.h" />
|
||||
<ClInclude Include="element_t.h" />
|
||||
<ClInclude Include="opcode_test_suite_t.h" />
|
||||
<ClInclude Include="parser_t.h" />
|
||||
<ClInclude Include="ports_t.h" />
|
||||
<ClInclude Include="port_t.h" />
|
||||
<ClInclude Include="processor_test_suite_t.h" />
|
||||
<ClInclude Include="ram_t.h" />
|
||||
<ClInclude Include="simdjson\simdjson.h" />
|
||||
<ClInclude Include="state_t.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="TestRunner.h" />
|
||||
<ClInclude Include="test_t.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="checker_t.cpp" />
|
||||
<ClCompile Include="opcode_test_suite_t.cpp" />
|
||||
<ClCompile Include="parser_t.cpp" />
|
||||
<ClCompile Include="processor_test_suite_t.cpp" />
|
||||
<ClCompile Include="simdjson\simdjson.cpp" />
|
||||
<ClCompile Include="stdafx.cpp" />
|
||||
<ClCompile Include="TestRunner.cpp" />
|
||||
<ClCompile Include="tests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\M6502\src\M6502.vcxproj">
|
||||
<Project>{d8726a1b-bbfe-47ef-9860-26b90140ba66}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\EightBit.vcxproj">
|
||||
<Project>{a9c24bd9-0cb4-4c84-b09b-46b815f9da47}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\src\Z80.vcxproj">
|
||||
<Project>{01974f81-2750-45af-b845-2c903a54a334}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
92
Z80/HarteTest_Z80/HarteTest_Z80.vcxproj.filters
Normal file
92
Z80/HarteTest_Z80/HarteTest_Z80.vcxproj.filters
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="array_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="byte_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="checker_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cycle_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cycles_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="element_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="opcode_test_suite_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="parser_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="processor_test_suite_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ram_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="state_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="test_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TestRunner.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="simdjson\simdjson.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="port_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ports_t.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="checker_t.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="opcode_test_suite_t.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="parser_t.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="processor_test_suite_t.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestRunner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="simdjson\simdjson.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
23
Z80/HarteTest_Z80/TestRunner.cpp
Normal file
23
Z80/HarteTest_Z80/TestRunner.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "stdafx.h"
|
||||
#include "TestRunner.h"
|
||||
|
||||
TestRunner::TestRunner() {}
|
||||
|
||||
EightBit::MemoryMapping TestRunner::mapping(const uint16_t address) noexcept {
|
||||
return { RAM(), 0x0000, 0xffff, EightBit::MemoryMapping::AccessLevel::ReadWrite };
|
||||
}
|
||||
|
||||
void TestRunner::raisePOWER() noexcept {
|
||||
EightBit::Bus::raisePOWER();
|
||||
CPU().raisePOWER();
|
||||
CPU().raiseRESET();
|
||||
CPU().raiseINT();
|
||||
CPU().raiseNMI();
|
||||
}
|
||||
|
||||
void TestRunner::lowerPOWER() noexcept {
|
||||
CPU().lowerPOWER();
|
||||
EightBit::Bus::lowerPOWER();
|
||||
}
|
||||
|
||||
void TestRunner::initialise() {}
|
||||
27
Z80/HarteTest_Z80/TestRunner.h
Normal file
27
Z80/HarteTest_Z80/TestRunner.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <Bus.h>
|
||||
#include <Ram.h>
|
||||
#include <Z80.h>
|
||||
|
||||
class TestRunner final : public EightBit::Bus {
|
||||
private:
|
||||
EightBit::Ram m_ram = 0x10000;
|
||||
EightBit::InputOutput m_ports;
|
||||
EightBit::Z80 m_cpu = { *this, m_ports };
|
||||
|
||||
protected:
|
||||
EightBit::MemoryMapping mapping(uint16_t address) noexcept final;
|
||||
|
||||
public:
|
||||
TestRunner();
|
||||
|
||||
void raisePOWER() noexcept final;
|
||||
void lowerPOWER() noexcept final;
|
||||
|
||||
void initialise() final;
|
||||
|
||||
[[nodiscard]] constexpr auto& RAM() noexcept { return m_ram; }
|
||||
[[nodiscard]] constexpr auto& ports() noexcept { return m_ports; }
|
||||
[[nodiscard]] constexpr auto& CPU() noexcept { return m_cpu; }
|
||||
};
|
||||
61
Z80/HarteTest_Z80/array_t.h
Normal file
61
Z80/HarteTest_Z80/array_t.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
class array_t {
|
||||
private:
|
||||
const simdjson::dom::array m_raw;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] simdjson::dom::array raw() const noexcept { return m_raw; }
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto begin() const noexcept { return raw().begin(); }
|
||||
[[nodiscard]] auto end() const noexcept { return raw().end(); }
|
||||
[[nodiscard]] auto size() const noexcept { return raw().size(); }
|
||||
|
||||
[[nodiscard]] auto at(size_t idx) const noexcept { return raw().at(idx); }
|
||||
[[nodiscard]] auto operator[](size_t idx) const noexcept { return at(idx); }
|
||||
|
||||
protected:
|
||||
|
||||
[[nodiscard]] std::optional<int64_t> maybe_integer_at(size_t idx) const noexcept {
|
||||
auto possible = at(idx);
|
||||
if (possible.has_value() && possible.is_int64())
|
||||
return std::optional<int64_t>(possible.get_int64());
|
||||
return std::optional<int64_t>();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string_view> maybe_string_at(size_t idx) const noexcept {
|
||||
auto possible = at(idx);
|
||||
if (possible.has_value() && possible.is_string())
|
||||
return std::optional<std::string_view>(possible.get_string());
|
||||
return std::optional<std::string_view>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] auto maybe_cast_integer_at(size_t idx) const noexcept {
|
||||
auto original = maybe_integer_at(idx);
|
||||
if (original.has_value())
|
||||
return std::optional<T>(T(original.value()));
|
||||
return std::optional<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto integer_at(size_t idx) const noexcept { return maybe_integer_at(idx).value(); }
|
||||
[[nodiscard]] auto string_at(size_t idx) const noexcept { return maybe_string_at(idx).value(); }
|
||||
|
||||
public:
|
||||
array_t(const simdjson::dom::array input) noexcept
|
||||
: m_raw(input) {}
|
||||
|
||||
[[nodiscard]] auto maybe_address_at(size_t idx) const noexcept {
|
||||
return maybe_cast_integer_at<uint16_t>(idx);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto maybe_byte_at(size_t idx) const noexcept {
|
||||
return maybe_cast_integer_at<uint8_t>(idx);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto address_at(size_t idx) const noexcept { return maybe_address_at(idx).value(); }
|
||||
[[nodiscard]] auto byte_at(size_t idx) const noexcept { return maybe_byte_at(idx).value(); }
|
||||
};
|
||||
16
Z80/HarteTest_Z80/byte_t.h
Normal file
16
Z80/HarteTest_Z80/byte_t.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
#include "array_t.h"
|
||||
|
||||
class byte_t : public array_t {
|
||||
public:
|
||||
byte_t(const simdjson::dom::array input) noexcept
|
||||
: array_t(input) {}
|
||||
|
||||
[[nodiscard]] auto address() const noexcept { return address_at(0); }
|
||||
[[nodiscard]] auto value() const noexcept { return byte_at(1); }
|
||||
};
|
||||
384
Z80/HarteTest_Z80/checker_t.cpp
Normal file
384
Z80/HarteTest_Z80/checker_t.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
#include "stdafx.h"
|
||||
#include "checker_t.h"
|
||||
#include "port_t.h"
|
||||
|
||||
void checker_t::addActualCycle(const uint16_t address, const uint8_t value, const std::string& action) {
|
||||
m_actualCycles.push_back({ address, value, action });
|
||||
}
|
||||
|
||||
void checker_t::addActualCycle(const EightBit::register16_t address, const uint8_t value, const std::string& action) {
|
||||
addActualCycle(address.word, value, action);
|
||||
}
|
||||
|
||||
void checker_t::dumpCycles(const std::string which, const actual_cycles_t& events) {
|
||||
m_messages.push_back(which);
|
||||
dumpCycles(events);
|
||||
}
|
||||
|
||||
void checker_t::dumpCycles(const actual_cycles_t& cycles) {
|
||||
for (const auto& cycle : cycles)
|
||||
dumpCycle(cycle);
|
||||
}
|
||||
|
||||
void checker_t::dumpCycle(const actual_cycle_t& cycle) {
|
||||
dumpCycle(std::get<0>(cycle), std::get<1>(cycle), std::get<2>(cycle));
|
||||
}
|
||||
|
||||
void checker_t::dumpCycles(const std::string which, const cycles_t events) {
|
||||
m_messages.push_back(which);
|
||||
dumpCycles(events);
|
||||
}
|
||||
|
||||
void checker_t::dumpCycles(const cycles_t cycles) {
|
||||
for (const auto cycle : cycles)
|
||||
dumpCycle(cycle_t(cycle));
|
||||
}
|
||||
|
||||
void checker_t::dumpCycle(const cycle_t cycle) {
|
||||
dumpCycle(cycle.address(), cycle.value(), cycle.action());
|
||||
}
|
||||
|
||||
void checker_t::raise(const std::string& what, const uint16_t expected, const uint16_t actual) {
|
||||
os()
|
||||
<< std::setw(2) << std::setfill(' ')
|
||||
<< what
|
||||
<< std::setw(4) << std::setfill('0')
|
||||
<< ": expected: " << (int)expected
|
||||
<< ", actual: " << (int)actual;
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
void checker_t::raise(const std::string& what, const uint8_t expected, const uint8_t actual) {
|
||||
os()
|
||||
<< std::setw(2) << std::setfill(' ')
|
||||
<< what
|
||||
<< std::setfill('0')
|
||||
<< ": expected: " << (int)expected
|
||||
<< ", actual: " << (int)actual;
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
void checker_t::raiseFlags(const std::string& what, const uint8_t expected, const uint8_t actual) {
|
||||
os()
|
||||
<< std::setw(2) << std::setfill(' ')
|
||||
<< what
|
||||
<< std::setfill('0')
|
||||
<< ": expected: " << EightBit::Disassembler::flags(expected)
|
||||
<< ", actual: " << EightBit::Disassembler::flags(actual);
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
void checker_t::raise(const std::string& what, const std::string& expected, const std::string& actual) {
|
||||
os()
|
||||
<< std::setw(0) << std::setfill(' ')
|
||||
<< what
|
||||
<< ": expected: " << expected
|
||||
<< ", actual: " << actual;
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
bool checker_t::check(const std::string& what, const uint16_t address, const uint8_t expected, const uint8_t actual) {
|
||||
const auto success = actual == expected;
|
||||
if (!success) {
|
||||
os() << what << ": " << std::setw(4) << std::setfill('0') << (int)address;
|
||||
raise(os().str(), expected, actual);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
checker_t::checker_t(TestRunner& runner)
|
||||
: m_runner(runner) {}
|
||||
|
||||
void checker_t::initialiseState(test_t test) {
|
||||
initialiseState(test.initial(), test.ports());
|
||||
}
|
||||
|
||||
void checker_t::initialiseState(const state_t initial, std::optional<ports_t> ports) {
|
||||
|
||||
auto& cpu = runner().CPU();
|
||||
|
||||
cpu.PC() = initial.pc();
|
||||
cpu.SP() = initial.sp();
|
||||
|
||||
// Alternate register set, then switch to the main set
|
||||
|
||||
cpu.AF() = initial.af_();
|
||||
cpu.BC() = initial.bc_();
|
||||
cpu.DE() = initial.de_();
|
||||
cpu.HL() = initial.hl_();
|
||||
|
||||
cpu.exx();
|
||||
cpu.exxAF();
|
||||
|
||||
// Now we're in the main register set, so set it up
|
||||
|
||||
cpu.AF() = { initial.f(), initial.a() };
|
||||
cpu.BC() = { initial.c(), initial.b() };
|
||||
cpu.DE() = { initial.e(), initial.d() };
|
||||
cpu.HL() = { initial.l(), initial.h() };
|
||||
|
||||
// miscellaneous registers
|
||||
|
||||
cpu.IV() = initial.i();
|
||||
cpu.REFRESH() = initial.r();
|
||||
|
||||
cpu.IM() = initial.im();
|
||||
|
||||
cpu.Q() = initial.q();
|
||||
|
||||
cpu.IFF1() = initial.iff1();
|
||||
cpu.IFF2() = initial.iff2();
|
||||
|
||||
cpu.MEMPTR() = initial.wz();
|
||||
|
||||
cpu.IX() = initial.ix();
|
||||
cpu.IY() = initial.iy();
|
||||
|
||||
auto& ram = runner().RAM();
|
||||
for (const auto entry : initial.ram()) {
|
||||
const byte_t byte{ entry };
|
||||
const auto address = byte.address();
|
||||
const auto value = byte.value();
|
||||
ram.poke(address, value);
|
||||
}
|
||||
|
||||
if (!ports.has_value())
|
||||
return;
|
||||
|
||||
for (const auto entry : ports.value()) {
|
||||
const port_t port{ entry };
|
||||
const auto address = port.address();
|
||||
const auto value = port.value();
|
||||
const auto type = port.type();
|
||||
if (type == "r") {
|
||||
runner().ports().writeInputPort(address, value);
|
||||
} else if (type == "w") {
|
||||
runner().ports().writeOutputPort(address, value);
|
||||
} else {
|
||||
throw std::out_of_range("Unknown port state type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checker_t::initialise() {
|
||||
|
||||
auto& bus = runner();
|
||||
auto& cpu = bus.CPU();
|
||||
cpu.Ticked.connect([this](EightBit::EventArgs&) {
|
||||
|
||||
auto& bus = runner();
|
||||
auto& cpu = bus.CPU();
|
||||
|
||||
const std::string read = EightBit::Device::lowered(cpu.RD()) ? "r" : "-";
|
||||
const std::string write = EightBit::Device::lowered(cpu.WR()) ? "w" : "-";
|
||||
const std::string memory = EightBit::Device::lowered(cpu.MREQ()) ? "m" : "-";
|
||||
const std::string io = EightBit::Device::lowered(cpu.IORQ()) ? "i" : "-";
|
||||
|
||||
addActualCycle(bus.ADDRESS(), bus.DATA(), read + write + memory + io);
|
||||
});
|
||||
|
||||
os() << std::hex << std::uppercase;
|
||||
}
|
||||
|
||||
//
|
||||
bool checker_t::checkState(test_t test) {
|
||||
|
||||
auto& cpu = runner().CPU();
|
||||
auto& ram = runner().RAM();
|
||||
|
||||
const auto& expected_cycles = test.cycles();
|
||||
const auto& actual_cycles = m_actualCycles;
|
||||
|
||||
size_t actual_idx = 0;
|
||||
for (const auto expected_cycle : expected_cycles) {
|
||||
|
||||
if (actual_idx >= actual_cycles.size()) {
|
||||
m_cycle_count_mismatch = true;
|
||||
return false; // more expected cycles than actual
|
||||
}
|
||||
|
||||
const cycle_t expected{ expected_cycle };
|
||||
const auto& actual = actual_cycles[actual_idx++];
|
||||
|
||||
const auto expected_address = expected.address();
|
||||
const auto actual_address = std::get<0>(actual);
|
||||
check("Cycle address", expected_address, actual_address);
|
||||
|
||||
const auto maybe_expected_value = expected.value();
|
||||
if (maybe_expected_value.has_value()) {
|
||||
const auto expected_value = maybe_expected_value.value();
|
||||
const auto actual_value = std::get<1>(actual);
|
||||
check("Cycle value", expected_value, actual_value);
|
||||
}
|
||||
|
||||
const auto expected_action = expected.action();
|
||||
const auto& actual_action = std::get<2>(actual);
|
||||
check("Cycle action", std::string(expected_action), actual_action);
|
||||
}
|
||||
|
||||
if (actual_idx < actual_cycles.size()) {
|
||||
m_cycle_count_mismatch = true;
|
||||
return false; // less expected cycles than actual
|
||||
}
|
||||
|
||||
if (!m_messages.empty())
|
||||
return false;
|
||||
|
||||
const auto final = test.final();
|
||||
|
||||
const auto pc_good = check("PC", final.pc(), cpu.PC().word);
|
||||
const auto sp_good = check("SP", final.sp(), cpu.SP().word);
|
||||
|
||||
const auto a_good = check("A", final.a(), cpu.A());
|
||||
const auto f_good = check("F", final.f(), cpu.F());
|
||||
const auto af_good = a_good && f_good;
|
||||
|
||||
const auto b_good = check("B", final.b(), cpu.B());
|
||||
const auto c_good = check("C", final.c(), cpu.C());
|
||||
const auto bc_good = b_good && c_good;
|
||||
|
||||
const auto d_good = check("D", final.d(), cpu.D());
|
||||
const auto e_good = check("E", final.e(), cpu.E());
|
||||
const auto de_good = d_good && e_good;
|
||||
|
||||
const auto h_good = check("H", final.h(), cpu.H());
|
||||
const auto l_good = check("L", final.l(), cpu.L());
|
||||
const auto hl_good = h_good && l_good;
|
||||
|
||||
cpu.exxAF();
|
||||
cpu.exx();
|
||||
|
||||
const auto af_alt_good = check("AF'", final.af_(), cpu.AF().word);
|
||||
const auto bc_alt_good = check("BC'", final.bc_(), cpu.BC().word);
|
||||
const auto de_alt_good = check("DE'", final.de_(), cpu.DE().word);
|
||||
const auto hl_alt_good = check("DE'", final.de_(), cpu.DE().word);
|
||||
|
||||
const auto i_good = check("I", final.i(), cpu.IV());
|
||||
const auto r_good = check("R", final.r(), (uint8_t)cpu.REFRESH());
|
||||
|
||||
const auto im_good = check("IM", final.im(), (uint8_t)cpu.IM());
|
||||
|
||||
const auto q_good = check("Q", final.im(), (uint8_t)cpu.IM());
|
||||
|
||||
const auto iff1_good = check("IFF1", final.iff1(), (uint8_t)cpu.IFF1());
|
||||
const auto iff2_good = check("IFF2", final.iff2(), (uint8_t)cpu.IFF2());
|
||||
const auto iff_good = iff1_good && iff2_good;
|
||||
|
||||
const auto memptr_good = check("MEMPTR", final.wz(), cpu.MEMPTR().word);
|
||||
|
||||
const auto ix_good = check("IX", final.ix(), cpu.IX().word);
|
||||
const auto iy_good = check("IY", final.iy(), cpu.IY().word);
|
||||
|
||||
bool ram_problem = false;
|
||||
for (const auto entry : final.ram()) {
|
||||
const auto byte = byte_t{ entry };
|
||||
const auto address = byte.address();
|
||||
const auto value = byte.value();
|
||||
if (!check("RAM", address, value, ram.peek(address)))
|
||||
ram_problem = true;
|
||||
}
|
||||
|
||||
return
|
||||
pc_good && sp_good
|
||||
&& af_good && bc_good && de_good && hl_good
|
||||
&& af_alt_good && bc_alt_good && de_alt_good && hl_alt_good
|
||||
&& i_good && r_good
|
||||
&& im_good
|
||||
&& q_good
|
||||
&& iff_good
|
||||
&& memptr_good
|
||||
&& ix_good && iy_good
|
||||
&& !ram_problem;
|
||||
}
|
||||
//
|
||||
void checker_t::pushCurrentMessage() {
|
||||
m_messages.push_back(os().str());
|
||||
os().str("");
|
||||
}
|
||||
|
||||
std::string checker_t::disassemble(uint16_t address) {
|
||||
return m_disassembler.disassemble(runner().CPU(), address);
|
||||
}
|
||||
|
||||
void checker_t::add_disassembly(uint16_t address) {
|
||||
try {
|
||||
os() << disassemble(address);
|
||||
}
|
||||
catch (const std::domain_error& error) {
|
||||
os() << "Disassembly problem: " << error.what();
|
||||
}
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
void checker_t::check(test_t test) {
|
||||
|
||||
auto& cpu = runner().CPU();
|
||||
|
||||
m_messages.clear();
|
||||
m_actualCycles.clear();
|
||||
|
||||
runner().raisePOWER();
|
||||
initialiseState(test);
|
||||
const auto pc = cpu.PC().word;
|
||||
|
||||
m_cycles = cpu.step();
|
||||
runner().lowerPOWER();
|
||||
|
||||
m_valid = checkState(test);
|
||||
|
||||
if (unimplemented()) {
|
||||
m_messages.push_back("Unimplemented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (invalid() && implemented()) {
|
||||
|
||||
add_disassembly(pc);
|
||||
|
||||
const auto final = test.final();
|
||||
raise("PC", final.pc(), cpu.PC().word);
|
||||
raise("SP", final.sp(), cpu.SP().word);
|
||||
|
||||
raise("A", final.a(), cpu.A());
|
||||
raiseFlags("F", final.a(), cpu.A());
|
||||
raise("B", final.b(), cpu.B());
|
||||
raise("C", final.c(), cpu.C());
|
||||
raise("D", final.d(), cpu.D());
|
||||
raise("E", final.e(), cpu.E());
|
||||
raise("H", final.h(), cpu.H());
|
||||
raise("L", final.l(), cpu.L());
|
||||
|
||||
cpu.exx();
|
||||
cpu.exxAF();
|
||||
|
||||
raise("'AF", final.af_(), cpu.AF().word);
|
||||
raise("'BC", final.bc_(), cpu.BC().word);
|
||||
raise("'DE", final.de_(), cpu.DE().word);
|
||||
raise("'HL", final.hl_(), cpu.HL().word);
|
||||
|
||||
raise("I", final.i(), cpu.IV());
|
||||
raise("R", final.r(), cpu.REFRESH());
|
||||
|
||||
raise("IM", final.im(), (uint8_t)cpu.IM());
|
||||
|
||||
raise("Q", final.q(), cpu.Q());
|
||||
|
||||
raise("IFF1", final.iff1(), (uint8_t)cpu.IFF1());
|
||||
raise("IFF2", final.iff2(), (uint8_t)cpu.IFF2());
|
||||
|
||||
raise("WZ", final.wz(), cpu.MEMPTR().word);
|
||||
|
||||
raise("IX", final.ix(), cpu.IX().word);
|
||||
raise("IY", final.iy(), cpu.IY().word);
|
||||
|
||||
os()
|
||||
<< std::dec << std::setfill(' ')
|
||||
<< "Stepped cycles: " << cycles()
|
||||
<< ", expected events: " << test.cycles().size()
|
||||
<< ", actual events: " << m_actualCycles.size();
|
||||
pushCurrentMessage();
|
||||
|
||||
dumpCycles("-- Expected cycles", test.cycles());
|
||||
dumpCycles("-- Actual cycles", m_actualCycles);
|
||||
}
|
||||
}
|
||||
102
Z80/HarteTest_Z80/checker_t.h
Normal file
102
Z80/HarteTest_Z80/checker_t.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include <Disassembler.h>
|
||||
|
||||
#include "cycles_t.h"
|
||||
#include "cycle_t.h"
|
||||
#include "test_t.h"
|
||||
|
||||
#include "TestRunner.h"
|
||||
|
||||
class checker_t {
|
||||
private:
|
||||
TestRunner& m_runner;
|
||||
EightBit::Disassembler m_disassembler = { m_runner };
|
||||
|
||||
std::ostringstream m_os;
|
||||
std::vector<std::string> m_messages;
|
||||
|
||||
typedef std::tuple<uint16_t, uint8_t, std::string> actual_cycle_t;
|
||||
typedef std::vector<actual_cycle_t> actual_cycles_t;
|
||||
actual_cycles_t m_actualCycles;
|
||||
bool m_cycle_count_mismatch = false;
|
||||
|
||||
int m_cycles = 0;
|
||||
bool m_valid = true;
|
||||
|
||||
[[nodiscard]] constexpr auto& os() noexcept { return m_os; }
|
||||
|
||||
[[nodiscard]] constexpr auto& runner() noexcept { return m_runner; }
|
||||
|
||||
[[nodiscard]] bool checkState(test_t test);
|
||||
|
||||
void pushCurrentMessage();
|
||||
|
||||
void raise(const std::string& what, uint16_t expected, uint16_t actual);
|
||||
void raise(const std::string& what, uint8_t expected, uint8_t actual);
|
||||
void raiseFlags(const std::string& what, uint8_t expected, uint8_t actual);
|
||||
void raise(const std::string& what, const std::string& expected, const std::string& actual);
|
||||
|
||||
template<class T>
|
||||
constexpr bool check(const std::string& what, T expected, T actual) noexcept {
|
||||
const auto success = actual == expected;
|
||||
if (!success)
|
||||
raise(what, expected, actual);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool check(const std::string& what, uint16_t address, uint8_t expected, uint8_t actual);
|
||||
|
||||
void addActualCycle(uint16_t address, uint8_t value, const std::string& action);
|
||||
void addActualCycle(EightBit::register16_t address, uint8_t value, const std::string& action);
|
||||
|
||||
void add_disassembly(uint16_t address);
|
||||
|
||||
template<class T>
|
||||
void dumpCycle(const uint16_t address, const std::optional<uint8_t> value, const T action) {
|
||||
if (value.has_value())
|
||||
m_os
|
||||
<< std::setfill('0') << std::hex
|
||||
<< "Address: " << std::setw(4) << (int)address
|
||||
<< ", value: " << std::setw(2) << (int)value.value()
|
||||
<< ", action: " << action;
|
||||
else
|
||||
m_os
|
||||
<< std::setfill('0') << std::hex
|
||||
<< "Address: " << std::setw(4) << (int)address
|
||||
<< " "
|
||||
<< ", action: " << action;
|
||||
pushCurrentMessage();
|
||||
}
|
||||
|
||||
void dumpCycles(std::string which, cycles_t cycles);
|
||||
void dumpCycles(cycles_t cycles);
|
||||
void dumpCycle(cycle_t cycle);
|
||||
|
||||
void dumpCycles(std::string which, const actual_cycles_t& cycles);
|
||||
void dumpCycles(const actual_cycles_t& cycles);
|
||||
void dumpCycle(const actual_cycle_t& cycle);
|
||||
|
||||
void initialiseState(test_t test);
|
||||
void initialiseState(state_t state, std::optional<ports_t> ports);
|
||||
|
||||
public:
|
||||
checker_t(TestRunner& runner);
|
||||
|
||||
[[nodiscard]] constexpr auto cycles() const noexcept { return m_cycles; }
|
||||
[[nodiscard]] constexpr auto valid() const noexcept { return m_valid; }
|
||||
[[nodiscard]] constexpr auto invalid() const noexcept { return !valid(); }
|
||||
[[nodiscard]] constexpr auto unimplemented() const noexcept { return invalid() && m_cycle_count_mismatch && (cycles() == 1); }
|
||||
[[nodiscard]] constexpr auto implemented() const noexcept { return !unimplemented(); }
|
||||
|
||||
[[nodiscard]] constexpr const auto& messages() const noexcept { return m_messages; }
|
||||
|
||||
void initialise();
|
||||
|
||||
[[nodiscard]] std::string disassemble(uint16_t address);
|
||||
|
||||
void check(test_t test);
|
||||
};
|
||||
17
Z80/HarteTest_Z80/cycle_t.h
Normal file
17
Z80/HarteTest_Z80/cycle_t.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
#include "byte_t.h"
|
||||
|
||||
class cycle_t final : public array_t {
|
||||
public:
|
||||
cycle_t(const simdjson::dom::array input) noexcept
|
||||
: array_t(input) {}
|
||||
|
||||
[[nodiscard]] auto address() const noexcept { return address_at(0); }
|
||||
[[nodiscard]] auto value() const noexcept { return maybe_byte_at(1); }
|
||||
[[nodiscard]] auto action() const noexcept { return string_at(2); }
|
||||
};
|
||||
5
Z80/HarteTest_Z80/cycles_t.h
Normal file
5
Z80/HarteTest_Z80/cycles_t.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "array_t.h"
|
||||
|
||||
typedef array_t cycles_t; // Intended to indicate that cycles_t is meant to hold cycle_t objects
|
||||
24
Z80/HarteTest_Z80/element_t.h
Normal file
24
Z80/HarteTest_Z80/element_t.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
class element_t {
|
||||
private:
|
||||
simdjson::dom::element m_raw;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] auto raw() const noexcept { return m_raw; }
|
||||
|
||||
public:
|
||||
element_t() noexcept {}
|
||||
|
||||
element_t(const simdjson::dom::element input) noexcept
|
||||
: m_raw(input) {}
|
||||
|
||||
[[nodiscard]] auto at(std::string_view key) const noexcept { return raw()[key]; }
|
||||
[[nodiscard]] auto operator[](std::string_view key) const noexcept { return at(key); }
|
||||
[[nodiscard]] auto array_at(std::string_view key) const noexcept { return at(key).get_array(); }
|
||||
[[nodiscard]] auto integer_at(std::string_view key) const noexcept { return int64_t(at(key)); }
|
||||
};
|
||||
10
Z80/HarteTest_Z80/opcode_test_suite_t.cpp
Normal file
10
Z80/HarteTest_Z80/opcode_test_suite_t.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
#include "opcode_test_suite_t.h"
|
||||
|
||||
opcode_test_suite_t::opcode_test_suite_t(const std::string path) noexcept
|
||||
: parser_t(path) {}
|
||||
|
||||
EightBit::co_generator_t<test_t> opcode_test_suite_t::generator() const {
|
||||
for (const auto element : *this)
|
||||
co_yield test_t(element);
|
||||
}
|
||||
23
Z80/HarteTest_Z80/opcode_test_suite_t.h
Normal file
23
Z80/HarteTest_Z80/opcode_test_suite_t.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <co_generator_t.h>
|
||||
|
||||
#include "parser_t.h"
|
||||
#include "test_t.h"
|
||||
|
||||
class opcode_test_suite_t final : public parser_t {
|
||||
private:
|
||||
[[nodiscard]] auto array() const noexcept { return raw().get_array(); }
|
||||
|
||||
public:
|
||||
opcode_test_suite_t() noexcept {}
|
||||
opcode_test_suite_t(std::string path) noexcept;
|
||||
|
||||
[[nodiscard]] auto begin() const noexcept { return array().begin(); }
|
||||
[[nodiscard]] auto end() const noexcept { return array().end(); }
|
||||
[[nodiscard]] auto size() const noexcept { return array().size(); }
|
||||
|
||||
[[nodiscard]] EightBit::co_generator_t<test_t> generator() const;
|
||||
};
|
||||
11
Z80/HarteTest_Z80/parser_t.cpp
Normal file
11
Z80/HarteTest_Z80/parser_t.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "parser_t.h"
|
||||
|
||||
simdjson::dom::parser parser_t::m_parser;
|
||||
|
||||
parser_t::parser_t(const std::string path) noexcept
|
||||
: m_path(path) {}
|
||||
|
||||
void parser_t::load() {
|
||||
m_raw = m_parser.load(m_path);
|
||||
}
|
||||
26
Z80/HarteTest_Z80/parser_t.h
Normal file
26
Z80/HarteTest_Z80/parser_t.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
class parser_t {
|
||||
private:
|
||||
// N.B.
|
||||
// The parser must be kept for the lifetime of any parsed data.
|
||||
// Therefore, it can only be used for one document at a time.
|
||||
static simdjson::dom::parser m_parser;
|
||||
|
||||
std::string m_path;
|
||||
simdjson::dom::element m_raw;
|
||||
|
||||
public:
|
||||
parser_t() noexcept {}
|
||||
parser_t(std::string path) noexcept;
|
||||
|
||||
[[nodiscard]] constexpr std::string_view path() const noexcept { return m_path; }
|
||||
[[nodiscard]] const auto raw() const noexcept { return m_raw; }
|
||||
|
||||
virtual void load();
|
||||
};
|
||||
15
Z80/HarteTest_Z80/port_t.h
Normal file
15
Z80/HarteTest_Z80/port_t.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
#include "byte_t.h"
|
||||
|
||||
class port_t : public byte_t {
|
||||
public:
|
||||
port_t(const simdjson::dom::array input) noexcept
|
||||
: byte_t(input) {}
|
||||
|
||||
[[nodiscard]] auto type() const noexcept { return string_at(2); }
|
||||
};
|
||||
5
Z80/HarteTest_Z80/ports_t.h
Normal file
5
Z80/HarteTest_Z80/ports_t.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "array_t.h"
|
||||
|
||||
typedef array_t ports_t; // Intended to indicate that ports_t is meant to hold port_t objects
|
||||
14
Z80/HarteTest_Z80/processor_test_suite_t.cpp
Normal file
14
Z80/HarteTest_Z80/processor_test_suite_t.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "stdafx.h"
|
||||
#include "processor_test_suite_t.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
processor_test_suite_t::processor_test_suite_t(std::string location) noexcept
|
||||
: m_location(location) {
|
||||
}
|
||||
|
||||
EightBit::co_generator_t<opcode_test_suite_t> processor_test_suite_t::generator() const {
|
||||
std::filesystem::path directory = location();
|
||||
for (const auto& entry : std::filesystem::directory_iterator{ directory })
|
||||
co_yield opcode_test_suite_t(entry.path().string());
|
||||
}
|
||||
20
Z80/HarteTest_Z80/processor_test_suite_t.h
Normal file
20
Z80/HarteTest_Z80/processor_test_suite_t.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <co_generator_t.h>
|
||||
|
||||
#include "opcode_test_suite_t.h"
|
||||
|
||||
class processor_test_suite_t final {
|
||||
private:
|
||||
std::string m_location;
|
||||
|
||||
public:
|
||||
processor_test_suite_t(std::string location) noexcept;
|
||||
|
||||
[[nodiscard]] constexpr std::string_view location() const noexcept { return m_location; }
|
||||
|
||||
[[nodiscard]] EightBit::co_generator_t<opcode_test_suite_t> generator() const;
|
||||
};
|
||||
5
Z80/HarteTest_Z80/ram_t.h
Normal file
5
Z80/HarteTest_Z80/ram_t.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "array_t.h"
|
||||
|
||||
typedef array_t ram_t; // Intended to indicate that ram_t is meant to hold byte_t objects
|
||||
65003
Z80/HarteTest_Z80/simdjson/simdjson.cpp
Normal file
65003
Z80/HarteTest_Z80/simdjson/simdjson.cpp
Normal file
File diff suppressed because it is too large
Load Diff
183843
Z80/HarteTest_Z80/simdjson/simdjson.h
Normal file
183843
Z80/HarteTest_Z80/simdjson/simdjson.h
Normal file
File diff suppressed because it is too large
Load Diff
56
Z80/HarteTest_Z80/state_t.h
Normal file
56
Z80/HarteTest_Z80/state_t.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
#include "element_t.h"
|
||||
#include "ram_t.h"
|
||||
|
||||
class state_t final : public element_t {
|
||||
public:
|
||||
state_t(const simdjson::dom::element input) noexcept
|
||||
: element_t(input) {}
|
||||
|
||||
[[nodiscard]] auto address_at(std::string_view key) const noexcept { return uint16_t(integer_at(key)); }
|
||||
[[nodiscard]] auto byte_at(std::string_view key) const noexcept { return uint8_t(integer_at(key)); }
|
||||
|
||||
[[nodiscard]] auto pc() const noexcept { return address_at("pc"); }
|
||||
[[nodiscard]] auto sp() const noexcept { return address_at("sp"); }
|
||||
|
||||
[[nodiscard]] auto a() const noexcept { return byte_at("a"); }
|
||||
[[nodiscard]] auto f() const noexcept { return byte_at("f"); }
|
||||
[[nodiscard]] auto b() const noexcept { return byte_at("b"); }
|
||||
[[nodiscard]] auto c() const noexcept { return byte_at("c"); }
|
||||
[[nodiscard]] auto d() const noexcept { return byte_at("d"); }
|
||||
[[nodiscard]] auto e() const noexcept { return byte_at("e"); }
|
||||
[[nodiscard]] auto h() const noexcept { return byte_at("h"); }
|
||||
[[nodiscard]] auto l() const noexcept { return byte_at("l"); }
|
||||
|
||||
[[nodiscard]] auto af_() const noexcept { return address_at("af_"); }
|
||||
[[nodiscard]] auto bc_() const noexcept { return address_at("bc_"); }
|
||||
[[nodiscard]] auto de_() const noexcept { return address_at("de_"); }
|
||||
[[nodiscard]] auto hl_() const noexcept { return address_at("hl_"); }
|
||||
|
||||
[[nodiscard]] auto i() const noexcept { return byte_at("i"); }
|
||||
[[nodiscard]] auto r() const noexcept { return byte_at("r"); }
|
||||
|
||||
[[nodiscard]] auto im() const noexcept { return byte_at("im"); }
|
||||
|
||||
[[nodiscard]] auto ei() const noexcept { return byte_at("ei"); }
|
||||
|
||||
[[nodiscard]] auto p() const noexcept { return byte_at("p"); }
|
||||
|
||||
[[nodiscard]] auto q() const noexcept { return byte_at("q"); }
|
||||
|
||||
[[nodiscard]] auto iff1() const noexcept { return byte_at("iff1"); }
|
||||
[[nodiscard]] auto iff2() const noexcept { return byte_at("iff2"); }
|
||||
|
||||
[[nodiscard]] auto wz() const noexcept { return address_at("wz"); }
|
||||
|
||||
[[nodiscard]] auto ix() const noexcept { return address_at("ix"); }
|
||||
[[nodiscard]] auto iy() const noexcept { return address_at("iy"); }
|
||||
|
||||
[[nodiscard]] auto ram() const noexcept { return ram_t(array_at("ram")); }
|
||||
};
|
||||
1
Z80/HarteTest_Z80/stdafx.cpp
Normal file
1
Z80/HarteTest_Z80/stdafx.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "stdafx.h"
|
||||
28
Z80/HarteTest_Z80/stdafx.h
Normal file
28
Z80/HarteTest_Z80/stdafx.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#define SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Bus.h>
|
||||
#include <Ram.h>
|
||||
#include <Z80.h>
|
||||
#include <Disassembler.h>
|
||||
|
||||
#include <co_generator_t.h>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
31
Z80/HarteTest_Z80/test_t.h
Normal file
31
Z80/HarteTest_Z80/test_t.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "simdjson/simdjson.h"
|
||||
|
||||
#include "element_t.h"
|
||||
#include "cycles_t.h"
|
||||
#include "ports_t.h"
|
||||
#include "state_t.h"
|
||||
|
||||
class test_t final : public element_t {
|
||||
public:
|
||||
test_t() noexcept {}
|
||||
|
||||
test_t(const simdjson::dom::element input) noexcept
|
||||
: element_t(input) {}
|
||||
|
||||
[[nodiscard]] auto name() const noexcept { return at("name"); }
|
||||
[[nodiscard]] auto initial() const noexcept { return state_t(at("initial")); }
|
||||
[[nodiscard]] auto final() const noexcept { return state_t(at("final")); }
|
||||
[[nodiscard]] auto cycles() const noexcept { return cycles_t(array_at("cycles")); }
|
||||
|
||||
[[nodiscard]] auto ports() const noexcept {
|
||||
std::optional<ports_t> returned;
|
||||
auto possible = array_at("ports");
|
||||
if (possible.has_value())
|
||||
returned.emplace(ports_t(possible.value()));
|
||||
return returned;;
|
||||
}
|
||||
};
|
||||
81
Z80/HarteTest_Z80/tests.cpp
Normal file
81
Z80/HarteTest_Z80/tests.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "TestRunner.h"
|
||||
#include "checker_t.h"
|
||||
#include "test_t.h"
|
||||
#include "opcode_test_suite_t.h"
|
||||
#include "processor_test_suite_t.h"
|
||||
|
||||
int main() {
|
||||
|
||||
std::string directory = "C:\\github\\spectrum\\libraries\\EightBit\\modules\\z80\\v2";
|
||||
|
||||
const auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
int unimplemented_opcode_count = 0;
|
||||
int invalid_opcode_count = 0;
|
||||
|
||||
TestRunner runner;
|
||||
runner.initialise();
|
||||
|
||||
checker_t checker(runner);
|
||||
checker.initialise();
|
||||
|
||||
processor_test_suite_t m6502_tests(directory);
|
||||
auto opcode_generator = m6502_tests.generator();
|
||||
while (opcode_generator) {
|
||||
|
||||
auto opcode = opcode_generator();
|
||||
|
||||
const auto path = std::filesystem::path(opcode.path());
|
||||
std::cout << "Processing: " << path.filename() << "\n";
|
||||
opcode.load();
|
||||
|
||||
auto test_generator = opcode.generator();
|
||||
std::vector<std::string_view> test_names;
|
||||
while (test_generator) {
|
||||
|
||||
const auto test = test_generator();
|
||||
checker.check(test);
|
||||
if (checker.invalid()) {
|
||||
|
||||
std::cout << "** Failed: " << test.name() << "\n";
|
||||
|
||||
++invalid_opcode_count;
|
||||
|
||||
// Was it just unimplemented?
|
||||
if (checker.unimplemented())
|
||||
++unimplemented_opcode_count;
|
||||
|
||||
// Let's see if we had any successes!
|
||||
if (!test_names.empty()) {
|
||||
std::cout << "**** The follow test variations succeeeded\n";
|
||||
for (const auto& test_name : test_names)
|
||||
std::cout << "****** " << test_name << "\n";
|
||||
}
|
||||
|
||||
// OK, we've attempted an implementation, how did it fail?
|
||||
for (const auto& message : checker.messages())
|
||||
std::cout << "**** " << message << "\n";
|
||||
|
||||
// I'm not really interested in the remaining tests for this opcode
|
||||
break;
|
||||
}
|
||||
|
||||
test_names.push_back(test.name().get_string().value());
|
||||
}
|
||||
}
|
||||
|
||||
const auto finish_time = std::chrono::steady_clock::now();
|
||||
const auto elapsed_time = finish_time - start_time;
|
||||
const auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(elapsed_time).count();
|
||||
std::cout
|
||||
<< "Elapsed time: " << seconds << " seconds"
|
||||
<< ", unimplemented opcode count: " << unimplemented_opcode_count
|
||||
<< ", invalid opcode count: " << (invalid_opcode_count - unimplemented_opcode_count)
|
||||
<< std::endl;
|
||||
}
|
||||
Reference in New Issue
Block a user