mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-22 08:16:42 +00:00
Attempt to build fixed operations into type.
This simplifies callees and should make all helper functions automatically able to optimise themselves for fixed operations.
This commit is contained in:
+72
-44
@@ -13,6 +13,8 @@
|
||||
#include "../../Numeric/RegisterSizes.hpp"
|
||||
#include "../../InstructionSets/M68k/RegisterSet.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace CPU::MC68000 {
|
||||
|
||||
using OperationT = unsigned int;
|
||||
@@ -68,13 +70,23 @@ static constexpr OperationT BusGrant = 1 << 12;
|
||||
/// .operation field.
|
||||
static constexpr OperationT DecodeDynamically = NewAddress | SameAddress;
|
||||
|
||||
/*! @returns true if any data select line is active; @c false otherwise. */
|
||||
constexpr bool data_select_active(OperationT operation) {
|
||||
return bool(operation & (SelectWord | SelectByte | InterruptAcknowledge));
|
||||
}
|
||||
// PermitRead and PermitWrite are used as part of the read/write mask
|
||||
// supplied to @c Microcycle::apply; they are picked to be small enough values that
|
||||
// a byte can be used for storage.
|
||||
static constexpr OperationT PermitRead = 1 << 3;
|
||||
static constexpr OperationT PermitWrite = 1 << 4;
|
||||
|
||||
};
|
||||
|
||||
template <OperationT op>
|
||||
struct MicrocycleOperationStorage {
|
||||
static constexpr auto operation = op;
|
||||
};
|
||||
template <>
|
||||
struct MicrocycleOperationStorage<Operation::DecodeDynamically> {
|
||||
OperationT operation = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
A microcycle is an atomic unit of 68000 bus activity — it is a single item large enough
|
||||
fully to specify a sequence of bus events that occur without any possible interruption.
|
||||
@@ -98,13 +110,10 @@ constexpr bool data_select_active(OperationT operation) {
|
||||
of an address-strobing microcycle, it can just supply those periods for accounting and
|
||||
avoid the runtime cost of actual DTack emulation. But such as the bus allows.)
|
||||
*/
|
||||
struct Microcycle {
|
||||
template <OperationT op = Operation::DecodeDynamically>
|
||||
struct Microcycle: public MicrocycleOperationStorage<op> {
|
||||
using OperationT = OperationT;
|
||||
|
||||
/// Contains a valid combination of the various static constexpr int flags, describing the operation
|
||||
/// performed by this Microcycle.
|
||||
OperationT operation = 0;
|
||||
|
||||
/// Describes the duration of this Microcycle.
|
||||
HalfCycles length = HalfCycles(4);
|
||||
|
||||
@@ -135,21 +144,46 @@ struct Microcycle {
|
||||
*/
|
||||
SlicedInt16 *value = nullptr;
|
||||
|
||||
Microcycle(OperationT operation) : operation(operation) {}
|
||||
Microcycle(OperationT operation, HalfCycles length) : operation(operation), length(length) {}
|
||||
Microcycle() {}
|
||||
constexpr Microcycle() noexcept {}
|
||||
constexpr Microcycle(OperationT dynamic_operation) noexcept {
|
||||
if constexpr (op == Operation::DecodeDynamically) {
|
||||
MicrocycleOperationStorage<op>::operation = dynamic_operation;
|
||||
} else {
|
||||
assert(MicrocycleOperationStorage<op>::operation == dynamic_operation);
|
||||
}
|
||||
}
|
||||
constexpr Microcycle(OperationT dynamic_operation, HalfCycles length) noexcept :
|
||||
Microcycle(dynamic_operation), length(length) {}
|
||||
constexpr Microcycle(HalfCycles length) noexcept {
|
||||
static_assert(op != Operation::DecodeDynamically);
|
||||
this->length = length;
|
||||
}
|
||||
|
||||
template <typename MicrocycleRHS>
|
||||
Microcycle &operator =(const MicrocycleRHS &rhs) {
|
||||
static_assert(op == Operation::DecodeDynamically);
|
||||
/* TODO */
|
||||
this->operation = rhs.operation;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @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;
|
||||
if(length != rhs.length) return false;
|
||||
if(operation != rhs.operation) return false;
|
||||
if(this->operation != rhs.operation) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Various inspectors.
|
||||
|
||||
/*! @returns true if any data select line is active; @c false otherwise. */
|
||||
bool data_select_active() const {
|
||||
return bool(this->operation & (Operation::SelectWord | Operation::SelectByte | Operation::InterruptAcknowledge));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns 0 if this byte access wants the low part of a 16-bit word; 8 if it wants the high part,
|
||||
i.e. take a word quantity and shift it right by this amount to get the quantity being
|
||||
@@ -190,14 +224,14 @@ struct Microcycle {
|
||||
@returns non-zero if the 68000 LDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int lower_data_select() const {
|
||||
return (operation & Operation::SelectByte & *address) | (operation & Operation::SelectWord);
|
||||
return (this->operation & Operation::SelectByte & *address) | (this->operation & Operation::SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns non-zero if the 68000 UDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int upper_data_select() const {
|
||||
return (operation & Operation::SelectByte & ~*address) | (operation & Operation::SelectWord);
|
||||
return (this->operation & Operation::SelectByte & ~*address) | (this->operation & Operation::SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -232,9 +266,9 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint32_t host_endian_byte_address() const {
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
return *address & 0xffffff;
|
||||
return *address & 0xff'ffff;
|
||||
#else
|
||||
return (*address ^ (operation & Operation::SelectByte)) & 0xffffff;
|
||||
return (*address ^ (this->operation & Operation::SelectByte)) & 0xff'ffff;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -245,7 +279,7 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint16_t value16() const {
|
||||
const uint16_t values[] = { value->w, uint16_t((value->b << 8) | value->b) };
|
||||
return values[operation & Operation::SelectByte];
|
||||
return values[this->operation & Operation::SelectByte];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -253,7 +287,7 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint8_t value8_high() const {
|
||||
const uint8_t values[] = { uint8_t(value->w >> 8), value->b};
|
||||
return values[operation & Operation::SelectByte];
|
||||
return values[this->operation & Operation::SelectByte];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -268,8 +302,8 @@ struct Microcycle {
|
||||
currently being read. Assumes this is a read cycle.
|
||||
*/
|
||||
forceinline void set_value16(uint16_t v) const {
|
||||
assert(operation & Operation::Read);
|
||||
if(operation & Operation::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = v;
|
||||
} else {
|
||||
value->b = uint8_t(v >> byte_shift());
|
||||
@@ -280,8 +314,8 @@ struct Microcycle {
|
||||
Equivalent to set_value16((v << 8) | 0x00ff).
|
||||
*/
|
||||
forceinline void set_value8_high(uint8_t v) const {
|
||||
assert(operation & Operation::Read);
|
||||
if(operation & Operation::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = uint16_t(0x00ff | (v << 8));
|
||||
} else {
|
||||
value->b = uint8_t(v | byte_mask());
|
||||
@@ -292,20 +326,14 @@ struct Microcycle {
|
||||
Equivalent to set_value16(v | 0xff00).
|
||||
*/
|
||||
forceinline void set_value8_low(uint8_t v) const {
|
||||
assert(operation & Operation::Read);
|
||||
if(operation & Operation::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = 0xff00 | v;
|
||||
} else {
|
||||
value->b = uint8_t(v | untouched_byte_mask());
|
||||
}
|
||||
}
|
||||
|
||||
// PermitRead and PermitWrite are used as part of the read/write mask
|
||||
// supplied to @c apply; they are picked to be small enough values that
|
||||
// a byte can be used for storage.
|
||||
static constexpr OperationT PermitRead = 1 << 3;
|
||||
static constexpr OperationT PermitWrite = 1 << 4;
|
||||
|
||||
/*!
|
||||
Assuming this to be a cycle with a data select active, applies it to @c target
|
||||
subject to the read_write_mask, where 'applies' means:
|
||||
@@ -314,27 +342,27 @@ struct Microcycle {
|
||||
* if this is a word read, reads a word (in the host platform's endianness) from @c target; and
|
||||
* if this is a write, does the converse of a read.
|
||||
*/
|
||||
forceinline void apply(uint8_t *target, OperationT read_write_mask = PermitRead | PermitWrite) const {
|
||||
assert( (operation & (Operation::SelectWord | Operation::SelectByte)) != (Operation::SelectWord | Operation::SelectByte));
|
||||
forceinline void apply(uint8_t *target, OperationT read_write_mask = Operation::PermitRead | Operation::PermitWrite) const {
|
||||
assert( (this->operation & (Operation::SelectWord | Operation::SelectByte)) != (Operation::SelectWord | Operation::SelectByte));
|
||||
|
||||
switch((operation | read_write_mask) & (Operation::SelectWord | Operation::SelectByte | Operation::Read | PermitRead | PermitWrite)) {
|
||||
switch((this->operation | read_write_mask) & (Operation::SelectWord | Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite)) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case Operation::SelectWord | Operation::Read | PermitRead:
|
||||
case Operation::SelectWord | Operation::Read | PermitRead | PermitWrite:
|
||||
case Operation::SelectWord | Operation::Read | Operation::PermitRead:
|
||||
case Operation::SelectWord | Operation::Read | Operation::PermitRead | Operation::PermitWrite:
|
||||
value->w = *reinterpret_cast<uint16_t *>(target);
|
||||
break;
|
||||
case Operation::SelectByte | Operation::Read | PermitRead:
|
||||
case Operation::SelectByte | Operation::Read | PermitRead | PermitWrite:
|
||||
case Operation::SelectByte | Operation::Read | Operation::PermitRead:
|
||||
case Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite:
|
||||
value->b = *target;
|
||||
break;
|
||||
case Operation::SelectWord | PermitWrite:
|
||||
case Operation::SelectWord | PermitWrite | PermitRead:
|
||||
case Operation::SelectWord | Operation::PermitWrite:
|
||||
case Operation::SelectWord | Operation::PermitWrite | Operation::PermitRead:
|
||||
*reinterpret_cast<uint16_t *>(target) = value->w;
|
||||
break;
|
||||
case Operation::SelectByte | PermitWrite:
|
||||
case Operation::SelectByte | PermitWrite | PermitRead:
|
||||
case Operation::SelectByte | Operation::PermitWrite:
|
||||
case Operation::SelectByte | Operation::PermitWrite | Operation::PermitRead:
|
||||
*target = value->b;
|
||||
break;
|
||||
}
|
||||
@@ -357,8 +385,8 @@ class BusHandler {
|
||||
can be used to select an appropriate execution path at compile time. Otherwise
|
||||
cycle.operation must be inspected at runtime.
|
||||
*/
|
||||
template <Microcycle::OperationT operation>
|
||||
HalfCycles perform_bus_operation([[maybe_unused]] const Microcycle &cycle, [[maybe_unused]] int is_supervisor) {
|
||||
template <typename Microcycle>
|
||||
HalfCycles perform_bus_operation(const Microcycle &, [[maybe_unused]] int is_supervisor) {
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,8 +195,8 @@ enum ExecutionState: int {
|
||||
#undef AddressingDispatch
|
||||
|
||||
/// @returns The proper select lines for @c instruction's operand size, assuming it is either byte or word.
|
||||
template <typename InstructionT> Microcycle::OperationT data_select(const InstructionT &instruction) {
|
||||
return Microcycle::OperationT(1 << int(instruction.operand_size()));
|
||||
template <typename InstructionT> OperationT data_select(const InstructionT &instruction) {
|
||||
return OperationT(1 << int(instruction.operand_size()));
|
||||
}
|
||||
|
||||
// MARK: - The state machine.
|
||||
@@ -282,8 +282,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Performs the bus operation and then applies a `Spend` of its length
|
||||
// plus any additional length returned by the bus handler.
|
||||
#define PerformBusOperation(x, op) \
|
||||
delay = bus_handler_.template perform_bus_operation<op>(x, is_supervisor_); \
|
||||
#define PerformBusOperation(x) \
|
||||
delay = bus_handler_.perform_bus_operation(x, is_supervisor_); \
|
||||
Spend(x.length + delay)
|
||||
|
||||
// TODO: the templated operation type to perform_bus_operation is intended to allow a much
|
||||
@@ -292,7 +292,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Performs no bus activity for the specified number of microcycles.
|
||||
#define IdleBus(n) \
|
||||
idle.length = HalfCycles((n) << 2); \
|
||||
PerformBusOperation(idle, 0)
|
||||
PerformBusOperation(idle)
|
||||
|
||||
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
||||
// holding the bus cycle provided.
|
||||
@@ -315,7 +315,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
// (1) wait until end of current 10-cycle window;
|
||||
// (2) run for the next 10-cycle window.
|
||||
#define CompleteAccess(x, op) \
|
||||
#define CompleteAccess(x) \
|
||||
if(berr_) { \
|
||||
RaiseBusOrAddressError(AccessFault, x); \
|
||||
} \
|
||||
@@ -324,11 +324,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
} else { \
|
||||
x.length = HalfCycles(4); \
|
||||
} \
|
||||
PerformBusOperation(x, op)
|
||||
PerformBusOperation(x)
|
||||
|
||||
// Performs the memory access implied by the announce, perform pair,
|
||||
// honouring DTACK, BERR and VPA as necessary.
|
||||
#define AccessPair(val, announce, announce_op, perform, perform_op) \
|
||||
#define AccessPair(val, announce, perform) \
|
||||
perform.value = &val; \
|
||||
if constexpr (!dtack_is_implicit) { \
|
||||
announce.length = HalfCycles(4); \
|
||||
@@ -336,9 +336,9 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
if(*perform.address & (perform.operation >> 1) & 1) { \
|
||||
RaiseBusOrAddressError(AddressError, perform); \
|
||||
} \
|
||||
PerformBusOperation(announce, announce_op); \
|
||||
PerformBusOperation(announce); \
|
||||
WaitForDTACK(announce); \
|
||||
CompleteAccess(perform, perform_op);
|
||||
CompleteAccess(perform);
|
||||
|
||||
// Sets up the next data access size and read flags.
|
||||
#define SetupDataAccess(read_flag, select_flag) \
|
||||
@@ -351,15 +351,15 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Performs the access established by SetupDataAccess into val.
|
||||
#define Access(val) \
|
||||
AccessPair(val, access_announce, Operation::DecodeDynamically, access, Operation::DecodeDynamically)
|
||||
AccessPair(val, access_announce, access)
|
||||
|
||||
// Performs the access established by SetupDataAccess into val.
|
||||
#define AccessOp(val, read_flag, select_flag) \
|
||||
AccessPair(val, access_announce, Operation::NewAddress | Operation::IsData | (read_flag), access, Operation::SameAddress | Operation::IsData | (read_flag) | (select_flag))
|
||||
#define AccessOp(val) \
|
||||
AccessPair(val, access_announce, access)
|
||||
|
||||
// Reads the program (i.e. non-data) word from addr into val.
|
||||
#define ReadProgramWord(val) \
|
||||
AccessPair(val, read_program_announce, ReadProgramAnnounceOperation, read_program, ReadProgramOperation); \
|
||||
AccessPair(val, read_program_announce, read_program); \
|
||||
program_counter_.l += 2;
|
||||
|
||||
// Reads one futher word from the program counter and inserts it into
|
||||
@@ -384,7 +384,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Spin in place, one cycle at a time, until one of DTACK,
|
||||
// BERR or VPA is asserted.
|
||||
BeginState(WaitForDTACK):
|
||||
PerformBusOperation(awaiting_dtack, 0);
|
||||
PerformBusOperation(awaiting_dtack);
|
||||
|
||||
if(dtack_ || berr_ || vpa_) {
|
||||
MoveToStateDynamic(post_dtack_state_);
|
||||
@@ -428,16 +428,16 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = 0;
|
||||
AccessOp(registers_[15].high, Operation::Read, Operation::SelectWord); // nF
|
||||
AccessOp(registers_[15].high); // nF
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(registers_[15].low, Operation::Read, Operation::SelectWord); // nf
|
||||
AccessOp(registers_[15].low); // nf
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
Prefetch(); // np
|
||||
IdleBus(1); // n
|
||||
@@ -457,13 +457,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Push status and current program counter.
|
||||
// Write order is wacky here, but I think it's correct.
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(instruction_address_.low, 0, Operation::SelectWord); // ns
|
||||
AccessOp(instruction_address_.low); // ns
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Operation::SelectWord); // ns
|
||||
AccessOp(captured_status_); // ns
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(instruction_address_.high, 0, Operation::SelectWord); // nS
|
||||
AccessOp(instruction_address_.high); // nS
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
@@ -471,10 +471,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
||||
AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@@ -530,17 +530,17 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// COMPLETE GUESS.
|
||||
temporary_address_.l = program_counter_.l - 4;
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(temporary_address_.low, 0, Operation::SelectWord); // ns [pc.l]
|
||||
AccessOp(temporary_address_.low); // ns [pc.l]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Operation::SelectWord); // ns [sr]
|
||||
AccessOp(captured_status_); // ns [sr]
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(temporary_address_.high, 0, Operation::SelectWord); // nS [pc.h]
|
||||
AccessOp(temporary_address_.high); // nS [pc.h]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
temporary_value_.w = opcode_;
|
||||
AccessOp(temporary_value_.low, 0, Operation::SelectWord); // ns [instruction register]
|
||||
AccessOp(temporary_value_.low); // ns [instruction register]
|
||||
|
||||
// Construct the function code; which is:
|
||||
//
|
||||
@@ -561,13 +561,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
temporary_address_.l = *bus_error_.address;
|
||||
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(temporary_address_.low, 0, Operation::SelectWord); // ns [error address.l]
|
||||
AccessOp(temporary_address_.low); // ns [error address.l]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(temporary_value_.low, 0, Operation::SelectWord); // ns [function code]
|
||||
AccessOp(temporary_value_.low); // ns [function code]
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(temporary_address_.high, 0, Operation::SelectWord); // nS [error address.h]
|
||||
AccessOp(temporary_address_.high); // nS [error address.h]
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
@@ -575,10 +575,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
||||
AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@@ -602,14 +602,14 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Push low part of program counter.
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(instruction_address_.low, 0, Operation::SelectWord); // ns
|
||||
AccessOp(instruction_address_.low); // ns
|
||||
|
||||
// Do the interrupt cycle, to obtain a vector.
|
||||
temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1);
|
||||
interrupt_cycles[0].address = interrupt_cycles[1].address = &temporary_address_.l;
|
||||
interrupt_cycles[0].value = interrupt_cycles[1].value = &temporary_value_.low;
|
||||
PerformBusOperation(interrupt_cycles[0], InterruptCycleOperations[0]);
|
||||
CompleteAccess(interrupt_cycles[1], InterruptCycleOperations[1]); // ni
|
||||
PerformBusOperation(interrupt_cycles[0]);
|
||||
CompleteAccess(interrupt_cycles[1]); // ni
|
||||
|
||||
// If VPA is set, autovector.
|
||||
if(vpa_) {
|
||||
@@ -628,10 +628,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Operation::SelectWord); // ns
|
||||
AccessOp(captured_status_); // ns
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(instruction_address_.high, 0, Operation::SelectWord); // nS
|
||||
AccessOp(instruction_address_.high); // nS
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
@@ -639,10 +639,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(temporary_value_.b << 2);
|
||||
AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@@ -2634,11 +2634,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
tas_cycles[3].value = tas_cycles[4].value = &operand_[0].low;
|
||||
|
||||
// First two parts: the read.
|
||||
PerformBusOperation(tas_cycles[0], TASOperations[0]);
|
||||
CompleteAccess(tas_cycles[1], TASOperations[1]);
|
||||
PerformBusOperation(tas_cycles[0]);
|
||||
CompleteAccess(tas_cycles[1]);
|
||||
|
||||
// Third part: processing time.
|
||||
PerformBusOperation(tas_cycles[2], TASOperations[2]);
|
||||
PerformBusOperation(tas_cycles[2]);
|
||||
|
||||
// Do the actual TAS operation.
|
||||
status_.overflow_flag = status_.carry_flag = 0;
|
||||
@@ -2647,8 +2647,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Final parts: write back.
|
||||
operand_[0].b |= 0x80;
|
||||
PerformBusOperation(tas_cycles[3], TASOperations[3]);
|
||||
CompleteAccess(tas_cycles[4], TASOperations[4]);
|
||||
PerformBusOperation(tas_cycles[3]);
|
||||
CompleteAccess(tas_cycles[4]);
|
||||
|
||||
Prefetch();
|
||||
MoveToStateSpecific(Decode);
|
||||
@@ -2762,7 +2762,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
BeginState(RESET):
|
||||
IdleBus(2);
|
||||
PerformBusOperation(reset_cycle, ResetOperation);
|
||||
PerformBusOperation(reset_cycle);
|
||||
Prefetch();
|
||||
MoveToStateSpecific(Decode);
|
||||
|
||||
@@ -3088,13 +3088,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
captured_interrupt_level_ = bus_interrupt_level_;
|
||||
|
||||
read_program.value = &prefetch_.high;
|
||||
bus_handler_.template perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||
bus_handler_.template perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
||||
program_counter_.l += 2;
|
||||
|
||||
read_program.value = &prefetch_.low;
|
||||
bus_handler_.template perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||
bus_handler_.template perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
||||
program_counter_.l += 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,10 +138,10 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
|
||||
/// Used by some dedicated read-modify-write perform patterns to
|
||||
/// determine the size of the bus operation.
|
||||
Microcycle::OperationT select_flag_ = 0;
|
||||
OperationT select_flag_ = 0;
|
||||
|
||||
// Captured bus/address-error state.
|
||||
Microcycle bus_error_;
|
||||
Microcycle<Operation::DecodeDynamically> bus_error_;
|
||||
|
||||
// Flow controller methods implemented.
|
||||
using Preinstruction = InstructionSet::M68k::Preinstruction;
|
||||
@@ -181,26 +181,26 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
// Some microcycles that will be modified as required and used in the main loop;
|
||||
// the semantics of a switch statement make in-place declarations awkward and
|
||||
// some of these may persist across multiple calls to run_for.
|
||||
Microcycle idle{0};
|
||||
Microcycle<OperationT(0)> idle;
|
||||
|
||||
// Read a program word. All accesses via the program counter are word sized.
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr OperationT
|
||||
ReadProgramAnnounceOperation = Operation::Read | Operation::NewAddress | Operation::IsProgram;
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr OperationT
|
||||
ReadProgramOperation = Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsProgram;
|
||||
Microcycle read_program_announce { ReadProgramAnnounceOperation };
|
||||
Microcycle read_program { ReadProgramOperation };
|
||||
Microcycle<ReadProgramAnnounceOperation> read_program_announce{};
|
||||
Microcycle<ReadProgramOperation> read_program{};
|
||||
|
||||
// Read a data word or byte.
|
||||
Microcycle access_announce {
|
||||
Microcycle<Operation::DecodeDynamically> access_announce {
|
||||
Operation::Read | Operation::NewAddress | Operation::IsData
|
||||
};
|
||||
Microcycle access {
|
||||
Microcycle<Operation::DecodeDynamically> access {
|
||||
Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsData
|
||||
};
|
||||
|
||||
// TAS.
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr OperationT
|
||||
TASOperations[5] = {
|
||||
Operation::Read | Operation::NewAddress | Operation::IsData,
|
||||
Operation::Read | Operation::SameAddress | Operation::IsData | Operation::SelectByte,
|
||||
@@ -208,7 +208,7 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
Operation::SameAddress | Operation::IsData,
|
||||
Operation::SameAddress | Operation::IsData | Operation::SelectByte,
|
||||
};
|
||||
Microcycle tas_cycles[5] = {
|
||||
Microcycle<Operation::DecodeDynamically> tas_cycles[5] = {
|
||||
{ TASOperations[0] },
|
||||
{ TASOperations[1] },
|
||||
{ TASOperations[2] },
|
||||
@@ -217,22 +217,22 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
};
|
||||
|
||||
// Reset.
|
||||
static constexpr Microcycle::OperationT ResetOperation = CPU::MC68000::Operation::Reset;
|
||||
Microcycle reset_cycle { ResetOperation, HalfCycles(248) };
|
||||
static constexpr OperationT ResetOperation = CPU::MC68000::Operation::Reset;
|
||||
Microcycle<ResetOperation> reset_cycle { HalfCycles(248) };
|
||||
|
||||
// Interrupt acknowledge.
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr OperationT
|
||||
InterruptCycleOperations[2] = {
|
||||
Operation::InterruptAcknowledge | Operation::Read | Operation::NewAddress,
|
||||
Operation::InterruptAcknowledge | Operation::Read | Operation::SameAddress | Operation::SelectByte
|
||||
};
|
||||
Microcycle interrupt_cycles[2] = {
|
||||
Microcycle<Operation::DecodeDynamically> interrupt_cycles[2] = {
|
||||
{ InterruptCycleOperations[0] },
|
||||
{ InterruptCycleOperations[1] },
|
||||
};
|
||||
|
||||
// Holding spot when awaiting DTACK/etc.
|
||||
Microcycle awaiting_dtack;
|
||||
Microcycle<Operation::DecodeDynamically> awaiting_dtack;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user