mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #779 from TomHarte/6502State
Provisionally adds `State` and `get/set_state` to the 6502.
This commit is contained in:
commit
c4b114133a
2
.github/workflows/ccpp.yml
vendored
2
.github/workflows/ccpp.yml
vendored
@ -17,6 +17,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt-get --allow-releaseinfo-change update; sudo apt-get install libsdl2-dev scons
|
run: sudo apt-get --allow-releaseinfo-change update; sudo apt-get --fix-missing install libsdl2-dev scons
|
||||||
- name: Make
|
- name: Make
|
||||||
run: cd OSBindings/SDL; scons
|
run: cd OSBindings/SDL; scons
|
||||||
|
@ -30,7 +30,7 @@ class MultiStruct: public Reflection::Struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> all_keys() final {
|
std::vector<std::string> all_keys() const final {
|
||||||
std::set<std::string> keys;
|
std::set<std::string> keys;
|
||||||
for(auto &options: options_) {
|
for(auto &options: options_) {
|
||||||
const auto new_keys = options->all_keys();
|
const auto new_keys = options->all_keys();
|
||||||
@ -39,7 +39,7 @@ class MultiStruct: public Reflection::Struct {
|
|||||||
return std::vector<std::string>(keys.begin(), keys.end());
|
return std::vector<std::string>(keys.begin(), keys.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> values_for(const std::string &name) final {
|
std::vector<std::string> values_for(const std::string &name) const final {
|
||||||
std::set<std::string> values;
|
std::set<std::string> values;
|
||||||
for(auto &options: options_) {
|
for(auto &options: options_) {
|
||||||
const auto new_values = options->values_for(name);
|
const auto new_values = options->values_for(name);
|
||||||
@ -48,7 +48,7 @@ class MultiStruct: public Reflection::Struct {
|
|||||||
return std::vector<std::string>(values.begin(), values.end());
|
return std::vector<std::string>(values.begin(), values.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::type_info *type_of(const std::string &name) final {
|
const std::type_info *type_of(const std::string &name) const final {
|
||||||
for(auto &options: options_) {
|
for(auto &options: options_) {
|
||||||
auto info = options->type_of(name);
|
auto info = options->type_of(name);
|
||||||
if(info) return info;
|
if(info) return info;
|
||||||
@ -56,7 +56,7 @@ class MultiStruct: public Reflection::Struct {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *get(const std::string &name) final {
|
const void *get(const std::string &name) const final {
|
||||||
for(auto &options: options_) {
|
for(auto &options: options_) {
|
||||||
auto value = options->get(name);
|
auto value = options->get(name);
|
||||||
if(value) return value;
|
if(value) return value;
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
#include "../RegisterSizes.hpp"
|
#include "../RegisterSizes.hpp"
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "../../Reflection/Enum.hpp"
|
||||||
|
#include "../../Reflection/Struct.hpp"
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
namespace MOS6502 {
|
namespace MOS6502 {
|
||||||
@ -29,8 +31,7 @@ enum Register {
|
|||||||
Flags,
|
Flags,
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y,
|
Y
|
||||||
S
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -128,6 +129,81 @@ class ProcessorBase: public ProcessorStorage {
|
|||||||
public:
|
public:
|
||||||
ProcessorBase(Personality personality) : ProcessorStorage(personality) {}
|
ProcessorBase(Personality personality) : ProcessorStorage(personality) {}
|
||||||
|
|
||||||
|
struct State: public Reflection::StructImpl<State> {
|
||||||
|
/*!
|
||||||
|
Provides the current state of the well-known, published internal registers.
|
||||||
|
*/
|
||||||
|
struct Registers: public Reflection::StructImpl<Registers> {
|
||||||
|
uint16_t program_counter;
|
||||||
|
uint8_t stack_pointer;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t a, x, y;
|
||||||
|
|
||||||
|
Registers();
|
||||||
|
} registers;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides the current state of the processor's various input lines that aren't
|
||||||
|
related to an access cycle.
|
||||||
|
*/
|
||||||
|
struct Inputs: public Reflection::StructImpl<Inputs> {
|
||||||
|
bool ready;
|
||||||
|
bool irq;
|
||||||
|
bool nmi;
|
||||||
|
bool reset;
|
||||||
|
|
||||||
|
Inputs();
|
||||||
|
} inputs;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Contains internal state used by this particular implementation of a 6502. Most of it
|
||||||
|
does not necessarily correlate with anything in a real 6502, and some of it very
|
||||||
|
obviously doesn't.
|
||||||
|
*/
|
||||||
|
struct ExecutionState: public Reflection::StructImpl<ExecutionState> {
|
||||||
|
ReflectableEnum(Phase,
|
||||||
|
Instruction, Stopped, Waiting, Jammed, Ready
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Current executon phase, e.g. standard instruction flow or responding to an IRQ.
|
||||||
|
Phase phase;
|
||||||
|
int micro_program;
|
||||||
|
int micro_program_offset;
|
||||||
|
|
||||||
|
// The following are very internal things. At the minute I
|
||||||
|
// consider these 'reliable' for inter-launch state
|
||||||
|
// preservation only on the grounds that this implementation
|
||||||
|
// of a 6502 is now empirically stable.
|
||||||
|
//
|
||||||
|
// If cycles_into_phase is 0, the values below need not be
|
||||||
|
// retained, they're entirely ephemeral. If providing a state
|
||||||
|
// for persistance, machines that can should advance until
|
||||||
|
// cycles_into_phase is 0.
|
||||||
|
uint8_t operation, operand;
|
||||||
|
uint16_t address, next_address;
|
||||||
|
|
||||||
|
ExecutionState();
|
||||||
|
} execution_state;
|
||||||
|
|
||||||
|
State() {
|
||||||
|
if(needs_declare()) {
|
||||||
|
DeclareField(registers);
|
||||||
|
DeclareField(execution_state);
|
||||||
|
DeclareField(inputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets current processor state.
|
||||||
|
*/
|
||||||
|
State get_state();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets current processor state.
|
||||||
|
*/
|
||||||
|
void set_state(const State &);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Gets the value of a register.
|
Gets the value of a register.
|
||||||
|
|
||||||
@ -198,6 +274,40 @@ class ProcessorBase: public ProcessorStorage {
|
|||||||
bool is_jammed();
|
bool is_jammed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Boilerplate follows here, to establish 'reflection' for the state struct defined above.
|
||||||
|
inline ProcessorBase::State::Registers::Registers() {
|
||||||
|
if(needs_declare()) {
|
||||||
|
DeclareField(program_counter);
|
||||||
|
DeclareField(stack_pointer);
|
||||||
|
DeclareField(flags);
|
||||||
|
DeclareField(a);
|
||||||
|
DeclareField(x);
|
||||||
|
DeclareField(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ProcessorBase::State::ExecutionState::ExecutionState() {
|
||||||
|
if(needs_declare()) {
|
||||||
|
AnnounceEnum(Phase);
|
||||||
|
DeclareField(phase);
|
||||||
|
DeclareField(micro_program);
|
||||||
|
DeclareField(micro_program_offset);
|
||||||
|
DeclareField(operation);
|
||||||
|
DeclareField(operand);
|
||||||
|
DeclareField(address);
|
||||||
|
DeclareField(next_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ProcessorBase::State::Inputs::Inputs() {
|
||||||
|
if(needs_declare()) {
|
||||||
|
DeclareField(ready);
|
||||||
|
DeclareField(irq);
|
||||||
|
DeclareField(nmi);
|
||||||
|
DeclareField(reset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@abstact Template providing emulation of a 6502 processor.
|
@abstact Template providing emulation of a 6502 processor.
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "../6502.hpp"
|
#include "../6502.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace CPU::MOS6502;
|
using namespace CPU::MOS6502;
|
||||||
|
|
||||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
||||||
@ -21,7 +23,6 @@ uint16_t ProcessorBase::get_value_of_register(Register r) {
|
|||||||
case Register::A: return a_;
|
case Register::A: return a_;
|
||||||
case Register::X: return x_;
|
case Register::X: return x_;
|
||||||
case Register::Y: return y_;
|
case Register::Y: return y_;
|
||||||
case Register::S: return s_;
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +35,6 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
|
|||||||
case Register::A: a_ = static_cast<uint8_t>(value); break;
|
case Register::A: a_ = static_cast<uint8_t>(value); break;
|
||||||
case Register::X: x_ = static_cast<uint8_t>(value); break;
|
case Register::X: x_ = static_cast<uint8_t>(value); break;
|
||||||
case Register::Y: y_ = static_cast<uint8_t>(value); break;
|
case Register::Y: y_ = static_cast<uint8_t>(value); break;
|
||||||
case Register::S: s_ = static_cast<uint8_t>(value); break;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,3 +42,79 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
|
|||||||
bool ProcessorBase::is_jammed() {
|
bool ProcessorBase::is_jammed() {
|
||||||
return is_jammed_;
|
return is_jammed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcessorBase::State ProcessorBase::get_state() {
|
||||||
|
ProcessorBase::State state;
|
||||||
|
|
||||||
|
// Fill in registers.
|
||||||
|
state.registers.program_counter = pc_.full;
|
||||||
|
state.registers.stack_pointer = s_;
|
||||||
|
state.registers.flags = get_flags();
|
||||||
|
state.registers.a = a_;
|
||||||
|
state.registers.x = x_;
|
||||||
|
state.registers.y = y_;
|
||||||
|
|
||||||
|
// Fill in other inputs.
|
||||||
|
state.inputs.ready = ready_line_is_enabled_;
|
||||||
|
state.inputs.irq = irq_line_;
|
||||||
|
state.inputs.nmi = nmi_line_is_enabled_;
|
||||||
|
state.inputs.reset = interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn);
|
||||||
|
|
||||||
|
// Fill in execution state.
|
||||||
|
state.execution_state.operation = operation_;
|
||||||
|
state.execution_state.operand = operand_;
|
||||||
|
state.execution_state.address = address_.full;
|
||||||
|
state.execution_state.next_address = next_address_.full;
|
||||||
|
if(ready_is_active_) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Ready;
|
||||||
|
} else if(is_jammed_) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Jammed;
|
||||||
|
} else if(wait_is_active_) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Waiting;
|
||||||
|
} else if(stop_is_active_) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Stopped;
|
||||||
|
} else {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto micro_offset = size_t(scheduled_program_counter_ - &operations_[0][0]);
|
||||||
|
const auto list_length = sizeof(InstructionList) / sizeof(MicroOp);
|
||||||
|
|
||||||
|
state.execution_state.micro_program = int(micro_offset / list_length);
|
||||||
|
state.execution_state.micro_program_offset = int(micro_offset % list_length);
|
||||||
|
assert(&operations_[state.execution_state.micro_program][state.execution_state.micro_program_offset] == scheduled_program_counter_);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessorBase::set_state(const State &state) {
|
||||||
|
// Grab registers.
|
||||||
|
pc_.full = state.registers.program_counter;
|
||||||
|
s_ = state.registers.stack_pointer;
|
||||||
|
set_flags(state.registers.flags);
|
||||||
|
a_ = state.registers.a;
|
||||||
|
x_ = state.registers.x;
|
||||||
|
y_ = state.registers.y;
|
||||||
|
|
||||||
|
// Grab other inputs.
|
||||||
|
ready_line_is_enabled_ = state.inputs.ready;
|
||||||
|
set_irq_line(state.inputs.irq);
|
||||||
|
set_nmi_line(state.inputs.nmi);
|
||||||
|
set_reset_line(state.inputs.reset);
|
||||||
|
|
||||||
|
// Set execution state.
|
||||||
|
ready_is_active_ = is_jammed_ = wait_is_active_ = stop_is_active_ = false;
|
||||||
|
switch(state.execution_state.phase) {
|
||||||
|
case State::ExecutionState::Phase::Ready: ready_is_active_ = true; break;
|
||||||
|
case State::ExecutionState::Phase::Jammed: is_jammed_ = true; break;
|
||||||
|
case State::ExecutionState::Phase::Stopped: stop_is_active_ = true; break;
|
||||||
|
case State::ExecutionState::Phase::Waiting: wait_is_active_ = true; break;
|
||||||
|
case State::ExecutionState::Phase::Instruction: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
operation_ = state.execution_state.operation;
|
||||||
|
operand_ = state.execution_state.operand;
|
||||||
|
address_.full = state.execution_state.address;
|
||||||
|
next_address_.full = state.execution_state.next_address;
|
||||||
|
scheduled_program_counter_ = &operations_[state.execution_state.micro_program][state.execution_state.micro_program_offset];
|
||||||
|
}
|
||||||
|
@ -13,17 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||||
static const MicroOp do_branch[] = {
|
|
||||||
CycleReadFromPC,
|
|
||||||
CycleAddSignedOperandToPC,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
static uint8_t throwaway_target;
|
static uint8_t throwaway_target;
|
||||||
static const MicroOp fetch_decode_execute[] = {
|
|
||||||
CycleFetchOperation,
|
|
||||||
CycleFetchOperand,
|
|
||||||
OperationDecodeOperation
|
|
||||||
};
|
|
||||||
|
|
||||||
// These plus program below act to give the compiler permission to update these values
|
// These plus program below act to give the compiler permission to update these values
|
||||||
// without touching the class storage (i.e. it explicitly says they need be completely up
|
// without touching the class storage (i.e. it explicitly says they need be completely up
|
||||||
@ -33,28 +23,29 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
uint16_t busAddress = bus_address_;
|
uint16_t busAddress = bus_address_;
|
||||||
uint8_t *busValue = bus_value_;
|
uint8_t *busValue = bus_value_;
|
||||||
|
|
||||||
#define checkSchedule(op) \
|
#define checkSchedule() \
|
||||||
if(!scheduled_program_counter_) {\
|
if(!scheduled_program_counter_) {\
|
||||||
if(interrupt_requests_) {\
|
if(interrupt_requests_) {\
|
||||||
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
|
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
|
||||||
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
|
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
|
||||||
scheduled_program_counter_ = get_reset_program();\
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::Reset)];\
|
||||||
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
|
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
|
||||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
|
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
|
||||||
scheduled_program_counter_ = get_nmi_program();\
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::NMI)];\
|
||||||
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
||||||
scheduled_program_counter_ = get_irq_program();\
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::IRQ)];\
|
||||||
} \
|
} \
|
||||||
} else {\
|
} else {\
|
||||||
scheduled_program_counter_ = fetch_decode_execute;\
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\
|
||||||
}\
|
}\
|
||||||
op;\
|
cycles_in_phase_ = 0; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define bus_access() \
|
#define bus_access() \
|
||||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
||||||
|
++cycles_in_phase_; \
|
||||||
nextBusOperation = BusOperation::None; \
|
nextBusOperation = BusOperation::None; \
|
||||||
if(number_of_cycles <= Cycles(0)) break;
|
if(number_of_cycles <= Cycles(0)) break;
|
||||||
|
|
||||||
@ -66,11 +57,13 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
|
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
|
||||||
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||||
|
++cycles_in_phase_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deal with a potential STP state, if this 6502 implements STP.
|
// Deal with a potential STP state, if this 6502 implements STP.
|
||||||
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||||
|
++cycles_in_phase_;
|
||||||
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
||||||
stop_is_active_ = false;
|
stop_is_active_ = false;
|
||||||
checkSchedule();
|
checkSchedule();
|
||||||
@ -81,6 +74,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
// Deal with a potential WAI state, if this 6502 implements WAI.
|
// Deal with a potential WAI state, if this 6502 implements WAI.
|
||||||
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||||
|
++cycles_in_phase_;
|
||||||
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
||||||
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
||||||
wait_is_active_ = false;
|
wait_is_active_ = false;
|
||||||
@ -566,7 +560,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
#define BRA(condition) \
|
#define BRA(condition) \
|
||||||
pc_.full++; \
|
pc_.full++; \
|
||||||
if(condition) { \
|
if(condition) { \
|
||||||
scheduled_program_counter_ = do_branch; \
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||||
@ -593,7 +587,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
||||||
// to target its destination skips any pending interrupts.
|
// to target its destination skips any pending interrupts.
|
||||||
// Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634
|
// Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634
|
||||||
scheduled_program_counter_ = fetch_decode_execute;
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -611,22 +605,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
// and (iii) read from the corresponding zero page.
|
// and (iii) read from the corresponding zero page.
|
||||||
const uint8_t mask = uint8_t(1 << ((operation_ >> 4)&7));
|
const uint8_t mask = uint8_t(1 << ((operation_ >> 4)&7));
|
||||||
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
|
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
|
||||||
static const MicroOp do_branch[] = {
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBBRBBS)];
|
||||||
CycleFetchOperand, // Fetch offset.
|
|
||||||
OperationIncrementPC,
|
|
||||||
CycleFetchFromHalfUpdatedPC,
|
|
||||||
OperationAddSignedOperandToPC16,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
scheduled_program_counter_ = do_branch;
|
|
||||||
} else {
|
} else {
|
||||||
static const MicroOp do_not_branch[] = {
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoNotBBRBBS)];
|
||||||
CycleFetchOperand,
|
|
||||||
OperationIncrementPC,
|
|
||||||
CycleFetchFromHalfUpdatedPC,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
scheduled_program_counter_ = do_not_branch;
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -730,56 +711,6 @@ void ProcessorBase::set_nmi_line(bool active) {
|
|||||||
nmi_line_is_enabled_ = active;
|
nmi_line_is_enabled_ = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
|
||||||
static const MicroOp reset[] = {
|
|
||||||
CycleFetchOperand,
|
|
||||||
CycleFetchOperand,
|
|
||||||
CycleNoWritePush,
|
|
||||||
CycleNoWritePush,
|
|
||||||
OperationRSTPickVector,
|
|
||||||
CycleNoWritePush,
|
|
||||||
OperationSetNMIRSTFlags,
|
|
||||||
CycleReadVectorLow,
|
|
||||||
CycleReadVectorHigh,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
return reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
|
||||||
static const MicroOp reset[] = {
|
|
||||||
CycleFetchOperand,
|
|
||||||
CycleFetchOperand,
|
|
||||||
CyclePushPCH,
|
|
||||||
CyclePushPCL,
|
|
||||||
OperationBRKPickVector,
|
|
||||||
OperationSetOperandFromFlags,
|
|
||||||
CyclePushOperand,
|
|
||||||
OperationSetIRQFlags,
|
|
||||||
CycleReadVectorLow,
|
|
||||||
CycleReadVectorHigh,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
return reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
|
||||||
static const MicroOp reset[] = {
|
|
||||||
CycleFetchOperand,
|
|
||||||
CycleFetchOperand,
|
|
||||||
CyclePushPCH,
|
|
||||||
CyclePushPCL,
|
|
||||||
OperationNMIPickVector,
|
|
||||||
OperationSetOperandFromFlags,
|
|
||||||
CyclePushOperand,
|
|
||||||
OperationSetNMIRSTFlags,
|
|
||||||
CycleReadVectorLow,
|
|
||||||
CycleReadVectorHigh,
|
|
||||||
OperationMoveToNextProgram
|
|
||||||
};
|
|
||||||
return reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ProcessorStorage::get_flags() {
|
uint8_t ProcessorStorage::get_flags() {
|
||||||
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
|
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
decimal_flag_ &= Flag::Decimal;
|
decimal_flag_ &= Flag::Decimal;
|
||||||
overflow_flag_ &= Flag::Overflow;
|
overflow_flag_ &= Flag::Overflow;
|
||||||
|
|
||||||
const InstructionList operations_6502[256] = {
|
const InstructionList operations_6502[] = {
|
||||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||||
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
||||||
@ -218,8 +218,79 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
/* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS),
|
/* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS),
|
||||||
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
|
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
|
||||||
/* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS),
|
/* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS),
|
||||||
|
|
||||||
|
/* 0x100: Fetch, decode, execute. */
|
||||||
|
{
|
||||||
|
CycleFetchOperation,
|
||||||
|
CycleFetchOperand,
|
||||||
|
OperationDecodeOperation
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 0x101: Reset. */
|
||||||
|
Program(
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleNoWritePush,
|
||||||
|
CycleNoWritePush,
|
||||||
|
OperationRSTPickVector,
|
||||||
|
CycleNoWritePush,
|
||||||
|
OperationSetNMIRSTFlags,
|
||||||
|
CycleReadVectorLow,
|
||||||
|
CycleReadVectorHigh
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 0x102: IRQ. */
|
||||||
|
Program(
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleFetchOperand,
|
||||||
|
CyclePushPCH,
|
||||||
|
CyclePushPCL,
|
||||||
|
OperationBRKPickVector,
|
||||||
|
OperationSetOperandFromFlags,
|
||||||
|
CyclePushOperand,
|
||||||
|
OperationSetIRQFlags,
|
||||||
|
CycleReadVectorLow,
|
||||||
|
CycleReadVectorHigh
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 0x103: NMI. */
|
||||||
|
Program(
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleFetchOperand,
|
||||||
|
CyclePushPCH,
|
||||||
|
CyclePushPCL,
|
||||||
|
OperationNMIPickVector,
|
||||||
|
OperationSetOperandFromFlags,
|
||||||
|
CyclePushOperand,
|
||||||
|
OperationSetNMIRSTFlags,
|
||||||
|
CycleReadVectorLow,
|
||||||
|
CycleReadVectorHigh
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 0x104: Do BRA. */
|
||||||
|
Program(
|
||||||
|
CycleReadFromPC,
|
||||||
|
CycleAddSignedOperandToPC
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 0x105: Do BBR or BBS. */
|
||||||
|
Program(
|
||||||
|
CycleFetchOperand, // Fetch offset.
|
||||||
|
OperationIncrementPC,
|
||||||
|
CycleFetchFromHalfUpdatedPC,
|
||||||
|
OperationAddSignedOperandToPC16
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 0x106: Complete BBR or BBS without branching. */
|
||||||
|
Program(
|
||||||
|
CycleFetchOperand,
|
||||||
|
OperationIncrementPC,
|
||||||
|
CycleFetchFromHalfUpdatedPC
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(operations_6502) == sizeof(operations_));
|
||||||
|
|
||||||
// Install the basic 6502 table.
|
// Install the basic 6502 table.
|
||||||
memcpy(operations_, operations_6502, sizeof(operations_));
|
memcpy(operations_, operations_6502, sizeof(operations_));
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal.
|
This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal.
|
||||||
*/
|
*/
|
||||||
enum MicroOp {
|
enum MicroOp: uint8_t {
|
||||||
CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it
|
CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it
|
||||||
CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op
|
CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op
|
||||||
OperationDecodeOperation, // schedules the microprogram associated with operation_
|
OperationDecodeOperation, // schedules the microprogram associated with operation_
|
||||||
@ -197,10 +197,35 @@ class ProcessorStorage {
|
|||||||
OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received)
|
OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received)
|
||||||
};
|
};
|
||||||
|
|
||||||
using InstructionList = MicroOp[10];
|
using InstructionList = MicroOp[12];
|
||||||
InstructionList operations_[256];
|
/// Defines the locations in operations_ of various named microprograms; the first 256 entries
|
||||||
|
/// in operations_ are mapped directly from instruction codes and therefore not named.
|
||||||
|
enum class OperationsSlot {
|
||||||
|
/// Fetches the next operation, and its operand, then schedules the corresponding set of operations_.
|
||||||
|
/// [Caveat: the 65C02 adds single-cycle NOPs; this microprogram won't fetch an operand for those].
|
||||||
|
FetchDecodeExecute = 256,
|
||||||
|
|
||||||
|
/// Performs the 6502's reset sequence.
|
||||||
|
Reset,
|
||||||
|
/// Performs the 6502's IRQ sequence.
|
||||||
|
IRQ,
|
||||||
|
/// Performs the 6502's NMI sequence.
|
||||||
|
NMI,
|
||||||
|
|
||||||
|
/// Performs a branch, e.g. the entry for BCC will evaluate whether carry is clear and, if so, will jump
|
||||||
|
/// to this instruction list.
|
||||||
|
DoBRA,
|
||||||
|
|
||||||
|
/// On a 65c02,
|
||||||
|
DoBBRBBS,
|
||||||
|
DoNotBBRBBS,
|
||||||
|
|
||||||
|
Max
|
||||||
|
};
|
||||||
|
InstructionList operations_[size_t(OperationsSlot::Max)];
|
||||||
|
|
||||||
const MicroOp *scheduled_program_counter_ = nullptr;
|
const MicroOp *scheduled_program_counter_ = nullptr;
|
||||||
|
int cycles_in_phase_ = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Storage for the 6502 registers; F is stored as individual flags.
|
Storage for the 6502 registers; F is stored as individual flags.
|
||||||
@ -260,27 +285,6 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
||||||
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
||||||
|
|
||||||
/*!
|
|
||||||
Gets the program representing an RST response.
|
|
||||||
|
|
||||||
@returns The program representing an RST response.
|
|
||||||
*/
|
|
||||||
inline const MicroOp *get_reset_program();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Gets the program representing an IRQ response.
|
|
||||||
|
|
||||||
@returns The program representing an IRQ response.
|
|
||||||
*/
|
|
||||||
inline const MicroOp *get_irq_program();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Gets the program representing an NMI response.
|
|
||||||
|
|
||||||
@returns The program representing an NMI response.
|
|
||||||
*/
|
|
||||||
inline const MicroOp *get_nmi_program();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _502Storage_h */
|
#endif /* _502Storage_h */
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include "Struct.hpp"
|
#include "Struct.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
// MARK: - Setters
|
// MARK: - Setters
|
||||||
|
|
||||||
@ -107,18 +109,84 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s
|
|||||||
|
|
||||||
// MARK: - Getters
|
// MARK: - Getters
|
||||||
|
|
||||||
template <typename Type> bool Reflection::get(Struct &target, const std::string &name, Type &value) {
|
template <typename Type> bool Reflection::get(const Struct &target, const std::string &name, Type &value) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> bool Reflection::get(Struct &target, const std::string &name, bool &value) {
|
|
||||||
const auto target_type = target.type_of(name);
|
const auto target_type = target.type_of(name);
|
||||||
if(!target_type) return false;
|
if(!target_type) return false;
|
||||||
|
|
||||||
if(*target_type == typeid(bool)) {
|
if(*target_type == typeid(Type)) {
|
||||||
value = *reinterpret_cast<const bool *>(target.get(name));
|
memcpy(&value, target.get(name), sizeof(Type));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Type> Type Reflection::get(const Struct &target, const std::string &name) {
|
||||||
|
Type value;
|
||||||
|
get(target, name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Description
|
||||||
|
|
||||||
|
std::string Reflection::Struct::description() const {
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << "{";
|
||||||
|
|
||||||
|
bool is_first = true;
|
||||||
|
for(const auto &key: all_keys()) {
|
||||||
|
if(!is_first) stream << ", ";
|
||||||
|
is_first = false;
|
||||||
|
stream << key << ": ";
|
||||||
|
|
||||||
|
const auto type = type_of(key);
|
||||||
|
|
||||||
|
// Output Bools as yes/no.
|
||||||
|
if(*type == typeid(bool)) {
|
||||||
|
bool value;
|
||||||
|
::Reflection::get(*this, key, value);
|
||||||
|
stream << (value ? "true" : "false");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output Ints of all sizes as hex.
|
||||||
|
#define OutputIntC(int_type, cast_type) if(*type == typeid(int_type)) { stream << std::setfill('0') << std::setw(sizeof(int_type)*2) << std::hex << cast_type(::Reflection::get<int_type>(*this, key)); continue; }
|
||||||
|
#define OutputInt(int_type) OutputIntC(int_type, int_type)
|
||||||
|
OutputIntC(int8_t, int16_t);
|
||||||
|
OutputIntC(uint8_t, uint16_t);
|
||||||
|
OutputInt(int16_t);
|
||||||
|
OutputInt(uint16_t);
|
||||||
|
OutputInt(int32_t);
|
||||||
|
OutputInt(uint32_t);
|
||||||
|
OutputInt(int64_t);
|
||||||
|
OutputInt(uint64_t);
|
||||||
|
#undef OutputInt
|
||||||
|
|
||||||
|
// Output floats and strings natively.
|
||||||
|
#define OutputNative(val_type) if(*type == typeid(val_type)) { stream << ::Reflection::get<val_type>(*this, key); continue; }
|
||||||
|
OutputNative(float);
|
||||||
|
OutputNative(double);
|
||||||
|
OutputNative(char *);
|
||||||
|
OutputNative(std::string);
|
||||||
|
#undef OutputNAtive
|
||||||
|
|
||||||
|
// Output the current value of any enums.
|
||||||
|
if(!Enum::name(*type).empty()) {
|
||||||
|
const int value = ::Reflection::get<int>(*this, key);
|
||||||
|
stream << Enum::to_string(*type, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse to deal with embedded objects.
|
||||||
|
if(*type == typeid(Reflection::Struct)) {
|
||||||
|
const Reflection::Struct *const child = reinterpret_cast<const Reflection::Struct *>(get(key));
|
||||||
|
stream << child->description();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << "}";
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifndef Struct_hpp
|
#ifndef Struct_hpp
|
||||||
#define Struct_hpp
|
#define Struct_hpp
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -24,12 +25,18 @@ namespace Reflection {
|
|||||||
#define DeclareField(Name) declare(&Name, #Name)
|
#define DeclareField(Name) declare(&Name, #Name)
|
||||||
|
|
||||||
struct Struct {
|
struct Struct {
|
||||||
virtual std::vector<std::string> all_keys() = 0;
|
virtual std::vector<std::string> all_keys() const = 0;
|
||||||
virtual const std::type_info *type_of(const std::string &name) = 0;
|
virtual const std::type_info *type_of(const std::string &name) const = 0;
|
||||||
virtual void set(const std::string &name, const void *value) = 0;
|
virtual void set(const std::string &name, const void *value) = 0;
|
||||||
virtual const void *get(const std::string &name) = 0;
|
virtual const void *get(const std::string &name) const = 0;
|
||||||
virtual std::vector<std::string> values_for(const std::string &name) = 0;
|
virtual std::vector<std::string> values_for(const std::string &name) const = 0;
|
||||||
virtual ~Struct() {}
|
virtual ~Struct() {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns A string describing this struct. This string has no guaranteed layout, may not be
|
||||||
|
sufficiently formed for a formal language parser, etc.
|
||||||
|
*/
|
||||||
|
std::string description() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -86,9 +93,14 @@ bool fuzzy_set(Struct &target, const std::string &name, const std::string &value
|
|||||||
|
|
||||||
@returns @c true if the property was successfully read; @c false otherwise.
|
@returns @c true if the property was successfully read; @c false otherwise.
|
||||||
*/
|
*/
|
||||||
template <typename Type> bool get(Struct &target, const std::string &name, Type &value);
|
template <typename Type> bool get(const Struct &target, const std::string &name, Type &value);
|
||||||
|
|
||||||
template <> bool get(Struct &target, const std::string &name, bool &value);
|
/*!
|
||||||
|
Attempts to get the property @c name to @c value ; will perform limited type conversions.
|
||||||
|
|
||||||
|
@returns @c true if the property was successfully read; a default-constructed instance of Type otherwise.
|
||||||
|
*/
|
||||||
|
template <typename Type> Type get(const Struct &target, const std::string &name);
|
||||||
|
|
||||||
|
|
||||||
// TODO: move this elsewhere. It's just a sketch anyway.
|
// TODO: move this elsewhere. It's just a sketch anyway.
|
||||||
@ -106,10 +118,10 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
@returns the value of type @c Type that is loaded from the offset registered for the field @c name.
|
@returns the value of type @c Type that is loaded from the offset registered for the field @c name.
|
||||||
It is the caller's responsibility to provide an appropriate type of data.
|
It is the caller's responsibility to provide an appropriate type of data.
|
||||||
*/
|
*/
|
||||||
const void *get(const std::string &name) final {
|
const void *get(const std::string &name) const final {
|
||||||
const auto iterator = contents_.find(name);
|
const auto iterator = contents_.find(name);
|
||||||
if(iterator == contents_.end()) return nullptr;
|
if(iterator == contents_.end()) return nullptr;
|
||||||
return reinterpret_cast<uint8_t *>(this) + iterator->second.offset;
|
return reinterpret_cast<const uint8_t *>(this) + iterator->second.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -126,7 +138,7 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
/*!
|
/*!
|
||||||
@returns @c type_info for the field @c name.
|
@returns @c type_info for the field @c name.
|
||||||
*/
|
*/
|
||||||
const std::type_info *type_of(const std::string &name) final {
|
const std::type_info *type_of(const std::string &name) const final {
|
||||||
const auto iterator = contents_.find(name);
|
const auto iterator = contents_.find(name);
|
||||||
if(iterator == contents_.end()) return nullptr;
|
if(iterator == contents_.end()) return nullptr;
|
||||||
return iterator->second.type;
|
return iterator->second.type;
|
||||||
@ -136,7 +148,7 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
@returns a list of the valid enum value names for field @c name if it is a declared enum field of this struct;
|
@returns a list of the valid enum value names for field @c name if it is a declared enum field of this struct;
|
||||||
the empty list otherwise.
|
the empty list otherwise.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> values_for(const std::string &name) final {
|
std::vector<std::string> values_for(const std::string &name) const final {
|
||||||
std::vector<std::string> result;
|
std::vector<std::string> result;
|
||||||
|
|
||||||
// Return an empty vector if this field isn't declared.
|
// Return an empty vector if this field isn't declared.
|
||||||
@ -168,7 +180,7 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
/*!
|
/*!
|
||||||
@returns A vector of all declared fields for this struct.
|
@returns A vector of all declared fields for this struct.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> all_keys() final {
|
std::vector<std::string> all_keys() const final {
|
||||||
std::vector<std::string> keys;
|
std::vector<std::string> keys;
|
||||||
for(const auto &pair: contents_) {
|
for(const auto &pair: contents_) {
|
||||||
keys.push_back(pair.first);
|
keys.push_back(pair.first);
|
||||||
@ -190,14 +202,14 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Exposes the field pointed to by @c t for reflection as @c name.
|
Exposes the field pointed to by @c t for reflection as @c name. If @c t is itself a Reflection::Struct,
|
||||||
|
it'll be the struct that's exposed.
|
||||||
*/
|
*/
|
||||||
template <typename Type> void declare(Type *t, const std::string &name) {
|
template <typename Type> void declare(Type *t, const std::string &name) {
|
||||||
contents_.emplace(
|
if constexpr (std::is_class<Type>()) {
|
||||||
std::make_pair(
|
if(declare_reflectable(t, name)) return;
|
||||||
name,
|
}
|
||||||
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type))
|
declare_emplace(t, name);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -233,7 +245,7 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
@returns @c true if this subclass of @c Struct has not yet declared any fields.
|
@returns @c true if this subclass of @c Struct has not yet declared any fields.
|
||||||
*/
|
*/
|
||||||
bool needs_declare() {
|
bool needs_declare() {
|
||||||
return !contents_.size();
|
return contents_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -256,6 +268,24 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <typename Type> bool declare_reflectable(Type *t, const std::string &name) {
|
||||||
|
Reflection::Struct *const str = static_cast<Reflection::Struct *>(t);
|
||||||
|
if(str) {
|
||||||
|
declare_emplace(str, name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type> void declare_emplace(Type *t, const std::string &name) {
|
||||||
|
contents_.emplace(
|
||||||
|
std::make_pair(
|
||||||
|
name,
|
||||||
|
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
struct Field {
|
struct Field {
|
||||||
const std::type_info *type;
|
const std::type_info *type;
|
||||||
ssize_t offset;
|
ssize_t offset;
|
||||||
|
@ -45,7 +45,7 @@ void Drive::set_rotation_speed(float revolutions_per_minute) {
|
|||||||
// From there derive the appropriate rotational multiplier and possibly update the
|
// From there derive the appropriate rotational multiplier and possibly update the
|
||||||
// count of cycles since the index hole proportionally.
|
// count of cycles since the index hole proportionally.
|
||||||
const float new_rotational_multiplier = float(cycles_per_revolution_) / float(get_input_clock_rate());
|
const float new_rotational_multiplier = float(cycles_per_revolution_) / float(get_input_clock_rate());
|
||||||
cycles_since_index_hole_ *= new_rotational_multiplier / rotational_multiplier_;
|
cycles_since_index_hole_ = Cycles::IntType(float(cycles_since_index_hole_) * new_rotational_multiplier / rotational_multiplier_);
|
||||||
rotational_multiplier_ = new_rotational_multiplier;
|
rotational_multiplier_ = new_rotational_multiplier;
|
||||||
cycles_since_index_hole_ %= cycles_per_revolution_;
|
cycles_since_index_hole_ %= cycles_per_revolution_;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user