Make the test run output a little more comprehensible.

Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2021-10-21 13:38:44 +01:00
parent 0deb37ab19
commit d59c72cf00
3 changed files with 146 additions and 59 deletions

View File

@ -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,
};
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <set>
#include <sstream>
#include <string>
#include <vector>
@ -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<uint8_t> 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();
};

View File

@ -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<std::chrono::duration<double>>(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;
}