diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp index b4c9364b0..c3b8e3875 100644 --- a/Processors/68000/68000.hpp +++ b/Processors/68000/68000.hpp @@ -65,21 +65,25 @@ struct Microcycle { /// A Reset cycle is one in which the RESET output is asserted. static const int Reset = 3; + /// The interrupt acknowledge cycle is that during which the 68000 seeks to obtain the vector for + /// an interrupt it plans to observe. Noted on a real 68000 by all FCs being set to 1. + static const int InterruptAcknowledge = 4; + /// Indicates that the address and both data select strobes are active. - static const int SelectWord = 1 << 2; + static const int SelectWord = 1 << 3; /// Indicates that the address strobe and exactly one of the data strobes are active; you can determine /// which by inspecting the low bit of the provided address. The RW line indicates a read. - static const int SelectByte = 1 << 3; + static const int SelectByte = 1 << 4; /// If set, indicates a read. Otherwise, a write. - static const int Read = 1 << 4; + static const int Read = 1 << 5; /// Contains the value of line FC0. - static const int IsData = 1 << 5; + static const int IsData = 1 << 6; /// Contains the value of line FC1. - static const int IsProgram = 1 << 6; + static const int IsProgram = 1 << 7; int operation = 0; HalfCycles length = HalfCycles(4); diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 11450d2e3..7546dd713 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -1785,6 +1785,28 @@ template void Proces address_[7].full += 6; break; + case int(MicroOp::Action::PrepareINT): + accepted_interrupt_level_ = bus_interrupt_level_; + populate_trap_steps(0, get_status()); + break; + + case int(MicroOp::Action::PrepareINTVector): + // Bus error => spurious interrupt. + if(bus_error_) { + effective_address_[0].full = 24 << 4; + break; + } + + // Valid peripheral address => autovectored interrupt. + if(is_peripheral_address_) { + effective_address_[0].full = (24 + accepted_interrupt_level_) << 4; + break; + } + + // Otherwise, the vector is whatever we were just told it is. + effective_address_[0].full = source_bus_data_[0].halves.low.halves.low << 4; + break; + case int(MicroOp::Action::CopyNextWord): next_word_ = prefetch_queue_.halves.low.full; break; diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index f592d0281..b1a1718d0 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -147,6 +147,7 @@ struct ProcessorStorageConstructor { * nf: fetch the SSP's LSW; * _: hold the reset line active for the usual period. * tas: perform the final 6 cycles of a TAS: like an n nw but with the address strobe active for the entire period. + * int: the interrupt acknowledge cycle. Quite a lot of that is duplicative, implying both something about internal state and something about what's observable on the bus, but it's helpful to @@ -337,6 +338,19 @@ struct ProcessorStorageConstructor { continue; } + // Interrupt acknowledge. + if(token == "int") { + step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::IsData | Microcycle::IsProgram | 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; + steps.push_back(step); + + step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::IsData | Microcycle::IsProgram | Microcycle::SameAddress | Microcycle::SelectByte; + steps.push_back(step); + + continue; + } + std::cerr << "MC68000 program builder; Unknown access token " << token << std::endl; assert(false); } @@ -3298,6 +3312,12 @@ struct ProcessorStorageConstructor { #undef dec } + // Throw in the interrupt program. + const auto interrupt_pointer = storage_.all_micro_ops_.size(); + op(Action::PrepareINT, seq("int")); // Perform a cycle that will obtain an interrupt vector, or else dictate an autovector or a spurious interrupt. + op(Action::PrepareINTVector); // The standard trap steps will be appended here, and PrepareINT will set them up according to the vector received. + op(); + #undef Dn #undef An #undef Ind @@ -3321,21 +3341,31 @@ struct ProcessorStorageConstructor { #undef op #undef pseq + /*! + Iterates through the micro-sequence beginning at @c start, finalising bus_program + pointers that have been transiently stored as relative to @c arbitrary_base. + */ + const auto link_operations = [this](MicroOp *start, BusStep *arbitrary_base) { + while(!start->is_terminal()) { + const auto offset = size_t(start->bus_program - arbitrary_base); + assert(offset >= 0 && offset < storage_.all_bus_steps_.size()); + start->bus_program = &storage_.all_bus_steps_[offset]; + ++start; + } + }; + // Finalise micro-op and program pointers. for(size_t instruction = 0; instruction < 65536; ++instruction) { if(micro_op_pointers[instruction] != std::numeric_limits::max()) { storage_.instructions[instruction].micro_operations = &storage_.all_micro_ops_[micro_op_pointers[instruction]]; - - auto operation = storage_.instructions[instruction].micro_operations; - while(!operation->is_terminal()) { - const auto offset = size_t(operation->bus_program - &arbitrary_base); - assert(offset >= 0 && offset < storage_.all_bus_steps_.size()); - operation->bus_program = &storage_.all_bus_steps_[offset]; - ++operation; - } + link_operations(storage_.instructions[instruction].micro_operations, &arbitrary_base); } } + // Link up the interrupt micro ops. + storage_.interrupt_micro_ops_ = &storage_.all_micro_ops_[interrupt_pointer]; + link_operations(storage_.interrupt_micro_ops_, &arbitrary_base); + printf("%lu total steps\n", storage_.all_bus_steps_.size()); } @@ -3478,6 +3508,9 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { long_exception_micro_ops_ = &all_micro_ops_[long_exception_offset]; long_exception_micro_ops_->bus_program = bus_error_steps_; + // Apply the TRAP steps to the interrupt routine. + interrupt_micro_ops_[1].bus_program = trap_steps_; + // Set initial state. active_step_ = reset_bus_steps_; effective_address_[0] = 0; diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index afa0ce93a..81a244241 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -59,6 +59,8 @@ class ProcessorStorage { bool bus_acknowledge_ = false; bool halt_ = false; + int accepted_interrupt_level_ = 0; + // Generic sources and targets for memory operations; // by convention: [0] = source, [1] = destination. RegisterPair32 effective_address_[2]; @@ -294,6 +296,15 @@ class ProcessorStorage { // (i) fills in the proper stack addresses to the bus steps for this micro-op; and // (ii) adjusts the stack pointer appropriately. PrepareRTE_RTR, + + // Performs the necessary status word substitution for the current interrupt level, + // and does the first part of initialising the trap steps. + PrepareINT, + + // Observes the bus_error_, valid_peripheral_address_ and/or the value currently in + // source_bus_data_ to determine an interrupt vector, and fills in the final trap + // steps detail appropriately. + PrepareINTVector, }; static const int SourceMask = 1 << 30; static const int DestinationMask = 1 << 29;