mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +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.
|
||||
struct Test68000: public CPU::MC68000Mk2::BusHandler {
|
||||
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;
|
||||
|
||||
Test68000() : processor(*this) {}
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "../../Numeric/RegisterSizes.hpp"
|
||||
#include "../../InstructionSets/M68k/RegisterSet.hpp"
|
||||
|
||||
#include "Implementation/68000Mk2Storage.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MC68000Mk2 {
|
||||
|
||||
@ -361,6 +359,14 @@ struct State {
|
||||
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:
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
// 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:
|
||||
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.
|
||||
|
||||
// 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,
|
||||
// honouring DTACK, BERR and VPA as necessary.
|
||||
#define AccessPair(addr, val, announce, perform) \
|
||||
perform.address = announce.address = &addr; \
|
||||
announce.address = perform.address = &addr; \
|
||||
perform.value = &val; \
|
||||
if constexpr (!dtack_is_implicit) { \
|
||||
announce.length = HalfCycles(4); \
|
||||
@ -135,10 +111,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
CompleteAccess(perform);
|
||||
|
||||
// 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.
|
||||
#define ReadProgramWord(val) \
|
||||
#define ReadProgramWord(val) \
|
||||
AccessPair(program_counter_.l, val, read_program_announce, read_program); \
|
||||
program_counter_.l += 2;
|
||||
|
||||
@ -175,10 +152,17 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
status_.trace_flag = 0;
|
||||
did_update_status();
|
||||
|
||||
address = 0; ReadDataWord(address, registers_[15].high); // nF
|
||||
address += 2; ReadDataWord(address, registers_[15].low); // nf
|
||||
address += 2; ReadDataWord(address, program_counter_.high); // nV
|
||||
address += 2; ReadDataWord(address, program_counter_.low); // nv
|
||||
temporary_address_ = 0;
|
||||
ReadDataWord(temporary_address_, registers_[15].high); // nF
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
// Obtain operand flags and pick a perform pattern.
|
||||
setup_operation();
|
||||
|
||||
// Signal the bus handler if requested.
|
||||
if constexpr (signal_will_perform) {
|
||||
bus_handler_.will_perform(instruction_address_, opcode_);
|
||||
}
|
||||
|
||||
// Obtain operand flags and pick a perform pattern.
|
||||
setup_operation();
|
||||
|
||||
// Ensure the first parameter is next fetched.
|
||||
next_operand_ = 0;
|
||||
[[fallthrough]];
|
||||
@ -235,16 +219,33 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
}
|
||||
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.
|
||||
//
|
||||
#define MoveToWritePhase() \
|
||||
next_operand_ = operand_flags_ >> 3; \
|
||||
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode)
|
||||
|
||||
case State::Perform_np:
|
||||
InstructionSet::M68k::perform<InstructionSet::M68k::Model::M68000>(
|
||||
instruction_, operand_[0], operand_[1], status_, *static_cast<ProcessorBase *>(this));
|
||||
Prefetch(); // np
|
||||
|
||||
next_operand_ = 0;
|
||||
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode);
|
||||
MoveToWritePhase();
|
||||
break;
|
||||
|
||||
case State::Perform_np_n:
|
||||
@ -253,10 +254,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
Prefetch(); // np
|
||||
IdleBus(1); // n
|
||||
|
||||
next_operand_ = 0;
|
||||
MoveToState(operand_flags_ & 0x0c ? State::StoreOperand : State::Decode);
|
||||
MoveToWritePhase();
|
||||
break;
|
||||
|
||||
#undef MoveToWritePhase
|
||||
|
||||
default:
|
||||
printf("Unhandled state: %d\n", state_);
|
||||
|
@ -90,37 +90,67 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
/// or store.
|
||||
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.
|
||||
using Preinstruction = InstructionSet::M68k::Preinstruction;
|
||||
|
||||
template <typename IntT> void did_mulu(IntT) {}
|
||||
template <typename IntT> void did_muls(IntT) {}
|
||||
void did_chk([[maybe_unused]] bool was_under, [[maybe_unused]] bool was_over) {}
|
||||
void did_shift([[maybe_unused]] int bit_count) {}
|
||||
template <bool did_overflow> void did_divu([[maybe_unused]] uint32_t dividend, [[maybe_unused]] uint32_t divisor) {}
|
||||
template <bool did_overflow> void did_divs([[maybe_unused]] int32_t dividend, [[maybe_unused]] int32_t divisor) {}
|
||||
void did_bit_op([[maybe_unused]] int bit_position) {}
|
||||
void did_chk(bool, bool) {}
|
||||
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) {}
|
||||
void did_bit_op(int) {}
|
||||
inline void did_update_status();
|
||||
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset) {}
|
||||
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset) {}
|
||||
void bsr(uint32_t offset) {}
|
||||
void jsr(uint32_t address) {}
|
||||
void jmp(uint32_t address) {}
|
||||
template <typename IntT> void complete_bcc(bool, IntT) {}
|
||||
void complete_dbcc(bool, bool, int16_t) {}
|
||||
void bsr(uint32_t) {}
|
||||
void jsr(uint32_t) {}
|
||||
void jmp(uint32_t) {}
|
||||
void rtr() {}
|
||||
void rte() {}
|
||||
void rts() {}
|
||||
void stop() {}
|
||||
void reset() {}
|
||||
void link(Preinstruction instruction, uint32_t offset) {}
|
||||
void unlink(uint32_t &address) {}
|
||||
void pea(uint32_t address) {}
|
||||
void move_to_usp(uint32_t address) {}
|
||||
void move_from_usp(uint32_t &address) {}
|
||||
void tas(Preinstruction instruction, uint32_t address) {}
|
||||
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest) {}
|
||||
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t mask, uint32_t address) {}
|
||||
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t mask, uint32_t address) {}
|
||||
template <bool use_current_instruction_pc = true> void raise_exception([[maybe_unused]] int vector) {}
|
||||
void link(Preinstruction, uint32_t) {}
|
||||
void unlink(uint32_t &) {}
|
||||
void pea(uint32_t) {}
|
||||
void move_to_usp(uint32_t) {}
|
||||
void move_from_usp(uint32_t &) {}
|
||||
void tas(Preinstruction, uint32_t) {}
|
||||
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) {}
|
||||
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