diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm index 6017f35ac..9426756d8 100644 --- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm @@ -14,20 +14,27 @@ #include #include +#include "6502Selector.hpp" + namespace { struct StopException {}; -struct BusHandler: public CPU::MOS6502Esque::BusHandler { - // Use a map to store RAM contents, in order to preserve initialised state. - std::unordered_map ram; - std::unordered_map inventions; +template +struct BusHandler: public CPU::MOS6502Esque::BusHandlerT { + using AddressType = typename CPU::MOS6502Esque::BusHandlerT::AddressType; - Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) { + // Use a map to store RAM contents, in order to preserve initialised state. + std::unordered_map ram; + std::unordered_map inventions; + + Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, AddressType address, uint8_t *value) { // Record the basics of the operation. auto &cycle = cycles.emplace_back(); cycle.operation = operation; - cycle.extended_bus = processor.get_extended_bus_output(); + if constexpr (has_extended_bus_output(type)) { + cycle.extended_bus = processor.get_extended_bus_output(); + } // Perform the operation, and fill in the cycle's value. using BusOperation = CPU::MOS6502Esque::BusOperation; @@ -92,25 +99,27 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler { allow_pc_repetition = opcode == 0x54 || opcode == 0x44; using Register = CPU::MOS6502Esque::Register; - const uint32_t pc = - processor.value_of(Register::ProgramCounter) | - (processor.value_of(Register::ProgramBank) << 16); + const auto pc = + AddressType( + processor.value_of(Register::ProgramCounter) | + (processor.value_of(Register::ProgramBank) << 16) + ); inventions[pc] = ram[pc] = opcode; } int pc_overshoot = 0; - std::optional initial_pc; + std::optional initial_pc; bool allow_pc_repetition = false; struct Cycle { CPU::MOS6502Esque::BusOperation operation; - std::optional address; + std::optional address; std::optional value; - int extended_bus; + int extended_bus = 0; }; std::vector cycles; - CPU::WDC65816::Processor processor; + CPU::MOS6502Esque::Processor, false> processor; BusHandler() : processor(*this) { // Never run the official reset procedure. @@ -118,7 +127,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler { } }; -template void print_registers(FILE *file, const Processor &processor, int pc_offset) { +template void print_registers(FILE *file, const Processor &processor, int pc_offset) { using Register = CPU::MOS6502Esque::Register; fprintf(file, "\"pc\": %d, ", (processor.value_of(Register::ProgramCounter) + pc_offset) & 65535); fprintf(file, "\"s\": %d, ", processor.value_of(Register::StackPointer)); @@ -126,13 +135,16 @@ template void print_registers(FILE *file, const Processor & fprintf(file, "\"a\": %d, ", processor.value_of(Register::A)); fprintf(file, "\"x\": %d, ", processor.value_of(Register::X)); fprintf(file, "\"y\": %d, ", processor.value_of(Register::Y)); - fprintf(file, "\"dbr\": %d, ", processor.value_of(Register::DataBank)); - fprintf(file, "\"d\": %d, ", processor.value_of(Register::Direct)); - fprintf(file, "\"pbr\": %d, ", processor.value_of(Register::ProgramBank)); - fprintf(file, "\"e\": %d, ", processor.value_of(Register::EmulationFlag)); + if constexpr (has_emulation) { + fprintf(file, "\"dbr\": %d, ", processor.value_of(Register::DataBank)); + fprintf(file, "\"d\": %d, ", processor.value_of(Register::Direct)); + fprintf(file, "\"pbr\": %d, ", processor.value_of(Register::ProgramBank)); + fprintf(file, "\"e\": %d, ", processor.value_of(Register::EmulationFlag)); + } } -void print_ram(FILE *file, const std::unordered_map &data) { +template +void print_ram(FILE *file, const std::unordered_map &data) { fprintf(file, "\"ram\": ["); bool is_first = true; for(const auto &pair: data) { @@ -143,22 +155,18 @@ void print_ram(FILE *file, const std::unordered_map &data) { fprintf(file, "]"); } -} // MARK: - New test generator. -@interface TestGenerator : NSObject -@end -@implementation TestGenerator - -- (void)generate { - BusHandler handler; +template void generate() { + BusHandler handler; + constexpr bool has_emulation = has(type, CPU::MOS6502Esque::Register::EmulationFlag); NSString *const tempDir = NSTemporaryDirectory(); NSLog(@"Outputting to %@", tempDir); - for(int operation = 0; operation < 512; operation++) { + for(int operation = 0; operation < (has_emulation ? 512 : 256); operation++) { // Make tests repeatable, at least for any given instance of // the runtime. srand(65816 + operation); @@ -166,7 +174,10 @@ void print_ram(FILE *file, const std::unordered_map &data) { const bool is_emulated = operation & 256; const uint8_t opcode = operation & 255; - NSString *const targetName = [NSString stringWithFormat:@"%@%02x.%c.json", tempDir, opcode, is_emulated ? 'e' : 'n']; + NSString *const targetName = + has_emulation ? + [NSString stringWithFormat:@"%@%02x.%c.json", tempDir, opcode, is_emulated ? 'e' : 'n'] : + [NSString stringWithFormat:@"%@%02x.json", tempDir, opcode]; FILE *const target = fopen(targetName.UTF8String, "wt"); bool is_first_test = true; @@ -186,21 +197,28 @@ void print_ram(FILE *file, const std::unordered_map &data) { handler.processor.set_value_of(Register::Y, rand() >> 8); handler.processor.set_value_of(Register::ProgramCounter, rand() >> 8); handler.processor.set_value_of(Register::StackPointer, rand() >> 8); - handler.processor.set_value_of(Register::DataBank, rand() >> 8); - handler.processor.set_value_of(Register::ProgramBank, rand() >> 8); - handler.processor.set_value_of(Register::Direct, rand() >> 8); - // ... except for emulation mode, which is a given. - // And is set last to ensure proper internal state is applied. - handler.processor.set_value_of(Register::EmulationFlag, is_emulated); + if(has_emulation) { + handler.processor.set_value_of(Register::DataBank, rand() >> 8); + handler.processor.set_value_of(Register::ProgramBank, rand() >> 8); + handler.processor.set_value_of(Register::Direct, rand() >> 8); + + // ... except for emulation mode, which is a given. + // And is set last to ensure proper internal state is applied. + handler.processor.set_value_of(Register::EmulationFlag, is_emulated); + } // Establish the opcode. handler.setup(opcode); // Dump initial state. - fprintf(target, "{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1); + if(has_emulation) { + fprintf(target, "{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1); + } else { + fprintf(target, "{ \"name\": \"%02x %d\", ", opcode, test + 1); + } fprintf(target, "\"initial\": {"); - print_registers(target, handler.processor, 0); + print_registers(target, handler.processor, 0); // Run to the second opcode fetch. try { @@ -212,7 +230,7 @@ void print_ram(FILE *file, const std::unordered_map &data) { // Dump final state. fprintf(target, "}, \"final\": {"); - print_registers(target, handler.processor, handler.pc_overshoot); + print_registers(target, handler.processor, handler.pc_overshoot); print_ram(target, handler.ram); fprintf(target, "}, "); @@ -247,12 +265,6 @@ void print_ram(FILE *file, const std::unordered_map &data) { assert(false); } - using ExtendedBusOutput = CPU::WDC65816::ExtendedBusOutput; - const bool emulation = cycle.extended_bus & ExtendedBusOutput::Emulation; - const bool memory_size = cycle.extended_bus & ExtendedBusOutput::MemorySize; - const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize; - const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock; - fprintf(target, "["); if(cycle.address) { fprintf(target, "%d, ", *cycle.address); @@ -264,16 +276,31 @@ void print_ram(FILE *file, const std::unordered_map &data) { } else { fprintf(target, "null, "); } - fprintf(target, "\"%c%c%c%c%c%c%c%c\"]", - vda ? 'd' : '-', - vpa ? 'p' : '-', - vpb ? 'v' : '-', - wait ? '-' : (read ? 'r' : 'w'), - wait ? '-' : (emulation ? 'e' : '-'), - wait ? '-' : (memory_size ? 'm' : '-'), - wait ? '-' : (index_size ? 'x' : '-'), - wait ? '-' : (memory_lock ? 'l' : '-') - ); + + if(has_emulation) { + using ExtendedBusOutput = CPU::WDC65816::ExtendedBusOutput; + const bool emulation = cycle.extended_bus & ExtendedBusOutput::Emulation; + const bool memory_size = cycle.extended_bus & ExtendedBusOutput::MemorySize; + const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize; + const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock; + + fprintf(target, "\"%c%c%c%c%c%c%c%c\"]", + vda ? 'd' : '-', + vpa ? 'p' : '-', + vpb ? 'v' : '-', + wait ? '-' : (read ? 'r' : 'w'), + wait ? '-' : (emulation ? 'e' : '-'), + wait ? '-' : (memory_size ? 'm' : '-'), + wait ? '-' : (index_size ? 'x' : '-'), + wait ? '-' : (memory_lock ? 'l' : '-') + ); + } else { + if(read) { + fprintf(target, "\"read\"]"); + } else { + fprintf(target, "\"write\"]"); + } + } } // Terminate object. @@ -285,7 +312,7 @@ void print_ram(FILE *file, const std::unordered_map &data) { } } -@end +} // MARK: - Existing test evaluator. @@ -296,7 +323,7 @@ void print_ram(FILE *file, const std::unordered_map &data) { // A generator for tests; not intended to be a permanent fixture. //- (void)testGenerate { -// [[[TestGenerator alloc] init] generate]; +// generate(); //} @end diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 8ed3ff0f9..15edc54cd 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -124,6 +124,12 @@ class ProcessorBase: public ProcessorStorage { @returns @c true if the 6502 is jammed; @c false otherwise. */ inline bool is_jammed() const; + + /*! + FOR TESTING PURPOSES ONLY: forces the processor into a state where + the next thing it intends to do is fetch a new opcode. + */ + inline void restart_operation_fetch(); }; /*! diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index d07bfe9ee..80b0d7a00 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -728,3 +728,8 @@ void ProcessorBase::set_value_of(Register r, uint16_t value) { default: break; } } + +void ProcessorBase::restart_operation_fetch() { + scheduled_program_counter_ = nullptr; + next_bus_operation_ = BusOperation::None; +} diff --git a/Processors/6502Esque/6502Selector.hpp b/Processors/6502Esque/6502Selector.hpp index 2b75d33d6..31bf93eaa 100644 --- a/Processors/6502Esque/6502Selector.hpp +++ b/Processors/6502Esque/6502Selector.hpp @@ -9,6 +9,7 @@ #ifndef _502Selector_h #define _502Selector_h +#include "6502Esque.hpp" #include "../6502/6502.hpp" #include "../65816/65816.hpp" @@ -45,6 +46,32 @@ template class Processor class BusHandlerT: public BusHandler {}; template <> class BusHandlerT: public BusHandler {}; +/* + Query for implemented registers. +*/ +constexpr bool has(Type processor_type, Register r) { + switch(r) { + case Register::LastOperationAddress: + case Register::ProgramCounter: + case Register::StackPointer: + case Register::Flags: + case Register::A: + case Register::X: + case Register::Y: + return true; + + case Register::EmulationFlag: + case Register::DataBank: + case Register::ProgramBank: + case Register::Direct: + return processor_type == Type::TWDC65816; + } +} + +constexpr bool has_extended_bus_output(Type processor_type) { + return processor_type == Type::TWDC65816; +} + } #endif /* _502Selector_h */