1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Implement STOPpages, waits for DTack, and bus and address error exceptions.

This commit is contained in:
Thomas Harte 2019-04-30 19:24:22 -04:00
parent e430f2658f
commit 31bb770fdd
4 changed files with 174 additions and 57 deletions

View File

@ -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;

View File

@ -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 <class T, bool dtack_is_implicit, bool signal_will_perform> void Processor<T, dtack_is_implicit, signal_will_perform>::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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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 <class T, bool dtack_is_implicit, bool signal_will_perform> 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;

View File

@ -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_;

View File

@ -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;