1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-09 06:29:33 +00:00

Takes further steps towards supporting interrupts.

Specifically:
* introduces the necessary bus signalling; and
* adds corresponding functional steps.

Still to figure out: getting into and out of an interrupt cycle.
This commit is contained in:
Thomas Harte 2019-05-01 15:19:24 -04:00
parent 078c3135df
commit f6ac407e4d
4 changed files with 83 additions and 13 deletions

View File

@ -65,21 +65,25 @@ struct Microcycle {
/// A Reset cycle is one in which the RESET output is asserted. /// A Reset cycle is one in which the RESET output is asserted.
static const int Reset = 3; 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. /// 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 /// 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. /// 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. /// 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. /// Contains the value of line FC0.
static const int IsData = 1 << 5; static const int IsData = 1 << 6;
/// Contains the value of line FC1. /// Contains the value of line FC1.
static const int IsProgram = 1 << 6; static const int IsProgram = 1 << 7;
int operation = 0; int operation = 0;
HalfCycles length = HalfCycles(4); HalfCycles length = HalfCycles(4);

View File

@ -1785,6 +1785,28 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
address_[7].full += 6; address_[7].full += 6;
break; 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): case int(MicroOp::Action::CopyNextWord):
next_word_ = prefetch_queue_.halves.low.full; next_word_ = prefetch_queue_.halves.low.full;
break; break;

View File

@ -147,6 +147,7 @@ struct ProcessorStorageConstructor {
* nf: fetch the SSP's LSW; * nf: fetch the SSP's LSW;
* _: hold the reset line active for the usual period. * _: 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. * 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 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 state and something about what's observable on the bus, but it's helpful to
@ -337,6 +338,19 @@ struct ProcessorStorageConstructor {
continue; 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 13; 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; std::cerr << "MC68000 program builder; Unknown access token " << token << std::endl;
assert(false); assert(false);
} }
@ -3298,6 +3312,12 @@ struct ProcessorStorageConstructor {
#undef dec #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 Dn
#undef An #undef An
#undef Ind #undef Ind
@ -3321,21 +3341,31 @@ struct ProcessorStorageConstructor {
#undef op #undef op
#undef pseq #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. // Finalise micro-op and program pointers.
for(size_t instruction = 0; instruction < 65536; ++instruction) { for(size_t instruction = 0; instruction < 65536; ++instruction) {
if(micro_op_pointers[instruction] != std::numeric_limits<size_t>::max()) { if(micro_op_pointers[instruction] != std::numeric_limits<size_t>::max()) {
storage_.instructions[instruction].micro_operations = &storage_.all_micro_ops_[micro_op_pointers[instruction]]; storage_.instructions[instruction].micro_operations = &storage_.all_micro_ops_[micro_op_pointers[instruction]];
link_operations(storage_.instructions[instruction].micro_operations, &arbitrary_base);
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 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()); 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_ = &all_micro_ops_[long_exception_offset];
long_exception_micro_ops_->bus_program = bus_error_steps_; 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. // Set initial state.
active_step_ = reset_bus_steps_; active_step_ = reset_bus_steps_;
effective_address_[0] = 0; effective_address_[0] = 0;

View File

@ -59,6 +59,8 @@ class ProcessorStorage {
bool bus_acknowledge_ = false; bool bus_acknowledge_ = false;
bool halt_ = false; bool halt_ = false;
int accepted_interrupt_level_ = 0;
// Generic sources and targets for memory operations; // Generic sources and targets for memory operations;
// by convention: [0] = source, [1] = destination. // by convention: [0] = source, [1] = destination.
RegisterPair32 effective_address_[2]; 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 // (i) fills in the proper stack addresses to the bus steps for this micro-op; and
// (ii) adjusts the stack pointer appropriately. // (ii) adjusts the stack pointer appropriately.
PrepareRTE_RTR, 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 SourceMask = 1 << 30;
static const int DestinationMask = 1 << 29; static const int DestinationMask = 1 << 29;