mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Provisionally adds State
and get_state
to the 6502.
`set_state` may be a little more complicated, requiring a way to advance in single-cycle steps **without applying bus accesses**.
This commit is contained in:
parent
272383cac7
commit
4fbe983527
@ -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 {
|
||||||
@ -128,6 +130,82 @@ 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<Registers> {
|
||||||
|
ReflectableEnum(Phase,
|
||||||
|
Reset, IRQ, NMI, Instruction, Stopped, Waiting, Jammed
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Current executon phase, e.g. standard instruction flow or responding to an IRQ.
|
||||||
|
Phase phase;
|
||||||
|
/// A count of the number of cycles since this instance of this phase last began.
|
||||||
|
/// E.g. if the phase is currently execution an instruction, this might be 0 to 7.
|
||||||
|
int cycles_into_phase;
|
||||||
|
|
||||||
|
// 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 +276,39 @@ 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(cycles_into_phase);
|
||||||
|
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.
|
||||||
|
|
||||||
|
@ -42,3 +42,59 @@ 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.cycles_into_phase = cycles_in_phase_;
|
||||||
|
state.execution_state.address = address_.full;
|
||||||
|
state.execution_state.next_address = next_address_.full;
|
||||||
|
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 {
|
||||||
|
// Test for the micro-op pointer being inside the reset, IRQ or NMI programs.
|
||||||
|
// If not then the only thing left is instruction.
|
||||||
|
auto is_in_program = [this](const MicroOp *const op) -> bool {
|
||||||
|
if(scheduled_program_counter_ < op) return false;
|
||||||
|
|
||||||
|
const MicroOp *final_op = op;
|
||||||
|
while(*final_op != OperationMoveToNextProgram) {
|
||||||
|
++final_op;
|
||||||
|
}
|
||||||
|
return scheduled_program_counter_ < final_op;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(is_in_program(get_reset_program())) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Reset;
|
||||||
|
} else if(is_in_program(get_irq_program())) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::IRQ;
|
||||||
|
} else if(is_in_program(get_nmi_program())) {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::NMI;
|
||||||
|
} else {
|
||||||
|
state.execution_state.phase = State::ExecutionState::Phase::Instruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
@ -33,28 +33,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_ = get_reset_program();\
|
||||||
} 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_ = get_nmi_program();\
|
||||||
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
||||||
scheduled_program_counter_ = get_irq_program();\
|
scheduled_program_counter_ = get_irq_program();\
|
||||||
} \
|
} \
|
||||||
} else {\
|
} else {\
|
||||||
scheduled_program_counter_ = fetch_decode_execute;\
|
scheduled_program_counter_ = fetch_decode_execute;\
|
||||||
}\
|
}\
|
||||||
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 +67,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 +84,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;
|
||||||
@ -731,7 +735,7 @@ void ProcessorBase::set_nmi_line(bool active) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
||||||
static const MicroOp reset[] = {
|
static constexpr MicroOp reset[] = {
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CycleNoWritePush,
|
CycleNoWritePush,
|
||||||
@ -747,7 +751,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
||||||
static const MicroOp reset[] = {
|
static constexpr MicroOp irq[] = {
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CyclePushPCH,
|
CyclePushPCH,
|
||||||
@ -760,11 +764,11 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
|||||||
CycleReadVectorHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
};
|
};
|
||||||
return reset;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
||||||
static const MicroOp reset[] = {
|
static constexpr MicroOp nmi[] = {
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CyclePushPCH,
|
CyclePushPCH,
|
||||||
@ -777,7 +781,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
|||||||
CycleReadVectorHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
};
|
};
|
||||||
return reset;
|
return nmi;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ProcessorStorage::get_flags() {
|
uint8_t ProcessorStorage::get_flags() {
|
||||||
|
@ -201,6 +201,7 @@ class ProcessorStorage {
|
|||||||
InstructionList operations_[256];
|
InstructionList operations_[256];
|
||||||
|
|
||||||
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.
|
||||||
|
Loading…
Reference in New Issue
Block a user