mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-19 07:31:15 +00:00
Completes performance of NBCD D0.
This commit is contained in:
parent
eed2672db5
commit
4a40581deb
@ -181,7 +181,7 @@ struct Test68000 {
|
|||||||
// This is the test class for 68000 execution.
|
// This is the test class for 68000 execution.
|
||||||
struct Test68000: public CPU::MC68000Mk2::BusHandler {
|
struct Test68000: public CPU::MC68000Mk2::BusHandler {
|
||||||
std::array<uint8_t, 16*1024*1024> ram;
|
std::array<uint8_t, 16*1024*1024> ram;
|
||||||
CPU::MC68000Mk2::Processor<Test68000> processor;
|
CPU::MC68000Mk2::Processor<Test68000, true, false, true> processor;
|
||||||
std::function<void(void)> comparitor;
|
std::function<void(void)> comparitor;
|
||||||
|
|
||||||
Test68000() : processor(*this) {}
|
Test68000() : processor(*this) {}
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
#include "../../Numeric/RegisterSizes.hpp"
|
#include "../../Numeric/RegisterSizes.hpp"
|
||||||
#include "../../InstructionSets/M68k/RegisterSet.hpp"
|
#include "../../InstructionSets/M68k/RegisterSet.hpp"
|
||||||
|
|
||||||
#include "Implementation/68000Mk2Storage.hpp"
|
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
namespace MC68000Mk2 {
|
namespace MC68000Mk2 {
|
||||||
|
|
||||||
@ -361,6 +359,14 @@ struct State {
|
|||||||
InstructionSet::M68k::RegisterSet registers;
|
InstructionSet::M68k::RegisterSet registers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "Implementation/68000Mk2Storage.hpp"
|
||||||
|
|
||||||
|
namespace CPU {
|
||||||
|
namespace MC68000Mk2 {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Provides an emulation of the 68000 with accurate bus logic via the @c BusHandler, subject to the following template parameters:
|
Provides an emulation of the 68000 with accurate bus logic via the @c BusHandler, subject to the following template parameters:
|
||||||
|
|
||||||
|
@ -60,31 +60,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
// (* an extension supported by at least GCC, Clang and MSVC)
|
// (* an extension supported by at least GCC, Clang and MSVC)
|
||||||
|
|
||||||
|
|
||||||
// Some microcycles that will be modified as required and used in the loop below;
|
|
||||||
// the semantics of a switch statement make in-place declarations awkward.
|
|
||||||
Microcycle idle{0};
|
|
||||||
|
|
||||||
// Read a data word.
|
|
||||||
Microcycle read_word_data_announce {
|
|
||||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData
|
|
||||||
};
|
|
||||||
Microcycle read_word_data {
|
|
||||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read a program word. All accesses via the program counter are word sized.
|
|
||||||
Microcycle read_program_announce {
|
|
||||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram
|
|
||||||
};
|
|
||||||
Microcycle read_program {
|
|
||||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram
|
|
||||||
};
|
|
||||||
|
|
||||||
// Holding spot when awaiting DTACK/etc.
|
|
||||||
Microcycle awaiting_dtack;
|
|
||||||
|
|
||||||
// Spare containers:
|
// Spare containers:
|
||||||
uint32_t address = 0; // For an address, where it isn't provideable by reference.
|
|
||||||
HalfCycles delay; // To receive any additional time added on by calls to perform_bus_operation.
|
HalfCycles delay; // To receive any additional time added on by calls to perform_bus_operation.
|
||||||
|
|
||||||
// Helper macros for common bus transactions:
|
// Helper macros for common bus transactions:
|
||||||
@ -125,7 +101,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
// Performs the memory access implied by the announce, perform pair,
|
// Performs the memory access implied by the announce, perform pair,
|
||||||
// honouring DTACK, BERR and VPA as necessary.
|
// honouring DTACK, BERR and VPA as necessary.
|
||||||
#define AccessPair(addr, val, announce, perform) \
|
#define AccessPair(addr, val, announce, perform) \
|
||||||
perform.address = announce.address = &addr; \
|
announce.address = perform.address = &addr; \
|
||||||
perform.value = &val; \
|
perform.value = &val; \
|
||||||
if constexpr (!dtack_is_implicit) { \
|
if constexpr (!dtack_is_implicit) { \
|
||||||
announce.length = HalfCycles(4); \
|
announce.length = HalfCycles(4); \
|
||||||
@ -135,10 +111,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
CompleteAccess(perform);
|
CompleteAccess(perform);
|
||||||
|
|
||||||
// Reads the data (i.e. non-program) word from addr into val.
|
// Reads the data (i.e. non-program) word from addr into val.
|
||||||
#define ReadDataWord(addr, val) AccessPair(addr, val, read_word_data_announce, read_word_data)
|
#define ReadDataWord(addr, val) \
|
||||||
|
AccessPair(addr, val, read_word_data_announce, read_word_data)
|
||||||
|
|
||||||
// Reads the program (i.e. non-data) word from addr into val.
|
// Reads the program (i.e. non-data) word from addr into val.
|
||||||
#define ReadProgramWord(val) \
|
#define ReadProgramWord(val) \
|
||||||
AccessPair(program_counter_.l, val, read_program_announce, read_program); \
|
AccessPair(program_counter_.l, val, read_program_announce, read_program); \
|
||||||
program_counter_.l += 2;
|
program_counter_.l += 2;
|
||||||
|
|
||||||
@ -175,10 +152,17 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
status_.trace_flag = 0;
|
status_.trace_flag = 0;
|
||||||
did_update_status();
|
did_update_status();
|
||||||
|
|
||||||
address = 0; ReadDataWord(address, registers_[15].high); // nF
|
temporary_address_ = 0;
|
||||||
address += 2; ReadDataWord(address, registers_[15].low); // nf
|
ReadDataWord(temporary_address_, registers_[15].high); // nF
|
||||||
address += 2; ReadDataWord(address, program_counter_.high); // nV
|
|
||||||
address += 2; ReadDataWord(address, program_counter_.low); // nv
|
temporary_address_ += 2;
|
||||||
|
ReadDataWord(temporary_address_, registers_[15].low); // nf
|
||||||
|
|
||||||
|
temporary_address_ += 2;
|
||||||
|
ReadDataWord(temporary_address_, program_counter_.high); // nV
|
||||||
|
|
||||||
|
temporary_address_ += 2;
|
||||||
|
ReadDataWord(temporary_address_, program_counter_.low); // nv
|
||||||
|
|
||||||
Prefetch(); // np
|
Prefetch(); // np
|
||||||
IdleBus(1); // n
|
IdleBus(1); // n
|
||||||
@ -196,14 +180,14 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
|
|
||||||
// TODO: check for privilege and unrecognised instructions.
|
// TODO: check for privilege and unrecognised instructions.
|
||||||
|
|
||||||
// Obtain operand flags and pick a perform pattern.
|
|
||||||
setup_operation();
|
|
||||||
|
|
||||||
// Signal the bus handler if requested.
|
// Signal the bus handler if requested.
|
||||||
if constexpr (signal_will_perform) {
|
if constexpr (signal_will_perform) {
|
||||||
bus_handler_.will_perform(instruction_address_, opcode_);
|
bus_handler_.will_perform(instruction_address_, opcode_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Obtain operand flags and pick a perform pattern.
|
||||||
|
setup_operation();
|
||||||
|
|
||||||
// Ensure the first parameter is next fetched.
|
// Ensure the first parameter is next fetched.
|
||||||
next_operand_ = 0;
|
next_operand_ = 0;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
@ -235,16 +219,33 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
case State::StoreOperand:
|
||||||
|
if(instruction_.mode(next_operand_) <= Mode::AddressRegisterDirect) {
|
||||||
|
registers_[instruction_.lreg(next_operand_)] = operand_[next_operand_];
|
||||||
|
state_ = State::Decode;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make a decision on how I'm going to deal with byte/word/longword.
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Various forms of perform.
|
// Various forms of perform.
|
||||||
//
|
//
|
||||||
|
#define MoveToWritePhase() \
|
||||||
|
next_operand_ = operand_flags_ >> 3; \
|
||||||
|
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode)
|
||||||
|
|
||||||
case State::Perform_np:
|
case State::Perform_np:
|
||||||
InstructionSet::M68k::perform<InstructionSet::M68k::Model::M68000>(
|
InstructionSet::M68k::perform<InstructionSet::M68k::Model::M68000>(
|
||||||
instruction_, operand_[0], operand_[1], status_, *static_cast<ProcessorBase *>(this));
|
instruction_, operand_[0], operand_[1], status_, *static_cast<ProcessorBase *>(this));
|
||||||
Prefetch(); // np
|
Prefetch(); // np
|
||||||
|
|
||||||
next_operand_ = 0;
|
MoveToWritePhase();
|
||||||
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::Perform_np_n:
|
case State::Perform_np_n:
|
||||||
@ -253,10 +254,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
Prefetch(); // np
|
Prefetch(); // np
|
||||||
IdleBus(1); // n
|
IdleBus(1); // n
|
||||||
|
|
||||||
next_operand_ = 0;
|
MoveToWritePhase();
|
||||||
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#undef MoveToWritePhase
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("Unhandled state: %d\n", state_);
|
printf("Unhandled state: %d\n", state_);
|
||||||
|
@ -90,37 +90,67 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
|||||||
/// or store.
|
/// or store.
|
||||||
int next_operand_ = 0;
|
int next_operand_ = 0;
|
||||||
|
|
||||||
|
/// 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).
|
||||||
|
uint32_t temporary_address_ = 0;
|
||||||
|
|
||||||
// Flow controller... all TODO.
|
// Flow controller... all TODO.
|
||||||
using Preinstruction = InstructionSet::M68k::Preinstruction;
|
using Preinstruction = InstructionSet::M68k::Preinstruction;
|
||||||
|
|
||||||
template <typename IntT> void did_mulu(IntT) {}
|
template <typename IntT> void did_mulu(IntT) {}
|
||||||
template <typename IntT> void did_muls(IntT) {}
|
template <typename IntT> void did_muls(IntT) {}
|
||||||
void did_chk([[maybe_unused]] bool was_under, [[maybe_unused]] bool was_over) {}
|
void did_chk(bool, bool) {}
|
||||||
void did_shift([[maybe_unused]] int bit_count) {}
|
void did_shift(int) {}
|
||||||
template <bool did_overflow> void did_divu([[maybe_unused]] uint32_t dividend, [[maybe_unused]] uint32_t divisor) {}
|
template <bool did_overflow> void did_divu(uint32_t, uint32_t) {}
|
||||||
template <bool did_overflow> void did_divs([[maybe_unused]] int32_t dividend, [[maybe_unused]] int32_t divisor) {}
|
template <bool did_overflow> void did_divs(int32_t, int32_t) {}
|
||||||
void did_bit_op([[maybe_unused]] int bit_position) {}
|
void did_bit_op(int) {}
|
||||||
inline void did_update_status();
|
inline void did_update_status();
|
||||||
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset) {}
|
template <typename IntT> void complete_bcc(bool, IntT) {}
|
||||||
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset) {}
|
void complete_dbcc(bool, bool, int16_t) {}
|
||||||
void bsr(uint32_t offset) {}
|
void bsr(uint32_t) {}
|
||||||
void jsr(uint32_t address) {}
|
void jsr(uint32_t) {}
|
||||||
void jmp(uint32_t address) {}
|
void jmp(uint32_t) {}
|
||||||
void rtr() {}
|
void rtr() {}
|
||||||
void rte() {}
|
void rte() {}
|
||||||
void rts() {}
|
void rts() {}
|
||||||
void stop() {}
|
void stop() {}
|
||||||
void reset() {}
|
void reset() {}
|
||||||
void link(Preinstruction instruction, uint32_t offset) {}
|
void link(Preinstruction, uint32_t) {}
|
||||||
void unlink(uint32_t &address) {}
|
void unlink(uint32_t &) {}
|
||||||
void pea(uint32_t address) {}
|
void pea(uint32_t) {}
|
||||||
void move_to_usp(uint32_t address) {}
|
void move_to_usp(uint32_t) {}
|
||||||
void move_from_usp(uint32_t &address) {}
|
void move_from_usp(uint32_t &) {}
|
||||||
void tas(Preinstruction instruction, uint32_t address) {}
|
void tas(Preinstruction, uint32_t) {}
|
||||||
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest) {}
|
template <typename IntT> void movep(Preinstruction, uint32_t, uint32_t) {}
|
||||||
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t mask, uint32_t address) {}
|
template <typename IntT> void movem_toM(Preinstruction, uint32_t, uint32_t) {}
|
||||||
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t mask, uint32_t address) {}
|
template <typename IntT> void movem_toR(Preinstruction, uint32_t, uint32_t) {}
|
||||||
template <bool use_current_instruction_pc = true> void raise_exception([[maybe_unused]] int vector) {}
|
template <bool use_current_instruction_pc = true> void raise_exception(int) {}
|
||||||
|
|
||||||
|
// 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 data word.
|
||||||
|
Microcycle read_word_data_announce {
|
||||||
|
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData
|
||||||
|
};
|
||||||
|
Microcycle read_word_data {
|
||||||
|
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read a program word. All accesses via the program counter are word sized.
|
||||||
|
Microcycle read_program_announce {
|
||||||
|
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram
|
||||||
|
};
|
||||||
|
Microcycle read_program {
|
||||||
|
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram
|
||||||
|
};
|
||||||
|
|
||||||
|
// Holding spot when awaiting DTACK/etc.
|
||||||
|
Microcycle awaiting_dtack;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user