From 4fbe9835270d820feef88bd6f53b9d2b640a0f32 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Mar 2020 00:33:27 -0400 Subject: [PATCH 01/11] 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**. --- Processors/6502/6502.hpp | 111 ++++++++++++++++++ Processors/6502/Implementation/6502Base.cpp | 56 +++++++++ .../Implementation/6502Implementation.hpp | 44 +++---- .../6502/Implementation/6502Storage.hpp | 1 + 4 files changed, 192 insertions(+), 20 deletions(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 2dac65439..6e511b9f8 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -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 { + /*! + Provides the current state of the well-known, published internal registers. + */ + struct Registers: public Reflection::StructImpl { + 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 { + 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 { + 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. diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index 7de1bc331..8b4b16afc 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -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; +} diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 9e8c04984..c23eb6f9a 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -33,28 +33,29 @@ template 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 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 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() { diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index bc06c0e2c..f41965aa7 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -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. From cfb75b58caea74e0befefa61eabe0f57c2840bb2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Mar 2020 18:36:41 -0400 Subject: [PATCH 02/11] Pulls all 6502 MicroOp sequences into the main operations_ table. This will make state restoration somewhat more tractable. --- Processors/6502/6502.hpp | 10 +-- Processors/6502/Implementation/6502Base.cpp | 25 ++---- .../Implementation/6502Implementation.hpp | 89 ++----------------- .../6502/Implementation/6502Storage.cpp | 73 ++++++++++++++- .../6502/Implementation/6502Storage.hpp | 51 ++++++----- 5 files changed, 117 insertions(+), 131 deletions(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 6e511b9f8..067399fbd 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -163,14 +163,13 @@ class ProcessorBase: public ProcessorStorage { */ struct ExecutionState: public Reflection::StructImpl { 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); diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index 8b4b16afc..7e4e677e3 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -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; diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index c23eb6f9a..03706c04f 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -13,17 +13,7 @@ */ template void Processor::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 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 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 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 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_; } diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index e2d365272..e87d6140a 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -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_)); diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index f41965aa7..89801bd4f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -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 */ From f720a6201b228f406ff1cc7b4e58f12dbfaf93c3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Mar 2020 18:36:57 -0400 Subject: [PATCH 03/11] Adds explicit type cast. --- Storage/Disk/Drive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index b79f87532..e8a5a461f 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -45,7 +45,7 @@ void Drive::set_rotation_speed(float revolutions_per_minute) { // From there derive the appropriate rotational multiplier and possibly update the // count of cycles since the index hole proportionally. const float new_rotational_multiplier = float(cycles_per_revolution_) / float(get_input_clock_rate()); - cycles_since_index_hole_ *= new_rotational_multiplier / rotational_multiplier_; + cycles_since_index_hole_ = Cycles::IntType(float(cycles_since_index_hole_) * new_rotational_multiplier / rotational_multiplier_); rotational_multiplier_ = new_rotational_multiplier; cycles_since_index_hole_ %= cycles_per_revolution_; } From 1810ef60befb932e75898c7ebf5781d3073a00f8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Mar 2020 18:41:30 -0400 Subject: [PATCH 04/11] Adds --fix-missing in the hope of catching more issues automatically. --- .github/workflows/ccpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 655cd5a67..92e96470f 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -17,6 +17,6 @@ jobs: steps: - uses: actions/checkout@v1 - name: Install dependencies - run: sudo apt-get --allow-releaseinfo-change update; sudo apt-get install libsdl2-dev scons + run: sudo apt-get --allow-releaseinfo-change update; sudo apt-get --fix-missing install libsdl2-dev scons - name: Make run: cd OSBindings/SDL; scons From 4f2ebad8e0a154ac684123dafa8186bffbe9185a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Mar 2020 22:50:30 -0400 Subject: [PATCH 05/11] Takes a shot a set_state. --- Processors/6502/6502.hpp | 2 +- Processors/6502/Implementation/6502Base.cpp | 48 ++++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 067399fbd..ac5e2e486 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -163,7 +163,7 @@ class ProcessorBase: public ProcessorStorage { */ struct ExecutionState: public Reflection::StructImpl { ReflectableEnum(Phase, - Instruction, Stopped, Waiting, Jammed + Instruction, Stopped, Waiting, Jammed, Ready ); /// Current executon phase, e.g. standard instruction flow or responding to an IRQ. diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index 7e4e677e3..fee80c91e 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -65,7 +65,9 @@ ProcessorBase::State ProcessorBase::get_state() { state.execution_state.operand = operand_; state.execution_state.address = address_.full; state.execution_state.next_address = next_address_.full; - if(is_jammed_) { + 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; @@ -73,13 +75,45 @@ ProcessorBase::State ProcessorBase::get_state() { 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); } + 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); + 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]; +} From edc553fa1d0facfaf3d849d1f748c096564b22d2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Mar 2020 22:58:00 -0400 Subject: [PATCH 06/11] Removes duplicative 'register'. --- Processors/6502/6502.hpp | 3 +-- Processors/6502/Implementation/6502Base.cpp | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index ac5e2e486..5f7febbf8 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -31,8 +31,7 @@ enum Register { Flags, A, X, - Y, - S + Y }; /* diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index fee80c91e..54fe21138 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -21,7 +21,6 @@ uint16_t ProcessorBase::get_value_of_register(Register r) { case Register::A: return a_; case Register::X: return x_; case Register::Y: return y_; - case Register::S: return s_; default: return 0; } } @@ -34,7 +33,6 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) { case Register::A: a_ = static_cast(value); break; case Register::X: x_ = static_cast(value); break; case Register::Y: y_ = static_cast(value); break; - case Register::S: s_ = static_cast(value); break; default: break; } } From 60aa383c95aa1074798379e96d12fdc0ac0b4726 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 00:24:49 -0400 Subject: [PATCH 07/11] Makes a not-quite-correct attempt at a .description for reflective structs. --- .../Implementation/MultiConfigurable.cpp | 8 +- Reflection/Struct.cpp | 81 +++++++++++++++++-- Reflection/Struct.hpp | 65 ++++++++++----- 3 files changed, 125 insertions(+), 29 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp index 6688debba..55bf935ba 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp @@ -30,7 +30,7 @@ class MultiStruct: public Reflection::Struct { } } - std::vector all_keys() final { + std::vector all_keys() const final { std::set keys; for(auto &options: options_) { const auto new_keys = options->all_keys(); @@ -39,7 +39,7 @@ class MultiStruct: public Reflection::Struct { return std::vector(keys.begin(), keys.end()); } - std::vector values_for(const std::string &name) final { + std::vector values_for(const std::string &name) const final { std::set values; for(auto &options: options_) { const auto new_values = options->values_for(name); @@ -48,7 +48,7 @@ class MultiStruct: public Reflection::Struct { return std::vector(values.begin(), values.end()); } - const std::type_info *type_of(const std::string &name) final { + const std::type_info *type_of(const std::string &name) const final { for(auto &options: options_) { auto info = options->type_of(name); if(info) return info; @@ -56,7 +56,7 @@ class MultiStruct: public Reflection::Struct { return nullptr; } - const void *get(const std::string &name) final { + const void *get(const std::string &name) const final { for(auto &options: options_) { auto value = options->get(name); if(value) return value; diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 19096e0ad..6597a8c63 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -9,6 +9,7 @@ #include "Struct.hpp" #include +#include // MARK: - Setters @@ -107,18 +108,84 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s // MARK: - Getters -template bool Reflection::get(Struct &target, const std::string &name, Type &value) { - return false; -} - -template <> bool Reflection::get(Struct &target, const std::string &name, bool &value) { +template bool Reflection::get(const Struct &target, const std::string &name, Type &value) { const auto target_type = target.type_of(name); if(!target_type) return false; - if(*target_type == typeid(bool)) { - value = *reinterpret_cast(target.get(name)); + if(*target_type == typeid(Type)) { + memcpy(&value, target.get(name), sizeof(Type)); return true; } return false; } + +template Type Reflection::get(const Struct &target, const std::string &name) { + Type value; + get(target, name, value); + return value; +} + +// MARK: - Description + +std::string Reflection::Struct::description() const { + std::ostringstream stream; + + stream << "{"; + + bool is_first = true; + for(const auto &key: all_keys()) { + if(!is_first) stream << ", "; + is_first = false; + stream << key << ": "; + + const auto type = type_of(key); + + // Output Bools as yes/no. + if(*type == typeid(bool)) { + bool value; + ::Reflection::get(*this, key, value); + stream << (value ? "true" : "false"); + continue; + } + + // Output Ints of all sizes as hex. +#define OutputIntC(int_type, cast_type) if(*type == typeid(int_type)) { stream << std::hex << cast_type(::Reflection::get(*this, key)); continue; } +#define OutputInt(int_type) OutputIntC(int_type, int_type) + OutputIntC(int8_t, int16_t); + OutputIntC(uint8_t, uint16_t); + OutputInt(int16_t); + OutputInt(uint16_t); + OutputInt(int32_t); + OutputInt(uint32_t); + OutputInt(int64_t); + OutputInt(uint64_t); +#undef OutputInt + + // Output floats and strings natively. +#define OutputNative(val_type) if(*type == typeid(val_type)) { stream << ::Reflection::get(*this, key); continue; } + OutputNative(float); + OutputNative(double); + OutputNative(char *); + OutputNative(std::string); +#undef OutputNAtive + + // Output the current value of any enums. + if(!Enum::name(*type).empty()) { + const int value = ::Reflection::get(*this, key); + stream << Enum::to_string(*type, value); + continue; + } + + // Recurse to deal with embedded objects. + if(*type == typeid(Reflection::Struct)) { + const Reflection::Struct *const child = reinterpret_cast(get(key)); + stream << child->description(); + continue; + } + } + + stream << "}"; + + return stream.str(); +} diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index e6423deae..f60ce06c1 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -24,12 +24,18 @@ namespace Reflection { #define DeclareField(Name) declare(&Name, #Name) struct Struct { - virtual std::vector all_keys() = 0; - virtual const std::type_info *type_of(const std::string &name) = 0; + virtual std::vector all_keys() const = 0; + virtual const std::type_info *type_of(const std::string &name) const = 0; virtual void set(const std::string &name, const void *value) = 0; - virtual const void *get(const std::string &name) = 0; - virtual std::vector values_for(const std::string &name) = 0; + virtual const void *get(const std::string &name) const = 0; + virtual std::vector values_for(const std::string &name) const = 0; virtual ~Struct() {} + + /*! + @returns A string describing this struct. This string has no guaranteed layout, may not be + sufficiently formed for a formal language parser, etc. + */ + std::string description() const; }; /*! @@ -86,9 +92,14 @@ bool fuzzy_set(Struct &target, const std::string &name, const std::string &value @returns @c true if the property was successfully read; @c false otherwise. */ -template bool get(Struct &target, const std::string &name, Type &value); +template bool get(const Struct &target, const std::string &name, Type &value); -template <> bool get(Struct &target, const std::string &name, bool &value); +/*! + Attempts to get the property @c name to @c value ; will perform limited type conversions. + + @returns @c true if the property was successfully read; a default-constructed instance of Type otherwise. +*/ +template Type get(const Struct &target, const std::string &name); // TODO: move this elsewhere. It's just a sketch anyway. @@ -106,10 +117,10 @@ template class StructImpl: public Struct { @returns the value of type @c Type that is loaded from the offset registered for the field @c name. It is the caller's responsibility to provide an appropriate type of data. */ - const void *get(const std::string &name) final { + const void *get(const std::string &name) const final { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; - return reinterpret_cast(this) + iterator->second.offset; + return reinterpret_cast(this) + iterator->second.offset; } /*! @@ -126,7 +137,7 @@ template class StructImpl: public Struct { /*! @returns @c type_info for the field @c name. */ - const std::type_info *type_of(const std::string &name) final { + const std::type_info *type_of(const std::string &name) const final { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; return iterator->second.type; @@ -136,7 +147,7 @@ template class StructImpl: public Struct { @returns a list of the valid enum value names for field @c name if it is a declared enum field of this struct; the empty list otherwise. */ - std::vector values_for(const std::string &name) final { + std::vector values_for(const std::string &name) const final { std::vector result; // Return an empty vector if this field isn't declared. @@ -168,7 +179,7 @@ template class StructImpl: public Struct { /*! @returns A vector of all declared fields for this struct. */ - std::vector all_keys() final { + std::vector all_keys() const final { std::vector keys; for(const auto &pair: contents_) { keys.push_back(pair.first); @@ -190,14 +201,14 @@ template class StructImpl: public Struct { */ /*! - Exposes the field pointed to by @c t for reflection as @c name. + Exposes the field pointed to by @c t for reflection as @c name. If @c t is itself a Reflection::Struct, + it'll be the struct that's exposed. */ template void declare(Type *t, const std::string &name) { - contents_.emplace( - std::make_pair( - name, - Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type)) - )); + if constexpr (std::is_class()) { + if(declare_reflectable(t, name)) return; + } + declare_emplace(t, name); } /*! @@ -233,7 +244,7 @@ template class StructImpl: public Struct { @returns @c true if this subclass of @c Struct has not yet declared any fields. */ bool needs_declare() { - return !contents_.size(); + return contents_.empty(); } /*! @@ -256,6 +267,24 @@ template class StructImpl: public Struct { } private: + template bool declare_reflectable(Type *t, const std::string &name) { + Reflection::Struct *const str = static_cast(t); + if(str) { + declare_emplace(str, name); + return true; + } + + return false; + } + + template void declare_emplace(Type *t, const std::string &name) { + contents_.emplace( + std::make_pair( + name, + Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type)) + )); + } + struct Field { const std::type_info *type; ssize_t offset; From 95c68c76e1e73910500a92bac87175f319e9c454 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 00:27:40 -0400 Subject: [PATCH 08/11] Corrects use of StructImpl. --- Processors/6502/6502.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 5f7febbf8..77192a93d 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -160,7 +160,7 @@ class ProcessorBase: public ProcessorStorage { does not necessarily correlate with anything in a real 6502, and some of it very obviously doesn't. */ - struct ExecutionState: public Reflection::StructImpl { + struct ExecutionState: public Reflection::StructImpl { ReflectableEnum(Phase, Instruction, Stopped, Waiting, Jammed, Ready ); From 6805acd74f635a64761d6b021473a084da95b337 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 00:31:25 -0400 Subject: [PATCH 09/11] Adds padding for all integer types. --- Reflection/Struct.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 6597a8c63..5567172d8 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -9,6 +9,7 @@ #include "Struct.hpp" #include +#include #include // MARK: - Setters @@ -150,7 +151,7 @@ std::string Reflection::Struct::description() const { } // Output Ints of all sizes as hex. -#define OutputIntC(int_type, cast_type) if(*type == typeid(int_type)) { stream << std::hex << cast_type(::Reflection::get(*this, key)); continue; } +#define OutputIntC(int_type, cast_type) if(*type == typeid(int_type)) { stream << std::setfill('0') << std::setw(sizeof(int_type)*2) << std::hex << cast_type(::Reflection::get(*this, key)); continue; } #define OutputInt(int_type) OutputIntC(int_type, int_type) OutputIntC(int8_t, int16_t); OutputIntC(uint8_t, uint16_t); From a491650c8ba7e15941d20c494e6b078283f262c1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 21:39:31 -0400 Subject: [PATCH 10/11] Adds safety asserts. --- Processors/6502/Implementation/6502Base.cpp | 3 +++ Reflection/Struct.hpp | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index 54fe21138..f82294b72 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -8,6 +8,8 @@ #include "../6502.hpp" +#include + using namespace CPU::MOS6502; const uint8_t CPU::MOS6502::JamOpcode = 0xf2; @@ -80,6 +82,7 @@ ProcessorBase::State ProcessorBase::get_state() { 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; } diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index f60ce06c1..8b1860e3c 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -9,6 +9,7 @@ #ifndef Struct_hpp #define Struct_hpp +#include #include #include #include @@ -113,6 +114,15 @@ struct Serialisable { template class StructImpl: public Struct { public: +#ifndef NDEBUG + StructImpl() { + // Protect against declarations that nominate the wrong Owner; this isn't + // a static assert because that wouldn't catch all invalid cases. + const auto owner = static_cast(this); + assert(owner != nullptr); + } +#endif + /*! @returns the value of type @c Type that is loaded from the offset registered for the field @c name. It is the caller's responsibility to provide an appropriate type of data. From 2f4b0c2b9ac851019cbe4d844396aaa0446a674a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 21:48:07 -0400 Subject: [PATCH 11/11] Removes non-functional assert. --- Reflection/Struct.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 8b1860e3c..c51758d8a 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -114,15 +114,6 @@ struct Serialisable { template class StructImpl: public Struct { public: -#ifndef NDEBUG - StructImpl() { - // Protect against declarations that nominate the wrong Owner; this isn't - // a static assert because that wouldn't catch all invalid cases. - const auto owner = static_cast(this); - assert(owner != nullptr); - } -#endif - /*! @returns the value of type @c Type that is loaded from the offset registered for the field @c name. It is the caller's responsibility to provide an appropriate type of data.