mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Fill in just enough to attempt the reset exception, assuming DTACK rather than VPA or BERR.
This commit is contained in:
parent
13848ddbbc
commit
345f7c3c62
@ -121,6 +121,9 @@ struct Microcycle {
|
||||
*/
|
||||
SlicedInt16 *value = nullptr;
|
||||
|
||||
Microcycle(OperationT operation) : operation(operation) {}
|
||||
Microcycle() {}
|
||||
|
||||
/// @returns @c true if two Microcycles are equal; @c false otherwise.
|
||||
bool operator ==(const Microcycle &rhs) const {
|
||||
if(value != rhs.value) return false;
|
||||
|
@ -9,27 +9,29 @@
|
||||
#ifndef _8000Mk2Implementation_h
|
||||
#define _8000Mk2Implementation_h
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace CPU {
|
||||
namespace MC68000Mk2 {
|
||||
|
||||
template <class BusHandler, bool dtack_is_implicit, bool permit_overrun, bool signal_will_perform>
|
||||
void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perform>::run_for(HalfCycles duration) {
|
||||
// Accumulate the newly paid-in cycles. If this instance remains in deficit, exit.
|
||||
time_remaining += duration;
|
||||
if(time_remaining <= HalfCycles(0)) return;
|
||||
time_remaining_ += duration;
|
||||
if(time_remaining_ <= HalfCycles(0)) return;
|
||||
|
||||
// Check whether all remaining time has been expended; if so then exit, having set this line up as
|
||||
// the next resumption point.
|
||||
#define ConsiderExit() if(time_remaining <= HalfCycles(0)) { state = __LINE__; return; } [[fallthrough]]; case __LINE__:
|
||||
#define ConsiderExit() if(time_remaining_ <= HalfCycles(0)) { state_ = __LINE__; return; } [[fallthrough]]; case __LINE__:
|
||||
|
||||
// Subtracts `n` half-cycles from `time_remaining`; if permit_overrun is false, also ConsiderExit()
|
||||
#define Spend(n) cycles_owed -= HalfCycles(n); if constexpr (!permit_overrun) ConsiderExit()
|
||||
// Subtracts `n` half-cycles from `time_remaining_`; if permit_overrun is false, also ConsiderExit()
|
||||
#define Spend(n) time_remaining_ -= (n); if constexpr (!permit_overrun) ConsiderExit()
|
||||
|
||||
// Performs ConsiderExit() only if permit_overrun is true
|
||||
#define CheckOverrun() if constexpr (permit_overrun) ConsiderExit()
|
||||
|
||||
//
|
||||
// So structure is, in general:
|
||||
// So basic structure is, in general:
|
||||
//
|
||||
// case Action:
|
||||
// do_something();
|
||||
@ -40,7 +42,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Spend(30);
|
||||
// CheckOverrun();
|
||||
//
|
||||
// state = next_action;
|
||||
// state_ = next_action;
|
||||
// break;
|
||||
//
|
||||
// Additional notes:
|
||||
@ -50,11 +52,128 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// its invented entry- and exit-points, meaning that negative numbers are
|
||||
// the easiest group that is safely definitely never going to collide.
|
||||
|
||||
|
||||
// Some microcycles that will be modified as required and used in the loop below;
|
||||
// the semantics of a switch statement make in-place declarations awkward.
|
||||
Microcycle idle{0};
|
||||
|
||||
// Read a data word.
|
||||
Microcycle read_word_data_announce {
|
||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData
|
||||
};
|
||||
Microcycle read_word_data {
|
||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData
|
||||
};
|
||||
|
||||
// Read a program word. All accesses via the program counter are word sized.
|
||||
Microcycle read_program_announce {
|
||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram
|
||||
};
|
||||
Microcycle read_program {
|
||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram
|
||||
};
|
||||
|
||||
// Holding spot when awaiting DTACK/etc.
|
||||
Microcycle awaiting_dtack;
|
||||
|
||||
// Spare containers:
|
||||
uint32_t address; // For an address, where it isn't provideable by reference.
|
||||
HalfCycles delay; // To receive any additional time added on by calls to perform_bus_operation.
|
||||
|
||||
// Helper macros for common bus transactions:
|
||||
|
||||
// Performs the bus operation and then applies a `Spend` of its length
|
||||
// plus any additional length returned by the bus handler.
|
||||
#define PerformBusOperation(x) \
|
||||
delay = bus_handler_.perform_bus_operation(x, is_supervisor_); \
|
||||
Spend(x.length + delay)
|
||||
|
||||
// Performs no bus activity for the specified number of microcycles.
|
||||
#define IdleBus(n) \
|
||||
idle.length = HalfCycles(n * 4); \
|
||||
PerformBusOperation(idle)
|
||||
|
||||
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
||||
// holding the bus cycle provided.
|
||||
#define WaitForDTACK(x) \
|
||||
if constexpr (!dtack_is_implicit && !dtack_ && !vpa_ && !berr_) { \
|
||||
awaiting_dtack = x; \
|
||||
awaiting_dtack.length = HalfCycles(2); \
|
||||
post_dtack_state_ = __LINE__ + 4; \
|
||||
state_ = State::WaitForDTACK; \
|
||||
break; \
|
||||
} \
|
||||
[[fallthrough]]; case __LINE__:
|
||||
|
||||
// Performs the bus operation provided, which will be one with a
|
||||
// SelectWord or SelectByte operation, stretching it to match the E
|
||||
// bus if VPA is currently asserted.
|
||||
//
|
||||
// TODO: If BERR is asserted, stop here and perform a bus error exception.
|
||||
//
|
||||
// TODO: If VPA is asserted, stretch this cycle.
|
||||
#define CompleteAccess(x) \
|
||||
PerformBusOperation(x)
|
||||
|
||||
// Performs the memory access implied by the announce, perform pair,
|
||||
// honouring DTACK, BERR and VPA as necessary.
|
||||
#define AccessPair(addr, val, announce, perform) \
|
||||
perform.address = announce.address = &addr; \
|
||||
perform.value = &val; \
|
||||
if constexpr (!dtack_is_implicit) { \
|
||||
announce.length = HalfCycles(4); \
|
||||
} \
|
||||
PerformBusOperation(announce); \
|
||||
WaitForDTACK(announce); \
|
||||
CompleteAccess(perform);
|
||||
|
||||
// Reads the data (i.e. non-program) word from addr into val.
|
||||
#define ReadDataWord(addr, val) AccessPair(addr, val, read_word_data_announce, read_word_data)
|
||||
|
||||
// Reads the program (i.e. non-data) word from addr into val.
|
||||
#define ReadProgramWord(val) \
|
||||
AccessPair(program_counter_.l, val, read_program_announce, read_program); \
|
||||
program_counter_.l += 2;
|
||||
|
||||
// Reads one futher word from the program counter and inserts it into
|
||||
// the prefetch queue.
|
||||
#define Prefetch() \
|
||||
prefetch_[0] = prefetch_[1]; \
|
||||
ReadProgramWord(prefetch_[1])
|
||||
|
||||
// Otherwise continue for all time, until back in debt.
|
||||
// Formatting is slightly obtuse here to make this look more like a coroutine.
|
||||
while(true) { switch(state) {
|
||||
while(true) { switch(state_) {
|
||||
|
||||
// Spin in place, one cycle at a time, until one of DTACK,
|
||||
// BERR or VPA is asserted.
|
||||
case State::WaitForDTACK:
|
||||
PerformBusOperation(awaiting_dtack);
|
||||
|
||||
if(dtack_ || berr_ || vpa_) {
|
||||
state_ = post_dtack_state_;
|
||||
}
|
||||
break;
|
||||
|
||||
// Perform the RESET exception, which seeds the stack pointer and program
|
||||
// counter, populates the prefetch queue, and then moves to instruction dispatch.
|
||||
case State::Reset:
|
||||
IdleBus(7); // (n-)*5 nn
|
||||
|
||||
address = 0; ReadDataWord(address, stack_pointers_[7].high); // nF
|
||||
address += 2; ReadDataWord(address, stack_pointers_[7].low); // nf
|
||||
address += 2; ReadDataWord(address, program_counter_.high); // nV
|
||||
address += 2; ReadDataWord(address, program_counter_.low); // nv
|
||||
|
||||
Prefetch(); // np
|
||||
IdleBus(1); // n
|
||||
Prefetch(); // np
|
||||
|
||||
state_ = State::Dispatch;
|
||||
CheckOverrun();
|
||||
break;
|
||||
|
||||
default: assert(false);
|
||||
}}
|
||||
|
||||
#undef CheckOverrun
|
||||
|
@ -9,16 +9,38 @@
|
||||
#ifndef _8000Mk2Storage_h
|
||||
#define _8000Mk2Storage_h
|
||||
|
||||
#include "../../../InstructionSets/M68k/Status.hpp"
|
||||
#include "../../../InstructionSets/M68k/Decoder.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MC68000Mk2 {
|
||||
|
||||
struct ProcessorBase {
|
||||
enum State: int {
|
||||
Reset = -1,
|
||||
Dispatch = -2,
|
||||
WaitForDTACK = -3,
|
||||
};
|
||||
|
||||
HalfCycles time_remaining;
|
||||
int state = State::Reset;
|
||||
HalfCycles time_remaining_;
|
||||
int state_ = State::Reset;
|
||||
int post_dtack_state_ = 0;
|
||||
int is_supervisor_ = 1;
|
||||
|
||||
InstructionSet::M68k::Predecoder<InstructionSet::M68k::Model::M68000> decoder_;
|
||||
InstructionSet::M68k::Preinstruction instruction_;
|
||||
uint16_t opcode_;
|
||||
|
||||
InstructionSet::M68k::Status status_;
|
||||
SlicedInt32 program_counter_;
|
||||
SlicedInt32 registers_[16]; // D0–D7 followed by A0–A7.
|
||||
SlicedInt32 stack_pointers_[2];
|
||||
|
||||
bool dtack_ = false;
|
||||
bool vpa_ = false;
|
||||
bool berr_ = false;
|
||||
|
||||
SlicedInt16 prefetch_[2];
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user