1375 lines
40 KiB
C++
1375 lines
40 KiB
C++
//
|
|
// 6809.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 25/02/2026.
|
|
// Copyright © 2026 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include "Perform.hpp"
|
|
#include "Registers.hpp"
|
|
#include "ClockReceiver/ClockReceiver.hpp"
|
|
|
|
#include "Reflection/Dispatcher.hpp"
|
|
#include "InstructionSets/6809/OperationMapper.hpp"
|
|
|
|
#include "Processors/Bus/PartialAddresses.hpp"
|
|
#include "Processors/Bus/Data.hpp"
|
|
|
|
#include <type_traits>
|
|
|
|
namespace CPU::M6809 {
|
|
|
|
// MARK: - Processor bus signalling.
|
|
|
|
// Defined as BA:BS.
|
|
enum class BusState {
|
|
Normal = 0b00,
|
|
InterruptOrResetAcknowledge = 0b01,
|
|
SyncAcknowledge = 0b10,
|
|
HaltOrBusGrantAcknowledge = 0b11,
|
|
};
|
|
|
|
enum class ReadWrite {
|
|
NoData,
|
|
Read,
|
|
Write,
|
|
};
|
|
constexpr bool is_read(const ReadWrite read_write) {
|
|
return read_write < ReadWrite::Write;
|
|
}
|
|
constexpr bool is_write(const ReadWrite read_write) {
|
|
return read_write >= ReadWrite::Write;
|
|
}
|
|
constexpr Bus::Data::AccessType access_type(const ReadWrite read_write) {
|
|
if(read_write == ReadWrite::NoData) {
|
|
return Bus::Data::AccessType::NoData;
|
|
} else if(is_read(read_write)) {
|
|
return Bus::Data::AccessType::Read;
|
|
} else {
|
|
return Bus::Data::AccessType::Write;
|
|
}
|
|
}
|
|
|
|
enum class BusPhase {
|
|
PreMRDY,
|
|
MRDY,
|
|
PostMRDY,
|
|
FullCycle,
|
|
};
|
|
constexpr bool is_terminal(const BusPhase phase) {
|
|
return phase >= BusPhase::PostMRDY;
|
|
}
|
|
|
|
// Time getter.
|
|
template <typename Timescale>
|
|
static constexpr Timescale duration([[maybe_unused]] const BusPhase phase) {
|
|
if constexpr (std::is_same_v<Timescale, Cycles>) {
|
|
return Cycles(1);
|
|
} else {
|
|
// My reading of the data sheet is that if accessing memory then in each EQ quadrants
|
|
// the 6809 proceeds as:
|
|
//
|
|
// EQ: 00
|
|
// EQ: 01 address becomes valid
|
|
// EQ: 11 data transfer begins
|
|
// ... waiting if MRDY requested
|
|
// EQ: 10
|
|
//
|
|
// So that splits each access into 3/4 of a cycle in which the state of MRDY doesn't matter,
|
|
// arbitrarily more quarters of a cycle in which the processor pauses if MRDY is active,
|
|
// then a final quarter of a cycle to complete the access.
|
|
//
|
|
// Code expectation: some machines won't use MRDY and therefore won't be interested in bookkeeping at
|
|
// quarter-cycle precision. In that case all machine cycles last one cycle long, as per the if constexpr
|
|
// above. Otherwise machines can take the BusPhase handed to them and enquire as to length.
|
|
switch(phase) {
|
|
case BusPhase::PreMRDY: return QuarterCycles(3);
|
|
case BusPhase::MRDY: return QuarterCycles(1);
|
|
case BusPhase::PostMRDY: return QuarterCycles(1);
|
|
case BusPhase::FullCycle: return QuarterCycles(4);
|
|
default: __builtin_unreachable();
|
|
}
|
|
}
|
|
}
|
|
|
|
enum class Line {
|
|
Halt,
|
|
DMABusReq,
|
|
NMI,
|
|
IRQ,
|
|
FIRQ,
|
|
MRDY, // Allows the bus to be stretched in 1/4 cycle increments (on a non-E 6809).
|
|
Reset,
|
|
PowerOnReset,
|
|
};
|
|
|
|
enum class LIC {
|
|
Active, // LIC is active.
|
|
Inactive, // LIC is inactive.
|
|
|
|
InstructionFetch, // LIC is inactive, and this is the first byte of an instruction fetch. LIC's stated purpose is
|
|
// so that observers can detect instruction fetches by watching for its trailing edge. As an
|
|
// emulation nicety, and pragmatically because it's doable without additional cost, this
|
|
// implementation directly signals InstructionFetch so that interested observers don't need to
|
|
// maintain their own state.
|
|
};
|
|
constexpr bool is_active(const LIC lic) {
|
|
return lic == LIC::Active;
|
|
}
|
|
|
|
enum Vector: uint16_t {
|
|
SWI3 = 0xfff2,
|
|
SWI2 = 0xfff4,
|
|
FIRQ = 0xfff6,
|
|
IRQ = 0xfff8,
|
|
SWI = 0xfffa,
|
|
NMI = 0xfffc,
|
|
Reset = 0xfffe,
|
|
};
|
|
|
|
// Missing outputs:
|
|
//
|
|
// AVMA — advanced VMA, i.e. bus will be accessed in the next cycle.
|
|
//
|
|
// clocks: (Q, E, TSC, XTAL, EXTAL)
|
|
|
|
// MARK: - Bus.
|
|
|
|
namespace Address {
|
|
|
|
using Literal = Bus::Address::Literal<uint16_t>;
|
|
template <uint16_t address>
|
|
using Fixed = Bus::Address::Fixed<uint16_t, address>;
|
|
|
|
}
|
|
|
|
namespace Data {
|
|
|
|
using Writeable = Bus::Data::Writeable<uint8_t, false>;
|
|
using NoValue = Bus::Data::NoValue<uint8_t>;
|
|
|
|
}
|
|
|
|
template <ReadWrite read_write>
|
|
using data_t = Bus::Data::data_t<uint8_t, false, access_type(read_write)>;
|
|
|
|
// MARK: - Bus-access helper.
|
|
|
|
/*!
|
|
Interprets @c read_write to perform either a `read<address>` or a `write<address>` on `component`.
|
|
*/
|
|
template <
|
|
int address,
|
|
ReadWrite read_write,
|
|
typename ComponentT
|
|
>
|
|
static void access(ComponentT &component, CPU::M6809::data_t<read_write> value) {
|
|
if constexpr (CPU::M6809::is_read(read_write)) {
|
|
if constexpr (requires { component.operator->(); }) {
|
|
value = component->template read<address>();
|
|
} else {
|
|
value = component.template read<address>();
|
|
}
|
|
} else {
|
|
if constexpr (requires { component.operator->(); }) {
|
|
component->template write<address>(value);
|
|
} else {
|
|
component.template write<address>(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Code-generation choices.
|
|
|
|
enum class PausePrecision {
|
|
/// The 6809 will potentially exit only at the start of each distinct instruction.
|
|
BetweenInstructions,
|
|
/// The 6809 will potentially exit before beginning any bus access.
|
|
BetweenAccesses,
|
|
/// The 6809 will potentially exit before beginning any call to the bus handler; if MRDY is in use then this is more granular than
|
|
/// between accesses — the processor may end its current run during an MRDY pause.
|
|
BetweenBusActions,
|
|
};
|
|
|
|
// MARK: - Processor implementation.
|
|
|
|
template <typename Traits>
|
|
struct Processor {
|
|
template <Line line>
|
|
void set(const bool value) {
|
|
const auto set_exception = [&](const Exception exception) {
|
|
exceptions_ = (exceptions_ & ~uint8_t(exception)) | (value ? uint8_t(exception): 0);
|
|
};
|
|
|
|
// As below, NMI is edge triggered whereas all the rest are level-triggered.
|
|
switch(line) {
|
|
case Line::PowerOnReset: set_exception(Exception::PowerOnReset); break;
|
|
case Line::Reset: set_exception(Exception::Reset); break;
|
|
case Line::NMI: if(value) exceptions_ |= uint8_t(Exception::NMI); break;
|
|
case Line::IRQ: set_exception(Exception::IRQ); break;
|
|
case Line::FIRQ: set_exception(Exception::FIRQ); break;
|
|
case Line::MRDY: mrdy_ = value; break;
|
|
case Line::Halt: set_exception(Exception::Halt); break;
|
|
case Line::DMABusReq: set_exception(Exception::DMABusReq); break;
|
|
}
|
|
}
|
|
|
|
template <Line line>
|
|
bool get() const {
|
|
const auto exception = [&](const Exception exception) {
|
|
return exceptions_ & uint8_t(exception);
|
|
};
|
|
|
|
// As below, NMI is edge triggered whereas all the rest are level-triggered.
|
|
switch(line) {
|
|
case Line::PowerOnReset: return exception(Exception::PowerOnReset);
|
|
case Line::Reset: return exception(Exception::Reset);
|
|
case Line::NMI: return exception(Exception::NMI);
|
|
case Line::IRQ: return exception(Exception::IRQ);
|
|
case Line::FIRQ: return exception(Exception::FIRQ);
|
|
case Line::MRDY: return mrdy_;
|
|
case Line::Halt: return exception(Exception::Halt);
|
|
case Line::DMABusReq: return exception(Exception::DMABusReq);
|
|
}
|
|
}
|
|
|
|
Processor(Traits::BusHandlerT &bus_handler) noexcept : bus_handler_(bus_handler) {}
|
|
|
|
using Timescale = std::conditional_t<Traits::uses_mrdy, QuarterCycles, Cycles>;
|
|
void run_for(const Timescale duration) {
|
|
static constexpr auto FirstCounter = __COUNTER__;
|
|
#define addressing_program(name) int(ResumePoint::Max) + int(name)
|
|
#define access_program(name) int(ResumePoint::Max) + int(AddressingMode::Max) + int(name)
|
|
#define restore_point() \
|
|
(__COUNTER__ - FirstCounter + int(ResumePoint::Max) + int(AddressingMode::Max) + int(AccessType::Max))
|
|
|
|
#define join(a, b) a##b
|
|
#define attach(a, b) join(a, b)
|
|
#define local_label(name) attach(name, __LINE__)
|
|
#define access_label() local_label(repeat)
|
|
|
|
#define check_pause(precision, location) \
|
|
if constexpr (Traits::pause_precision >= precision) { \
|
|
if(time_ <= 0) { \
|
|
resume_point_ = location; \
|
|
return; \
|
|
} \
|
|
}
|
|
|
|
#define read(bus_state, lic, addr, value, ...) { \
|
|
if constexpr (!Traits::uses_mrdy) { \
|
|
time_ -= Cycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::FullCycle, lic, ReadWrite::Read, bus_state> \
|
|
(addr,target_); \
|
|
goto local_label(skipMRDY); \
|
|
} \
|
|
\
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(3); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::PreMRDY, lic, ReadWrite::Read, bus_state> \
|
|
(addr, target_); \
|
|
} \
|
|
\
|
|
static constexpr auto check_mrdy = restore_point(); \
|
|
[[fallthrough]]; \
|
|
case check_mrdy: \
|
|
while(mrdy_) { \
|
|
check_pause(PausePrecision::BetweenBusActions, check_mrdy); \
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::MRDY, lic, ReadWrite::Read, bus_state> \
|
|
(addr, target_); \
|
|
} \
|
|
} \
|
|
\
|
|
static constexpr auto post_mrdy = restore_point(); \
|
|
[[fallthrough]]; \
|
|
case post_mrdy: \
|
|
check_pause(PausePrecision::BetweenBusActions, post_mrdy); \
|
|
\
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::PostMRDY, lic, ReadWrite::Read, bus_state> \
|
|
(addr, target_); \
|
|
} \
|
|
\
|
|
local_label(skipMRDY): \
|
|
value = target_; \
|
|
__VA_ARGS__; \
|
|
}
|
|
|
|
#define write(bus_state, lic, addr, value, ...) { \
|
|
if constexpr (!Traits::uses_mrdy) { \
|
|
time_ -= Cycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::FullCycle, lic, ReadWrite::Write, bus_state> \
|
|
(addr, value); \
|
|
\
|
|
goto local_label(skipMRDY); \
|
|
} \
|
|
\
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(3); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::PreMRDY, lic, ReadWrite::Write, bus_state> \
|
|
(addr, value); \
|
|
} \
|
|
\
|
|
static constexpr auto check_mrdy = restore_point(); \
|
|
[[fallthrough]]; \
|
|
case check_mrdy: \
|
|
while(mrdy_) { \
|
|
check_pause(PausePrecision::BetweenBusActions, check_mrdy); \
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::MRDY, lic, ReadWrite::Write, bus_state> \
|
|
(addr, value); \
|
|
} \
|
|
} \
|
|
\
|
|
static constexpr auto post_mrdy = restore_point(); \
|
|
[[fallthrough]]; \
|
|
case post_mrdy: \
|
|
check_pause(PausePrecision::BetweenBusActions, post_mrdy); \
|
|
\
|
|
if constexpr (Traits::uses_mrdy) { \
|
|
time_ -= QuarterCycles(1); \
|
|
time_ -= \
|
|
bus_handler_. \
|
|
template perform<BusPhase::PostMRDY, lic, ReadWrite::Write, bus_state> \
|
|
(addr, value); \
|
|
} \
|
|
\
|
|
local_label(skipMRDY): \
|
|
__VA_ARGS__; \
|
|
}
|
|
|
|
#define inactive_bus(lic) \
|
|
time_ -= Cycles(1); \
|
|
time_ -= \
|
|
bus_handler_.template perform< \
|
|
BusPhase::FullCycle, \
|
|
lic, \
|
|
ReadWrite::NoData, \
|
|
BusState::HaltOrBusGrantAcknowledge \
|
|
>(Address::Fixed<0xffff>(), Data::NoValue());
|
|
|
|
#define internal_cycles(final_lic, n) { \
|
|
perform_cost_ = (n); \
|
|
if(perform_cost_ == 0) goto local_label(noPerform); \
|
|
\
|
|
static constexpr auto perform_spin = restore_point(); \
|
|
local_label(performSpin): \
|
|
[[fallthrough]]; \
|
|
case perform_spin: \
|
|
\
|
|
check_pause(PausePrecision::BetweenBusActions, perform_spin); \
|
|
time_ -= Cycles(1); \
|
|
\
|
|
if(perform_cost_ == 1) goto local_label(finishPerform); \
|
|
--perform_cost_; \
|
|
\
|
|
time_ -= \
|
|
bus_handler_.template perform< \
|
|
BusPhase::FullCycle, \
|
|
LIC::Inactive, \
|
|
ReadWrite::NoData, \
|
|
BusState::Normal \
|
|
>(Address::Fixed<0xffff>(), Data::NoValue()); \
|
|
goto local_label(performSpin); \
|
|
\
|
|
local_label(finishPerform): \
|
|
time_ -= \
|
|
bus_handler_.template perform< \
|
|
BusPhase::FullCycle, \
|
|
final_lic, \
|
|
ReadWrite::NoData, \
|
|
BusState::Normal \
|
|
>(Address::Fixed<0xffff>(), Data::NoValue()); \
|
|
\
|
|
local_label(noPerform): \
|
|
(void)0; \
|
|
}
|
|
|
|
#define perform_operation(final_lic) { \
|
|
internal_cycles(final_lic, CPU::M6809::perform( \
|
|
operation_.operation, \
|
|
registers_, \
|
|
operand_, \
|
|
[]{} \
|
|
)) \
|
|
}
|
|
|
|
#define addressed_internal_cycle(lic, address) { \
|
|
static constexpr auto access = restore_point(); \
|
|
[[fallthrough]]; case access: \
|
|
check_pause(PausePrecision::BetweenBusActions, access); \
|
|
time_ -= Cycles(1); \
|
|
time_ -= \
|
|
bus_handler_.template perform< \
|
|
BusPhase::FullCycle, \
|
|
lic, \
|
|
ReadWrite::NoData, \
|
|
BusState::Normal \
|
|
>(address, Data::NoValue()); \
|
|
}
|
|
|
|
#define internal_cycle(lic) addressed_internal_cycle(lic, Address::Fixed<0xffff>())
|
|
|
|
#define pull_from(r, stack) \
|
|
read(BusState::Normal, LIC::Inactive, Literal(stack), r, ++(stack))
|
|
|
|
#define pull(r) pull_from(r, registers_.reg<R16::S>())
|
|
|
|
#define push_to(r, stack) \
|
|
-- (stack); \
|
|
write(BusState::Normal, LIC::Inactive, Literal(stack), r);
|
|
|
|
#define push(r) push_to(r, registers_.reg<R16::S>())
|
|
|
|
#define dynamic_goto(r) \
|
|
resume_point_ = (r); \
|
|
break;
|
|
|
|
time_ += duration;
|
|
|
|
using Literal = Address::Literal;
|
|
|
|
InstructionSet::M6809::OperationReturner op_returner;
|
|
InstructionSet::M6809::OperationMapper<InstructionSet::M6809::Page::Page0> op_mapper0;
|
|
InstructionSet::M6809::OperationMapper<InstructionSet::M6809::Page::Page1> op_mapper1;
|
|
InstructionSet::M6809::OperationMapper<InstructionSet::M6809::Page::Page2> op_mapper2;
|
|
|
|
uint8_t opcode = 0;
|
|
while(true) switch(resume_point_) {
|
|
default: {
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
// MARK: - Exceptions.
|
|
|
|
reset:
|
|
registers_.dp = 0;
|
|
exceptions_ &= ~(uint8_t(Exception::NMI) | uint8_t(Exception::PowerOnReset));
|
|
registers_.cc.set<ConditionCode::IRQMask>(true);
|
|
registers_.cc.set<ConditionCode::FIRQMask>(true);
|
|
|
|
reset_spin:
|
|
[[fallthrough]]; case ResumePoint::ResetSpin:
|
|
if(time_ <= 0) {
|
|
resume_point_ = ResumePoint::ResetSpin;
|
|
return;
|
|
}
|
|
addressed_internal_cycle(LIC::Inactive, Address::Fixed<Vector::Reset>())
|
|
if(exceptions_ & (uint8_t(Exception::Halt) | uint8_t(Exception::DMABusReq) | uint8_t(Exception::Reset))) {
|
|
goto reset_spin;
|
|
}
|
|
|
|
addressed_internal_cycle(LIC::Inactive, Address::Fixed<Vector::Reset>())
|
|
addressed_internal_cycle(LIC::Inactive, Address::Fixed<Vector::Reset>())
|
|
addressed_internal_cycle(LIC::Inactive, Address::Fixed<Vector::Reset>())
|
|
|
|
read(
|
|
BusState::InterruptOrResetAcknowledge,
|
|
LIC::Inactive,
|
|
Address::Fixed<Vector::Reset>(),
|
|
registers_.pc.halves.high
|
|
);
|
|
read(
|
|
BusState::InterruptOrResetAcknowledge,
|
|
LIC::Inactive,
|
|
Address::Fixed<Vector::Reset + 1>(),
|
|
registers_.pc.halves.low
|
|
);
|
|
|
|
internal_cycle(LIC::Active);
|
|
|
|
goto fetch_decode;
|
|
|
|
firq:
|
|
internal_cycles(LIC::Inactive, 3);
|
|
|
|
registers_.cc.set<ConditionCode::Entire>(false);
|
|
|
|
operand_ = registers_.reg<R16::PC>();
|
|
push(operand_.halves.low);
|
|
push(operand_.halves.high);
|
|
push(registers_.reg<R8::CC>());
|
|
|
|
address_.full = Vector::FIRQ;
|
|
goto interrupt_dispatch;
|
|
|
|
// MARK: - Fetch/decode.
|
|
|
|
halt:
|
|
inactive_bus(LIC::Inactive);
|
|
|
|
fetch_decode:
|
|
[[fallthrough]];
|
|
case ResumePoint::FetchDecode:
|
|
if(time_ <= 0) {
|
|
resume_point_ = ResumePoint::FetchDecode;
|
|
return;
|
|
}
|
|
|
|
if(exceptions_) {
|
|
// Avoid potential accidental detection of SWIs on the interrupt paths.
|
|
operation_.operation = Operation::None;
|
|
|
|
if(exceptions_ & uint8_t(Exception::Halt)) {
|
|
goto halt;
|
|
}
|
|
|
|
if(exceptions_ & (uint8_t(Exception::PowerOnReset) | uint8_t(Exception::Reset))) {
|
|
address_ = Vector::Reset;
|
|
goto reset;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::NMI)) {
|
|
address_ = Vector::NMI;
|
|
exceptions_ &= ~uint8_t(Exception::NMI);
|
|
goto nmi_irq;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::FIRQ) && !registers_.cc.get<ConditionCode::FIRQMask>()) {
|
|
address_ = Vector::FIRQ;
|
|
goto firq;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::IRQ) && !registers_.cc.get<ConditionCode::IRQMask>()) {
|
|
address_ = Vector::IRQ;
|
|
goto nmi_irq;
|
|
}
|
|
}
|
|
|
|
read(
|
|
BusState::Normal,
|
|
LIC::InstructionFetch,
|
|
Literal(registers_.pc.full),
|
|
opcode,
|
|
++registers_.pc.full
|
|
);
|
|
operation_ = Reflection::dispatch(op_mapper0, opcode, op_returner);
|
|
dynamic_goto(addressing_program(operation_.mode));
|
|
|
|
fetch_decode_page1:
|
|
read(BusState::Normal, LIC::Inactive, Literal(registers_.pc.full), opcode, ++registers_.pc.full);
|
|
operation_ = Reflection::dispatch(op_mapper1, opcode, op_returner);
|
|
dynamic_goto(addressing_program(operation_.mode));
|
|
|
|
fetch_decode_page2:
|
|
read(BusState::Normal, LIC::Inactive,Literal(registers_.pc.full), opcode, ++registers_.pc.full);
|
|
operation_ = Reflection::dispatch(op_mapper2, opcode, op_returner);
|
|
dynamic_goto(addressing_program(operation_.mode));
|
|
|
|
// MARK: - 'Invalid' addressing mode (i.e. invalid operation).
|
|
|
|
case addressing_program(AddressingMode::Illegal):
|
|
// TODO: probably coming here is a mistake; the broad strokes of 'illegal' don't cover different CPU
|
|
// response to different illegal operations.
|
|
goto fetch_decode;
|
|
|
|
// MARK: - Variant addressing mode.
|
|
|
|
case addressing_program(AddressingMode::Variant):
|
|
if(operation_.operation == Operation::Page2) {
|
|
goto fetch_decode_page2;
|
|
} else {
|
|
goto fetch_decode_page1;
|
|
}
|
|
|
|
// MARK: - Inherent addressing mode.
|
|
|
|
case addressing_program(AddressingMode::Inherent):
|
|
if(is_zero_costed(operation_.operation)) {
|
|
goto inherent_lic_fetch;
|
|
}
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
inherent_lic_fetch:
|
|
addressed_internal_cycle(LIC::Active, Address::Literal(registers_.pc.full));
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
// MARK: - Immediate and relative addressing modes.
|
|
|
|
case addressing_program(AddressingMode::Relative8):
|
|
if(operation_.operation == Operation::BSR) {
|
|
goto bsr;
|
|
}
|
|
[[fallthrough]];
|
|
case addressing_program(AddressingMode::Immediate8):
|
|
if(is_zero_costed(operation_.operation)) {
|
|
goto immediate8_lic_fetch;
|
|
}
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
immediate8_lic_fetch:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Active,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
case addressing_program(AddressingMode::Relative16):
|
|
if(operation_.operation == Operation::LBSR) {
|
|
goto lbsr;
|
|
}
|
|
[[fallthrough]];
|
|
case addressing_program(AddressingMode::Immediate16):
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.high,
|
|
++registers_.pc.full
|
|
);
|
|
|
|
if(is_zero_costed(operation_.operation)) {
|
|
goto immediate16_lic_fetch;
|
|
}
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
immediate16_lic_fetch:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Active,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
|
|
// MARK: - Direct addressing mode.
|
|
|
|
case addressing_program(AddressingMode::Direct):
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
address_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
address_.halves.high = registers_.reg<R8::DP>();
|
|
|
|
if(operation_.operation == Operation::JMP) {
|
|
goto internal_lic_break;
|
|
}
|
|
internal_cycle(LIC::Inactive);
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
internal_lic_break:
|
|
internal_cycle(LIC::Active);
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
// MARK: - Extended addressing mode.
|
|
|
|
case addressing_program(AddressingMode::Extended):
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
address_.halves.high,
|
|
++registers_.pc.full
|
|
);
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
address_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
|
|
if(operation_.operation == Operation::JMP) {
|
|
goto internal_lic_break;
|
|
}
|
|
internal_cycle(LIC::Inactive);
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
|
|
// MARK: - Indexed addressing mode.
|
|
|
|
// TODO: internal cycles.
|
|
case addressing_program(AddressingMode::Indexed):
|
|
|
|
//
|
|
// Determine target address.
|
|
//
|
|
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
indexer_ = IndexedAddressDecoder(operand_.halves.low);
|
|
|
|
if(!indexer_.required_continuation()) {
|
|
goto no_continuation;
|
|
} else if(indexer_.required_continuation() == 1) {
|
|
operand_.halves.high = 0;
|
|
goto indexed_getlow;
|
|
}
|
|
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.high,
|
|
++registers_.pc.full
|
|
);
|
|
|
|
indexed_getlow:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
indexer_.set_continuation(operand_.full);
|
|
goto continue_indexed;
|
|
|
|
no_continuation:
|
|
if(is_zero_costed(operation_.operation) && !indexer_.indirect() && !indexer_.address_cost()) {
|
|
goto lic_internal_complete_address;
|
|
}
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
|
|
continue_indexed:
|
|
address_.full = indexer_.address(registers_);
|
|
|
|
if(is_zero_costed(operation_.operation) && !indexer_.indirect()) {
|
|
goto lic_complete_address;
|
|
}
|
|
internal_cycles(LIC::Inactive, indexer_.address_cost());
|
|
if(!indexer_.indirect()) {
|
|
goto complete_address;
|
|
}
|
|
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.high, ++address_.full);
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.low, ++address_.full);
|
|
|
|
address_ = operand_;
|
|
if(operation_.operation == Operation::JMP) {
|
|
goto internal_lic_break;
|
|
}
|
|
internal_cycle(LIC::Inactive);
|
|
|
|
complete_address:
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
lic_complete_address:
|
|
internal_cycles(LIC::Active, indexer_.address_cost());
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
lic_internal_complete_address:
|
|
addressed_internal_cycle(LIC::Active, Address::Literal(registers_.pc.full));
|
|
address_.full = indexer_.address(registers_);
|
|
dynamic_goto(access_program(operation_.type));
|
|
|
|
// MARK: - 'Specialised' addresing mode (i.e. irregulars0.
|
|
|
|
case addressing_program(AddressingMode::Specialised):
|
|
switch(operation_.operation) {
|
|
case Operation::PULU:
|
|
case Operation::PULS:
|
|
goto pull;
|
|
|
|
case Operation::PSHU:
|
|
case Operation::PSHS:
|
|
goto push;
|
|
|
|
case Operation::SYNC: goto sync;
|
|
case Operation::CWAI: goto cwai;
|
|
case Operation::RTS: goto rts;
|
|
case Operation::RTI: goto rti;
|
|
|
|
case Operation::SWI:
|
|
address_ = Vector::SWI;
|
|
goto swi_reset;
|
|
|
|
case Operation::SWI2:
|
|
address_ = Vector::SWI2;
|
|
goto swi_reset;
|
|
|
|
case Operation::SWI3:
|
|
address_.full = Vector::SWI3;
|
|
goto swi_reset;
|
|
|
|
case Operation::RESET:
|
|
address_.full = Vector::Reset;
|
|
goto swi_reset;
|
|
|
|
default: __builtin_unreachable();
|
|
}
|
|
|
|
// MARK: - PULU/PULS.
|
|
|
|
pull:
|
|
stack_ =
|
|
operation_.operation == Operation::PULU ? ®isters_.reg<R16::U>(): ®isters_.reg<R16::S>();
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
|
|
internal_cycles(LIC::Inactive, 2);
|
|
|
|
if(!(operand_.halves.low & 0b0000'0001)) {
|
|
goto no_pull_cc;
|
|
}
|
|
pull_from(registers_.reg<R8::CC>(), *stack_);
|
|
no_pull_cc:
|
|
|
|
if(!(operand_.halves.low & 0b0000'0010)) {
|
|
goto no_pull_a;
|
|
}
|
|
pull_from(registers_.reg<R8::A>(), *stack_);
|
|
no_pull_a:
|
|
|
|
if(!(operand_.halves.low & 0b0000'0100)) {
|
|
goto no_pull_b;
|
|
}
|
|
pull_from(registers_.reg<R8::B>(), *stack_);
|
|
no_pull_b:
|
|
|
|
if(!(operand_.halves.low & 0b0000'1000)) {
|
|
goto no_pull_dp;
|
|
}
|
|
pull_from(registers_.reg<R8::DP>(), *stack_);
|
|
no_pull_dp:
|
|
|
|
if(!(operand_.halves.low & 0b0001'0000)) {
|
|
goto no_pull_x;
|
|
}
|
|
pull_from(address_.halves.high, *stack_);
|
|
pull_from(address_.halves.low, *stack_);
|
|
registers_.reg<R16::X>() = address_.full;
|
|
no_pull_x:
|
|
|
|
if(!(operand_.halves.low & 0b0010'0000)) {
|
|
goto no_pull_y;
|
|
}
|
|
pull_from(address_.halves.high, *stack_);
|
|
pull_from(address_.halves.low, *stack_);
|
|
registers_.reg<R16::Y>() = address_.full;
|
|
no_pull_y:
|
|
|
|
if(!(operand_.halves.low & 0b0100'0000)) {
|
|
goto no_pull_s;
|
|
}
|
|
pull_from(address_.halves.high, *stack_);
|
|
pull_from(address_.halves.low, *stack_);
|
|
(operation_.operation == Operation::PULU ? registers_.reg<R16::S>() : registers_.reg<R16::U>())
|
|
= address_.full;
|
|
no_pull_s:
|
|
|
|
if(!(operand_.halves.low & 0b1000'0000)) {
|
|
goto end_pull;
|
|
}
|
|
pull_from(address_.halves.high, *stack_);
|
|
pull_from(address_.halves.low, *stack_);
|
|
registers_.reg<R16::PC>() = address_.full;
|
|
|
|
end_pull:
|
|
internal_cycle(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
// MARK: - PHSH/PSHS.
|
|
|
|
push:
|
|
stack_ =
|
|
operation_.operation == Operation::PSHU ? ®isters_.reg<R16::U>(): ®isters_.reg<R16::S>();
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
|
|
internal_cycles(LIC::Inactive, 2);
|
|
if(!operand_.halves.low) {
|
|
goto terminate_push_no_activity;
|
|
}
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(*stack_));
|
|
|
|
if(!(operand_.halves.low & 0b1000'0000)) {
|
|
goto no_push_pc;
|
|
}
|
|
address_.full = registers_.reg<R16::PC>();
|
|
push_to(address_.halves.low, *stack_);
|
|
if(!(operand_.halves.low & 0b0111'1111)) {
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(address_.halves.high, *stack_);
|
|
|
|
no_push_pc:
|
|
if(!(operand_.halves.low & 0b0100'0000)) {
|
|
goto no_push_s;
|
|
}
|
|
address_.full =
|
|
operation_.operation == Operation::PSHU ? registers_.reg<R16::S>() : registers_.reg<R16::U>();
|
|
push_to(address_.halves.low, *stack_);
|
|
if(!(operand_.halves.low & 0b0011'1111)) {
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(address_.halves.high, *stack_);
|
|
|
|
no_push_s:
|
|
if(!(operand_.halves.low & 0b0010'0000)) {
|
|
goto no_push_y;
|
|
}
|
|
address_.full = registers_.reg<R16::Y>();
|
|
push_to(address_.halves.low, *stack_);
|
|
if(!(operand_.halves.low & 0b0001'1111)) {
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(address_.halves.high, *stack_);
|
|
|
|
no_push_y:
|
|
if(!(operand_.halves.low & 0b0001'0000)) {
|
|
goto no_push_x;
|
|
}
|
|
address_.full = registers_.reg<R16::X>();
|
|
push_to(address_.halves.low, *stack_);
|
|
if(!(operand_.halves.low & 0b0000'1111)) {
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(address_.halves.high, *stack_);
|
|
|
|
no_push_x:
|
|
if(!(operand_.halves.low & 0b000'1000)) {
|
|
goto no_push_dp;
|
|
}
|
|
if(!(operand_.halves.low & 0b0000'0111)) {
|
|
address_.halves.high = registers_.reg<R8::DP>();
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(registers_.reg<R8::DP>(), *stack_);
|
|
|
|
no_push_dp:
|
|
if(!(operand_.halves.low & 0b000'0100)) {
|
|
goto no_push_b;
|
|
}
|
|
if(!(operand_.halves.low & 0b0000'0011)) {
|
|
address_.halves.high = registers_.reg<R8::B>();
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(registers_.reg<R8::B>(), *stack_);
|
|
|
|
no_push_b:
|
|
if(!(operand_.halves.low & 0b000'0010)) {
|
|
goto no_push_a;
|
|
}
|
|
if(!(operand_.halves.low & 0b0000'0001)) {
|
|
address_.halves.high = registers_.reg<R8::A>();
|
|
goto terminate_push_address_high;
|
|
}
|
|
push_to(registers_.reg<R8::A>(), *stack_);
|
|
|
|
no_push_a:
|
|
if(!(operand_.halves.low & 0b000'0001)) {
|
|
goto fetch_decode;
|
|
}
|
|
(*stack_)--; write(BusState::Normal, LIC::Active, Literal(*stack_), registers_.reg<R8::CC>());
|
|
goto fetch_decode;
|
|
|
|
terminate_push_address_high:
|
|
(*stack_)--; write(BusState::Normal, LIC::Active, Literal(*stack_), address_.halves.high);
|
|
goto fetch_decode;
|
|
|
|
terminate_push_no_activity:
|
|
addressed_internal_cycle(LIC::Active, Address::Literal(*stack_));
|
|
goto fetch_decode;
|
|
|
|
// MARK: - Stack-related control flow.
|
|
|
|
rti:
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
pull(registers_.cc);
|
|
if(!registers_.cc.get<ConditionCode::Entire>()) {
|
|
goto rti_not_entire;
|
|
}
|
|
|
|
pull(registers_.reg<R8::A>());
|
|
pull(registers_.reg<R8::B>());
|
|
pull(registers_.reg<R8::DP>());
|
|
|
|
pull(address_.halves.high);
|
|
pull(address_.halves.low);
|
|
registers_.reg<R16::X>() = address_.full;
|
|
|
|
pull(address_.halves.high);
|
|
pull(address_.halves.low);
|
|
registers_.reg<R16::Y>() = address_.full;
|
|
|
|
pull(address_.halves.high);
|
|
pull(address_.halves.low);
|
|
registers_.reg<R16::U>() = address_.full;
|
|
|
|
rti_not_entire:
|
|
pull(registers_.pc.halves.high);
|
|
pull(registers_.pc.halves.low);
|
|
internal_cycle(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
rts:
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
goto rti_not_entire;
|
|
|
|
bsr:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
address_.full = uint16_t(registers_.pc.full + int8_t(operand_.halves.low));
|
|
internal_cycle(LIC::Inactive);
|
|
goto jsr;
|
|
|
|
lbsr:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.high,
|
|
++registers_.pc.full
|
|
);
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
address_.full = registers_.pc.full + operand_.full;
|
|
internal_cycles(LIC::Inactive, 2);
|
|
goto jsr;
|
|
|
|
case ResumePoint::CWAISpin:
|
|
finish_cwai:
|
|
if(time_ <= 0) {
|
|
resume_point_ = ResumePoint::CWAISpin;
|
|
return;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::NMI)) {
|
|
address_ = Vector::NMI;
|
|
goto interrupt_dispatch;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::FIRQ) && !registers_.cc.get<ConditionCode::FIRQMask>()) {
|
|
address_ = Vector::FIRQ;
|
|
goto interrupt_dispatch;
|
|
}
|
|
|
|
if(exceptions_ & uint8_t(Exception::IRQ) && !registers_.cc.get<ConditionCode::IRQMask>()) {
|
|
address_ = Vector::IRQ;
|
|
goto interrupt_dispatch;
|
|
}
|
|
|
|
time_ -= Cycles(1);
|
|
time_ -=
|
|
bus_handler_.template perform<
|
|
BusPhase::FullCycle,
|
|
LIC::Inactive,
|
|
ReadWrite::NoData,
|
|
BusState::Normal
|
|
>(Address::Fixed<0xffff>(), Data::NoValue());
|
|
|
|
goto finish_cwai;
|
|
|
|
cwai:
|
|
read(
|
|
BusState::Normal,
|
|
LIC::Inactive,
|
|
Literal(registers_.pc.full),
|
|
operand_.halves.low,
|
|
++registers_.pc.full
|
|
);
|
|
CPU::M6809::perform(Operation::ANDCC, registers_, operand_);
|
|
/* Fallthrough. */
|
|
|
|
swi_reset:
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
goto exception;
|
|
|
|
nmi_irq:
|
|
internal_cycles(LIC::Inactive, 2);
|
|
goto exception;
|
|
|
|
exception:
|
|
internal_cycle(LIC::Inactive);
|
|
if(operation_.operation != Operation::RESET) {
|
|
registers_.cc.set<ConditionCode::Entire>(true);
|
|
}
|
|
|
|
operand_ = registers_.reg<R16::PC>();
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.low);
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.high);
|
|
|
|
operand_ = registers_.reg<R16::U>();
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.low);
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.high);
|
|
|
|
operand_ = registers_.reg<R16::Y>();
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.low);
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.high);
|
|
|
|
operand_ = registers_.reg<R16::X>();
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.low);
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), operand_.halves.high);
|
|
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), registers_.reg<R8::DP>());
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), registers_.reg<R8::B>());
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), registers_.reg<R8::A>());
|
|
-- registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), registers_.reg<R8::CC>());
|
|
|
|
if(operation_.operation == Operation::CWAI) {
|
|
goto finish_cwai;
|
|
}
|
|
|
|
interrupt_dispatch:
|
|
if(operation_.operation != Operation::SWI2 && operation_.operation != Operation::SWI3) {
|
|
registers_.cc.set<ConditionCode::IRQMask>(true);
|
|
registers_.cc.set<ConditionCode::FIRQMask>(true);
|
|
}
|
|
|
|
internal_cycle(LIC::Inactive);
|
|
read(
|
|
BusState::InterruptOrResetAcknowledge,
|
|
LIC::Inactive,
|
|
Literal(address_.full),
|
|
registers_.pc.halves.high,
|
|
++address_.full
|
|
);
|
|
read(
|
|
BusState::InterruptOrResetAcknowledge,
|
|
LIC::Inactive,
|
|
Literal(address_.full),
|
|
registers_.pc.halves.low,
|
|
++address_.full
|
|
);
|
|
internal_cycle(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
sync:
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(registers_.pc.full));
|
|
[[fallthrough]]; case ResumePoint::Sync:
|
|
// Always consider taking a break here, regardless of selected pause precision; otherwise there's
|
|
// a risk of never exiting.
|
|
if(time_ <= 0) {
|
|
resume_point_ = ResumePoint::Sync;
|
|
return;
|
|
}
|
|
|
|
time_ -= Cycles(1);
|
|
time_ -=
|
|
bus_handler_.template perform<
|
|
BusPhase::FullCycle,
|
|
LIC::Inactive,
|
|
ReadWrite::NoData,
|
|
BusState::SyncAcknowledge
|
|
>(Address::Fixed<0xffff>(), Data::NoValue());
|
|
|
|
if(!(exceptions_ & (uint8_t(Exception::NMI) | uint8_t(Exception::IRQ) | uint8_t(Exception::FIRQ)))) {
|
|
goto sync;
|
|
}
|
|
|
|
// Per AN-865 there is a one-cycle latency in response.
|
|
internal_cycle(LIC::Active);
|
|
|
|
goto fetch_decode;
|
|
|
|
//
|
|
// Access atoms.
|
|
//
|
|
case access_program(AccessType::LEA):
|
|
operand_.full = address_.full;
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::JSR):
|
|
jsr:
|
|
addressed_internal_cycle(LIC::Inactive, Address::Literal(address_.full));
|
|
internal_cycle(LIC::Inactive);
|
|
|
|
--registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Inactive, Literal(registers_.reg<R16::S>()), registers_.pc.halves.low);
|
|
--registers_.reg<R16::S>();
|
|
write(BusState::Normal, LIC::Active, Literal(registers_.reg<R16::S>()), registers_.pc.halves.high);
|
|
registers_.reg<R16::PC>() = address_.full;
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::Read8):
|
|
if(is_zero_costed(operation_.operation)) {
|
|
goto read8_lic_fetch;
|
|
}
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.low);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
read8_lic_fetch:
|
|
read(BusState::Normal, LIC::Active, Literal(address_.full), operand_.halves.low);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::Read16):
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.high, ++address_.full);
|
|
|
|
if(is_zero_costed(operation_.operation)) {
|
|
goto read16_lic_fetch;
|
|
}
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.low);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
read16_lic_fetch:
|
|
read(BusState::Normal, LIC::Active, Literal(address_.full), operand_.halves.low);
|
|
perform_operation(LIC::Active);
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::Write8):
|
|
perform_operation(LIC::Inactive);
|
|
write(BusState::Normal, LIC::Active, Literal(address_.full), operand_.halves.low);
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::Write16):
|
|
perform_operation(LIC::Inactive);
|
|
write(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.high, ++address_.full);
|
|
write(BusState::Normal, LIC::Active, Literal(address_.full), operand_.halves.low);
|
|
goto fetch_decode;
|
|
|
|
case access_program(AccessType::Modify8):
|
|
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.low);
|
|
perform_operation(LIC::Inactive);
|
|
internal_cycle(LIC::Inactive);
|
|
write(BusState::Normal, LIC::Active, Literal(address_.full), operand_.halves.low);
|
|
goto fetch_decode;
|
|
}
|
|
|
|
#undef addressing_program
|
|
#undef access_program
|
|
#undef restore_point
|
|
#undef join
|
|
#undef attach
|
|
#undef local_label
|
|
#undef access_label
|
|
#undef check_pause
|
|
#undef read
|
|
#undef write
|
|
#undef inactive_bus
|
|
#undef internal_cycles
|
|
#undef perform_operation
|
|
#undef addressed_internal_cycle
|
|
#undef pull
|
|
#undef pull_from
|
|
#undef push
|
|
#undef push_to
|
|
#undef dynamic_goto
|
|
}
|
|
|
|
Registers ®isters() {
|
|
return registers_;
|
|
}
|
|
|
|
private:
|
|
Traits::BusHandlerT &bus_handler_;
|
|
Timescale time_;
|
|
|
|
enum ResumePoint {
|
|
FetchDecode,
|
|
ResetSpin,
|
|
CWAISpin,
|
|
Sync,
|
|
Max,
|
|
};
|
|
using Operation = InstructionSet::M6809::Operation;
|
|
using AddressingMode = InstructionSet::M6809::AddressingMode;
|
|
using AccessType = InstructionSet::M6809::AccessType;
|
|
|
|
int resume_point_ = ResumePoint::FetchDecode;
|
|
InstructionSet::M6809::OperationReturner::MetaOperation operation_;
|
|
Registers registers_;
|
|
Cycles perform_cost_;
|
|
|
|
enum class Exception: uint8_t {
|
|
Reset = 1 << 0,
|
|
PowerOnReset = 1 << 1,
|
|
NMI = 1 << 2,
|
|
IRQ = 1 << 3,
|
|
FIRQ = 1 << 4,
|
|
Halt = 1 << 5,
|
|
DMABusReq = 1 << 6, // TODO: implement.
|
|
};
|
|
uint8_t exceptions_ = uint8_t(Exception::PowerOnReset);
|
|
bool mrdy_ = false;
|
|
|
|
// Transient storage.
|
|
Data::Writeable target_;
|
|
RegisterPair16 operand_;
|
|
RegisterPair16 address_;
|
|
IndexedAddressDecoder indexer_;
|
|
uint16_t *stack_ = nullptr;
|
|
};
|
|
}
|