2022-05-16 15:44:16 +00:00
|
|
|
|
//
|
2023-05-10 22:13:01 +00:00
|
|
|
|
// 68000Implementation.hpp
|
2022-05-16 15:44:16 +00:00
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 16/05/2022.
|
|
|
|
|
// Copyright © 2022 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2024-01-17 04:34:46 +00:00
|
|
|
|
#pragma once
|
2022-05-16 15:44:16 +00:00
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
#include <cassert>
|
2022-05-17 18:08:50 +00:00
|
|
|
|
#include <cstdio>
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2022-05-19 20:27:39 +00:00
|
|
|
|
#include "../../../InstructionSets/M68k/ExceptionVectors.hpp"
|
|
|
|
|
|
2023-05-10 22:13:01 +00:00
|
|
|
|
namespace CPU::MC68000 {
|
2022-05-16 15:44:16 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
#define AddressingDispatch(x) \
|
|
|
|
|
x, x##__end = x + InstructionSet::M68k::AddressingModeCount
|
|
|
|
|
|
2022-05-18 19:35:38 +00:00
|
|
|
|
/// 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 {
|
2022-06-30 01:40:48 +00:00
|
|
|
|
Reset,
|
2022-05-18 19:35:38 +00:00
|
|
|
|
Decode,
|
|
|
|
|
WaitForDTACK,
|
2022-06-15 14:50:03 +00:00
|
|
|
|
WaitForInterrupt,
|
2022-05-19 14:27:51 +00:00
|
|
|
|
|
2022-05-18 19:35:38 +00:00
|
|
|
|
StoreOperand,
|
2022-05-19 20:55:16 +00:00
|
|
|
|
StoreOperand_bw,
|
2022-05-19 15:23:26 +00:00
|
|
|
|
StoreOperand_l,
|
2022-05-18 19:35:38 +00:00
|
|
|
|
|
2022-05-19 20:27:39 +00:00
|
|
|
|
StandardException,
|
|
|
|
|
BusOrAddressErrorException,
|
2022-05-24 14:53:59 +00:00
|
|
|
|
DoInterrupt,
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-18 20:45:40 +00:00
|
|
|
|
// Specific addressing mode fetches.
|
2022-05-20 20:23:52 +00:00
|
|
|
|
//
|
|
|
|
|
// 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-18 20:45:40 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
/// Perform the proper sequence to fetch a byte or word operand.
|
2022-06-13 19:27:23 +00:00
|
|
|
|
/// i.e.
|
|
|
|
|
///
|
|
|
|
|
/// Dn/An/Q - (An) nr
|
|
|
|
|
/// (An)+ nr -(An) n nr
|
|
|
|
|
/// (d16, An) np nr (d8, An, Xn) n np nr
|
|
|
|
|
/// (d16, PC) np nr (d8, PC, Xn) n np nr
|
|
|
|
|
/// (xxx).w np nr (xxx).l np np nr
|
|
|
|
|
/// # np
|
2022-06-13 12:57:49 +00:00
|
|
|
|
AddressingDispatch(FetchOperand_bw),
|
2022-06-13 18:08:42 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
/// Perform the proper sequence to fetch a long-word operand.
|
2022-06-13 19:27:23 +00:00
|
|
|
|
/// i.e.
|
|
|
|
|
///
|
|
|
|
|
/// Dn/An/Q - (An) nR nr
|
|
|
|
|
/// (An)+ nR nr -(An) n nR nr
|
|
|
|
|
/// (d16, An) np nR nr (d8, An, Xn) n np nR nr
|
|
|
|
|
/// (d16, PC) np nR nr (d8, PC, Xn) n np nR nr
|
|
|
|
|
/// (xxx).w np nR nr (xxx).l np np nR nr
|
|
|
|
|
/// # np np
|
2022-06-13 12:57:49 +00:00
|
|
|
|
AddressingDispatch(FetchOperand_l),
|
2022-06-13 18:08:42 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
/// Perform the sequence to calculate an effective address, but don't fetch from it.
|
|
|
|
|
/// There's a lack of uniformity in the bus programs used by the 68000 for relevant
|
|
|
|
|
/// instructions; this entry point uses:
|
|
|
|
|
///
|
2022-06-13 19:27:23 +00:00
|
|
|
|
/// Dn/An - (An) -
|
2022-06-13 12:57:49 +00:00
|
|
|
|
/// (An)+ - -(An) -
|
|
|
|
|
/// (d16, An) np (d8, An, Xn) np n
|
|
|
|
|
/// (d16, PC) np (d8, PC, Xn) np n
|
|
|
|
|
/// (xxx).w np (xxx).l np np
|
|
|
|
|
AddressingDispatch(CalcEffectiveAddress),
|
2022-06-13 18:08:42 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
/// Similar to CalcEffectiveAddress, but varies slightly in the patterns:
|
|
|
|
|
///
|
|
|
|
|
/// -(An) n
|
|
|
|
|
/// (d8, An, Xn) n np n
|
|
|
|
|
/// (d8, PC, Xn) n np n
|
2022-06-13 14:27:22 +00:00
|
|
|
|
AddressingDispatch(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec),
|
2022-06-01 12:50:43 +00:00
|
|
|
|
|
2022-05-18 19:35:38 +00:00
|
|
|
|
// Various forms of perform; each of these will
|
|
|
|
|
// perform the current instruction, then do the
|
|
|
|
|
// indicated bus cycle.
|
|
|
|
|
|
|
|
|
|
Perform_np,
|
|
|
|
|
Perform_np_n,
|
2022-05-19 16:18:47 +00:00
|
|
|
|
Perform_np_nn,
|
2022-05-18 19:35:38 +00:00
|
|
|
|
|
2022-05-20 11:02:02 +00:00
|
|
|
|
TwoOp_Predec_bw,
|
|
|
|
|
TwoOp_Predec_l,
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
|
|
|
|
CHK,
|
|
|
|
|
CHK_no_trap,
|
|
|
|
|
CHK_was_over,
|
|
|
|
|
CHK_was_under,
|
2022-05-20 15:19:16 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2022-05-23 19:09:46 +00:00
|
|
|
|
Bccb,
|
|
|
|
|
Bccw,
|
2022-05-20 16:04:43 +00:00
|
|
|
|
Bcc_branch_taken,
|
2022-05-23 19:09:46 +00:00
|
|
|
|
Bccb_branch_not_taken,
|
|
|
|
|
Bccw_branch_not_taken,
|
2022-05-20 16:40:35 +00:00
|
|
|
|
|
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,
|
2022-05-20 18:29:14 +00:00
|
|
|
|
|
|
|
|
|
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,
|
2022-05-21 19:56:09 +00:00
|
|
|
|
|
|
|
|
|
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 20:08:30 +00:00
|
|
|
|
TAS,
|
2022-05-22 23:45:22 +00:00
|
|
|
|
MOVEtoCCRSR,
|
2022-05-23 01:16:38 +00:00
|
|
|
|
RTR,
|
|
|
|
|
RTE,
|
|
|
|
|
RTS,
|
2022-05-23 19:09:46 +00:00
|
|
|
|
LINKw,
|
2022-05-23 14:27:44 +00:00
|
|
|
|
UNLINK,
|
2022-05-23 19:09:46 +00:00
|
|
|
|
RESET,
|
|
|
|
|
NOP,
|
2022-05-24 14:25:40 +00:00
|
|
|
|
STOP,
|
2022-05-24 19:14:20 +00:00
|
|
|
|
TRAP,
|
|
|
|
|
TRAPV,
|
2022-06-11 01:52:07 +00:00
|
|
|
|
|
|
|
|
|
AddressRegisterIndirectWithIndex8bitDisplacement_n_np,
|
|
|
|
|
ProgramCounterIndirectWithIndex8bitDisplacement_n_np,
|
2022-06-13 18:08:42 +00:00
|
|
|
|
|
|
|
|
|
AddressingDispatch(PEA),
|
|
|
|
|
PEA_np_nS_ns, // Used to complete (An), (d16, [An/PC]) and (d8, [An/PC], Xn).
|
|
|
|
|
PEA_np_nS_ns_np, // Used to complete (xxx).w and (xxx).l
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
MOVE_b, MOVE_w,
|
|
|
|
|
AddressingDispatch(MOVE_bw), MOVE_bw_AbsoluteLong_prefetch_first,
|
2022-06-15 01:22:28 +00:00
|
|
|
|
AddressingDispatch(MOVE_l), MOVE_l_AbsoluteLong_prefetch_first,
|
2022-06-30 01:40:48 +00:00
|
|
|
|
|
|
|
|
|
Max
|
2022-05-18 19:35:38 +00:00
|
|
|
|
};
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
#undef AddressingDispatch
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
/// @returns The proper select lines for @c instruction's operand size, assuming it is either byte or word.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
template <typename InstructionT> OperationT data_select(const InstructionT &instruction) {
|
|
|
|
|
return OperationT(1 << int(instruction.operand_size()));
|
2022-06-14 21:04:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 19:05:11 +00:00
|
|
|
|
// MARK: - The state machine.
|
|
|
|
|
|
2022-06-30 01:13:00 +00:00
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-label"
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-05-16 15:59:03 +00:00
|
|
|
|
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.
|
2022-05-24 13:08:31 +00:00
|
|
|
|
e_clock_phase_ += duration;
|
2022-05-16 20:57:40 +00:00
|
|
|
|
time_remaining_ += duration;
|
2022-05-23 19:11:33 +00:00
|
|
|
|
if(time_remaining_ < HalfCycles(0)) return;
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
|
|
|
|
// Check whether all remaining time has been expended; if so then exit, having set this line up as
|
|
|
|
|
// the next resumption point.
|
2022-06-30 12:31:51 +00:00
|
|
|
|
#define ConsiderExit() if(time_remaining_ < HalfCycles(0)) { state_ = ExecutionState::Max + ((__COUNTER__+1) >> 1); return; } [[fallthrough]]; case ExecutionState::Max + (__COUNTER__ >> 1):
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// 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()
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
2022-05-17 01:00:25 +00:00
|
|
|
|
// Performs ConsiderExit() only if permit_overrun is true.
|
2022-05-16 15:59:03 +00:00
|
|
|
|
#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; }
|
2022-05-18 19:35:38 +00:00
|
|
|
|
|
|
|
|
|
// Sets the start position for state x.
|
2022-06-30 01:13:00 +00:00
|
|
|
|
#define BeginState(x) case ExecutionState::x: x
|
2022-06-13 12:57:49 +00:00
|
|
|
|
|
|
|
|
|
// Sets the start position for the addressing mode y within state x,
|
|
|
|
|
// where x was declared as an AddressingDispatch.
|
|
|
|
|
#define BeginStateMode(x, y) case ExecutionState::x + int(InstructionSet::M68k::AddressingMode::y) + 1
|
|
|
|
|
|
|
|
|
|
// Moves dynamically to addressing mode y within state x, where x was declared
|
|
|
|
|
// as an AddressingDispatch.
|
|
|
|
|
#define MoveToAddressingMode(x, y) MoveToStateDynamic(ExecutionState::x + int(y) + 1)
|
2022-05-17 01:00:25 +00:00
|
|
|
|
|
2022-05-16 15:59:03 +00:00
|
|
|
|
//
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// So basic structure is, in general:
|
2022-05-16 15:59:03 +00:00
|
|
|
|
//
|
2022-05-18 19:35:38 +00:00
|
|
|
|
// BeginState(Action):
|
2022-05-16 15:59:03 +00:00
|
|
|
|
// do_something();
|
|
|
|
|
// Spend(20);
|
|
|
|
|
// do_something_else();
|
|
|
|
|
// Spend(10);
|
|
|
|
|
// do_a_third_thing();
|
|
|
|
|
// Spend(30);
|
2022-05-18 19:35:38 +00:00
|
|
|
|
// MoveToState(next_action);
|
2022-05-16 15:59:03 +00:00
|
|
|
|
//
|
|
|
|
|
// 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
|
2022-05-16 15:59:03 +00:00
|
|
|
|
// 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)
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
// 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.
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
// Helper macros for common bus transactions:
|
|
|
|
|
|
2022-06-08 18:43:31 +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);
|
|
|
|
|
|
|
|
|
|
// Raises a bus/address error with integer vector x for access v.
|
|
|
|
|
// x is the vector identifier, not its address.
|
|
|
|
|
#define RaiseBusOrAddressError(x, v) \
|
|
|
|
|
exception_vector_ = InstructionSet::M68k::x; \
|
|
|
|
|
bus_error_ = v; \
|
|
|
|
|
MoveToStateSpecific(BusOrAddressErrorException);
|
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// Performs the bus operation and then applies a `Spend` of its length
|
|
|
|
|
// plus any additional length returned by the bus handler.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
#define PerformBusOperation(x) \
|
|
|
|
|
delay = bus_handler_.perform_bus_operation(x, is_supervisor_); \
|
2022-05-16 20:57:40 +00:00
|
|
|
|
Spend(x.length + delay)
|
|
|
|
|
|
2023-11-27 16:48:34 +00:00
|
|
|
|
// TODO: the templated operation type to perform_bus_operation is intended to allow a much
|
|
|
|
|
// cheaper through cost where the operation is knowable in advance. So use that pathway.
|
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// 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); \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
PerformBusOperation(idle)
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
|
|
|
|
// holding the bus cycle provided.
|
2022-06-30 12:31:51 +00:00
|
|
|
|
#define WaitForDTACK(x) \
|
|
|
|
|
if constexpr (!dtack_is_implicit && !dtack_ && !vpa_ && !berr_) { \
|
|
|
|
|
awaiting_dtack = x; \
|
|
|
|
|
awaiting_dtack.length = HalfCycles(2); \
|
|
|
|
|
post_dtack_state_ = ExecutionState::Max + ((__COUNTER__ + 1) >> 1); \
|
|
|
|
|
state_ = ExecutionState::WaitForDTACK; \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
[[fallthrough]]; case ExecutionState::Max + (__COUNTER__ >> 1):
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
// Performs the bus operation provided, which will be one with a
|
|
|
|
|
// SelectWord or SelectByte operation, stretching it to match the E
|
2022-05-24 13:08:31 +00:00
|
|
|
|
// bus if VPA is currently asserted or seguing elsewhere if a bus
|
|
|
|
|
// error is signalled or an adress error observed.
|
2022-05-16 20:57:40 +00:00
|
|
|
|
//
|
2022-05-24 13:08:31 +00:00
|
|
|
|
// E clock behaviour implemented, which I think is correct:
|
2022-05-16 20:57:40 +00:00
|
|
|
|
//
|
2022-05-24 13:08:31 +00:00
|
|
|
|
// (1) wait until end of current 10-cycle window;
|
|
|
|
|
// (2) run for the next 10-cycle window.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
#define CompleteAccess(x) \
|
2022-06-08 18:43:31 +00:00
|
|
|
|
if(berr_) { \
|
|
|
|
|
RaiseBusOrAddressError(AccessFault, x); \
|
2022-05-24 13:08:31 +00:00
|
|
|
|
} \
|
|
|
|
|
if(vpa_) { \
|
|
|
|
|
x.length = HalfCycles(20) + (HalfCycles(20) + (e_clock_phase_ - time_remaining_) % HalfCycles(20)) % HalfCycles(20); \
|
|
|
|
|
} else { \
|
|
|
|
|
x.length = HalfCycles(4); \
|
|
|
|
|
} \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
PerformBusOperation(x)
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
// Performs the memory access implied by the announce, perform pair,
|
|
|
|
|
// honouring DTACK, BERR and VPA as necessary.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
#define AccessPair(val, announce, perform) \
|
2023-11-27 16:48:34 +00:00
|
|
|
|
perform.value = &val; \
|
|
|
|
|
if constexpr (!dtack_is_implicit) { \
|
|
|
|
|
announce.length = HalfCycles(4); \
|
|
|
|
|
} \
|
|
|
|
|
if(*perform.address & (perform.operation >> 1) & 1) { \
|
|
|
|
|
RaiseBusOrAddressError(AddressError, perform); \
|
|
|
|
|
} \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
PerformBusOperation(announce); \
|
2023-11-27 16:48:34 +00:00
|
|
|
|
WaitForDTACK(announce); \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
CompleteAccess(perform);
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2022-05-19 16:18:47 +00:00
|
|
|
|
// Sets up the next data access size and read flags.
|
|
|
|
|
#define SetupDataAccess(read_flag, select_flag) \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
access_announce.operation = Operation::NewAddress | Operation::IsData | (read_flag); \
|
|
|
|
|
access.operation = Operation::SameAddress | Operation::IsData | (read_flag) | (select_flag);
|
2022-05-19 14:27:51 +00:00
|
|
|
|
|
2022-05-19 16:18:47 +00:00
|
|
|
|
// Sets the address source for the next data access.
|
|
|
|
|
#define SetDataAddress(addr) \
|
|
|
|
|
access.address = access_announce.address = &addr;
|
|
|
|
|
|
2022-05-19 14:27:51 +00:00
|
|
|
|
// Performs the access established by SetupDataAccess into val.
|
|
|
|
|
#define Access(val) \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessPair(val, access_announce, access)
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2023-11-28 18:50:53 +00:00
|
|
|
|
// Performs the access established by SetupDataAccess into val.
|
2023-12-22 04:08:18 +00:00
|
|
|
|
#define AccessOp(val) \
|
|
|
|
|
AccessPair(val, access_announce, access)
|
2023-11-28 18:50:53 +00:00
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// Reads the program (i.e. non-data) word from addr into val.
|
2022-05-19 14:27:51 +00:00
|
|
|
|
#define ReadProgramWord(val) \
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessPair(val, read_program_announce, read_program); \
|
2022-05-16 20:57:40 +00:00
|
|
|
|
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() \
|
|
|
|
|
prefetch_.high = prefetch_.low; \
|
2022-06-03 19:39:53 +00:00
|
|
|
|
ReadProgramWord(prefetch_.low) \
|
|
|
|
|
captured_interrupt_level_ = bus_interrupt_level_;
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// Copies the current program counter, adjusted to allow for the prefetch queue,
|
|
|
|
|
// into the instruction_address_ latch, which is the source of the value written
|
|
|
|
|
// during exceptions.
|
|
|
|
|
#define ReloadInstructionAddress() \
|
|
|
|
|
instruction_address_.l = program_counter_.l - 4
|
|
|
|
|
|
2022-05-17 18:08:50 +00:00
|
|
|
|
using Mode = InstructionSet::M68k::AddressingMode;
|
|
|
|
|
|
2022-05-16 15:59:03 +00:00
|
|
|
|
// Otherwise continue for all time, until back in debt.
|
|
|
|
|
// Formatting is slightly obtuse here to make this look more like a coroutine.
|
2022-05-16 20:57:40 +00:00
|
|
|
|
while(true) { switch(state_) {
|
|
|
|
|
|
|
|
|
|
// Spin in place, one cycle at a time, until one of DTACK,
|
|
|
|
|
// BERR or VPA is asserted.
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(WaitForDTACK):
|
2023-12-22 04:08:18 +00:00
|
|
|
|
PerformBusOperation(awaiting_dtack);
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
if(dtack_ || berr_ || vpa_) {
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_dtack_state_);
|
2022-05-16 20:57:40 +00:00
|
|
|
|
}
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(WaitForDTACK);
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2022-05-24 14:25:40 +00:00
|
|
|
|
BeginState(STOP):
|
2022-06-15 14:50:03 +00:00
|
|
|
|
// Apply the suffix status.
|
2022-06-15 14:54:14 +00:00
|
|
|
|
status_.set_status(prefetch_.w);
|
2022-06-15 14:50:03 +00:00
|
|
|
|
did_update_status();
|
2022-06-16 19:10:19 +00:00
|
|
|
|
|
|
|
|
|
// Ensure that after this STOP exits, the eventual RTE returns to
|
|
|
|
|
// after the STOP rather than to it. Unlike most instructions, STOP
|
|
|
|
|
// has not prefetched the next instruction, so the program counter
|
|
|
|
|
// is at the actual return point, not beyond it.
|
|
|
|
|
instruction_address_.l = program_counter_.l;
|
|
|
|
|
|
2022-06-15 14:50:03 +00:00
|
|
|
|
[[fallthrough]];
|
|
|
|
|
|
|
|
|
|
BeginState(WaitForInterrupt):
|
|
|
|
|
// Spin in place until an interrupt arrives.
|
2022-05-24 14:53:59 +00:00
|
|
|
|
captured_interrupt_level_ = bus_interrupt_level_;
|
2022-06-03 12:31:35 +00:00
|
|
|
|
if(status_.would_accept_interrupt(captured_interrupt_level_)) {
|
2022-05-24 14:53:59 +00:00
|
|
|
|
MoveToStateSpecific(DoInterrupt);
|
|
|
|
|
}
|
2022-06-15 14:50:03 +00:00
|
|
|
|
IdleBus(1);
|
2022-06-15 14:56:45 +00:00
|
|
|
|
CheckOverrun();
|
2022-06-15 14:50:03 +00:00
|
|
|
|
MoveToStateSpecific(WaitForInterrupt);
|
2022-05-24 14:25:40 +00:00
|
|
|
|
|
2022-05-16 20:57:40 +00:00
|
|
|
|
// Perform the RESET exception, which seeds the stack pointer and program
|
|
|
|
|
// counter, populates the prefetch queue, and then moves to instruction dispatch.
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(Reset):
|
2023-05-16 20:40:09 +00:00
|
|
|
|
IdleBus(7); // (n-)*5 nn
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
2022-05-17 18:51:49 +00:00
|
|
|
|
// Establish general reset state.
|
2022-06-01 12:20:33 +00:00
|
|
|
|
status_.begin_exception(7);
|
2022-05-24 19:47:47 +00:00
|
|
|
|
should_trace_ = 0;
|
2022-05-17 18:51:49 +00:00
|
|
|
|
did_update_status();
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-20 16:40:35 +00:00
|
|
|
|
SetDataAddress(temporary_address_.l);
|
2022-05-19 14:27:51 +00:00
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
temporary_address_.l = 0;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(registers_[15].high); // nF
|
2022-05-17 20:10:20 +00:00
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(registers_[15].low); // nf
|
2022-05-17 20:10:20 +00:00
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.high); // nV
|
2022-05-17 20:10:20 +00:00
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.low); // nv
|
2022-05-16 20:57:40 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(Decode);
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
2022-05-19 20:27:39 +00:00
|
|
|
|
// Perform a 'standard' exception, i.e. a Group 1 or 2.
|
|
|
|
|
BeginState(StandardException):
|
2022-05-24 19:42:50 +00:00
|
|
|
|
// Switch to supervisor mode, disable interrupts.
|
2022-06-01 12:20:33 +00:00
|
|
|
|
captured_status_.w = status_.begin_exception();
|
2022-05-24 19:47:47 +00:00
|
|
|
|
should_trace_ = 0;
|
2022-05-19 20:27:39 +00:00
|
|
|
|
did_update_status();
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
|
|
|
|
// Push status and current program counter.
|
|
|
|
|
// Write order is wacky here, but I think it's correct.
|
2022-06-08 20:15:11 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(instruction_address_.low); // ns
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-06-08 20:15:11 +00:00
|
|
|
|
registers_[15].l -= 4;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(captured_status_); // ns
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-06-08 20:15:11 +00:00
|
|
|
|
registers_[15].l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(instruction_address_.high); // nS
|
2022-05-19 20:27:39 +00:00
|
|
|
|
registers_[15].l -= 2;
|
|
|
|
|
|
|
|
|
|
// Grab new program counter.
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-20 16:40:35 +00:00
|
|
|
|
SetDataAddress(temporary_address_.l);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-24 15:05:24 +00:00
|
|
|
|
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.high); // nV
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.low); // nv
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
|
|
|
|
// Populate the prefetch queue.
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(Decode);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
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:
|
|
|
|
|
//
|
2023-05-12 18:14:45 +00:00
|
|
|
|
// R/W, I/N, function code word; [at -14]
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// access address; [-12]
|
2022-05-24 19:42:50 +00:00
|
|
|
|
// instruction register; [-8]
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// status register; [-6]
|
|
|
|
|
// program counter. [-4]
|
2022-05-24 19:42:50 +00:00
|
|
|
|
//
|
|
|
|
|
// With the instruction register definitely being written before the
|
|
|
|
|
// function code word.
|
|
|
|
|
//
|
|
|
|
|
// And the documented bus pattern is:
|
|
|
|
|
//
|
2023-05-16 20:40:09 +00:00
|
|
|
|
// nn ns ns nS ns ns ns nS nV nv np n np
|
2022-05-24 19:42:50 +00:00
|
|
|
|
//
|
|
|
|
|
// So, based on the hoopy ordering of a standard exception, maybe:
|
|
|
|
|
//
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// 1) program counter low;
|
|
|
|
|
// 2) captured state;
|
|
|
|
|
// 3) program counter high;
|
|
|
|
|
// 4) instruction register;
|
2022-06-09 20:17:09 +00:00
|
|
|
|
// 5) access address low;
|
|
|
|
|
// 6) function code;
|
|
|
|
|
// 7) access address high?
|
|
|
|
|
//
|
|
|
|
|
// Noteworthy in this guess: access code and function code are written in
|
|
|
|
|
// the same interleaved order as program counter and captured status register,
|
|
|
|
|
// which is the order that I know to be correct for a standard exception.
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(2);
|
|
|
|
|
|
|
|
|
|
// Switch to supervisor mode, disable interrupts.
|
2022-06-12 01:10:24 +00:00
|
|
|
|
captured_status_.w = status_.begin_exception();
|
2022-05-24 19:47:47 +00:00
|
|
|
|
should_trace_ = 0;
|
2022-05-24 19:42:50 +00:00
|
|
|
|
did_update_status();
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-24 19:42:50 +00:00
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// Guess: the written program counter is adjusted to discount the prefetch queue.
|
|
|
|
|
// COMPLETE GUESS.
|
|
|
|
|
temporary_address_.l = program_counter_.l - 4;
|
2022-05-24 19:42:50 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_address_.low); // ns [pc.l]
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
registers_[15].l -= 4;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(captured_status_); // ns [sr]
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
registers_[15].l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_address_.high); // nS [pc.h]
|
2022-06-03 12:27:49 +00:00
|
|
|
|
|
|
|
|
|
registers_[15].l -= 4;
|
2022-05-24 19:42:50 +00:00
|
|
|
|
temporary_value_.w = opcode_;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_value_.low); // ns [instruction register]
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
// Construct the function code; which is:
|
|
|
|
|
//
|
|
|
|
|
// b4: 1 = was a read; 0 = was a write;
|
|
|
|
|
// b3: 0 = was reading an instruction; 1 = wasn't;
|
|
|
|
|
// b2–b0: the regular 68000 function code;
|
|
|
|
|
// [all other bits]: left over from the instruction register write, above.
|
|
|
|
|
//
|
|
|
|
|
// I'm unable to come up with a reason why the function code isn't duplicative
|
|
|
|
|
// of b3, but given the repetition of supervisor state which is also in the
|
|
|
|
|
// captured status register I guess maybe it is just duplicative.
|
|
|
|
|
temporary_value_.w =
|
|
|
|
|
(temporary_value_.w & ~31) |
|
2023-12-21 21:03:53 +00:00
|
|
|
|
((bus_error_.operation & Operation::Read) ? 0x10 : 0x00) |
|
|
|
|
|
((bus_error_.operation & Operation::IsProgram) ? 0x08 : 0x00) |
|
|
|
|
|
((bus_error_.operation & Operation::IsProgram) ? 0x02 : 0x01) |
|
2022-06-03 12:27:49 +00:00
|
|
|
|
((captured_status_.w & InstructionSet::M68k::ConditionCode::Supervisor) ? 0x04 : 0x00);
|
2022-05-24 19:42:50 +00:00
|
|
|
|
temporary_address_.l = *bus_error_.address;
|
|
|
|
|
|
|
|
|
|
registers_[15].l -= 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_address_.low); // ns [error address.l]
|
2022-06-09 20:17:09 +00:00
|
|
|
|
|
|
|
|
|
registers_[15].l -= 4;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_value_.low); // ns [function code]
|
2022-06-09 20:17:09 +00:00
|
|
|
|
|
|
|
|
|
registers_[15].l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(temporary_address_.high); // nS [error address.h]
|
2022-06-03 12:27:49 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
|
|
|
|
// Grab new program counter.
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-24 19:42:50 +00:00
|
|
|
|
SetDataAddress(temporary_address_.l);
|
|
|
|
|
|
|
|
|
|
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.high); // nV
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.low); // nv
|
2022-05-24 19:42:50 +00:00
|
|
|
|
|
|
|
|
|
// 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.
|
2022-06-01 12:20:33 +00:00
|
|
|
|
captured_status_.w = status_.begin_exception(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.
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-24 14:53:59 +00:00
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
2022-05-28 01:54:23 +00:00
|
|
|
|
// Push low part of program counter.
|
2022-05-24 14:53:59 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(instruction_address_.low); // ns
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
// 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);
|
2023-12-22 04:12:14 +00:00
|
|
|
|
interrupt_cycle0.address = interrupt_cycle1.address = &temporary_address_.l;
|
|
|
|
|
interrupt_cycle0.value = interrupt_cycle1.value = &temporary_value_.low;
|
|
|
|
|
PerformBusOperation(interrupt_cycle0);
|
|
|
|
|
CompleteAccess(interrupt_cycle1); // ni
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
// If VPA is set, autovector.
|
|
|
|
|
if(vpa_) {
|
2022-06-04 19:20:38 +00:00
|
|
|
|
temporary_value_.b = uint8_t(InstructionSet::M68k::Exception::InterruptAutovectorBase - 1 + captured_interrupt_level_);
|
2022-05-24 14:53:59 +00:00
|
|
|
|
}
|
2022-06-15 15:00:27 +00:00
|
|
|
|
if(berr_) {
|
|
|
|
|
temporary_value_.b = uint8_t(InstructionSet::M68k::Exception::SpuriousInterrupt);
|
|
|
|
|
}
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
2022-06-15 15:00:27 +00:00
|
|
|
|
// TODO: check documentation for other potential interrupt outcomes;
|
|
|
|
|
// and presumably spin here if DTACK isn't implicit.
|
2022-05-27 00:20:28 +00:00
|
|
|
|
|
2022-06-03 12:27:49 +00:00
|
|
|
|
IdleBus(3); // n- n
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
// Do the rest of the stack work.
|
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
2022-05-28 01:54:23 +00:00
|
|
|
|
registers_[15].l -= 4;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(captured_status_); // ns
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
2022-05-28 01:54:23 +00:00
|
|
|
|
registers_[15].l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(instruction_address_.high); // nS
|
2022-05-24 14:53:59 +00:00
|
|
|
|
registers_[15].l -= 2;
|
|
|
|
|
|
|
|
|
|
// Grab new program counter.
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-24 14:53:59 +00:00
|
|
|
|
SetDataAddress(temporary_address_.l);
|
|
|
|
|
|
2022-06-04 19:20:38 +00:00
|
|
|
|
temporary_address_.l = uint32_t(temporary_value_.b << 2);
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.high); // nV
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
temporary_address_.l += 2;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
AccessOp(program_counter_.low); // nv
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
// 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.
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(Decode):
|
2022-05-18 20:45:40 +00:00
|
|
|
|
CheckOverrun();
|
|
|
|
|
|
2022-05-24 14:53:59 +00:00
|
|
|
|
// Capture the address of the next instruction.
|
2022-06-03 12:27:49 +00:00
|
|
|
|
ReloadInstructionAddress();
|
2022-05-24 14:53:59 +00:00
|
|
|
|
|
|
|
|
|
// Head off into an interrupt if one is found.
|
2022-06-03 12:31:35 +00:00
|
|
|
|
if(status_.would_accept_interrupt(captured_interrupt_level_)) {
|
2022-05-24 14:53:59 +00:00
|
|
|
|
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_);
|
2022-05-17 18:08:50 +00:00
|
|
|
|
|
2022-05-19 22:50:43 +00:00
|
|
|
|
// Signal the bus handler if requested.
|
|
|
|
|
if constexpr (signal_will_perform) {
|
2022-06-01 19:27:09 +00:00
|
|
|
|
// Set the state to Decode, so that if the callee pulls any shenanigans in order
|
|
|
|
|
// to force an exit here, the interpreter can resume without skipping a beat.
|
|
|
|
|
//
|
|
|
|
|
// signal_will_perform is overtly a debugging/testing feature.
|
|
|
|
|
state_ = Decode;
|
2022-05-19 22:50:43 +00:00
|
|
|
|
bus_handler_.will_perform(instruction_address_.l, opcode_);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 19:05:11 +00:00
|
|
|
|
// Ensure the first parameter is next fetched.
|
2022-05-17 18:08:50 +00:00
|
|
|
|
next_operand_ = 0;
|
2022-05-18 20:45:40 +00:00
|
|
|
|
|
2022-05-29 18:56:44 +00:00
|
|
|
|
/// If operation x requires supervisor privileges, checks whether the user is currently in supervisor mode;
|
|
|
|
|
/// if not then raises a privilege violation exception.
|
2022-06-01 12:20:33 +00:00
|
|
|
|
#define CheckSupervisor(x) \
|
|
|
|
|
if constexpr (InstructionSet::M68k::requires_supervisor<InstructionSet::M68k::Model::M68000>(InstructionSet::M68k::Operation::x)) { \
|
|
|
|
|
if(!status_.is_supervisor) { \
|
|
|
|
|
RaiseException(InstructionSet::M68k::Exception::PrivilegeViolation); \
|
|
|
|
|
} \
|
2022-05-29 18:56:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CASE(x) \
|
|
|
|
|
case InstructionSet::M68k::Operation::x: \
|
|
|
|
|
CheckSupervisor(x); \
|
2022-05-19 19:03:22 +00:00
|
|
|
|
operand_flags_ = InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::x>();
|
|
|
|
|
|
|
|
|
|
#define StdCASE(x, y) \
|
|
|
|
|
CASE(x) \
|
2022-05-19 16:18:47 +00:00
|
|
|
|
y; \
|
|
|
|
|
\
|
|
|
|
|
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::LongWord) { \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord); \
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(FetchOperand_l); \
|
2022-05-19 14:27:51 +00:00
|
|
|
|
} else { \
|
2022-05-19 16:18:47 +00:00
|
|
|
|
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::Byte) { \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectByte); \
|
2022-05-19 16:18:47 +00:00
|
|
|
|
} else { \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord); \
|
2022-05-19 16:18:47 +00:00
|
|
|
|
} \
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(FetchOperand_bw); \
|
2022-05-19 16:18:47 +00:00
|
|
|
|
}
|
2022-05-19 14:27:51 +00:00
|
|
|
|
|
2022-05-19 19:49:42 +00:00
|
|
|
|
#define Duplicate(x, y) \
|
|
|
|
|
case InstructionSet::M68k::Operation::x: \
|
|
|
|
|
static_assert( \
|
2022-06-01 13:22:47 +00:00
|
|
|
|
InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::x>() == \
|
|
|
|
|
InstructionSet::M68k::operand_flags<InstructionSet::M68k::Model::M68000, InstructionSet::M68k::Operation::y>() && \
|
2023-05-12 18:14:45 +00:00
|
|
|
|
InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == \
|
2022-06-01 13:22:47 +00:00
|
|
|
|
InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::y>() && \
|
2023-05-12 18:14:45 +00:00
|
|
|
|
InstructionSet::M68k::requires_supervisor<InstructionSet::M68k::Model::M68000>(InstructionSet::M68k::Operation::x) == \
|
2022-06-01 13:22:47 +00:00
|
|
|
|
InstructionSet::M68k::requires_supervisor<InstructionSet::M68k::Model::M68000>(InstructionSet::M68k::Operation::y) \
|
|
|
|
|
); \
|
2022-05-20 11:02:02 +00:00
|
|
|
|
[[fallthrough]];
|
2022-05-19 19:49:42 +00:00
|
|
|
|
|
2022-05-29 18:56:44 +00:00
|
|
|
|
#define SpecialCASE(x) case InstructionSet::M68k::Operation::x: CheckSupervisor(x); MoveToStateSpecific(x)
|
2022-05-23 19:09:46 +00:00
|
|
|
|
|
2022-05-18 20:45:40 +00:00
|
|
|
|
switch(instruction_.operation) {
|
2022-05-24 19:52:53 +00:00
|
|
|
|
case InstructionSet::M68k::Operation::Undefined:
|
2023-05-15 14:17:04 +00:00
|
|
|
|
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;
|
2022-05-24 16:42:34 +00:00
|
|
|
|
}
|
2023-05-15 14:17:04 +00:00
|
|
|
|
MoveToStateSpecific(StandardException);
|
2022-05-24 16:42:34 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
StdCASE(NBCD, {
|
2022-05-18 20:45:40 +00:00
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
|
|
|
|
perform_state_ = Perform_np_n;
|
|
|
|
|
} else {
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
}
|
2022-05-19 16:18:47 +00:00
|
|
|
|
})
|
|
|
|
|
|
2022-05-19 20:30:08 +00:00
|
|
|
|
Duplicate(CLRb, NEGXb) Duplicate(NEGb, NEGXb) Duplicate(NOTb, NEGXb)
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(NEGXb, perform_state_ = Perform_np);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-19 20:30:08 +00:00
|
|
|
|
Duplicate(CLRw, NEGXw) Duplicate(NEGw, NEGXw) Duplicate(NOTw, NEGXw)
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(NEGXw, perform_state_ = Perform_np);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-19 20:30:08 +00:00
|
|
|
|
Duplicate(CLRl, NEGXl) Duplicate(NEGl, NEGXl) Duplicate(NOTl, NEGXl)
|
2022-05-19 20:27:39 +00:00
|
|
|
|
StdCASE(NEGXl,
|
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
|
|
|
|
perform_state_ = Perform_np_n;
|
|
|
|
|
} else {
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(SWAP, perform_state_ = Perform_np);
|
|
|
|
|
StdCASE(EXG, perform_state_ = Perform_np_n);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(EXTbtow, perform_state_ = Perform_np);
|
|
|
|
|
StdCASE(EXTwtol, perform_state_ = Perform_np);
|
2022-05-19 19:41:02 +00:00
|
|
|
|
|
2022-06-14 01:49:00 +00:00
|
|
|
|
StdCASE(MOVEb, perform_state_ = MOVE_b);
|
2022-05-24 15:30:09 +00:00
|
|
|
|
Duplicate(MOVEAw, MOVEw)
|
2022-06-14 21:04:11 +00:00
|
|
|
|
StdCASE(MOVEw, perform_state_ = MOVE_w);
|
2022-05-24 15:30:09 +00:00
|
|
|
|
Duplicate(MOVEAl, MOVEl)
|
2022-06-15 01:22:28 +00:00
|
|
|
|
StdCASE(MOVEl, perform_state_ = MOVE_l);
|
2022-05-19 16:18:47 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
StdCASE(CMPb, perform_state_ = Perform_np);
|
|
|
|
|
StdCASE(CMPw, perform_state_ = Perform_np);
|
2022-05-29 18:49:42 +00:00
|
|
|
|
StdCASE(CMPl,
|
|
|
|
|
perform_state_ = instruction_.mode(1) == Mode::DataRegisterDirect ? Perform_np_n : Perform_np
|
|
|
|
|
);
|
2022-05-19 16:18:47 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
StdCASE(CMPAw, perform_state_ = Perform_np_n);
|
|
|
|
|
StdCASE(CMPAl, perform_state_ = Perform_np_n);
|
2022-05-19 16:18:47 +00:00
|
|
|
|
|
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, {
|
2022-05-19 16:18:47 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-05-18 20:45:40 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
StdCASE(EORb, perform_state_ = Perform_np);
|
|
|
|
|
StdCASE(EORw, perform_state_ = Perform_np);
|
|
|
|
|
StdCASE(EORl, {
|
2022-05-19 16:18:47 +00:00
|
|
|
|
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;
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(FetchOperand_bw);
|
2022-05-19 19:19:00 +00:00
|
|
|
|
} else {
|
2023-12-21 21:03:53 +00:00
|
|
|
|
select_flag_ = Operation::SelectByte;
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(TwoOp_Predec_bw);
|
2022-05-19 19:19:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-22 19:20:30 +00:00
|
|
|
|
StdCASE(CHKw, perform_state_ = CHK);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-20 11:02:02 +00:00
|
|
|
|
Duplicate(SUBb, ADDb) StdCASE(ADDb, perform_state_ = Perform_np)
|
|
|
|
|
Duplicate(SUBw, ADDw) StdCASE(ADDw, perform_state_ = Perform_np)
|
|
|
|
|
Duplicate(SUBl, ADDl) StdCASE(ADDl, {
|
2022-05-29 18:34:06 +00:00
|
|
|
|
if(instruction_.mode(0) == Mode::Quick) {
|
|
|
|
|
perform_state_ = (
|
|
|
|
|
instruction_.mode(1) == Mode::AddressRegisterDirect ||
|
|
|
|
|
instruction_.mode(1) == Mode::DataRegisterDirect
|
|
|
|
|
) ? Perform_np_nn : Perform_np;
|
2022-05-20 11:02:02 +00:00
|
|
|
|
} else {
|
2022-05-29 18:34:06 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2022-05-20 11:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Duplicate(SUBAw, ADDAw) StdCASE(ADDAw, perform_state_ = Perform_np_nn)
|
|
|
|
|
Duplicate(SUBAl, ADDAl) StdCASE(ADDAl, {
|
2022-06-01 19:03:03 +00:00
|
|
|
|
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;
|
2022-05-20 11:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Duplicate(SUBXb, ADDXb) StdCASE(ADDXb, {
|
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
} else {
|
2023-12-21 21:03:53 +00:00
|
|
|
|
select_flag_ = Operation::SelectByte;
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(TwoOp_Predec_bw);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
Duplicate(SUBXw, ADDXw) StdCASE(ADDXw, {
|
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
} else {
|
2023-12-21 21:03:53 +00:00
|
|
|
|
select_flag_ = Operation::SelectWord;
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(TwoOp_Predec_bw);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
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);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2022-05-20 15:19:16 +00:00
|
|
|
|
StdCASE(Scc, {
|
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
|
|
|
|
perform_state_ = Scc_Dn;
|
|
|
|
|
} else {
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2022-05-23 19:09:46 +00:00
|
|
|
|
SpecialCASE(DBcc);
|
2022-05-20 15:32:06 +00:00
|
|
|
|
|
2022-05-23 19:09:46 +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-20 16:40:35 +00:00
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2022-05-20 18:29:14 +00:00
|
|
|
|
Duplicate(ORItoCCR, EORItoCCR); Duplicate(ANDItoCCR, EORItoCCR);
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(EORItoCCR, perform_state_ = LogicalToSR);
|
2022-05-20 18:29:14 +00:00
|
|
|
|
|
|
|
|
|
Duplicate(ORItoSR, EORItoSR); Duplicate(ANDItoSR, EORItoSR);
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(EORItoSR, perform_state_ = LogicalToSR);
|
2022-05-20 18:29:14 +00:00
|
|
|
|
|
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);
|
|
|
|
|
|
2022-10-22 19:20:30 +00:00
|
|
|
|
StdCASE(DIVUw, perform_state_ = DIVU_DIVS);
|
|
|
|
|
StdCASE(DIVSw, perform_state_ = DIVU_DIVS);
|
|
|
|
|
StdCASE(MULUw, perform_state_ = Perform_idle_dyamic_Dn);
|
|
|
|
|
StdCASE(MULSw, perform_state_ = Perform_idle_dyamic_Dn);
|
2022-05-21 19:56:09 +00:00
|
|
|
|
|
2022-05-22 12:29:12 +00:00
|
|
|
|
StdCASE(LEA, {
|
|
|
|
|
post_ea_state_ = LEA;
|
2022-06-10 19:57:35 +00:00
|
|
|
|
MoveToStateSpecific(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec);
|
2022-05-22 12:29:12 +00:00
|
|
|
|
});
|
2022-06-13 18:08:42 +00:00
|
|
|
|
SpecialCASE(PEA);
|
2022-05-22 12:29:12 +00:00
|
|
|
|
|
2022-05-22 20:08:30 +00:00
|
|
|
|
StdCASE(TAS, {
|
|
|
|
|
// TAS uses a special atomic bus cycle for memory accesses,
|
|
|
|
|
// but is also available as DataRegisterDirect, with no
|
2022-06-10 19:57:35 +00:00
|
|
|
|
// memory access whatsoever. It's also atypical in its layout
|
|
|
|
|
// for (d8, An, Xn). So segue here appropriately.
|
|
|
|
|
switch(instruction_.mode(0)) {
|
|
|
|
|
case Mode::DataRegisterDirect:
|
|
|
|
|
perform_state_ = Perform_np;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
|
2022-06-11 01:52:07 +00:00
|
|
|
|
post_ea_state_ = TAS;
|
|
|
|
|
MoveToStateSpecific(AddressRegisterIndirectWithIndex8bitDisplacement_n_np);
|
2022-06-10 19:57:35 +00:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
post_ea_state_ = TAS;
|
|
|
|
|
MoveToStateSpecific(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec);
|
|
|
|
|
}
|
2022-05-22 20:08:30 +00:00
|
|
|
|
});
|
|
|
|
|
|
2023-05-12 18:14:45 +00:00
|
|
|
|
StdCASE(MOVEtoCCR, perform_state_ = MOVEtoCCRSR);
|
|
|
|
|
StdCASE(MOVEtoSR, perform_state_ = MOVEtoCCRSR);
|
2022-05-22 23:45:22 +00:00
|
|
|
|
StdCASE(MOVEfromSR, {
|
|
|
|
|
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
2022-05-27 14:32:28 +00:00
|
|
|
|
perform_state_ = Perform_np_n;
|
2022-05-22 23:45:22 +00:00
|
|
|
|
} else {
|
2022-05-27 14:32:28 +00:00
|
|
|
|
perform_state_ = Perform_np;
|
2022-05-22 23:45:22 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2022-05-23 19:09:46 +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
|
|
|
|
|
|
2022-05-23 19:09:46 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2022-05-18 20:45:40 +00:00
|
|
|
|
default:
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 19:49:42 +00:00
|
|
|
|
#undef Duplicate
|
|
|
|
|
#undef StdCASE
|
2022-05-18 20:45:40 +00:00
|
|
|
|
#undef CASE
|
2022-05-23 19:09:46 +00:00
|
|
|
|
#undef SpecialCASE
|
2022-05-29 18:56:44 +00:00
|
|
|
|
#undef CheckSupervisor
|
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-18 20:45:40 +00:00
|
|
|
|
|
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):
|
2022-05-18 20:45:40 +00:00
|
|
|
|
// 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.
|
2022-06-15 16:59:03 +00:00
|
|
|
|
assert(next_operand_ >= 0 && next_operand_ < 2);
|
2022-05-17 20:57:33 +00:00
|
|
|
|
if(!(operand_flags_ & (1 << next_operand_))) {
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(perform_state_);
|
2022-05-17 20:57:33 +00:00
|
|
|
|
}
|
2022-06-13 12:57:49 +00:00
|
|
|
|
MoveToAddressingMode(FetchOperand_bw, instruction_.mode(next_operand_));
|
2022-05-17 18:08:50 +00:00
|
|
|
|
|
2022-05-17 18:51:49 +00:00
|
|
|
|
|
2022-05-19 14:47:57 +00:00
|
|
|
|
// As above, but for .l.
|
|
|
|
|
BeginState(FetchOperand_l):
|
2022-06-15 16:59:03 +00:00
|
|
|
|
assert(next_operand_ >= 0 && next_operand_ < 2);
|
2022-05-19 14:47:57 +00:00
|
|
|
|
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
|
|
|
|
}
|
2022-06-13 12:57:49 +00:00
|
|
|
|
MoveToAddressingMode(FetchOperand_l, instruction_.mode(next_operand_));
|
2022-05-19 14:47:57 +00:00
|
|
|
|
|
|
|
|
|
|
2022-06-10 19:57:35 +00:00
|
|
|
|
BeginState(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec):
|
2022-06-13 14:27:22 +00:00
|
|
|
|
MoveToAddressingMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, instruction_.mode(next_operand_));
|
2022-06-01 12:50:43 +00:00
|
|
|
|
|
|
|
|
|
|
2022-05-20 20:23:52 +00:00
|
|
|
|
BeginState(CalcEffectiveAddress):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
MoveToAddressingMode(CalcEffectiveAddress, instruction_.mode(next_operand_));
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-05-19 14:47:57 +00:00
|
|
|
|
// MARK: - Fetch, addressing modes.
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
//
|
|
|
|
|
// DataRegisterDirect, AddressRegisterDirect
|
|
|
|
|
//
|
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterDirect):
|
|
|
|
|
BeginStateMode(FetchOperand_bw, DataRegisterDirect):
|
|
|
|
|
operand_[next_operand_] = registers_[instruction_.lreg(next_operand_)];
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterDirect):
|
|
|
|
|
BeginStateMode(FetchOperand_l, DataRegisterDirect):
|
|
|
|
|
operand_[next_operand_] = registers_[instruction_.lreg(next_operand_)];
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterDirect):
|
|
|
|
|
BeginStateMode(MOVE_l, DataRegisterDirect):
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterDirect):
|
2022-06-15 01:56:48 +00:00
|
|
|
|
registers_[instruction_.lreg(1)] = operand_[1];
|
2022-06-14 21:04:11 +00:00
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
|
|
|
|
BeginStateMode(MOVE_bw, DataRegisterDirect): {
|
|
|
|
|
const uint32_t write_mask = size_masks[int(instruction_.operand_size())];
|
|
|
|
|
const int reg = instruction_.lreg(1);
|
|
|
|
|
|
|
|
|
|
registers_[reg].l =
|
|
|
|
|
(operand_[1].l & write_mask) |
|
|
|
|
|
(registers_[reg].l & ~write_mask);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-14 01:49:00 +00:00
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
//
|
|
|
|
|
// Quick
|
|
|
|
|
//
|
|
|
|
|
BeginStateMode(FetchOperand_bw, Quick):
|
|
|
|
|
operand_[next_operand_].l = InstructionSet::M68k::quick(opcode_, instruction_.operation);
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
|
|
|
|
BeginStateMode(FetchOperand_l, Quick):
|
|
|
|
|
operand_[next_operand_].l = InstructionSet::M68k::quick(opcode_, instruction_.operation);
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// AddressRegisterIndirect
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterIndirect):
|
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);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterIndirect):
|
2022-06-15 01:22:28 +00:00
|
|
|
|
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterIndirect):
|
|
|
|
|
effective_address_[1] = registers_[8 + instruction_.reg(1)];
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
2022-06-15 21:06:56 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterIndirect):
|
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);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AddressRegisterIndirect):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AddressRegisterIndirect):
|
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-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, AddressRegisterIndirect):
|
|
|
|
|
effective_address_[0].l = registers_[8 + instruction_.reg(next_operand_)].l;
|
|
|
|
|
MoveToStateDynamic(PEA_np_nS_ns);
|
|
|
|
|
|
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;
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// AddressRegisterIndirectWithPostincrement
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterIndirectWithPostincrement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +=
|
2022-05-20 20:23:52 +00:00
|
|
|
|
address_increments[int(instruction_.operand_size())][instruction_.reg(next_operand_)];
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterIndirectWithPostincrement):
|
2022-06-15 01:22:28 +00:00
|
|
|
|
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
|
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +=
|
|
|
|
|
address_increments[int(instruction_.operand_size())][instruction_.reg(1)];
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterIndirectWithPostincrement):
|
|
|
|
|
SetDataAddress(registers_[8 + instruction_.reg(next_operand_)].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nW
|
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l += 2;
|
2022-06-14 21:04:11 +00:00
|
|
|
|
|
2022-06-14 01:49:00 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterIndirectWithPostincrement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l += 4;
|
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +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 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AddressRegisterIndirectWithPostincrement):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AddressRegisterIndirectWithPostincrement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = registers_[8 + instruction_.reg(next_operand_)].l;
|
2022-05-20 20:23:52 +00:00
|
|
|
|
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_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// AddressRegisterIndirectWithPredecrement
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterIndirectWithPredecrement):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l -=
|
2022-05-20 20:23:52 +00:00
|
|
|
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterIndirectWithPredecrement):
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(1)];
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l = registers_[8 + instruction_.reg(1)].l;
|
2022-06-14 21:04:11 +00:00
|
|
|
|
|
2022-06-14 01:49:00 +00:00
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterIndirectWithPredecrement):
|
|
|
|
|
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l -= 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l -= 2;
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterIndirectWithPredecrement):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Access(operand_[next_operand_].high); // nR
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l += 2;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AddressRegisterIndirectWithPredecrement):
|
|
|
|
|
IdleBus(1);
|
|
|
|
|
[[fallthrough]];
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AddressRegisterIndirectWithPredecrement):
|
2022-05-20 20:23:52 +00:00
|
|
|
|
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_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// AddressRegisterIndirectWithDisplacement
|
|
|
|
|
//
|
2022-06-13 18:08:42 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterIndirectWithDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l =
|
2022-05-19 19:03:22 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterIndirectWithDisplacement):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l =
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterIndirectWithDisplacement):
|
|
|
|
|
effective_address_[1].l =
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterIndirectWithDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l =
|
2022-05-19 19:03:22 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nR
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l += 2;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AddressRegisterIndirectWithDisplacement):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AddressRegisterIndirectWithDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l =
|
2022-05-20 20:23:52 +00:00
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-20 20:23:52 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, AddressRegisterIndirectWithDisplacement):
|
|
|
|
|
effective_address_[0].l =
|
|
|
|
|
registers_[8 + instruction_.reg(next_operand_)].l +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateDynamic(PEA_np_nS_ns);
|
|
|
|
|
|
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 +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-21 14:29:36 +00:00
|
|
|
|
IdleBus(1); // n
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// ProgramCounterIndirectWithDisplacement
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, ProgramCounterIndirectWithDisplacement):
|
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 +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, ProgramCounterIndirectWithDisplacement):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l =
|
|
|
|
|
program_counter_.l - 2 +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, ProgramCounterIndirectWithDisplacement):
|
|
|
|
|
effective_address_[1].l =
|
|
|
|
|
program_counter_.l - 2 +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, ProgramCounterIndirectWithDisplacement):
|
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 +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nR
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l += 2;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, ProgramCounterIndirectWithDisplacement):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, ProgramCounterIndirectWithDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l =
|
2022-05-20 20:23:52 +00:00
|
|
|
|
program_counter_.l - 2 +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-20 20:23:52 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, ProgramCounterIndirectWithDisplacement):
|
|
|
|
|
effective_address_[0].l =
|
|
|
|
|
program_counter_.l - 2 +
|
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateDynamic(PEA_np_nS_ns);
|
|
|
|
|
|
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 +
|
2022-05-24 15:05:24 +00:00
|
|
|
|
uint32_t(int16_t(prefetch_.w));
|
2022-05-21 14:29:36 +00:00
|
|
|
|
IdleBus(1); // n
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-20 20:23:52 +00:00
|
|
|
|
//
|
|
|
|
|
// AddressRegisterIndirectWithIndex8bitDisplacement
|
|
|
|
|
//
|
2022-05-24 15:05:24 +00:00
|
|
|
|
#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));
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AddressRegisterIndirectWithIndex8bitDisplacement):
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AddressRegisterIndirectWithIndex8bitDisplacement):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l = d8Xn(registers_[8 + instruction_.reg(1)].l);
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AddressRegisterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
effective_address_[1].l = d8Xn(registers_[8 + instruction_.reg(1)].l);
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AddressRegisterIndirectWithIndex8bitDisplacement):
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AddressRegisterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
[[fallthrough]];
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AddressRegisterIndirectWithIndex8bitDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
|
2022-05-21 20:20:01 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-20 20:23:52 +00:00
|
|
|
|
IdleBus(1); // n
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, AddressRegisterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
effective_address_[0].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
MoveToStateDynamic(PEA_np_nS_ns);
|
|
|
|
|
|
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
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-06-11 01:52:07 +00:00
|
|
|
|
BeginState(AddressRegisterIndirectWithIndex8bitDisplacement_n_np):
|
|
|
|
|
effective_address_[next_operand_].l = d8Xn(registers_[8 + instruction_.reg(next_operand_)].l);
|
2022-06-10 19:57:35 +00:00
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
2022-06-11 01:52:07 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-06-10 19:57:35 +00:00
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// ProgramCounterIndirectWithIndex8bitDisplacement
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, ProgramCounterIndirectWithIndex8bitDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, ProgramCounterIndirectWithIndex8bitDisplacement):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, ProgramCounterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
effective_address_[1].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, ProgramCounterIndirectWithIndex8bitDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, ProgramCounterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
[[fallthrough]];
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, ProgramCounterIndirectWithIndex8bitDisplacement):
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
|
2022-05-21 20:20:01 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-20 20:23:52 +00:00
|
|
|
|
IdleBus(1); // n
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, ProgramCounterIndirectWithIndex8bitDisplacement):
|
|
|
|
|
effective_address_[0].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
MoveToStateDynamic(PEA_np_nS_ns);
|
|
|
|
|
|
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
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-06-11 01:52:07 +00:00
|
|
|
|
BeginState(ProgramCounterIndirectWithIndex8bitDisplacement_n_np):
|
|
|
|
|
effective_address_[next_operand_].l = d8Xn(program_counter_.l - 2);
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
#undef d8Xn
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// AbsoluteShort
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AbsoluteShort):
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AbsoluteShort):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
effective_address_[1].l = uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AbsoluteShort):
|
|
|
|
|
effective_address_[1].l = uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AbsoluteShort):
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nR
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l += 2;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AbsoluteShort):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AbsoluteShort):
|
2022-05-24 15:05:24 +00:00
|
|
|
|
effective_address_[next_operand_].l = uint32_t(int16_t(prefetch_.w));
|
2022-05-20 20:23:52 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, AbsoluteShort):
|
|
|
|
|
effective_address_[0].l = uint32_t(int16_t(prefetch_.w));
|
|
|
|
|
MoveToStateSpecific(PEA_np_nS_ns_np);
|
|
|
|
|
|
2022-05-22 11:16:38 +00:00
|
|
|
|
BeginState(JSRJMPAbsoluteShort):
|
2022-05-24 15:05:24 +00:00
|
|
|
|
effective_address_[0].l = uint32_t(int16_t(prefetch_.w));
|
2022-05-21 14:29:36 +00:00
|
|
|
|
IdleBus(1); // n
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// AbsoluteLong
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, AbsoluteLong):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = prefetch_.l;
|
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginStateMode(MOVE_bw, AbsoluteLong):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
|
|
|
|
effective_address_[1].l = prefetch_.l;
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
SetupDataAccess(0, data_select(instruction_));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
switch(instruction_.mode(0)) {
|
|
|
|
|
case Mode::AddressRegisterDirect:
|
|
|
|
|
case Mode::DataRegisterDirect:
|
|
|
|
|
case Mode::ImmediateData:
|
2022-06-14 21:04:11 +00:00
|
|
|
|
MoveToStateSpecific(MOVE_bw_AbsoluteLong_prefetch_first);
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-14 21:04:11 +00:00
|
|
|
|
BeginState(MOVE_bw_AbsoluteLong_prefetch_first):
|
2022-06-14 01:49:00 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginStateMode(MOVE_l, AbsoluteLong):
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
|
|
|
|
effective_address_[1].l = prefetch_.l;
|
|
|
|
|
|
|
|
|
|
SetDataAddress(effective_address_[1].l);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-06-15 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
switch(instruction_.mode(0)) {
|
|
|
|
|
case Mode::AddressRegisterDirect:
|
|
|
|
|
case Mode::DataRegisterDirect:
|
|
|
|
|
case Mode::ImmediateData:
|
|
|
|
|
MoveToStateSpecific(MOVE_l_AbsoluteLong_prefetch_first);
|
|
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
|
|
|
|
BeginState(MOVE_l_AbsoluteLong_prefetch_first):
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
|
|
|
|
effective_address_[1].l += 2;
|
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, AbsoluteLong):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = prefetch_.l;
|
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 19:03:22 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Access(operand_[next_operand_].high); // nR
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l += 2;
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nr
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-06-13 14:27:22 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddressIdleFor8bitDisplacementAndPreDec, AbsoluteLong):
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(CalcEffectiveAddress, AbsoluteLong):
|
2022-05-20 20:23:52 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l = prefetch_.l;
|
2022-05-20 20:23:52 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateDynamic(post_ea_state_);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
|
2022-06-13 18:08:42 +00:00
|
|
|
|
BeginStateMode(PEA, AbsoluteLong):
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
effective_address_[0].l = prefetch_.l;
|
|
|
|
|
MoveToStateSpecific(PEA_np_nS_ns_np);
|
|
|
|
|
|
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;
|
2022-05-21 18:28:44 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-19 19:03:22 +00:00
|
|
|
|
//
|
|
|
|
|
// ImmediateData
|
|
|
|
|
//
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_bw, ImmediateData):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
operand_[next_operand_].w = prefetch_.w;
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
2022-06-13 12:57:49 +00:00
|
|
|
|
BeginStateMode(FetchOperand_l, ImmediateData):
|
2022-05-19 19:03:22 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
operand_[next_operand_].l = prefetch_.l;
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-10-22 19:20:30 +00:00
|
|
|
|
//
|
|
|
|
|
// ExtensionWord; always the same size.
|
|
|
|
|
//
|
|
|
|
|
BeginStateMode(FetchOperand_bw, ExtensionWord):
|
|
|
|
|
operand_[next_operand_].w = prefetch_.w;
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToNextOperand(FetchOperand_bw);
|
|
|
|
|
|
|
|
|
|
BeginStateMode(FetchOperand_l, ExtensionWord):
|
|
|
|
|
operand_[next_operand_].w = prefetch_.w;
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
MoveToNextOperand(FetchOperand_l);
|
|
|
|
|
|
2022-05-19 20:55:16 +00:00
|
|
|
|
#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-19 20:55:16 +00:00
|
|
|
|
|
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.
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(StoreOperand):
|
2022-05-19 20:55:16 +00:00
|
|
|
|
switch(instruction_.operand_size()) {
|
|
|
|
|
case InstructionSet::M68k::DataSize::LongWord:
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(StoreOperand_l);
|
2022-05-19 20:55:16 +00:00
|
|
|
|
|
2024-04-24 02:23:56 +00:00
|
|
|
|
default: // Convince GCC that nothing here is amiss.
|
2022-05-19 20:55:16 +00:00
|
|
|
|
case InstructionSet::M68k::DataSize::Word:
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(StoreOperand_bw);
|
2022-05-19 20:55:16 +00:00
|
|
|
|
|
|
|
|
|
case InstructionSet::M68k::DataSize::Byte:
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectByte);
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(StoreOperand_bw);
|
2022-05-17 20:10:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 20:55:16 +00:00
|
|
|
|
BeginState(StoreOperand_bw):
|
|
|
|
|
if(!(operand_flags_ & 0x4 << next_operand_)) {
|
|
|
|
|
MoveToNextOperand(StoreOperand_bw);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-23 00:52:00 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2022-05-23 00:52:00 +00:00
|
|
|
|
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.
|
2022-05-23 00:52:00 +00:00
|
|
|
|
case Mode::AddressRegisterDirect:
|
|
|
|
|
registers_[instruction_.lreg(next_operand_)] = operand_[next_operand_];
|
2022-05-19 20:55:16 +00:00
|
|
|
|
MoveToNextOperand(StoreOperand_bw);
|
2022-05-23 12:18:37 +00:00
|
|
|
|
|
2022-05-23 00:52:00 +00:00
|
|
|
|
default: break;
|
2022-05-19 15:23:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 15:23:26 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
2022-05-19 20:55:16 +00:00
|
|
|
|
MoveToNextOperand(StoreOperand_bw);
|
2022-05-19 15:23:26 +00:00
|
|
|
|
|
|
|
|
|
BeginState(StoreOperand_l):
|
2022-05-19 20:55:16 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-22 15:27:38 +00:00
|
|
|
|
SetDataAddress(effective_address_[next_operand_].l);
|
2022-05-19 15:23:26 +00:00
|
|
|
|
Access(operand_[next_operand_].low); // nw
|
|
|
|
|
|
2022-05-22 15:27:38 +00:00
|
|
|
|
effective_address_[next_operand_].l -= 2;
|
2022-05-19 15:23:26 +00:00
|
|
|
|
Access(operand_[next_operand_].high); // nW
|
2022-05-19 20:55:16 +00:00
|
|
|
|
MoveToNextOperand(StoreOperand_l);
|
2022-05-17 20:10:20 +00:00
|
|
|
|
|
2022-05-22 11:08:14 +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-17 18:51:49 +00:00
|
|
|
|
//
|
2022-05-19 01:00:10 +00:00
|
|
|
|
// Various generic forms of perform.
|
2022-05-17 18:51:49 +00:00
|
|
|
|
//
|
2022-05-24 15:30:09 +00:00
|
|
|
|
#define MoveToWritePhase() \
|
|
|
|
|
if(operand_flags_ & 0x0c) { \
|
|
|
|
|
next_operand_ = 0; \
|
|
|
|
|
MoveToStateSpecific(StoreOperand); \
|
|
|
|
|
} else { \
|
|
|
|
|
MoveToStateSpecific(Decode); \
|
|
|
|
|
}
|
2022-05-17 20:10:20 +00:00
|
|
|
|
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(Perform_np):
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-17 18:51:49 +00:00
|
|
|
|
Prefetch(); // np
|
2022-05-18 19:35:38 +00:00
|
|
|
|
MoveToWritePhase();
|
2022-05-17 18:51:49 +00:00
|
|
|
|
|
2022-05-18 19:35:38 +00:00
|
|
|
|
BeginState(Perform_np_n):
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-17 18:51:49 +00:00
|
|
|
|
Prefetch(); // np
|
|
|
|
|
IdleBus(1); // n
|
2022-05-18 19:35:38 +00:00
|
|
|
|
MoveToWritePhase();
|
2022-05-17 18:51:49 +00:00
|
|
|
|
|
2022-05-19 16:18:47 +00:00
|
|
|
|
BeginState(Perform_np_nn):
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-19 16:18:47 +00:00
|
|
|
|
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-18 19:35:38 +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-06-15 01:22:28 +00:00
|
|
|
|
//
|
|
|
|
|
// MOVE
|
|
|
|
|
//
|
2022-06-14 01:49:00 +00:00
|
|
|
|
BeginState(MOVE_b):
|
|
|
|
|
PerformSpecific(MOVEb);
|
2022-06-14 21:04:11 +00:00
|
|
|
|
MoveToAddressingMode(MOVE_bw, instruction_.mode(1));
|
|
|
|
|
|
|
|
|
|
BeginState(MOVE_w):
|
2022-06-15 01:56:48 +00:00
|
|
|
|
PerformDynamic(); // Could be MOVE.w or MOVEA.w.
|
2022-06-14 21:04:11 +00:00
|
|
|
|
MoveToAddressingMode(MOVE_bw, instruction_.mode(1));
|
2022-06-14 01:49:00 +00:00
|
|
|
|
|
2022-06-15 01:22:28 +00:00
|
|
|
|
BeginState(MOVE_l):
|
2022-06-15 01:56:48 +00:00
|
|
|
|
PerformDynamic(); // Could be MOVE.l or MOVEA.l.
|
2022-06-15 01:22:28 +00:00
|
|
|
|
MoveToAddressingMode(MOVE_l, instruction_.mode(1));
|
|
|
|
|
|
2022-05-20 16:04:43 +00:00
|
|
|
|
//
|
|
|
|
|
// [ABCD/SBCD/SUBX/ADDX] (An)-, (An)-
|
|
|
|
|
//
|
2022-05-20 11:02:02 +00:00
|
|
|
|
BeginState(TwoOp_Predec_bw):
|
2022-05-19 19:19:00 +00:00
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, select_flag_);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
|
2022-05-19 19:19:00 +00:00
|
|
|
|
SetDataAddress(registers_[8 + instruction_.reg(0)].l);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
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);
|
2022-05-20 20:23:52 +00:00
|
|
|
|
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
|
|
|
|
|
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-19 19:19:00 +00:00
|
|
|
|
|
2022-05-20 11:02:02 +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);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
|
|
|
|
|
BeginState(TwoOp_Predec_l):
|
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-20 11:02:02 +00:00
|
|
|
|
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
2022-05-20 11:02:02 +00:00
|
|
|
|
|
|
|
|
|
registers_[8 + instruction_.reg(1)].l += 2;
|
|
|
|
|
Access(operand_[1].low); // nw
|
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
2022-05-19 19:19:00 +00:00
|
|
|
|
|
2022-05-20 11:02:02 +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
|
|
|
|
|
2022-05-20 15:19:16 +00:00
|
|
|
|
//
|
|
|
|
|
// CHK
|
|
|
|
|
//
|
2022-05-19 20:27:39 +00:00
|
|
|
|
BeginState(CHK):
|
|
|
|
|
Prefetch(); // np
|
2022-10-22 19:20:30 +00:00
|
|
|
|
PerformSpecific(CHKw);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
|
|
|
|
// 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);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
|
|
|
|
BeginState(CHK_was_over):
|
|
|
|
|
IdleBus(2); // nn
|
2022-06-03 12:27:49 +00:00
|
|
|
|
ReloadInstructionAddress();
|
2022-05-25 19:45:09 +00:00
|
|
|
|
RaiseException(InstructionSet::M68k::Exception::CHK);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
|
|
|
|
BeginState(CHK_was_under):
|
|
|
|
|
IdleBus(3); // n nn
|
2022-06-03 12:27:49 +00:00
|
|
|
|
ReloadInstructionAddress();
|
2022-05-25 19:45:09 +00:00
|
|
|
|
RaiseException(InstructionSet::M68k::Exception::CHK);
|
2022-05-19 20:27:39 +00:00
|
|
|
|
|
2022-05-20 15:19:16 +00:00
|
|
|
|
//
|
|
|
|
|
// Scc
|
|
|
|
|
//
|
|
|
|
|
BeginState(Scc_Dn):
|
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformSpecific(Scc);
|
2022-05-20 15:19:16 +00:00
|
|
|
|
|
|
|
|
|
// 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:19:16 +00:00
|
|
|
|
|
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)];
|
2022-05-24 15:05:24 +00:00
|
|
|
|
operand_[1].w = uint16_t(int16_t(prefetch_.w));
|
2022-05-22 11:08:14 +00:00
|
|
|
|
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.
|
2022-05-21 23:53:28 +00:00
|
|
|
|
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]
|
|
|
|
|
//
|
2022-05-23 19:09:46 +00:00
|
|
|
|
BeginState(Bccb):
|
2022-05-22 00:59:34 +00:00
|
|
|
|
operand_[0].b = uint8_t(opcode_);
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformSpecific(Bccb);
|
2022-05-22 00:59:34 +00:00
|
|
|
|
|
|
|
|
|
// Next state was set by complete_bcc.
|
|
|
|
|
break;
|
|
|
|
|
|
2022-05-23 19:09:46 +00:00
|
|
|
|
BeginState(Bccw):
|
2022-05-22 00:59:34 +00:00
|
|
|
|
operand_[0].w = prefetch_.w;
|
2022-05-22 11:08:14 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-23 19:09:46 +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
|
|
|
|
|
2022-05-23 19:09:46 +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) \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord); \
|
2022-05-22 15:27:38 +00:00
|
|
|
|
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) \
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord); \
|
2022-05-23 14:27:44 +00:00
|
|
|
|
SetDataAddress(registers_[15].l); \
|
|
|
|
|
Access(x.high); \
|
|
|
|
|
registers_[15].l += 2; \
|
|
|
|
|
Access(x.low); \
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
|
2022-05-20 16:40:35 +00:00
|
|
|
|
//
|
|
|
|
|
// BSR
|
|
|
|
|
//
|
2022-05-25 20:32:02 +00:00
|
|
|
|
BeginState(BSRb):
|
|
|
|
|
BeginState(BSRw):
|
2022-05-20 16:40:35 +00:00
|
|
|
|
IdleBus(1); // n
|
|
|
|
|
|
2022-05-25 20:32:02 +00:00
|
|
|
|
// Calculate the address of the next instruction and the next program counter.
|
2022-05-20 16:40:35 +00:00
|
|
|
|
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;
|
2022-05-20 16:40:35 +00:00
|
|
|
|
} 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-20 16:40:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
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_);
|
2022-05-20 16:40:35 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
Prefetch(); // np
|
2022-05-22 11:39:16 +00:00
|
|
|
|
MoveToStateSpecific(Decode);
|
2022-05-20 16:40:35 +00:00
|
|
|
|
|
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):
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-20 16:58:45 +00:00
|
|
|
|
|
2022-05-22 01:05:05 +00:00
|
|
|
|
Prefetch();
|
2022-05-21 19:56:09 +00:00
|
|
|
|
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):
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformSpecific(BCLR);
|
2022-05-20 16:58:45 +00:00
|
|
|
|
|
2022-05-22 01:05:05 +00:00
|
|
|
|
Prefetch();
|
2022-05-21 19:56:09 +00:00
|
|
|
|
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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectByte);
|
2022-05-20 18:22:32 +00:00
|
|
|
|
|
|
|
|
|
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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectByte);
|
2022-05-20 18:22:32 +00:00
|
|
|
|
|
|
|
|
|
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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
2022-05-20 18:22:32 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
|
|
|
|
Access(temporary_value_.low); // nR
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
2022-05-20 18:22:32 +00:00
|
|
|
|
|
|
|
|
|
Prefetch(); // np
|
|
|
|
|
|
|
|
|
|
Access(temporary_value_.low); // nR
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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
|
2022-05-24 15:05:24 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-20 18:29:14 +00:00
|
|
|
|
//
|
|
|
|
|
// [EORI/ORI/ANDI] #, [CCR/SR]
|
|
|
|
|
//
|
|
|
|
|
BeginState(LogicalToSR):
|
2022-05-25 20:20:26 +00:00
|
|
|
|
IdleBus(4);
|
|
|
|
|
|
2022-05-20 18:29:14 +00:00
|
|
|
|
// Perform the operation.
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-20 18:29:14 +00:00
|
|
|
|
|
|
|
|
|
// 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 18:29:14 +00:00
|
|
|
|
|
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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-06-11 01:52:07 +00:00
|
|
|
|
|
|
|
|
|
switch(instruction_.mode(1)) {
|
|
|
|
|
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
|
|
|
|
|
MoveToStateSpecific(AddressRegisterIndirectWithIndex8bitDisplacement_n_np);
|
|
|
|
|
|
|
|
|
|
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
|
|
|
|
|
MoveToStateSpecific(ProgramCounterIndirectWithIndex8bitDisplacement_n_np);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
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;
|
2022-05-21 00:47:54 +00:00
|
|
|
|
++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;
|
2022-05-21 00:47:54 +00:00
|
|
|
|
++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;
|
2022-05-21 00:47:54 +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-21 00:47:54 +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 00:47:54 +00:00
|
|
|
|
++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);
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(0, Operation::SelectWord);
|
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-06-11 01:52:07 +00:00
|
|
|
|
|
|
|
|
|
// Predecrement writes registers the other way around, but still reads the
|
|
|
|
|
// mask from LSB.
|
|
|
|
|
switch(instruction_.mode(1)) {
|
|
|
|
|
case Mode::AddressRegisterIndirectWithPredecrement:
|
|
|
|
|
register_index_ = 15;
|
|
|
|
|
effective_address_[1].l = registers_[8 + instruction_.reg(1)].l;
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
MoveToStateSpecific(MOVEMtoM_l_write_predec);
|
|
|
|
|
} else {
|
|
|
|
|
MoveToStateSpecific(MOVEMtoM_w_write_predec);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
|
|
|
|
|
MoveToStateSpecific(AddressRegisterIndirectWithIndex8bitDisplacement_n_np);
|
|
|
|
|
|
|
|
|
|
case Mode::ProgramCounterIndirectWithIndex8bitDisplacement:
|
|
|
|
|
MoveToStateSpecific(ProgramCounterIndirectWithIndex8bitDisplacement_n_np);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
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
|
|
|
|
|
2022-05-21 19:56:09 +00:00
|
|
|
|
//
|
|
|
|
|
// DIVU and DIVUS
|
|
|
|
|
//
|
|
|
|
|
BeginState(DIVU_DIVS):
|
|
|
|
|
// Set a no-interrupt-occurred sentinel.
|
|
|
|
|
exception_vector_ = -1;
|
|
|
|
|
|
|
|
|
|
// Perform the instruction.
|
2022-05-22 11:08:14 +00:00
|
|
|
|
PerformDynamic();
|
2022-05-21 19:56:09 +00:00
|
|
|
|
|
|
|
|
|
// 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);
|
2022-05-21 19:56:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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-21 19:56:09 +00:00
|
|
|
|
|
2022-05-22 12:02:32 +00:00
|
|
|
|
//
|
2022-05-23 13:29:19 +00:00
|
|
|
|
// MULU, MULS and shifts
|
2022-05-22 12:02:32 +00:00
|
|
|
|
//
|
2022-05-23 13:29:19 +00:00
|
|
|
|
BeginState(Perform_idle_dyamic_Dn):
|
2022-05-22 12:02:32 +00:00
|
|
|
|
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):
|
2022-06-13 18:08:42 +00:00
|
|
|
|
MoveToAddressingMode(PEA, instruction_.mode(0));
|
|
|
|
|
|
|
|
|
|
BeginState(PEA_np_nS_ns):
|
|
|
|
|
Prefetch();
|
|
|
|
|
Push(effective_address_[0]);
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
|
|
|
|
BeginState(PEA_np_nS_ns_np):
|
|
|
|
|
Prefetch();
|
2022-05-22 15:27:38 +00:00
|
|
|
|
Push(effective_address_[0]);
|
2022-05-22 12:29:12 +00:00
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-05-22 20:08:30 +00:00
|
|
|
|
//
|
|
|
|
|
// TAS
|
|
|
|
|
//
|
|
|
|
|
BeginState(TAS):
|
2022-05-22 20:14:03 +00:00
|
|
|
|
// Populate all addresses.
|
2023-12-22 15:46:10 +00:00
|
|
|
|
tas_cycle0.address = tas_cycle1.address =
|
|
|
|
|
tas_cycle2.address =
|
|
|
|
|
tas_cycle3.address = tas_cycle4.address = &effective_address_[0].l;
|
2022-05-22 20:14:03 +00:00
|
|
|
|
|
|
|
|
|
// Populate values to the relevant subset.
|
2023-12-22 15:46:10 +00:00
|
|
|
|
tas_cycle0.value = tas_cycle1.value =
|
|
|
|
|
tas_cycle3.value = tas_cycle4.value = &operand_[0].low;
|
2022-05-22 20:14:03 +00:00
|
|
|
|
|
|
|
|
|
// First two parts: the read.
|
2023-12-22 15:46:10 +00:00
|
|
|
|
PerformBusOperation(tas_cycle0);
|
|
|
|
|
CompleteAccess(tas_cycle1);
|
2022-05-22 20:14:03 +00:00
|
|
|
|
|
|
|
|
|
// Third part: processing time.
|
2023-12-22 15:46:10 +00:00
|
|
|
|
PerformBusOperation(tas_cycle2);
|
2022-05-22 20:14:03 +00:00
|
|
|
|
|
|
|
|
|
// 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;
|
2023-12-22 15:46:10 +00:00
|
|
|
|
PerformBusOperation(tas_cycle3);
|
|
|
|
|
CompleteAccess(tas_cycle4);
|
2022-05-22 20:08:30 +00:00
|
|
|
|
|
|
|
|
|
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):
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-23 01:16:38 +00:00
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
|
|
|
|
Access(program_counter_.high);
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
Access(program_counter_.low);
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
|
|
|
|
|
Prefetch();
|
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-06-10 01:47:28 +00:00
|
|
|
|
// Yacht cites the bus activity for RTE and RTR as nS ns ns, so
|
|
|
|
|
// the program counter high word must be the first thing
|
|
|
|
|
// retrieved; the order of the other two is a guess,
|
|
|
|
|
// being the converse of the write order.
|
|
|
|
|
|
2022-05-23 01:16:38 +00:00
|
|
|
|
BeginState(RTE):
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-23 01:16:38 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2022-06-10 01:47:28 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2022-05-23 01:16:38 +00:00
|
|
|
|
Access(temporary_value_.low);
|
2022-06-10 01:47:28 +00:00
|
|
|
|
|
|
|
|
|
registers_[15].l += 4;
|
|
|
|
|
Access(program_counter_.low);
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
|
2022-05-23 01:16:38 +00:00
|
|
|
|
status_.set_status(temporary_value_.w);
|
2022-06-06 20:08:50 +00:00
|
|
|
|
did_update_status();
|
2022-05-23 01:16:38 +00:00
|
|
|
|
|
|
|
|
|
Prefetch();
|
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
|
|
|
|
BeginState(RTR):
|
2023-12-21 21:03:53 +00:00
|
|
|
|
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
2022-05-23 01:16:38 +00:00
|
|
|
|
SetDataAddress(registers_[15].l);
|
|
|
|
|
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
Access(program_counter_.high);
|
|
|
|
|
|
2022-06-10 01:47:28 +00:00
|
|
|
|
registers_[15].l -= 2;
|
2022-05-23 01:16:38 +00:00
|
|
|
|
Access(temporary_value_.low);
|
2022-06-10 01:47:28 +00:00
|
|
|
|
|
|
|
|
|
registers_[15].l += 4;
|
|
|
|
|
Access(program_counter_.low);
|
|
|
|
|
registers_[15].l += 2;
|
|
|
|
|
|
2022-05-23 01:16:38 +00:00
|
|
|
|
status_.set_ccr(temporary_value_.w);
|
|
|
|
|
|
|
|
|
|
Prefetch();
|
|
|
|
|
Prefetch();
|
|
|
|
|
MoveToStateSpecific(Decode);
|
|
|
|
|
|
2022-05-23 14:27:44 +00:00
|
|
|
|
//
|
2022-05-23 19:09:46 +00:00
|
|
|
|
// LINK[.w] and UNLINK
|
2022-05-23 14:27:44 +00:00
|
|
|
|
//
|
2022-05-23 19:09:46 +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);
|
|
|
|
|
|
2022-05-23 19:09:46 +00:00
|
|
|
|
//
|
|
|
|
|
// RESET
|
|
|
|
|
//
|
|
|
|
|
BeginState(RESET):
|
|
|
|
|
IdleBus(2);
|
2023-12-22 04:08:18 +00:00
|
|
|
|
PerformBusOperation(reset_cycle);
|
2022-05-23 19:09:46 +00:00
|
|
|
|
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);
|
2022-05-25 15:47:21 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2022-05-25 15:47:21 +00:00
|
|
|
|
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
|
|
|
|
|
2022-05-17 01:00:25 +00:00
|
|
|
|
default:
|
2022-05-19 14:47:57 +00:00
|
|
|
|
printf("Unhandled state: %d; opcode is %04x\n", state_, opcode_);
|
2022-05-17 01:00:25 +00:00
|
|
|
|
assert(false);
|
2022-05-16 15:59:03 +00:00
|
|
|
|
}}
|
|
|
|
|
|
2022-05-23 14:27:44 +00:00
|
|
|
|
#undef Pop
|
2022-05-22 15:27:38 +00:00
|
|
|
|
#undef Push
|
2022-05-22 11:08:14 +00:00
|
|
|
|
#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
|
2022-05-16 15:59:03 +00:00
|
|
|
|
#undef CheckOverrun
|
|
|
|
|
#undef Spend
|
|
|
|
|
#undef ConsiderExit
|
2022-06-03 12:27:49 +00:00
|
|
|
|
#undef ReloadInstructionAddress
|
2022-06-13 12:57:49 +00:00
|
|
|
|
#undef MoveToAddressingMode
|
|
|
|
|
#undef BeginStateMode
|
2022-05-16 15:59:03 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 18:51:49 +00:00
|
|
|
|
// MARK: - Flow Controller.
|
|
|
|
|
|
|
|
|
|
void ProcessorBase::did_update_status() {
|
|
|
|
|
// Shuffle the stack pointers.
|
2022-05-17 20:51:26 +00:00
|
|
|
|
stack_pointers_[is_supervisor_] = registers_[15];
|
|
|
|
|
registers_[15] = stack_pointers_[int(status_.is_supervisor)];
|
2022-05-17 18:51:49 +00:00
|
|
|
|
is_supervisor_ = int(status_.is_supervisor);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 20:27:39 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 15:19:16 +00:00
|
|
|
|
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) ?
|
2022-05-23 19:09:46 +00:00
|
|
|
|
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) {
|
2022-05-21 19:56:09 +00:00
|
|
|
|
dynamic_instruction_length_ = int(bit_position > 15);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 18:56:04 +00:00
|
|
|
|
template <bool did_overflow> void ProcessorBase::did_divu(uint32_t dividend, uint32_t divisor) {
|
|
|
|
|
if(!divisor) {
|
|
|
|
|
dynamic_instruction_length_ = 4; // nn nn precedes the usual exception activity.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-05-21 19:56:09 +00:00
|
|
|
|
|
2022-05-27 18:56:04 +00:00
|
|
|
|
if(did_overflow) {
|
2022-06-02 16:26:25 +00:00
|
|
|
|
dynamic_instruction_length_ = 3; // Covers the nn n to get into the loop.
|
2022-05-27 18:56:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate cost; this is based on the flowchart in yacht.txt.
|
|
|
|
|
// I could actually calculate the division result using this code,
|
|
|
|
|
// since this is a classic divide algorithm, but would rather that
|
|
|
|
|
// errors produce incorrect timing only, not incorrect timing plus
|
|
|
|
|
// incorrect results.
|
2022-06-02 16:26:25 +00:00
|
|
|
|
dynamic_instruction_length_ =
|
|
|
|
|
3 + // nn n to get into the loop;
|
|
|
|
|
30 + // nn per iteration of the loop below;
|
|
|
|
|
3; // n nn upon completion of the loop.
|
2022-05-27 18:56:04 +00:00
|
|
|
|
|
|
|
|
|
divisor <<= 16;
|
|
|
|
|
for(int c = 0; c < 15; ++c) {
|
2022-06-02 16:26:25 +00:00
|
|
|
|
if(dividend & 0x8000'0000) {
|
2022-05-27 18:56:04 +00:00
|
|
|
|
dividend = (dividend << 1) - divisor;
|
|
|
|
|
} else {
|
|
|
|
|
dividend <<= 1;
|
|
|
|
|
|
|
|
|
|
// Yacht.txt, and indeed a real microprogram, would just subtract here
|
|
|
|
|
// and test the sign of the result, but this is easier to follow:
|
|
|
|
|
if (dividend >= divisor) {
|
|
|
|
|
dividend -= divisor;
|
2022-06-02 16:26:25 +00:00
|
|
|
|
dynamic_instruction_length_ += 1; // i.e. the original nn plus one further n before going down the MSB=0 route.
|
2022-05-27 18:56:04 +00:00
|
|
|
|
} else {
|
2022-06-02 16:26:25 +00:00
|
|
|
|
dynamic_instruction_length_ += 2; // The costliest path (since in real life it's a subtraction and then a step
|
2022-05-27 18:56:04 +00:00
|
|
|
|
// back from there) — all costs accrue. So the fixed nn loop plus another n,
|
|
|
|
|
// plus another one.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-21 19:56:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 18:41:42 +00:00
|
|
|
|
#define convert_to_bit_count_16(x) \
|
|
|
|
|
x = ((x & 0xaaaa) >> 1) + (x & 0x5555); \
|
|
|
|
|
x = ((x & 0xcccc) >> 2) + (x & 0x3333); \
|
|
|
|
|
x = ((x & 0xf0f0) >> 4) + (x & 0x0f0f); \
|
|
|
|
|
x = ((x & 0xff00) >> 8) + (x & 0x00ff);
|
|
|
|
|
|
2022-05-27 18:56:04 +00:00
|
|
|
|
template <bool did_overflow> void ProcessorBase::did_divs(int32_t dividend, int32_t divisor) {
|
|
|
|
|
// The route to spotting divide by 0 is just nn nn.
|
|
|
|
|
if(!divisor) {
|
|
|
|
|
dynamic_instruction_length_ = 4; // nn nn precedes the usual exception activity.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 19:37:40 +00:00
|
|
|
|
// It's either six or seven microcycles to get into the main loop, depending
|
2022-05-27 18:56:04 +00:00
|
|
|
|
// on dividend sign.
|
2022-05-27 19:37:40 +00:00
|
|
|
|
dynamic_instruction_length_ = 6 + (dividend < 0);
|
2022-05-27 18:56:04 +00:00
|
|
|
|
|
|
|
|
|
if(did_overflow) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 19:37:40 +00:00
|
|
|
|
// There's a fixed cost per bit, plus an additional one for each that is zero.
|
2022-05-27 18:56:04 +00:00
|
|
|
|
//
|
2022-05-27 19:37:40 +00:00
|
|
|
|
// The sign bit does not count here; it's the high fifteen bits that matter
|
2022-05-27 18:56:04 +00:00
|
|
|
|
// only, in the unsigned version of the result.
|
2022-05-27 19:37:40 +00:00
|
|
|
|
//
|
|
|
|
|
// Disclaimer: per the flowchart it looks to me like this constant should be 60
|
|
|
|
|
// rather than 49 — four microcycles per bit. But the number 49 makes this
|
|
|
|
|
// algorithm exactly fit the stated minimum and maximum costs. Possibly the
|
|
|
|
|
// undefined difference between a nop cycle an an idle wait is relevant here?
|
|
|
|
|
dynamic_instruction_length_ += 49;
|
2022-05-27 18:56:04 +00:00
|
|
|
|
|
2022-05-27 19:37:40 +00:00
|
|
|
|
int result_bits = ~abs(dividend / divisor) & 0xfffe;
|
2022-05-27 18:56:04 +00:00
|
|
|
|
convert_to_bit_count_16(result_bits);
|
|
|
|
|
dynamic_instruction_length_ += result_bits;
|
|
|
|
|
|
|
|
|
|
// Determine the tail cost; a divisor of less than 0 leads to one exit,
|
|
|
|
|
// a divisor of greater than zero makes the result a function of the
|
|
|
|
|
// sign of the dividend.
|
|
|
|
|
//
|
|
|
|
|
// In all cases, this is counting from 'No more bits' in the Yacht diagram.
|
|
|
|
|
if(divisor < 0) {
|
|
|
|
|
dynamic_instruction_length_ += 4;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dividend < 0) {
|
|
|
|
|
dynamic_instruction_length_ += 5;
|
|
|
|
|
} else {
|
|
|
|
|
dynamic_instruction_length_ += 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 18:41:42 +00:00
|
|
|
|
template <typename IntT> void ProcessorBase::did_mulu(IntT multiplier) {
|
|
|
|
|
// Count number of bits set.
|
|
|
|
|
convert_to_bit_count_16(multiplier);
|
2022-06-01 18:05:23 +00:00
|
|
|
|
dynamic_instruction_length_ = 17 + multiplier;
|
2022-05-22 12:02:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 18:41:42 +00:00
|
|
|
|
template <typename IntT> void ProcessorBase::did_muls(IntT multiplier) {
|
|
|
|
|
// Count number of transitions from 0 to 1 or from 1 to 0 — i.e. the
|
|
|
|
|
// number of times that a bit is not equal to the one to its right.
|
|
|
|
|
// Treat the bit to the right of b0 as 0.
|
|
|
|
|
int number_of_pairs = (multiplier ^ (multiplier << 1)) & 0xffff;
|
|
|
|
|
convert_to_bit_count_16(number_of_pairs);
|
2022-06-01 18:05:23 +00:00
|
|
|
|
dynamic_instruction_length_ = 17 + number_of_pairs;
|
2022-05-22 12:02:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 18:41:42 +00:00
|
|
|
|
#undef convert_to_bit_count_16
|
|
|
|
|
|
2022-06-02 00:30:51 +00:00
|
|
|
|
template <typename IntT> void ProcessorBase::did_shift(int bits_shifted) {
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
|
dynamic_instruction_length_ = bits_shifted + 2;
|
|
|
|
|
} else {
|
|
|
|
|
dynamic_instruction_length_ = bits_shifted + 1;
|
|
|
|
|
}
|
2022-05-23 13:29:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-21 19:56:09 +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
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 20:08:30 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 18:51:49 +00:00
|
|
|
|
// MARK: - External state.
|
|
|
|
|
|
2022-05-17 00:04:13 +00:00
|
|
|
|
template <class BusHandler, bool dtack_is_implicit, bool permit_overrun, bool signal_will_perform>
|
2023-05-10 22:13:01 +00:00
|
|
|
|
CPU::MC68000::State Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perform>::get_state() {
|
|
|
|
|
CPU::MC68000::State state;
|
2022-05-17 20:51:26 +00:00
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
2022-09-06 02:00:04 +00:00
|
|
|
|
state.prefetch[0] = prefetch_.high.w;
|
|
|
|
|
state.prefetch[1] = prefetch_.low.w;
|
|
|
|
|
|
2022-05-17 20:51:26 +00:00
|
|
|
|
return state;
|
2022-05-17 00:04:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class BusHandler, bool dtack_is_implicit, bool permit_overrun, bool signal_will_perform>
|
2023-05-10 22:13:01 +00:00
|
|
|
|
void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perform>::set_state(const CPU::MC68000::State &state) {
|
2022-05-17 20:51:26 +00:00
|
|
|
|
// 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();
|
2022-09-06 02:00:04 +00:00
|
|
|
|
|
|
|
|
|
// Populate the prefetch.
|
|
|
|
|
prefetch_.high.w = state.prefetch[0];
|
|
|
|
|
prefetch_.low.w = state.prefetch[1];
|
2022-05-17 00:04:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 00:22:05 +00:00
|
|
|
|
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 ®isters) {
|
|
|
|
|
// Populate registers.
|
2023-05-10 22:13:01 +00:00
|
|
|
|
CPU::MC68000::State state;
|
2022-05-26 00:22:05 +00:00
|
|
|
|
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;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
|
|
|
|
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
2022-05-26 00:22:05 +00:00
|
|
|
|
program_counter_.l += 2;
|
|
|
|
|
|
|
|
|
|
read_program.value = &prefetch_.low;
|
2023-12-22 04:08:18 +00:00
|
|
|
|
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
|
|
|
|
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
2022-05-26 00:22:05 +00:00
|
|
|
|
program_counter_.l += 2;
|
|
|
|
|
}
|
2022-05-17 18:51:49 +00:00
|
|
|
|
|
2022-06-07 20:55:39 +00:00
|
|
|
|
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>::reset() {
|
|
|
|
|
state_ = Reset;
|
2022-06-08 19:12:32 +00:00
|
|
|
|
time_remaining_ = HalfCycles(0);
|
2022-06-07 20:55:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 01:13:00 +00:00
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-05-16 15:44:16 +00:00
|
|
|
|
}
|