From 57898ed6ddc3eab121d01dc55f8eb56689314506 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 12 Mar 2019 22:46:31 -0400 Subject: [PATCH] This is where my thinking now resides. Two levels of indirection, and consolidated collections. --- .../Implementation/68000Implementation.hpp | 45 +++-- .../68000/Implementation/68000Storage.cpp | 182 +++++++++--------- .../68000/Implementation/68000Storage.hpp | 67 ++++++- 3 files changed, 183 insertions(+), 111 deletions(-) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 9e51b13bc..7e80f16a7 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -9,17 +9,34 @@ template void Processor::run_for(HalfCycles duration) { // TODO: obey the 'cycles' count. while(true) { - // Check whether the program is exhausted. - if(active_program_->action == Step::Action::ScheduleNextProgram) { - std::cerr << "68000 Abilities exhausted" << std::endl; - return; + // Check whether the current list of bus steps is exhausted; if so then + // seek out another one from the current program (if any), and if there + // are no more to do, revert to scheduling something else (after checking + // for interrupts). + if(active_step_->action == BusStep::Action::ScheduleNextProgram) { + if(active_micro_op_) { + ++active_micro_op_; + switch(active_micro_op_->action) { + case MicroOp::Action::None: break; + + case MicroOp::Action::PerformOperation: + std::cerr << "Should do something with program operation " << int(active_program_->operation) << std::endl; + break; + } + active_step_ = active_micro_op_->bus_program; + } + + if(!active_step_) { + std::cerr << "68000 Abilities exhausted; should schedule an instruction or something?" << std::endl; + return; + } } - // The program is not exhausted, so perform the microcycle. + // The bus step list is not exhausted, so perform the microcycle. // Check for DTack if this isn't being treated implicitly. if(!dtack_is_implicit) { - if(active_program_->microcycle.operation & (Microcycle::UpperData | Microcycle::LowerData) && !dtack_) { + if(active_step_->microcycle.operation & (Microcycle::UpperData | Microcycle::LowerData) && !dtack_) { // TODO: perform wait state. continue; } @@ -28,26 +45,26 @@ template void Processor: // TODO: synchronous bus. // Perform the microcycle. - bus_handler_.perform_bus_operation(active_program_->microcycle, is_supervisor_); + bus_handler_.perform_bus_operation(active_step_->microcycle, is_supervisor_); // Perform the post-hoc action. - switch(active_program_->action) { + switch(active_step_->action) { default: - std::cerr << "Unimplemented 68000 action: " << int(active_program_->action) << std::endl; + std::cerr << "Unimplemented 68000 bus step action: " << int(active_step_->action) << std::endl; return; break; - case Step::Action::None: break; + case BusStep::Action::None: break; - case Step::Action::IncrementEffectiveAddress: effective_address_ += 2; break; - case Step::Action::IncrementProgramCounter: program_counter_.full += 2; break; + case BusStep::Action::IncrementEffectiveAddress: effective_address_ += 2; break; + case BusStep::Action::IncrementProgramCounter: program_counter_.full += 2; break; - case Step::Action::AdvancePrefetch: + case BusStep::Action::AdvancePrefetch: prefetch_queue_[0] = prefetch_queue_[1]; break; } // Move to the next program step. - ++active_program_; + ++active_step_; } } diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 9a8db17a0..a90a4b11a 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -8,32 +8,35 @@ #include "../68000.hpp" +#include + using namespace CPU::MC68000; ProcessorStorage::ProcessorStorage() { - // Create the reset program. - reset_program_ = assemble_program("n- n- n- n- n- nn nF nf nV nv np np"); + // Create the exception programs. + const size_t reset_offset = assemble_program("n- n- n- n- n- nn nF nf nV nv np np"); - // TODO: install access patterns. + // Install all necessary access patterns. + const BusStepCollection bus_steps = assemble_standard_bus_steps(); // Install operations. - for(int c = 0; c < 65536; ++c) { - install_instruction(c); - } + install_instructions(bus_steps); + + // Realise the exception programs as direct pointers. + reset_program_ = &all_bus_steps_[reset_offset]; // Set initial state. Largely TODO. - active_program_ = reset_program_.data(); + active_step_ = reset_program_; effective_address_ = 0; is_supervisor_ = 1; } -// TODO: allow actions to be specified, of course. -std::vector ProcessorStorage::assemble_program(const char *access_pattern) { - std::vector steps; +size_t ProcessorStorage::assemble_program(const char *access_pattern) { + const size_t start = all_bus_steps_.size(); // Parse the access pattern to build microcycles. while(*access_pattern) { - Step step; + BusStep step; switch(*access_pattern) { case '\t': case ' ': // White space acts as a no-op; it's for clarity only. @@ -45,13 +48,13 @@ std::vector ProcessorStorage::assemble_program(const cha switch(access_pattern[1]) { default: // This is probably a pure NOP; if what comes after this 'n' isn't actually // valid, it should be caught in the outer switch the next time around the loop. - steps.push_back(step); + all_bus_steps_.push_back(step); ++access_pattern; break; case '-': // This is two NOPs in a row. - steps.push_back(step); - steps.push_back(step); + all_bus_steps_.push_back(step); + all_bus_steps_.push_back(step); access_pattern += 2; break; @@ -61,12 +64,12 @@ std::vector ProcessorStorage::assemble_program(const cha step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess. step.microcycle.address = &effective_address_; step.microcycle.value = isupper(access_pattern[1]) ? &stack_pointers_[1].halves.high : &stack_pointers_[1].halves.low; - steps.push_back(step); + all_bus_steps_.push_back(step); step.microcycle.length = HalfCycles(3); step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; - step.action = Step::Action::IncrementEffectiveAddress; - steps.push_back(step); + step.action = BusStep::Action::IncrementEffectiveAddress; + all_bus_steps_.push_back(step); access_pattern += 2; break; @@ -77,12 +80,12 @@ std::vector ProcessorStorage::assemble_program(const cha step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess. step.microcycle.address = &effective_address_; step.microcycle.value = isupper(access_pattern[1]) ? &program_counter_.halves.high : &program_counter_.halves.low; - steps.push_back(step); + all_bus_steps_.push_back(step); step.microcycle.length = HalfCycles(3); step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; - step.action = Step::Action::IncrementEffectiveAddress; - steps.push_back(step); + step.action = BusStep::Action::IncrementEffectiveAddress; + all_bus_steps_.push_back(step); access_pattern += 2; break; @@ -92,13 +95,13 @@ std::vector ProcessorStorage::assemble_program(const cha step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; step.microcycle.address = &program_counter_.full; step.microcycle.value = &prefetch_queue_[1]; - step.action = Step::Action::AdvancePrefetch; - steps.push_back(step); + step.action = BusStep::Action::AdvancePrefetch; + all_bus_steps_.push_back(step); step.microcycle.length = HalfCycles(3); step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; - step.action = Step::Action::IncrementProgramCounter; - steps.push_back(step); + step.action = BusStep::Action::IncrementProgramCounter; + all_bus_steps_.push_back(step); access_pattern += 2; break; @@ -112,11 +115,20 @@ std::vector ProcessorStorage::assemble_program(const cha } // Add a final 'ScheduleNextProgram' sentinel. - Step end_program; - end_program.action = Step::Action::ScheduleNextProgram; - steps.push_back(end_program); + BusStep end_program; + end_program.action = BusStep::Action::ScheduleNextProgram; + all_bus_steps_.push_back(end_program); - return steps; + return start; +} + +ProcessorStorage::BusStepCollection ProcessorStorage::assemble_standard_bus_steps() { + ProcessorStorage::BusStepCollection collection; + + collection.four_step_Dn = assemble_program("np"); + collection.six_step_Dn = assemble_program("np n"); + + return collection; } /* @@ -131,81 +143,75 @@ std::vector ProcessorStorage::assemble_program(const cha from known instructions to their disassembly rather than vice versa; especially (iii) given that there are plentiful disassemblers against which to test work in progress. */ -void ProcessorStorage::install_instruction(int instruction) { - enum class Operation { - ABCD, ADD, ADDA, ADDI, ADDQ, ADDX, AND, ANDI, - ASL, ASLrmw, ASR, ASRrmw, Bcc, - - TODO +void ProcessorStorage::install_instructions(const BusStepCollection &bus_step_collection) { + enum class Decoder { + Decimal, + RegOpModeReg, + SizeModeRegisterImmediate, + DataSizeModeQuick }; struct PatternMapping { uint16_t mask, value; Operation operation; + Decoder decoder; }; /* - Credit here is due to 'wrm' (https://github.com/wrm-za I assume) for his public - domain 68000 disassembler, from which the table below was largely sourced. - Manual legwork has been extended to check this table against the M68000 - Programmer's Reference Manual, currently available at + Inspired partly by 'wrm' (https://github.com/wrm-za I assume); the following + table draws from the M68000 Programmer's Reference Manual, currently available at https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf + + After each line is the internal page number on which documentation of that + instruction mapping can be found, followed by the page number within the PDF + linked above. + + NB: a vector is used to allow easy iteration. */ const std::vector mappings = { - {0xf1f0, 0xc100, Operation::ABCD}, {0xf000, 0xd000, Operation::ADD}, - {0xf0c0, 0xd0c0, Operation::ADDA}, {0xff00, 0x0600, Operation::ADDI}, - {0xf100, 0x5000, Operation::ADDQ}, {0xf130, 0xd100, Operation::ADDX}, - {0xf000, 0xc000, Operation::AND}, {0xff00, 0x0200, Operation::ANDI}, - {0xf118, 0xe100, Operation::ASL}, {0xffc0, 0xe1c0, Operation::ASLrmw}, - {0xf118, 0xe000, Operation::ASR}, {0xffc0, 0xe0c0, Operation::ASRrmw}, - {0xf000, 0x6000, Operation::Bcc}, {0xf1c0, 0x0140, Operation::TODO}, - {0xffc0, 0x0840, Operation::TODO}, {0xf1c0, 0x0180, Operation::TODO}, - {0xffc0, 0x0880, Operation::TODO}, {0xf1c0, 0x01c0, Operation::TODO}, - {0xffc0, 0x08c0, Operation::TODO}, {0xf1c0, 0x0100, Operation::TODO}, - {0xffc0, 0x0800, Operation::TODO}, {0xf1c0, 0x4180, Operation::TODO}, - {0xff00, 0x4200, Operation::TODO}, {0xf100, 0xb000, Operation::TODO}, - {0xf0c0, 0xb0c0, Operation::TODO}, {0xff00, 0x0c00, Operation::TODO}, - {0xf138, 0xb108, Operation::TODO}, {0xf0f8, 0x50c8, Operation::TODO}, - {0xf1c0, 0x81c0, Operation::TODO}, {0xf1c0, 0x80c0, Operation::TODO}, - {0xf100, 0xb100, Operation::TODO}, {0xff00, 0x0a00, Operation::TODO}, - {0xf100, 0xc100, Operation::TODO}, {0xffb8, 0x4880, Operation::TODO}, - {0xffc0, 0x4ec0, Operation::TODO}, {0xffc0, 0x4e80, Operation::TODO}, - {0xf1c0, 0x41c0, Operation::TODO}, {0xfff8, 0x4e50, Operation::TODO}, - {0xf118, 0xe108, Operation::TODO}, {0xffc0, 0xe3c0, Operation::TODO}, - {0xf118, 0xe008, Operation::TODO}, {0xffc0, 0xe2c0, Operation::TODO}, - {0xc000, 0x0000, Operation::TODO}, {0xffc0, 0x44c0, Operation::TODO}, - {0xffc0, 0x46c0, Operation::TODO}, {0xffc0, 0x40c0, Operation::TODO}, - {0xfff0, 0x4e60, Operation::TODO}, {0xc1c0, 0x0040, Operation::TODO}, - {0xfb80, 0x4880, Operation::TODO}, {0xf138, 0x0108, Operation::TODO}, - {0xf100, 0x7000, Operation::TODO}, {0xf1c0, 0xc1c0, Operation::TODO}, - {0xf1c0, 0xc0c0, Operation::TODO}, {0xffc0, 0x4800, Operation::TODO}, - {0xff00, 0x4400, Operation::TODO}, {0xff00, 0x4000, Operation::TODO}, - {0xffff, 0x4e71, Operation::TODO}, {0xff00, 0x4600, Operation::TODO}, - {0xf000, 0x8000, Operation::TODO}, {0xff00, 0x0000, Operation::TODO}, - {0xffc0, 0x4840, Operation::TODO}, {0xffff, 0x4e70, Operation::TODO}, - {0xf118, 0xe118, Operation::TODO}, {0xffc0, 0xe7c0, Operation::TODO}, - {0xf118, 0xe018, Operation::TODO}, {0xffc0, 0xe6c0, Operation::TODO}, - {0xf118, 0xe110, Operation::TODO}, {0xffc0, 0xe5c0, Operation::TODO}, - {0xf118, 0xe010, Operation::TODO}, {0xffc0, 0xe4c0, Operation::TODO}, - {0xffff, 0x4e73, Operation::TODO}, {0xffff, 0x4e77, Operation::TODO}, - {0xffff, 0x4e75, Operation::TODO}, {0xf1f0, 0x8100, Operation::TODO}, - {0xf0c0, 0x50c0, Operation::TODO}, {0xffff, 0x4e72, Operation::TODO}, - {0xf000, 0x9000, Operation::TODO}, {0xf0c0, 0x90c0, Operation::TODO}, - {0xff00, 0x0400, Operation::TODO}, {0xf100, 0x5100, Operation::TODO}, - {0xf130, 0x9100, Operation::TODO}, {0xfff8, 0x4840, Operation::TODO}, - {0xffc0, 0x4ac0, Operation::TODO}, {0xfff0, 0x4e40, Operation::TODO}, - {0xffff, 0x4e76, Operation::TODO}, {0xff00, 0x4a00, Operation::TODO}, - {0xfff8, 0x4e58, Operation::TODO} + {0xf1f0, 0x8100, Operation::SBCD, Decoder::Decimal}, // 4-171 (p275) + {0xf1f0, 0xc100, Operation::ABCD, Decoder::Decimal}, // 4-3 (p107) + + {0xf000, 0x8000, Operation::OR, Decoder::RegOpModeReg}, // 4-150 (p226) + {0xf000, 0x9000, Operation::SUB, Decoder::RegOpModeReg}, // 4-174 (p278) + {0xf000, 0xb000, Operation::EOR, Decoder::RegOpModeReg}, // 4-100 (p204) + {0xf000, 0xc000, Operation::AND, Decoder::RegOpModeReg}, // 4-15 (p119) + {0xf000, 0xd000, Operation::ADD, Decoder::RegOpModeReg}, // 4-4 (p108) + + {0xff00, 0x0600, Operation::ADD, Decoder::SizeModeRegisterImmediate}, // 4-9 (p113) + + {0xff00, 0x0600, Operation::ADD, Decoder::DataSizeModeQuick}, // 4-11 (p115) }; // Perform a linear search of the mappings above for this instruction. - for(const auto &mapping: mappings) { - if((instruction & mapping.mask) == mapping.value) { - if(mapping.operation == Operation::TODO) { - std::cout << std::hex << std::setw(4) << std::setfill('0'); - std::cout << "Yet to implement, instruction matching: x & " << mapping.mask << " == " << mapping.value << std::endl; + for(int instruction = 0; instruction < 65536; ++instruction) { + for(const auto &mapping: mappings) { + if((instruction & mapping.mask) == mapping.value) { + switch(mapping.decoder) { + case Decoder::Decimal: { + const int destination = (instruction >> 8) & 7; + const int source = instruction & 7; + if(instruction & 8) { + std::cout << "Address to address (both predecrement) from " << source << " to " << destination << std::endl; + } else { + instructions[instruction].operation = mapping.operation; + instructions[instruction].source = &data_[source]; + instructions[instruction].destination = &data_[destination]; +// instructions[instruction].destination.micro_operations = + std::cout << "Data register to data register from " << source << " to " << destination << std::endl; + } + } break; + + case Decoder::RegOpModeReg: { + } break; + + default: + std::cerr << "Unhandled decoder " << int(mapping.decoder) << std::endl; + break; + } + + break; } - break; } } } diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index d7829c36e..580a14de4 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -29,10 +29,15 @@ class ProcessorStorage { uint32_t effective_address_; RegisterPair32 bus_data_; + enum class Operation { + ABCD, SBCD, + ADD, AND, EOR, OR, SUB, + }; + /*! - A step is a microcycle to perform plus an action to occur afterwards, if any. + Bus steps are sequences of things to communicate to the bus. */ - struct Step { + struct BusStep { Microcycle microcycle; enum class Action { None, @@ -56,15 +61,53 @@ class ProcessorStorage { } action = Action::None; }; - // Special programs. - std::vector reset_program_; + /*! + A micro-op is: (i) an action to take; and (ii) a sequence of bus operations + to perform after taking the action. - // Current program pointer. - Step *active_program_ = nullptr; + A nullptr bus_program terminates a sequence of micro operations. + */ + struct MicroOp { + enum class Action { + None, + PerformOperation + } action = Action::None; + BusStep *bus_program = nullptr; + }; + + /*! + A program represents the implementation of a particular opcode, as a sequence + of micro-ops and, separately, the operation to perform plus whatever other + fields the operation requires. + */ + struct Program { + MicroOp *micro_operations = nullptr; + Operation operation; + RegisterPair32 *source; + RegisterPair32 *destination; + }; + + // Storage for all the sequences of bus steps and micro-ops used throughout + // the 68000. + std::vector all_bus_steps_; + std::vector all_micro_ops_; + + // A lookup table from instructions to implementations. + Program instructions[65536]; + + // Special programs, for exception handlers. + BusStep *reset_program_; + + // Current bus step pointer, and outer program pointer. + Program *active_program_ = nullptr; + MicroOp *active_micro_op_ = nullptr; + BusStep *active_step_ = nullptr; private: /*! - Produces a vector of Steps that implement the described program. + Installs BusSteps that implement the described program into the relevant + instance storage, returning the offset within @c all_bus_steps_ at which + the generated steps begin. @param access_pattern A string describing the bus activity that occurs during this program. This should follow the same general pattern as @@ -103,13 +146,19 @@ class ProcessorStorage { The user should fill in the steps necessary to get data into or extract data from those. */ - std::vector assemble_program(const char *access_pattern); + size_t assemble_program(const char *access_pattern); + + struct BusStepCollection { + size_t six_step_Dn; + size_t four_step_Dn; + }; + BusStepCollection assemble_standard_bus_steps(); /*! Disassembles the instruction @c instruction and inserts it into the appropriate lookup tables. */ - void install_instruction(int instruction); + void install_instructions(const BusStepCollection &); }; #endif /* MC68000Storage_h */