1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-22 12:33:29 +00:00

Attempts fully to capture 68000 state.

Albeit that it can't be put back yet.
This commit is contained in:
Thomas Harte 2020-05-18 23:55:54 -04:00
parent 6f16928215
commit a3d4c7599b
5 changed files with 232 additions and 62 deletions

View File

@ -210,7 +210,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
if(bus_interrupt_level_ > interrupt_level_) {
pending_interrupt_level_ = bus_interrupt_level_;
program_counter_.full += 4; // Don't return to this stop.
execution_state_ = ExecutionState::BeginInterrupt;
execution_state_ = ExecutionState::WillBeginInterrupt;
continue;
}
@ -244,7 +244,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
bus_handler_.perform_bus_operation(stop_cycle_, is_supervisor_);
continue;
case ExecutionState::BeginInterrupt:
case ExecutionState::WillBeginInterrupt:
#ifdef LOG_TRACE
// should_log = true;
#endif
@ -269,7 +269,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
// no instruction was ongoing. Either way, do a standard instruction operation.
if(pending_interrupt_level_) {
execution_state_ = ExecutionState::BeginInterrupt;
execution_state_ = ExecutionState::WillBeginInterrupt;
break;
}
@ -302,7 +302,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
#ifndef NDEBUG
/* Debugging feature: reset the effective addresses and data latches, so that it's
more obvious if some of the instructions aren't properly feeding them. */
effective_address_[0].full = effective_address_[1].full = source_bus_data_[0].full = destination_bus_data_[0].full = 0x12344321;
effective_address_[0].full = effective_address_[1].full = source_bus_data_.full = destination_bus_data_.full = 0x12344321;
#endif
#ifdef LOG_TRACE
@ -829,7 +829,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
// JMP: copies the source bus data to the program counter.
case Operation::RTS:
program_counter_ = source_bus_data_[0];
program_counter_ = source_bus_data_;
break;
/*
@ -879,7 +879,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
break;
case Operation::PEA:
destination_bus_data_[0] = effective_address_[0];
destination_bus_data_ = effective_address_[0];
break;
/*
@ -1119,30 +1119,30 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
case Operation::MOVEPtoMw:
// Write pattern is nW+ nw, which should write the low word of the source in big-endian form.
destination_bus_data_[0].halves.high.full = source()->halves.low.halves.high;
destination_bus_data_[0].halves.low.full = source()->halves.low.halves.low;
destination_bus_data_.halves.high.full = source()->halves.low.halves.high;
destination_bus_data_.halves.low.full = source()->halves.low.halves.low;
break;
case Operation::MOVEPtoMl:
// Write pattern is nW+ nWr+ nw+ nwr, which should write the source in big-endian form.
destination_bus_data_[0].halves.high.full = source()->halves.high.halves.high;
source_bus_data_[0].halves.high.full = source()->halves.high.halves.low;
destination_bus_data_[0].halves.low.full = source()->halves.low.halves.high;
source_bus_data_[0].halves.low.full = source()->halves.low.halves.low;
destination_bus_data_.halves.high.full = source()->halves.high.halves.high;
source_bus_data_.halves.high.full = source()->halves.high.halves.low;
destination_bus_data_.halves.low.full = source()->halves.low.halves.high;
source_bus_data_.halves.low.full = source()->halves.low.halves.low;
break;
case Operation::MOVEPtoRw:
// Read pattern is nRd+ nrd.
source()->halves.low.halves.high = destination_bus_data_[0].halves.high.halves.low;
source()->halves.low.halves.low = destination_bus_data_[0].halves.low.halves.low;
source()->halves.low.halves.high = destination_bus_data_.halves.high.halves.low;
source()->halves.low.halves.low = destination_bus_data_.halves.low.halves.low;
break;
case Operation::MOVEPtoRl:
// Read pattern is nRd+ nR+ nrd+ nr.
source()->halves.high.halves.high = destination_bus_data_[0].halves.high.halves.low;
source()->halves.high.halves.low = source_bus_data_[0].halves.high.halves.low;
source()->halves.low.halves.high = destination_bus_data_[0].halves.low.halves.low;
source()->halves.low.halves.low = source_bus_data_[0].halves.low.halves.low;
source()->halves.high.halves.high = destination_bus_data_.halves.high.halves.low;
source()->halves.high.halves.low = source_bus_data_.halves.high.halves.low;
source()->halves.low.halves.high = destination_bus_data_.halves.low.halves.low;
source()->halves.low.halves.low = source_bus_data_.halves.low.halves.low;
break;
/*
@ -1448,7 +1448,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
effective_address_[1].full = address_[7].full;
// The current value of the address register will be pushed.
destination_bus_data_[0].full = source()->full;
destination_bus_data_.full = source()->full;
// The address register will then contain the bottom of the stack,
// and the stack pointer will be offset.
@ -1458,7 +1458,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
case Operation::UNLINK:
address_[7].full = effective_address_[1].full + 2;
destination()->full = destination_bus_data_[0].full;
destination()->full = destination_bus_data_.full;
break;
/*
@ -1859,10 +1859,10 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
// If this is RTR, patch out the supervisor half of the status register.
if(decoded_instruction_.full == 0x4e77) {
const auto current_status = status();
source_bus_data_[0].halves.low.halves.high =
source_bus_data_.halves.low.halves.high =
uint8_t(current_status >> 8);
}
apply_status(source_bus_data_[0].full);
apply_status(source_bus_data_.full);
break;
/*
@ -1938,9 +1938,9 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
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) */
case 2: destination_bus_data_.full = program_counter_.full - 2; break; /* (An) */
default:
destination_bus_data_[0].full = program_counter_.full; /* Everything other than (An) */
destination_bus_data_.full = program_counter_.full; /* Everything other than (An) */
break;
}
address_[7].full -= 4;
@ -1948,7 +1948,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
} break;
case int_type(MicroOp::Action::PrepareBSR):
destination_bus_data_[0].full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full;
destination_bus_data_.full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full;
address_[7].full -= 4;
effective_address_[1].full = address_[7].full;
break;
@ -2002,7 +2002,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
}
// Otherwise, the vector is whatever we were just told it is.
effective_address_[0].full = uint32_t(source_bus_data_[0].halves.low.halves.low << 2);
effective_address_[0].full = uint32_t(source_bus_data_.halves.low.halves.low << 2);
// printf("Interrupt vector: %06x\n", effective_address_[0].full);
break;
@ -2143,19 +2143,19 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
break;
case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask:
source_bus_data_[0] = prefetch_queue_.halves.low.full;
source_bus_data_ = prefetch_queue_.halves.low.full;
break;
case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::DestinationMask:
destination_bus_data_[0] = prefetch_queue_.halves.low.full;
destination_bus_data_ = prefetch_queue_.halves.low.full;
break;
case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask:
source_bus_data_[0] = prefetch_queue_.full;
source_bus_data_ = prefetch_queue_.full;
break;
case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::DestinationMask:
destination_bus_data_[0] = prefetch_queue_.full;
destination_bus_data_ = prefetch_queue_.full;
break;
case int_type(MicroOp::Action::CopyToEffectiveAddress) | MicroOp::SourceMask:

View File

@ -300,7 +300,7 @@ struct ProcessorStorageConstructor {
if(tolower(access_pattern[1]) == 's') {
step.microcycle.operation = Microcycle::NewAddress;
step.microcycle.address = &storage_.effective_address_[1].full;
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_[0].halves.high : &storage_.destination_bus_data_[0].halves.low;
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_.halves.high : &storage_.destination_bus_data_.halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord;
@ -312,7 +312,7 @@ struct ProcessorStorageConstructor {
// A stack read.
if(tolower(access_pattern[1]) == 'u') {
RegisterPair32 *const scratch_data = &storage_.source_bus_data_[0];
RegisterPair32 *const scratch_data = &storage_.source_bus_data_;
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read;
step.microcycle.address = &storage_.effective_address_[0].full;
@ -350,7 +350,7 @@ struct ProcessorStorageConstructor {
) {
const bool is_read = tolower(access_pattern[1]) == 'r';
const bool use_source_storage = tolower(end_of_pattern[-1]) == 'r';
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_[0] : &storage_.destination_bus_data_[0];
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_ : &storage_.destination_bus_data_;
assert(address_iterator != addresses.end());
@ -377,7 +377,7 @@ struct ProcessorStorageConstructor {
if(token_length == 3) {
// The completing part of a TAS.
if(access_pattern[0] == 't' && access_pattern[1] == 'a' && access_pattern[2] == 's') {
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0];
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_;
assert(address_iterator != addresses.end());
@ -399,7 +399,7 @@ struct ProcessorStorageConstructor {
if(access_pattern[0] == 'i' && access_pattern[1] == 'n' && access_pattern[2] == 't') {
step.microcycle.operation = Microcycle::InterruptAcknowledge | 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;
step.microcycle.value = &storage_.source_bus_data_.halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte;
@ -3250,20 +3250,20 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
//
// Order of output is: PC.l, SR, PC.h.
trap_steps_ = &all_bus_steps_[trap_offset];
constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high });
constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high });
// Fill in the same order of writes for the interrupt micro-ops, though it divides the work differently.
constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high });
constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &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];
constructor.replace_write_values(bus_error_steps_, {
&program_counter_.halves.low,
&destination_bus_data_[0].halves.low,
&destination_bus_data_.halves.low,
&program_counter_.halves.high,
&decoded_instruction_,
&effective_address_[1].halves.low,
&destination_bus_data_[0].halves.high,
&destination_bus_data_.halves.high,
&effective_address_[1].halves.high
});

View File

@ -38,7 +38,7 @@ class ProcessorStorage {
Halted,
/// Signals a transition from some other straight directly to cueing up an interrupt.
BeginInterrupt,
WillBeginInterrupt,
} execution_state_ = ExecutionState::Executing;
Microcycle dtack_cycle_;
Microcycle stop_cycle_;
@ -75,8 +75,8 @@ class ProcessorStorage {
// Generic sources and targets for memory operations;
// by convention: [0] = source, [1] = destination.
RegisterPair32 effective_address_[2];
RegisterPair32 source_bus_data_[1];
RegisterPair32 destination_bus_data_[1];
RegisterPair32 source_bus_data_;
RegisterPair32 destination_bus_data_;
HalfCycles half_cycles_left_to_run_;
HalfCycles e_clock_phase_;
@ -394,18 +394,18 @@ class ProcessorStorage {
void set_source(ProcessorStorage &storage, int mode, int reg) {
set_source_address(storage, reg);
switch(mode) {
case 0: set_source(storage, &storage.data_[reg]); break;
case 1: set_source(storage, &storage.address_[reg]); break;
default: set_source(storage, &storage.source_bus_data_[0]); break;
case 0: set_source(storage, &storage.data_[reg]); break;
case 1: set_source(storage, &storage.address_[reg]); break;
default: set_source(storage, &storage.source_bus_data_); break;
}
}
void set_destination(ProcessorStorage &storage, int mode, int reg) {
set_destination_address(storage, reg);
switch(mode) {
case 0: set_destination(storage, &storage.data_[reg]); break;
case 1: set_destination(storage, &storage.address_[reg]); break;
default: set_destination(storage, &storage.destination_bus_data_[0]); break;
case 0: set_destination(storage, &storage.data_[reg]); break;
case 1: set_destination(storage, &storage.address_[reg]); break;
default: set_destination(storage, &storage.destination_bus_data_); break;
}
}
};
@ -438,13 +438,15 @@ class ProcessorStorage {
BusStep *movem_read_steps_;
BusStep *movem_write_steps_;
// These two are dynamically modified depending on the particular
// TRAP and bus error.
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;
const Program *active_program_ = nullptr;
const MicroOp *active_micro_op_ = nullptr;
const BusStep *active_step_ = nullptr;
RegisterPair16 decoded_instruction_ = 0;
uint16_t next_word_ = 0;
@ -455,9 +457,9 @@ class ProcessorStorage {
void set_is_supervisor(bool);
// Transient storage for MOVEM, TRAP and others.
uint32_t precomputed_addresses_[65];
RegisterPair16 throwaway_value_;
uint32_t movem_final_address_;
uint32_t precomputed_addresses_[65]; // This is a big chunk of rarely-used storage. It's placed last deliberately.
/*!
Evaluates the conditional described by @c code and returns @c true or @c false to
@ -496,7 +498,7 @@ class ProcessorStorage {
*/
forceinline void populate_trap_steps(uint32_t vector, uint16_t status) {
// Fill in the status word value.
destination_bus_data_[0].full = status;
destination_bus_data_.full = status;
// Switch to supervisor mode, disable the trace bit.
set_is_supervisor(true);
@ -507,7 +509,7 @@ class ProcessorStorage {
// Schedule the proper stack activity.
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_[1] = address_[7].full - 6; // status word (in destination_bus_data_)
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
address_[7].full -= 6;
@ -517,8 +519,8 @@ class ProcessorStorage {
forceinline 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;
destination_bus_data_.halves.low.full = status;
destination_bus_data_.halves.high.full = bus_status;
effective_address_[1] = faulting_address;
// Switch to supervisor mode, disable the trace bit.

View File

@ -33,22 +33,133 @@ State::State(const ProcessorBase &src): State() {
inputs.bus_grant = false; // TODO (within the 68000).
inputs.halt = src.halt_;
// TODO:
// execution_state_
// active_[program_/micro_op_/step_]
// Execution state.
execution_state.e_clock_phase = src.e_clock_phase_.as<uint8_t>();
execution_state.effective_address[0] = src.effective_address_[0].full;
execution_state.effective_address[1] = src.effective_address_[1].full;
execution_state.source_data = src.source_bus_data_[0].full;
execution_state.destination_data = src.destination_bus_data_[0].full;
execution_state.source_data = src.source_bus_data_.full;
execution_state.destination_data = src.destination_bus_data_.full;
execution_state.last_trace_flag = src.last_trace_flag_;
execution_state.next_word = src.next_word_;
execution_state.dbcc_false_address = src.dbcc_false_address_;
execution_state.is_starting_interrupt = src.is_starting_interrupt_;
execution_state.pending_interrupt_level = uint8_t(src.pending_interrupt_level_);
execution_state.accepted_interrupt_level = uint8_t(src.accepted_interrupt_level_);
execution_state.movem_final_address = src.movem_final_address_;
static_assert(sizeof(execution_state.source_addresses) == sizeof(src.precomputed_addresses_));
memcpy(&execution_state.source_addresses, &src.precomputed_addresses_, sizeof(src.precomputed_addresses_));
// This is collapsed to a Boolean; if there is an active program then it's the
// one implied by the current instruction.
execution_state.active_program = src.active_program_;
// Slightly dodgy assumption here: the Phase enum will always exactly track
// the 68000's ExecutionState enum.
execution_state.phase = ExecutionState::Phase(src.execution_state_);
auto contained_by = [](const auto *source, const auto *reference) -> bool {
while(true) {
if(source == reference) return true;
if(source->is_terminal()) return false;
++source;
}
};
// Store enough information to relocate the MicroOp.
const ProcessorBase::MicroOp *micro_op_base = nullptr;
if(src.active_program_) {
micro_op_base = &src.all_micro_ops_[src.instructions[src.decoded_instruction_.full].micro_operations];
assert(contained_by(micro_op_base, src.active_micro_op_));
execution_state.micro_op_source = ExecutionState::MicroOpSource::ActiveProgram;
} else {
if(contained_by(src.long_exception_micro_ops_, src.active_micro_op_)) {
execution_state.micro_op_source = ExecutionState::MicroOpSource::LongException;
micro_op_base = src.long_exception_micro_ops_;
} else if(contained_by(src.short_exception_micro_ops_, src.active_micro_op_)) {
execution_state.micro_op_source = ExecutionState::MicroOpSource::ShortException;
micro_op_base = src.short_exception_micro_ops_;
} else if(contained_by(src.interrupt_micro_ops_, src.active_micro_op_)) {
execution_state.micro_op_source = ExecutionState::MicroOpSource::Interrupt;
micro_op_base = src.interrupt_micro_ops_;
} else {
assert(false);
}
}
execution_state.micro_op = uint8_t(src.active_micro_op_ - micro_op_base);
// Encode the BusStep.
struct BusStepOption {
const ProcessorBase::BusStep *const base;
const ExecutionState::BusStepSource source;
};
BusStepOption bus_step_options[] = {
{
src.reset_bus_steps_,
ExecutionState::BusStepSource::Reset
},
{
src.branch_taken_bus_steps_,
ExecutionState::BusStepSource::BranchTaken
},
{
src.branch_byte_not_taken_bus_steps_,
ExecutionState::BusStepSource::BranchByteNotTaken
},
{
src.branch_word_not_taken_bus_steps_,
ExecutionState::BusStepSource::BranchWordNotTaken
},
{
src.bsr_bus_steps_,
ExecutionState::BusStepSource::BSR
},
{
src.dbcc_condition_true_steps_,
ExecutionState::BusStepSource::DBccConditionTrue
},
{
src.dbcc_condition_false_no_branch_steps_,
ExecutionState::BusStepSource::DBccConditionFalseNoBranch
},
{
src.dbcc_condition_false_branch_steps_,
ExecutionState::BusStepSource::DBccConditionFalseBranch
},
{
src.movem_read_steps_,
ExecutionState::BusStepSource::MovemRead
},
{
src.movem_write_steps_,
ExecutionState::BusStepSource::MovemWrite
},
{
src.trap_steps_,
ExecutionState::BusStepSource::Trap
},
{
src.bus_error_steps_,
ExecutionState::BusStepSource::BusError
},
{
&src.all_bus_steps_[src.active_micro_op_->bus_program],
ExecutionState::BusStepSource::FollowMicroOp
},
{nullptr}
};
const BusStepOption *bus_step_option = bus_step_options;
const ProcessorBase::BusStep *bus_step_base = nullptr;
while(bus_step_option->base) {
if(contained_by(bus_step_option->base, src.active_step_)) {
bus_step_base = bus_step_option->base;
execution_state.bus_step_source = bus_step_option->source;
break;
}
++bus_step_option;
}
assert(bus_step_base);
execution_state.bus_step = uint8_t(src.active_step_ - bus_step_base);
}
void State::apply(ProcessorBase &target) {
@ -100,5 +211,19 @@ State::ExecutionState::ExecutionState() {
DeclareField(is_starting_interrupt);
DeclareField(pending_interrupt_level);
DeclareField(accepted_interrupt_level);
DeclareField(active_program);
DeclareField(movem_final_address);
DeclareField(source_addresses);
AnnounceEnum(Phase);
DeclareField(phase);
AnnounceEnum(MicroOpSource);
DeclareField(micro_op_source);
DeclareField(micro_op);
AnnounceEnum(BusStepSource);
DeclareField(bus_step_source);
DeclareField(bus_step);
}
}

View File

@ -72,6 +72,49 @@ struct State: public Reflection::StructImpl<State> {
uint8_t pending_interrupt_level;
uint8_t accepted_interrupt_level;
// This is a reflective do-over of the ExecutionState enum within
// MC68000Storage; I've yet to decide how happy I am with that
// as an approach.
ReflectableEnum(Phase,
Executing,
WaitingForDTack,
Stopped,
Halted,
WillBeginInterrupt
);
Phase phase;
bool active_program;
uint32_t movem_final_address;
uint32_t source_addresses[65];
ReflectableEnum(MicroOpSource,
ActiveProgram,
LongException,
ShortException,
Interrupt
);
MicroOpSource micro_op_source;
uint8_t micro_op;
ReflectableEnum(BusStepSource,
FollowMicroOp,
BusError,
Trap,
Reset,
BranchTaken,
BranchByteNotTaken,
BranchWordNotTaken,
BSR,
DBccConditionTrue,
DBccConditionFalseNoBranch,
DBccConditionFalseBranch,
MovemRead,
MovemWrite,
);
BusStepSource bus_step_source;
uint8_t bus_step;
ExecutionState();
} execution_state;