mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 18:30:21 +00:00
Pulls all 6502 MicroOp sequences into the main operations_ table.
This will make state restoration somewhat more tractable.
This commit is contained in:
parent
4fbe983527
commit
cfb75b58ca
@ -163,14 +163,13 @@ class ProcessorBase: public ProcessorStorage {
|
||||
*/
|
||||
struct ExecutionState: public Reflection::StructImpl<Registers> {
|
||||
ReflectableEnum(Phase,
|
||||
Reset, IRQ, NMI, Instruction, Stopped, Waiting, Jammed
|
||||
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;
|
||||
int micro_program;
|
||||
int micro_program_offset;
|
||||
|
||||
// The following are very internal things. At the minute I
|
||||
// consider these 'reliable' for inter-launch state
|
||||
@ -292,7 +291,8 @@ inline ProcessorBase::State::ExecutionState::ExecutionState() {
|
||||
if(needs_declare()) {
|
||||
AnnounceEnum(Phase);
|
||||
DeclareField(phase);
|
||||
DeclareField(cycles_into_phase);
|
||||
DeclareField(micro_program);
|
||||
DeclareField(micro_program_offset);
|
||||
DeclareField(operation);
|
||||
DeclareField(operand);
|
||||
DeclareField(address);
|
||||
|
@ -63,7 +63,6 @@ ProcessorBase::State ProcessorBase::get_state() {
|
||||
// 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_) {
|
||||
@ -73,27 +72,13 @@ ProcessorBase::State ProcessorBase::get_state() {
|
||||
} 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;
|
||||
state.execution_state.phase = State::ExecutionState::Phase::Instruction;
|
||||
|
||||
const MicroOp *final_op = op;
|
||||
while(*final_op != OperationMoveToNextProgram) {
|
||||
++final_op;
|
||||
}
|
||||
return scheduled_program_counter_ < final_op;
|
||||
};
|
||||
const auto micro_offset = size_t(scheduled_program_counter_ - &operations_[0][0]);
|
||||
const auto list_length = sizeof(InstructionList) / sizeof(MicroOp);
|
||||
|
||||
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;
|
||||
}
|
||||
state.execution_state.micro_program = int(micro_offset / list_length);
|
||||
state.execution_state.micro_program_offset = int(micro_offset % list_length);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -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) {
|
||||
static const MicroOp do_branch[] = {
|
||||
CycleReadFromPC,
|
||||
CycleAddSignedOperandToPC,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
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
|
||||
// without touching the class storage (i.e. it explicitly says they need be completely up
|
||||
@ -38,15 +28,15 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
if(interrupt_requests_) {\
|
||||
if(interrupt_requests_ & (InterruptRequestFlags::Reset | 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) {\
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
|
||||
scheduled_program_counter_ = get_nmi_program();\
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::NMI)];\
|
||||
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
|
||||
scheduled_program_counter_ = get_irq_program();\
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::IRQ)];\
|
||||
} \
|
||||
} else {\
|
||||
scheduled_program_counter_ = fetch_decode_execute;\
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\
|
||||
}\
|
||||
cycles_in_phase_ = 0; \
|
||||
}
|
||||
@ -570,7 +560,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
#define BRA(condition) \
|
||||
pc_.full++; \
|
||||
if(condition) { \
|
||||
scheduled_program_counter_ = do_branch; \
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
|
||||
}
|
||||
|
||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||
@ -597,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
|
||||
// to target its destination skips any pending interrupts.
|
||||
// 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;
|
||||
|
||||
@ -615,22 +605,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
// and (iii) read from the corresponding zero page.
|
||||
const uint8_t mask = uint8_t(1 << ((operation_ >> 4)&7));
|
||||
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
|
||||
static const MicroOp do_branch[] = {
|
||||
CycleFetchOperand, // Fetch offset.
|
||||
OperationIncrementPC,
|
||||
CycleFetchFromHalfUpdatedPC,
|
||||
OperationAddSignedOperandToPC16,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
scheduled_program_counter_ = do_branch;
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBBRBBS)];
|
||||
} else {
|
||||
static const MicroOp do_not_branch[] = {
|
||||
CycleFetchOperand,
|
||||
OperationIncrementPC,
|
||||
CycleFetchFromHalfUpdatedPC,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
scheduled_program_counter_ = do_not_branch;
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoNotBBRBBS)];
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -734,56 +711,6 @@ void ProcessorBase::set_nmi_line(bool active) {
|
||||
nmi_line_is_enabled_ = active;
|
||||
}
|
||||
|
||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
||||
static constexpr MicroOp reset[] = {
|
||||
CycleFetchOperand,
|
||||
CycleFetchOperand,
|
||||
CycleNoWritePush,
|
||||
CycleNoWritePush,
|
||||
OperationRSTPickVector,
|
||||
CycleNoWritePush,
|
||||
OperationSetNMIRSTFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
return reset;
|
||||
}
|
||||
|
||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
||||
static constexpr MicroOp irq[] = {
|
||||
CycleFetchOperand,
|
||||
CycleFetchOperand,
|
||||
CyclePushPCH,
|
||||
CyclePushPCL,
|
||||
OperationBRKPickVector,
|
||||
OperationSetOperandFromFlags,
|
||||
CyclePushOperand,
|
||||
OperationSetIRQFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
return irq;
|
||||
}
|
||||
|
||||
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
||||
static constexpr MicroOp nmi[] = {
|
||||
CycleFetchOperand,
|
||||
CycleFetchOperand,
|
||||
CyclePushPCH,
|
||||
CyclePushPCL,
|
||||
OperationNMIPickVector,
|
||||
OperationSetOperandFromFlags,
|
||||
CyclePushOperand,
|
||||
OperationSetNMIRSTFlags,
|
||||
CycleReadVectorLow,
|
||||
CycleReadVectorHigh,
|
||||
OperationMoveToNextProgram
|
||||
};
|
||||
return nmi;
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||
decimal_flag_ &= Flag::Decimal;
|
||||
overflow_flag_ &= Flag::Overflow;
|
||||
|
||||
const InstructionList operations_6502[256] = {
|
||||
const InstructionList operations_6502[] = {
|
||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||
/* 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),
|
||||
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
|
||||
/* 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.
|
||||
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.
|
||||
*/
|
||||
enum MicroOp {
|
||||
enum MicroOp: uint8_t {
|
||||
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
|
||||
OperationDecodeOperation, // schedules the microprogram associated with operation_
|
||||
@ -197,8 +197,32 @@ class ProcessorStorage {
|
||||
OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received)
|
||||
};
|
||||
|
||||
using InstructionList = MicroOp[10];
|
||||
InstructionList operations_[256];
|
||||
using InstructionList = MicroOp[12];
|
||||
/// 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;
|
||||
int cycles_in_phase_ = 0;
|
||||
@ -261,27 +285,6 @@ class ProcessorStorage {
|
||||
|
||||
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user