1
0
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:
Thomas Harte 2020-05-19 22:33:57 -04:00 committed by GitHub
commit 41fc6c20a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 702 additions and 149 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}
}

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

View File

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

View File

@ -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 << "]";
}
}

View File

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