2019-03-09 05:00:23 +00:00
|
|
|
//
|
|
|
|
// 68000Storage.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 08/03/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef MC68000Storage_h
|
|
|
|
#define MC68000Storage_h
|
|
|
|
|
|
|
|
class ProcessorStorage {
|
|
|
|
public:
|
|
|
|
ProcessorStorage();
|
|
|
|
|
|
|
|
protected:
|
2019-03-10 21:27:34 +00:00
|
|
|
RegisterPair32 data_[8];
|
2019-03-14 01:08:13 +00:00
|
|
|
RegisterPair32 address_[8];
|
2019-03-10 21:27:34 +00:00
|
|
|
RegisterPair32 program_counter_;
|
|
|
|
|
2019-03-14 01:08:13 +00:00
|
|
|
RegisterPair32 stack_pointers_[2]; // [0] = user stack pointer; [1] = supervisor; the values from here
|
|
|
|
// are copied into/out of address_[7] upon mode switches.
|
|
|
|
|
2019-03-20 01:33:52 +00:00
|
|
|
RegisterPair32 prefetch_queue_; // Each word will go into the low part of the word, then proceed upward.
|
2019-03-09 05:00:23 +00:00
|
|
|
|
2020-07-03 02:24:04 +00:00
|
|
|
// Generic sources and targets for memory operations;
|
|
|
|
// by convention: effective_address_[0] = source, [1] = destination.
|
|
|
|
//
|
|
|
|
// These, and the various register contents above, should be kept
|
|
|
|
// close to the top of this class so that small-integer offsets can be
|
|
|
|
// used in instances of Program (see below).
|
|
|
|
RegisterPair32 effective_address_[2];
|
|
|
|
RegisterPair32 source_bus_data_;
|
|
|
|
RegisterPair32 destination_bus_data_;
|
|
|
|
|
2019-04-30 23:24:22 +00:00
|
|
|
enum class ExecutionState {
|
2019-05-01 02:07:48 +00:00
|
|
|
/// The normal mode, this means the 68000 is expending processing effort.
|
2019-04-30 23:24:22 +00:00
|
|
|
Executing,
|
2019-05-01 02:07:48 +00:00
|
|
|
|
|
|
|
/// The 68000 is in a holding loop, waiting for either DTack or to be notified of a bus error.
|
2019-04-30 23:24:22 +00:00
|
|
|
WaitingForDTack,
|
2019-05-01 02:07:48 +00:00
|
|
|
|
|
|
|
/// Occurs after executing a STOP instruction; the processor will idle waiting for an interrupt or reset.
|
|
|
|
Stopped,
|
|
|
|
|
|
|
|
/// Occurs at the end of the current bus cycle after detection of the HALT input, continuing until
|
|
|
|
/// HALT is no longer signalled.
|
2019-05-01 19:26:36 +00:00
|
|
|
Halted,
|
|
|
|
|
|
|
|
/// Signals a transition from some other straight directly to cueing up an interrupt.
|
2020-05-19 03:55:54 +00:00
|
|
|
WillBeginInterrupt,
|
2019-04-30 23:24:22 +00:00
|
|
|
} execution_state_ = ExecutionState::Executing;
|
|
|
|
Microcycle dtack_cycle_;
|
|
|
|
Microcycle stop_cycle_;
|
2019-04-29 23:30:00 +00:00
|
|
|
|
2020-05-16 04:06:04 +00:00
|
|
|
// Various status parts.
|
2019-03-10 21:42:13 +00:00
|
|
|
int is_supervisor_;
|
2019-03-24 22:20:54 +00:00
|
|
|
int interrupt_level_;
|
|
|
|
uint_fast32_t zero_result_; // The zero flag is set if this value is zero.
|
2019-03-16 21:54:58 +00:00
|
|
|
uint_fast32_t carry_flag_; // The carry flag is set if this value is non-zero.
|
|
|
|
uint_fast32_t extend_flag_; // The extend flag is set if this value is non-zero.
|
|
|
|
uint_fast32_t overflow_flag_; // The overflow flag is set if this value is non-zero.
|
|
|
|
uint_fast32_t negative_flag_; // The negative flag is set if this value is non-zero.
|
2019-03-24 22:20:54 +00:00
|
|
|
uint_fast32_t trace_flag_; // The trace flag is set if this value is non-zero.
|
2019-03-09 05:00:23 +00:00
|
|
|
|
2020-01-19 03:06:00 +00:00
|
|
|
uint_fast32_t last_trace_flag_ = 0;
|
|
|
|
|
2019-04-29 23:22:05 +00:00
|
|
|
// Bus inputs.
|
|
|
|
int bus_interrupt_level_ = 0;
|
|
|
|
bool dtack_ = false;
|
|
|
|
bool is_peripheral_address_ = false;
|
|
|
|
bool bus_error_ = false;
|
|
|
|
bool bus_request_ = false;
|
|
|
|
bool bus_acknowledge_ = false;
|
2019-05-01 02:07:48 +00:00
|
|
|
bool halt_ = false;
|
2019-04-29 23:22:05 +00:00
|
|
|
|
2019-08-05 01:06:34 +00:00
|
|
|
// Holds the interrupt level that should be serviced at the next instruction
|
|
|
|
// dispatch, if any.
|
|
|
|
int pending_interrupt_level_ = 0;
|
|
|
|
// Holds the interrupt level that is currently being serviced.
|
|
|
|
// TODO: surely this doesn't need to be distinct from the pending_interrupt_level_?
|
2019-05-01 19:19:24 +00:00
|
|
|
int accepted_interrupt_level_ = 0;
|
2019-05-01 19:26:36 +00:00
|
|
|
bool is_starting_interrupt_ = false;
|
2019-05-01 19:19:24 +00:00
|
|
|
|
2019-03-22 02:30:41 +00:00
|
|
|
HalfCycles half_cycles_left_to_run_;
|
2019-05-01 02:07:48 +00:00
|
|
|
HalfCycles e_clock_phase_;
|
2019-03-22 02:30:41 +00:00
|
|
|
|
2019-07-25 02:02:50 +00:00
|
|
|
enum class Operation: uint8_t {
|
2019-03-30 03:40:54 +00:00
|
|
|
None,
|
2019-04-28 01:29:50 +00:00
|
|
|
ABCD, SBCD, NBCD,
|
2019-04-02 01:21:26 +00:00
|
|
|
|
|
|
|
ADDb, ADDw, ADDl,
|
2019-04-16 15:19:45 +00:00
|
|
|
ADDQb, ADDQw, ADDQl,
|
2019-04-02 01:21:26 +00:00
|
|
|
ADDAw, ADDAl,
|
2019-04-24 13:59:54 +00:00
|
|
|
ADDQAw, ADDQAl,
|
2019-04-27 20:57:21 +00:00
|
|
|
ADDXb, ADDXw, ADDXl,
|
2019-04-16 15:19:45 +00:00
|
|
|
|
|
|
|
SUBb, SUBw, SUBl,
|
|
|
|
SUBQb, SUBQw, SUBQl,
|
2019-04-02 01:21:26 +00:00
|
|
|
SUBAw, SUBAl,
|
2019-04-24 13:59:54 +00:00
|
|
|
SUBQAw, SUBQAl,
|
2019-04-27 20:57:21 +00:00
|
|
|
SUBXb, SUBXw, SUBXl,
|
2019-03-16 21:54:58 +00:00
|
|
|
|
2019-03-30 03:13:41 +00:00
|
|
|
MOVEb, MOVEw, MOVEl, MOVEq,
|
2019-03-24 22:20:54 +00:00
|
|
|
MOVEAw, MOVEAl,
|
2019-05-29 18:37:15 +00:00
|
|
|
PEA,
|
2019-03-24 22:20:54 +00:00
|
|
|
|
2019-03-25 03:05:57 +00:00
|
|
|
MOVEtoSR, MOVEfromSR,
|
2019-04-08 02:24:17 +00:00
|
|
|
MOVEtoCCR,
|
2019-03-25 03:05:57 +00:00
|
|
|
|
2019-04-24 21:38:59 +00:00
|
|
|
ORItoSR, ORItoCCR,
|
|
|
|
ANDItoSR, ANDItoCCR,
|
|
|
|
EORItoSR, EORItoCCR,
|
|
|
|
|
2019-04-05 01:43:22 +00:00
|
|
|
BTSTb, BTSTl,
|
2019-04-15 22:11:02 +00:00
|
|
|
BCLRl, BCLRb,
|
|
|
|
CMPb, CMPw, CMPl,
|
2019-06-27 01:42:48 +00:00
|
|
|
CMPAw,
|
2019-04-15 22:11:02 +00:00
|
|
|
TSTb, TSTw, TSTl,
|
2019-03-26 02:54:49 +00:00
|
|
|
|
2019-06-03 19:29:50 +00:00
|
|
|
JMP, RTS,
|
2019-04-07 03:21:01 +00:00
|
|
|
BRA, Bcc,
|
|
|
|
DBcc,
|
2019-04-16 19:17:40 +00:00
|
|
|
Scc,
|
2019-04-08 02:07:39 +00:00
|
|
|
|
|
|
|
CLRb, CLRw, CLRl,
|
|
|
|
NEGXb, NEGXw, NEGXl,
|
|
|
|
NEGb, NEGw, NEGl,
|
2019-04-09 20:54:41 +00:00
|
|
|
|
|
|
|
ASLb, ASLw, ASLl, ASLm,
|
|
|
|
ASRb, ASRw, ASRl, ASRm,
|
|
|
|
LSLb, LSLw, LSLl, LSLm,
|
|
|
|
LSRb, LSRw, LSRl, LSRm,
|
|
|
|
ROLb, ROLw, ROLl, ROLm,
|
|
|
|
RORb, RORw, RORl, RORm,
|
|
|
|
ROXLb, ROXLw, ROXLl, ROXLm,
|
|
|
|
ROXRb, ROXRw, ROXRl, ROXRm,
|
2019-04-14 18:09:28 +00:00
|
|
|
|
|
|
|
MOVEMtoRl, MOVEMtoRw,
|
|
|
|
MOVEMtoMl, MOVEMtoMw,
|
2019-04-15 02:39:13 +00:00
|
|
|
|
2019-04-29 02:52:54 +00:00
|
|
|
MOVEPtoRl, MOVEPtoRw,
|
|
|
|
MOVEPtoMl, MOVEPtoMw,
|
|
|
|
|
2019-04-16 19:17:40 +00:00
|
|
|
ANDb, ANDw, ANDl,
|
|
|
|
EORb, EORw, EORl,
|
|
|
|
NOTb, NOTw, NOTl,
|
|
|
|
ORb, ORw, ORl,
|
2019-04-17 02:16:43 +00:00
|
|
|
|
|
|
|
MULU, MULS,
|
2019-04-27 02:22:35 +00:00
|
|
|
DIVU, DIVS,
|
2019-04-18 02:21:56 +00:00
|
|
|
|
2019-04-19 00:50:58 +00:00
|
|
|
RTE_RTR,
|
|
|
|
|
2019-04-28 19:52:58 +00:00
|
|
|
TRAP, TRAPV,
|
2019-04-28 19:47:21 +00:00
|
|
|
CHK,
|
2019-04-19 15:27:43 +00:00
|
|
|
|
|
|
|
EXG, SWAP,
|
2019-04-25 03:01:32 +00:00
|
|
|
|
|
|
|
BCHGl, BCHGb,
|
|
|
|
BSETl, BSETb,
|
2019-04-25 16:19:40 +00:00
|
|
|
|
|
|
|
TAS,
|
2019-04-25 22:22:19 +00:00
|
|
|
|
2019-04-26 02:54:58 +00:00
|
|
|
EXTbtow, EXTwtol,
|
2019-04-28 21:12:31 +00:00
|
|
|
|
|
|
|
LINK, UNLINK,
|
2019-04-29 23:30:00 +00:00
|
|
|
|
|
|
|
STOP,
|
2019-03-13 02:46:31 +00:00
|
|
|
};
|
|
|
|
|
2019-03-09 05:00:23 +00:00
|
|
|
/*!
|
2019-03-13 02:46:31 +00:00
|
|
|
Bus steps are sequences of things to communicate to the bus.
|
2019-03-17 02:36:09 +00:00
|
|
|
Standard behaviour is: (i) perform microcycle; (ii) perform action.
|
2019-03-09 05:00:23 +00:00
|
|
|
*/
|
2019-03-13 02:46:31 +00:00
|
|
|
struct BusStep {
|
2019-03-09 05:00:23 +00:00
|
|
|
Microcycle microcycle;
|
|
|
|
enum class Action {
|
2019-03-10 21:27:34 +00:00
|
|
|
None,
|
|
|
|
|
2019-03-19 02:51:32 +00:00
|
|
|
/// Performs effective_address_[0] += 2.
|
|
|
|
IncrementEffectiveAddress0,
|
|
|
|
|
|
|
|
/// Performs effective_address_[1] += 2.
|
|
|
|
IncrementEffectiveAddress1,
|
2019-03-10 21:27:34 +00:00
|
|
|
|
2019-04-03 23:13:10 +00:00
|
|
|
/// Performs effective_address_[0] -= 2.
|
|
|
|
DecrementEffectiveAddress0,
|
|
|
|
|
|
|
|
/// Performs effective_address_[1] -= 2.
|
|
|
|
DecrementEffectiveAddress1,
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
/// Performs program_counter_ += 2.
|
|
|
|
IncrementProgramCounter,
|
|
|
|
|
|
|
|
/// Copies prefetch_queue_[1] to prefetch_queue_[0].
|
|
|
|
AdvancePrefetch,
|
|
|
|
|
2021-09-09 01:03:37 +00:00
|
|
|
/// Performs effective_address_[0] += 2 and zeroes the final bit of the stack pointer.
|
|
|
|
IncrementEffectiveAddress0AlignStackPointer,
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
/*!
|
|
|
|
Terminates an atomic program; if nothing else is pending, schedules the next instruction.
|
|
|
|
This action is special in that it usurps any included microcycle. So any Step with this
|
|
|
|
as its action acts as an end-of-list sentinel.
|
|
|
|
*/
|
|
|
|
ScheduleNextProgram
|
|
|
|
|
|
|
|
} action = Action::None;
|
2019-03-17 01:47:46 +00:00
|
|
|
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline bool operator ==(const BusStep &rhs) const {
|
2019-03-17 01:47:46 +00:00
|
|
|
if(action != rhs.action) return false;
|
|
|
|
return microcycle == rhs.microcycle;
|
|
|
|
}
|
2019-03-17 02:36:09 +00:00
|
|
|
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline bool is_terminal() const {
|
2019-03-17 02:36:09 +00:00
|
|
|
return action == Action::ScheduleNextProgram;
|
|
|
|
}
|
2019-03-09 05:00:23 +00:00
|
|
|
};
|
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
/*!
|
|
|
|
A micro-op is: (i) an action to take; and (ii) a sequence of bus operations
|
|
|
|
to perform after taking the action.
|
|
|
|
|
2019-03-17 02:36:09 +00:00
|
|
|
NOTE: this therefore has the opposite order of behaviour compared to a BusStep,
|
|
|
|
the action occurs BEFORE the bus operations, not after.
|
|
|
|
|
|
|
|
A nullptr bus_program terminates a sequence of micro operations; the is_terminal
|
|
|
|
test should be used to query for that. The action on the final operation will
|
|
|
|
be performed.
|
2019-03-13 02:46:31 +00:00
|
|
|
*/
|
|
|
|
struct MicroOp {
|
2019-07-25 14:14:36 +00:00
|
|
|
enum class Action: uint8_t {
|
2019-03-13 02:46:31 +00:00
|
|
|
None,
|
2019-03-22 02:30:41 +00:00
|
|
|
|
|
|
|
/// Does whatever this instruction says is the main operation.
|
2019-03-14 01:08:13 +00:00
|
|
|
PerformOperation,
|
|
|
|
|
2019-03-19 02:51:32 +00:00
|
|
|
/*
|
|
|
|
All of the below will honour the source and destination masks
|
|
|
|
in deciding where to apply their actions.
|
|
|
|
*/
|
|
|
|
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Subtracts 1 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Decrement1,
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Subtracts 2 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Decrement2,
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Subtracts 4 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Decrement4,
|
|
|
|
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Adds 1 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Increment1,
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Adds 2 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Increment2,
|
2019-04-03 01:50:58 +00:00
|
|
|
/// Adds 4 from the [source/destination]_address.
|
2019-03-19 02:51:32 +00:00
|
|
|
Increment4,
|
|
|
|
|
2019-03-30 03:13:41 +00:00
|
|
|
/// Copies the source and/or destination to effective_address_.
|
|
|
|
CopyToEffectiveAddress,
|
2019-03-24 01:03:52 +00:00
|
|
|
|
2019-03-23 01:43:51 +00:00
|
|
|
/// Peeking into the end of the prefetch queue, calculates the proper target of (d16,An) addressing.
|
2019-03-19 02:51:32 +00:00
|
|
|
CalcD16An,
|
|
|
|
|
2019-03-23 01:43:51 +00:00
|
|
|
/// Peeking into the end of the prefetch queue, calculates the proper target of (d8,An,Xn) addressing.
|
2019-03-19 02:51:32 +00:00
|
|
|
CalcD8AnXn,
|
|
|
|
|
|
|
|
/// Peeking into the prefetch queue, calculates the proper target of (d16,PC) addressing,
|
|
|
|
/// adjusting as though it had been performed after the proper PC fetches. The source
|
|
|
|
/// and destination mask flags affect only the destination of the result.
|
|
|
|
CalcD16PC,
|
|
|
|
|
|
|
|
/// Peeking into the prefetch queue, calculates the proper target of (d8,An,Xn) addressing,
|
|
|
|
/// adjusting as though it had been performed after the proper PC fetches. The source
|
|
|
|
/// and destination mask flags affect only the destination of the result.
|
|
|
|
CalcD8PCXn,
|
|
|
|
|
|
|
|
/// Sets the high word according to the MSB of the low word.
|
|
|
|
SignExtendWord,
|
|
|
|
|
|
|
|
/// Sets the high three bytes according to the MSB of the low byte.
|
|
|
|
SignExtendByte,
|
2019-03-19 15:53:37 +00:00
|
|
|
|
2019-04-30 23:24:22 +00:00
|
|
|
/// From the next word in the prefetch queue assembles a sign-extended long word in either or
|
2019-03-23 01:43:51 +00:00
|
|
|
/// both of effective_address_[0] and effective_address_[1].
|
2019-03-25 03:05:57 +00:00
|
|
|
AssembleWordAddressFromPrefetch,
|
|
|
|
|
|
|
|
/// From the next word in the prefetch queue assembles a 0-padded 32-bit long word in either or
|
|
|
|
/// both of bus_data_[0] and bus_data_[1].
|
|
|
|
AssembleWordDataFromPrefetch,
|
2019-03-23 01:43:51 +00:00
|
|
|
|
|
|
|
/// Copies the next two prefetch words into one of the effective_address_.
|
2019-03-25 03:05:57 +00:00
|
|
|
AssembleLongWordAddressFromPrefetch,
|
|
|
|
|
|
|
|
/// Copies the next two prefetch words into one of the bus_data_.
|
2019-04-07 03:21:01 +00:00
|
|
|
AssembleLongWordDataFromPrefetch,
|
2019-04-14 18:09:28 +00:00
|
|
|
|
|
|
|
/// Copies the low part of the prefetch queue into next_word_.
|
|
|
|
CopyNextWord,
|
2019-04-15 00:02:18 +00:00
|
|
|
|
|
|
|
/// Performs write-back of post-increment address and/or sign extensions as necessary.
|
|
|
|
MOVEMtoRComplete,
|
|
|
|
|
|
|
|
/// Performs write-back of pre-decrement address.
|
|
|
|
MOVEMtoMComplete,
|
2019-04-15 19:14:38 +00:00
|
|
|
|
2019-04-17 01:29:37 +00:00
|
|
|
// (i) inspects the prefetch queue to determine the length of this instruction and copies the next PC to destination_bus_data_;
|
2019-04-16 02:02:52 +00:00
|
|
|
// (ii) copies the stack pointer minus 4 to effective_address_[1];
|
2019-04-15 19:14:38 +00:00
|
|
|
// (iii) decrements the stack pointer by four.
|
2019-04-17 14:02:14 +00:00
|
|
|
PrepareJSR,
|
|
|
|
PrepareBSR,
|
2019-04-16 23:50:10 +00:00
|
|
|
|
2019-04-15 19:14:38 +00:00
|
|
|
// (i) copies the stack pointer to effective_address_[0];
|
|
|
|
// (ii) increments the stack pointer by four.
|
|
|
|
PrepareRTS,
|
2019-04-19 00:50:58 +00:00
|
|
|
|
|
|
|
// (i) fills in the proper stack addresses to the bus steps for this micro-op; and
|
|
|
|
// (ii) adjusts the stack pointer appropriately.
|
|
|
|
PrepareRTE_RTR,
|
2019-05-01 19:19:24 +00:00
|
|
|
|
|
|
|
// Performs the necessary status word substitution for the current interrupt level,
|
|
|
|
// and does the first part of initialising the trap steps.
|
|
|
|
PrepareINT,
|
|
|
|
|
|
|
|
// Observes the bus_error_, valid_peripheral_address_ and/or the value currently in
|
|
|
|
// source_bus_data_ to determine an interrupt vector, and fills in the final trap
|
|
|
|
// steps detail appropriately.
|
|
|
|
PrepareINTVector,
|
2019-03-19 02:51:32 +00:00
|
|
|
};
|
2019-12-22 05:22:17 +00:00
|
|
|
static constexpr int SourceMask = 1 << 7;
|
|
|
|
static constexpr int DestinationMask = 1 << 6;
|
2019-07-25 14:14:36 +00:00
|
|
|
uint8_t action = uint8_t(Action::None);
|
2019-03-17 18:34:16 +00:00
|
|
|
|
2020-05-13 02:22:21 +00:00
|
|
|
static constexpr uint16_t NoBusProgram = std::numeric_limits<uint16_t>::max();
|
2019-07-25 14:14:36 +00:00
|
|
|
uint16_t bus_program = NoBusProgram;
|
2019-03-15 01:22:02 +00:00
|
|
|
|
|
|
|
MicroOp() {}
|
2019-07-25 14:14:36 +00:00
|
|
|
MicroOp(uint8_t action) : action(action) {}
|
|
|
|
MicroOp(uint8_t action, uint16_t bus_program) : action(action), bus_program(bus_program) {}
|
2019-03-19 02:51:32 +00:00
|
|
|
|
2019-07-25 14:14:36 +00:00
|
|
|
MicroOp(Action action) : MicroOp(uint8_t(action)) {}
|
|
|
|
MicroOp(Action action, uint16_t bus_program) : MicroOp(uint8_t(action), bus_program) {}
|
2019-03-17 01:47:46 +00:00
|
|
|
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline bool is_terminal() const {
|
2019-07-25 14:14:36 +00:00
|
|
|
return bus_program == std::numeric_limits<uint16_t>::max();
|
2019-03-17 01:47:46 +00:00
|
|
|
}
|
2019-03-13 02:46:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
A program represents the implementation of a particular opcode, as a sequence
|
|
|
|
of micro-ops and, separately, the operation to perform plus whatever other
|
|
|
|
fields the operation requires.
|
2019-07-24 03:03:15 +00:00
|
|
|
|
2019-07-25 02:02:50 +00:00
|
|
|
Some of the fields are slightly convoluted in how they identify the information
|
|
|
|
they reference; this is done to keep this struct as small as possible due to
|
|
|
|
concerns about cache size.
|
2019-07-24 03:03:15 +00:00
|
|
|
|
2019-07-25 02:02:50 +00:00
|
|
|
On the 64-bit Intel processor this emulator was developed on, the struct below
|
|
|
|
adds up to 8 bytes; four for the initial uint32_t and then one each for the
|
|
|
|
remaining fields, with no additional padding being inserted by the compiler.
|
2019-03-13 02:46:31 +00:00
|
|
|
*/
|
|
|
|
struct Program {
|
2021-12-23 21:28:55 +00:00
|
|
|
Program() {
|
|
|
|
// Initialisers for bitfields aren't available until C++20. So, yuck, do it manually.
|
|
|
|
requires_supervisor = 0;
|
|
|
|
source = 0;
|
|
|
|
dest = 0;
|
|
|
|
destination_offset = 0;
|
|
|
|
source_offset = 0;
|
|
|
|
}
|
|
|
|
|
2021-12-23 21:32:21 +00:00
|
|
|
static constexpr uint32_t NoSuchProgram = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
2019-07-25 02:07:17 +00:00
|
|
|
/// The offset into the all_micro_ops_ at which micro-ops for this instruction begin,
|
|
|
|
/// or std::numeric_limits<uint32_t>::max() if this is an invalid Program.
|
2021-12-23 21:32:21 +00:00
|
|
|
uint32_t micro_operations = NoSuchProgram;
|
2019-07-25 02:02:50 +00:00
|
|
|
/// The overarching operation applied by this program when the moment comes.
|
2019-07-24 22:39:36 +00:00
|
|
|
Operation operation;
|
2019-07-25 02:02:50 +00:00
|
|
|
/// The number of bytes after the beginning of an instance of ProcessorStorage that the RegisterPair32 containing
|
|
|
|
/// a source value for this operation lies at.
|
2021-12-23 21:28:55 +00:00
|
|
|
uint8_t source_offset: 7;
|
2019-07-25 02:02:50 +00:00
|
|
|
/// The number of bytes after the beginning of an instance of ProcessorStorage that the RegisterPair32 containing
|
|
|
|
/// a destination value for this operation lies at.
|
2021-12-23 21:28:55 +00:00
|
|
|
uint8_t destination_offset: 7;
|
|
|
|
/// Set if this program requires supervisor mode.
|
|
|
|
bool requires_supervisor: 1;
|
|
|
|
/// The source address register (for pre-decrement and post-increment actions).
|
|
|
|
uint8_t source: 3;
|
|
|
|
/// Destination address register.
|
|
|
|
uint8_t dest: 3;
|
2019-07-24 22:39:36 +00:00
|
|
|
|
2020-05-30 04:37:06 +00:00
|
|
|
void set_source_address([[maybe_unused]] ProcessorStorage &storage, int index) {
|
2021-12-23 21:28:55 +00:00
|
|
|
source = uint8_t(index);
|
|
|
|
assert(int(source) == index);
|
2019-07-24 22:39:36 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 04:37:06 +00:00
|
|
|
void set_destination_address([[maybe_unused]] ProcessorStorage &storage, int index) {
|
2021-12-23 21:28:55 +00:00
|
|
|
dest = uint8_t(index);
|
|
|
|
assert(int(dest) == index);
|
2019-07-24 22:39:36 +00:00
|
|
|
}
|
|
|
|
|
2021-12-23 21:28:55 +00:00
|
|
|
void set_requires_supervisor(bool req) {
|
|
|
|
requires_supervisor = req;
|
|
|
|
assert(requires_supervisor == req);
|
2019-07-25 02:02:50 +00:00
|
|
|
}
|
|
|
|
|
2019-07-24 22:39:36 +00:00
|
|
|
void set_source(ProcessorStorage &storage, RegisterPair32 *target) {
|
2019-07-25 02:02:50 +00:00
|
|
|
source_offset = decltype(source_offset)(reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage));
|
2020-07-03 02:24:04 +00:00
|
|
|
// Test that destination_offset could be stored fully within the integer size provided for source_offset.
|
2019-07-25 02:02:50 +00:00
|
|
|
assert(source_offset == (reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage)));
|
2019-07-24 22:39:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_destination(ProcessorStorage &storage, RegisterPair32 *target) {
|
2019-07-25 02:02:50 +00:00
|
|
|
destination_offset = decltype(destination_offset)(reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage));
|
2020-07-03 02:24:04 +00:00
|
|
|
// Test that destination_offset could be stored fully within the integer size provided for destination_offset.
|
2019-07-25 02:02:50 +00:00
|
|
|
assert(destination_offset == (reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage)));
|
2019-07-24 22:39:36 +00:00
|
|
|
}
|
2019-03-25 03:05:57 +00:00
|
|
|
|
|
|
|
void set_source(ProcessorStorage &storage, int mode, int reg) {
|
2019-07-24 22:39:36 +00:00
|
|
|
set_source_address(storage, reg);
|
2019-03-25 03:05:57 +00:00
|
|
|
switch(mode) {
|
2020-05-19 03:55:54 +00:00
|
|
|
case 0: set_source(storage, &storage.data_[reg]); break;
|
|
|
|
case 1: set_source(storage, &storage.address_[reg]); break;
|
|
|
|
default: set_source(storage, &storage.source_bus_data_); break;
|
2019-03-25 03:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_destination(ProcessorStorage &storage, int mode, int reg) {
|
2019-07-24 22:39:36 +00:00
|
|
|
set_destination_address(storage, reg);
|
2019-03-25 03:05:57 +00:00
|
|
|
switch(mode) {
|
2020-05-19 03:55:54 +00:00
|
|
|
case 0: set_destination(storage, &storage.data_[reg]); break;
|
|
|
|
case 1: set_destination(storage, &storage.address_[reg]); break;
|
|
|
|
default: set_destination(storage, &storage.destination_bus_data_); break;
|
2019-03-25 03:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-13 02:46:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Storage for all the sequences of bus steps and micro-ops used throughout
|
|
|
|
// the 68000.
|
|
|
|
std::vector<BusStep> all_bus_steps_;
|
|
|
|
std::vector<MicroOp> all_micro_ops_;
|
2019-03-09 05:00:23 +00:00
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
// A lookup table from instructions to implementations.
|
|
|
|
Program instructions[65536];
|
|
|
|
|
2019-04-29 17:45:53 +00:00
|
|
|
// Special steps and programs for exception handlers.
|
2019-03-26 02:54:49 +00:00
|
|
|
BusStep *reset_bus_steps_;
|
2019-04-30 23:24:22 +00:00
|
|
|
MicroOp *long_exception_micro_ops_; // i.e. those that leave 14 bytes on the stack — bus error and address error.
|
|
|
|
MicroOp *short_exception_micro_ops_; // i.e. those that leave 6 bytes on the stack — everything else (other than interrupts).
|
|
|
|
MicroOp *interrupt_micro_ops_;
|
2019-03-26 02:54:49 +00:00
|
|
|
|
2019-04-07 03:21:01 +00:00
|
|
|
// Special micro-op sequences and storage for conditionals.
|
2019-03-26 02:54:49 +00:00
|
|
|
BusStep *branch_taken_bus_steps_;
|
|
|
|
BusStep *branch_byte_not_taken_bus_steps_;
|
|
|
|
BusStep *branch_word_not_taken_bus_steps_;
|
2019-04-16 03:20:36 +00:00
|
|
|
BusStep *bsr_bus_steps_;
|
2019-03-13 02:46:31 +00:00
|
|
|
|
2019-04-07 03:21:01 +00:00
|
|
|
uint32_t dbcc_false_address_;
|
|
|
|
BusStep *dbcc_condition_true_steps_;
|
|
|
|
BusStep *dbcc_condition_false_no_branch_steps_;
|
|
|
|
BusStep *dbcc_condition_false_branch_steps_;
|
|
|
|
|
2019-04-18 02:21:56 +00:00
|
|
|
BusStep *movem_read_steps_;
|
|
|
|
BusStep *movem_write_steps_;
|
|
|
|
|
2020-05-19 03:55:54 +00:00
|
|
|
// These two are dynamically modified depending on the particular
|
|
|
|
// TRAP and bus error.
|
2019-04-18 02:21:56 +00:00
|
|
|
BusStep *trap_steps_;
|
2019-04-30 23:24:22 +00:00
|
|
|
BusStep *bus_error_steps_;
|
2019-04-14 18:31:13 +00:00
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
// Current bus step pointer, and outer program pointer.
|
2020-05-19 03:55:54 +00:00
|
|
|
const Program *active_program_ = nullptr;
|
|
|
|
const MicroOp *active_micro_op_ = nullptr;
|
|
|
|
const BusStep *active_step_ = nullptr;
|
2019-04-30 23:24:22 +00:00
|
|
|
RegisterPair16 decoded_instruction_ = 0;
|
2019-04-14 18:09:28 +00:00
|
|
|
uint16_t next_word_ = 0;
|
2019-03-10 21:27:34 +00:00
|
|
|
|
2019-03-18 01:57:00 +00:00
|
|
|
/// Copies address_[7] to the proper stack pointer based on current mode.
|
|
|
|
void write_back_stack_pointer();
|
|
|
|
|
|
|
|
/// Sets or clears the supervisor flag, ensuring the stack pointer is properly updated.
|
|
|
|
void set_is_supervisor(bool);
|
|
|
|
|
2019-04-18 02:21:56 +00:00
|
|
|
// Transient storage for MOVEM, TRAP and others.
|
|
|
|
RegisterPair16 throwaway_value_;
|
2019-04-15 00:02:18 +00:00
|
|
|
uint32_t movem_final_address_;
|
2020-05-19 03:55:54 +00:00
|
|
|
uint32_t precomputed_addresses_[65]; // This is a big chunk of rarely-used storage. It's placed last deliberately.
|
2019-04-14 18:09:28 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Evaluates the conditional described by @c code and returns @c true or @c false to
|
|
|
|
indicate the result of that evaluation.
|
|
|
|
*/
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline bool evaluate_condition(uint8_t code) {
|
2019-04-07 03:21:01 +00:00
|
|
|
switch(code & 0xf) {
|
|
|
|
default:
|
|
|
|
case 0x00: return true; // true
|
|
|
|
case 0x01: return false; // false
|
|
|
|
case 0x02: return zero_result_ && !carry_flag_; // high
|
|
|
|
case 0x03: return !zero_result_ || carry_flag_; // low or same
|
|
|
|
case 0x04: return !carry_flag_; // carry clear
|
|
|
|
case 0x05: return carry_flag_; // carry set
|
|
|
|
case 0x06: return zero_result_; // not equal
|
|
|
|
case 0x07: return !zero_result_; // equal
|
|
|
|
case 0x08: return !overflow_flag_; // overflow clear
|
|
|
|
case 0x09: return overflow_flag_; // overflow set
|
|
|
|
case 0x0a: return !negative_flag_; // positive
|
|
|
|
case 0x0b: return negative_flag_; // negative
|
|
|
|
case 0x0c: // greater than or equal
|
|
|
|
return (negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_);
|
|
|
|
case 0x0d: // less than
|
2019-04-24 14:07:17 +00:00
|
|
|
return (negative_flag_ && !overflow_flag_) || (!negative_flag_ && overflow_flag_);
|
2019-04-07 03:21:01 +00:00
|
|
|
case 0x0e: // greater than
|
|
|
|
return zero_result_ && ((negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_));
|
|
|
|
case 0x0f: // less than or equal
|
2019-04-24 14:07:17 +00:00
|
|
|
return !zero_result_ || (negative_flag_ && !overflow_flag_) || (!negative_flag_ && overflow_flag_);
|
2019-04-07 03:21:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-30 23:24:22 +00:00
|
|
|
/*!
|
|
|
|
Fills in the appropriate addresses and values to complete the TRAP steps — those
|
|
|
|
representing a short-form exception — and mutates the status register as if one
|
|
|
|
were beginning.
|
|
|
|
*/
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline void populate_trap_steps(uint32_t vector, uint16_t status) {
|
2019-04-28 19:47:21 +00:00
|
|
|
// Fill in the status word value.
|
2020-05-19 03:55:54 +00:00
|
|
|
destination_bus_data_.full = status;
|
2019-04-28 19:47:21 +00:00
|
|
|
|
2019-04-29 17:45:53 +00:00
|
|
|
// Switch to supervisor mode, disable the trace bit.
|
2019-04-28 19:47:21 +00:00
|
|
|
set_is_supervisor(true);
|
2020-01-19 03:06:00 +00:00
|
|
|
trace_flag_ = last_trace_flag_ = 0;
|
2019-04-28 19:47:21 +00:00
|
|
|
|
|
|
|
// Pick a vector.
|
|
|
|
effective_address_[0].full = vector << 2;
|
|
|
|
|
|
|
|
// Schedule the proper stack activity.
|
2019-04-30 23:24:22 +00:00
|
|
|
precomputed_addresses_[0] = address_[7].full - 2; // PC.l
|
2020-05-19 03:55:54 +00:00
|
|
|
precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_)
|
2019-04-30 23:24:22 +00:00
|
|
|
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
|
2019-04-28 19:47:21 +00:00
|
|
|
address_[7].full -= 6;
|
2019-04-30 02:08:16 +00:00
|
|
|
|
|
|
|
// Set the default timing.
|
|
|
|
trap_steps_->microcycle.length = HalfCycles(8);
|
2019-04-28 19:47:21 +00:00
|
|
|
}
|
|
|
|
|
2019-07-17 19:09:26 +00:00
|
|
|
forceinline void populate_bus_error_steps(uint32_t vector, uint16_t status, uint16_t bus_status, RegisterPair32 faulting_address) {
|
2019-04-30 23:24:22 +00:00
|
|
|
// Fill in the status word value.
|
2020-05-19 03:55:54 +00:00
|
|
|
destination_bus_data_.halves.low.full = status;
|
|
|
|
destination_bus_data_.halves.high.full = bus_status;
|
2019-04-30 23:24:22 +00:00
|
|
|
effective_address_[1] = faulting_address;
|
|
|
|
|
|
|
|
// Switch to supervisor mode, disable the trace bit.
|
|
|
|
set_is_supervisor(true);
|
2020-01-19 03:06:00 +00:00
|
|
|
trace_flag_ = last_trace_flag_ = 0;
|
2019-04-30 23:24:22 +00:00
|
|
|
|
|
|
|
// Pick a vector.
|
|
|
|
effective_address_[0].full = vector << 2;
|
|
|
|
|
|
|
|
// Schedule the proper stack activity.
|
|
|
|
precomputed_addresses_[0] = address_[7].full - 2; // PC.l
|
|
|
|
precomputed_addresses_[1] = address_[7].full - 6; // status word
|
|
|
|
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
|
|
|
|
precomputed_addresses_[3] = address_[7].full - 8; // current instruction
|
|
|
|
precomputed_addresses_[4] = address_[7].full - 10; // fault address.l
|
|
|
|
precomputed_addresses_[5] = address_[7].full - 14; // bus cycle status word
|
|
|
|
precomputed_addresses_[6] = address_[7].full - 12; // fault address.h
|
|
|
|
address_[7].full -= 14;
|
|
|
|
}
|
|
|
|
|
2020-05-16 04:06:04 +00:00
|
|
|
inline uint16_t get_status() const;
|
|
|
|
inline void set_status(uint16_t);
|
|
|
|
|
2019-03-09 05:00:23 +00:00
|
|
|
private:
|
2020-05-30 23:31:17 +00:00
|
|
|
friend struct ProcessorStorageConstructor;
|
2019-04-26 02:06:05 +00:00
|
|
|
friend class ProcessorStorageTests;
|
2020-05-30 23:31:17 +00:00
|
|
|
friend struct State;
|
2019-03-09 05:00:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* MC68000Storage_h */
|