1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-29 16:55:59 +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:
Thomas Harte 2020-03-28 00:33:27 -04:00
parent 272383cac7
commit 4fbe983527
4 changed files with 192 additions and 20 deletions

View File

@ -15,6 +15,8 @@
#include "../RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../Reflection/Enum.hpp"
#include "../../Reflection/Struct.hpp"
namespace CPU {
namespace MOS6502 {
@ -128,6 +130,82 @@ class ProcessorBase: public ProcessorStorage {
public:
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.
@ -198,6 +276,39 @@ class ProcessorBase: public ProcessorStorage {
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.

View File

@ -42,3 +42,59 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
bool ProcessorBase::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;
}

View File

@ -33,28 +33,29 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
uint16_t busAddress = bus_address_;
uint8_t *busValue = bus_value_;
#define checkSchedule(op) \
#define checkSchedule() \
if(!scheduled_program_counter_) {\
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
scheduled_program_counter_ = fetch_decode_execute;\
}\
op;\
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
scheduled_program_counter_ = fetch_decode_execute;\
}\
cycles_in_phase_ = 0; \
}
#define bus_access() \
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
++cycles_in_phase_; \
nextBusOperation = BusOperation::None; \
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.
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
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.
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
++cycles_in_phase_;
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
stop_is_active_ = false;
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.
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
++cycles_in_phase_;
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
wait_is_active_ = false;
@ -731,7 +735,7 @@ void ProcessorBase::set_nmi_line(bool active) {
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
static const MicroOp reset[] = {
static constexpr MicroOp reset[] = {
CycleFetchOperand,
CycleFetchOperand,
CycleNoWritePush,
@ -747,7 +751,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
static const MicroOp reset[] = {
static constexpr MicroOp irq[] = {
CycleFetchOperand,
CycleFetchOperand,
CyclePushPCH,
@ -760,11 +764,11 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
CycleReadVectorHigh,
OperationMoveToNextProgram
};
return reset;
return irq;
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
static const MicroOp reset[] = {
static constexpr MicroOp nmi[] = {
CycleFetchOperand,
CycleFetchOperand,
CyclePushPCH,
@ -777,7 +781,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
CycleReadVectorHigh,
OperationMoveToNextProgram
};
return reset;
return nmi;
}
uint8_t ProcessorStorage::get_flags() {

View File

@ -201,6 +201,7 @@ class ProcessorStorage {
InstructionList operations_[256];
const MicroOp *scheduled_program_counter_ = nullptr;
int cycles_in_phase_ = 0;
/*
Storage for the 6502 registers; F is stored as individual flags.