From 9e9c15e28975aa84be7caaa05e97f708af055b64 Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Wed, 27 Oct 2021 09:53:58 +0100 Subject: [PATCH] Use C++20 co-routines as generators for opcode suite and test generation. Signed-off-by: Adrian Conlon --- M6502/HarteTest_6502/HarteTest_6502.vcxproj | 12 +- .../HarteTest_6502.vcxproj.filters | 9 + M6502/HarteTest_6502/TestRunner.cpp | 22 +- M6502/HarteTest_6502/TestRunner.h | 25 +- M6502/HarteTest_6502/checker_t.cpp | 245 ++++++++++++++++++ M6502/HarteTest_6502/checker_t.h | 107 ++++++++ M6502/HarteTest_6502/co_generator_t.h | 57 ++++ M6502/HarteTest_6502/element_t.h | 12 +- M6502/HarteTest_6502/opcode_test_suite_t.cpp | 5 + M6502/HarteTest_6502/opcode_test_suite_t.h | 5 + M6502/HarteTest_6502/parser_t.cpp | 2 +- M6502/HarteTest_6502/parser_t.h | 6 +- .../HarteTest_6502/processor_test_suite_t.cpp | 13 + M6502/HarteTest_6502/processor_test_suite_t.h | 20 ++ M6502/HarteTest_6502/state_t.h | 6 +- M6502/HarteTest_6502/tests.cpp | 61 ++++- 16 files changed, 560 insertions(+), 47 deletions(-) create mode 100644 M6502/HarteTest_6502/checker_t.cpp create mode 100644 M6502/HarteTest_6502/checker_t.h create mode 100644 M6502/HarteTest_6502/co_generator_t.h create mode 100644 M6502/HarteTest_6502/processor_test_suite_t.cpp create mode 100644 M6502/HarteTest_6502/processor_test_suite_t.h diff --git a/M6502/HarteTest_6502/HarteTest_6502.vcxproj b/M6502/HarteTest_6502/HarteTest_6502.vcxproj index 93577ab..7f99a99 100644 --- a/M6502/HarteTest_6502/HarteTest_6502.vcxproj +++ b/M6502/HarteTest_6502/HarteTest_6502.vcxproj @@ -24,6 +24,7 @@ {894eb6ef-4c8d-4401-bbaa-aba05a021abb} HarteTest_6502 HarteTest_6502 + 10.0 @@ -94,7 +95,7 @@ true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 + stdcpp20 Use @@ -107,7 +108,7 @@ Level3 true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdcpp17 + stdcpp20 Use false false @@ -127,7 +128,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 + stdcpp20 Use @@ -140,7 +141,7 @@ Level3 true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdcpp17 + stdcpp20 Use false false @@ -162,6 +163,7 @@ + NotUsing @@ -183,11 +185,13 @@ + + diff --git a/M6502/HarteTest_6502/HarteTest_6502.vcxproj.filters b/M6502/HarteTest_6502/HarteTest_6502.vcxproj.filters index eddd4ad..e487be5 100644 --- a/M6502/HarteTest_6502/HarteTest_6502.vcxproj.filters +++ b/M6502/HarteTest_6502/HarteTest_6502.vcxproj.filters @@ -56,6 +56,9 @@ Source Files + + Source Files + @@ -97,5 +100,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/M6502/HarteTest_6502/TestRunner.cpp b/M6502/HarteTest_6502/TestRunner.cpp index 465e0a5..fd38f08 100644 --- a/M6502/HarteTest_6502/TestRunner.cpp +++ b/M6502/HarteTest_6502/TestRunner.cpp @@ -25,11 +25,11 @@ void TestRunner::lowerPOWER() { EightBit::Bus::lowerPOWER(); } -void TestRunner::addActualCycle(const uint16_t address, const uint8_t value, const std::string action) { - m_actualCycles.push_back({ address, value, action }); +void TestRunner::addActualCycle(const uint16_t address, const uint8_t value, std::string_view action) { + m_actualCycles.push_back({ address, value, std::string(action) }); } -void TestRunner::addActualCycle(const EightBit::register16_t address, const uint8_t value, const std::string action) { +void TestRunner::addActualCycle(const EightBit::register16_t address, const uint8_t value, std::string_view action) { addActualCycle(address.word, value, action); } @@ -41,8 +41,8 @@ void TestRunner::addActualWriteCycle(const EightBit::register16_t address, const addActualCycle(address, value, "write"); } -void TestRunner::dumpCycles(const std::string which, const actual_cycles_t& events) { - m_messages.push_back(which); +void TestRunner::dumpCycles(std::string_view which, const actual_cycles_t& events) { + m_messages.push_back(std::string(which)); dumpCycles(events); } @@ -55,8 +55,8 @@ void TestRunner::dumpCycle(const actual_cycle_t& cycle) { dumpCycle(std::get<0>(cycle), std::get<1>(cycle), std::get<2>(cycle)); } -void TestRunner::dumpCycles(const std::string which, const cycles_t events) { - m_messages.push_back(which); +void TestRunner::dumpCycles(std::string_view which, const cycles_t events) { + m_messages.push_back(std::string(which)); dumpCycles(events); } @@ -84,7 +84,7 @@ void TestRunner::initialise() { os() << std::hex << std::uppercase; } -void TestRunner::raise(const std::string what, const uint16_t expected, const uint16_t actual) { +void TestRunner::raise(std::string_view what, const uint16_t expected, const uint16_t actual) { os() << std::setw(2) << std::setfill(' ') << what @@ -94,7 +94,7 @@ void TestRunner::raise(const std::string what, const uint16_t expected, const ui pushCurrentMessage(); } -void TestRunner::raise(const std::string what, const uint8_t expected, const uint8_t actual) { +void TestRunner::raise(std::string_view what, const uint8_t expected, const uint8_t actual) { os() << std::setw(2) << std::setfill(' ') << what @@ -106,7 +106,7 @@ void TestRunner::raise(const std::string what, const uint8_t expected, const uin pushCurrentMessage(); } -void TestRunner::raise(const std::string what, const std::string_view expected, const std::string_view actual) { +void TestRunner::raise(std::string_view what, std::string_view expected, std::string_view actual) { os() << std::setw(0) << std::setfill(' ') << what @@ -115,7 +115,7 @@ void TestRunner::raise(const std::string what, const std::string_view expected, pushCurrentMessage(); } -bool TestRunner::check(const std::string what, const uint16_t address, const uint8_t expected, const uint8_t actual) { +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; diff --git a/M6502/HarteTest_6502/TestRunner.h b/M6502/HarteTest_6502/TestRunner.h index 2de325b..90d6792 100644 --- a/M6502/HarteTest_6502/TestRunner.h +++ b/M6502/HarteTest_6502/TestRunner.h @@ -45,23 +45,30 @@ private: void pushCurrentMessage(); - void raise(std::string what, uint16_t expected, uint16_t actual); - void raise(std::string what, uint8_t expected, uint8_t actual); - void raise(std::string what, std::string_view expected, std::string_view actual); + 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 - bool check(std::string what, T expected, T actual) { + 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 what, uint16_t address, uint8_t expected, uint8_t actual); + 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 action); - void addActualCycle(EightBit::register16_t address, uint8_t value, std::string action); + 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); @@ -78,11 +85,11 @@ private: pushCurrentMessage(); } - void dumpCycles(std::string which, cycles_t cycles); + void dumpCycles(std::string_view 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(std::string_view which, const actual_cycles_t& cycles); void dumpCycles(const actual_cycles_t& cycles); void dumpCycle(const actual_cycle_t& cycle); diff --git a/M6502/HarteTest_6502/checker_t.cpp b/M6502/HarteTest_6502/checker_t.cpp new file mode 100644 index 0000000..a2ee7d8 --- /dev/null +++ b/M6502/HarteTest_6502/checker_t.cpp @@ -0,0 +1,245 @@ +#include "stdafx.h" +#include "checker_t.h" + +std::set checker_t::m_undocumented_opcodes; +bool checker_t::m_undocumented_opcodes_initialised = false; + +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::addActualReadCycle(const EightBit::register16_t address, const uint8_t value) { + addActualCycle(address, value, "read"); +} + +void checker_t::addActualWriteCycle(const EightBit::register16_t address, const uint8_t value) { + addActualCycle(address, value, "write"); +} + +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 + << " (" << EightBit::Disassembly::dump_Flags(expected) << ")" + << ", actual: " << (int)actual + << " (" << EightBit::Disassembly::dump_Flags(actual) << ")"; + pushCurrentMessage(); +} + +void checker_t::raise(const std::string what, const std::string_view expected, const std::string_view actual) { + os() + << std::setw(0) << std::setfill(' ') + << what + << ": expected: " << expected + << ", actual: " << actual; + 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) +: m_test(test) {} + +void checker_t::initialiseState(EightBit::MOS6502& cpu, EightBit::Ram& ram) { + + 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()); + } +} + +void checker_t::initialise(EightBit::Bus& bus) { + + seedUndocumentedOpcodes(); + + bus.ReadByte.connect([this, &bus](EightBit::EventArgs&) { + addActualReadCycle(bus.ADDRESS(), bus.DATA()); + }); + + bus.WrittenByte.connect([this, &bus](EightBit::EventArgs&) { + addActualWriteCycle(bus.ADDRESS(), bus.DATA()); + }); + + os() << std::hex << std::uppercase; +} + +// +bool checker_t::checkState(EightBit::MOS6502& cpu, EightBit::Ram& ram) { + + 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 checker_t::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() { +// initialise(); +// 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 checker_t::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; +} diff --git a/M6502/HarteTest_6502/checker_t.h b/M6502/HarteTest_6502/checker_t.h new file mode 100644 index 0000000..4eff3f5 --- /dev/null +++ b/M6502/HarteTest_6502/checker_t.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +#include "cycles_t.h" +#include "cycle_t.h" +#include "test_t.h" + +class checker_t { +public: +private: + static std::set m_undocumented_opcodes; + static bool m_undocumented_opcodes_initialised; + + const test_t m_test; + + std::ostringstream m_os; + std::vector m_messages; + + typedef std::tuple actual_cycle_t; + typedef std::vector 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; + + [[nodiscard]] auto& os() { return m_os; } + + void seedUndocumentedOpcodes(); + [[nodiscard]] bool checkState(EightBit::MOS6502& cpu, EightBit::Ram& ram); + + void pushCurrentMessage(); + + void raise(std::string what, uint16_t expected, uint16_t actual); + void raise(std::string what, uint8_t expected, uint8_t actual); + void raise(std::string what, std::string_view expected, std::string_view actual); + + template + bool check(std::string what, T expected, T actual) { + const auto success = actual == expected; + if (!success) + raise(what, expected, actual); + return success; + } + + 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(EightBit::register16_t address, uint8_t value, std::string 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 + void dumpCycle(const uint16_t address, const uint8_t value, const T action) { + 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 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); + + + + + + + //[[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; } + + + void initialise(EightBit::Bus& bus); + void initialiseState(EightBit::MOS6502& cpu, EightBit::Ram& ram); + +}; + diff --git a/M6502/HarteTest_6502/co_generator_t.h b/M6502/HarteTest_6502/co_generator_t.h new file mode 100644 index 0000000..97c7d91 --- /dev/null +++ b/M6502/HarteTest_6502/co_generator_t.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +// from https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html + +template +struct co_generator_t final { + + struct promise_type; + using handle_type = std::coroutine_handle; + + struct promise_type { + T value_; + std::exception_ptr exception_; + + co_generator_t get_return_object() { + return co_generator_t(handle_type::from_promise(*this)); + } + std::suspend_always initial_suspend() { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + void unhandled_exception() { exception_ = std::current_exception(); } + template From> // C++20 concept + std::suspend_always yield_value(From&& from) { + value_ = std::forward(from); + return {}; + } + void return_void() {} + }; + + handle_type h_; + + co_generator_t(handle_type h) : h_(h) {} + ~co_generator_t() { h_.destroy(); } + explicit operator bool() { + fill(); + return !h_.done(); + } + T operator()() { + fill(); + full_ = false; + return std::move(h_.promise().value_); + } + +private: + bool full_ = false; + + void fill() { + if (!full_) { + h_(); + if (h_.promise().exception_) + std::rethrow_exception(h_.promise().exception_); + full_ = true; + } + } +}; diff --git a/M6502/HarteTest_6502/element_t.h b/M6502/HarteTest_6502/element_t.h index 7053df8..6826904 100644 --- a/M6502/HarteTest_6502/element_t.h +++ b/M6502/HarteTest_6502/element_t.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "simdjson/simdjson.h" @@ -14,10 +14,8 @@ protected: [[nodiscard]] auto raw() const noexcept { return m_raw; } - [[nodiscard]] auto at(std::string key) const noexcept { return raw()[key]; } - [[nodiscard]] auto operator[](std::string key) const noexcept { return at(key); } - [[nodiscard]] auto array_at(std::string key) const noexcept { return at(key).get_array(); } - [[nodiscard]] auto integer_at(std::string key) const noexcept { return at(key).get_int64(); } - - + [[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 at(key).get_int64(); } }; diff --git a/M6502/HarteTest_6502/opcode_test_suite_t.cpp b/M6502/HarteTest_6502/opcode_test_suite_t.cpp index 3efbe46..8daa2fa 100644 --- a/M6502/HarteTest_6502/opcode_test_suite_t.cpp +++ b/M6502/HarteTest_6502/opcode_test_suite_t.cpp @@ -3,3 +3,8 @@ opcode_test_suite_t::opcode_test_suite_t(const std::string path) noexcept : parser_t(path) {} + +co_generator_t opcode_test_suite_t::generator() { + for (const auto element : *this) + co_yield test_t(element); +} diff --git a/M6502/HarteTest_6502/opcode_test_suite_t.h b/M6502/HarteTest_6502/opcode_test_suite_t.h index 69ad42c..6d512ab 100644 --- a/M6502/HarteTest_6502/opcode_test_suite_t.h +++ b/M6502/HarteTest_6502/opcode_test_suite_t.h @@ -3,14 +3,19 @@ #include #include "parser_t.h" +#include "co_generator_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(); } + + co_generator_t generator(); }; diff --git a/M6502/HarteTest_6502/parser_t.cpp b/M6502/HarteTest_6502/parser_t.cpp index 5f5a026..c252248 100644 --- a/M6502/HarteTest_6502/parser_t.cpp +++ b/M6502/HarteTest_6502/parser_t.cpp @@ -7,5 +7,5 @@ parser_t::parser_t(const std::string path) noexcept : m_path(path) {} void parser_t::load() { - m_raw = m_parser.load(path()); + m_raw = m_parser.load(m_path); } diff --git a/M6502/HarteTest_6502/parser_t.h b/M6502/HarteTest_6502/parser_t.h index 2d5c157..8a383ba 100644 --- a/M6502/HarteTest_6502/parser_t.h +++ b/M6502/HarteTest_6502/parser_t.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "simdjson/simdjson.h" @@ -11,13 +12,14 @@ private: // Therefore, it can only be used for one document at a time. static simdjson::dom::parser m_parser; - const std::string m_path; + std::string m_path; simdjson::dom::element m_raw; public: + parser_t() noexcept {} parser_t(std::string path) noexcept; - [[nodiscard]] constexpr const auto& path() const noexcept { return m_path; } + [[nodiscard]] constexpr std::string_view path() const noexcept { return m_path; } [[nodiscard]] const auto raw() const noexcept { return m_raw; } virtual void load(); diff --git a/M6502/HarteTest_6502/processor_test_suite_t.cpp b/M6502/HarteTest_6502/processor_test_suite_t.cpp new file mode 100644 index 0000000..7778ef6 --- /dev/null +++ b/M6502/HarteTest_6502/processor_test_suite_t.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "processor_test_suite_t.h" + +#include + +processor_test_suite_t::processor_test_suite_t(std::string location) noexcept +: m_location(location) { +} +co_generator_t processor_test_suite_t::generator() { + std::filesystem::path directory = location(); + for (const auto& entry : std::filesystem::directory_iterator{ directory }) + co_yield opcode_test_suite_t(entry.path().string()); +} diff --git a/M6502/HarteTest_6502/processor_test_suite_t.h b/M6502/HarteTest_6502/processor_test_suite_t.h new file mode 100644 index 0000000..e37ab48 --- /dev/null +++ b/M6502/HarteTest_6502/processor_test_suite_t.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#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; + + std::string_view location() const noexcept { return m_location; } + + co_generator_t generator(); +}; + diff --git a/M6502/HarteTest_6502/state_t.h b/M6502/HarteTest_6502/state_t.h index 17e8f7e..eb1983b 100644 --- a/M6502/HarteTest_6502/state_t.h +++ b/M6502/HarteTest_6502/state_t.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include "simdjson/simdjson.h" @@ -10,8 +10,8 @@ class state_t final : public element_t { private: - [[nodiscard]] auto address_at(std::string key) const noexcept { return (uint16_t)integer_at(key); } - [[nodiscard]] auto byte_at(std::string key) const noexcept { return (uint8_t)integer_at(key); } + [[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); } public: state_t(simdjson::dom::element input) noexcept; diff --git a/M6502/HarteTest_6502/tests.cpp b/M6502/HarteTest_6502/tests.cpp index f02101c..dd1b62b 100644 --- a/M6502/HarteTest_6502/tests.cpp +++ b/M6502/HarteTest_6502/tests.cpp @@ -7,10 +7,13 @@ #include "TestRunner.h" #include "test_t.h" #include "opcode_test_suite_t.h" +#include "processor_test_suite_t.h" + +#define USE_COROUTINES int main() { - std::filesystem::path location = "C:\\github\\spectrum\\libraries\\EightBit\\modules\\ProcessorTests\\6502\\v1"; + auto directory = std::string("C:\\github\\spectrum\\libraries\\EightBit\\modules\\ProcessorTests\\6502\\v1"); const auto start_time = std::chrono::steady_clock::now(); @@ -21,6 +24,42 @@ int main() { TestRunner runner; runner.initialise(); +#ifdef USE_COROUTINES + + 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(); + while (test_generator) { + + const auto test = test_generator(); + + runner.check(test); + if (runner.invalid()) { + ++invalid_opcode_count; + if (runner.unimplemented()) + ++unimplemented_opcode_count; + if (runner.undocumented()) + ++undocumented_opcode_count; + std::cout << "** Failed: " << test.name() << "\n"; + for (const auto& message : runner.messages()) + std::cout << "**** " << message << "\n"; + break; + } + } + } + +#else + + std::filesystem::path location = directory; + for (const auto& entry : std::filesystem::directory_iterator{ location }) { const auto path = entry.path(); @@ -48,13 +87,15 @@ int main() { } } - const auto finish_time = std::chrono::steady_clock::now(); - const auto elapsed_time = finish_time - start_time; - const auto seconds = std::chrono::duration_cast>(elapsed_time).count(); - std::cout - << "Elapsed time: " << seconds << " seconds" - << ", undocumented opcode count: " << undocumented_opcode_count - << ", unimplemented opcode count: " << unimplemented_opcode_count - << ", invalid opcode count: " << (invalid_opcode_count - unimplemented_opcode_count) - << std::endl; +#endif + + const auto finish_time = std::chrono::steady_clock::now(); + const auto elapsed_time = finish_time - start_time; + const auto seconds = std::chrono::duration_cast>(elapsed_time).count(); + std::cout + << "Elapsed time: " << seconds << " seconds" + << ", undocumented opcode count: " << undocumented_opcode_count + << ", unimplemented opcode count: " << unimplemented_opcode_count + << ", invalid opcode count: " << (invalid_opcode_count - unimplemented_opcode_count) + << std::endl; }