diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 0971685c4..3bf4b7615 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -194,9 +194,13 @@ namespace Primitive { template void push(IntT &value, ContextT &context) { context.registers.sp_ -= sizeof(IntT); - context.memory.template access( - Source::SS, - context.registers.sp_) = value; + if constexpr (preauthorised) { + context.memory.template preauthorised_write(Source::SS, context.registers.sp_, value); + } else { + context.memory.template access( + Source::SS, + context.registers.sp_) = value; + } context.memory.template write_back(); } diff --git a/InstructionSets/x86/Perform.hpp b/InstructionSets/x86/Perform.hpp index 2b61a100b..cbeacab3b 100644 --- a/InstructionSets/x86/Perform.hpp +++ b/InstructionSets/x86/Perform.hpp @@ -33,7 +33,6 @@ enum class AccessType { /// all necessary stack space is available ahead of pushing anything, though each individual push will then result in /// a further `Preauthorised` access. PreauthorisedRead, - PreauthorisedWrite, }; template < diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 2e61e651e..831223d1c 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -106,7 +106,6 @@ struct Memory { // Writes: return a reference. template struct ReturnType { using type = IntT &; }; template struct ReturnType { using type = IntT &; }; - template struct ReturnType { using type = IntT &; }; // Constructor. Memory(Registers ®isters) : registers_(registers) { @@ -155,22 +154,24 @@ struct Memory { } } + // // Access call-ins. + // // Accesses an address based on segment:offset. template - typename ReturnType::type &access(InstructionSet::x86::Source segment, uint32_t address) { + 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. - if(address == 0xffff) { - write_back_address_[0] = (segment_base(segment) + address) & 0xf'ffff; + if(offset == 0xffff) { + write_back_address_[0] = address(segment, offset); write_back_address_[1] = (write_back_address_[0] - 65535) & 0xf'ffff; write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8); return write_back_value_; } } - auto &value = access(segment, address, Tag::Accessed); + auto &value = access(segment, offset, Tag::Accessed); // If the CPU has indicated a write, it should be safe to fuzz the value now. if(type == AccessType::Write) { @@ -197,6 +198,41 @@ struct Memory { } } + // + // Direct write. + // + template + void preauthorised_write(InstructionSet::x86::Source segment, uint16_t offset, IntT value) { + if(!test_preauthorisation(address(segment, offset))) { + printf("Non-preauthorised access\n"); + } + + // Bytes can be written without further ado. + if constexpr (std::is_same_v) { + memory[address(segment, offset) & 0xf'ffff] = value; + return; + } + + // Words that straddle the segment end must be split in two. + if(offset == 0xffff) { + memory[address(segment, offset) & 0xf'ffff] = value & 0xff; + memory[address(segment, 0x0000) & 0xf'ffff] = value >> 8; + return; + } + + const uint32_t target = address(segment, offset) & 0xf'ffff; + + // Words that straddle the end of physical RAM must also be split in two. + if(target == 0xf'ffff) { + memory[0xf'ffff] = value & 0xff; + memory[0x0'0000] = value >> 8; + return; + } + + // It's safe just to write then. + *reinterpret_cast(&memory[target]) = value; + } + private: enum class Tag { Seeded, @@ -236,12 +272,16 @@ struct Memory { return physical_address << 4; } + uint32_t address(InstructionSet::x86::Source segment, uint16_t offset) { + return (segment_base(segment) + offset) & 0xf'ffff; + } + // 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 ReturnType::type &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) { - const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff; + 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); } @@ -249,7 +289,7 @@ struct Memory { // to a selector, they're just at an absolute location. template typename ReturnType::type &access(uint32_t address, Tag tag) { - if constexpr (type == AccessType::PreauthorisedRead || type == AccessType::PreauthorisedWrite) { + if constexpr (type == AccessType::PreauthorisedRead) { if(!test_preauthorisation(address)) { printf("Non preauthorised access\n"); }