1
0
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:
Thomas Harte 2020-03-29 18:36:41 -04:00
parent 4fbe983527
commit cfb75b58ca
5 changed files with 117 additions and 131 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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_;
}

View File

@ -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_));

View File

@ -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 */