From a3d4c7599b81e85b35e98d588a359dc5a533d594 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 18 May 2020 23:55:54 -0400 Subject: [PATCH] Attempts fully to capture 68000 state. Albeit that it can't be put back yet. --- .../Implementation/68000Implementation.hpp | 60 ++++---- .../68000/Implementation/68000Storage.cpp | 18 +-- .../68000/Implementation/68000Storage.hpp | 36 ++--- Processors/68000/State/State.cpp | 137 +++++++++++++++++- Processors/68000/State/State.hpp | 43 ++++++ 5 files changed, 232 insertions(+), 62 deletions(-) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 2c41c4f83..c077afe0a 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -210,7 +210,7 @@ template void Proces if(bus_interrupt_level_ > interrupt_level_) { pending_interrupt_level_ = bus_interrupt_level_; program_counter_.full += 4; // Don't return to this stop. - execution_state_ = ExecutionState::BeginInterrupt; + execution_state_ = ExecutionState::WillBeginInterrupt; continue; } @@ -244,7 +244,7 @@ template void Proces bus_handler_.perform_bus_operation(stop_cycle_, is_supervisor_); continue; - case ExecutionState::BeginInterrupt: + case ExecutionState::WillBeginInterrupt: #ifdef LOG_TRACE // should_log = true; #endif @@ -269,7 +269,7 @@ template void Proces // no instruction was ongoing. Either way, do a standard instruction operation. if(pending_interrupt_level_) { - execution_state_ = ExecutionState::BeginInterrupt; + execution_state_ = ExecutionState::WillBeginInterrupt; break; } @@ -302,7 +302,7 @@ template void Proces #ifndef NDEBUG /* Debugging feature: reset the effective addresses and data latches, so that it's more obvious if some of the instructions aren't properly feeding them. */ - effective_address_[0].full = effective_address_[1].full = source_bus_data_[0].full = destination_bus_data_[0].full = 0x12344321; + effective_address_[0].full = effective_address_[1].full = source_bus_data_.full = destination_bus_data_.full = 0x12344321; #endif #ifdef LOG_TRACE @@ -829,7 +829,7 @@ template void Proces // JMP: copies the source bus data to the program counter. case Operation::RTS: - program_counter_ = source_bus_data_[0]; + program_counter_ = source_bus_data_; break; /* @@ -879,7 +879,7 @@ template void Proces break; case Operation::PEA: - destination_bus_data_[0] = effective_address_[0]; + destination_bus_data_ = effective_address_[0]; break; /* @@ -1119,30 +1119,30 @@ template void Proces case Operation::MOVEPtoMw: // Write pattern is nW+ nw, which should write the low word of the source in big-endian form. - destination_bus_data_[0].halves.high.full = source()->halves.low.halves.high; - destination_bus_data_[0].halves.low.full = source()->halves.low.halves.low; + destination_bus_data_.halves.high.full = source()->halves.low.halves.high; + destination_bus_data_.halves.low.full = source()->halves.low.halves.low; break; case Operation::MOVEPtoMl: // Write pattern is nW+ nWr+ nw+ nwr, which should write the source in big-endian form. - destination_bus_data_[0].halves.high.full = source()->halves.high.halves.high; - source_bus_data_[0].halves.high.full = source()->halves.high.halves.low; - destination_bus_data_[0].halves.low.full = source()->halves.low.halves.high; - source_bus_data_[0].halves.low.full = source()->halves.low.halves.low; + destination_bus_data_.halves.high.full = source()->halves.high.halves.high; + source_bus_data_.halves.high.full = source()->halves.high.halves.low; + destination_bus_data_.halves.low.full = source()->halves.low.halves.high; + source_bus_data_.halves.low.full = source()->halves.low.halves.low; break; case Operation::MOVEPtoRw: // Read pattern is nRd+ nrd. - source()->halves.low.halves.high = destination_bus_data_[0].halves.high.halves.low; - source()->halves.low.halves.low = destination_bus_data_[0].halves.low.halves.low; + source()->halves.low.halves.high = destination_bus_data_.halves.high.halves.low; + source()->halves.low.halves.low = destination_bus_data_.halves.low.halves.low; break; case Operation::MOVEPtoRl: // Read pattern is nRd+ nR+ nrd+ nr. - source()->halves.high.halves.high = destination_bus_data_[0].halves.high.halves.low; - source()->halves.high.halves.low = source_bus_data_[0].halves.high.halves.low; - source()->halves.low.halves.high = destination_bus_data_[0].halves.low.halves.low; - source()->halves.low.halves.low = source_bus_data_[0].halves.low.halves.low; + source()->halves.high.halves.high = destination_bus_data_.halves.high.halves.low; + source()->halves.high.halves.low = source_bus_data_.halves.high.halves.low; + source()->halves.low.halves.high = destination_bus_data_.halves.low.halves.low; + source()->halves.low.halves.low = source_bus_data_.halves.low.halves.low; break; /* @@ -1448,7 +1448,7 @@ template void Proces effective_address_[1].full = address_[7].full; // The current value of the address register will be pushed. - destination_bus_data_[0].full = source()->full; + destination_bus_data_.full = source()->full; // The address register will then contain the bottom of the stack, // and the stack pointer will be offset. @@ -1458,7 +1458,7 @@ template void Proces case Operation::UNLINK: address_[7].full = effective_address_[1].full + 2; - destination()->full = destination_bus_data_[0].full; + destination()->full = destination_bus_data_.full; break; /* @@ -1859,10 +1859,10 @@ template void Proces // If this is RTR, patch out the supervisor half of the status register. if(decoded_instruction_.full == 0x4e77) { const auto current_status = status(); - source_bus_data_[0].halves.low.halves.high = + source_bus_data_.halves.low.halves.high = uint8_t(current_status >> 8); } - apply_status(source_bus_data_[0].full); + apply_status(source_bus_data_.full); break; /* @@ -1938,9 +1938,9 @@ template void Proces const auto mode = (decoded_instruction_.full >> 3) & 7; // Determine the proper resumption address. switch(mode) { - case 2: destination_bus_data_[0].full = program_counter_.full - 2; break; /* (An) */ + case 2: destination_bus_data_.full = program_counter_.full - 2; break; /* (An) */ default: - destination_bus_data_[0].full = program_counter_.full; /* Everything other than (An) */ + destination_bus_data_.full = program_counter_.full; /* Everything other than (An) */ break; } address_[7].full -= 4; @@ -1948,7 +1948,7 @@ template void Proces } break; case int_type(MicroOp::Action::PrepareBSR): - destination_bus_data_[0].full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full; + destination_bus_data_.full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full; address_[7].full -= 4; effective_address_[1].full = address_[7].full; break; @@ -2002,7 +2002,7 @@ template void Proces } // Otherwise, the vector is whatever we were just told it is. - effective_address_[0].full = uint32_t(source_bus_data_[0].halves.low.halves.low << 2); + effective_address_[0].full = uint32_t(source_bus_data_.halves.low.halves.low << 2); // printf("Interrupt vector: %06x\n", effective_address_[0].full); break; @@ -2143,19 +2143,19 @@ template void Proces break; case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask: - source_bus_data_[0] = prefetch_queue_.halves.low.full; + source_bus_data_ = prefetch_queue_.halves.low.full; break; case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::DestinationMask: - destination_bus_data_[0] = prefetch_queue_.halves.low.full; + destination_bus_data_ = prefetch_queue_.halves.low.full; break; case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask: - source_bus_data_[0] = prefetch_queue_.full; + source_bus_data_ = prefetch_queue_.full; break; case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::DestinationMask: - destination_bus_data_[0] = prefetch_queue_.full; + destination_bus_data_ = prefetch_queue_.full; break; case int_type(MicroOp::Action::CopyToEffectiveAddress) | MicroOp::SourceMask: diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 00532fd84..a082b99af 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -300,7 +300,7 @@ struct ProcessorStorageConstructor { if(tolower(access_pattern[1]) == 's') { step.microcycle.operation = Microcycle::NewAddress; step.microcycle.address = &storage_.effective_address_[1].full; - step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_[0].halves.high : &storage_.destination_bus_data_[0].halves.low; + step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_.halves.high : &storage_.destination_bus_data_.halves.low; steps.push_back(step); step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord; @@ -312,7 +312,7 @@ struct ProcessorStorageConstructor { // A stack read. if(tolower(access_pattern[1]) == 'u') { - RegisterPair32 *const scratch_data = &storage_.source_bus_data_[0]; + RegisterPair32 *const scratch_data = &storage_.source_bus_data_; step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read; step.microcycle.address = &storage_.effective_address_[0].full; @@ -350,7 +350,7 @@ struct ProcessorStorageConstructor { ) { const bool is_read = tolower(access_pattern[1]) == 'r'; const bool use_source_storage = tolower(end_of_pattern[-1]) == 'r'; - RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_[0] : &storage_.destination_bus_data_[0]; + RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_ : &storage_.destination_bus_data_; assert(address_iterator != addresses.end()); @@ -377,7 +377,7 @@ struct ProcessorStorageConstructor { if(token_length == 3) { // The completing part of a TAS. if(access_pattern[0] == 't' && access_pattern[1] == 'a' && access_pattern[2] == 's') { - RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0]; + RegisterPair32 *const scratch_data = &storage_.destination_bus_data_; assert(address_iterator != addresses.end()); @@ -399,7 +399,7 @@ struct ProcessorStorageConstructor { if(access_pattern[0] == 'i' && access_pattern[1] == 'n' && access_pattern[2] == 't') { step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::NewAddress; step.microcycle.address = &storage_.effective_address_[0].full; // The selected interrupt should be in bits 1–3; but 0 should be set. - step.microcycle.value = &storage_.source_bus_data_[0].halves.low; + step.microcycle.value = &storage_.source_bus_data_.halves.low; steps.push_back(step); step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte; @@ -3250,20 +3250,20 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { // // Order of output is: PC.l, SR, PC.h. trap_steps_ = &all_bus_steps_[trap_offset]; - constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high }); + constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high }); // Fill in the same order of writes for the interrupt micro-ops, though it divides the work differently. - constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high }); + constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high }); // Link the bus error exception steps and fill in the proper sources. bus_error_steps_ = &all_bus_steps_[bus_error_offset]; constructor.replace_write_values(bus_error_steps_, { &program_counter_.halves.low, - &destination_bus_data_[0].halves.low, + &destination_bus_data_.halves.low, &program_counter_.halves.high, &decoded_instruction_, &effective_address_[1].halves.low, - &destination_bus_data_[0].halves.high, + &destination_bus_data_.halves.high, &effective_address_[1].halves.high }); diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 7e2ed16e8..a0de08b93 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -38,7 +38,7 @@ class ProcessorStorage { Halted, /// Signals a transition from some other straight directly to cueing up an interrupt. - BeginInterrupt, + WillBeginInterrupt, } execution_state_ = ExecutionState::Executing; Microcycle dtack_cycle_; Microcycle stop_cycle_; @@ -75,8 +75,8 @@ class ProcessorStorage { // Generic sources and targets for memory operations; // by convention: [0] = source, [1] = destination. RegisterPair32 effective_address_[2]; - RegisterPair32 source_bus_data_[1]; - RegisterPair32 destination_bus_data_[1]; + RegisterPair32 source_bus_data_; + RegisterPair32 destination_bus_data_; HalfCycles half_cycles_left_to_run_; HalfCycles e_clock_phase_; @@ -394,18 +394,18 @@ class ProcessorStorage { void set_source(ProcessorStorage &storage, int mode, int reg) { set_source_address(storage, reg); switch(mode) { - case 0: set_source(storage, &storage.data_[reg]); break; - case 1: set_source(storage, &storage.address_[reg]); break; - default: set_source(storage, &storage.source_bus_data_[0]); break; + case 0: set_source(storage, &storage.data_[reg]); break; + case 1: set_source(storage, &storage.address_[reg]); break; + default: set_source(storage, &storage.source_bus_data_); break; } } void set_destination(ProcessorStorage &storage, int mode, int reg) { set_destination_address(storage, reg); switch(mode) { - case 0: set_destination(storage, &storage.data_[reg]); break; - case 1: set_destination(storage, &storage.address_[reg]); break; - default: set_destination(storage, &storage.destination_bus_data_[0]); break; + case 0: set_destination(storage, &storage.data_[reg]); break; + case 1: set_destination(storage, &storage.address_[reg]); break; + default: set_destination(storage, &storage.destination_bus_data_); break; } } }; @@ -438,13 +438,15 @@ class ProcessorStorage { BusStep *movem_read_steps_; BusStep *movem_write_steps_; + // These two are dynamically modified depending on the particular + // TRAP and bus error. BusStep *trap_steps_; BusStep *bus_error_steps_; // Current bus step pointer, and outer program pointer. - Program *active_program_ = nullptr; - MicroOp *active_micro_op_ = nullptr; - BusStep *active_step_ = nullptr; + const Program *active_program_ = nullptr; + const MicroOp *active_micro_op_ = nullptr; + const BusStep *active_step_ = nullptr; RegisterPair16 decoded_instruction_ = 0; uint16_t next_word_ = 0; @@ -455,9 +457,9 @@ class ProcessorStorage { void set_is_supervisor(bool); // Transient storage for MOVEM, TRAP and others. - uint32_t precomputed_addresses_[65]; RegisterPair16 throwaway_value_; uint32_t movem_final_address_; + uint32_t precomputed_addresses_[65]; // This is a big chunk of rarely-used storage. It's placed last deliberately. /*! Evaluates the conditional described by @c code and returns @c true or @c false to @@ -496,7 +498,7 @@ class ProcessorStorage { */ forceinline void populate_trap_steps(uint32_t vector, uint16_t status) { // Fill in the status word value. - destination_bus_data_[0].full = status; + destination_bus_data_.full = status; // Switch to supervisor mode, disable the trace bit. set_is_supervisor(true); @@ -507,7 +509,7 @@ class ProcessorStorage { // Schedule the proper stack activity. precomputed_addresses_[0] = address_[7].full - 2; // PC.l - precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_[0]) + precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_) precomputed_addresses_[2] = address_[7].full - 4; // PC.h address_[7].full -= 6; @@ -517,8 +519,8 @@ class ProcessorStorage { forceinline void populate_bus_error_steps(uint32_t vector, uint16_t status, uint16_t bus_status, RegisterPair32 faulting_address) { // Fill in the status word value. - destination_bus_data_[0].halves.low.full = status; - destination_bus_data_[0].halves.high.full = bus_status; + destination_bus_data_.halves.low.full = status; + destination_bus_data_.halves.high.full = bus_status; effective_address_[1] = faulting_address; // Switch to supervisor mode, disable the trace bit. diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index 0acaca89c..1ae4fe2db 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -33,22 +33,133 @@ State::State(const ProcessorBase &src): State() { inputs.bus_grant = false; // TODO (within the 68000). inputs.halt = src.halt_; - // TODO: - // execution_state_ - // active_[program_/micro_op_/step_] - // Execution state. execution_state.e_clock_phase = src.e_clock_phase_.as(); execution_state.effective_address[0] = src.effective_address_[0].full; execution_state.effective_address[1] = src.effective_address_[1].full; - execution_state.source_data = src.source_bus_data_[0].full; - execution_state.destination_data = src.destination_bus_data_[0].full; + execution_state.source_data = src.source_bus_data_.full; + execution_state.destination_data = src.destination_bus_data_.full; execution_state.last_trace_flag = src.last_trace_flag_; execution_state.next_word = src.next_word_; execution_state.dbcc_false_address = src.dbcc_false_address_; execution_state.is_starting_interrupt = src.is_starting_interrupt_; execution_state.pending_interrupt_level = uint8_t(src.pending_interrupt_level_); execution_state.accepted_interrupt_level = uint8_t(src.accepted_interrupt_level_); + execution_state.movem_final_address = src.movem_final_address_; + + static_assert(sizeof(execution_state.source_addresses) == sizeof(src.precomputed_addresses_)); + memcpy(&execution_state.source_addresses, &src.precomputed_addresses_, sizeof(src.precomputed_addresses_)); + + // This is collapsed to a Boolean; if there is an active program then it's the + // one implied by the current instruction. + execution_state.active_program = src.active_program_; + + // Slightly dodgy assumption here: the Phase enum will always exactly track + // the 68000's ExecutionState enum. + execution_state.phase = ExecutionState::Phase(src.execution_state_); + + auto contained_by = [](const auto *source, const auto *reference) -> bool { + while(true) { + if(source == reference) return true; + if(source->is_terminal()) return false; + ++source; + } + }; + + // Store enough information to relocate the MicroOp. + const ProcessorBase::MicroOp *micro_op_base = nullptr; + if(src.active_program_) { + micro_op_base = &src.all_micro_ops_[src.instructions[src.decoded_instruction_.full].micro_operations]; + assert(contained_by(micro_op_base, src.active_micro_op_)); + execution_state.micro_op_source = ExecutionState::MicroOpSource::ActiveProgram; + } else { + if(contained_by(src.long_exception_micro_ops_, src.active_micro_op_)) { + execution_state.micro_op_source = ExecutionState::MicroOpSource::LongException; + micro_op_base = src.long_exception_micro_ops_; + } else if(contained_by(src.short_exception_micro_ops_, src.active_micro_op_)) { + execution_state.micro_op_source = ExecutionState::MicroOpSource::ShortException; + micro_op_base = src.short_exception_micro_ops_; + } else if(contained_by(src.interrupt_micro_ops_, src.active_micro_op_)) { + execution_state.micro_op_source = ExecutionState::MicroOpSource::Interrupt; + micro_op_base = src.interrupt_micro_ops_; + } else { + assert(false); + } + } + execution_state.micro_op = uint8_t(src.active_micro_op_ - micro_op_base); + + // Encode the BusStep. + struct BusStepOption { + const ProcessorBase::BusStep *const base; + const ExecutionState::BusStepSource source; + }; + BusStepOption bus_step_options[] = { + { + src.reset_bus_steps_, + ExecutionState::BusStepSource::Reset + }, + { + src.branch_taken_bus_steps_, + ExecutionState::BusStepSource::BranchTaken + }, + { + src.branch_byte_not_taken_bus_steps_, + ExecutionState::BusStepSource::BranchByteNotTaken + }, + { + src.branch_word_not_taken_bus_steps_, + ExecutionState::BusStepSource::BranchWordNotTaken + }, + { + src.bsr_bus_steps_, + ExecutionState::BusStepSource::BSR + }, + { + src.dbcc_condition_true_steps_, + ExecutionState::BusStepSource::DBccConditionTrue + }, + { + src.dbcc_condition_false_no_branch_steps_, + ExecutionState::BusStepSource::DBccConditionFalseNoBranch + }, + { + src.dbcc_condition_false_branch_steps_, + ExecutionState::BusStepSource::DBccConditionFalseBranch + }, + { + src.movem_read_steps_, + ExecutionState::BusStepSource::MovemRead + }, + { + src.movem_write_steps_, + ExecutionState::BusStepSource::MovemWrite + }, + { + src.trap_steps_, + ExecutionState::BusStepSource::Trap + }, + { + src.bus_error_steps_, + ExecutionState::BusStepSource::BusError + }, + { + &src.all_bus_steps_[src.active_micro_op_->bus_program], + ExecutionState::BusStepSource::FollowMicroOp + }, + {nullptr} + }; + const BusStepOption *bus_step_option = bus_step_options; + const ProcessorBase::BusStep *bus_step_base = nullptr; + while(bus_step_option->base) { + if(contained_by(bus_step_option->base, src.active_step_)) { + bus_step_base = bus_step_option->base; + execution_state.bus_step_source = bus_step_option->source; + break; + } + ++bus_step_option; + } + assert(bus_step_base); + execution_state.bus_step = uint8_t(src.active_step_ - bus_step_base); } void State::apply(ProcessorBase &target) { @@ -100,5 +211,19 @@ State::ExecutionState::ExecutionState() { DeclareField(is_starting_interrupt); DeclareField(pending_interrupt_level); DeclareField(accepted_interrupt_level); + DeclareField(active_program); + DeclareField(movem_final_address); + DeclareField(source_addresses); + + AnnounceEnum(Phase); + DeclareField(phase); + + AnnounceEnum(MicroOpSource); + DeclareField(micro_op_source); + DeclareField(micro_op); + + AnnounceEnum(BusStepSource); + DeclareField(bus_step_source); + DeclareField(bus_step); } } diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index 579bfafb5..f15337e86 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -72,6 +72,49 @@ struct State: public Reflection::StructImpl { uint8_t pending_interrupt_level; uint8_t accepted_interrupt_level; + // This is a reflective do-over of the ExecutionState enum within + // MC68000Storage; I've yet to decide how happy I am with that + // as an approach. + ReflectableEnum(Phase, + Executing, + WaitingForDTack, + Stopped, + Halted, + WillBeginInterrupt + ); + Phase phase; + + bool active_program; + uint32_t movem_final_address; + uint32_t source_addresses[65]; + + ReflectableEnum(MicroOpSource, + ActiveProgram, + LongException, + ShortException, + Interrupt + ); + MicroOpSource micro_op_source; + uint8_t micro_op; + + ReflectableEnum(BusStepSource, + FollowMicroOp, + BusError, + Trap, + Reset, + BranchTaken, + BranchByteNotTaken, + BranchWordNotTaken, + BSR, + DBccConditionTrue, + DBccConditionFalseNoBranch, + DBccConditionFalseBranch, + MovemRead, + MovemWrite, + ); + BusStepSource bus_step_source; + uint8_t bus_step; + ExecutionState(); } execution_state;