From 2af774601f5eddf59bd09928cffb96480536b612 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 6 Nov 2023 16:04:31 -0500 Subject: [PATCH] Temporarily disentangle `Memory` and access internals; start to be overt in PerformImplementation. --- InstructionSets/x86/AccessType.hpp | 17 +++++--- .../Implementation/PerformImplementation.hpp | 24 ++++++----- .../x86/Implementation/Resolver.hpp | 42 +++++++++---------- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 17 ++++++-- 4 files changed, 59 insertions(+), 41 deletions(-) diff --git a/InstructionSets/x86/AccessType.hpp b/InstructionSets/x86/AccessType.hpp index 12eb03846..6f90345a4 100644 --- a/InstructionSets/x86/AccessType.hpp +++ b/InstructionSets/x86/AccessType.hpp @@ -31,11 +31,11 @@ enum class AccessType { PreauthorisedRead, }; -template struct ReturnType; +template struct Accessor; // Reads: return a value directly. -template struct ReturnType { using type = IntT; }; -template struct ReturnType { using type = IntT; }; +template struct Accessor { using type = IntT; }; +template struct Accessor { using type = IntT; }; // Writes: return a custom type that can be written but not read. template @@ -46,11 +46,16 @@ class Writeable { private: IntT &target_; }; -//template struct ReturnType { using type = Writeable; }; -template struct ReturnType { using type = IntT &; }; +//template struct Accessor { using type = Writeable; }; +template struct Accessor { using type = IntT &; }; // Read-modify-writes: return a reference. -template struct ReturnType { using type = IntT &; }; +template struct Accessor { using type = IntT &; }; + +// Shorthands; assumed that preauthorised reads have the same return type as reads. +template using read_t = typename Accessor::type; +template using write_t = typename Accessor::type; +template using modify_t = typename Accessor::type; } diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index d932357a3..3d854b86a 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -279,8 +279,12 @@ void add(IntT &destination, IntT source, ContextT &context) { destination = result; } -template -void sub(IntT &destination, IntT source, ContextT &context) { +template +void sub( + typename Accessor::type destination, + read_t source, + ContextT &context +) { /* DEST ← DEST - (SRC [+ CF]); */ @@ -298,7 +302,7 @@ void sub(IntT &destination, IntT source, ContextT &context) { context.flags.template set_from(result); - if constexpr (write_back) { + if constexpr (destination_type == AccessType::Write) { destination = result; } } @@ -1374,8 +1378,8 @@ template < // // (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking) IntT immediate; - const auto source_r = [&]() -> IntT& { - return *resolve( + const auto source_r = [&]() -> IntT { + return resolve( instruction, instruction.source().source(), instruction.source(), @@ -1384,7 +1388,7 @@ template < &immediate); }; const auto source_rmw = [&]() -> IntT& { - return *resolve( + return resolve( instruction, instruction.source().source(), instruction.source(), @@ -1392,8 +1396,8 @@ template < nullptr, &immediate); }; - const auto destination_r = [&]() -> IntT& { - return *resolve( + const auto destination_r = [&]() -> IntT { + return resolve( instruction, instruction.destination().source(), instruction.destination(), @@ -1402,7 +1406,7 @@ template < &immediate); }; const auto destination_w = [&]() -> IntT& { - return *resolve( + return resolve( instruction, instruction.destination().source(), instruction.destination(), @@ -1411,7 +1415,7 @@ template < &immediate); }; const auto destination_rmw = [&]() -> IntT& { - return *resolve( + return resolve( instruction, instruction.destination().source(), instruction.destination(), diff --git a/InstructionSets/x86/Implementation/Resolver.hpp b/InstructionSets/x86/Implementation/Resolver.hpp index 300191119..9f9c9d9e1 100644 --- a/InstructionSets/x86/Implementation/Resolver.hpp +++ b/InstructionSets/x86/Implementation/Resolver.hpp @@ -21,7 +21,7 @@ namespace InstructionSet::x86 { /// If @c source is Source::Immediate then the appropriate portion of @c instrucion's operand /// is copied to @c *immediate and @c immediate is returned. template -IntT *resolve( +typename Accessor::type resolve( InstructionT &instruction, Source source, DataPointer pointer, @@ -44,7 +44,7 @@ uint32_t address( uint32_t address; uint16_t zero = 0; - address = *resolve(instruction, pointer.index(), pointer, context, &zero); + address = resolve(instruction, pointer.index(), pointer, context, &zero); if constexpr (is_32bit(ContextT::model)) { address <<= pointer.scale(); } @@ -53,7 +53,7 @@ uint32_t address( if constexpr (source == Source::IndirectNoBase) { return address; } - return address + *resolve(instruction, pointer.base(), pointer, context); + return address + resolve(instruction, pointer.base(), pointer, context); } /// @returns a pointer to the contents of the register identified by the combination of @c IntT and @c Source if any; @@ -150,7 +150,7 @@ uint32_t address( // See forward declaration, above, for details. template -IntT *resolve( +typename Accessor::type resolve( InstructionT &instruction, Source source, DataPointer pointer, @@ -165,26 +165,26 @@ IntT *resolve( uint32_t target_address; switch(source) { // Defer all register accesses to the register-specific lookup. - case Source::eAX: return register_(context); - case Source::eCX: return register_(context); - case Source::eDX: return register_(context); - case Source::eBX: return register_(context); - case Source::eSPorAH: return register_(context); - case Source::eBPorCH: return register_(context); - case Source::eSIorDH: return register_(context); - case Source::eDIorBH: return register_(context); - case Source::ES: return register_(context); - case Source::CS: return register_(context); - case Source::SS: return register_(context); - case Source::DS: return register_(context); - case Source::FS: return register_(context); - case Source::GS: return register_(context); + case Source::eAX: return *register_(context); + case Source::eCX: return *register_(context); + case Source::eDX: return *register_(context); + case Source::eBX: return *register_(context); + case Source::eSPorAH: return *register_(context); + case Source::eBPorCH: return *register_(context); + case Source::eSIorDH: return *register_(context); + case Source::eDIorBH: return *register_(context); + case Source::ES: return *register_(context); + case Source::CS: return *register_(context); + case Source::SS: return *register_(context); + case Source::DS: return *register_(context); + case Source::FS: return *register_(context); + case Source::GS: return *register_(context); - case Source::None: return none; + case Source::None: return *none; case Source::Immediate: *immediate = instruction.operand(); - return immediate; + return *immediate; case Source::Indirect: target_address = address(instruction, pointer, context); @@ -199,7 +199,7 @@ IntT *resolve( // If execution has reached here then a memory fetch is required. // Do it and exit. - return &context.memory.template access(instruction.data_segment(), target_address); + return context.memory.template access(instruction.data_segment(), target_address); } } diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index d39e79df3..275fc2852 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -98,6 +98,15 @@ struct Memory { public: using AccessType = InstructionSet::x86::AccessType; + template struct ReturnType; + + // Reads: return a value directly. + template struct ReturnType { using type = IntT; }; + template struct ReturnType { using type = IntT; }; + + // Writes: return a reference. + template struct ReturnType { using type = IntT &; }; + // Constructor. Memory(Registers ®isters) : registers_(registers) { memory.resize(1024*1024); @@ -153,7 +162,7 @@ struct Memory { // Accesses an address based on segment:offset. template - typename InstructionSet::x86::ReturnType::type &access(InstructionSet::x86::Source segment, uint16_t offset) { + typename ReturnType::type &access(InstructionSet::x86::Source segment, uint16_t offset) { if constexpr (std::is_same_v) { // If this is a 16-bit access that runs past the end of the segment, it'll wrap back // to the start. So the 16-bit value will need to be a local cache. @@ -176,7 +185,7 @@ struct Memory { // Accesses an address based on physical location. template - typename InstructionSet::x86::ReturnType::type &access(uint32_t address) { + typename ReturnType::type &access(uint32_t address) { return access(address, Tag::Accessed); } @@ -273,7 +282,7 @@ struct Memory { // Entry point used by the flow controller so that it can mark up locations at which the flags were written, // so that defined-flag-only masks can be applied while verifying RAM contents. template - typename InstructionSet::x86::ReturnType::type &access(InstructionSet::x86::Source segment, uint16_t offset, Tag tag) { + typename ReturnType::type &access(InstructionSet::x86::Source segment, uint16_t offset, Tag tag) { const uint32_t physical_address = address(segment, offset); return access(physical_address, tag); } @@ -281,7 +290,7 @@ struct Memory { // An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative // to a selector, they're just at an absolute location. template - typename InstructionSet::x86::ReturnType::type &access(uint32_t address, Tag tag) { + typename ReturnType::type &access(uint32_t address, Tag tag) { if constexpr (type == AccessType::PreauthorisedRead) { if(!test_preauthorisation(address)) { printf("Non preauthorised access\n");