mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-08-10 23:25:02 +00:00
Refactor the checking code into into it's own class.
Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
@@ -158,6 +158,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="array_t.cpp" />
|
<ClCompile Include="array_t.cpp" />
|
||||||
<ClCompile Include="byte_t.cpp" />
|
<ClCompile Include="byte_t.cpp" />
|
||||||
|
<ClCompile Include="checker_t.cpp" />
|
||||||
<ClCompile Include="cycles_t.cpp" />
|
<ClCompile Include="cycles_t.cpp" />
|
||||||
<ClCompile Include="cycle_t.cpp" />
|
<ClCompile Include="cycle_t.cpp" />
|
||||||
<ClCompile Include="element_t.cpp" />
|
<ClCompile Include="element_t.cpp" />
|
||||||
@@ -184,6 +185,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="array_t.h" />
|
<ClInclude Include="array_t.h" />
|
||||||
<ClInclude Include="byte_t.h" />
|
<ClInclude Include="byte_t.h" />
|
||||||
|
<ClInclude Include="checker_t.h" />
|
||||||
<ClInclude Include="co_generator_t.h" />
|
<ClInclude Include="co_generator_t.h" />
|
||||||
<ClInclude Include="cycles_t.h" />
|
<ClInclude Include="cycles_t.h" />
|
||||||
<ClInclude Include="cycle_t.h" />
|
<ClInclude Include="cycle_t.h" />
|
||||||
|
@@ -56,6 +56,9 @@
|
|||||||
<ClCompile Include="processor_test_suite_t.cpp">
|
<ClCompile Include="processor_test_suite_t.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="checker_t.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="stdafx.h">
|
<ClInclude Include="stdafx.h">
|
||||||
@@ -103,5 +106,8 @@
|
|||||||
<ClInclude Include="co_generator_t.h">
|
<ClInclude Include="co_generator_t.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="checker_t.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -1,9 +1,6 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "TestRunner.h"
|
#include "TestRunner.h"
|
||||||
|
|
||||||
std::set<uint8_t> TestRunner::m_undocumented_opcodes;
|
|
||||||
bool TestRunner::m_undocumented_opcodes_initialised = false;
|
|
||||||
|
|
||||||
TestRunner::TestRunner() {}
|
TestRunner::TestRunner() {}
|
||||||
|
|
||||||
EightBit::MemoryMapping TestRunner::mapping(const uint16_t address) noexcept {
|
EightBit::MemoryMapping TestRunner::mapping(const uint16_t address) noexcept {
|
||||||
@@ -25,244 +22,4 @@ void TestRunner::lowerPOWER() {
|
|||||||
EightBit::Bus::lowerPOWER();
|
EightBit::Bus::lowerPOWER();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestRunner::addActualCycle(const uint16_t address, const uint8_t value, std::string_view action) {
|
void TestRunner::initialise() {}
|
||||||
m_actualCycles.push_back({ address, value, std::string(action) });
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::addActualCycle(const EightBit::register16_t address, const uint8_t value, std::string_view action) {
|
|
||||||
addActualCycle(address.word, value, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::addActualReadCycle(const EightBit::register16_t address, const uint8_t value) {
|
|
||||||
addActualCycle(address, value, "read");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::addActualWriteCycle(const EightBit::register16_t address, const uint8_t value) {
|
|
||||||
addActualCycle(address, value, "write");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycles(std::string_view which, const actual_cycles_t& events) {
|
|
||||||
m_messages.push_back(std::string(which));
|
|
||||||
dumpCycles(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycles(const actual_cycles_t& cycles) {
|
|
||||||
for (const auto& cycle: cycles)
|
|
||||||
dumpCycle(cycle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycle(const actual_cycle_t& cycle) {
|
|
||||||
dumpCycle(std::get<0>(cycle), std::get<1>(cycle), std::get<2>(cycle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycles(std::string_view which, const cycles_t events) {
|
|
||||||
m_messages.push_back(std::string(which));
|
|
||||||
dumpCycles(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycles(const cycles_t cycles) {
|
|
||||||
for (const auto cycle: cycles)
|
|
||||||
dumpCycle(cycle_t(cycle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::dumpCycle(const cycle_t cycle) {
|
|
||||||
dumpCycle(cycle.address(), cycle.value(), cycle.action());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::initialise() {
|
|
||||||
|
|
||||||
seedUndocumentedOpcodes();
|
|
||||||
|
|
||||||
ReadByte.connect([this](EightBit::EventArgs&) {
|
|
||||||
addActualReadCycle(ADDRESS(), DATA());
|
|
||||||
});
|
|
||||||
|
|
||||||
WrittenByte.connect([this](EightBit::EventArgs&) {
|
|
||||||
addActualWriteCycle(ADDRESS(), DATA());
|
|
||||||
});
|
|
||||||
|
|
||||||
os() << std::hex << std::uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::raise(std::string_view 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 TestRunner::raise(std::string_view what, const uint8_t expected, const uint8_t actual) {
|
|
||||||
os()
|
|
||||||
<< std::setw(2) << std::setfill(' ')
|
|
||||||
<< what
|
|
||||||
<< std::setfill('0')
|
|
||||||
<< ": expected: " << (int)expected
|
|
||||||
<< " (" << EightBit::Disassembly::dump_Flags(expected) << ")"
|
|
||||||
<< ", actual: " << (int)actual
|
|
||||||
<< " (" << EightBit::Disassembly::dump_Flags(actual) << ")";
|
|
||||||
pushCurrentMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::raise(std::string_view what, std::string_view expected, std::string_view actual) {
|
|
||||||
os()
|
|
||||||
<< std::setw(0) << std::setfill(' ')
|
|
||||||
<< what
|
|
||||||
<< ": expected: " << expected
|
|
||||||
<< ", actual: " << actual;
|
|
||||||
pushCurrentMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestRunner::check(std::string_view 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::initialiseState() {
|
|
||||||
|
|
||||||
const auto initial = test().initial();
|
|
||||||
|
|
||||||
CPU().PC().word = initial.pc();
|
|
||||||
CPU().S() = initial.s();
|
|
||||||
CPU().A() = initial.a();
|
|
||||||
CPU().X() = initial.x();
|
|
||||||
CPU().Y() = initial.y();
|
|
||||||
CPU().P() = initial.p();
|
|
||||||
for (const auto entry : initial.ram()) {
|
|
||||||
const byte_t byte(entry);
|
|
||||||
RAM().poke(byte.address(), byte.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestRunner::checkState() {
|
|
||||||
|
|
||||||
const auto expected_cycles = test().cycles();
|
|
||||||
const auto actual_cycles = m_actualCycles;
|
|
||||||
m_cycle_count_mismatch = expected_cycles.size() != actual_cycles.size();
|
|
||||||
if (m_cycle_count_mismatch)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
size_t actual_idx = 0;
|
|
||||||
for (const auto expected_cycle : expected_cycles) {
|
|
||||||
const auto expected = cycle_t(expected_cycle);
|
|
||||||
const auto actual = actual_cycles.at(actual_idx++); // actual could be less than expected
|
|
||||||
check("Cycle address", expected.address(), std::get<0>(actual));
|
|
||||||
check("Cycle value", expected.value(), std::get<1>(actual));
|
|
||||||
check("Cycle action", expected.action(), std::string_view(std::get<2>(actual)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto final = test().final();
|
|
||||||
const auto pc_good = check("PC", final.pc(), CPU().PC().word);
|
|
||||||
const auto s_good = check("S", final.s(), CPU().S());
|
|
||||||
const auto a_good = check("A", final.a(), CPU().A());
|
|
||||||
const auto x_good = check("X", final.x(), CPU().X());
|
|
||||||
const auto y_good = check("Y", final.y(), CPU().Y());
|
|
||||||
const auto p_good = check("P", final.p(), CPU().P());
|
|
||||||
|
|
||||||
bool ram_problem = false;
|
|
||||||
for (const auto entry : final.ram()) {
|
|
||||||
const byte_t byte(entry);
|
|
||||||
const auto ram_good = check("RAM", byte.address(), byte.value(), RAM().peek(byte.address()));
|
|
||||||
if (!ram_good && !ram_problem)
|
|
||||||
ram_problem = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pc_good && s_good && a_good && x_good && y_good && p_good && !ram_problem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::pushCurrentMessage() {
|
|
||||||
m_messages.push_back(os().str());
|
|
||||||
os().str("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::disassemble(uint16_t address) {
|
|
||||||
try {
|
|
||||||
os() << m_disassembler.disassemble(address);
|
|
||||||
} catch (const std::domain_error& error) {
|
|
||||||
os() << "Disassembly problem: " << error.what();
|
|
||||||
}
|
|
||||||
pushCurrentMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::check(const test_t updated) {
|
|
||||||
|
|
||||||
m_test = updated;
|
|
||||||
|
|
||||||
m_messages.clear();
|
|
||||||
m_actualCycles.clear();
|
|
||||||
m_cycles = 0;
|
|
||||||
m_valid = true;
|
|
||||||
m_undocumented = false;
|
|
||||||
|
|
||||||
raisePOWER();
|
|
||||||
initialiseState();
|
|
||||||
const auto pc = CPU().PC().word;
|
|
||||||
const auto start_opcode = peek(pc);
|
|
||||||
m_cycles = CPU().step();
|
|
||||||
lowerPOWER();
|
|
||||||
|
|
||||||
m_valid = checkState();
|
|
||||||
|
|
||||||
m_undocumented = m_undocumented_opcodes.find(start_opcode) != m_undocumented_opcodes.end();
|
|
||||||
if (undocumented()) {
|
|
||||||
m_messages.push_back("Undocumented");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unimplemented()) {
|
|
||||||
m_messages.push_back("Unimplemented");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalid() && implemented()) {
|
|
||||||
|
|
||||||
disassemble(pc);
|
|
||||||
|
|
||||||
const auto final = test().final();
|
|
||||||
raise("PC", final.pc(), CPU().PC().word);
|
|
||||||
raise("S", final.s(), CPU().S());
|
|
||||||
raise("A", final.a(), CPU().A());
|
|
||||||
raise("X", final.x(), CPU().X());
|
|
||||||
raise("Y", final.y(), CPU().Y());
|
|
||||||
raise("P", final.p(), CPU().P());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestRunner::seedUndocumentedOpcodes() {
|
|
||||||
if (m_undocumented_opcodes_initialised) return;
|
|
||||||
m_undocumented_opcodes = {
|
|
||||||
0x02, 0x03, 0x04, 0x07, 0x0b, 0x0c, 0x0f,
|
|
||||||
0x12, 0x13, 0x14, 0x17, 0x1a, 0x1b, 0x1c, 0x1f,
|
|
||||||
0x22, 0x23, 0x27, 0x2b, 0x2f,
|
|
||||||
0x32, 0x33, 0x34, 0x37, 0x3a, 0x3b, 0x3c, 0x3f,
|
|
||||||
0x42, 0x43, 0x44, 0x47, 0x4b, 0x4f,
|
|
||||||
0x52, 0x53, 0x54, 0x57, 0x5a, 0x5b, 0x5c, 0x5f,
|
|
||||||
0x62, 0x63, 0x64, 0x67, 0x6b, 0x6f,
|
|
||||||
0x72, 0x73, 0x74, 0x77, 0x7a, 0x7b, 0x7c, 0x7f,
|
|
||||||
0x80, 0x82, 0x83, 0x87, 0x89, 0x8b, 0x8f,
|
|
||||||
0x92, 0x93, 0x97, 0x9b, 0x9c, 0x9e, 0x9f,
|
|
||||||
0xa3, 0xa7, 0xab, 0xaf,
|
|
||||||
0xb2, 0xb3, 0xb7, 0xbb, 0xbf,
|
|
||||||
0xc2, 0xc3, 0xc7, 0xcb, 0xcf,
|
|
||||||
0xd2, 0xd3, 0xd4, 0xd7, 0xda, 0xdb, 0xdc, 0xdf,
|
|
||||||
0xe2, 0xe3, 0xe7, 0xeb, 0xef,
|
|
||||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf7, 0xfa, 0xfb, 0xfc, 0xff,
|
|
||||||
};
|
|
||||||
m_undocumented_opcodes_initialised = true;
|
|
||||||
}
|
|
||||||
|
@@ -9,11 +9,6 @@
|
|||||||
#include <Bus.h>
|
#include <Bus.h>
|
||||||
#include <Ram.h>
|
#include <Ram.h>
|
||||||
#include <mos6502.h>
|
#include <mos6502.h>
|
||||||
#include <Disassembly.h>
|
|
||||||
#include <Symbols.h>
|
|
||||||
|
|
||||||
#include "test_t.h"
|
|
||||||
#include "cycle_t.h"
|
|
||||||
|
|
||||||
class TestRunner final : public EightBit::Bus {
|
class TestRunner final : public EightBit::Bus {
|
||||||
private:
|
private:
|
||||||
@@ -22,78 +17,6 @@ private:
|
|||||||
|
|
||||||
EightBit::Ram m_ram = 0x10000;
|
EightBit::Ram m_ram = 0x10000;
|
||||||
EightBit::MOS6502 m_cpu = { *this };
|
EightBit::MOS6502 m_cpu = { *this };
|
||||||
EightBit::Symbols m_symbols;
|
|
||||||
EightBit::Disassembly m_disassembler = { *this, m_cpu, m_symbols };
|
|
||||||
|
|
||||||
test_t m_test;
|
|
||||||
|
|
||||||
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;
|
|
||||||
bool m_undocumented = false;
|
|
||||||
|
|
||||||
void seedUndocumentedOpcodes();
|
|
||||||
void initialiseState();
|
|
||||||
[[nodiscard]] bool checkState();
|
|
||||||
|
|
||||||
void pushCurrentMessage();
|
|
||||||
|
|
||||||
void raise(std::string_view what, uint16_t expected, uint16_t actual);
|
|
||||||
void raise(std::string_view what, uint8_t expected, uint8_t actual);
|
|
||||||
void raise(std::string_view what, std::string_view expected, std::string_view actual);
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool check(std::string_view what, T expected, T actual) {
|
|
||||||
const auto success = actual == expected;
|
|
||||||
if (!success)
|
|
||||||
raise(what, expected, actual);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check(std::string_view what, std::string_view expected, std::string_view actual) {
|
|
||||||
const auto success = actual == expected;
|
|
||||||
if (!success)
|
|
||||||
raise(what, expected, actual);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check(std::string_view what, uint16_t address, uint8_t expected, uint8_t actual);
|
|
||||||
|
|
||||||
void addActualCycle(const actual_cycle_t& value);
|
|
||||||
void addActualCycle(uint16_t address, uint8_t value, std::string_view action);
|
|
||||||
void addActualCycle(EightBit::register16_t address, uint8_t value, std::string_view action);
|
|
||||||
|
|
||||||
void addActualReadCycle(EightBit::register16_t address, uint8_t value);
|
|
||||||
void addActualWriteCycle(EightBit::register16_t address, uint8_t value);
|
|
||||||
|
|
||||||
void disassemble(uint16_t address);
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void dumpCycle(const uint16_t address, const uint8_t value, const T action) {
|
|
||||||
m_os
|
|
||||||
<< std::setfill('0') << std::hex
|
|
||||||
<< "Address: " << std::setw(4) << (int)address
|
|
||||||
<< ", value: " << std::setw(2) << (int)value
|
|
||||||
<< ", action: " << action;
|
|
||||||
pushCurrentMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dumpCycles(std::string_view which, cycles_t cycles);
|
|
||||||
void dumpCycles(cycles_t cycles);
|
|
||||||
void dumpCycle(cycle_t cycle);
|
|
||||||
|
|
||||||
void dumpCycles(std::string_view which, const actual_cycles_t& cycles);
|
|
||||||
void dumpCycles(const actual_cycles_t& cycles);
|
|
||||||
void dumpCycle(const actual_cycle_t& cycle);
|
|
||||||
|
|
||||||
[[nodiscard]] auto& os() { return m_os; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual EightBit::MemoryMapping mapping(uint16_t address) noexcept final;
|
virtual EightBit::MemoryMapping mapping(uint16_t address) noexcept final;
|
||||||
@@ -108,16 +31,4 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] constexpr auto& RAM() noexcept { return m_ram; }
|
[[nodiscard]] constexpr auto& RAM() noexcept { return m_ram; }
|
||||||
[[nodiscard]] constexpr auto& CPU() noexcept { return m_cpu; }
|
[[nodiscard]] constexpr auto& CPU() noexcept { return m_cpu; }
|
||||||
[[nodiscard]] auto test() const noexcept { return m_test; }
|
|
||||||
[[nodiscard]] constexpr const auto& messages() const noexcept { return m_messages; }
|
|
||||||
|
|
||||||
[[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 auto undocumented() const noexcept { return m_undocumented; }
|
|
||||||
[[nodiscard]] constexpr auto documented() const noexcept { return !undocumented(); }
|
|
||||||
|
|
||||||
void check(test_t updated);
|
|
||||||
};
|
};
|
||||||
|
@@ -78,21 +78,23 @@ void checker_t::raise(const std::string what, const std::string_view expected, c
|
|||||||
<< ", actual: " << actual;
|
<< ", actual: " << actual;
|
||||||
pushCurrentMessage();
|
pushCurrentMessage();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
//bool TestRunner::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(const test_t test)
|
bool checker_t::check(const std::string what, const uint16_t address, const uint8_t expected, const uint8_t actual) {
|
||||||
: m_test(test) {}
|
const auto success = actual == expected;
|
||||||
|
if (!success) {
|
||||||
|
os() << what << ": " << std::setw(4) << std::setfill('0') << (int)address;
|
||||||
|
raise(os().str(), expected, actual);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
void checker_t::initialiseState(EightBit::MOS6502& cpu, EightBit::Ram& ram) {
|
checker_t::checker_t(TestRunner& runner)
|
||||||
|
: m_runner(runner) {}
|
||||||
|
|
||||||
|
void checker_t::initialiseState() {
|
||||||
|
|
||||||
|
auto& cpu = runner().CPU();
|
||||||
|
auto& ram = runner().RAM();
|
||||||
|
|
||||||
const auto initial = test().initial();
|
const auto initial = test().initial();
|
||||||
|
|
||||||
@@ -108,10 +110,12 @@ void checker_t::initialiseState(EightBit::MOS6502& cpu, EightBit::Ram& ram) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checker_t::initialise(EightBit::Bus& bus) {
|
void checker_t::initialise() {
|
||||||
|
|
||||||
seedUndocumentedOpcodes();
|
seedUndocumentedOpcodes();
|
||||||
|
|
||||||
|
auto& bus = runner();
|
||||||
|
|
||||||
bus.ReadByte.connect([this, &bus](EightBit::EventArgs&) {
|
bus.ReadByte.connect([this, &bus](EightBit::EventArgs&) {
|
||||||
addActualReadCycle(bus.ADDRESS(), bus.DATA());
|
addActualReadCycle(bus.ADDRESS(), bus.DATA());
|
||||||
});
|
});
|
||||||
@@ -124,7 +128,10 @@ void checker_t::initialise(EightBit::Bus& bus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
bool checker_t::checkState(EightBit::MOS6502& cpu, EightBit::Ram& ram) {
|
bool checker_t::checkState() {
|
||||||
|
|
||||||
|
auto& cpu = runner().CPU();
|
||||||
|
auto& ram = runner().RAM();
|
||||||
|
|
||||||
const auto expected_cycles = test().cycles();
|
const auto expected_cycles = test().cycles();
|
||||||
const auto actual_cycles = m_actualCycles;
|
const auto actual_cycles = m_actualCycles;
|
||||||
@@ -165,62 +172,69 @@ void checker_t::pushCurrentMessage() {
|
|||||||
os().str("");
|
os().str("");
|
||||||
}
|
}
|
||||||
|
|
||||||
//void TestRunner::disassemble(uint16_t address) {
|
void checker_t::disassemble(uint16_t address) {
|
||||||
// try {
|
try {
|
||||||
// os() << m_disassembler.disassemble(address);
|
os() << m_disassembler.disassemble(address);
|
||||||
// }
|
}
|
||||||
// catch (const std::domain_error& error) {
|
catch (const std::domain_error& error) {
|
||||||
// os() << "Disassembly problem: " << error.what();
|
os() << "Disassembly problem: " << error.what();
|
||||||
// }
|
}
|
||||||
// pushCurrentMessage();
|
pushCurrentMessage();
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//void TestRunner::check() {
|
void checker_t::check(test_t current) {
|
||||||
// initialise();
|
|
||||||
// raisePOWER();
|
m_test = current;
|
||||||
// initialiseState();
|
|
||||||
// const auto pc = CPU().PC().word;
|
auto& cpu = runner().CPU();
|
||||||
// const auto start_opcode = peek(pc);
|
|
||||||
// m_cycles = CPU().step();
|
m_messages.clear();
|
||||||
// lowerPOWER();
|
m_actualCycles.clear();
|
||||||
//
|
|
||||||
// m_valid = checkState();
|
runner().raisePOWER();
|
||||||
//
|
initialiseState();
|
||||||
// m_undocumented = m_undocumented_opcodes.find(start_opcode) != m_undocumented_opcodes.end();
|
const auto pc = cpu.PC().word;
|
||||||
// if (undocumented()) {
|
const auto start_opcode = runner().peek(pc);
|
||||||
// m_messages.push_back("Undocumented");
|
m_cycles = cpu.step();
|
||||||
// return;
|
runner().lowerPOWER();
|
||||||
// }
|
|
||||||
//
|
m_valid = checkState();
|
||||||
// if (unimplemented()) {
|
|
||||||
// m_messages.push_back("Unimplemented");
|
m_undocumented = m_undocumented_opcodes.find(start_opcode) != m_undocumented_opcodes.end();
|
||||||
// return;
|
if (undocumented()) {
|
||||||
// }
|
m_messages.push_back("Undocumented");
|
||||||
//
|
return;
|
||||||
// if (invalid() && implemented()) {
|
}
|
||||||
//
|
|
||||||
// disassemble(pc);
|
if (unimplemented()) {
|
||||||
//
|
m_messages.push_back("Unimplemented");
|
||||||
// const auto final = test().final();
|
return;
|
||||||
// raise("PC", final.pc(), CPU().PC().word);
|
}
|
||||||
// raise("S", final.s(), CPU().S());
|
|
||||||
// raise("A", final.a(), CPU().A());
|
if (invalid() && implemented()) {
|
||||||
// raise("X", final.x(), CPU().X());
|
|
||||||
// raise("Y", final.y(), CPU().Y());
|
disassemble(pc);
|
||||||
// raise("P", final.p(), CPU().P());
|
|
||||||
//
|
const auto final = test().final();
|
||||||
// os()
|
raise("PC", final.pc(), cpu.PC().word);
|
||||||
// << std::dec << std::setfill(' ')
|
raise("S", final.s(), cpu.S());
|
||||||
// << "Stepped cycles: " << cycles()
|
raise("A", final.a(), cpu.A());
|
||||||
// << ", expected events: " << test().cycles().size()
|
raise("X", final.x(), cpu.X());
|
||||||
// << ", actual events: " << m_actualCycles.size();
|
raise("Y", final.y(), cpu.Y());
|
||||||
// pushCurrentMessage();
|
raise("P", final.p(), cpu.P());
|
||||||
//
|
|
||||||
// dumpCycles("-- Expected cycles", test().cycles());
|
os()
|
||||||
// dumpCycles("-- Actual cycles", m_actualCycles);
|
<< 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void checker_t::seedUndocumentedOpcodes() {
|
void checker_t::seedUndocumentedOpcodes() {
|
||||||
if (m_undocumented_opcodes_initialised) return;
|
if (m_undocumented_opcodes_initialised) return;
|
||||||
m_undocumented_opcodes = {
|
m_undocumented_opcodes = {
|
||||||
|
@@ -3,17 +3,26 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <Disassembly.h>
|
||||||
|
#include <Symbols.h>
|
||||||
|
|
||||||
#include "cycles_t.h"
|
#include "cycles_t.h"
|
||||||
#include "cycle_t.h"
|
#include "cycle_t.h"
|
||||||
#include "test_t.h"
|
#include "test_t.h"
|
||||||
|
|
||||||
|
#include "TestRunner.h"
|
||||||
|
|
||||||
class checker_t {
|
class checker_t {
|
||||||
public:
|
public:
|
||||||
private:
|
private:
|
||||||
static std::set<uint8_t> m_undocumented_opcodes;
|
static std::set<uint8_t> m_undocumented_opcodes;
|
||||||
static bool m_undocumented_opcodes_initialised;
|
static bool m_undocumented_opcodes_initialised;
|
||||||
|
|
||||||
const test_t m_test;
|
TestRunner& m_runner;
|
||||||
|
EightBit::Symbols m_symbols;
|
||||||
|
EightBit::Disassembly m_disassembler = { m_runner, m_runner.CPU(), m_symbols };
|
||||||
|
|
||||||
|
test_t m_test;
|
||||||
|
|
||||||
std::ostringstream m_os;
|
std::ostringstream m_os;
|
||||||
std::vector<std::string> m_messages;
|
std::vector<std::string> m_messages;
|
||||||
@@ -23,14 +32,16 @@ private:
|
|||||||
actual_cycles_t m_actualCycles;
|
actual_cycles_t m_actualCycles;
|
||||||
bool m_cycle_count_mismatch = false;
|
bool m_cycle_count_mismatch = false;
|
||||||
|
|
||||||
//int m_cycles = 0;
|
int m_cycles = 0;
|
||||||
//bool m_valid = true;
|
bool m_valid = true;
|
||||||
//bool m_undocumented = false;
|
bool m_undocumented = false;
|
||||||
|
|
||||||
[[nodiscard]] auto& os() { return m_os; }
|
[[nodiscard]] auto& os() { return m_os; }
|
||||||
|
|
||||||
|
[[nodiscard]] auto& runner() noexcept { return m_runner; }
|
||||||
|
|
||||||
void seedUndocumentedOpcodes();
|
void seedUndocumentedOpcodes();
|
||||||
[[nodiscard]] bool checkState(EightBit::MOS6502& cpu, EightBit::Ram& ram);
|
[[nodiscard]] bool checkState();
|
||||||
|
|
||||||
void pushCurrentMessage();
|
void pushCurrentMessage();
|
||||||
|
|
||||||
@@ -48,18 +59,17 @@ private:
|
|||||||
|
|
||||||
bool check(std::string what, uint16_t address, uint8_t expected, uint8_t actual);
|
bool check(std::string what, uint16_t address, uint8_t expected, uint8_t actual);
|
||||||
|
|
||||||
//void addActualCycle(const actual_cycle_t& value);
|
|
||||||
void addActualCycle(uint16_t address, uint8_t value, std::string action);
|
void addActualCycle(uint16_t address, uint8_t value, std::string action);
|
||||||
void addActualCycle(EightBit::register16_t address, uint8_t value, std::string action);
|
void addActualCycle(EightBit::register16_t address, uint8_t value, std::string action);
|
||||||
|
|
||||||
void addActualReadCycle(EightBit::register16_t address, uint8_t value);
|
void addActualReadCycle(EightBit::register16_t address, uint8_t value);
|
||||||
void addActualWriteCycle(EightBit::register16_t address, uint8_t value);
|
void addActualWriteCycle(EightBit::register16_t address, uint8_t value);
|
||||||
|
|
||||||
//void disassemble(uint16_t address);
|
void disassemble(uint16_t address);
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void dumpCycle(const uint16_t address, const uint8_t value, const T action) {
|
void dumpCycle(const uint16_t address, const uint8_t value, const T action) {
|
||||||
os()
|
m_os
|
||||||
<< std::setfill('0') << std::hex
|
<< std::setfill('0') << std::hex
|
||||||
<< "Address: " << std::setw(4) << (int)address
|
<< "Address: " << std::setw(4) << (int)address
|
||||||
<< ", value: " << std::setw(2) << (int)value
|
<< ", value: " << std::setw(2) << (int)value
|
||||||
@@ -75,33 +85,24 @@ private:
|
|||||||
void dumpCycles(const actual_cycles_t& cycles);
|
void dumpCycles(const actual_cycles_t& cycles);
|
||||||
void dumpCycle(const actual_cycle_t& cycle);
|
void dumpCycle(const actual_cycle_t& cycle);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//[[nodiscard]] auto test() const noexcept { return m_test; }
|
|
||||||
//[[nodiscard]] constexpr const auto& messages() const noexcept { return m_messages; }
|
|
||||||
|
|
||||||
//[[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 auto undocumented() const noexcept { return m_undocumented; }
|
|
||||||
//[[nodiscard]] constexpr auto documented() const noexcept { return !undocumented(); }
|
|
||||||
|
|
||||||
//void check();
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
checker_t(const test_t test);
|
|
||||||
|
|
||||||
[[nodiscard]] auto test() const noexcept { return m_test; }
|
[[nodiscard]] auto test() const noexcept { return m_test; }
|
||||||
|
|
||||||
|
void initialiseState();
|
||||||
|
|
||||||
void initialise(EightBit::Bus& bus);
|
public:
|
||||||
void initialiseState(EightBit::MOS6502& cpu, EightBit::Ram& ram);
|
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 auto undocumented() const noexcept { return m_undocumented; }
|
||||||
|
[[nodiscard]] constexpr auto documented() const noexcept { return !undocumented(); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const auto& messages() const noexcept { return m_messages; }
|
||||||
|
|
||||||
|
void initialise();
|
||||||
|
|
||||||
|
void check(test_t current);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include "TestRunner.h"
|
#include "TestRunner.h"
|
||||||
|
#include "checker_t.h"
|
||||||
#include "test_t.h"
|
#include "test_t.h"
|
||||||
#include "opcode_test_suite_t.h"
|
#include "opcode_test_suite_t.h"
|
||||||
#include "processor_test_suite_t.h"
|
#include "processor_test_suite_t.h"
|
||||||
@@ -24,6 +25,9 @@ int main() {
|
|||||||
TestRunner runner;
|
TestRunner runner;
|
||||||
runner.initialise();
|
runner.initialise();
|
||||||
|
|
||||||
|
checker_t checker(runner);
|
||||||
|
checker.initialise();
|
||||||
|
|
||||||
#ifdef USE_COROUTINES
|
#ifdef USE_COROUTINES
|
||||||
|
|
||||||
processor_test_suite_t m6502_tests(directory);
|
processor_test_suite_t m6502_tests(directory);
|
||||||
@@ -40,16 +44,16 @@ int main() {
|
|||||||
while (test_generator) {
|
while (test_generator) {
|
||||||
|
|
||||||
const auto test = test_generator();
|
const auto test = test_generator();
|
||||||
|
checker.check(test);
|
||||||
|
|
||||||
runner.check(test);
|
if (checker.invalid()) {
|
||||||
if (runner.invalid()) {
|
|
||||||
++invalid_opcode_count;
|
++invalid_opcode_count;
|
||||||
if (runner.unimplemented())
|
if (checker.unimplemented())
|
||||||
++unimplemented_opcode_count;
|
++unimplemented_opcode_count;
|
||||||
if (runner.undocumented())
|
if (checker.undocumented())
|
||||||
++undocumented_opcode_count;
|
++undocumented_opcode_count;
|
||||||
std::cout << "** Failed: " << test.name() << "\n";
|
std::cout << "** Failed: " << test.name() << "\n";
|
||||||
for (const auto& message : runner.messages())
|
for (const auto& message : checker.messages())
|
||||||
std::cout << "**** " << message << "\n";
|
std::cout << "**** " << message << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -70,17 +74,17 @@ int main() {
|
|||||||
|
|
||||||
for (const auto opcode_test_element : opcode) {
|
for (const auto opcode_test_element : opcode) {
|
||||||
|
|
||||||
const auto opcode_test = test_t(opcode_test_element);
|
const auto test = test_t(opcode_test_element);
|
||||||
runner.check(opcode_test);
|
checker.check(test);
|
||||||
|
|
||||||
if (runner.invalid()) {
|
if (checker.invalid()) {
|
||||||
++invalid_opcode_count;
|
++invalid_opcode_count;
|
||||||
if (runner.unimplemented())
|
if (checker.unimplemented())
|
||||||
++unimplemented_opcode_count;
|
++unimplemented_opcode_count;
|
||||||
if (runner.undocumented())
|
if (checker.undocumented())
|
||||||
++undocumented_opcode_count;
|
++undocumented_opcode_count;
|
||||||
std::cout << "** Failed: " << opcode_test.name() << "\n";
|
std::cout << "** Failed: " << test.name() << "\n";
|
||||||
for (const auto& message : runner.messages())
|
for (const auto& message : checker.messages())
|
||||||
std::cout << "**** " << message << "\n";
|
std::cout << "**** " << message << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user