mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #782 from TomHarte/6502Tidy
Makes `State`, and therefore the 'Reflection' dependency, an optional adjunct to the 6502.
This commit is contained in:
commit
ab81d1093d
@ -783,6 +783,8 @@
|
||||
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131752346DE9100E4FF3D /* StaticAnalyser.cpp */; };
|
||||
4BC1317A2346DF2B00E4FF3D /* MSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131782346DF2B00E4FF3D /* MSA.cpp */; };
|
||||
4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131782346DF2B00E4FF3D /* MSA.cpp */; };
|
||||
4BC57CD92436A62900FBC404 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CD82436A62900FBC404 /* State.cpp */; };
|
||||
4BC57CDA2436A62900FBC404 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CD82436A62900FBC404 /* State.cpp */; };
|
||||
4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */; };
|
||||
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; };
|
||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
|
||||
@ -1655,6 +1657,8 @@
|
||||
4BC57CD2243427C700FBC404 /* AudioProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AudioProducer.hpp; sourceTree = "<group>"; };
|
||||
4BC57CD32434282000FBC404 /* TimedMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TimedMachine.hpp; sourceTree = "<group>"; };
|
||||
4BC57CD424342E0600FBC404 /* MachineTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MachineTypes.hpp; sourceTree = "<group>"; };
|
||||
4BC57CD72436A61300FBC404 /* State.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = "<group>"; };
|
||||
4BC57CD82436A62900FBC404 /* State.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = "<group>"; };
|
||||
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000MoveTests.mm; sourceTree = "<group>"; };
|
||||
4BC5FC2F20CDDDEE00410AA0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AppleIIOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
||||
@ -1939,6 +1943,7 @@
|
||||
4B6A4C8E1F58F09E00E3F787 /* 6502.hpp */,
|
||||
4B6A4C901F58F09E00E3F787 /* AllRAM */,
|
||||
4B6A4C931F58F09E00E3F787 /* Implementation */,
|
||||
4BC57CD62436A61300FBC404 /* State */,
|
||||
);
|
||||
path = 6502;
|
||||
sourceTree = "<group>";
|
||||
@ -3504,6 +3509,15 @@
|
||||
path = AtariST;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BC57CD62436A61300FBC404 /* State */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BC57CD72436A61300FBC404 /* State.hpp */,
|
||||
4BC57CD82436A62900FBC404 /* State.cpp */,
|
||||
);
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BC9DF4A1D04691600F44158 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4289,6 +4303,7 @@
|
||||
4B055AAA1FAE85F50060FFFF /* CPM.cpp in Sources */,
|
||||
4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */,
|
||||
4B0ACC3123775819008902D0 /* TIASound.cpp in Sources */,
|
||||
4BC57CDA2436A62900FBC404 /* State.cpp in Sources */,
|
||||
4B055ACB1FAE9AFB0060FFFF /* SerialBus.cpp in Sources */,
|
||||
4B8318B122D3E53A006DB630 /* DiskIICard.cpp in Sources */,
|
||||
4B055A9B1FAE85DA0060FFFF /* AcornADF.cpp in Sources */,
|
||||
@ -4552,6 +4567,7 @@
|
||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
||||
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
|
||||
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
|
||||
4BC57CD92436A62900FBC404 /* State.cpp in Sources */,
|
||||
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
|
||||
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
|
||||
4B0ACC3223775819008902D0 /* Atari2600.cpp in Sources */,
|
||||
|
@ -82,6 +82,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/*.cpp')
|
||||
SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp')
|
||||
|
||||
SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/6502/State/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Reflection/Enum.hpp"
|
||||
#include "../../Reflection/Struct.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502 {
|
||||
@ -129,81 +127,6 @@ 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<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.
|
||||
|
||||
@ -212,7 +135,7 @@ class ProcessorBase: public ProcessorStorage {
|
||||
@param r The register to set.
|
||||
@returns The value of the register. 8-bit registers will be returned as unsigned.
|
||||
*/
|
||||
uint16_t get_value_of_register(Register r);
|
||||
uint16_t get_value_of_register(Register r) const;
|
||||
|
||||
/*!
|
||||
Sets the value of a register.
|
||||
@ -236,7 +159,7 @@ class ProcessorBase: public ProcessorStorage {
|
||||
|
||||
@returns @c true if the line is logically active; @c false otherwise.
|
||||
*/
|
||||
inline bool get_is_resetting();
|
||||
inline bool get_is_resetting() const;
|
||||
|
||||
/*!
|
||||
This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
|
||||
@ -271,43 +194,9 @@ class ProcessorBase: public ProcessorStorage {
|
||||
|
||||
@returns @c true if the 6502 is jammed; @c false otherwise.
|
||||
*/
|
||||
bool is_jammed();
|
||||
bool is_jammed() const;
|
||||
};
|
||||
|
||||
// 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.
|
||||
|
||||
|
@ -14,7 +14,7 @@ using namespace CPU::MOS6502;
|
||||
|
||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
||||
|
||||
uint16_t ProcessorBase::get_value_of_register(Register r) {
|
||||
uint16_t ProcessorBase::get_value_of_register(Register r) const {
|
||||
switch (r) {
|
||||
case Register::ProgramCounter: return pc_.full;
|
||||
case Register::LastOperationAddress: return last_operation_pc_.full;
|
||||
@ -39,82 +39,6 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessorBase::is_jammed() {
|
||||
bool ProcessorBase::is_jammed() const {
|
||||
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];
|
||||
}
|
||||
|
@ -38,14 +38,12 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
} else {\
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\
|
||||
}\
|
||||
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;
|
||||
|
||||
@ -57,13 +55,11 @@ 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();
|
||||
@ -74,7 +70,6 @@ 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;
|
||||
@ -685,7 +680,7 @@ void ProcessorBase::set_reset_line(bool active) {
|
||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0);
|
||||
}
|
||||
|
||||
bool ProcessorBase::get_is_resetting() {
|
||||
bool ProcessorBase::get_is_resetting() const {
|
||||
return interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn);
|
||||
}
|
||||
|
||||
@ -711,7 +706,7 @@ void ProcessorBase::set_nmi_line(bool active) {
|
||||
nmi_line_is_enabled_ = active;
|
||||
}
|
||||
|
||||
uint8_t ProcessorStorage::get_flags() {
|
||||
uint8_t ProcessorStorage::get_flags() const {
|
||||
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,6 @@ class ProcessorStorage {
|
||||
InstructionList operations_[size_t(OperationsSlot::Max)];
|
||||
|
||||
const MicroOp *scheduled_program_counter_ = nullptr;
|
||||
int cycles_in_phase_ = 0;
|
||||
|
||||
/*
|
||||
Storage for the 6502 registers; F is stored as individual flags.
|
||||
@ -255,7 +254,7 @@ class ProcessorStorage {
|
||||
|
||||
@returns The current value of the flags register.
|
||||
*/
|
||||
inline uint8_t get_flags();
|
||||
inline uint8_t get_flags() const;
|
||||
|
||||
/*!
|
||||
Sets the flags register.
|
||||
@ -285,6 +284,9 @@ class ProcessorStorage {
|
||||
|
||||
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
||||
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
||||
|
||||
// Allow state objects to capture and install state.
|
||||
friend class State;
|
||||
};
|
||||
|
||||
#endif /* _502Storage_h */
|
||||
|
125
Processors/6502/State/State.cpp
Normal file
125
Processors/6502/State/State.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
//
|
||||
// State.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/04/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "State.hpp"
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
State::State(const ProcessorBase &src): State() {
|
||||
// Fill in registers.
|
||||
registers.program_counter = src.pc_.full;
|
||||
registers.stack_pointer = src.s_;
|
||||
registers.flags = src.get_flags();
|
||||
registers.a = src.a_;
|
||||
registers.x = src.x_;
|
||||
registers.y = src.y_;
|
||||
|
||||
// Fill in other inputs.
|
||||
inputs.ready = src.ready_line_is_enabled_;
|
||||
inputs.irq = src.irq_line_;
|
||||
inputs.nmi = src.nmi_line_is_enabled_;
|
||||
inputs.reset = src.interrupt_requests_ & (ProcessorStorage::InterruptRequestFlags::Reset | ProcessorStorage::InterruptRequestFlags::PowerOn);
|
||||
|
||||
// Fill in execution state.
|
||||
execution_state.operation = src.operation_;
|
||||
execution_state.operand = src.operand_;
|
||||
execution_state.address = src.address_.full;
|
||||
execution_state.next_address = src.next_address_.full;
|
||||
if(src.ready_is_active_) {
|
||||
execution_state.phase = State::ExecutionState::Phase::Ready;
|
||||
} else if(src.is_jammed_) {
|
||||
execution_state.phase = State::ExecutionState::Phase::Jammed;
|
||||
} else if(src.wait_is_active_) {
|
||||
execution_state.phase = State::ExecutionState::Phase::Waiting;
|
||||
} else if(src.stop_is_active_) {
|
||||
execution_state.phase = State::ExecutionState::Phase::Stopped;
|
||||
} else {
|
||||
execution_state.phase = State::ExecutionState::Phase::Instruction;
|
||||
}
|
||||
|
||||
const auto micro_offset = size_t(src.scheduled_program_counter_ - &src.operations_[0][0]);
|
||||
const auto list_length = sizeof(ProcessorStorage::InstructionList) / sizeof(ProcessorStorage::MicroOp);
|
||||
|
||||
execution_state.micro_program = int(micro_offset / list_length);
|
||||
execution_state.micro_program_offset = int(micro_offset % list_length);
|
||||
assert(&src.operations_[execution_state.micro_program][execution_state.micro_program_offset] == src.scheduled_program_counter_);
|
||||
}
|
||||
|
||||
void State::apply(ProcessorBase &target) {
|
||||
// Grab registers.
|
||||
target.pc_.full = registers.program_counter;
|
||||
target.s_ = registers.stack_pointer;
|
||||
target.set_flags(registers.flags);
|
||||
target.a_ = registers.a;
|
||||
target.x_ = registers.x;
|
||||
target.y_ = registers.y;
|
||||
|
||||
// Grab other inputs.
|
||||
target.ready_line_is_enabled_ = inputs.ready;
|
||||
target.set_irq_line(inputs.irq);
|
||||
target.set_nmi_line(inputs.nmi);
|
||||
target.set_reset_line(inputs.reset);
|
||||
|
||||
// Set execution state.
|
||||
target.ready_is_active_ = target.is_jammed_ = target.wait_is_active_ = target.stop_is_active_ = false;
|
||||
switch(execution_state.phase) {
|
||||
case State::ExecutionState::Phase::Ready: target.ready_is_active_ = true; break;
|
||||
case State::ExecutionState::Phase::Jammed: target.is_jammed_ = true; break;
|
||||
case State::ExecutionState::Phase::Stopped: target.stop_is_active_ = true; break;
|
||||
case State::ExecutionState::Phase::Waiting: target.wait_is_active_ = true; break;
|
||||
case State::ExecutionState::Phase::Instruction: break;
|
||||
}
|
||||
|
||||
target.operation_ = execution_state.operation;
|
||||
target.operand_ = execution_state.operand;
|
||||
target.address_.full = execution_state.address;
|
||||
target.next_address_.full = execution_state.next_address;
|
||||
target.scheduled_program_counter_ = &target.operations_[execution_state.micro_program][execution_state.micro_program_offset];
|
||||
}
|
||||
|
||||
// Boilerplate follows here, to establish 'reflection'.
|
||||
State::State() {
|
||||
if(needs_declare()) {
|
||||
DeclareField(registers);
|
||||
DeclareField(execution_state);
|
||||
DeclareField(inputs);
|
||||
}
|
||||
}
|
||||
|
||||
State::Registers::Registers() {
|
||||
if(needs_declare()) {
|
||||
DeclareField(program_counter);
|
||||
DeclareField(stack_pointer);
|
||||
DeclareField(flags);
|
||||
DeclareField(a);
|
||||
DeclareField(x);
|
||||
DeclareField(y);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
State::Inputs::Inputs() {
|
||||
if(needs_declare()) {
|
||||
DeclareField(ready);
|
||||
DeclareField(irq);
|
||||
DeclareField(nmi);
|
||||
DeclareField(reset);
|
||||
}
|
||||
}
|
96
Processors/6502/State/State.hpp
Normal file
96
Processors/6502/State/State.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// State.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/04/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef State_h
|
||||
#define State_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../6502.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502 {
|
||||
|
||||
/*!
|
||||
Provides a means for capturing or restoring complete 6502 state.
|
||||
|
||||
This is an optional adjunct to the 6502 class. If you want to take the rest of the 6502
|
||||
implementation but don't want any of the overhead of my sort-of half-reflection as
|
||||
encapsulated in Reflection/[Enum/Struct].hpp just don't use this class.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// Default constructor; makes no guarantees as to field values beyond those given above.
|
||||
State();
|
||||
|
||||
/// Instantiates a new State based on the processor @c src.
|
||||
State(const ProcessorBase &src);
|
||||
|
||||
/// Applies this state to @c target.
|
||||
void apply(ProcessorBase &target);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* State_h */
|
Loading…
Reference in New Issue
Block a user