diff --git a/M6502/HarteTest_6502/TestRunner.cpp b/M6502/HarteTest_6502/TestRunner.cpp index 1d46f85..be6e4f6 100644 --- a/M6502/HarteTest_6502/TestRunner.cpp +++ b/M6502/HarteTest_6502/TestRunner.cpp @@ -49,22 +49,23 @@ void TestRunner::dumpCycles(std::string which, const cycles_t& events) { } void TestRunner::dumpCycles(const cycles_t& cycles) { - os() << std::hex << std::uppercase << std::setfill('0'); for (const auto& cycle: cycles) dumpCycle(cycle); } void TestRunner::dumpCycle(const cycle_t& cycle) { os() + << std::setfill('0') << std::hex << "Address: " << std::setw(4) << cycle.address() << ", value: " << std::setw(2) << (int)cycle.value() << ", action: " << cycle.action(); - m_messages.push_back(os().str()); - os().str(""); + pushCurrentMessage(); } void TestRunner::initialise() { + seedUndocumentedOpcodes(); + ReadByte.connect([this](EightBit::EventArgs&) { addActualReadCycle(ADDRESS(), DATA()); }); @@ -73,46 +74,45 @@ void TestRunner::initialise() { addActualWriteCycle(ADDRESS(), DATA()); }); - os() << std::hex << std::uppercase << std::setfill('0'); + os() << std::hex << std::uppercase; } void TestRunner::raise(std::string what, uint16_t expected, uint16_t actual) { os() - << std::setw(4) + << std::setw(2) << std::setfill(' ') << what + << std::setw(4) << std::setfill('0') << ": expected: " << (int)expected << ", actual: " << (int)actual; - m_messages.push_back(os().str()); - os().str(""); + pushCurrentMessage(); } void TestRunner::raise(std::string what, uint8_t expected, uint8_t actual) { os() - << std::setw(2) + << std::setw(2) << std::setfill(' ') << what + << std::setfill('0') << ": expected: " << (int)expected - << "(" << EightBit::Disassembly::dump_Flags(expected) << ")" + << " (" << EightBit::Disassembly::dump_Flags(expected) << ")" << ", actual: " << (int)actual - << "(" << EightBit::Disassembly::dump_Flags(actual) << ")"; - m_messages.push_back(os().str()); - os().str(""); + << " (" << EightBit::Disassembly::dump_Flags(actual) << ")"; + pushCurrentMessage(); } void TestRunner::raise(std::string what, std::string expected, std::string actual) { os() + << std::setw(0) << std::setfill(' ') << what << ": expected: " << expected << ", actual: " << actual; - m_messages.push_back(os().str()); - os().str(""); + pushCurrentMessage(); } bool TestRunner::check(std::string what, uint16_t address, uint8_t expected, uint8_t actual) { const auto success = actual == expected; if (!success) { - os() << what << ": " << std::setw(4) << (int)address; + os() << what << ": " << std::setw(4) << std::setfill('0') << (int)address; raise(os().str(), expected, actual); - os().str(""); } return success; } @@ -170,38 +170,83 @@ bool TestRunner::checkState() { return pc_good && s_good && a_good && x_good && y_good && p_good && !ram_problem; } -bool TestRunner::check() { +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() { initialise(); raisePOWER(); initialiseState(); const auto pc = CPU().PC(); - const int cycles = CPU().step(); - const auto valid = checkState(); - if (m_cycle_count_mismatch) { - if (cycles == 1) { - m_messages.push_back("Unimplemented"); - } else { - - try { - os() << m_disassembler.disassemble(pc.word); - } catch (const std::domain_error& error) { - os() << "Disassembly problem: " << error.what(); - } - m_messages.push_back(os().str()); - os().str(""); - - os() - << std::dec << std::setfill(' ') - << "Stepped cycles: " << cycles - << ", expected events: " << test().cycles().size() - << ", actual events: " << m_actualCycles.size(); - m_messages.push_back(os().str()); - os().str(""); - - dumpCycles("-- Expected cycles", test().cycles()); - dumpCycles("-- Actual cycles", m_actualCycles); - } - } + const auto start_opcode = peek(pc); + m_cycles = CPU().step(); lowerPOWER(); - return valid; + + 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.word); + + raise("PC", test().final_state().pc(), CPU().PC().word); + raise("S", test().final_state().s(), CPU().S()); + raise("A", test().final_state().a(), CPU().A()); + raise("X", test().final_state().x(), CPU().X()); + raise("Y", test().final_state().y(), CPU().Y()); + raise("P", test().final_state().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() { + 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, + }; +} \ No newline at end of file diff --git a/M6502/HarteTest_6502/TestRunner.h b/M6502/HarteTest_6502/TestRunner.h index ac6d0d7..1efeec3 100644 --- a/M6502/HarteTest_6502/TestRunner.h +++ b/M6502/HarteTest_6502/TestRunner.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -28,9 +29,18 @@ private: cycles_t m_actualCycles; bool m_cycle_count_mismatch = false; + int m_cycles = 0; + bool m_valid = true; + bool m_undocumented = false; + + std::set m_undocumented_opcodes; + + void seedUndocumentedOpcodes(); void initialiseState(); [[nodiscard]] bool checkState(); + 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 expected, std::string actual); @@ -52,6 +62,8 @@ private: void addActualReadCycle(EightBit::register16_t address, uint8_t value); void addActualWriteCycle(EightBit::register16_t address, uint8_t value); + void disassemble(uint16_t address); + void dumpCycles(std::string which, const cycles_t& cycles); void dumpCycles(const cycles_t& cycles); void dumpCycle(const cycle_t& cycle); @@ -74,5 +86,13 @@ public: [[nodiscard]] constexpr const auto& test() const noexcept { return m_test; } [[nodiscard]] constexpr const auto& messages() const noexcept { return m_messages; } - [[nodiscard]] bool check(); + [[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(); }; diff --git a/M6502/HarteTest_6502/tests.cpp b/M6502/HarteTest_6502/tests.cpp index 0bb23d9..d95352c 100644 --- a/M6502/HarteTest_6502/tests.cpp +++ b/M6502/HarteTest_6502/tests.cpp @@ -14,7 +14,10 @@ int main() { const auto start_time = std::chrono::steady_clock::now(); - int bad_opcode_count = 0; + int undocumented_opcode_count = 0; + int unimplemented_opcode_count = 0; + int invalid_opcode_count = 0; + for (const auto& entry : std::filesystem::directory_iterator{ location }) { const auto path = entry.path(); @@ -26,24 +29,41 @@ int main() { const auto opcode_test_array = opcode.raw().get_array(); - bool opcode_bad = false; + bool opcode_undocumented = false; + bool opcode_unimplemented = false; + bool opcode_invalid = false; + for (const auto& opcode_test_element : opcode_test_array) { const auto opcode_test = test_t(opcode_test_element); TestRunner runner(opcode_test); - const auto good = runner.check(); - if (!good) { - if (!opcode_bad) { - std::cout << "** Failed: " << opcode_test.name() << "\n"; - for (const auto& message : runner.messages()) - std::cout << "**** " << message << "\n"; - opcode_bad = true; - } + runner.check(); + + auto undocumented = runner.undocumented(); + auto unimplemented = runner.unimplemented(); + auto implemented = runner.implemented(); + auto invalid = runner.invalid(); + + if (invalid) { + opcode_invalid = true; + if (unimplemented) + opcode_unimplemented = true; + if (undocumented) + opcode_undocumented = true; + std::cout << "** Failed: " << opcode_test.name() << "\n"; + for (const auto& message : runner.messages()) + std::cout << "**** " << message << "\n"; + break; } } - if (opcode_bad) - ++bad_opcode_count; + + if (opcode_undocumented) + ++undocumented_opcode_count; + if (opcode_unimplemented) + ++unimplemented_opcode_count; + if (opcode_invalid) + ++invalid_opcode_count; } const auto finish_time = std::chrono::steady_clock::now(); @@ -51,6 +71,8 @@ int main() { const auto seconds = std::chrono::duration_cast>(elapsed_time).count(); std::cout << "Elapsed time: " << seconds << " seconds" - << ", bad opcode count: " << bad_opcode_count + << ", undocumented opcode count: " << undocumented_opcode_count + << ", unimplemented opcode count: " << unimplemented_opcode_count + << ", invalid opcode count: " << (invalid_opcode_count - unimplemented_opcode_count) << std::endl; }