mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-01 13:58:20 +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:
parent
078c3135df
commit
f6ac407e4d
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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 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;
|
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,20 +3341,30 @@ 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;
|
// Link up the interrupt micro ops.
|
||||||
while(!operation->is_terminal()) {
|
storage_.interrupt_micro_ops_ = &storage_.all_micro_ops_[interrupt_pointer];
|
||||||
const auto offset = size_t(operation->bus_program - &arbitrary_base);
|
link_operations(storage_.interrupt_micro_ops_, &arbitrary_base);
|
||||||
assert(offset >= 0 && offset < storage_.all_bus_steps_.size());
|
|
||||||
operation->bus_program = &storage_.all_bus_steps_[offset];
|
|
||||||
++operation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user