diff --git a/InstructionSets/x86/Implementation/LoadStore.hpp b/InstructionSets/x86/Implementation/LoadStore.hpp index e8bfe8a13..dd8cb30e7 100644 --- a/InstructionSets/x86/Implementation/LoadStore.hpp +++ b/InstructionSets/x86/Implementation/LoadStore.hpp @@ -77,4 +77,54 @@ void mov( destination = source; } +template +void smsw( + write_t destination, + ContextT &context +) { + destination = context.registers.msw(); +} + +template +void ldt( + read_t source_address, + const InstructionT &instruction, + ContextT &context +) { + const auto segment = instruction.data_segment(); + context.memory.preauthorise_read( + segment, + source_address, + 6); + + DescriptorTableLocation location; + location.limit = + context.memory.template access(segment, source_address); + location.base = + context.memory.template access(segment, AddressT(source_address + 2)); + if constexpr (std::is_same_v) { + location.base &= 0xff'ff'ff; + } + + context.registers.template set(location); + context.segments.did_update(table); +} + +template +void sdt( + read_t destination_address, + const InstructionT &instruction, + ContextT &context +) { + const auto segment = instruction.data_segment(); + context.memory.preauthorise_write( + segment, + destination_address, + 6); + + const auto location = context.registers.template get
(); + context.memory.template preauthorised_write(segment, destination_address, location.limit); + context.memory.template preauthorised_write(segment, AddressT(destination_address + 2), location.base); +} + } diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 0e14ba949..3b3578b77 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -115,6 +115,13 @@ template < } }; + // Currently a special case for descriptor loading; assumes an indirect operand and returns the + // address indicated. Unlike [source/destination]_r it doesn't read an IntT from that address, + // since those instructions load an atypical six bytes. + const auto source_indirect = [&]() -> AddressT { + return address(instruction, instruction.source(), context); + }; + // Some instructions use a pair of registers as an extended accumulator — DX:AX or EDX:EAX. // The two following return the high and low parts of that pair; they also work in Byte mode to return AH:AL, // i.e. AX split into high and low parts. @@ -283,6 +290,42 @@ template < } break; + case Operation::SMSW: + if constexpr (ContextT::model >= Model::i80286 && std::is_same_v) { + Primitive::smsw(destination_w(), context); + } else { + assert(false); + } + break; + case Operation::LIDT: + if constexpr (ContextT::model >= Model::i80286) { + Primitive::ldt(source_indirect(), instruction, context); + } else { + assert(false); + } + break; + case Operation::LGDT: + if constexpr (ContextT::model >= Model::i80286) { + Primitive::ldt(source_indirect(), instruction, context); + } else { + assert(false); + } + break; + case Operation::SIDT: + if constexpr (ContextT::model >= Model::i80286) { + Primitive::sdt(source_indirect(), instruction, context); + } else { + assert(false); + } + break; + case Operation::SGDT: + if constexpr (ContextT::model >= Model::i80286) { + Primitive::sdt(source_indirect(), instruction, context); + } else { + assert(false); + } + break; + case Operation::JO: jcc(context.flags.template condition()); return; case Operation::JNO: jcc(!context.flags.template condition()); return; case Operation::JB: jcc(context.flags.template condition()); return; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index d292e9fa2..f02eb7580 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -551,6 +551,15 @@ constexpr Operation rep_operation(Operation operation, Repetition repetition) { } } +enum class DescriptorTable { + Global, Local, Interrupt, +}; +struct DescriptorTableLocation { + uint16_t limit; + uint32_t base; +}; + + /// Provides a 32-bit-style scale, index and base; to produce the address this represents, /// calcluate base() + (index() << scale()). /// diff --git a/Machines/PCCompatible/Memory.hpp b/Machines/PCCompatible/Memory.hpp index 558c95e39..de394d5a8 100644 --- a/Machines/PCCompatible/Memory.hpp +++ b/Machines/PCCompatible/Memory.hpp @@ -38,6 +38,8 @@ public: void preauthorise_stack_read([[maybe_unused]] uint32_t length) {} void preauthorise_read([[maybe_unused]] InstructionSet::x86::Source segment, [[maybe_unused]] uint16_t start, [[maybe_unused]] uint32_t length) {} void preauthorise_read([[maybe_unused]] uint32_t start, [[maybe_unused]] uint32_t length) {} + void preauthorise_write([[maybe_unused]] InstructionSet::x86::Source segment, [[maybe_unused]] uint16_t start, [[maybe_unused]] uint32_t length) {} + void preauthorise_write([[maybe_unused]] uint32_t start, [[maybe_unused]] uint32_t length) {} // // Access call-ins. diff --git a/Machines/PCCompatible/Registers.hpp b/Machines/PCCompatible/Registers.hpp index 49e64a518..fe06f0128 100644 --- a/Machines/PCCompatible/Registers.hpp +++ b/Machines/PCCompatible/Registers.hpp @@ -8,6 +8,7 @@ #pragma once +#include "InstructionSets/x86/Instruction.hpp" // For DescriptorTable. #include "InstructionSets/x86/Model.hpp" #include "Numeric/RegisterSizes.hpp" @@ -18,68 +19,96 @@ struct Registers; template <> struct Registers { - public: - static constexpr bool is_32bit = false; +public: + static constexpr bool is_32bit = false; - uint8_t &al() { return ax_.halves.low; } - uint8_t &ah() { return ax_.halves.high; } - uint16_t &ax() { return ax_.full; } + uint8_t &al() { return ax_.halves.low; } + uint8_t &ah() { return ax_.halves.high; } + uint16_t &ax() { return ax_.full; } - CPU::RegisterPair16 &axp() { return ax_; } + CPU::RegisterPair16 &axp() { return ax_; } - uint8_t &cl() { return cx_.halves.low; } - uint8_t &ch() { return cx_.halves.high; } - uint16_t &cx() { return cx_.full; } + uint8_t &cl() { return cx_.halves.low; } + uint8_t &ch() { return cx_.halves.high; } + uint16_t &cx() { return cx_.full; } - uint8_t &dl() { return dx_.halves.low; } - uint8_t &dh() { return dx_.halves.high; } - uint16_t &dx() { return dx_.full; } + uint8_t &dl() { return dx_.halves.low; } + uint8_t &dh() { return dx_.halves.high; } + uint16_t &dx() { return dx_.full; } - uint8_t &bl() { return bx_.halves.low; } - uint8_t &bh() { return bx_.halves.high; } - uint16_t &bx() { return bx_.full; } + uint8_t &bl() { return bx_.halves.low; } + uint8_t &bh() { return bx_.halves.high; } + uint16_t &bx() { return bx_.full; } - uint16_t &sp() { return sp_; } - uint16_t &bp() { return bp_; } - uint16_t &si() { return si_; } - uint16_t &di() { return di_; } + uint16_t &sp() { return sp_; } + uint16_t &bp() { return bp_; } + uint16_t &si() { return si_; } + uint16_t &di() { return di_; } - uint16_t &ip() { return ip_; } + uint16_t &ip() { return ip_; } - uint16_t &es() { return es_; } - uint16_t &cs() { return cs_; } - uint16_t &ds() { return ds_; } - uint16_t &ss() { return ss_; } - uint16_t es() const { return es_; } - uint16_t cs() const { return cs_; } - uint16_t ds() const { return ds_; } - uint16_t ss() const { return ss_; } + uint16_t &es() { return es_; } + uint16_t &cs() { return cs_; } + uint16_t &ds() { return ds_; } + uint16_t &ss() { return ss_; } + uint16_t es() const { return es_; } + uint16_t cs() const { return cs_; } + uint16_t ds() const { return ds_; } + uint16_t ss() const { return ss_; } - void reset() { - cs_ = 0xffff; - ip_ = 0; - } + void reset() { + cs_ = 0xffff; + ip_ = 0; + } - private: - CPU::RegisterPair16 ax_; - CPU::RegisterPair16 cx_; - CPU::RegisterPair16 dx_; - CPU::RegisterPair16 bx_; +private: + CPU::RegisterPair16 ax_; + CPU::RegisterPair16 cx_; + CPU::RegisterPair16 dx_; + CPU::RegisterPair16 bx_; - uint16_t sp_; - uint16_t bp_; - uint16_t si_; - uint16_t di_; - uint16_t es_, cs_, ds_, ss_; - uint16_t ip_; + uint16_t sp_; + uint16_t bp_; + uint16_t si_; + uint16_t di_; + uint16_t es_, cs_, ds_, ss_; + uint16_t ip_; }; template <> -struct Registers: public Registers { -}; +struct Registers: public Registers {}; template <> struct Registers: public Registers { +public: + void reset() { + Registers::reset(); + machine_status_ = 0; + } + + uint16_t msw() const { return machine_status_; } + + using DescriptorTable = InstructionSet::x86::DescriptorTable; + using DescriptorTableLocation = InstructionSet::x86::DescriptorTableLocation; + + template + void set(const DescriptorTableLocation location) { + static constexpr bool is_global = table == DescriptorTable::Global; + static_assert(is_global || table == DescriptorTable::Interrupt); + auto &target = is_global ? global_ : interrupt_; + target = location; + } + + template + DescriptorTableLocation get() { + static constexpr bool is_global = table == DescriptorTable::Global; + static_assert(is_global || table == DescriptorTable::Interrupt); + return is_global ? global_ : interrupt_; + } + +private: + uint16_t machine_status_; + DescriptorTableLocation global_, interrupt_; }; } diff --git a/Machines/PCCompatible/Segments.hpp b/Machines/PCCompatible/Segments.hpp index c49d25b82..a153adede 100644 --- a/Machines/PCCompatible/Segments.hpp +++ b/Machines/PCCompatible/Segments.hpp @@ -21,6 +21,7 @@ public: Segments(const Registers ®isters) : registers_(registers) {} using Source = InstructionSet::x86::Source; + using DescriptorTable = InstructionSet::x86::DescriptorTable; /// Posted by @c perform after any operation which *might* have affected a segment register. void did_update(const Source segment) { @@ -33,6 +34,8 @@ public: } } + void did_update(DescriptorTable) {} + void reset() { did_update(Source::ES); did_update(Source::CS);