mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-06 20:54:31 +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 */; };
|
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131752346DE9100E4FF3D /* StaticAnalyser.cpp */; };
|
||||||
4BC1317A2346DF2B00E4FF3D /* MSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131782346DF2B00E4FF3D /* MSA.cpp */; };
|
4BC1317A2346DF2B00E4FF3D /* MSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131782346DF2B00E4FF3D /* MSA.cpp */; };
|
||||||
4BC1317B2346DF2B00E4FF3D /* 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 */; };
|
4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */; };
|
||||||
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; };
|
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; };
|
||||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
||||||
@ -1939,6 +1943,7 @@
|
|||||||
4B6A4C8E1F58F09E00E3F787 /* 6502.hpp */,
|
4B6A4C8E1F58F09E00E3F787 /* 6502.hpp */,
|
||||||
4B6A4C901F58F09E00E3F787 /* AllRAM */,
|
4B6A4C901F58F09E00E3F787 /* AllRAM */,
|
||||||
4B6A4C931F58F09E00E3F787 /* Implementation */,
|
4B6A4C931F58F09E00E3F787 /* Implementation */,
|
||||||
|
4BC57CD62436A61300FBC404 /* State */,
|
||||||
);
|
);
|
||||||
path = 6502;
|
path = 6502;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -3504,6 +3509,15 @@
|
|||||||
path = AtariST;
|
path = AtariST;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4BC57CD62436A61300FBC404 /* State */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4BC57CD72436A61300FBC404 /* State.hpp */,
|
||||||
|
4BC57CD82436A62900FBC404 /* State.cpp */,
|
||||||
|
);
|
||||||
|
path = State;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4BC9DF4A1D04691600F44158 /* Components */ = {
|
4BC9DF4A1D04691600F44158 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -4289,6 +4303,7 @@
|
|||||||
4B055AAA1FAE85F50060FFFF /* CPM.cpp in Sources */,
|
4B055AAA1FAE85F50060FFFF /* CPM.cpp in Sources */,
|
||||||
4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */,
|
4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */,
|
||||||
4B0ACC3123775819008902D0 /* TIASound.cpp in Sources */,
|
4B0ACC3123775819008902D0 /* TIASound.cpp in Sources */,
|
||||||
|
4BC57CDA2436A62900FBC404 /* State.cpp in Sources */,
|
||||||
4B055ACB1FAE9AFB0060FFFF /* SerialBus.cpp in Sources */,
|
4B055ACB1FAE9AFB0060FFFF /* SerialBus.cpp in Sources */,
|
||||||
4B8318B122D3E53A006DB630 /* DiskIICard.cpp in Sources */,
|
4B8318B122D3E53A006DB630 /* DiskIICard.cpp in Sources */,
|
||||||
4B055A9B1FAE85DA0060FFFF /* AcornADF.cpp in Sources */,
|
4B055A9B1FAE85DA0060FFFF /* AcornADF.cpp in Sources */,
|
||||||
@ -4552,6 +4567,7 @@
|
|||||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
||||||
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
|
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
|
||||||
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
|
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
|
||||||
|
4BC57CD92436A62900FBC404 /* State.cpp in Sources */,
|
||||||
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
|
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,
|
||||||
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
|
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
|
||||||
4B0ACC3223775819008902D0 /* Atari2600.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('../../Outputs/OpenGL/Primitives/*.cpp')
|
||||||
|
|
||||||
SOURCES += glob.glob('../../Processors/6502/Implementation/*.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/68000/Implementation/*.cpp')
|
||||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
|
|
||||||
#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 {
|
||||||
@ -129,81 +127,6 @@ 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.
|
||||||
|
|
||||||
@ -212,7 +135,7 @@ class ProcessorBase: public ProcessorStorage {
|
|||||||
@param r The register to set.
|
@param r The register to set.
|
||||||
@returns The value of the register. 8-bit registers will be returned as unsigned.
|
@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.
|
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.
|
@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
|
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.
|
@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.
|
@abstact Template providing emulation of a 6502 processor.
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ using namespace CPU::MOS6502;
|
|||||||
|
|
||||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
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) {
|
switch (r) {
|
||||||
case Register::ProgramCounter: return pc_.full;
|
case Register::ProgramCounter: return pc_.full;
|
||||||
case Register::LastOperationAddress: return last_operation_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_;
|
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 {\
|
} else {\
|
||||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\
|
||||||
}\
|
}\
|
||||||
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;
|
||||||
|
|
||||||
@ -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.
|
// 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();
|
||||||
@ -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.
|
// 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;
|
||||||
@ -685,7 +680,7 @@ void ProcessorBase::set_reset_line(bool active) {
|
|||||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0);
|
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);
|
return interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,7 +706,7 @@ void ProcessorBase::set_nmi_line(bool active) {
|
|||||||
nmi_line_is_enabled_ = 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_;
|
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)];
|
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.
|
||||||
@ -255,7 +254,7 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
@returns The current value of the flags register.
|
@returns The current value of the flags register.
|
||||||
*/
|
*/
|
||||||
inline uint8_t get_flags();
|
inline uint8_t get_flags() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the flags register.
|
Sets the flags register.
|
||||||
@ -285,6 +284,9 @@ 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;
|
||||||
|
|
||||||
|
// Allow state objects to capture and install state.
|
||||||
|
friend class State;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _502Storage_h */
|
#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