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

Takes a shot at the synchronous bus.

This commit is contained in:
Thomas Harte 2019-05-03 14:20:59 -04:00
parent 857f74b320
commit 291e91375f
3 changed files with 63 additions and 19 deletions

View File

@ -27,8 +27,8 @@ namespace MC68000 {
Concretely, a standard read cycle breaks down into at least two microcycles:
1) a 5 half-cycle length microcycle in which the address strobe is signalled; and
2) a 3 half-cycle length microcycle in which at least one of the data strobes is
1) a 4 half-cycle length microcycle in which the address strobe is signalled; and
2) a 4 half-cycle length microcycle in which at least one of the data strobes is
signalled, and the data bus is sampled.
That is, assuming DTack were signalled when microcycle (1) ended. If not then additional
@ -76,7 +76,15 @@ struct Microcycle {
/// an interrupt it plans to observe. Noted on a real 68000 by all FCs being set to 1.
static const int InterruptAcknowledge = 1 << 8;
/// Represents the state of the 68000's valid memory address line — indicating whether this microcycle
/// is synchronised with the E clock to satisfy a valid peripheral address request.
static const int IsPeripheral = 1 << 9;
/// Contains a valid combination of the various static const int flags, describing the operation
/// performed by this Microcycle.
int operation = 0;
/// Describes the duration of this Microcycle.
HalfCycles length = HalfCycles(4);
/*!
@ -84,11 +92,29 @@ struct Microcycle {
if reading indirectly via an address register, this will indicate the full
value of the address register.
The receiver should ignore bits 0 and 24+.
The receiver should ignore bits 0 and 24+. Use word_address() automatically
to obtain the only the 68000's real address lines, giving a 23-bit address
at word resolution.
*/
const uint32_t *address = nullptr;
/*!
If this is a write cycle, dereference value to get the value loaded onto
the data bus.
If this is a read cycle, write the value on the data bus to it.
Otherwise, this value is undefined.
Byte values are provided via @c value.halves.low. @c value.halves.high is undefined.
This is true regardless of whether the upper or lower byte of a word is being
accessed.
Word values occupy the entirety of @c value.full.
*/
RegisterPair16 *value = nullptr;
/// @returns @c true if two Microcycles are equal; @c false otherwise.
bool operator ==(const Microcycle &rhs) const {
if(value != rhs.value) return false;
if(address != rhs.address) return false;
@ -101,7 +127,7 @@ struct Microcycle {
/*! @returns true if any data select line is active; @c false otherwise. */
inline bool data_select_active() const {
return bool(operation & (SelectWord | SelectByte));
return bool(operation & (SelectWord | SelectByte | InterruptAcknowledge));
}
/*!

View File

@ -97,10 +97,28 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
populate_bus_error_steps(3, get_status(), get_bus_code(), offending_address);
}
// Perform the microcycle.
// Perform the microcycle if it is of non-zero length. If this is an operation that
// would normally strobe one of the data selects and VPA is active, it will also need
// stretching.
if(active_step_->microcycle.length != HalfCycles(0)) {
if(is_peripheral_address_ && active_step_->microcycle.data_select_active()) {
auto cycle_copy = active_step_->microcycle;
cycle_copy.operation |= Microcycle::IsPeripheral;
// Extend length by: (i) distance to next E low, plus (ii) difference between
// current length and a whole E cycle.
cycle_copy.length = HalfCycles(20); // i.e. one E cycle in length.
cycle_copy.length += (e_clock_phase_ + cycles_run_for) % 10;
cycles_run_for +=
cycle_copy.length +
bus_handler_.perform_bus_operation(cycle_copy, is_supervisor_);
} else {
cycles_run_for +=
active_step_->microcycle.length +
bus_handler_.perform_bus_operation(active_step_->microcycle, is_supervisor_);
}
}
/*
PERFORM THE BUS STEP'S ACTION.

View File

@ -3340,10 +3340,10 @@ struct ProcessorStorageConstructor {
op(Action::None, seq(""));
// Perform a single write and then a cycle that will obtain an interrupt vector, or else dictate an autovector or a spurious interrupt.
op(Action::PrepareINT, seq("nw int", { &storage_.precomputed_addresses_[0] }));
op(Action::PrepareINT, seq("n nn nw int", { &storage_.precomputed_addresses_[0] }));
// The reset of the standard trap steps occur here; PrepareINT will set them up according to the vector received.
op(Action::PrepareINTVector, seq("nw nW nV nv np np", { &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] }));
op(Action::PrepareINTVector, seq("nn n nw nW nV nv np np", { &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] }));
// Terminate the sequence.
op();