diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp index 2285ed38b..faca0b082 100644 --- a/Processors/68000/68000.hpp +++ b/Processors/68000/68000.hpp @@ -52,8 +52,6 @@ struct Microcycle { */ static const int TypeMask = 3; - /// A NewAddress cycle is one in which the address strobe is initially low but becomes high; - /// this correlates to states 0 to 5 of a standard read/write cycle. static const int Idle = 0; /// A NewAddress cycle is one in which the address strobe is initially low but becomes high; diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index ba759f3ef..354a1e471 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -36,29 +36,79 @@ trace_flag_ = (x) & 0x8000; \ set_is_supervisor(!!(((x) >> 13) & 1)); +#define get_bus_code() \ + ((active_step_->microcycle.operation & Microcycle::IsProgram) ? 0x02 : 0x01) | \ + (is_supervisor_ << 2) | \ + (active_program_ ? 0x08 : 0) | \ + ((active_step_->microcycle.operation & Microcycle::Read) ? 0x10 : 0) + template void Processor::run_for(HalfCycles duration) { HalfCycles remaining_duration = duration + half_cycles_left_to_run_; while(remaining_duration > HalfCycles(0)) { /* PERFORM THE CURRENT BUS STEP'S MICROCYCLE. */ - // Check for DTack if this isn't being treated implicitly. - if(active_step_->microcycle.data_select_active()) { - if(!dtack_is_implicit) { - if(active_step_->microcycle.data_select_active() && !dtack_) { - // TODO: perform wait state. - continue; - } - } + switch(execution_state_) { + default: + break; - // TODO: synchronous bus. - } else { - // TODO: check for bus error (but here, or when checking for DTACK?) -// if(active_step_->microcycle.operation & MicroCycle::NewAddress) { -// } + case ExecutionState::Stopped: + // If an interrupt (TODO: or reset) has finally arrived that will be serviced, + // exit the STOP. + if(bus_interrupt_level_ >= interrupt_level_) { + execution_state_ = ExecutionState::Executing; + break; + } + + // Otherwise continue being stopped. + remaining_duration -= + stop_cycle_.length + + bus_handler_.perform_bus_operation(stop_cycle_, is_supervisor_); + continue; + + case ExecutionState::WaitingForDTack: + // If DTack or bus error has been signalled, stop waiting. + if(dtack_ || bus_error_) { + execution_state_ = ExecutionState::Executing; + break; + } + + // Otherwise, signal another cycle of wait. + remaining_duration -= + dtack_cycle_.length + + bus_handler_.perform_bus_operation(dtack_cycle_, is_supervisor_); + continue; } - // TODO: obey is_stopped_. + if(active_step_->microcycle.data_select_active()) { + if(!dtack_is_implicit && !dtack_ && !bus_error_) { + execution_state_ = ExecutionState::WaitingForDTack; + dtack_cycle_ = active_step_->microcycle; + dtack_cycle_.length = HalfCycles(2); + dtack_cycle_.operation &= ~(Microcycle::SelectByte | Microcycle::SelectWord); + continue; + } + + // Check for bus error here. + if(bus_error_) { + active_program_ = nullptr; + active_micro_op_ = long_exception_micro_ops_; + active_step_ = active_micro_op_->bus_program; + populate_bus_error_steps(2, get_status(), get_bus_code(), *active_step_->microcycle.address); + } + } + + // Check for an address error. Which I have assumed happens before the microcycle that + // would nominate the new address. + if( + (active_step_[0].microcycle.operation & Microcycle::NewAddress) && + (active_step_[1].microcycle.operation & Microcycle::SelectWord) && + *active_step_->microcycle.address & 1) { + active_program_ = nullptr; + active_micro_op_ = long_exception_micro_ops_; + active_step_ = active_micro_op_->bus_program; + populate_bus_error_steps(3, get_status(), get_bus_code(), *active_step_->microcycle.address); + } // Perform the microcycle. remaining_duration -= @@ -127,7 +177,7 @@ template void Proces if(trace_flag_) { // The user has set the trace bit in the status register. active_program_ = nullptr; - active_micro_op_ = exception_micro_ops_; + active_micro_op_ = short_exception_micro_ops_; populate_trap_steps(9, get_status()); } else { #ifdef LOG_TRACE @@ -144,34 +194,34 @@ template void Proces std::cout << '\n'; #endif - decoded_instruction_ = prefetch_queue_.halves.high.full; + decoded_instruction_.full = prefetch_queue_.halves.high.full; #ifdef LOG_TRACE - std::cout << std::hex << (program_counter_.full - 4) << ": " << std::setw(4) << decoded_instruction_ << '\t'; + std::cout << std::hex << (program_counter_.full - 4) << ": " << std::setw(4) << decoded_instruction_.full << '\t'; #endif if(signal_will_perform) { - bus_handler_.will_perform(program_counter_.full - 4, decoded_instruction_); + bus_handler_.will_perform(program_counter_.full - 4, decoded_instruction_.full); } - if(instructions[decoded_instruction_].micro_operations) { - if(instructions[decoded_instruction_].requires_supervisor && !is_supervisor_) { + if(instructions[decoded_instruction_.full].micro_operations) { + if(instructions[decoded_instruction_.full].requires_supervisor && !is_supervisor_) { // A privilege violation has been detected. active_program_ = nullptr; - active_micro_op_ = exception_micro_ops_; + active_micro_op_ = short_exception_micro_ops_; populate_trap_steps(8, get_status()); } else { // Standard instruction dispatch. - active_program_ = &instructions[decoded_instruction_]; + active_program_ = &instructions[decoded_instruction_.full]; active_micro_op_ = active_program_->micro_operations; } } else { // The opcode fetched isn't valid. active_program_ = nullptr; - active_micro_op_ = exception_micro_ops_; + active_micro_op_ = short_exception_micro_ops_; // The vector used depends on whether this is a vanilla unrecognised instruction, // or one on the A or F lines. - switch(decoded_instruction_ >> 12) { + switch(decoded_instruction_.full >> 12) { default: populate_trap_steps(4, get_status()); break; case 0xa: populate_trap_steps(10, get_status()); break; case 0xf: populate_trap_steps(11, get_status()); break; @@ -183,7 +233,7 @@ template void Proces auto bus_program = active_micro_op_->bus_program; switch(active_micro_op_->action) { default: - std::cerr << "Unhandled 68000 micro op action " << std::hex << active_micro_op_->action << " within instruction " << decoded_instruction_ << std::endl; + std::cerr << "Unhandled 68000 micro op action " << std::hex << active_micro_op_->action << " within instruction " << decoded_instruction_.full << std::endl; break; case int(MicroOp::Action::None): break; @@ -267,7 +317,7 @@ template void Proces #define no_extend(op, a, b, c) op(a, b, c, 0, z_set) #define extend(op, a, b, c) op(a, b, c, extend_flag_, z_or) -#define q() (((decoded_instruction_ >> 9)&7) ? ((decoded_instruction_ >> 9)&7) : 8) +#define q() (((decoded_instruction_.full >> 9)&7) ? ((decoded_instruction_.full >> 9)&7) : 8) case Operation::ADDb: { no_extend( addb, @@ -508,10 +558,10 @@ template void Proces const int8_t byte_offset = int8_t(prefetch_queue_.halves.high.halves.low); // Check whether this is secretly BSR. - const bool is_bsr = ((decoded_instruction_ >> 8) & 0xf) == 1; + const bool is_bsr = ((decoded_instruction_.full >> 8) & 0xf) == 1; // Test the conditional, treating 'false' as true. - const bool should_branch = is_bsr || evaluate_condition(decoded_instruction_ >> 8); + const bool should_branch = is_bsr || evaluate_condition(decoded_instruction_.full >> 8); // Schedule something appropriate, by rewriting the program for this instruction temporarily. if(should_branch) { @@ -533,7 +583,7 @@ template void Proces case Operation::DBcc: { // Decide what sort of DBcc this is. - if(!evaluate_condition(decoded_instruction_ >> 8)) { + if(!evaluate_condition(decoded_instruction_.full >> 8)) { -- active_program_->source->halves.low.full; const auto target_program_counter = program_counter_.full + int16_t(prefetch_queue_.halves.low.full) - 2; @@ -557,7 +607,7 @@ template void Proces case Operation::Scc: { active_program_->destination->halves.low.halves.low = - evaluate_condition(decoded_instruction_ >> 8) ? 0xff : 0x00; + evaluate_condition(decoded_instruction_.full >> 8) ? 0xff : 0x00; } break; /* @@ -781,7 +831,7 @@ template void Proces if(!active_program_->source->halves.low.full) { // Schedule a divide-by-zero exception. active_program_ = nullptr; - active_micro_op_ = exception_micro_ops_; + active_micro_op_ = short_exception_micro_ops_; bus_program = active_micro_op_->bus_program; populate_trap_steps(5, get_status()); program_counter_.full -= 2; @@ -844,7 +894,7 @@ template void Proces if(!active_program_->source->halves.low.full) { // Schedule a divide-by-zero exception. active_program_ = nullptr; - active_micro_op_ = exception_micro_ops_; + active_micro_op_ = short_exception_micro_ops_; bus_program = active_micro_op_->bus_program; populate_trap_steps(5, get_status()); program_counter_.full -= 2; @@ -946,7 +996,7 @@ template void Proces bus_program = base + (64 - total_to_move*words_per_reg)*2; \ \ /* Fill in the proper addresses and targets. */ \ - const auto mode = (decoded_instruction_ >> 3) & 7; \ + const auto mode = (decoded_instruction_.full >> 3) & 7; \ uint32_t start_address; \ if(mode <= 4) { \ start_address = active_program_->destination_address->full; \ @@ -1081,7 +1131,7 @@ template void Proces case Operation::TRAP: { // Select the trap steps as next; the initial microcycle should be 4 cycles long. bus_program = trap_steps_; - populate_trap_steps((decoded_instruction_ & 15) + 32, get_status()); + populate_trap_steps((decoded_instruction_.full & 15) + 32, get_status()); bus_program->microcycle.length = HalfCycles(8); // The program counter to push is actually one slot ago. @@ -1365,7 +1415,7 @@ template void Proces overflow_flag_ = (value ^ zero_result_) & (m); #define decode_shift_count() \ - int shift_count = (decoded_instruction_ & 32) ? data_[(decoded_instruction_ >> 9) & 7].full&63 : ( ((decoded_instruction_ >> 9)&7) ? ((decoded_instruction_ >> 9)&7) : 8) ; \ + int shift_count = (decoded_instruction_.full & 32) ? data_[(decoded_instruction_.full >> 9) & 7].full&63 : ( ((decoded_instruction_.full >> 9)&7) ? ((decoded_instruction_.full >> 9)&7) : 8) ; \ active_step_->microcycle.length = HalfCycles(4 * shift_count); #define set_flags_b(t) set_flags(active_program_->destination->halves.low.halves.low, 0x80, t) @@ -1604,7 +1654,7 @@ template void Proces */ case Operation::RTE_RTR: // If this is RTR, patch out the is_supervisor bit. - if(decoded_instruction_ == 0x4e77) { + if(decoded_instruction_.full == 0x4e77) { source_bus_data_[0].full = (source_bus_data_[0].full & ~(1 << 13)) | (is_supervisor_ << 13); @@ -1636,7 +1686,7 @@ template void Proces case Operation::STOP: set_status(prefetch_queue_.halves.low.full); - is_stopped_ = true; + execution_state_ = ExecutionState::Stopped; break; /* @@ -1666,23 +1716,23 @@ template void Proces } // If the post-increment mode was used, overwrite the source register. - const auto mode = (decoded_instruction_ >> 3) & 7; + const auto mode = (decoded_instruction_.full >> 3) & 7; if(mode == 3) { - const auto reg = decoded_instruction_ & 7; + const auto reg = decoded_instruction_.full & 7; address_[reg] = movem_final_address_; } } break; case int(MicroOp::Action::MOVEMtoMComplete): { - const auto mode = (decoded_instruction_ >> 3) & 7; + const auto mode = (decoded_instruction_.full >> 3) & 7; if(mode == 4) { - const auto reg = decoded_instruction_ & 7; + const auto reg = decoded_instruction_.full & 7; address_[reg] = movem_final_address_; } } break; case int(MicroOp::Action::PrepareJSR): { - const auto mode = (decoded_instruction_ >> 3) & 7; + 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) */ @@ -1695,7 +1745,7 @@ template void Proces } break; case int(MicroOp::Action::PrepareBSR): - destination_bus_data_[0].full = (decoded_instruction_ & 0xff) ? program_counter_.full - 2 : program_counter_.full; + destination_bus_data_[0].full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full; address_[7].full -= 4; effective_address_[1].full = address_[7].full; break; @@ -1828,7 +1878,6 @@ template void Proces #undef CalculateD8AnXn case int(MicroOp::Action::AssembleWordAddressFromPrefetch) | MicroOp::SourceMask: - // Assumption: this will be assembling right at the start of the instruction. effective_address_[0] = int16_t(prefetch_queue_.halves.low.full); break; @@ -1845,7 +1894,6 @@ template void Proces break; case int(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask: - // Assumption: this will be assembling right at the start of the instruction. source_bus_data_[0] = prefetch_queue_.halves.low.full; break; diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 899d549c3..b52e3fc82 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -3407,9 +3407,26 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { // Target addresses and values will be filled in by TRAP/illegal too. const size_t trap_offset = constructor.assemble_program("r nw nw nW nV nv np np", { &precomputed_addresses_[0], &precomputed_addresses_[1], &precomputed_addresses_[2] }); + const size_t bus_error_offset = + constructor.assemble_program( + "nn nw nw nW nw nw nw nW nV nv np np", + { + &precomputed_addresses_[0], + &precomputed_addresses_[1], + &precomputed_addresses_[2], + &precomputed_addresses_[3], + &precomputed_addresses_[4], + &precomputed_addresses_[5], + &precomputed_addresses_[6] + } + ); // Chuck in the proper micro-ops for handling an exception. - const auto exception_offset = all_micro_ops_.size(); + const auto short_exception_offset = all_micro_ops_.size(); + all_micro_ops_.emplace_back(ProcessorBase::MicroOp::Action::None); + all_micro_ops_.emplace_back(); + + const auto long_exception_offset = all_micro_ops_.size(); all_micro_ops_.emplace_back(ProcessorBase::MicroOp::Action::None); all_micro_ops_.emplace_back(); @@ -3444,6 +3461,16 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { trap_steps_[3].microcycle.value = trap_steps_[4].microcycle.value = &destination_bus_data_[0].halves.low; trap_steps_[5].microcycle.value = trap_steps_[6].microcycle.value = &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]; + bus_error_steps_[1].microcycle.value = bus_error_steps_[2].microcycle.value = &program_counter_.halves.low; + bus_error_steps_[3].microcycle.value = bus_error_steps_[4].microcycle.value = &destination_bus_data_[0].halves.low; + bus_error_steps_[5].microcycle.value = bus_error_steps_[6].microcycle.value = &program_counter_.halves.high; + bus_error_steps_[7].microcycle.value = bus_error_steps_[8].microcycle.value = &decoded_instruction_; + bus_error_steps_[9].microcycle.value = bus_error_steps_[10].microcycle.value = &effective_address_[0].halves.low; + bus_error_steps_[11].microcycle.value = bus_error_steps_[12].microcycle.value = &destination_bus_data_[0].halves.high; + bus_error_steps_[13].microcycle.value = bus_error_steps_[14].microcycle.value = &effective_address_[0].halves.high; + // Also relink the RTE and RTR bus steps to collect the program counter. // // Assumed order of input: PC.h, SR, PC.l (i.e. the opposite of TRAP's output). @@ -3453,9 +3480,15 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { steps[4].microcycle.value = steps[5].microcycle.value = &program_counter_.halves.low; } + // Setup the stop cycle. + stop_cycle_.length = HalfCycles(2); + // Complete linkage of the exception micro program. - exception_micro_ops_ = &all_micro_ops_[exception_offset]; - exception_micro_ops_->bus_program = trap_steps_; + short_exception_micro_ops_ = &all_micro_ops_[short_exception_offset]; + short_exception_micro_ops_->bus_program = trap_steps_; + + long_exception_micro_ops_ = &all_micro_ops_[long_exception_offset]; + long_exception_micro_ops_->bus_program = bus_error_steps_; // Set initial state. active_step_ = reset_bus_steps_; diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index acebfaf78..8c729355c 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -23,7 +23,13 @@ class ProcessorStorage { RegisterPair32 prefetch_queue_; // Each word will go into the low part of the word, then proceed upward. - bool is_stopped_ = false; + enum class ExecutionState { + Executing, + WaitingForDTack, + Stopped + } execution_state_ = ExecutionState::Executing; + Microcycle dtack_cycle_; + Microcycle stop_cycle_; // Various status bits. int is_supervisor_; @@ -241,7 +247,7 @@ class ProcessorStorage { /// Sets the high three bytes according to the MSB of the low byte. SignExtendByte, - /// From the next word in the prefetch queue assembles a 0-padded 32-bit long word in either or + /// From the next word in the prefetch queue assembles a sign-extended long word in either or /// both of effective_address_[0] and effective_address_[1]. AssembleWordAddressFromPrefetch, @@ -339,7 +345,9 @@ class ProcessorStorage { // Special steps and programs for exception handlers. BusStep *reset_bus_steps_; - MicroOp *exception_micro_ops_; + MicroOp *long_exception_micro_ops_; // i.e. those that leave 14 bytes on the stack — bus error and address error. + MicroOp *short_exception_micro_ops_; // i.e. those that leave 6 bytes on the stack — everything else (other than interrupts). + MicroOp *interrupt_micro_ops_; // Special micro-op sequences and storage for conditionals. BusStep *branch_taken_bus_steps_; @@ -356,12 +364,13 @@ class ProcessorStorage { BusStep *movem_write_steps_; 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; - uint16_t decoded_instruction_ = 0; + RegisterPair16 decoded_instruction_ = 0; uint16_t next_word_ = 0; /// Copies address_[7] to the proper stack pointer based on current mode. @@ -405,6 +414,11 @@ class ProcessorStorage { } } + /*! + Fills in the appropriate addresses and values to complete the TRAP steps — those + representing a short-form exception — and mutates the status register as if one + were beginning. + */ inline void populate_trap_steps(uint32_t vector, uint16_t status) { // Fill in the status word value. destination_bus_data_[0].full = status; @@ -417,15 +431,39 @@ class ProcessorStorage { effective_address_[0].full = vector << 2; // Schedule the proper stack activity. - precomputed_addresses_[0] = address_[7].full - 2; - precomputed_addresses_[1] = address_[7].full - 6; - precomputed_addresses_[2] = address_[7].full - 4; + 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_[2] = address_[7].full - 4; // PC.h address_[7].full -= 6; // Set the default timing. trap_steps_->microcycle.length = HalfCycles(8); } + inline 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; + effective_address_[1] = faulting_address; + + // Switch to supervisor mode, disable the trace bit. + set_is_supervisor(true); + trace_flag_ = 0; + + // Pick a vector. + effective_address_[0].full = vector << 2; + + // Schedule the proper stack activity. + precomputed_addresses_[0] = address_[7].full - 2; // PC.l + precomputed_addresses_[1] = address_[7].full - 6; // status word + precomputed_addresses_[2] = address_[7].full - 4; // PC.h + precomputed_addresses_[3] = address_[7].full - 8; // current instruction + precomputed_addresses_[4] = address_[7].full - 10; // fault address.l + precomputed_addresses_[5] = address_[7].full - 14; // bus cycle status word + precomputed_addresses_[6] = address_[7].full - 12; // fault address.h + address_[7].full -= 14; + } + private: friend class ProcessorStorageConstructor;