EightBit/M6502/HarteTest_6502/checker_t.cpp

255 lines
7.5 KiB
C++

#include "stdafx.h"
#include "checker_t.h"
void checker_t::addActualCycle(const uint16_t address, const uint8_t value, const std::string_view action) {
m_actualCycles.push_back({ address, value, action });
}
void checker_t::addActualCycle(const EightBit::register16_t address, const uint8_t value, const std::string_view action) {
addActualCycle(address.word, value, action);
}
void checker_t::addActualReadCycle(const EightBit::register16_t address, const uint8_t value) {
addActualCycle(address, value, m_read_action);
}
void checker_t::addActualWriteCycle(const EightBit::register16_t address, const uint8_t value) {
addActualCycle(address, value, m_write_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(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 checker_t::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 checker_t::raise(std::string_view what, const std::string_view expected, const std::string_view actual) {
os()
<< std::setw(0) << std::setfill(' ')
<< what
<< ": expected: " << expected
<< ", actual: " << actual;
pushCurrentMessage();
}
bool checker_t::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;
}
checker_t::checker_t(TestRunner& runner)
: m_runner(runner) {}
void checker_t::initialiseState(const test_t test) {
auto& cpu = runner().CPU();
auto& ram = runner().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()) {
auto data = entry.begin();
const auto address = uint16_t(int64_t(*data));
const auto value = uint8_t(int64_t(*++data));
ram.poke(address, value);
}
}
void checker_t::initialise() {
auto& bus = runner();
bus.ReadByte.connect([this](EightBit::EventArgs&) {
addActualReadCycle(runner().ADDRESS(), runner().DATA());
});
bus.WrittenByte.connect([this](EightBit::EventArgs&) {
addActualWriteCycle(runner().ADDRESS(), runner().DATA());
});
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
}
auto expected_data = expected_cycle.begin();
const auto& actual = actual_cycles[actual_idx++];
const auto expected_address = uint16_t(int64_t(*expected_data));
const auto actual_address = std::get<0>(actual);
check("Cycle address", expected_address, actual_address);
const auto expected_value = uint8_t(int64_t(*++expected_data));
const auto actual_value = std::get<1>(actual);
check("Cycle value", expected_value, actual_value);
const auto expected_action = (*++expected_data).get_string();
const auto actual_action = std::get<2>(actual);
check("Cycle action", expected_action.value_unsafe(), 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 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()) {
auto data = entry.begin();
const auto address = uint16_t(int64_t(*data));
const auto value = uint8_t(int64_t(*++data));
const auto ram_good = check("RAM", address, value, ram.peek(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("");
}
std::string checker_t::disassemble(uint16_t address) {
return m_disassembler.disassemble(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("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);
}
}