From 8b76d4007e9491fd1ae07143750271c73e63e893 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 14 May 2020 22:46:40 -0400 Subject: [PATCH 01/11] Starts adding `State` for the 68000. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 +++++ Processors/6502/State/State.hpp | 6 +- Processors/68000/State/State.cpp | 43 ++++++++++++ Processors/68000/State/State.hpp | 66 +++++++++++++++++++ Processors/Z80/State/State.hpp | 6 +- 5 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 Processors/68000/State/State.cpp create mode 100644 Processors/68000/State/State.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b8b1f8688..f44c62e07 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -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 = ""; }; 4B1B58F4246CC4E8009C171E /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = ""; }; 4B1B58F5246CC4E8009C171E /* State.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = ""; }; + 4B1B58FD246E19FD009C171E /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = State.cpp; sourceTree = ""; }; + 4B1B58FE246E19FD009C171E /* State.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = State.hpp; sourceTree = ""; }; 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiKeyboardMachine.cpp; sourceTree = ""; }; 4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiKeyboardMachine.hpp; sourceTree = ""; }; 4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiConfigurable.cpp; sourceTree = ""; }; @@ -2022,6 +2026,15 @@ path = Z80/State; sourceTree = ""; }; + 4B1B58FC246E19FD009C171E /* State */ = { + isa = PBXGroup; + children = ( + 4B1B58FD246E19FD009C171E /* State.cpp */, + 4B1B58FE246E19FD009C171E /* State.hpp */, + ); + path = State; + sourceTree = ""; + }; 4B1E85791D174DEC001EF87D /* 6532 */ = { isa = PBXGroup; children = ( @@ -3863,6 +3876,7 @@ children = ( 4BFF1D342233778C00838EA1 /* 68000.hpp */, 4BFF1D36223379D500838EA1 /* Implementation */, + 4B1B58FC246E19FD009C171E /* State */, ); path = 68000; sourceTree = ""; @@ -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 */, diff --git a/Processors/6502/State/State.hpp b/Processors/6502/State/State.hpp index c641c7fa6..adde3b761 100644 --- a/Processors/6502/State/State.hpp +++ b/Processors/6502/State/State.hpp @@ -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 { } } -#endif /* State_hpp */ +#endif /* MOS6502_State_hpp */ diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp new file mode 100644 index 000000000..13b074027 --- /dev/null +++ b/Processors/68000/State/State.cpp @@ -0,0 +1,43 @@ +// +// State.cpp +// Clock Signal +// +// Created by Thomas Harte on 14/05/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#include "State.hpp" + +using namespace CPU::MC68000; + +State::State(const ProcessorBase &src): State() { +} + +void State::apply(ProcessorBase &target) { +} + +// 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); + } +} + +State::ExecutionState::ExecutionState() { + if(needs_declare()) { + } +} + +State::Inputs::Inputs() { + if(needs_declare()) { + } +} diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp new file mode 100644 index 000000000..4a6ce8151 --- /dev/null +++ b/Processors/68000/State/State.hpp @@ -0,0 +1,66 @@ +// +// 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 { + /*! + Provides the current state of the well-known, published internal registers. + */ + struct Registers: public Reflection::StructImpl { + uint32_t data[8], address[7]; + + 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(); + } 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(); + } 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 */ diff --git a/Processors/Z80/State/State.hpp b/Processors/Z80/State/State.hpp index 05c210420..3a9aa838f 100644 --- a/Processors/Z80/State/State.hpp +++ b/Processors/Z80/State/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 { } } -#endif /* State_hpp */ +#endif /* Z80_State_hpp */ From 4481386a3dfd63abf9f43aef76c3b7bbd7575699 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 14 May 2020 22:59:44 -0400 Subject: [PATCH 02/11] Extends `Reflection::Struct` slightly to capture the lengths of arrays. --- .../Implementation/MultiConfigurable.cpp | 8 +++++ Reflection/Struct.hpp | 29 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp index 55bf935ba..5d8fb89cd 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp @@ -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); diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index c51758d8a..1e4e01c3d 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -27,6 +27,7 @@ namespace Reflection { struct Struct { virtual std::vector 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 values_for(const std::string &name) const = 0; @@ -144,6 +145,15 @@ template 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 +216,19 @@ template class StructImpl: public Struct { it'll be the struct that's exposed. */ template 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()) { 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()) { + declare_emplace(&t[0], name, sizeof(*t) / sizeof(*t[0])); + return; + } + declare_emplace(t, name); } @@ -278,11 +298,11 @@ template class StructImpl: public Struct { return false; } - template void declare_emplace(Type *t, const std::string &name) { + template void declare_emplace(Type *t, const std::string &name, size_t count = 1) { contents_.emplace( std::make_pair( name, - Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type)) + Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type), count) )); } @@ -290,8 +310,9 @@ template 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 contents_; static inline std::unordered_map> permitted_enum_values_; From 375835a950d142760950a6a851126c5272097389 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 14 May 2020 23:58:17 -0400 Subject: [PATCH 03/11] Extends `.description()` to handle arrays. --- Reflection/Struct.cpp | 95 +++++++++++++++++++++++++------------------ Reflection/Struct.hpp | 9 ++-- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 2a2d80736..c3b676805 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -110,13 +110,13 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s // MARK: - Getters -template bool Reflection::get(const Struct &target, const std::string &name, Type &value) { +template 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(target.get(name)) + offset * sizeof(Type), sizeof(Type)); return true; } @@ -131,14 +131,58 @@ template bool Reflection::get(const Struct &target, const std::s return false; } -template Type Reflection::get(const Struct &target, const std::string &name) { +template 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(*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(*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(*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(*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(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(*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(*this, key)); continue; } -#define OutputInt(int_type) OutputIntC(int_type, int_type) - OutputIntC(int8_t, int16_t); - OutputIntC(uint8_t, uint16_t); - OutputInt(int16_t); - OutputInt(uint16_t); - OutputInt(int32_t); - OutputInt(uint32_t); - OutputInt(int64_t); - OutputInt(uint64_t); -#undef OutputInt - - // Output floats and strings natively. -#define OutputNative(val_type) if(*type == typeid(val_type)) { stream << ::Reflection::get(*this, key); continue; } - OutputNative(float); - OutputNative(double); - OutputNative(char *); - OutputNative(std::string); -#undef OutputNAtive - - // Output the current value of any enums. - if(!Enum::name(*type).empty()) { - const int value = ::Reflection::get(*this, key); - stream << Enum::to_string(*type, value); - continue; + 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(get(key)); - stream << child->description(); - continue; + if(count != 1) { + stream << "]"; } } diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 1e4e01c3d..833fe8d76 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -38,6 +38,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; }; /*! @@ -94,14 +97,14 @@ bool fuzzy_set(Struct &target, const std::string &name, const std::string &value @returns @c true if the property was successfully read; @c false otherwise. */ -template bool get(const Struct &target, const std::string &name, Type &value); +template 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 Type get(const Struct &target, const std::string &name); +template Type get(const Struct &target, const std::string &name, size_t offset = 0); // TODO: move this elsewhere. It's just a sketch anyway. @@ -225,7 +228,7 @@ template class StructImpl: public Struct { // If the declared item is an array, record it as a pointer to the // first element plus a size. if constexpr (std::is_array()) { - declare_emplace(&t[0], name, sizeof(*t) / sizeof(*t[0])); + declare_emplace(&(*t)[0], name, sizeof(*t) / sizeof(*t[0])); return; } From f7a16762b4f8123c958125015c4fb8a5f204f6aa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 16 May 2020 00:06:04 -0400 Subject: [PATCH 04/11] Starts populating the 68000 state registers. --- .../Implementation/68000Implementation.hpp | 90 ++++++++++--------- .../68000/Implementation/68000Storage.hpp | 6 +- Processors/68000/State/State.cpp | 15 ++++ Processors/68000/State/State.hpp | 5 ++ 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 0f32a4159..2c41c4f83 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.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 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 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]; @@ -277,7 +277,7 @@ template 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 @@ -331,7 +331,7 @@ template 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 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; } } @@ -887,15 +887,15 @@ template 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 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 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; @@ -1297,7 +1297,7 @@ template 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 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 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 { @@ -1858,11 +1858,11 @@ template 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(); + const auto current_status = status(); source_bus_data_[0].halves.low.halves.high = uint8_t(current_status >> 8); } - set_status(source_bus_data_[0].full); + apply_status(source_bus_data_[0].full); break; /* @@ -1888,7 +1888,7 @@ template 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; @@ -1968,7 +1968,7 @@ template 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 @@ -2202,7 +2202,7 @@ template 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 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 diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 913deb09f..7e2ed16e8 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -43,7 +43,7 @@ class ProcessorStorage { 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. @@ -539,9 +539,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 */ diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index 13b074027..083d13f2f 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -11,6 +11,16 @@ using namespace CPU::MC68000; State::State(const ProcessorBase &src): State() { + 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; } void State::apply(ProcessorBase &target) { @@ -29,6 +39,11 @@ 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); } } diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index 4a6ce8151..15eda7273 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -29,6 +29,11 @@ struct State: public Reflection::StructImpl { */ struct Registers: public Reflection::StructImpl { 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; Registers(); } registers; From dcc0ee36790fa62d23bdb899fe5dc0fdbedebbf7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 16 May 2020 17:44:15 -0400 Subject: [PATCH 05/11] Adds input line capture. --- Processors/68000/State/State.cpp | 27 ++++++++++++++++++++++----- Processors/68000/State/State.hpp | 8 ++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index 083d13f2f..a24dca4a2 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -11,6 +11,7 @@ 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; @@ -21,6 +22,15 @@ State::State(const ProcessorBase &src): State() { registers.status = src.get_status(); registers.program_counter = src.program_counter_.full; registers.prefetch = src.prefetch_queue_.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_; } void State::apply(ProcessorBase &target) { @@ -47,12 +57,19 @@ State::Registers::Registers() { } } +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()) { } } - -State::Inputs::Inputs() { - if(needs_declare()) { - } -} diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index 15eda7273..4ed4fb14e 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -43,6 +43,14 @@ struct State: public Reflection::StructImpl { related to an access cycle. */ struct Inputs: public Reflection::StructImpl { + uint8_t bus_interrupt_level; + bool dtack; + bool is_peripheral_address; + bool bus_error; + bool bus_request; + bool bus_grant; + bool halt; + Inputs(); } inputs; From 57edfe87513256825ea82a046991ff3f2cb81ab6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 16 May 2020 18:31:43 -0400 Subject: [PATCH 06/11] Formalises TODO list and marches onward into execution state. --- Processors/68000/State/State.cpp | 23 +++++++++++++++++++++++ Processors/68000/State/State.hpp | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index a24dca4a2..ce718dec0 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -22,6 +22,7 @@ State::State(const ProcessorBase &src): State() { 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_); @@ -31,6 +32,23 @@ State::State(const ProcessorBase &src): State() { inputs.bus_request = src.bus_request_; inputs.bus_grant = false; // TODO (within the 68000). inputs.halt = src.halt_; + + // TODO: + // execution_state_ + // last_trace_flag_ + // pending_interrupt_level_ + // accepted_interrupt_level_ + // is_starting_interrupt_ + // dbcc_false_address_ + // next_word_ + // active_[program_/micro_op_/step_] + + // Execution state. + execution_state.e_clock_phase = src.e_clock_phase_.as(); + 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_[0].full; + execution_state.destination_data = src.destination_bus_data_[0].full; } void State::apply(ProcessorBase &target) { @@ -54,6 +72,7 @@ State::Registers::Registers() { DeclareField(status); DeclareField(program_counter); DeclareField(prefetch); + DeclareField(instruction); } } @@ -71,5 +90,9 @@ State::Inputs::Inputs() { State::ExecutionState::ExecutionState() { if(needs_declare()) { + DeclareField(e_clock_phase); + DeclareField(effective_address); + DeclareField(source_data); + DeclareField(destination_data); } } diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index 4ed4fb14e..fa1aa1d3f 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -34,6 +34,7 @@ struct State: public Reflection::StructImpl { uint16_t status; uint32_t program_counter; uint32_t prefetch; + uint16_t instruction; Registers(); } registers; @@ -60,6 +61,11 @@ struct State: public Reflection::StructImpl { obviously doesn't. */ struct ExecutionState: public Reflection::StructImpl { + uint8_t e_clock_phase; + uint32_t effective_address[2]; + uint32_t source_data; + uint32_t destination_data; + ExecutionState(); } execution_state; From ff3c2fdc59577fdc8cf41502a12a842260a178eb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 16 May 2020 18:33:36 -0400 Subject: [PATCH 07/11] Adds 68000 state to SConstruct. --- OSBindings/SDL/SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/OSBindings/SDL/SConstruct b/OSBindings/SDL/SConstruct index e644eec10..e1a746ac8 100644 --- a/OSBindings/SDL/SConstruct +++ b/OSBindings/SDL/SConstruct @@ -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') From 6f169282150b8738ef11ab8102a691d07f889826 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 16 May 2020 22:47:04 -0400 Subject: [PATCH 08/11] Adds all remaining simple scalar fields. --- Processors/68000/State/State.cpp | 18 ++++++++++++------ Processors/68000/State/State.hpp | 6 ++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index ce718dec0..0acaca89c 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -35,12 +35,6 @@ State::State(const ProcessorBase &src): State() { // TODO: // execution_state_ - // last_trace_flag_ - // pending_interrupt_level_ - // accepted_interrupt_level_ - // is_starting_interrupt_ - // dbcc_false_address_ - // next_word_ // active_[program_/micro_op_/step_] // Execution state. @@ -49,6 +43,12 @@ State::State(const ProcessorBase &src): State() { execution_state.effective_address[1] = src.effective_address_[1].full; execution_state.source_data = src.source_bus_data_[0].full; execution_state.destination_data = src.destination_bus_data_[0].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_); } void State::apply(ProcessorBase &target) { @@ -94,5 +94,11 @@ State::ExecutionState::ExecutionState() { 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); } } diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index fa1aa1d3f..579bfafb5 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -65,6 +65,12 @@ struct State: public Reflection::StructImpl { 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; ExecutionState(); } execution_state; From a3d4c7599b81e85b35e98d588a359dc5a533d594 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 18 May 2020 23:55:54 -0400 Subject: [PATCH 09/11] Attempts fully to capture 68000 state. Albeit that it can't be put back yet. --- .../Implementation/68000Implementation.hpp | 60 ++++---- .../68000/Implementation/68000Storage.cpp | 18 +-- .../68000/Implementation/68000Storage.hpp | 36 ++--- Processors/68000/State/State.cpp | 137 +++++++++++++++++- Processors/68000/State/State.hpp | 43 ++++++ 5 files changed, 232 insertions(+), 62 deletions(-) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 2c41c4f83..c077afe0a 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -210,7 +210,7 @@ template 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 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 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; } @@ -302,7 +302,7 @@ template 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 @@ -829,7 +829,7 @@ template 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 void Proces break; case Operation::PEA: - destination_bus_data_[0] = effective_address_[0]; + destination_bus_data_ = effective_address_[0]; break; /* @@ -1119,30 +1119,30 @@ template 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; /* @@ -1448,7 +1448,7 @@ template 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 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; /* @@ -1859,10 +1859,10 @@ template void Proces // If this is RTR, patch out the supervisor half of the status register. if(decoded_instruction_.full == 0x4e77) { const auto current_status = status(); - source_bus_data_[0].halves.low.halves.high = + source_bus_data_.halves.low.halves.high = uint8_t(current_status >> 8); } - apply_status(source_bus_data_[0].full); + apply_status(source_bus_data_.full); break; /* @@ -1938,9 +1938,9 @@ template 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 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; @@ -2002,7 +2002,7 @@ template 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 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: diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 00532fd84..a082b99af 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -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 }); diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 7e2ed16e8..a0de08b93 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -38,7 +38,7 @@ 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_; @@ -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. diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index 0acaca89c..1ae4fe2db 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -33,22 +33,133 @@ State::State(const ProcessorBase &src): State() { inputs.bus_grant = false; // TODO (within the 68000). inputs.halt = src.halt_; - // TODO: - // execution_state_ - // active_[program_/micro_op_/step_] - // Execution state. execution_state.e_clock_phase = src.e_clock_phase_.as(); 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_[0].full; - execution_state.destination_data = src.destination_bus_data_[0].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) { @@ -100,5 +211,19 @@ State::ExecutionState::ExecutionState() { 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); } } diff --git a/Processors/68000/State/State.hpp b/Processors/68000/State/State.hpp index 579bfafb5..f15337e86 100644 --- a/Processors/68000/State/State.hpp +++ b/Processors/68000/State/State.hpp @@ -72,6 +72,49 @@ struct State: public Reflection::StructImpl { 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; From a16b710d227e28dc63d29cfd059e22760da384a5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 19 May 2020 00:06:29 -0400 Subject: [PATCH 10/11] Removes from Struct.h (which means it's needed in the 68000's State). --- Processors/68000/State/State.cpp | 2 ++ Reflection/Struct.hpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index 1ae4fe2db..c9964901e 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -8,6 +8,8 @@ #include "State.hpp" +#include + using namespace CPU::MC68000; State::State(const ProcessorBase &src): State() { diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 833fe8d76..f0d22e5e9 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -9,7 +9,6 @@ #ifndef Struct_hpp #define Struct_hpp -#include #include #include #include From 28881cb391cf5b548372ccb297b7442e4ae22870 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 19 May 2020 18:27:10 -0400 Subject: [PATCH 11/11] Implements `apply`. --- Processors/68000/State/State.cpp | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/Processors/68000/State/State.cpp b/Processors/68000/State/State.cpp index c9964901e..5faab1dcd 100644 --- a/Processors/68000/State/State.cpp +++ b/Processors/68000/State/State.cpp @@ -165,6 +165,115 @@ State::State(const ProcessorBase &src): State() { } 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'.