1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00
CLK/Processors/68000/Implementation/68000Storage.hpp
Thomas Harte 5c7f94d2ef Introduce the possibility of operation type as a template parameter.
It's already proven possible to provide this for instruction fetch, so I think it'll immediately be a win. But more importantly it opens a path forwards for further improvement.
2023-11-27 11:48:34 -05:00

241 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 68000Storage.hpp
// Clock Signal
//
// Created by Thomas Harte on 16/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef MC68000Storage_h
#define MC68000Storage_h
#include "../../../InstructionSets/M68k/Decoder.hpp"
#include "../../../InstructionSets/M68k/Perform.hpp"
#include "../../../InstructionSets/M68k/Status.hpp"
#include <limits>
namespace CPU::MC68000 {
struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
ProcessorBase() {
read_program_announce.address = read_program.address = &program_counter_.l;
}
ProcessorBase(const ProcessorBase& rhs) = delete;
ProcessorBase& operator=(const ProcessorBase& rhs) = delete;
int state_ = 0;
/// Counts time left on the clock before the current batch of processing
/// is complete; may be less than zero.
HalfCycles time_remaining_;
/// E clock phase.
HalfCycles e_clock_phase_;
/// Current supervisor state, for direct provision to the bus handler.
int is_supervisor_ = 1;
// A decoder for instructions, plus all collected information about the
// current instruction.
InstructionSet::M68k::Predecoder<InstructionSet::M68k::Model::M68000> decoder_;
InstructionSet::M68k::Preinstruction instruction_;
uint16_t opcode_;
uint8_t operand_flags_;
SlicedInt32 instruction_address_;
// Register state.
InstructionSet::M68k::Status status_;
SlicedInt32 program_counter_;
SlicedInt32 registers_[16]{}; // D0D7 followed by A0A7.
SlicedInt32 stack_pointers_[2];
/// Current state of the DTACK input.
bool dtack_ = false;
/// Current state of the VPA input.
bool vpa_ = false;
/// Current state of the BERR input.
bool berr_ = false;
/// Current input interrupt level.
int bus_interrupt_level_ = 0;
// Whether to trace at the end of this instruction.
InstructionSet::M68k::Status::FlagT should_trace_ = 0;
// I don't have great information on the 68000 interrupt latency; as a first
// guess, capture the bus interrupt level upon every prefetch, and use that for
// the inner-loop decision.
int captured_interrupt_level_ = 0;
/// Contains the prefetch queue; the most-recently fetched thing is the
/// low portion of this word, and the thing fetched before that has
/// proceeded to the high portion.
SlicedInt32 prefetch_;
// Temporary storage for the current instruction's operands
// and the corresponding effective addresses.
CPU::SlicedInt32 operand_[2];
CPU::SlicedInt32 effective_address_[2];
/// If currently in the wait-for-DTACK state, this indicates where to go
/// upon receipt of DTACK or VPA. BERR will automatically segue
/// into the proper exception.
int post_dtack_state_ = 0;
/// If using CalcEffectiveAddress, this is the state to adopt after the
/// effective address for next_operand_ has been calculated.
int post_ea_state_ = 0;
/// The perform state for this operation.
int perform_state_ = 0;
/// When fetching or storing operands, this is the next one to fetch
/// or store.
int next_operand_ = -1;
/// Storage for a temporary address, which can't be a local because it'll
/// be used to populate microcycles, which may persist beyond an entry
/// and exit of run_for (especially between an address announcement, and
/// a data select).
SlicedInt32 temporary_address_;
/// Storage for a temporary value; primarily used by MOVEP to split a 32-bit
/// source into bus-compatible byte units.
SlicedInt32 temporary_value_;
/// A record of the exception to trigger.
int exception_vector_ = 0;
/// Transient storage for exception processing.
SlicedInt16 captured_status_;
/// An internal flag used during various dynamically-sized instructions
/// (e.g. BCHG, DIVU) to indicate how much additional processing happened;
/// this is measured in microcycles.
int dynamic_instruction_length_ = 0;
/// Two bits of state for MOVEM, being the curent register and what to
/// add to it to get to the next register.
int register_index_ = 0, register_delta_ = 0;
// A lookup table that aids with effective address calculation in
// predecrement and postincrement modes; index as [size][register]
// and note that [0][7] is 2 rather than 1.
static constexpr uint32_t address_increments[3][8] = {
{ 1, 1, 1, 1, 1, 1, 1, 2, },
{ 2, 2, 2, 2, 2, 2, 2, 2, },
{ 4, 4, 4, 4, 4, 4, 4, 4, },
};
// A lookup table that ensures write-back to data registers affects
// only the correct bits.
static constexpr uint32_t size_masks[3] = { 0xff, 0xffff, 0xffff'ffff };
// Assumptions used by the lookup tables above:
static_assert(int(InstructionSet::M68k::DataSize::Byte) == 0);
static_assert(int(InstructionSet::M68k::DataSize::Word) == 1);
static_assert(int(InstructionSet::M68k::DataSize::LongWord) == 2);
/// Used by some dedicated read-modify-write perform patterns to
/// determine the size of the bus operation.
Microcycle::OperationT select_flag_ = 0;
// Captured bus/address-error state.
Microcycle bus_error_;
// Flow controller methods implemented.
using Preinstruction = InstructionSet::M68k::Preinstruction;
template <typename IntT> void did_mulu(IntT);
template <typename IntT> void did_muls(IntT);
inline void did_chk(bool, bool);
inline void did_scc(bool);
template <typename IntT> void did_shift(int);
template <bool did_overflow> void did_divu(uint32_t, uint32_t);
template <bool did_overflow> void did_divs(int32_t, int32_t);
inline void did_bit_op(int);
inline void did_update_status();
template <typename IntT> void complete_bcc(bool, IntT);
inline void complete_dbcc(bool, bool, int16_t);
inline void move_to_usp(uint32_t);
inline void move_from_usp(uint32_t &);
inline void tas(Preinstruction, uint32_t);
template <bool use_current_instruction_pc = true> void raise_exception(int);
// These aren't implemented because the specific details of the implementation
// mean that the performer call-out isn't necessary.
template <typename IntT> void movep(Preinstruction, uint32_t, uint32_t) {}
template <typename IntT> void movem_toM(Preinstruction, uint32_t, uint32_t) {}
template <typename IntT> void movem_toR(Preinstruction, uint32_t, uint32_t) {}
void jsr(uint32_t) {}
void bsr(uint32_t) {}
void jmp(uint32_t) {}
inline void pea(uint32_t) {}
inline void link(Preinstruction, uint32_t) {}
inline void unlink(uint32_t &) {}
inline void rtr() {}
inline void rte() {}
inline void rts() {}
inline void reset() {}
inline void stop() {}
// Some microcycles that will be modified as required and used in the main loop;
// the semantics of a switch statement make in-place declarations awkward and
// some of these may persist across multiple calls to run_for.
Microcycle idle{0};
// Read a program word. All accesses via the program counter are word sized.
static constexpr Microcycle::OperationT
ReadProgramAnnounceOperation = Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram;
static constexpr Microcycle::OperationT
ReadProgramOperation = Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram;
Microcycle read_program_announce { ReadProgramAnnounceOperation };
Microcycle read_program { ReadProgramOperation };
// Read a data word or byte.
Microcycle access_announce {
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData
};
Microcycle access {
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData
};
// TAS.
static constexpr Microcycle::OperationT
TASOperations[5] = {
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData,
Microcycle::Read | Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte,
Microcycle::SameAddress,
Microcycle::SameAddress | Microcycle::IsData,
Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte,
};
Microcycle tas_cycles[5] = {
{ TASOperations[0] },
{ TASOperations[1] },
{ TASOperations[2] },
{ TASOperations[3] },
{ TASOperations[4] },
};
// Reset.
static constexpr Microcycle::OperationT ResetOperation = Microcycle::Reset;
Microcycle reset_cycle { ResetOperation, HalfCycles(248) };
// Interrupt acknowledge.
static constexpr Microcycle::OperationT
InterruptCycleOperations[2] = {
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress,
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte
};
Microcycle interrupt_cycles[2] = {
{ InterruptCycleOperations[0] },
{ InterruptCycleOperations[1] },
};
// Holding spot when awaiting DTACK/etc.
Microcycle awaiting_dtack;
};
}
#endif /* MC68000Storage_h */