From 22c792dc46956b5d84e3e6492cede6e0764024db Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 25 Sep 2020 17:18:25 -0400 Subject: [PATCH] Adds enough logic to start serialising instructions to somewhere. Possibly extraneous for now, but it means I can start stepping and testing. --- .../65816/Implementation/65816Storage.cpp | 106 +++++++++++++----- .../65816/Implementation/65816Storage.hpp | 54 ++++----- 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index 163b0bb76..1e2506f7c 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -7,10 +7,83 @@ // #include "../65816.hpp" +#include using namespace CPU::WDC65816; struct CPU::WDC65816::ProcessorStorageConstructor { + // Establish that a storage constructor needs access to ProcessorStorage. + ProcessorStorage &storage_; + ProcessorStorageConstructor(ProcessorStorage &storage) : storage_(storage) {} + + enum class AccessType { + Read, Write + }; + + constexpr AccessType access_type_for_operation(Operation operation) { + switch(operation) { + case ADC: case AND: case BIT: case CMP: + case CPX: case CPY: case EOR: case ORA: + case SBC: + + case LDA: case LDX: case LDY: + + case JMP: case JSR: + return AccessType::Read; + + case STA: case STX: case STY: case STZ: + return AccessType::Write; + } + } + + typedef void (* Generator)(AccessType, bool, const std::function&); + using GeneratorKey = std::tuple; + std::map> installed_patterns; + + uint8_t opcode = 0; + void install(Generator generator, Operation operation) { + // Determine the access type implied by this operation. + const AccessType access_type = access_type_for_operation(operation); + + // Check whether this access type + addressing mode generator has already been generated. + const auto key = std::make_pair(access_type, generator); + const auto map_entry = installed_patterns.find(key); + size_t micro_op_location_8, micro_op_location_16; + + // If it wasn't found, generate it now in both 8- and 16-bit variants. + // Otherwise, get the location of the existing entries. + if(map_entry == installed_patterns.end()) { + // Generate 8-bit steps. + micro_op_location_8 = storage_.micro_ops_.size(); + (*generator)(access_type, true, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Generate 16-bit steps. + micro_op_location_16 = storage_.micro_ops_.size(); + (*generator)(access_type, false, [this] (MicroOp op) { + this->storage_.micro_ops_.push_back(op); + }); + storage_.micro_ops_.push_back(OperationMoveToNextProgram); + + // Insert into the map. + installed_patterns[key] = std::make_pair(micro_op_location_8, micro_op_location_16); + } else { + micro_op_location_8 = map_entry->second.first; + micro_op_location_16 = map_entry->second.second; + } + + // Fill in the proper table entries and increment the opcode pointer. + storage_.instructions[opcode].program_offset = micro_op_location_8; + storage_.instructions[opcode].operation = operation; + + storage_.instructions[opcode + 256].program_offset = micro_op_location_16; + storage_.instructions[opcode + 256].operation = operation; + + ++opcode; + } + /* Code below is structured to ease translation from Table 5-7 of the 2018 edition of the WDC 65816 datasheet. @@ -112,41 +185,12 @@ struct CPU::WDC65816::ProcessorStorageConstructor { } }; -AccessType ProcessorStorage::access_type_for_operation(Operation operation) { - switch(operation) { - case ADC: case AND: case BIT: case CMP: - case CPX: case CPY: case EOR: case ORA: - case SBC: - - case LDA: case LDX: case LDY: - - case JMP: case JSR: - return AccessType::Read; - - case STA: case STX: case STY: case STZ: - return AccessType::Write; - } -} - -void ProcessorStorage::install(void (* generator)(AccessType, bool, const std::function&), Operation operation, ProcessorStorageConstructor &) { - const AccessType access_type = access_type_for_operation(operation); - (*generator)(access_type, true, [] (MicroOp) {}); - - // TODO: - // (1) use [hash of] pointer to generator to determine whether operation tables have already been built; - // (2) if not, build them in both 8- and 16-bit variations; - // (3) insert appropriate entries into an instruction table for the current instruction; and - // (4) increment the instruction counter. - // - // Additional observation: temporary storage would be helpful, so load that into ProcessorStorageConstructor. -} - ProcessorStorage::ProcessorStorage() { - ProcessorStorageConstructor constructor; + ProcessorStorageConstructor constructor(*this); // Install the instructions. -#define op(x, y) install(&ProcessorStorageConstructor::x, y, constructor) +#define op(x, y) constructor.install(&ProcessorStorageConstructor::x, y) /* 0x00 BRK s */ /* 0x01 ORA (d, x) */ diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index e69005239..d1f3dcf96 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -45,8 +45,25 @@ enum MicroOp: uint8_t { OperationMoveToNextProgram }; -enum class AccessType { - Read, Write +enum Operation: uint8_t { + // These perform the named operation using the value in the data buffer; + // they are implicitly AccessType::Read. + ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, + + // These load the respective register from the data buffer; + // they are implicitly AccessType::Read. + LDA, LDX, LDY, + + // These move the respective register (or value) to the data buffer; + // they are implicitly AccessType::Write. + STA, STX, STY, STZ, + + /// Loads the PC with the operand from the data buffer. + JMP, + + /// Loads the PC with the operand from the daa buffer, replacing + /// it with the old PC. + JSR, }; class ProcessorStorageConstructor; @@ -55,38 +72,21 @@ class ProcessorStorage { public: ProcessorStorage(); - enum Operation: uint8_t { - // These perform the named operation using the value in the data buffer; - // they are implicitly AccessType::Read. - ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, - - // These load the respective register from the data buffer; - // they are implicitly AccessType::Read. - LDA, LDX, LDY, - - // These move the respective register (or value) to the data buffer; - // they are implicitly AccessType::Write. - STA, STX, STY, STZ, - - /// Loads the PC with the operand from the data buffer. - JMP, - - /// Loads the PC with the operand from the daa buffer, replacing - /// it with the old PC. - JSR, - }; - struct Instruction { size_t program_offset; Operation operation; }; - Instruction instructions[256]; + Instruction instructions[512 + 3]; // Arranged as: + // 256 entries: emulation-mode instructions; + // 256 entries: 16-bit instructions; + // reset + // NMI + // IRQ private: - std::vector micro_ops_; + friend ProcessorStorageConstructor; - AccessType access_type_for_operation(Operation); - void install(void (* generator)(AccessType, bool, const std::function&), Operation, ProcessorStorageConstructor &); + std::vector micro_ops_; }; #endif /* WDC65816Implementation_h */