1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Completes performance of NBCD D0.

This commit is contained in:
Thomas Harte 2022-05-17 16:10:20 -04:00
parent eed2672db5
commit 4a40581deb
4 changed files with 98 additions and 61 deletions

View File

@ -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) {}

View File

@ -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:

View File

@ -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_);

View File

@ -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;
};
}