1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-18 04:28:57 +00:00
CLK/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp

2648 lines
79 KiB
C++
Raw Normal View History

2022-05-16 15:44:16 +00:00
//
// 68000Mk2Implementation.hpp
// Clock Signal
//
// Created by Thomas Harte on 16/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef _8000Mk2Implementation_h
#define _8000Mk2Implementation_h
#include <cassert>
#include <cstdio>
#include "../../../InstructionSets/M68k/ExceptionVectors.hpp"
2022-05-16 15:44:16 +00:00
namespace CPU {
namespace MC68000Mk2 {
/// States for the state machine which are named by
/// me for their purpose rather than automatically by file position.
/// These are negative to avoid ambiguity with the other group.
enum ExecutionState: int {
Reset = std::numeric_limits<int>::min(),
Decode,
WaitForDTACK,
/// Perform the proper sequence to fetch a byte or word operand.
2022-05-19 14:47:57 +00:00
FetchOperand_bw,
/// Perform the proper sequence to fetch a long-word operand.
2022-05-19 14:47:57 +00:00
FetchOperand_l,
StoreOperand,
StoreOperand_bw,
StoreOperand_l,
StandardException,
BusOrAddressErrorException,
2022-05-24 14:53:59 +00:00
DoInterrupt,
// Specific addressing mode fetches.
//
// Additional context here is that I'm very much on the fence but
// for now am telling myself:
//
// (1) the overwhelming majority of instructions that need an
// effective address calculation use it for an operand read
// immediately afterwards, so keeping those things bound
// avoids a large number of conditional branches; and
// (2) making a decision between byte/word and long-word once at
// the outset also saves a conditional for any two-operand
// instructions (which is also the majority); but
// (3) some instructions do just need the address calculation —
// LEA and PEA are obvious examples, but are not the
// exhaustive list — so a third route just to do the
// calculation is necessary.
//
// My internal dialogue then argues that each of these is actually
// a small amount of code, so the need manually to duplicate (per
// the control-flow constraints of using a switch as a coroutine)
// isn't too ugly. Possibly even less ugly than pulling things out
// with a macro, especially for debugging.
//
// Further consideration may be necessary. Especially once this is
// up on its feet and profiling becomes an option.
2022-05-19 14:47:57 +00:00
FetchAddressRegisterIndirect_bw,
FetchAddressRegisterIndirectWithPostincrement_bw,
FetchAddressRegisterIndirectWithPredecrement_bw,
FetchAddressRegisterIndirectWithDisplacement_bw,
FetchAddressRegisterIndirectWithIndex8bitDisplacement_bw,
FetchProgramCounterIndirectWithDisplacement_bw,
FetchProgramCounterIndirectWithIndex8bitDisplacement_bw,
FetchAbsoluteShort_bw,
FetchAbsoluteLong_bw,
FetchImmediateData_bw,
FetchAddressRegisterIndirect_l,
FetchAddressRegisterIndirectWithPostincrement_l,
FetchAddressRegisterIndirectWithPredecrement_l,
FetchAddressRegisterIndirectWithDisplacement_l,
FetchAddressRegisterIndirectWithIndex8bitDisplacement_l,
FetchProgramCounterIndirectWithDisplacement_l,
FetchProgramCounterIndirectWithIndex8bitDisplacement_l,
FetchAbsoluteShort_l,
FetchAbsoluteLong_l,
FetchImmediateData_l,
CalcEffectiveAddress, // -
CalcAddressRegisterIndirect, // -
CalcAddressRegisterIndirectWithPostincrement, // -
CalcAddressRegisterIndirectWithPredecrement, // -
CalcAddressRegisterIndirectWithDisplacement, // np
CalcAddressRegisterIndirectWithIndex8bitDisplacement, // n np n
CalcProgramCounterIndirectWithDisplacement, // np
CalcProgramCounterIndirectWithIndex8bitDisplacement, // n np n
CalcAbsoluteShort, // np
CalcAbsoluteLong, // np np
// Various forms of perform; each of these will
// perform the current instruction, then do the
// indicated bus cycle.
Perform_np,
Perform_np_n,
Perform_np_nn,
2022-05-23 12:46:06 +00:00
MOVE,
MOVE_predec,
2022-05-25 12:15:18 +00:00
MOVE_predec_l,
MOVE_prefetch_decode,
2022-05-23 12:46:06 +00:00
MOVE_complete,
MOVE_complete_l,
2022-05-19 19:19:00 +00:00
TwoOp_Predec_bw,
TwoOp_Predec_l,
CHK,
CHK_no_trap,
CHK_was_over,
CHK_was_under,
Scc_Dn,
Scc_Dn_did_not_set,
Scc_Dn_did_set,
2022-05-20 15:32:06 +00:00
DBcc,
DBcc_branch_taken,
DBcc_condition_true,
DBcc_counter_overflow,
2022-05-20 16:04:43 +00:00
Bccb,
Bccw,
2022-05-20 16:04:43 +00:00
Bcc_branch_taken,
Bccb_branch_not_taken,
Bccw_branch_not_taken,
2022-05-25 20:32:02 +00:00
BSRb,
BSRw,
2022-05-20 16:58:45 +00:00
2022-05-22 11:16:38 +00:00
JSRJMPAddressRegisterIndirect,
JSRJMPAddressRegisterIndirectWithDisplacement,
JSRJMPAddressRegisterIndirectWithIndex8bitDisplacement,
JSRJMPProgramCounterIndirectWithDisplacement,
JSRJMPProgramCounterIndirectWithIndex8bitDisplacement,
JSRJMPAbsoluteShort,
JSRJMPAbsoluteLong,
JSR, JMP,
2022-05-21 14:29:36 +00:00
2022-05-20 16:58:45 +00:00
BCHG_BSET_Dn,
BCLR_Dn,
2022-05-20 18:22:32 +00:00
MOVEPtoM_w,
MOVEPtoM_l,
MOVEPtoR_w,
MOVEPtoR_l,
LogicalToSR,
2022-05-20 22:48:19 +00:00
MOVEMtoR, MOVEMtoR_l_read, MOVEMtoR_w_read, MOVEMtoR_finish,
2022-05-21 12:17:39 +00:00
MOVEMtoM,
MOVEMtoM_l_write, MOVEMtoM_w_write,
MOVEMtoM_l_write_predec, MOVEMtoM_w_write_predec,
MOVEMtoM_finish,
DIVU_DIVS,
2022-05-23 13:29:19 +00:00
Perform_idle_dyamic_Dn,
2022-05-22 12:29:12 +00:00
LEA,
2022-05-22 15:27:38 +00:00
PEA,
TAS,
2022-05-22 23:45:22 +00:00
MOVEtoCCRSR,
2022-05-23 01:16:38 +00:00
RTR,
RTE,
RTS,
LINKw,
2022-05-23 14:27:44 +00:00
UNLINK,
RESET,
NOP,
2022-05-24 14:25:40 +00:00
STOP,
2022-05-24 19:14:20 +00:00
TRAP,
TRAPV,
};
// MARK: - The state machine.
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.
e_clock_phase_ += duration;
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_ = __COUNTER__+1; return; } [[fallthrough]]; case __COUNTER__:
// 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()
2022-05-22 11:39:16 +00:00
// Moves directly to state x, which must be a compile-time constant.
#define MoveToStateSpecific(x) goto x;
// Moves to state x by dynamic dispatch; x can be a regular variable.
#define MoveToStateDynamic(x) { state_ = x; continue; }
// Sets the start position for state x.
#define BeginState(x) case ExecutionState::x: [[maybe_unused]] x
//
// So basic structure is, in general:
//
// BeginState(Action):
// do_something();
// Spend(20);
// do_something_else();
// Spend(10);
// do_a_third_thing();
// Spend(30);
// MoveToState(next_action);
//
// Additional notes:
//
// Action and all equivalents should be negative values, since the
2022-05-17 00:38:17 +00:00
// switch-for-computed-goto-for-a-coroutine structure uses __COUNTER__* for
// its invented entry- and exit-points, meaning that negative numbers are
// the easiest group that is safely definitely never going to collide.
2022-05-17 00:38:17 +00:00
//
// (* an extension supported by at least GCC, Clang and MSVC)
// Spare containers:
2022-05-17 00:38:17 +00:00
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) \
2022-05-25 20:05:45 +00:00
idle.length = HalfCycles((n) << 2); \
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); \
2022-05-17 00:38:17 +00:00
post_dtack_state_ = __COUNTER__+1; \
state_ = ExecutionState::WaitForDTACK; \
break; \
} \
2022-05-17 00:38:17 +00:00
[[fallthrough]]; case __COUNTER__:
// 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 or seguing elsewhere if a bus
// error is signalled or an adress error observed.
//
// E clock behaviour implemented, which I think is correct:
//
// (1) wait until end of current 10-cycle window;
// (2) run for the next 10-cycle window.
#define CompleteAccess(x) \
if(berr_ || (*x.address & (x.operation >> 1) & 1)) { \
2022-05-24 19:42:50 +00:00
bus_error_ = x; \
exception_vector_ = berr_ ? InstructionSet::M68k::AccessFault : InstructionSet::M68k::AddressError; \
MoveToStateSpecific(BusOrAddressErrorException); \
} \
if(vpa_) { \
x.length = HalfCycles(20) + (HalfCycles(20) + (e_clock_phase_ - time_remaining_) % HalfCycles(20)) % HalfCycles(20); \
} else { \
x.length = HalfCycles(4); \
} \
PerformBusOperation(x)
// Performs the memory access implied by the announce, perform pair,
// honouring DTACK, BERR and VPA as necessary.
#define AccessPair(val, announce, perform) \
perform.value = &val; \
if constexpr (!dtack_is_implicit) { \
announce.length = HalfCycles(4); \
} \
PerformBusOperation(announce); \
WaitForDTACK(announce); \
CompleteAccess(perform);
// Sets up the next data access size and read flags.
#define SetupDataAccess(read_flag, select_flag) \
access_announce.operation = Microcycle::NewAddress | Microcycle::IsData | (read_flag); \
access.operation = access_announce.operation | (select_flag);
// Sets the address source for the next data access.
#define SetDataAddress(addr) \
access.address = access_announce.address = &addr;
// Performs the access established by SetupDataAccess into val.
#define Access(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, read_program); \
program_counter_.l += 2;
// Reads one futher word from the program counter and inserts it into
// the prefetch queue.
2022-05-24 14:53:59 +00:00
#define Prefetch() \
captured_interrupt_level_ = bus_interrupt_level_; \
prefetch_.high = prefetch_.low; \
2022-05-17 12:26:35 +00:00
ReadProgramWord(prefetch_.low)
2022-05-25 19:45:09 +00:00
// Raises the exception with integer vector x — x is the vector identifier,
// not its address.
#define RaiseException(x) \
exception_vector_ = x; \
MoveToStateSpecific(StandardException);
using Mode = InstructionSet::M68k::AddressingMode;
// 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_) {
// Spin in place, one cycle at a time, until one of DTACK,
// BERR or VPA is asserted.
BeginState(WaitForDTACK):
PerformBusOperation(awaiting_dtack);
if(dtack_ || berr_ || vpa_) {
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_dtack_state_);
}
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(WaitForDTACK);
2022-05-24 14:25:40 +00:00
// Spin in place until an interrupt arrives.
BeginState(STOP):
2022-05-24 14:53:59 +00:00
IdleBus(1);
captured_interrupt_level_ = bus_interrupt_level_;
if(captured_interrupt_level_ > status_.interrupt_level) {
MoveToStateSpecific(DoInterrupt);
}
2022-05-24 14:25:40 +00:00
MoveToStateSpecific(STOP);
// Perform the RESET exception, which seeds the stack pointer and program
// counter, populates the prefetch queue, and then moves to instruction dispatch.
BeginState(Reset):
IdleBus(7); // (n-)*5 nn
// Establish general reset state.
status_.is_supervisor = true;
status_.interrupt_level = 7;
status_.trace_flag = 0;
2022-05-24 19:47:47 +00:00
should_trace_ = 0;
did_update_status();
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(temporary_address_.l);
temporary_address_.l = 0;
Access(registers_[15].high); // nF
2022-05-17 20:10:20 +00:00
temporary_address_.l += 2;
Access(registers_[15].low); // nf
2022-05-17 20:10:20 +00:00
temporary_address_.l += 2;
Access(program_counter_.high); // nV
2022-05-17 20:10:20 +00:00
temporary_address_.l += 2;
Access(program_counter_.low); // nv
Prefetch(); // np
IdleBus(1); // n
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
// Perform a 'standard' exception, i.e. a Group 1 or 2.
BeginState(StandardException):
captured_status_.w = status_.status();
2022-05-24 19:42:50 +00:00
// Switch to supervisor mode, disable interrupts.
status_.is_supervisor = true;
status_.trace_flag = 0;
2022-05-24 19:47:47 +00:00
should_trace_ = 0;
did_update_status();
SetupDataAccess(0, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
// Push status and current program counter.
// Write order is wacky here, but I think it's correct.
registers_[15].l -= 6;
Access(captured_status_); // ns
registers_[15].l += 4;
Access(instruction_address_.low); // ns
registers_[15].l -= 2;
Access(instruction_address_.high); // nS
registers_[15].l -= 2;
// Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(exception_vector_ << 2);
Access(program_counter_.high); // nV
temporary_address_.l += 2;
Access(program_counter_.low); // nv
// Populate the prefetch queue.
Prefetch(); // np
IdleBus(1); // n
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-24 19:42:50 +00:00
BeginState(BusOrAddressErrorException):
// "The microcode pushes the stack frame in a non consecutive order"
// per Ijor's document, but little further information is given.
//
// So the below is a cross-your-fingers guess based on the constraints
// that the information writen, from lowest address to highest is:
//
// R/W, I/N, function code word; [at -2]
// access address; [-6]
// instruction register; [-8]
// status register; [-10]
// program counter. [-14]
//
// With the instruction register definitely being written before the
// function code word.
//
// And the documented bus pattern is:
//
// nn ns ns nS ns ns ns nS nV nv np n np
//
// So, based on the hoopy ordering of a standard exception, maybe:
//
// 1) status register;
// 2) program counter;
// 3) instruction register;
// 4) function code;
// 5) access address?
captured_status_.w = status_.status();
IdleBus(2);
// Switch to supervisor mode, disable interrupts.
status_.is_supervisor = true;
status_.trace_flag = 0;
status_.interrupt_level = 7;
2022-05-24 19:47:47 +00:00
should_trace_ = 0;
2022-05-24 19:42:50 +00:00
did_update_status();
SetupDataAccess(0, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
registers_[15].l -= 10;
Access(captured_status_); // ns
temporary_address_.l = program_counter_.l;
registers_[15].l -= 2;
Access(temporary_address_.low); // ns
registers_[15].l -= 2;
Access(temporary_address_.high); // nS
registers_[15].l += 6;
temporary_value_.w = opcode_;
Access(temporary_value_.low); // ns
// TODO: construct the function code.
temporary_value_.w = (temporary_value_.w & ~31);
registers_[15].l += 6;
Access(temporary_value_.low); // ns
temporary_address_.l = *bus_error_.address;
registers_[15].l -= 2;
Access(temporary_value_.low); // ns
registers_[15].l -= 2;
Access(temporary_value_.high); // nS
registers_[15].l -= 8;
// Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(exception_vector_ << 2);
Access(program_counter_.high); // nV
temporary_address_.l += 2;
Access(program_counter_.low); // nv
// Populate the prefetch queue.
Prefetch(); // np
IdleBus(1); // n
Prefetch(); // np
MoveToStateSpecific(Decode);
2022-05-24 14:53:59 +00:00
// Acknowledge an interrupt, thereby obtaining an exception vector,
// and do the exception.
BeginState(DoInterrupt):
IdleBus(3); // n nn
// Capture status and switch to supervisor mode.
captured_status_.w = status_.status();
status_.is_supervisor = true;
status_.trace_flag = 0;
status_.interrupt_level = captured_interrupt_level_;
2022-05-24 19:47:47 +00:00
should_trace_ = 0;
2022-05-24 14:53:59 +00:00
did_update_status();
// Prepare for stack activity.
SetupDataAccess(0, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
// Push status.
registers_[15].l -= 2;
Access(captured_status_); // ns
// Do the interrupt cycle, to obtain a vector.
2022-05-27 00:20:28 +00:00
temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1);
2022-05-24 14:53:59 +00:00
SetupDataAccess(0, Microcycle::InterruptAcknowledge);
SetDataAddress(temporary_address_.l);
Access(temporary_value_.low); // ni
// If VPA is set, autovector.
if(vpa_) {
temporary_value_.w = uint16_t(InstructionSet::M68k::Exception::InterruptAutovectorBase - 1 + captured_interrupt_level_);
2022-05-24 14:53:59 +00:00
}
2022-05-27 00:20:28 +00:00
// TODO: if bus error is set, treat interrupt as spurious.
2022-05-24 14:53:59 +00:00
IdleBus(3); // n- n
// Do the rest of the stack work.
SetupDataAccess(0, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
registers_[15].l -= 2;
Access(instruction_address_.high); // ns
registers_[15].l -= 2;
Access(instruction_address_.low); // nS
// Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(temporary_value_.w << 2);
2022-05-24 14:53:59 +00:00
Access(program_counter_.high); // nV
temporary_address_.l += 2;
Access(program_counter_.low); // nv
// Populate the prefetch queue.
Prefetch(); // np
IdleBus(1); // n
Prefetch(); // np
MoveToStateSpecific(Decode);
2022-05-17 12:26:35 +00:00
// Inspect the prefetch queue in order to decode the next instruction,
// and segue into the fetching of operands.
BeginState(Decode):
CheckOverrun();
2022-05-24 14:53:59 +00:00
// Capture the address of the next instruction.
instruction_address_.l = program_counter_.l - 4;
// Head off into an interrupt if one is found.
if(captured_interrupt_level_ > status_.interrupt_level) {
MoveToStateSpecific(DoInterrupt);
}
2022-05-24 19:47:47 +00:00
// Potentially perform a trace.
if(should_trace_) {
2022-05-25 19:45:09 +00:00
RaiseException(InstructionSet::M68k::Exception::Trace);
2022-05-24 19:47:47 +00:00
}
// Capture the current trace flag.
should_trace_ = status_.trace_flag;
2022-05-24 14:53:59 +00:00
// Read and decode an opcode.
2022-05-17 12:26:35 +00:00
opcode_ = prefetch_.high.w;
instruction_ = decoder_.decode(opcode_);
// Signal the bus handler if requested.
if constexpr (signal_will_perform) {
bus_handler_.will_perform(instruction_address_.l, opcode_);
}
// Check for a privilege violation.
if(instruction_.requires_supervisor() && !status_.is_supervisor) {
exception_vector_ = InstructionSet::M68k::Exception::PrivilegeViolation;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StandardException);
}
// Ensure the first parameter is next fetched.
next_operand_ = 0;
// Obtain operand flags and pick a perform pattern.
#define CASE(x) \
case InstructionSet::M68k::Operation::x: \
operand_flags_ = InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::x>();
#define StdCASE(x, y) \
CASE(x) \
y; \
\
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::LongWord) { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchOperand_l); \
} else { \
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::Byte) { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte); \
} else { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \
} \
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchOperand_bw); \
}
2022-05-19 19:49:42 +00:00
#define Duplicate(x, y) \
case InstructionSet::M68k::Operation::x: \
static_assert( \
InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::x>() == \
InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::y>() && \
InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == \
InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::y>() \
); \
[[fallthrough]];
2022-05-19 19:49:42 +00:00
#define SpecialCASE(x) case InstructionSet::M68k::Operation::x: MoveToStateSpecific(x)
switch(instruction_.operation) {
case InstructionSet::M68k::Operation::Undefined:
if(instruction_.operation == InstructionSet::M68k::Operation::Undefined) {
switch(opcode_ & 0xf000) {
default:
exception_vector_ = InstructionSet::M68k::Exception::IllegalInstruction;
break;
case 0xa000:
exception_vector_ = InstructionSet::M68k::Exception::Line1010;
break;
case 0xf000:
exception_vector_ = InstructionSet::M68k::Exception::Line1111;
break;
}
MoveToStateSpecific(StandardException);
}
StdCASE(NBCD, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_n;
} else {
perform_state_ = Perform_np;
}
})
Duplicate(CLRb, NEGXb) Duplicate(NEGb, NEGXb) Duplicate(NOTb, NEGXb)
StdCASE(NEGXb, perform_state_ = Perform_np);
Duplicate(CLRw, NEGXw) Duplicate(NEGw, NEGXw) Duplicate(NOTw, NEGXw)
StdCASE(NEGXw, perform_state_ = Perform_np);
Duplicate(CLRl, NEGXl) Duplicate(NEGl, NEGXl) Duplicate(NOTl, NEGXl)
StdCASE(NEGXl,
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_n;
} else {
perform_state_ = Perform_np;
}
);
StdCASE(SWAP, perform_state_ = Perform_np);
2022-05-19 19:19:00 +00:00
StdCASE(EXG, perform_state_ = Perform_np_n);
2022-05-19 19:41:02 +00:00
StdCASE(EXTbtow, perform_state_ = Perform_np);
StdCASE(EXTwtol, perform_state_ = Perform_np);
2022-05-23 12:46:06 +00:00
StdCASE(MOVEb, perform_state_ = MOVE);
Duplicate(MOVEAw, MOVEw)
2022-05-23 12:46:06 +00:00
StdCASE(MOVEw, perform_state_ = MOVE);
Duplicate(MOVEAl, MOVEl)
2022-05-23 12:46:06 +00:00
StdCASE(MOVEl, perform_state_ = MOVE);
StdCASE(CMPb, perform_state_ = Perform_np);
StdCASE(CMPw, perform_state_ = Perform_np);
StdCASE(CMPl, perform_state_ = Perform_np_n);
StdCASE(CMPAw, perform_state_ = Perform_np_n);
StdCASE(CMPAl, perform_state_ = Perform_np_n);
2022-05-19 19:49:42 +00:00
Duplicate(ANDb, ORb) StdCASE(ORb, perform_state_ = Perform_np);
Duplicate(ANDw, ORw) StdCASE(ORw, perform_state_ = Perform_np);
Duplicate(ANDl, ORl) StdCASE(ORl, {
if(instruction_.mode(1) == Mode::DataRegisterDirect) {
switch(instruction_.mode(0)) {
default:
perform_state_ = Perform_np_n;
break;
case Mode::DataRegisterDirect:
case Mode::ImmediateData:
perform_state_ = Perform_np_nn;
break;
}
} else {
perform_state_ = Perform_np;
}
});
StdCASE(EORb, perform_state_ = Perform_np);
StdCASE(EORw, perform_state_ = Perform_np);
StdCASE(EORl, {
if(instruction_.mode(1) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_nn;
} else {
perform_state_ = Perform_np;
}
})
2022-05-19 01:00:10 +00:00
2022-05-19 19:49:42 +00:00
Duplicate(SBCD, ABCD)
2022-05-19 19:19:00 +00:00
CASE(ABCD)
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_n;
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchOperand_bw);
2022-05-19 19:19:00 +00:00
} else {
select_flag_ = Microcycle::SelectByte;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(TwoOp_Predec_bw);
2022-05-19 19:19:00 +00:00
}
StdCASE(CHK, perform_state_ = CHK);
Duplicate(SUBb, ADDb) StdCASE(ADDb, perform_state_ = Perform_np)
Duplicate(SUBw, ADDw) StdCASE(ADDw, perform_state_ = Perform_np)
Duplicate(SUBl, ADDl) StdCASE(ADDl, {
if(instruction_.mode(1) != Mode::DataRegisterDirect) {
perform_state_ = Perform_np;
} else {
switch(instruction_.mode(0)) {
default:
perform_state_ = Perform_np_n;
break;
case Mode::DataRegisterDirect:
case Mode::AddressRegisterDirect:
case Mode::ImmediateData:
perform_state_ = Perform_np_nn;
break;
}
}
})
Duplicate(SUBAw, ADDAw) StdCASE(ADDAw, perform_state_ = Perform_np_nn)
Duplicate(SUBAl, ADDAl) StdCASE(ADDAl, {
if(instruction_.mode(1) == Mode::AddressRegisterDirect) {
perform_state_ = Perform_np_nn;
} else {
switch(instruction_.mode(0)) {
default:
perform_state_ = Perform_np_n;
break;
case Mode::DataRegisterDirect:
case Mode::AddressRegisterDirect:
case Mode::ImmediateData:
perform_state_ = Perform_np_nn;
break;
}
}
})
Duplicate(SUBXb, ADDXb) StdCASE(ADDXb, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np;
} else {
select_flag_ = Microcycle::SelectByte;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(TwoOp_Predec_bw);
}
})
Duplicate(SUBXw, ADDXw) StdCASE(ADDXw, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np;
} else {
select_flag_ = Microcycle::SelectWord;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(TwoOp_Predec_bw);
}
})
Duplicate(SUBXl, ADDXl) StdCASE(ADDXl, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_nn;
} else {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(TwoOp_Predec_l);
}
})
StdCASE(Scc, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Scc_Dn;
} else {
perform_state_ = Perform_np;
}
});
SpecialCASE(DBcc);
2022-05-20 15:32:06 +00:00
SpecialCASE(Bccb);
SpecialCASE(Bccw);
2022-05-20 16:04:43 +00:00
2022-05-25 20:32:02 +00:00
SpecialCASE(BSRb);
SpecialCASE(BSRw);
2022-05-22 11:16:38 +00:00
Duplicate(JMP, JSR)
2022-05-21 14:29:36 +00:00
StdCASE(JSR, {
2022-05-22 11:16:38 +00:00
post_ea_state_ =
(instruction_.operation == InstructionSet::M68k::Operation::JSR) ?
JSR : JMP;
2022-05-21 14:29:36 +00:00
switch(instruction_.mode(0)) {
case Mode::AddressRegisterIndirect:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPAddressRegisterIndirect);
2022-05-21 14:29:36 +00:00
case Mode::AddressRegisterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPAddressRegisterIndirectWithDisplacement);
2022-05-21 14:29:36 +00:00
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPAddressRegisterIndirectWithIndex8bitDisplacement);
2022-05-21 14:29:36 +00:00
case Mode::ProgramCounterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPProgramCounterIndirectWithDisplacement);
2022-05-21 14:29:36 +00:00
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPProgramCounterIndirectWithIndex8bitDisplacement);
2022-05-21 14:29:36 +00:00
case Mode::AbsoluteShort:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPAbsoluteShort);
2022-05-21 14:29:36 +00:00
case Mode::AbsoluteLong:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(JSRJMPAbsoluteLong);
2022-05-21 14:29:36 +00:00
default: assert(false);
}
});
2022-05-20 16:58:45 +00:00
StdCASE(BTST, {
switch(instruction_.mode(1)) {
default:
perform_state_ = Perform_np;
break;
case Mode::DataRegisterDirect:
case Mode::ImmediateData:
perform_state_ = Perform_np_n;
break;
}
});
Duplicate(BCHG, BSET)
StdCASE(BSET, {
switch(instruction_.mode(1)) {
default:
perform_state_ = Perform_np;
break;
case Mode::DataRegisterDirect:
case Mode::ImmediateData:
perform_state_ = BCHG_BSET_Dn;
break;
}
});
StdCASE(BCLR, {
switch(instruction_.mode(1)) {
default:
perform_state_ = Perform_np;
break;
case Mode::DataRegisterDirect:
case Mode::ImmediateData:
perform_state_ = BCLR_Dn;
break;
}
});
2022-05-20 18:22:32 +00:00
StdCASE(MOVEPl, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEPtoM_l);
2022-05-20 18:22:32 +00:00
} else {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEPtoR_l);
2022-05-20 18:22:32 +00:00
}
});
StdCASE(MOVEPw, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEPtoM_w);
2022-05-20 18:22:32 +00:00
} else {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEPtoR_w);
2022-05-20 18:22:32 +00:00
}
});
Duplicate(ORItoCCR, EORItoCCR); Duplicate(ANDItoCCR, EORItoCCR);
StdCASE(EORItoCCR, perform_state_ = LogicalToSR);
Duplicate(ORItoSR, EORItoSR); Duplicate(ANDItoSR, EORItoSR);
StdCASE(EORItoSR, perform_state_ = LogicalToSR);
2022-05-20 22:48:19 +00:00
StdCASE(MOVEMtoRl, perform_state_ = MOVEMtoR);
StdCASE(MOVEMtoRw, perform_state_ = MOVEMtoR);
StdCASE(MOVEMtoMl, perform_state_ = MOVEMtoM);
StdCASE(MOVEMtoMw, perform_state_ = MOVEMtoM);
2022-05-21 14:29:36 +00:00
StdCASE(TSTb, perform_state_ = Perform_np);
StdCASE(TSTw, perform_state_ = Perform_np);
StdCASE(TSTl, perform_state_ = Perform_np);
StdCASE(DIVU, perform_state_ = DIVU_DIVS);
StdCASE(DIVS, perform_state_ = DIVU_DIVS);
2022-05-23 13:29:19 +00:00
StdCASE(MULU, perform_state_ = Perform_idle_dyamic_Dn);
StdCASE(MULS, perform_state_ = Perform_idle_dyamic_Dn);
2022-05-22 12:29:12 +00:00
StdCASE(LEA, {
post_ea_state_ = LEA;
MoveToStateSpecific(CalcEffectiveAddress);
});
2022-05-22 15:27:38 +00:00
StdCASE(PEA, {
post_ea_state_ = PEA;
MoveToStateSpecific(CalcEffectiveAddress);
});
2022-05-22 12:29:12 +00:00
StdCASE(TAS, {
// TAS uses a special atomic bus cycle for memory accesses,
// but is also available as DataRegisterDirect, with no
// memory access whatsoever. So segue elsewhere here only
// for the other cases.
if(instruction_.mode(0) != Mode::DataRegisterDirect) {
post_ea_state_ = TAS;
MoveToStateSpecific(CalcEffectiveAddress);
}
perform_state_ = Perform_np;
});
2022-05-22 23:45:22 +00:00
Duplicate(MOVEtoCCR, MOVEtoSR);
StdCASE(MOVEtoSR, perform_state_ = MOVEtoCCRSR);
StdCASE(MOVEfromSR, {
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_n;
2022-05-22 23:45:22 +00:00
} else {
perform_state_ = Perform_np;
2022-05-22 23:45:22 +00:00
}
});
SpecialCASE(RTR);
SpecialCASE(RTE);
SpecialCASE(RTS);
2022-05-23 01:16:38 +00:00
2022-05-23 13:29:19 +00:00
#define ShiftGroup(suffix, state) \
2022-05-23 14:09:46 +00:00
Duplicate(ASL##suffix, ASR##suffix); \
Duplicate(LSL##suffix, ASR##suffix); \
Duplicate(LSR##suffix, ASR##suffix); \
Duplicate(ROL##suffix, ASR##suffix); \
Duplicate(ROR##suffix, ASR##suffix); \
Duplicate(ROXL##suffix, ASR##suffix); \
Duplicate(ROXR##suffix, ASR##suffix); \
StdCASE(ASR##suffix, perform_state_ = state );
2022-05-23 13:29:19 +00:00
ShiftGroup(m, Perform_np)
ShiftGroup(b, Perform_idle_dyamic_Dn)
ShiftGroup(w, Perform_idle_dyamic_Dn)
ShiftGroup(l, Perform_idle_dyamic_Dn)
#undef ShiftGroup
SpecialCASE(LINKw);
SpecialCASE(UNLINK);
SpecialCASE(RESET);
SpecialCASE(NOP);
2022-05-23 14:27:44 +00:00
2022-05-24 00:42:41 +00:00
StdCASE(MOVEtoUSP, perform_state_ = Perform_np);
StdCASE(MOVEfromUSP, perform_state_ = Perform_np);
2022-05-24 14:25:40 +00:00
SpecialCASE(STOP);
2022-05-24 19:14:20 +00:00
SpecialCASE(TRAP);
SpecialCASE(TRAPV);
default:
assert(false);
}
2022-05-19 19:49:42 +00:00
#undef Duplicate
#undef StdCASE
#undef CASE
#undef SpecialCASE
2022-05-17 12:26:35 +00:00
2022-05-19 14:47:57 +00:00
// MARK: - Fetch, dispatch.
2022-05-22 11:39:16 +00:00
#define MoveToNextOperand(x) \
++next_operand_; \
if(next_operand_ == 2) { \
MoveToStateDynamic(perform_state_); \
} \
MoveToStateSpecific(x)
2022-05-19 14:47:57 +00:00
// Check the operand flags to determine whether the byte or word
// operand at index next_operand_ needs to be fetched, and if so
// then calculate the EA and do so.
BeginState(FetchOperand_bw):
// Check that this operand is meant to be fetched; if not then either:
//
// (i) this operand isn't used; or
// (ii) its address calculation will end up conflated with performance,
// so there's no generic bus-accurate approach.
if(!(operand_flags_ & (1 << next_operand_))) {
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(perform_state_);
}
// Figure out how to fetch it.
switch(instruction_.mode(next_operand_)) {
case Mode::AddressRegisterDirect:
case Mode::DataRegisterDirect:
operand_[next_operand_] = registers_[instruction_.lreg(next_operand_)];
2022-05-19 14:47:57 +00:00
MoveToNextOperand(FetchOperand_bw);
case Mode::Quick:
operand_[next_operand_].l = InstructionSet::M68k::quick(opcode_, instruction_.operation);
2022-05-19 14:47:57 +00:00
MoveToNextOperand(FetchOperand_bw);
case Mode::AddressRegisterIndirect:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirect_bw);
case Mode::AddressRegisterIndirectWithPostincrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithPostincrement_bw);
case Mode::AddressRegisterIndirectWithPredecrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithPredecrement_bw);
case Mode::AddressRegisterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithDisplacement_bw);
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithIndex8bitDisplacement_bw);
case Mode::ProgramCounterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchProgramCounterIndirectWithDisplacement_bw);
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchProgramCounterIndirectWithIndex8bitDisplacement_bw);
case Mode::AbsoluteShort:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAbsoluteShort_bw);
case Mode::AbsoluteLong:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAbsoluteLong_bw);
case Mode::ImmediateData:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchImmediateData_bw);
// Should be impossible to reach.
default:
assert(false);
}
break;
2022-05-19 14:47:57 +00:00
// As above, but for .l.
BeginState(FetchOperand_l):
if(!(operand_flags_ & (1 << next_operand_))) {
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(perform_state_);
2022-05-19 14:47:57 +00:00
}
switch(instruction_.mode(next_operand_)) {
case Mode::AddressRegisterDirect:
case Mode::DataRegisterDirect:
operand_[next_operand_] = registers_[instruction_.lreg(next_operand_)];
2022-05-19 23:38:42 +00:00
MoveToNextOperand(FetchOperand_l);
2022-05-19 14:47:57 +00:00
case Mode::Quick:
operand_[next_operand_].l = InstructionSet::M68k::quick(opcode_, instruction_.operation);
2022-05-19 23:38:42 +00:00
MoveToNextOperand(FetchOperand_l);
2022-05-19 14:47:57 +00:00
case Mode::AddressRegisterIndirect:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirect_l);
2022-05-19 14:47:57 +00:00
case Mode::AddressRegisterIndirectWithPostincrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithPostincrement_l);
2022-05-19 14:47:57 +00:00
case Mode::AddressRegisterIndirectWithPredecrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithPredecrement_l);
2022-05-19 14:47:57 +00:00
case Mode::AddressRegisterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithDisplacement_l);
2022-05-19 14:47:57 +00:00
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAddressRegisterIndirectWithIndex8bitDisplacement_l);
2022-05-19 14:47:57 +00:00
case Mode::ProgramCounterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchProgramCounterIndirectWithDisplacement_l);
2022-05-19 14:47:57 +00:00
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchProgramCounterIndirectWithIndex8bitDisplacement_l);
2022-05-19 14:47:57 +00:00
case Mode::AbsoluteShort:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAbsoluteShort_l);
2022-05-19 14:47:57 +00:00
case Mode::AbsoluteLong:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchAbsoluteLong_l);
2022-05-19 14:47:57 +00:00
case Mode::ImmediateData:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(FetchImmediateData_l);
2022-05-19 14:47:57 +00:00
// Should be impossible to reach.
default:
assert(false);
}
break;
BeginState(CalcEffectiveAddress):
switch(instruction_.mode(next_operand_)) {
default:
MoveToStateDynamic(post_ea_state_);
case Mode::AddressRegisterIndirect:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAddressRegisterIndirect);
case Mode::AddressRegisterIndirectWithPostincrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAddressRegisterIndirectWithPostincrement);
case Mode::AddressRegisterIndirectWithPredecrement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAddressRegisterIndirectWithPredecrement);
case Mode::AddressRegisterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAddressRegisterIndirectWithDisplacement);
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAddressRegisterIndirectWithIndex8bitDisplacement);
case Mode::ProgramCounterIndirectWithDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcProgramCounterIndirectWithDisplacement);
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcProgramCounterIndirectWithIndex8bitDisplacement);
case Mode::AbsoluteShort:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAbsoluteShort);
case Mode::AbsoluteLong:
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcAbsoluteLong);
}
2022-05-19 14:47:57 +00:00
// MARK: - Fetch, addressing modes.
//
// AddressRegisterIndirect
//
2022-05-19 14:47:57 +00:00
BeginState(FetchAddressRegisterIndirect_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
SetDataAddress(effective_address_[next_operand_].l);
2022-05-19 14:47:57 +00:00
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAddressRegisterIndirect_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
SetDataAddress(effective_address_[next_operand_].l);
2022-05-19 14:47:57 +00:00
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
2022-05-19 14:47:57 +00:00
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAddressRegisterIndirect):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPAddressRegisterIndirect):
2022-05-22 15:27:38 +00:00
effective_address_[0].l = registers_[8 + instruction_.reg(next_operand_)].l;
temporary_address_.l = instruction_address_.l + 2;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// AddressRegisterIndirectWithPostincrement
//
BeginState(FetchAddressRegisterIndirectWithPostincrement_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
registers_[8 + instruction_.reg(next_operand_)].l +=
address_increments[int(instruction_.operand_size())][instruction_.reg(next_operand_)];
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAddressRegisterIndirectWithPostincrement_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
registers_[8 + instruction_.reg(next_operand_)].l += 4;
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAddressRegisterIndirectWithPostincrement):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
registers_[8 + instruction_.reg(next_operand_)].l +=
address_increments[int(instruction_.operand_size())][instruction_.reg(next_operand_)];
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
//
// AddressRegisterIndirectWithPredecrement
//
BeginState(FetchAddressRegisterIndirectWithPredecrement_bw):
registers_[8 + instruction_.reg(next_operand_)].l -=
address_increments[int(instruction_.operand_size())][instruction_.reg(next_operand_)];
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAddressRegisterIndirectWithPredecrement_l):
registers_[8 + instruction_.reg(next_operand_)].l -= 4;
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAddressRegisterIndirectWithPredecrement):
registers_[8 + instruction_.reg(next_operand_)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(next_operand_)];
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
//
// AddressRegisterIndirectWithDisplacement
//
BeginState(FetchAddressRegisterIndirectWithDisplacement_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
registers_[8 + instruction_.reg(next_operand_)].l +
uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAddressRegisterIndirectWithDisplacement_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
registers_[8 + instruction_.reg(next_operand_)].l +
uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAddressRegisterIndirectWithDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
registers_[8 + instruction_.reg(next_operand_)].l +
uint32_t(int16_t(prefetch_.w));
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPAddressRegisterIndirectWithDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[0].l =
2022-05-21 14:29:36 +00:00
registers_[8 + instruction_.reg(next_operand_)].l +
uint32_t(int16_t(prefetch_.w));
2022-05-21 14:29:36 +00:00
IdleBus(1); // n
temporary_address_.l = instruction_address_.l + 4;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// ProgramCounterIndirectWithDisplacement
//
BeginState(FetchProgramCounterIndirectWithDisplacement_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
2022-05-20 18:42:51 +00:00
program_counter_.l - 2 +
uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchProgramCounterIndirectWithDisplacement_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
2022-05-20 18:42:51 +00:00
program_counter_.l - 2 +
uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcProgramCounterIndirectWithDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l =
program_counter_.l - 2 +
uint32_t(int16_t(prefetch_.w));
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPProgramCounterIndirectWithDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[0].l =
2022-05-21 14:29:36 +00:00
program_counter_.l - 2 +
uint32_t(int16_t(prefetch_.w));
2022-05-21 14:29:36 +00:00
IdleBus(1); // n
temporary_address_.l = instruction_address_.l + 4;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// AddressRegisterIndirectWithIndex8bitDisplacement
//
#define d8Xn(base) \
base + \
((prefetch_.w & 0x800) ? \
registers_[prefetch_.w >> 12].l : \
uint32_t(int16_t(registers_[prefetch_.w >> 12].w))) + \
uint32_t(int8_t(prefetch_.b));
BeginState(FetchAddressRegisterIndirectWithIndex8bitDisplacement_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAddressRegisterIndirectWithIndex8bitDisplacement_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAddressRegisterIndirectWithIndex8bitDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
IdleBus(1); // n
Prefetch(); // np
IdleBus(1); // n
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPAddressRegisterIndirectWithIndex8bitDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[0].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
2022-05-21 14:29:36 +00:00
IdleBus(3); // n nn
temporary_address_.l = instruction_address_.l + 4;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// ProgramCounterIndirectWithIndex8bitDisplacement
//
BeginState(FetchProgramCounterIndirectWithIndex8bitDisplacement_bw):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchProgramCounterIndirectWithIndex8bitDisplacement_l):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
SetDataAddress(effective_address_[next_operand_].l);
IdleBus(1); // n
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcProgramCounterIndirectWithIndex8bitDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
IdleBus(1); // n
Prefetch(); // np
IdleBus(1); // n
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPProgramCounterIndirectWithIndex8bitDisplacement):
2022-05-22 15:27:38 +00:00
effective_address_[0].l = d8Xn(program_counter_.l - 2);
2022-05-21 14:29:36 +00:00
IdleBus(3); // n nn
temporary_address_.l = instruction_address_.l + 4;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
#undef d8Xn
//
// AbsoluteShort
//
BeginState(FetchAbsoluteShort_bw):
effective_address_[next_operand_].l = uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAbsoluteShort_l):
effective_address_[next_operand_].l = uint32_t(int16_t(prefetch_.w));
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAbsoluteShort):
effective_address_[next_operand_].l = uint32_t(int16_t(prefetch_.w));
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPAbsoluteShort):
effective_address_[0].l = uint32_t(int16_t(prefetch_.w));
2022-05-21 14:29:36 +00:00
IdleBus(1); // n
temporary_address_.l = instruction_address_.l + 4;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// AbsoluteLong
//
BeginState(FetchAbsoluteLong_bw):
Prefetch(); // np
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = prefetch_.l;
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchAbsoluteLong_l):
Prefetch(); // np
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = prefetch_.l;
SetDataAddress(effective_address_[next_operand_].l);
Prefetch(); // np
Access(operand_[next_operand_].high); // nR
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l += 2;
Access(operand_[next_operand_].low); // nr
MoveToNextOperand(FetchOperand_l);
BeginState(CalcAbsoluteLong):
Prefetch(); // np
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l = prefetch_.l;
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-22 11:16:38 +00:00
BeginState(JSRJMPAbsoluteLong):
2022-05-21 14:29:36 +00:00
Prefetch(); // np
2022-05-22 15:27:38 +00:00
effective_address_[0].l = prefetch_.l;
temporary_address_.l = instruction_address_.l + 6;
2022-05-22 11:39:16 +00:00
MoveToStateDynamic(post_ea_state_);
2022-05-21 14:29:36 +00:00
//
// ImmediateData
//
BeginState(FetchImmediateData_bw):
operand_[next_operand_].w = prefetch_.w;
Prefetch(); // np
MoveToNextOperand(FetchOperand_bw);
BeginState(FetchImmediateData_l):
Prefetch(); // np
operand_[next_operand_].l = prefetch_.l;
Prefetch(); // np
MoveToNextOperand(FetchOperand_l);
#undef MoveToNextOperand
2022-05-19 14:47:57 +00:00
// MARK: - Store.
2022-05-23 12:46:06 +00:00
#define MoveToNextOperand(x) \
++next_operand_; \
if(next_operand_ == 2) { \
MoveToStateSpecific(Decode); \
} \
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(x)
2022-05-17 20:10:20 +00:00
// Store operand is a lot simpler: only one operand is ever stored, and its address
// is already known. So this can either skip straight back to ::Decode if the target
// is a register, otherwise a single write operation can occur.
BeginState(StoreOperand):
switch(instruction_.operand_size()) {
case InstructionSet::M68k::DataSize::LongWord:
SetupDataAccess(0, Microcycle::SelectWord);
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StoreOperand_l);
case InstructionSet::M68k::DataSize::Word:
SetupDataAccess(0, Microcycle::SelectWord);
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StoreOperand_bw);
case InstructionSet::M68k::DataSize::Byte:
SetupDataAccess(0, Microcycle::SelectByte);
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StoreOperand_bw);
2022-05-17 20:10:20 +00:00
}
BeginState(StoreOperand_bw):
if(!(operand_flags_ & 0x4 << next_operand_)) {
MoveToNextOperand(StoreOperand_bw);
}
switch(instruction_.mode(next_operand_)) {
2022-05-23 12:18:37 +00:00
// Data register: write only the part of the word that has changed.
case Mode::DataRegisterDirect: {
const uint32_t write_mask = size_masks[int(instruction_.operand_size())];
const int reg = instruction_.reg(next_operand_);
registers_[reg].l =
(operand_[next_operand_].l & write_mask) |
(registers_[reg].l & ~write_mask);
}
MoveToNextOperand(StoreOperand_bw);
2022-05-23 12:18:37 +00:00
// Address register: always rewrite the whole word; the smaller
// result will have been sign extended.
case Mode::AddressRegisterDirect:
registers_[instruction_.lreg(next_operand_)] = operand_[next_operand_];
MoveToNextOperand(StoreOperand_bw);
2022-05-23 12:18:37 +00:00
default: break;
}
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Access(operand_[next_operand_].low); // nw
MoveToNextOperand(StoreOperand_bw);
BeginState(StoreOperand_l):
if(!(operand_flags_ & 0x4 << next_operand_)) {
MoveToNextOperand(StoreOperand_l);
}
if(instruction_.mode(next_operand_) <= Mode::AddressRegisterDirect) {
registers_[instruction_.lreg(next_operand_)] = operand_[next_operand_];
MoveToNextOperand(StoreOperand_l);
}
SetupDataAccess(0, Microcycle::SelectWord);
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[next_operand_].l);
Access(operand_[next_operand_].low); // nw
2022-05-22 15:27:38 +00:00
effective_address_[next_operand_].l -= 2;
Access(operand_[next_operand_].high); // nW
MoveToNextOperand(StoreOperand_l);
2022-05-17 20:10:20 +00:00
#define PerformDynamic() \
InstructionSet::M68k::perform<InstructionSet::M68k::Model::M68000>( \
instruction_, operand_[0], operand_[1], status_, *static_cast<ProcessorBase *>(this));
#define PerformSpecific(x) \
InstructionSet::M68k::perform< \
InstructionSet::M68k::Model::M68000, \
ProcessorBase, \
InstructionSet::M68k::Operation::x \
>( \
instruction_, operand_[0], operand_[1], status_, *static_cast<ProcessorBase *>(this));
//
2022-05-19 01:00:10 +00:00
// Various generic forms of perform.
//
#define MoveToWritePhase() \
if(operand_flags_ & 0x0c) { \
next_operand_ = 0; \
MoveToStateSpecific(StoreOperand); \
} else { \
MoveToStateSpecific(Decode); \
}
2022-05-17 20:10:20 +00:00
BeginState(Perform_np):
PerformDynamic();
Prefetch(); // np
MoveToWritePhase();
BeginState(Perform_np_n):
PerformDynamic();
Prefetch(); // np
IdleBus(1); // n
MoveToWritePhase();
BeginState(Perform_np_nn):
PerformDynamic();
Prefetch(); // np
IdleBus(2); // nn
MoveToWritePhase();
2022-05-17 20:10:20 +00:00
#undef MoveToWritePhase
2022-05-17 12:26:35 +00:00
2022-05-19 01:00:10 +00:00
//
2022-05-20 16:04:43 +00:00
// Specific forms of perform...
2022-05-19 01:00:10 +00:00
//
2022-05-23 12:46:06 +00:00
BeginState(MOVE):
PerformDynamic();
// In all cases except predecrement mode: do the usual address
// calculate and storage, then do the next prefetch and decode.
//
// In predecrement mode: do the prefetch, then write the result.
//
// For here, lump data and address register direct in with predec,
// so that all that's left is modes that write to memory and then
// prefetch.
2022-05-19 01:00:10 +00:00
switch(instruction_.mode(1)) {
2022-05-25 12:15:18 +00:00
case Mode::DataRegisterDirect: {
const uint32_t write_mask = size_masks[int(instruction_.operand_size())];
const int reg = instruction_.reg(1);
registers_[reg].l =
(operand_[1].l & write_mask) |
(registers_[reg].l & ~write_mask);
}
MoveToStateSpecific(MOVE_prefetch_decode);
2022-05-19 01:00:10 +00:00
case Mode::AddressRegisterDirect:
2022-05-25 12:15:18 +00:00
registers_[8 + instruction_.reg(1)].l = operand_[1].l;
MoveToStateSpecific(MOVE_prefetch_decode);
2022-05-23 12:46:06 +00:00
case Mode::AddressRegisterIndirectWithPredecrement:
MoveToStateSpecific(MOVE_predec);
2022-05-19 01:00:10 +00:00
2022-05-23 12:46:06 +00:00
default: break;
}
next_operand_ = 1;
post_ea_state_ = MOVE_complete;
MoveToStateSpecific(CalcEffectiveAddress);
2022-05-19 01:00:10 +00:00
2022-05-25 12:15:18 +00:00
BeginState(MOVE_prefetch_decode):
Prefetch();
MoveToStateSpecific(Decode);
2022-05-23 12:46:06 +00:00
BeginState(MOVE_predec):
Prefetch();
2022-05-25 12:15:18 +00:00
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
switch(instruction_.operand_size()) {
case InstructionSet::M68k::DataSize::LongWord:
MoveToStateSpecific(MOVE_predec_l);
case InstructionSet::M68k::DataSize::Word:
SetupDataAccess(0, Microcycle::SelectWord);
registers_[8 + instruction_.reg(1)].l -= 2;
break;
case InstructionSet::M68k::DataSize::Byte:
SetupDataAccess(0, Microcycle::SelectByte);
registers_[8 + instruction_.reg(1)].l -=
address_increments[0][instruction_.reg(next_operand_)];
break;
}
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
Access(operand_[1].low);
MoveToStateSpecific(Decode);
BeginState(MOVE_predec_l):
SetupDataAccess(0, Microcycle::SelectWord);
registers_[8 + instruction_.reg(1)].l -= 2;
Access(operand_[1].low);
registers_[8 + instruction_.reg(1)].l -= 2;
Access(operand_[1].high);
MoveToStateSpecific(Decode);
2022-05-23 12:46:06 +00:00
BeginState(MOVE_complete):
SetDataAddress(effective_address_[1].l);
switch(instruction_.operand_size()) {
case InstructionSet::M68k::DataSize::LongWord:
SetupDataAccess(0, Microcycle::SelectWord);
MoveToStateSpecific(MOVE_complete_l);
case InstructionSet::M68k::DataSize::Word:
SetupDataAccess(0, Microcycle::SelectWord);
break;
case InstructionSet::M68k::DataSize::Byte:
SetupDataAccess(0, Microcycle::SelectByte);
break;
2022-05-19 01:00:10 +00:00
}
2022-05-23 12:46:06 +00:00
Access(operand_[1].low);
Prefetch();
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-19 01:00:10 +00:00
2022-05-23 12:46:06 +00:00
BeginState(MOVE_complete_l):
Access(operand_[1].high);
effective_address_[1].l += 2;
Access(operand_[1].low);
Prefetch();
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-19 01:00:10 +00:00
2022-05-20 16:04:43 +00:00
//
// [ABCD/SBCD/SUBX/ADDX] (An)-, (An)-
//
BeginState(TwoOp_Predec_bw):
2022-05-19 19:19:00 +00:00
IdleBus(1); // n
SetupDataAccess(Microcycle::Read, select_flag_);
2022-05-19 19:19:00 +00:00
SetDataAddress(registers_[8 + instruction_.reg(0)].l);
registers_[8 + instruction_.reg(0)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(0)];
2022-05-19 19:19:00 +00:00
Access(operand_[0].low); // nr
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
registers_[8 + instruction_.reg(1)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(1)];
2022-05-19 19:19:00 +00:00
Access(operand_[1].low); // nr
Prefetch(); // np
PerformDynamic();
2022-05-19 19:19:00 +00:00
SetupDataAccess(0, select_flag_);
2022-05-19 19:19:00 +00:00
Access(operand_[1].low); // nw
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
BeginState(TwoOp_Predec_l):
IdleBus(1); // n
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(registers_[8 + instruction_.reg(0)].l);
registers_[8 + instruction_.reg(0)].l -= 2;
Access(operand_[0].low); // nr
registers_[8 + instruction_.reg(0)].l -= 2;
Access(operand_[0].high); // nR
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
registers_[8 + instruction_.reg(1)].l -= 2;
Access(operand_[1].low); // nr
registers_[8 + instruction_.reg(1)].l -= 2;
Access(operand_[1].high); // nR
PerformDynamic();
SetupDataAccess(0, Microcycle::SelectWord);
registers_[8 + instruction_.reg(1)].l += 2;
Access(operand_[1].low); // nw
Prefetch(); // np
2022-05-19 19:19:00 +00:00
registers_[8 + instruction_.reg(1)].l -= 2;
Access(operand_[1].high); // nW
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-19 19:19:00 +00:00
//
// CHK
//
BeginState(CHK):
Prefetch(); // np
PerformSpecific(CHK);
// Proper next state will have been set by the flow controller
// call-in; just allow dispatch to whatever it was.
break;
BeginState(CHK_no_trap):
IdleBus(3); // nn n
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
BeginState(CHK_was_over):
IdleBus(2); // nn
instruction_address_.l = program_counter_.l - 4;
2022-05-25 19:45:09 +00:00
RaiseException(InstructionSet::M68k::Exception::CHK);
BeginState(CHK_was_under):
IdleBus(3); // n nn
instruction_address_.l = program_counter_.l - 4;
2022-05-25 19:45:09 +00:00
RaiseException(InstructionSet::M68k::Exception::CHK);
//
// Scc
//
BeginState(Scc_Dn):
Prefetch(); // np
PerformSpecific(Scc);
// Next state will be set by did_scc.
break;
BeginState(Scc_Dn_did_set):
IdleBus(1); // n
[[fallthrough]];
BeginState(Scc_Dn_did_not_set):
next_operand_ = 0;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StoreOperand);
2022-05-20 15:32:06 +00:00
//
// DBcc
//
BeginState(DBcc):
2022-05-22 00:06:03 +00:00
operand_[0] = registers_[instruction_.reg(0)];
operand_[1].w = uint16_t(int16_t(prefetch_.w));
PerformSpecific(DBcc);
2022-05-20 15:37:18 +00:00
registers_[instruction_.reg(0)].w = operand_[0].w;
2022-05-20 16:04:43 +00:00
// Next state was set by complete_dbcc.
2022-05-20 15:32:06 +00:00
break;
BeginState(DBcc_branch_taken):
IdleBus(1); // n
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 15:32:06 +00:00
BeginState(DBcc_condition_true):
IdleBus(2); // n n
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 15:32:06 +00:00
BeginState(DBcc_counter_overflow):
IdleBus(1); // n
// Yacht lists an extra np here; I'm assuming it's a read from where
// the PC would have gone, had the branch been taken. So do that,
// but then reset the PC to where it would have been.
Prefetch(); // np
2022-05-20 15:32:06 +00:00
program_counter_.l = instruction_address_.l + 4;
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 15:32:06 +00:00
2022-05-20 16:04:43 +00:00
//
// Bcc [.b and .w]
//
BeginState(Bccb):
2022-05-22 00:59:34 +00:00
operand_[0].b = uint8_t(opcode_);
PerformSpecific(Bccb);
2022-05-22 00:59:34 +00:00
// Next state was set by complete_bcc.
break;
BeginState(Bccw):
2022-05-22 00:59:34 +00:00
operand_[0].w = prefetch_.w;
PerformSpecific(Bccw);
2022-05-20 16:04:43 +00:00
// Next state was set by complete_bcc.
break;
BeginState(Bcc_branch_taken):
IdleBus(1); // n
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 16:04:43 +00:00
BeginState(Bccb_branch_not_taken):
2022-05-20 16:04:43 +00:00
IdleBus(2); // nn
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 16:04:43 +00:00
BeginState(Bccw_branch_not_taken):
2022-05-20 16:04:43 +00:00
IdleBus(2); // nn
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 16:04:43 +00:00
2022-05-22 15:27:38 +00:00
#define Push(x) \
SetupDataAccess(0, Microcycle::SelectWord); \
SetDataAddress(registers_[15].l); \
registers_[15].l -= 4; \
Access(x.high); \
registers_[15].l += 2; \
Access(x.low); \
registers_[15].l -= 2;
2022-05-23 14:27:44 +00:00
#define Pop(x) \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \
SetDataAddress(registers_[15].l); \
Access(x.high); \
registers_[15].l += 2; \
Access(x.low); \
registers_[15].l += 2;
//
// BSR
//
2022-05-25 20:32:02 +00:00
BeginState(BSRb):
BeginState(BSRw):
IdleBus(1); // n
2022-05-25 20:32:02 +00:00
// Calculate the address of the next instruction and the next program counter.
if(instruction_.operand_size() == InstructionSet::M68k::DataSize::Word) {
temporary_address_.l = instruction_address_.l + 4;
2022-05-25 20:32:02 +00:00
program_counter_.l = instruction_address_.l + uint32_t(int16_t(prefetch_.w)) + 2;
} else {
temporary_address_.l = instruction_address_.l + 2;
2022-05-25 20:32:02 +00:00
program_counter_.l = instruction_address_.l + uint32_t(int8_t(opcode_)) + 2;
}
2022-05-25 20:32:02 +00:00
// Push the next instruction address to the stack.
2022-05-22 15:27:38 +00:00
Push(temporary_address_);
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-21 14:29:36 +00:00
//
2022-05-22 11:16:38 +00:00
// JSR [push only; address calculation elsewhere], JMP
2022-05-21 14:29:36 +00:00
//
2022-05-22 11:16:38 +00:00
BeginState(JSR):
2022-05-21 14:29:36 +00:00
// Update the program counter and prefetch once.
2022-05-22 15:27:38 +00:00
program_counter_.l = effective_address_[0].l;
2022-05-21 14:29:36 +00:00
Prefetch(); // np
// Push the old PC onto the stack in upper, lower order.
2022-05-22 15:27:38 +00:00
Push(temporary_address_);
2022-05-21 14:29:36 +00:00
// Prefetch once more.
Prefetch();
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-21 14:29:36 +00:00
2022-05-22 11:16:38 +00:00
BeginState(JMP):
// Update the program counter and prefetch once.
2022-05-22 15:27:38 +00:00
program_counter_.l = effective_address_[0].l;
2022-05-22 11:16:38 +00:00
Prefetch(); // np
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-22 11:16:38 +00:00
2022-05-20 16:58:45 +00:00
//
// BSET, BCHG, BCLR
//
BeginState(BCHG_BSET_Dn):
PerformDynamic();
2022-05-20 16:58:45 +00:00
Prefetch();
IdleBus(1 + dynamic_instruction_length_);
2022-05-20 16:58:45 +00:00
registers_[instruction_.reg(1)] = operand_[1];
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 16:58:45 +00:00
BeginState(BCLR_Dn):
PerformSpecific(BCLR);
2022-05-20 16:58:45 +00:00
Prefetch();
IdleBus(2 + dynamic_instruction_length_);
2022-05-20 16:58:45 +00:00
registers_[instruction_.reg(1)] = operand_[1];
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 16:58:45 +00:00
2022-05-20 18:22:32 +00:00
//
// MOVEP
//
BeginState(MOVEPtoM_l):
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l);
SetupDataAccess(0, Microcycle::SelectByte);
Prefetch(); // np
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l >> 24);
Access(temporary_value_.low); // nW
temporary_address_.l += 2;
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l >> 16);
Access(temporary_value_.low); // nW
temporary_address_.l += 2;
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l >> 8);
Access(temporary_value_.low); // nw
temporary_address_.l += 2;
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l);
Access(temporary_value_.low); // nw
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 18:22:32 +00:00
BeginState(MOVEPtoM_w):
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l);
SetupDataAccess(0, Microcycle::SelectByte);
Prefetch(); // np
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l >> 8);
Access(temporary_value_.low); // nW
temporary_address_.l += 2;
temporary_value_.b = uint8_t(registers_[instruction_.reg(0)].l);
Access(temporary_value_.low); // nw
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 18:22:32 +00:00
BeginState(MOVEPtoR_l):
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l);
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
Prefetch(); // np
Access(temporary_value_.low); // nR
registers_[instruction_.reg(1)].l = uint32_t(temporary_value_.b << 24);
2022-05-20 18:22:32 +00:00
temporary_address_.l += 2;
Access(temporary_value_.low); // nR
registers_[instruction_.reg(1)].l |= uint32_t(temporary_value_.b << 16);
2022-05-20 18:22:32 +00:00
temporary_address_.l += 2;
Access(temporary_value_.low); // nr
registers_[instruction_.reg(1)].l |= uint32_t(temporary_value_.b << 8);
2022-05-20 18:22:32 +00:00
temporary_address_.l += 2;
Access(temporary_value_.low); // nr
registers_[instruction_.reg(1)].l |= uint32_t(temporary_value_.b);
2022-05-20 18:22:32 +00:00
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 18:22:32 +00:00
BeginState(MOVEPtoR_w):
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l);
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
Prefetch(); // np
Access(temporary_value_.low); // nR
registers_[instruction_.reg(1)].w = uint16_t(temporary_value_.b << 8);
2022-05-20 18:22:32 +00:00
temporary_address_.l += 2;
Access(temporary_value_.low); // nr
registers_[instruction_.reg(1)].w |= uint16_t(temporary_value_.b);
2022-05-20 18:22:32 +00:00
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 18:22:32 +00:00
//
// [EORI/ORI/ANDI] #, [CCR/SR]
//
BeginState(LogicalToSR):
2022-05-25 20:20:26 +00:00
IdleBus(4);
// Perform the operation.
PerformDynamic();
// Recede the program counter and prefetch twice.
program_counter_.l -= 2;
Prefetch();
Prefetch();
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 22:48:19 +00:00
//
// MOVEM M --> R
//
BeginState(MOVEMtoR):
post_ea_state_ =
(instruction_.operation == InstructionSet::M68k::Operation::MOVEMtoRl) ?
MOVEMtoR_l_read : MOVEMtoR_w_read;
next_operand_ = 1;
register_index_ = 0;
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[1].l);
2022-05-20 22:48:19 +00:00
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcEffectiveAddress);
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoR_w_read):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoR_finish);
2022-05-20 22:48:19 +00:00
}
// Find the next register to read, read it and sign extend it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
++register_index_;
2022-05-20 22:48:19 +00:00
}
Access(registers_[register_index_].low);
registers_[register_index_].l = uint32_t(int16_t(registers_[register_index_].w));
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
2022-05-20 22:48:19 +00:00
// Drop the bottom bit.
operand_[0].w >>= 1;
++register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoR_w_read);
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoR_l_read):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoR_finish);
2022-05-20 22:48:19 +00:00
}
// Find the next register to read, read it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
++register_index_;
2022-05-20 22:48:19 +00:00
}
Access(registers_[register_index_].high);
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
Access(registers_[register_index_].low);
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
2022-05-20 22:48:19 +00:00
// Drop the bottom bit.
operand_[0].w >>= 1;
++register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoR_l_read);
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoR_finish):
// Perform one more read, spuriously.
Access(temporary_value_.low); // nr
2022-05-21 01:01:23 +00:00
// Write the address back to the register if
2022-05-21 12:17:39 +00:00
// this was postincrement mode.
2022-05-21 01:01:23 +00:00
if(instruction_.mode(1) == Mode::AddressRegisterIndirectWithPostincrement) {
2022-05-22 15:27:38 +00:00
registers_[8 + instruction_.reg(1)].l = effective_address_[1].l;
2022-05-21 01:01:23 +00:00
}
2022-05-20 22:48:19 +00:00
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 22:48:19 +00:00
//
// MOVEM R --> M
//
BeginState(MOVEMtoM):
next_operand_ = 1;
2022-05-22 15:27:38 +00:00
SetDataAddress(effective_address_[1].l);
2022-05-21 12:17:39 +00:00
SetupDataAccess(0, Microcycle::SelectWord);
2022-05-20 22:48:19 +00:00
// Predecrement writes registers the other way around, but still reads the
// mask from LSB.
if(instruction_.mode(1) == Mode::AddressRegisterIndirectWithPredecrement) {
register_index_ = 15;
2022-05-22 15:27:38 +00:00
effective_address_[1].l = registers_[8 + instruction_.reg(1)].l;
2022-05-21 12:17:39 +00:00
// Don't go through the usual calculate EA path because: (i) the test above
// has already told us the addressing mode, and it's trivial; and (ii) the
// predecrement isn't actually wanted.
if(instruction_.operation == InstructionSet::M68k::Operation::MOVEMtoMl) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_l_write_predec);
2022-05-21 12:17:39 +00:00
} else {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_w_write_predec);
2022-05-21 12:17:39 +00:00
}
2022-05-20 22:48:19 +00:00
}
2022-05-21 12:17:39 +00:00
register_index_ = 0;
post_ea_state_ =
(instruction_.operation == InstructionSet::M68k::Operation::MOVEMtoMl) ?
MOVEMtoM_l_write : MOVEMtoM_w_write;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(CalcEffectiveAddress);
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoM_w_write):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_finish);
2022-05-20 22:48:19 +00:00
}
// Find the next register to write, write it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
2022-05-21 12:17:39 +00:00
++register_index_;
2022-05-20 22:48:19 +00:00
}
Access(registers_[register_index_].low);
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
2022-05-20 22:48:19 +00:00
// Drop the bottom bit.
operand_[0].w >>= 1;
2022-05-21 12:17:39 +00:00
++register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_w_write);
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoM_l_write):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_finish);
2022-05-20 22:48:19 +00:00
}
// Find the next register to write, write it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
2022-05-21 12:17:39 +00:00
++register_index_;
2022-05-20 22:48:19 +00:00
}
Access(registers_[register_index_].high);
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
2022-05-20 22:48:19 +00:00
Access(registers_[register_index_].low);
2022-05-22 15:27:38 +00:00
effective_address_[1].l += 2;
2022-05-20 22:48:19 +00:00
// Drop the bottom bit.
operand_[0].w >>= 1;
2022-05-21 12:17:39 +00:00
++register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_l_write);
2022-05-20 22:48:19 +00:00
2022-05-21 12:17:39 +00:00
BeginState(MOVEMtoM_w_write_predec):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_finish);
2022-05-21 12:17:39 +00:00
}
// Find the next register to write, write it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
--register_index_;
}
2022-05-22 15:27:38 +00:00
effective_address_[1].l -= 2;
2022-05-21 12:17:39 +00:00
Access(registers_[register_index_].low);
// Drop the bottom bit.
operand_[0].w >>= 1;
--register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_w_write_predec);
2022-05-21 12:17:39 +00:00
BeginState(MOVEMtoM_l_write_predec):
// If there's nothing left to read, move on.
if(!operand_[0].w) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_finish);
2022-05-21 12:17:39 +00:00
}
// Find the next register to write, write it.
while(!(operand_[0].w & 1)) {
operand_[0].w >>= 1;
--register_index_;
}
2022-05-22 15:27:38 +00:00
effective_address_[1].l -= 2;
2022-05-21 12:17:39 +00:00
Access(registers_[register_index_].low);
2022-05-22 15:27:38 +00:00
effective_address_[1].l -= 2;
2022-05-21 12:17:39 +00:00
Access(registers_[register_index_].high);
// Drop the bottom bit.
operand_[0].w >>= 1;
--register_index_;
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(MOVEMtoM_l_write_predec);
2022-05-21 12:17:39 +00:00
2022-05-20 22:48:19 +00:00
BeginState(MOVEMtoM_finish):
2022-05-21 12:17:39 +00:00
// Write the address back to the register if
// this was predecrement mode.
if(instruction_.mode(1) == Mode::AddressRegisterIndirectWithPredecrement) {
2022-05-22 15:27:38 +00:00
registers_[8 + instruction_.reg(1)].l = effective_address_[1].l;
2022-05-21 12:17:39 +00:00
}
2022-05-20 22:48:19 +00:00
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
2022-05-20 22:48:19 +00:00
//
// DIVU and DIVUS
//
BeginState(DIVU_DIVS):
// Set a no-interrupt-occurred sentinel.
exception_vector_ = -1;
// Perform the instruction.
PerformDynamic();
// Delay the correct amount of time.
IdleBus(dynamic_instruction_length_);
// Either dispatch an exception or don't.
if(exception_vector_ >= 0) {
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(StandardException);
}
// DIVU and DIVS are always to a register, so just write back here
// to save on dispatch costs.
registers_[instruction_.reg(1)] = operand_[1];
Prefetch(); // np
2022-05-22 11:39:16 +00:00
MoveToStateSpecific(Decode);
//
2022-05-23 13:29:19 +00:00
// MULU, MULS and shifts
//
2022-05-23 13:29:19 +00:00
BeginState(Perform_idle_dyamic_Dn):
Prefetch(); // np
// Perform the instruction.
PerformDynamic();
// Delay the correct amount of time.
IdleBus(dynamic_instruction_length_);
// MULU and MULS are always to a register, so just write back here
// to save on dispatch costs.
registers_[instruction_.reg(1)] = operand_[1];
MoveToStateSpecific(Decode);
2022-05-22 12:29:12 +00:00
//
// LEA
//
BeginState(LEA):
2022-05-22 15:27:38 +00:00
registers_[8 + instruction_.reg(1)].l = effective_address_[0].l;
Prefetch();
MoveToStateSpecific(Decode);
//
// PEA
//
BeginState(PEA):
Push(effective_address_[0]);
2022-05-22 12:29:12 +00:00
Prefetch();
MoveToStateSpecific(Decode);
//
// TAS
//
BeginState(TAS):
2022-05-22 20:14:03 +00:00
// Populate all addresses.
tas_cycles[0].address = tas_cycles[1].address =
tas_cycles[2].address =
tas_cycles[3].address = tas_cycles[4].address = &effective_address_[0].l;
// Populate values to the relevant subset.
tas_cycles[0].value = tas_cycles[1].value =
tas_cycles[3].value = tas_cycles[4].value = &operand_[0].low;
// First two parts: the read.
PerformBusOperation(tas_cycles[0]);
2022-05-24 13:17:58 +00:00
CompleteAccess(tas_cycles[1]);
2022-05-22 20:14:03 +00:00
// Third part: processing time.
PerformBusOperation(tas_cycles[2]);
// Do the actual TAS operation.
status_.overflow_flag = status_.carry_flag = 0;
status_.zero_result = operand_[0].b;
status_.negative_flag = operand_[0].b & 0x80;
// Final parts: write back.
operand_[0].b |= 0x80;
PerformBusOperation(tas_cycles[3]);
2022-05-24 13:17:58 +00:00
CompleteAccess(tas_cycles[4]);
Prefetch();
MoveToStateSpecific(Decode);
2022-05-22 23:45:22 +00:00
//
// MOVE to [CCR/SR]
//
BeginState(MOVEtoCCRSR):
PerformDynamic();
// Rewind the program counter and prefetch twice.
IdleBus(2);
program_counter_.l -= 2;
Prefetch();
Prefetch();
MoveToStateSpecific(Decode);
2022-05-23 01:16:38 +00:00
//
// RTR, RTS, RTE
//
BeginState(RTS):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
Access(program_counter_.high);
registers_[15].l += 2;
Access(program_counter_.low);
registers_[15].l += 2;
Prefetch();
Prefetch();
MoveToStateSpecific(Decode);
BeginState(RTE):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
2022-05-23 01:17:28 +00:00
registers_[15].l += 2;
2022-05-23 01:16:38 +00:00
Access(program_counter_.high);
registers_[15].l += 2;
Access(program_counter_.low);
2022-05-23 01:17:28 +00:00
registers_[15].l -= 4;
2022-05-23 01:16:38 +00:00
Access(temporary_value_.low);
2022-05-23 01:17:28 +00:00
registers_[15].l += 6;
2022-05-23 01:16:38 +00:00
status_.set_status(temporary_value_.w);
Prefetch();
Prefetch();
MoveToStateSpecific(Decode);
BeginState(RTR):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
SetDataAddress(registers_[15].l);
registers_[15].l += 2;
Access(program_counter_.high);
registers_[15].l += 2;
Access(program_counter_.low);
registers_[15].l -= 4;
Access(temporary_value_.low);
registers_[15].l += 6;
status_.set_ccr(temporary_value_.w);
Prefetch();
Prefetch();
MoveToStateSpecific(Decode);
2022-05-23 14:27:44 +00:00
//
// LINK[.w] and UNLINK
2022-05-23 14:27:44 +00:00
//
BeginState(LINKw):
2022-05-23 14:27:44 +00:00
Prefetch();
2022-05-23 14:43:17 +00:00
// Ensure that the stack pointer is [seemingly] captured after
// having been decremented by four, if it's what should be captured.
registers_[15].l -= 4;
temporary_address_ = registers_[8 + instruction_.reg(0)];
registers_[15].l += 4;
// Push will actually decrement the stack pointer.
Push(temporary_address_);
// Make the exchange.
registers_[8 + instruction_.reg(0)].l = registers_[15].l;
registers_[15].l += uint32_t(int16_t(prefetch_.high.w));
2022-05-23 14:27:44 +00:00
Prefetch();
MoveToStateSpecific(Decode);
BeginState(UNLINK):
registers_[15] = registers_[8 + instruction_.reg(0)];
Pop(temporary_address_);
registers_[8 + instruction_.reg(0)] = temporary_address_;
Prefetch();
MoveToStateSpecific(Decode);
//
// RESET
//
BeginState(RESET):
IdleBus(2);
PerformBusOperation(reset_cycle);
Prefetch();
MoveToStateSpecific(Decode);
//
// NOP
//
BeginState(NOP):
Prefetch();
MoveToStateSpecific(Decode);
2022-05-24 19:14:20 +00:00
//
// TRAP, TRAPV
//
// TODO: which program counter is appropriate for TRAP? That of the TRAP,
// or that of the instruction after?
BeginState(TRAP):
IdleBus(2);
instruction_address_.l += 2; // Push the address of the instruction after the trap.
2022-05-25 19:45:09 +00:00
RaiseException((opcode_ & 15) + InstructionSet::M68k::Exception::TrapBase);
2022-05-24 19:14:20 +00:00
BeginState(TRAPV):
Prefetch();
if(!status_.overflow_flag) {
MoveToStateSpecific(Decode);
}
instruction_address_.l += 2; // Push the address of the instruction after the trap.
2022-05-25 19:45:09 +00:00
RaiseException(InstructionSet::M68k::Exception::TRAPV);
2022-05-24 19:14:20 +00:00
#undef TODOState
default:
2022-05-19 14:47:57 +00:00
printf("Unhandled state: %d; opcode is %04x\n", state_, opcode_);
assert(false);
}}
2022-05-23 14:27:44 +00:00
#undef Pop
2022-05-22 15:27:38 +00:00
#undef Push
#undef PerformDynamic
#undef PerformSpecific
2022-05-25 19:45:09 +00:00
#undef RaiseException
2022-05-17 01:02:25 +00:00
#undef Prefetch
#undef ReadProgramWord
#undef ReadDataWord
#undef AccessPair
#undef CompleteAccess
#undef WaitForDTACK
#undef IdleBus
#undef PerformBusOperation
2022-05-22 11:39:16 +00:00
#undef MoveToStateSpecific
#undef MoveToStateDynamic
#undef CheckOverrun
#undef Spend
#undef ConsiderExit
}
// MARK: - Flow Controller.
void ProcessorBase::did_update_status() {
// Shuffle the stack pointers.
stack_pointers_[is_supervisor_] = registers_[15];
registers_[15] = stack_pointers_[int(status_.is_supervisor)];
is_supervisor_ = int(status_.is_supervisor);
}
void ProcessorBase::did_chk(bool was_under, bool was_over) {
if(was_over) {
state_ = CHK_was_over;
} else if(was_under) {
state_ = CHK_was_under;
} else {
state_ = CHK_no_trap;
}
}
void ProcessorBase::did_scc(bool did_set_ff) {
state_ = did_set_ff ? Scc_Dn_did_set : Scc_Dn_did_not_set;
}
2022-05-20 15:32:06 +00:00
void ProcessorBase::complete_dbcc(bool matched_condition, bool overflowed, int16_t offset) {
// The actual DBcc rule is: branch if !matched_condition and !overflowed; but I think
// that a spurious read from the intended destination PC occurs if overflowed, so update
// the PC for any case of !matched_condition and rely on the DBcc_counter_overflow to
// set it back.
if(!matched_condition) {
state_ = overflowed ? DBcc_counter_overflow : DBcc_branch_taken;
program_counter_.l = instruction_address_.l + uint32_t(offset) + 2;
return;
}
state_ = DBcc_condition_true;
}
2022-05-20 16:04:43 +00:00
template <typename IntT> void ProcessorBase::complete_bcc(bool take_branch, IntT offset) {
if(take_branch) {
program_counter_.l = instruction_address_.l + uint32_t(offset) + 2;
state_ = Bcc_branch_taken;
return;
}
state_ =
(instruction_.operation == InstructionSet::M68k::Operation::Bccb) ?
Bccb_branch_not_taken : Bccw_branch_not_taken;
2022-05-20 16:04:43 +00:00
}
2022-05-20 16:58:45 +00:00
void ProcessorBase::did_bit_op(int bit_position) {
dynamic_instruction_length_ = int(bit_position > 15);
}
template <bool did_overflow> void ProcessorBase::did_divu(uint32_t, uint32_t) {
// TODO: calculate cost.
}
template <bool did_overflow> void ProcessorBase::did_divs(int32_t, int32_t) {
// TODO: calculate cost.
}
template <typename IntT> void ProcessorBase::did_mulu(IntT) {
// TODO: calculate cost.
}
template <typename IntT> void ProcessorBase::did_muls(IntT) {
// TODO: calculate cost.
}
2022-05-27 15:12:10 +00:00
void ProcessorBase::did_shift(int bits_shifted) {
dynamic_instruction_length_ = bits_shifted;
2022-05-23 13:29:19 +00:00
}
template <bool use_current_instruction_pc> void ProcessorBase::raise_exception(int vector) {
// No overt action is taken here; instructions that might throw an exception are required
// to check-in after the fact.
//
// As implemented above, that means:
//
// * DIVU;
// * DIVS.
exception_vector_ = vector;
2022-05-20 16:58:45 +00:00
}
inline void ProcessorBase::tas(Preinstruction instruction, uint32_t) {
// This will be reached only if addressing mode is Dn.
const uint8_t value = registers_[instruction.reg(0)].b;
registers_[instruction.reg(0)].b |= 0x80;
status_.overflow_flag = status_.carry_flag = 0;
status_.zero_result = value;
status_.negative_flag = value & 0x80;
}
2022-05-24 00:42:41 +00:00
inline void ProcessorBase::move_to_usp(uint32_t address) {
stack_pointers_[0].l = address;
}
inline void ProcessorBase::move_from_usp(uint32_t &address) {
address = stack_pointers_[0].l;
}
// MARK: - External state.
template <class BusHandler, bool dtack_is_implicit, bool permit_overrun, bool signal_will_perform>
CPU::MC68000Mk2::State Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perform>::get_state() {
CPU::MC68000Mk2::State state;
// This isn't true, but will ensure that both stack_pointers_ have their proper values.
did_update_status();
for(int c = 0; c < 7; c++) {
state.registers.data[c] = registers_[c].l;
state.registers.address[c] = registers_[c + 8].l;
}
state.registers.data[7] = registers_[7].l;
state.registers.program_counter = program_counter_.l;
state.registers.status = status_.status();
state.registers.user_stack_pointer = stack_pointers_[0].l;
state.registers.supervisor_stack_pointer = stack_pointers_[1].l;
return state;
}
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>::set_state(const CPU::MC68000Mk2::State &state) {
// Copy registers and the program counter.
for(int c = 0; c < 7; c++) {
registers_[c].l = state.registers.data[c];
registers_[c + 8].l = state.registers.address[c];
}
registers_[7].l = state.registers.data[7];
program_counter_.l = state.registers.program_counter;
// Set status first in order to get the proper is-supervisor flag in place.
status_.set_status(state.registers.status);
// Update stack pointers, being careful to copy the right one.
stack_pointers_[0].l = state.registers.user_stack_pointer;
stack_pointers_[1].l = state.registers.supervisor_stack_pointer;
registers_[15] = stack_pointers_[is_supervisor_];
// Ensure the local is-supervisor flag is updated.
did_update_status();
}
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>::decode_from_state(const InstructionSet::M68k::RegisterSet &registers) {
// Populate registers.
CPU::MC68000Mk2::State state;
state.registers = registers;
set_state(state);
// Ensure the state machine will resume at decode.
state_ = Decode;
// Fill the prefetch queue.
captured_interrupt_level_ = bus_interrupt_level_;
read_program.value = &prefetch_.high;
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_.perform_bus_operation(read_program_announce, is_supervisor_);
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
program_counter_.l += 2;
}
2022-05-16 15:44:16 +00:00
}
}
#endif /* _8000Mk2Implementation_h */