mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-27 15:29:34 +00:00
Merge pull request #794 from TomHarte/68000State
Adds a `State` for the 68000.
This commit is contained in:
commit
41fc6c20a0
@ -56,6 +56,14 @@ class MultiStruct: public Reflection::Struct {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t count_of(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return options->count_of(name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *get(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
|
@ -143,6 +143,8 @@
|
||||
4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */; };
|
||||
4B1B58F6246CC4E8009C171E /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B58F4246CC4E8009C171E /* State.cpp */; };
|
||||
4B1B58F7246CC4E8009C171E /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B58F4246CC4E8009C171E /* State.cpp */; };
|
||||
4B1B58FF246E19FD009C171E /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B58FD246E19FD009C171E /* State.cpp */; };
|
||||
4B1B5900246E19FD009C171E /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B58FD246E19FD009C171E /* State.cpp */; };
|
||||
4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */; };
|
||||
4B1B88BC202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */; };
|
||||
4B1B88BD202E3D3D00B67DFF /* MultiMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */; };
|
||||
@ -988,6 +990,8 @@
|
||||
4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StringSerialiser.hpp; sourceTree = "<group>"; };
|
||||
4B1B58F4246CC4E8009C171E /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = "<group>"; };
|
||||
4B1B58F5246CC4E8009C171E /* State.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = "<group>"; };
|
||||
4B1B58FD246E19FD009C171E /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = "<group>"; };
|
||||
4B1B58FE246E19FD009C171E /* State.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = "<group>"; };
|
||||
4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiKeyboardMachine.cpp; sourceTree = "<group>"; };
|
||||
4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiKeyboardMachine.hpp; sourceTree = "<group>"; };
|
||||
4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiConfigurable.cpp; sourceTree = "<group>"; };
|
||||
@ -2022,6 +2026,15 @@
|
||||
path = Z80/State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1B58FC246E19FD009C171E /* State */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B1B58FD246E19FD009C171E /* State.cpp */,
|
||||
4B1B58FE246E19FD009C171E /* State.hpp */,
|
||||
);
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1E85791D174DEC001EF87D /* 6532 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3863,6 +3876,7 @@
|
||||
children = (
|
||||
4BFF1D342233778C00838EA1 /* 68000.hpp */,
|
||||
4BFF1D36223379D500838EA1 /* Implementation */,
|
||||
4B1B58FC246E19FD009C171E /* State */,
|
||||
);
|
||||
path = 68000;
|
||||
sourceTree = "<group>";
|
||||
@ -4354,6 +4368,7 @@
|
||||
4B055AAA1FAE85F50060FFFF /* CPM.cpp in Sources */,
|
||||
4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */,
|
||||
4B0ACC3123775819008902D0 /* TIASound.cpp in Sources */,
|
||||
4B1B5900246E19FD009C171E /* State.cpp in Sources */,
|
||||
4BC57CDA2436A62900FBC404 /* State.cpp in Sources */,
|
||||
4B055ACB1FAE9AFB0060FFFF /* SerialBus.cpp in Sources */,
|
||||
4B8318B122D3E53A006DB630 /* DiskIICard.cpp in Sources */,
|
||||
@ -4617,6 +4632,7 @@
|
||||
4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */,
|
||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */,
|
||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
||||
4B1B58FF246E19FD009C171E /* State.cpp in Sources */,
|
||||
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */,
|
||||
4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */,
|
||||
4BC57CD92436A62900FBC404 /* State.cpp in Sources */,
|
||||
|
@ -85,6 +85,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/6502/State/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/State/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/Z80/State/*.cpp')
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef State_hpp
|
||||
#define State_hpp
|
||||
#ifndef MOS6502_State_hpp
|
||||
#define MOS6502_State_hpp
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
@ -93,4 +93,4 @@ struct State: public Reflection::StructImpl<State> {
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* State_hpp */
|
||||
#endif /* MOS6502_State_hpp */
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#define get_ccr() \
|
||||
#define ccr() \
|
||||
( \
|
||||
(carry_flag_ ? 0x0001 : 0x0000) | \
|
||||
(overflow_flag_ ? 0x0002 : 0x0000) | \
|
||||
@ -15,23 +15,23 @@
|
||||
(extend_flag_ ? 0x0010 : 0x0000) \
|
||||
)
|
||||
|
||||
#define get_status() \
|
||||
#define status() \
|
||||
uint16_t( \
|
||||
get_ccr() | \
|
||||
ccr() | \
|
||||
(interrupt_level_ << 8) | \
|
||||
(trace_flag_ ? 0x8000 : 0x0000) | \
|
||||
(is_supervisor_ << 13) \
|
||||
)
|
||||
|
||||
#define set_ccr(x) \
|
||||
#define apply_ccr(x) \
|
||||
carry_flag_ = (x) & 0x0001; \
|
||||
overflow_flag_ = (x) & 0x0002; \
|
||||
zero_result_ = ((x) & 0x0004) ^ 0x0004; \
|
||||
negative_flag_ = (x) & 0x0008; \
|
||||
extend_flag_ = (x) & 0x0010;
|
||||
|
||||
#define set_status(x) \
|
||||
set_ccr(x) \
|
||||
#define apply_status(x) \
|
||||
apply_ccr(x) \
|
||||
interrupt_level_ = ((x) >> 8) & 7; \
|
||||
trace_flag_ = (x) & 0x8000; \
|
||||
set_is_supervisor(!!(((x) >> 13) & 1));
|
||||
@ -101,7 +101,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
const auto offending_address = *active_step_->microcycle.address;
|
||||
active_program_ = nullptr;
|
||||
active_micro_op_ = long_exception_micro_ops_;
|
||||
populate_bus_error_steps(2, get_status(), get_bus_code(), offending_address);
|
||||
populate_bus_error_steps(2, status(), get_bus_code(), offending_address);
|
||||
program_counter_.full -= 4;
|
||||
active_step_ = &all_bus_steps_[active_micro_op_->bus_program];
|
||||
}
|
||||
@ -116,7 +116,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
const auto offending_address = *active_step_->microcycle.address;
|
||||
active_program_ = nullptr;
|
||||
active_micro_op_ = long_exception_micro_ops_;
|
||||
populate_bus_error_steps(3, get_status(), get_bus_code(), offending_address);
|
||||
populate_bus_error_steps(3, status(), get_bus_code(), offending_address);
|
||||
program_counter_.full -= 4;
|
||||
active_step_ = &all_bus_steps_[active_micro_op_->bus_program];
|
||||
|
||||
@ -210,7 +210,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
if(bus_interrupt_level_ > interrupt_level_) {
|
||||
pending_interrupt_level_ = bus_interrupt_level_;
|
||||
program_counter_.full += 4; // Don't return to this stop.
|
||||
execution_state_ = ExecutionState::BeginInterrupt;
|
||||
execution_state_ = ExecutionState::WillBeginInterrupt;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
bus_handler_.perform_bus_operation(stop_cycle_, is_supervisor_);
|
||||
continue;
|
||||
|
||||
case ExecutionState::BeginInterrupt:
|
||||
case ExecutionState::WillBeginInterrupt:
|
||||
#ifdef LOG_TRACE
|
||||
// should_log = true;
|
||||
#endif
|
||||
@ -269,7 +269,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// no instruction was ongoing. Either way, do a standard instruction operation.
|
||||
|
||||
if(pending_interrupt_level_) {
|
||||
execution_state_ = ExecutionState::BeginInterrupt;
|
||||
execution_state_ = ExecutionState::WillBeginInterrupt;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// The user has set the trace bit in the status register.
|
||||
active_program_ = nullptr;
|
||||
active_micro_op_ = short_exception_micro_ops_;
|
||||
populate_trap_steps(9, get_status());
|
||||
populate_trap_steps(9, status());
|
||||
program_counter_.full -= 4;
|
||||
} else {
|
||||
#ifdef LOG_TRACE
|
||||
@ -302,7 +302,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
#ifndef NDEBUG
|
||||
/* Debugging feature: reset the effective addresses and data latches, so that it's
|
||||
more obvious if some of the instructions aren't properly feeding them. */
|
||||
effective_address_[0].full = effective_address_[1].full = source_bus_data_[0].full = destination_bus_data_[0].full = 0x12344321;
|
||||
effective_address_[0].full = effective_address_[1].full = source_bus_data_.full = destination_bus_data_.full = 0x12344321;
|
||||
#endif
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
@ -331,7 +331,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// A privilege violation has been detected.
|
||||
active_program_ = nullptr;
|
||||
active_micro_op_ = short_exception_micro_ops_;
|
||||
populate_trap_steps(8, get_status());
|
||||
populate_trap_steps(8, status());
|
||||
} else {
|
||||
// Standard instruction dispatch.
|
||||
active_program_ = &instructions[decoded_instruction_.full];
|
||||
@ -353,13 +353,13 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// or one on the A or F lines.
|
||||
switch(decoded_instruction_.full >> 12) {
|
||||
default:
|
||||
populate_trap_steps(4, get_status());
|
||||
populate_trap_steps(4, status());
|
||||
break;
|
||||
case 0xa:
|
||||
populate_trap_steps(10, get_status());
|
||||
populate_trap_steps(10, status());
|
||||
break;
|
||||
case 0xf:
|
||||
populate_trap_steps(11, get_status());
|
||||
populate_trap_steps(11, status());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -829,7 +829,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
|
||||
// JMP: copies the source bus data to the program counter.
|
||||
case Operation::RTS:
|
||||
program_counter_ = source_bus_data_[0];
|
||||
program_counter_ = source_bus_data_;
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -879,7 +879,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
break;
|
||||
|
||||
case Operation::PEA:
|
||||
destination_bus_data_[0] = effective_address_[0];
|
||||
destination_bus_data_ = effective_address_[0];
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -887,15 +887,15 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
*/
|
||||
|
||||
case Operation::MOVEtoSR:
|
||||
set_status(source()->full);
|
||||
apply_status(source()->full);
|
||||
break;
|
||||
|
||||
case Operation::MOVEfromSR:
|
||||
destination()->halves.low.full = get_status();
|
||||
destination()->halves.low.full = status();
|
||||
break;
|
||||
|
||||
case Operation::MOVEtoCCR:
|
||||
set_ccr(source()->full);
|
||||
apply_ccr(source()->full);
|
||||
break;
|
||||
|
||||
case Operation::EXTbtow:
|
||||
@ -919,25 +919,25 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
#define eor_op(a, b) a ^= b
|
||||
|
||||
#define apply(op, func) {\
|
||||
auto status = get_status(); \
|
||||
auto status = status(); \
|
||||
op(status, prefetch_queue_.halves.high.full); \
|
||||
func(status); \
|
||||
program_counter_.full -= 2; \
|
||||
}
|
||||
|
||||
#define apply_sr(op) apply(op, set_status)
|
||||
#define apply_ccr(op) apply(op, set_ccr)
|
||||
#define apply_op_sr(op) apply(op, apply_status)
|
||||
#define apply_op_ccr(op) apply(op, apply_ccr)
|
||||
|
||||
case Operation::ANDItoSR: apply_sr(and_op); break;
|
||||
case Operation::EORItoSR: apply_sr(eor_op); break;
|
||||
case Operation::ORItoSR: apply_sr(or_op); break;
|
||||
case Operation::ANDItoSR: apply_op_sr(and_op); break;
|
||||
case Operation::EORItoSR: apply_op_sr(eor_op); break;
|
||||
case Operation::ORItoSR: apply_op_sr(or_op); break;
|
||||
|
||||
case Operation::ANDItoCCR: apply_ccr(and_op); break;
|
||||
case Operation::EORItoCCR: apply_ccr(eor_op); break;
|
||||
case Operation::ORItoCCR: apply_ccr(or_op); break;
|
||||
case Operation::ANDItoCCR: apply_op_ccr(and_op); break;
|
||||
case Operation::EORItoCCR: apply_op_ccr(eor_op); break;
|
||||
case Operation::ORItoCCR: apply_op_ccr(or_op); break;
|
||||
|
||||
#undef apply_ccr
|
||||
#undef apply_sr
|
||||
#undef apply_op_ccr
|
||||
#undef apply_op_sr
|
||||
#undef apply
|
||||
#undef eor_op
|
||||
#undef or_op
|
||||
@ -988,7 +988,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
active_micro_op_ = short_exception_micro_ops_; \
|
||||
bus_program = &all_bus_steps_[active_micro_op_->bus_program]; \
|
||||
\
|
||||
populate_trap_steps(5, get_status()); \
|
||||
populate_trap_steps(5, status()); \
|
||||
bus_program->microcycle.length = HalfCycles(20); \
|
||||
\
|
||||
program_counter_.full -= 2;
|
||||
@ -1119,30 +1119,30 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
|
||||
case Operation::MOVEPtoMw:
|
||||
// Write pattern is nW+ nw, which should write the low word of the source in big-endian form.
|
||||
destination_bus_data_[0].halves.high.full = source()->halves.low.halves.high;
|
||||
destination_bus_data_[0].halves.low.full = source()->halves.low.halves.low;
|
||||
destination_bus_data_.halves.high.full = source()->halves.low.halves.high;
|
||||
destination_bus_data_.halves.low.full = source()->halves.low.halves.low;
|
||||
break;
|
||||
|
||||
case Operation::MOVEPtoMl:
|
||||
// Write pattern is nW+ nWr+ nw+ nwr, which should write the source in big-endian form.
|
||||
destination_bus_data_[0].halves.high.full = source()->halves.high.halves.high;
|
||||
source_bus_data_[0].halves.high.full = source()->halves.high.halves.low;
|
||||
destination_bus_data_[0].halves.low.full = source()->halves.low.halves.high;
|
||||
source_bus_data_[0].halves.low.full = source()->halves.low.halves.low;
|
||||
destination_bus_data_.halves.high.full = source()->halves.high.halves.high;
|
||||
source_bus_data_.halves.high.full = source()->halves.high.halves.low;
|
||||
destination_bus_data_.halves.low.full = source()->halves.low.halves.high;
|
||||
source_bus_data_.halves.low.full = source()->halves.low.halves.low;
|
||||
break;
|
||||
|
||||
case Operation::MOVEPtoRw:
|
||||
// Read pattern is nRd+ nrd.
|
||||
source()->halves.low.halves.high = destination_bus_data_[0].halves.high.halves.low;
|
||||
source()->halves.low.halves.low = destination_bus_data_[0].halves.low.halves.low;
|
||||
source()->halves.low.halves.high = destination_bus_data_.halves.high.halves.low;
|
||||
source()->halves.low.halves.low = destination_bus_data_.halves.low.halves.low;
|
||||
break;
|
||||
|
||||
case Operation::MOVEPtoRl:
|
||||
// Read pattern is nRd+ nR+ nrd+ nr.
|
||||
source()->halves.high.halves.high = destination_bus_data_[0].halves.high.halves.low;
|
||||
source()->halves.high.halves.low = source_bus_data_[0].halves.high.halves.low;
|
||||
source()->halves.low.halves.high = destination_bus_data_[0].halves.low.halves.low;
|
||||
source()->halves.low.halves.low = source_bus_data_[0].halves.low.halves.low;
|
||||
source()->halves.high.halves.high = destination_bus_data_.halves.high.halves.low;
|
||||
source()->halves.high.halves.low = source_bus_data_.halves.high.halves.low;
|
||||
source()->halves.low.halves.high = destination_bus_data_.halves.low.halves.low;
|
||||
source()->halves.low.halves.low = source_bus_data_.halves.low.halves.low;
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -1297,7 +1297,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
case Operation::TRAP: {
|
||||
// Select the trap steps as next; the initial microcycle should be 4 cycles long.
|
||||
bus_program = trap_steps_;
|
||||
populate_trap_steps((decoded_instruction_.full & 15) + 32, get_status());
|
||||
populate_trap_steps((decoded_instruction_.full & 15) + 32, status());
|
||||
set_next_microcycle_length(HalfCycles(12));
|
||||
|
||||
// The program counter to push is actually one slot ago.
|
||||
@ -1308,7 +1308,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
if(overflow_flag_) {
|
||||
// Select the trap steps as next; the initial microcycle should be skipped.
|
||||
bus_program = trap_steps_;
|
||||
populate_trap_steps(7, get_status());
|
||||
populate_trap_steps(7, status());
|
||||
set_next_microcycle_length(HalfCycles(4));
|
||||
|
||||
// Push the address after the TRAPV.
|
||||
@ -1334,7 +1334,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// exception is necessary.
|
||||
if(is_under || is_over) {
|
||||
bus_program = trap_steps_;
|
||||
populate_trap_steps(6, get_status());
|
||||
populate_trap_steps(6, status());
|
||||
if(is_over) {
|
||||
set_next_microcycle_length(HalfCycles(20));
|
||||
} else {
|
||||
@ -1448,7 +1448,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
effective_address_[1].full = address_[7].full;
|
||||
|
||||
// The current value of the address register will be pushed.
|
||||
destination_bus_data_[0].full = source()->full;
|
||||
destination_bus_data_.full = source()->full;
|
||||
|
||||
// The address register will then contain the bottom of the stack,
|
||||
// and the stack pointer will be offset.
|
||||
@ -1458,7 +1458,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
|
||||
case Operation::UNLINK:
|
||||
address_[7].full = effective_address_[1].full + 2;
|
||||
destination()->full = destination_bus_data_[0].full;
|
||||
destination()->full = destination_bus_data_.full;
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -1858,11 +1858,11 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
case Operation::RTE_RTR:
|
||||
// If this is RTR, patch out the supervisor half of the status register.
|
||||
if(decoded_instruction_.full == 0x4e77) {
|
||||
const auto current_status = get_status();
|
||||
source_bus_data_[0].halves.low.halves.high =
|
||||
const auto current_status = status();
|
||||
source_bus_data_.halves.low.halves.high =
|
||||
uint8_t(current_status >> 8);
|
||||
}
|
||||
set_status(source_bus_data_[0].full);
|
||||
apply_status(source_bus_data_.full);
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -1888,7 +1888,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
break;
|
||||
|
||||
case Operation::STOP:
|
||||
set_status(prefetch_queue_.halves.low.full);
|
||||
apply_status(prefetch_queue_.halves.low.full);
|
||||
execution_state_ = ExecutionState::Stopped;
|
||||
break;
|
||||
|
||||
@ -1938,9 +1938,9 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
const auto mode = (decoded_instruction_.full >> 3) & 7;
|
||||
// Determine the proper resumption address.
|
||||
switch(mode) {
|
||||
case 2: destination_bus_data_[0].full = program_counter_.full - 2; break; /* (An) */
|
||||
case 2: destination_bus_data_.full = program_counter_.full - 2; break; /* (An) */
|
||||
default:
|
||||
destination_bus_data_[0].full = program_counter_.full; /* Everything other than (An) */
|
||||
destination_bus_data_.full = program_counter_.full; /* Everything other than (An) */
|
||||
break;
|
||||
}
|
||||
address_[7].full -= 4;
|
||||
@ -1948,7 +1948,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
} break;
|
||||
|
||||
case int_type(MicroOp::Action::PrepareBSR):
|
||||
destination_bus_data_[0].full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full;
|
||||
destination_bus_data_.full = (decoded_instruction_.full & 0xff) ? program_counter_.full - 2 : program_counter_.full;
|
||||
address_[7].full -= 4;
|
||||
effective_address_[1].full = address_[7].full;
|
||||
break;
|
||||
@ -1968,7 +1968,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
case int_type(MicroOp::Action::PrepareINT):
|
||||
// The INT sequence uses the same storage as the TRAP steps, so this'll get
|
||||
// the necessary stack work set up.
|
||||
populate_trap_steps(0, get_status());
|
||||
populate_trap_steps(0, status());
|
||||
|
||||
// Mutate neessary internal state — effective_address_[0] is exposed
|
||||
// on the data bus as the accepted interrupt number during the interrupt
|
||||
@ -2002,7 +2002,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
}
|
||||
|
||||
// Otherwise, the vector is whatever we were just told it is.
|
||||
effective_address_[0].full = uint32_t(source_bus_data_[0].halves.low.halves.low << 2);
|
||||
effective_address_[0].full = uint32_t(source_bus_data_.halves.low.halves.low << 2);
|
||||
|
||||
// printf("Interrupt vector: %06x\n", effective_address_[0].full);
|
||||
break;
|
||||
@ -2143,19 +2143,19 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
break;
|
||||
|
||||
case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask:
|
||||
source_bus_data_[0] = prefetch_queue_.halves.low.full;
|
||||
source_bus_data_ = prefetch_queue_.halves.low.full;
|
||||
break;
|
||||
|
||||
case int_type(MicroOp::Action::AssembleWordDataFromPrefetch) | MicroOp::DestinationMask:
|
||||
destination_bus_data_[0] = prefetch_queue_.halves.low.full;
|
||||
destination_bus_data_ = prefetch_queue_.halves.low.full;
|
||||
break;
|
||||
|
||||
case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask:
|
||||
source_bus_data_[0] = prefetch_queue_.full;
|
||||
source_bus_data_ = prefetch_queue_.full;
|
||||
break;
|
||||
|
||||
case int_type(MicroOp::Action::AssembleLongWordDataFromPrefetch) | MicroOp::DestinationMask:
|
||||
destination_bus_data_[0] = prefetch_queue_.full;
|
||||
destination_bus_data_ = prefetch_queue_.full;
|
||||
break;
|
||||
|
||||
case int_type(MicroOp::Action::CopyToEffectiveAddress) | MicroOp::SourceMask:
|
||||
@ -2202,7 +2202,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> ProcessorSt
|
||||
state.supervisor_stack_pointer = stack_pointers_[1].full;
|
||||
state.program_counter = program_counter_.full;
|
||||
|
||||
state.status = get_status();
|
||||
state.status = status();
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -2211,17 +2211,25 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
memcpy(data_, state.data, sizeof(state.data));
|
||||
memcpy(address_, state.address, sizeof(state.address));
|
||||
|
||||
set_status(state.status);
|
||||
apply_status(state.status);
|
||||
|
||||
stack_pointers_[0].full = state.user_stack_pointer;
|
||||
stack_pointers_[1].full = state.supervisor_stack_pointer;
|
||||
address_[7] = stack_pointers_[is_supervisor_];
|
||||
}
|
||||
|
||||
#undef get_status
|
||||
#undef set_status
|
||||
#undef set_ccr
|
||||
#undef get_ccr
|
||||
uint16_t ProcessorStorage::get_status() const {
|
||||
return status();
|
||||
}
|
||||
|
||||
void ProcessorStorage::set_status(uint16_t status) {
|
||||
apply_status(status);
|
||||
}
|
||||
|
||||
#undef status
|
||||
#undef apply_status
|
||||
#undef apply_ccr
|
||||
#undef ccr
|
||||
#undef u_extend16
|
||||
#undef u_extend8
|
||||
#undef s_extend16
|
||||
|
@ -300,7 +300,7 @@ struct ProcessorStorageConstructor {
|
||||
if(tolower(access_pattern[1]) == 's') {
|
||||
step.microcycle.operation = Microcycle::NewAddress;
|
||||
step.microcycle.address = &storage_.effective_address_[1].full;
|
||||
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_[0].halves.high : &storage_.destination_bus_data_[0].halves.low;
|
||||
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_.halves.high : &storage_.destination_bus_data_.halves.low;
|
||||
steps.push_back(step);
|
||||
|
||||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord;
|
||||
@ -312,7 +312,7 @@ struct ProcessorStorageConstructor {
|
||||
|
||||
// A stack read.
|
||||
if(tolower(access_pattern[1]) == 'u') {
|
||||
RegisterPair32 *const scratch_data = &storage_.source_bus_data_[0];
|
||||
RegisterPair32 *const scratch_data = &storage_.source_bus_data_;
|
||||
|
||||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read;
|
||||
step.microcycle.address = &storage_.effective_address_[0].full;
|
||||
@ -350,7 +350,7 @@ struct ProcessorStorageConstructor {
|
||||
) {
|
||||
const bool is_read = tolower(access_pattern[1]) == 'r';
|
||||
const bool use_source_storage = tolower(end_of_pattern[-1]) == 'r';
|
||||
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_[0] : &storage_.destination_bus_data_[0];
|
||||
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_ : &storage_.destination_bus_data_;
|
||||
|
||||
assert(address_iterator != addresses.end());
|
||||
|
||||
@ -377,7 +377,7 @@ struct ProcessorStorageConstructor {
|
||||
if(token_length == 3) {
|
||||
// The completing part of a TAS.
|
||||
if(access_pattern[0] == 't' && access_pattern[1] == 'a' && access_pattern[2] == 's') {
|
||||
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0];
|
||||
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_;
|
||||
|
||||
assert(address_iterator != addresses.end());
|
||||
|
||||
@ -399,7 +399,7 @@ struct ProcessorStorageConstructor {
|
||||
if(access_pattern[0] == 'i' && access_pattern[1] == 'n' && access_pattern[2] == 't') {
|
||||
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::NewAddress;
|
||||
step.microcycle.address = &storage_.effective_address_[0].full; // The selected interrupt should be in bits 1–3; but 0 should be set.
|
||||
step.microcycle.value = &storage_.source_bus_data_[0].halves.low;
|
||||
step.microcycle.value = &storage_.source_bus_data_.halves.low;
|
||||
steps.push_back(step);
|
||||
|
||||
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte;
|
||||
@ -3250,20 +3250,20 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
|
||||
//
|
||||
// Order of output is: PC.l, SR, PC.h.
|
||||
trap_steps_ = &all_bus_steps_[trap_offset];
|
||||
constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high });
|
||||
constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high });
|
||||
|
||||
// Fill in the same order of writes for the interrupt micro-ops, though it divides the work differently.
|
||||
constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_[0].halves.low, &program_counter_.halves.high });
|
||||
constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high });
|
||||
|
||||
// Link the bus error exception steps and fill in the proper sources.
|
||||
bus_error_steps_ = &all_bus_steps_[bus_error_offset];
|
||||
constructor.replace_write_values(bus_error_steps_, {
|
||||
&program_counter_.halves.low,
|
||||
&destination_bus_data_[0].halves.low,
|
||||
&destination_bus_data_.halves.low,
|
||||
&program_counter_.halves.high,
|
||||
&decoded_instruction_,
|
||||
&effective_address_[1].halves.low,
|
||||
&destination_bus_data_[0].halves.high,
|
||||
&destination_bus_data_.halves.high,
|
||||
&effective_address_[1].halves.high
|
||||
});
|
||||
|
||||
|
@ -38,12 +38,12 @@ class ProcessorStorage {
|
||||
Halted,
|
||||
|
||||
/// Signals a transition from some other straight directly to cueing up an interrupt.
|
||||
BeginInterrupt,
|
||||
WillBeginInterrupt,
|
||||
} execution_state_ = ExecutionState::Executing;
|
||||
Microcycle dtack_cycle_;
|
||||
Microcycle stop_cycle_;
|
||||
|
||||
// Various status bits.
|
||||
// Various status parts.
|
||||
int is_supervisor_;
|
||||
int interrupt_level_;
|
||||
uint_fast32_t zero_result_; // The zero flag is set if this value is zero.
|
||||
@ -75,8 +75,8 @@ class ProcessorStorage {
|
||||
// Generic sources and targets for memory operations;
|
||||
// by convention: [0] = source, [1] = destination.
|
||||
RegisterPair32 effective_address_[2];
|
||||
RegisterPair32 source_bus_data_[1];
|
||||
RegisterPair32 destination_bus_data_[1];
|
||||
RegisterPair32 source_bus_data_;
|
||||
RegisterPair32 destination_bus_data_;
|
||||
|
||||
HalfCycles half_cycles_left_to_run_;
|
||||
HalfCycles e_clock_phase_;
|
||||
@ -394,18 +394,18 @@ class ProcessorStorage {
|
||||
void set_source(ProcessorStorage &storage, int mode, int reg) {
|
||||
set_source_address(storage, reg);
|
||||
switch(mode) {
|
||||
case 0: set_source(storage, &storage.data_[reg]); break;
|
||||
case 1: set_source(storage, &storage.address_[reg]); break;
|
||||
default: set_source(storage, &storage.source_bus_data_[0]); break;
|
||||
case 0: set_source(storage, &storage.data_[reg]); break;
|
||||
case 1: set_source(storage, &storage.address_[reg]); break;
|
||||
default: set_source(storage, &storage.source_bus_data_); break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_destination(ProcessorStorage &storage, int mode, int reg) {
|
||||
set_destination_address(storage, reg);
|
||||
switch(mode) {
|
||||
case 0: set_destination(storage, &storage.data_[reg]); break;
|
||||
case 1: set_destination(storage, &storage.address_[reg]); break;
|
||||
default: set_destination(storage, &storage.destination_bus_data_[0]); break;
|
||||
case 0: set_destination(storage, &storage.data_[reg]); break;
|
||||
case 1: set_destination(storage, &storage.address_[reg]); break;
|
||||
default: set_destination(storage, &storage.destination_bus_data_); break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -438,13 +438,15 @@ class ProcessorStorage {
|
||||
BusStep *movem_read_steps_;
|
||||
BusStep *movem_write_steps_;
|
||||
|
||||
// These two are dynamically modified depending on the particular
|
||||
// TRAP and bus error.
|
||||
BusStep *trap_steps_;
|
||||
BusStep *bus_error_steps_;
|
||||
|
||||
// Current bus step pointer, and outer program pointer.
|
||||
Program *active_program_ = nullptr;
|
||||
MicroOp *active_micro_op_ = nullptr;
|
||||
BusStep *active_step_ = nullptr;
|
||||
const Program *active_program_ = nullptr;
|
||||
const MicroOp *active_micro_op_ = nullptr;
|
||||
const BusStep *active_step_ = nullptr;
|
||||
RegisterPair16 decoded_instruction_ = 0;
|
||||
uint16_t next_word_ = 0;
|
||||
|
||||
@ -455,9 +457,9 @@ class ProcessorStorage {
|
||||
void set_is_supervisor(bool);
|
||||
|
||||
// Transient storage for MOVEM, TRAP and others.
|
||||
uint32_t precomputed_addresses_[65];
|
||||
RegisterPair16 throwaway_value_;
|
||||
uint32_t movem_final_address_;
|
||||
uint32_t precomputed_addresses_[65]; // This is a big chunk of rarely-used storage. It's placed last deliberately.
|
||||
|
||||
/*!
|
||||
Evaluates the conditional described by @c code and returns @c true or @c false to
|
||||
@ -496,7 +498,7 @@ class ProcessorStorage {
|
||||
*/
|
||||
forceinline void populate_trap_steps(uint32_t vector, uint16_t status) {
|
||||
// Fill in the status word value.
|
||||
destination_bus_data_[0].full = status;
|
||||
destination_bus_data_.full = status;
|
||||
|
||||
// Switch to supervisor mode, disable the trace bit.
|
||||
set_is_supervisor(true);
|
||||
@ -507,7 +509,7 @@ class ProcessorStorage {
|
||||
|
||||
// Schedule the proper stack activity.
|
||||
precomputed_addresses_[0] = address_[7].full - 2; // PC.l
|
||||
precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_[0])
|
||||
precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_)
|
||||
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
|
||||
address_[7].full -= 6;
|
||||
|
||||
@ -517,8 +519,8 @@ class ProcessorStorage {
|
||||
|
||||
forceinline void populate_bus_error_steps(uint32_t vector, uint16_t status, uint16_t bus_status, RegisterPair32 faulting_address) {
|
||||
// Fill in the status word value.
|
||||
destination_bus_data_[0].halves.low.full = status;
|
||||
destination_bus_data_[0].halves.high.full = bus_status;
|
||||
destination_bus_data_.halves.low.full = status;
|
||||
destination_bus_data_.halves.high.full = bus_status;
|
||||
effective_address_[1] = faulting_address;
|
||||
|
||||
// Switch to supervisor mode, disable the trace bit.
|
||||
@ -539,9 +541,13 @@ class ProcessorStorage {
|
||||
address_[7].full -= 14;
|
||||
}
|
||||
|
||||
inline uint16_t get_status() const;
|
||||
inline void set_status(uint16_t);
|
||||
|
||||
private:
|
||||
friend class ProcessorStorageConstructor;
|
||||
friend class ProcessorStorageTests;
|
||||
friend class State;
|
||||
};
|
||||
|
||||
#endif /* MC68000Storage_h */
|
||||
|
340
Processors/68000/State/State.cpp
Normal file
340
Processors/68000/State/State.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
//
|
||||
// State.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "State.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace CPU::MC68000;
|
||||
|
||||
State::State(const ProcessorBase &src): State() {
|
||||
// Registers.
|
||||
for(int c = 0; c < 7; ++c) {
|
||||
registers.address[c] = src.address_[c].full;
|
||||
registers.data[c] = src.data_[c].full;
|
||||
}
|
||||
registers.data[7] = src.data_[7].full;
|
||||
registers.user_stack_pointer = src.is_supervisor_ ? src.stack_pointers_[0].full : src.address_[7].full;
|
||||
registers.supervisor_stack_pointer = src.is_supervisor_ ? src.address_[7].full : src.stack_pointers_[1].full;
|
||||
registers.status = src.get_status();
|
||||
registers.program_counter = src.program_counter_.full;
|
||||
registers.prefetch = src.prefetch_queue_.full;
|
||||
registers.instruction = src.decoded_instruction_.full;
|
||||
|
||||
// Inputs.
|
||||
inputs.bus_interrupt_level = uint8_t(src.bus_interrupt_level_);
|
||||
inputs.dtack = src.dtack_;
|
||||
inputs.is_peripheral_address = src.is_peripheral_address_;
|
||||
inputs.bus_error = src.bus_error_;
|
||||
inputs.bus_request = src.bus_request_;
|
||||
inputs.bus_grant = false; // TODO (within the 68000).
|
||||
inputs.halt = src.halt_;
|
||||
|
||||
// Execution state.
|
||||
execution_state.e_clock_phase = src.e_clock_phase_.as<uint8_t>();
|
||||
execution_state.effective_address[0] = src.effective_address_[0].full;
|
||||
execution_state.effective_address[1] = src.effective_address_[1].full;
|
||||
execution_state.source_data = src.source_bus_data_.full;
|
||||
execution_state.destination_data = src.destination_bus_data_.full;
|
||||
execution_state.last_trace_flag = src.last_trace_flag_;
|
||||
execution_state.next_word = src.next_word_;
|
||||
execution_state.dbcc_false_address = src.dbcc_false_address_;
|
||||
execution_state.is_starting_interrupt = src.is_starting_interrupt_;
|
||||
execution_state.pending_interrupt_level = uint8_t(src.pending_interrupt_level_);
|
||||
execution_state.accepted_interrupt_level = uint8_t(src.accepted_interrupt_level_);
|
||||
execution_state.movem_final_address = src.movem_final_address_;
|
||||
|
||||
static_assert(sizeof(execution_state.source_addresses) == sizeof(src.precomputed_addresses_));
|
||||
memcpy(&execution_state.source_addresses, &src.precomputed_addresses_, sizeof(src.precomputed_addresses_));
|
||||
|
||||
// This is collapsed to a Boolean; if there is an active program then it's the
|
||||
// one implied by the current instruction.
|
||||
execution_state.active_program = src.active_program_;
|
||||
|
||||
// Slightly dodgy assumption here: the Phase enum will always exactly track
|
||||
// the 68000's ExecutionState enum.
|
||||
execution_state.phase = ExecutionState::Phase(src.execution_state_);
|
||||
|
||||
auto contained_by = [](const auto *source, const auto *reference) -> bool {
|
||||
while(true) {
|
||||
if(source == reference) return true;
|
||||
if(source->is_terminal()) return false;
|
||||
++source;
|
||||
}
|
||||
};
|
||||
|
||||
// Store enough information to relocate the MicroOp.
|
||||
const ProcessorBase::MicroOp *micro_op_base = nullptr;
|
||||
if(src.active_program_) {
|
||||
micro_op_base = &src.all_micro_ops_[src.instructions[src.decoded_instruction_.full].micro_operations];
|
||||
assert(contained_by(micro_op_base, src.active_micro_op_));
|
||||
execution_state.micro_op_source = ExecutionState::MicroOpSource::ActiveProgram;
|
||||
} else {
|
||||
if(contained_by(src.long_exception_micro_ops_, src.active_micro_op_)) {
|
||||
execution_state.micro_op_source = ExecutionState::MicroOpSource::LongException;
|
||||
micro_op_base = src.long_exception_micro_ops_;
|
||||
} else if(contained_by(src.short_exception_micro_ops_, src.active_micro_op_)) {
|
||||
execution_state.micro_op_source = ExecutionState::MicroOpSource::ShortException;
|
||||
micro_op_base = src.short_exception_micro_ops_;
|
||||
} else if(contained_by(src.interrupt_micro_ops_, src.active_micro_op_)) {
|
||||
execution_state.micro_op_source = ExecutionState::MicroOpSource::Interrupt;
|
||||
micro_op_base = src.interrupt_micro_ops_;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
execution_state.micro_op = uint8_t(src.active_micro_op_ - micro_op_base);
|
||||
|
||||
// Encode the BusStep.
|
||||
struct BusStepOption {
|
||||
const ProcessorBase::BusStep *const base;
|
||||
const ExecutionState::BusStepSource source;
|
||||
};
|
||||
BusStepOption bus_step_options[] = {
|
||||
{
|
||||
src.reset_bus_steps_,
|
||||
ExecutionState::BusStepSource::Reset
|
||||
},
|
||||
{
|
||||
src.branch_taken_bus_steps_,
|
||||
ExecutionState::BusStepSource::BranchTaken
|
||||
},
|
||||
{
|
||||
src.branch_byte_not_taken_bus_steps_,
|
||||
ExecutionState::BusStepSource::BranchByteNotTaken
|
||||
},
|
||||
{
|
||||
src.branch_word_not_taken_bus_steps_,
|
||||
ExecutionState::BusStepSource::BranchWordNotTaken
|
||||
},
|
||||
{
|
||||
src.bsr_bus_steps_,
|
||||
ExecutionState::BusStepSource::BSR
|
||||
},
|
||||
{
|
||||
src.dbcc_condition_true_steps_,
|
||||
ExecutionState::BusStepSource::DBccConditionTrue
|
||||
},
|
||||
{
|
||||
src.dbcc_condition_false_no_branch_steps_,
|
||||
ExecutionState::BusStepSource::DBccConditionFalseNoBranch
|
||||
},
|
||||
{
|
||||
src.dbcc_condition_false_branch_steps_,
|
||||
ExecutionState::BusStepSource::DBccConditionFalseBranch
|
||||
},
|
||||
{
|
||||
src.movem_read_steps_,
|
||||
ExecutionState::BusStepSource::MovemRead
|
||||
},
|
||||
{
|
||||
src.movem_write_steps_,
|
||||
ExecutionState::BusStepSource::MovemWrite
|
||||
},
|
||||
{
|
||||
src.trap_steps_,
|
||||
ExecutionState::BusStepSource::Trap
|
||||
},
|
||||
{
|
||||
src.bus_error_steps_,
|
||||
ExecutionState::BusStepSource::BusError
|
||||
},
|
||||
{
|
||||
&src.all_bus_steps_[src.active_micro_op_->bus_program],
|
||||
ExecutionState::BusStepSource::FollowMicroOp
|
||||
},
|
||||
{nullptr}
|
||||
};
|
||||
const BusStepOption *bus_step_option = bus_step_options;
|
||||
const ProcessorBase::BusStep *bus_step_base = nullptr;
|
||||
while(bus_step_option->base) {
|
||||
if(contained_by(bus_step_option->base, src.active_step_)) {
|
||||
bus_step_base = bus_step_option->base;
|
||||
execution_state.bus_step_source = bus_step_option->source;
|
||||
break;
|
||||
}
|
||||
++bus_step_option;
|
||||
}
|
||||
assert(bus_step_base);
|
||||
execution_state.bus_step = uint8_t(src.active_step_ - bus_step_base);
|
||||
}
|
||||
|
||||
void State::apply(ProcessorBase &target) {
|
||||
// Registers.
|
||||
for(int c = 0; c < 7; ++c) {
|
||||
target.address_[c].full = registers.address[c];
|
||||
target.data_[c].full = registers.data[c];
|
||||
}
|
||||
target.data_[7].full = registers.data[7];
|
||||
target.stack_pointers_[0] = registers.user_stack_pointer;
|
||||
target.stack_pointers_[1] = registers.supervisor_stack_pointer;
|
||||
target.address_[7] = target.stack_pointers_[(registers.status & 0x2000) >> 13];
|
||||
target.set_status(registers.status);
|
||||
target.program_counter_.full = registers.program_counter;
|
||||
target.prefetch_queue_.full = registers.prefetch;
|
||||
target.decoded_instruction_.full = registers.instruction;
|
||||
|
||||
// Inputs.
|
||||
target.bus_interrupt_level_ = inputs.bus_interrupt_level;
|
||||
target.dtack_ = inputs.dtack;
|
||||
target.is_peripheral_address_ = inputs.is_peripheral_address;
|
||||
target.bus_error_ = inputs.bus_error;
|
||||
target.bus_request_ = inputs.bus_request;
|
||||
// TODO: bus_grant.
|
||||
target.halt_ = inputs.halt;
|
||||
|
||||
// Execution state.
|
||||
target.e_clock_phase_ = HalfCycles(execution_state.e_clock_phase);
|
||||
target.effective_address_[0].full = execution_state.effective_address[0];
|
||||
target.effective_address_[1].full = execution_state.effective_address[1];
|
||||
target.source_bus_data_.full = execution_state.source_data;
|
||||
target.destination_bus_data_.full = execution_state.destination_data;
|
||||
target.last_trace_flag_ = execution_state.last_trace_flag;
|
||||
target.next_word_ = execution_state.next_word;
|
||||
target.dbcc_false_address_ = execution_state.dbcc_false_address;
|
||||
target.is_starting_interrupt_ = execution_state.is_starting_interrupt;
|
||||
target.pending_interrupt_level_ = execution_state.pending_interrupt_level;
|
||||
target.accepted_interrupt_level_ = execution_state.accepted_interrupt_level;
|
||||
target.movem_final_address_ = execution_state.movem_final_address;
|
||||
|
||||
static_assert(sizeof(execution_state.source_addresses) == sizeof(target.precomputed_addresses_));
|
||||
memcpy(&target.precomputed_addresses_, &execution_state.source_addresses, sizeof(target.precomputed_addresses_));
|
||||
|
||||
// See above; this flag indicates whether to populate the field.
|
||||
target.active_program_ =
|
||||
execution_state.active_program ?
|
||||
&target.instructions[target.decoded_instruction_.full] : nullptr;
|
||||
|
||||
// Dodgy assumption duplicated here from above.
|
||||
target.execution_state_ = CPU::MC68000::ProcessorStorage::ExecutionState(execution_state.phase);
|
||||
|
||||
// Decode the MicroOp.
|
||||
switch(execution_state.micro_op_source) {
|
||||
case ExecutionState::MicroOpSource::ActiveProgram:
|
||||
target.active_micro_op_ = &target.all_micro_ops_[target.active_program_->micro_operations];
|
||||
break;
|
||||
case ExecutionState::MicroOpSource::LongException:
|
||||
target.active_micro_op_ = target.long_exception_micro_ops_;
|
||||
break;
|
||||
case ExecutionState::MicroOpSource::ShortException:
|
||||
target.active_micro_op_ = target.short_exception_micro_ops_;
|
||||
break;
|
||||
case ExecutionState::MicroOpSource::Interrupt:
|
||||
target.active_micro_op_ = target.interrupt_micro_ops_;
|
||||
break;
|
||||
}
|
||||
target.active_micro_op_ += execution_state.micro_op;
|
||||
|
||||
|
||||
// Decode the BusStep.
|
||||
switch(execution_state.bus_step_source) {
|
||||
case ExecutionState::BusStepSource::Reset:
|
||||
target.active_step_ = target.reset_bus_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::BranchTaken:
|
||||
target.active_step_ = target.branch_taken_bus_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::BranchByteNotTaken:
|
||||
target.active_step_ = target.branch_byte_not_taken_bus_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::BranchWordNotTaken:
|
||||
target.active_step_ = target.branch_word_not_taken_bus_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::BSR:
|
||||
target.active_step_ = target.bsr_bus_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::DBccConditionTrue:
|
||||
target.active_step_ = target.dbcc_condition_true_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::DBccConditionFalseNoBranch:
|
||||
target.active_step_ = target.dbcc_condition_false_no_branch_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::DBccConditionFalseBranch:
|
||||
target.active_step_ = target.dbcc_condition_false_branch_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::MovemRead:
|
||||
target.active_step_ = target.movem_read_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::MovemWrite:
|
||||
target.active_step_ = target.movem_write_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::Trap:
|
||||
target.active_step_ = target.trap_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::BusError:
|
||||
target.active_step_ = target.bus_error_steps_;
|
||||
break;
|
||||
case ExecutionState::BusStepSource::FollowMicroOp:
|
||||
target.active_step_ = &target.all_bus_steps_[target.active_micro_op_->bus_program];
|
||||
break;
|
||||
}
|
||||
target.active_step_ += execution_state.bus_step;
|
||||
}
|
||||
|
||||
// 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(data);
|
||||
DeclareField(address);
|
||||
DeclareField(user_stack_pointer);
|
||||
DeclareField(supervisor_stack_pointer);
|
||||
DeclareField(status);
|
||||
DeclareField(program_counter);
|
||||
DeclareField(prefetch);
|
||||
DeclareField(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
State::Inputs::Inputs() {
|
||||
if(needs_declare()) {
|
||||
DeclareField(bus_interrupt_level);
|
||||
DeclareField(dtack);
|
||||
DeclareField(is_peripheral_address);
|
||||
DeclareField(bus_error);
|
||||
DeclareField(bus_request);
|
||||
DeclareField(bus_grant);
|
||||
DeclareField(halt);
|
||||
}
|
||||
}
|
||||
|
||||
State::ExecutionState::ExecutionState() {
|
||||
if(needs_declare()) {
|
||||
DeclareField(e_clock_phase);
|
||||
DeclareField(effective_address);
|
||||
DeclareField(source_data);
|
||||
DeclareField(destination_data);
|
||||
DeclareField(last_trace_flag);
|
||||
DeclareField(next_word);
|
||||
DeclareField(dbcc_false_address);
|
||||
DeclareField(is_starting_interrupt);
|
||||
DeclareField(pending_interrupt_level);
|
||||
DeclareField(accepted_interrupt_level);
|
||||
DeclareField(active_program);
|
||||
DeclareField(movem_final_address);
|
||||
DeclareField(source_addresses);
|
||||
|
||||
AnnounceEnum(Phase);
|
||||
DeclareField(phase);
|
||||
|
||||
AnnounceEnum(MicroOpSource);
|
||||
DeclareField(micro_op_source);
|
||||
DeclareField(micro_op);
|
||||
|
||||
AnnounceEnum(BusStepSource);
|
||||
DeclareField(bus_step_source);
|
||||
DeclareField(bus_step);
|
||||
}
|
||||
}
|
134
Processors/68000/State/State.hpp
Normal file
134
Processors/68000/State/State.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
//
|
||||
// State.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MC68000_State_hpp
|
||||
#define MC68000_State_hpp
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../68000.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MC68000 {
|
||||
|
||||
/*!
|
||||
Provides a means for capturing or restoring complete 68000 state.
|
||||
|
||||
This is an optional adjunct to the 68000 class. If you want to take the rest of the 68000
|
||||
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> {
|
||||
uint32_t data[8], address[7];
|
||||
uint32_t user_stack_pointer;
|
||||
uint32_t supervisor_stack_pointer;
|
||||
uint16_t status;
|
||||
uint32_t program_counter;
|
||||
uint32_t prefetch;
|
||||
uint16_t instruction;
|
||||
|
||||
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> {
|
||||
uint8_t bus_interrupt_level;
|
||||
bool dtack;
|
||||
bool is_peripheral_address;
|
||||
bool bus_error;
|
||||
bool bus_request;
|
||||
bool bus_grant;
|
||||
bool halt;
|
||||
|
||||
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> {
|
||||
uint8_t e_clock_phase;
|
||||
uint32_t effective_address[2];
|
||||
uint32_t source_data;
|
||||
uint32_t destination_data;
|
||||
bool last_trace_flag;
|
||||
uint16_t next_word;
|
||||
uint32_t dbcc_false_address;
|
||||
bool is_starting_interrupt;
|
||||
uint8_t pending_interrupt_level;
|
||||
uint8_t accepted_interrupt_level;
|
||||
|
||||
// This is a reflective do-over of the ExecutionState enum within
|
||||
// MC68000Storage; I've yet to decide how happy I am with that
|
||||
// as an approach.
|
||||
ReflectableEnum(Phase,
|
||||
Executing,
|
||||
WaitingForDTack,
|
||||
Stopped,
|
||||
Halted,
|
||||
WillBeginInterrupt
|
||||
);
|
||||
Phase phase;
|
||||
|
||||
bool active_program;
|
||||
uint32_t movem_final_address;
|
||||
uint32_t source_addresses[65];
|
||||
|
||||
ReflectableEnum(MicroOpSource,
|
||||
ActiveProgram,
|
||||
LongException,
|
||||
ShortException,
|
||||
Interrupt
|
||||
);
|
||||
MicroOpSource micro_op_source;
|
||||
uint8_t micro_op;
|
||||
|
||||
ReflectableEnum(BusStepSource,
|
||||
FollowMicroOp,
|
||||
BusError,
|
||||
Trap,
|
||||
Reset,
|
||||
BranchTaken,
|
||||
BranchByteNotTaken,
|
||||
BranchWordNotTaken,
|
||||
BSR,
|
||||
DBccConditionTrue,
|
||||
DBccConditionFalseNoBranch,
|
||||
DBccConditionFalseBranch,
|
||||
MovemRead,
|
||||
MovemWrite,
|
||||
);
|
||||
BusStepSource bus_step_source;
|
||||
uint8_t bus_step;
|
||||
|
||||
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 /* MC68000_State_hpp */
|
@ -6,8 +6,8 @@
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef State_hpp
|
||||
#define State_hpp
|
||||
#ifndef Z80_State_hpp
|
||||
#define Z80_State_hpp
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
@ -97,4 +97,4 @@ struct State: public Reflection::StructImpl<State> {
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* State_hpp */
|
||||
#endif /* Z80_State_hpp */
|
||||
|
@ -110,13 +110,13 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s
|
||||
|
||||
// MARK: - Getters
|
||||
|
||||
template <typename Type> bool Reflection::get(const Struct &target, const std::string &name, Type &value) {
|
||||
template <typename Type> bool Reflection::get(const Struct &target, const std::string &name, Type &value, size_t offset) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
// If type is a direct match, copy.
|
||||
if(*target_type == typeid(Type)) {
|
||||
memcpy(&value, target.get(name), sizeof(Type));
|
||||
memcpy(&value, reinterpret_cast<const uint8_t *>(target.get(name)) + offset * sizeof(Type), sizeof(Type));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -131,14 +131,58 @@ template <typename Type> bool Reflection::get(const Struct &target, const std::s
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type> Type Reflection::get(const Struct &target, const std::string &name) {
|
||||
template <typename Type> Type Reflection::get(const Struct &target, const std::string &name, size_t offset) {
|
||||
Type value{};
|
||||
get(target, name, value);
|
||||
get(target, name, value, offset);
|
||||
return value;
|
||||
}
|
||||
|
||||
// MARK: - Description
|
||||
|
||||
void Reflection::Struct::append(std::ostringstream &stream, const std::string &key, const std::type_info *const type, size_t offset) const {
|
||||
// Output Bools as yes/no.
|
||||
if(*type == typeid(bool)) {
|
||||
stream << ::Reflection::get<bool>(*this, key, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
// Output Ints of all sizes as hex.
|
||||
#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<int_type>(*this, key, offset)); return; }
|
||||
#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
|
||||
#undef OutputIntC
|
||||
|
||||
// Output floats and strings natively.
|
||||
#define OutputNative(val_type) if(*type == typeid(val_type)) { stream << ::Reflection::get<val_type>(*this, key, offset); return; }
|
||||
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<int>(*this, key, offset);
|
||||
stream << Enum::to_string(*type, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Recurse to deal with embedded objects.
|
||||
if(*type == typeid(Reflection::Struct)) {
|
||||
const Reflection::Struct *const child = reinterpret_cast<const Reflection::Struct *>(get(key));
|
||||
stream << child->description();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Reflection::Struct::description() const {
|
||||
std::ostringstream stream;
|
||||
|
||||
@ -150,47 +194,20 @@ std::string Reflection::Struct::description() const {
|
||||
is_first = false;
|
||||
stream << key << ": ";
|
||||
|
||||
const auto count = count_of(key);
|
||||
const auto type = type_of(key);
|
||||
|
||||
// Output Bools as yes/no.
|
||||
if(*type == typeid(bool)) {
|
||||
stream << ::Reflection::get<bool>(*this, key);
|
||||
continue;
|
||||
if(count != 1) {
|
||||
stream << "[";
|
||||
}
|
||||
|
||||
// Output Ints of all sizes as hex.
|
||||
#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<int_type>(*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<val_type>(*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<int>(*this, key);
|
||||
stream << Enum::to_string(*type, value);
|
||||
continue;
|
||||
for(size_t index = 0; index < count; ++index) {
|
||||
append(stream, key, type, index);
|
||||
if(index != count-1) stream << ", ";
|
||||
}
|
||||
|
||||
// Recurse to deal with embedded objects.
|
||||
if(*type == typeid(Reflection::Struct)) {
|
||||
const Reflection::Struct *const child = reinterpret_cast<const Reflection::Struct *>(get(key));
|
||||
stream << child->description();
|
||||
continue;
|
||||
if(count != 1) {
|
||||
stream << "]";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#ifndef Struct_hpp
|
||||
#define Struct_hpp
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@ -27,6 +26,7 @@ namespace Reflection {
|
||||
struct Struct {
|
||||
virtual std::vector<std::string> all_keys() const = 0;
|
||||
virtual const std::type_info *type_of(const std::string &name) const = 0;
|
||||
virtual size_t count_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) const = 0;
|
||||
virtual std::vector<std::string> values_for(const std::string &name) const = 0;
|
||||
@ -37,6 +37,9 @@ struct Struct {
|
||||
sufficiently formed for a formal language parser, etc.
|
||||
*/
|
||||
std::string description() const;
|
||||
|
||||
private:
|
||||
void append(std::ostringstream &stream, const std::string &key, const std::type_info *type, size_t offset) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -93,14 +96,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 <typename Type> bool get(const Struct &target, const std::string &name, Type &value);
|
||||
template <typename Type> bool get(const Struct &target, const std::string &name, Type &value, size_t offset = 0);
|
||||
|
||||
/*!
|
||||
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 <typename Type> Type get(const Struct &target, const std::string &name);
|
||||
template <typename Type> Type get(const Struct &target, const std::string &name, size_t offset = 0);
|
||||
|
||||
|
||||
// TODO: move this elsewhere. It's just a sketch anyway.
|
||||
@ -144,6 +147,15 @@ template <typename Owner> class StructImpl: public Struct {
|
||||
return iterator->second.type;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The number of instances of objects of the same type as @c name that sit consecutively in memory.
|
||||
*/
|
||||
size_t count_of(const std::string &name) const final {
|
||||
const auto iterator = contents_.find(name);
|
||||
if(iterator == contents_.end()) return 0;
|
||||
return iterator->second.count;
|
||||
}
|
||||
|
||||
/*!
|
||||
@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.
|
||||
@ -206,9 +218,19 @@ template <typename Owner> class StructImpl: public Struct {
|
||||
it'll be the struct that's exposed.
|
||||
*/
|
||||
template <typename Type> void declare(Type *t, const std::string &name) {
|
||||
// If the declared item is a class, see whether it can be dynamically cast
|
||||
// to a reflectable for emplacement. If so, exit early.
|
||||
if constexpr (std::is_class<Type>()) {
|
||||
if(declare_reflectable(t, name)) return;
|
||||
}
|
||||
|
||||
// If the declared item is an array, record it as a pointer to the
|
||||
// first element plus a size.
|
||||
if constexpr (std::is_array<Type>()) {
|
||||
declare_emplace(&(*t)[0], name, sizeof(*t) / sizeof(*t[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
declare_emplace(t, name);
|
||||
}
|
||||
|
||||
@ -278,11 +300,11 @@ template <typename Owner> class StructImpl: public Struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type> void declare_emplace(Type *t, const std::string &name) {
|
||||
template <typename Type> void declare_emplace(Type *t, const std::string &name, size_t count = 1) {
|
||||
contents_.emplace(
|
||||
std::make_pair(
|
||||
name,
|
||||
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type))
|
||||
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type), count)
|
||||
));
|
||||
}
|
||||
|
||||
@ -290,8 +312,9 @@ template <typename Owner> class StructImpl: public Struct {
|
||||
const std::type_info *type;
|
||||
ssize_t offset;
|
||||
size_t size;
|
||||
Field(const std::type_info &type, ssize_t offset, size_t size) :
|
||||
type(&type), offset(offset), size(size) {}
|
||||
size_t count;
|
||||
Field(const std::type_info &type, ssize_t offset, size_t size, size_t count) :
|
||||
type(&type), offset(offset), size(size), count(count) {}
|
||||
};
|
||||
static inline std::unordered_map<std::string, Field> contents_;
|
||||
static inline std::unordered_map<std::string, std::vector<bool>> permitted_enum_values_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user