From 60cec9fc67d6523a666935140482658617c37ad3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 13 Nov 2023 11:45:17 -0500 Subject: [PATCH 1/8] Expand commentary. --- .../Implementation/PerformImplementation.hpp | 10 ++- InstructionSets/x86/Instruction.hpp | 72 +++++++++++++------ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 0016495bd..cd4c1b48e 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -211,7 +211,15 @@ template < case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source_r(), context); return; case Operation::DIV: Primitive::div(pair_high(), pair_low(), source_r(), context); return; case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source_r(), context); return; - case Operation::IDIV_REP: Primitive::idiv(pair_high(), pair_low(), source_r(), context); return; + case Operation::IDIV_REP: + if constexpr (ContextT::model == Model::i8086) { + Primitive::idiv(pair_high(), pair_low(), source_r(), context); + break; + } else { + // TODO: perform LEAVE as of the 80186. + static_assert(int(Operation::IDIV_REP) == int(Operation::LEAVE)); + } + return; case Operation::INC: Primitive::inc(destination_rmw(), context); break; case Operation::DEC: Primitive::dec(destination_rmw(), context); break; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 4fc19a297..da62be4d1 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -240,11 +240,11 @@ enum class Operation: uint8_t { /// Checks whether the signed value in the destination register is within the bounds /// stored at the location indicated by the source register, which will point to two - /// 16- or 32-bit words, the first being a signed lower bound and the signed upper. + /// 16- or 32-bit words, the first being a signed lower bound and the second + /// a signed upper. /// Raises a bounds exception if not. BOUND = SETMOC, - /// Create stack frame. See operand() for the nesting level and offset() /// for the dynamic storage size. ENTER, @@ -272,42 +272,70 @@ enum class Operation: uint8_t { // 80286 additions. // - // TODO: expand detail on all operations below. - - /// Adjusts requested privilege level. + /// Read a segment selector from the destination and one from the source. + /// If the destination RPL is less than the source, set ZF and set the destination RPL to the source. + /// Otherwise clear ZF and don't modify the destination selector. ARPL, - /// Clears the task-switched flag. + /// Clears the task-switched flag in CR0. CLTS, /// Loads access rights. LAR, - /// Loads the global descriptor table. + /// Loads the global descriptor table register from the source. + /// 32-bit operand: read a 16-bit limit followed by a 32-bit base. + /// 16-bit operand: read a 16-bit limit followed by a 24-bit base. LGDT, - /// Loads the interrupt descriptor table. - LIDT, - /// Loads the local descriptor table. - LLDT, - /// Stores the global descriptor table. + /// Stores the global descriptor table register at the destination; + /// Always stores a 16-bit limit followed by a 32-bit base though + /// the highest byte may be zero. SGDT, - /// Stores the interrupt descriptor table. + + /// Loads the interrupt descriptor table register from the source. + /// 32-bit operand: read a 16-bit limit followed by a 32-bit base. + /// 16-bit operand: read a 16-bit limit followed by a 24-bit base. + LIDT, + /// Stores the interrupt descriptor table register at the destination. + /// Always stores a 16-bit limit followed by a 32-bit base though + /// the highest byte may be zero. SIDT, - /// Stores the local descriptor table. + + /// Loads the local descriptor table register. + /// The source will contain a segment selector pointing into the local descriptor table. + /// That selector is loaded into the local descriptor table register, along with the corresponding + /// segment limit and base from the global descriptor table. + LLDT, + /// Stores the local descriptor table register. SLDT, - /// Verifies a segment for reading. + /// Verifies the segment indicated by source for reading, setting ZF. + /// + /// ZF is set if: (i) the selector is not null; (ii) the selector is within GDT or LDT bounds; + /// (iii) the selector points to code or data; (iv) the segment is readable; + /// (v) the segment is either a conforming code segment, or the segment's DPL + /// is greater than or equal to both the CPL and the selector's RPL. + /// + /// Otherwise ZF is clear. VERR, - /// Verifies a segment for writing. + /// Verifies a segment for writing. Operates as per VERR but checks for writeability + /// rather than readability. VERW, - /// Loads the machine status word. + /// Loads a 16-bit value from source into the machine status word. + /// The low order four bits of the source are copied into CR0, with the caveat + /// that if PE is set, the processor will enter protected mode, but if PE is clear + /// then there will be no change in protected mode. + /// + /// Usurped in function by MOV CR0 as of the 80286. LMSW, - /// Stores the machine status word. + /// Stores the machine status word, i.e. copies the low 16 bits of CR0 into the destination. SMSW, - /// Loads a segment limit + + /// Load the segment limit from descriptor specified by source into destination, + /// setting ZF if successful. LSL, - /// Loads the task register. + /// Load the source operand into the segment selector field of the task register. LTR, - /// Stores the task register. + /// Store the segment seleector of the task register into the destination. STR, /// Three-operand form of IMUL; multiply the immediate by the source and write to the destination. @@ -320,6 +348,8 @@ enum class Operation: uint8_t { // 80386 additions. // + // TODO: expand detail on all operations below. + /// Loads a pointer to FS. LFS, /// Loads a pointer to GS. From 1552500b101ca7ccaf4422155e5e9ff5ee85e2bb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 13 Nov 2023 22:33:46 -0500 Subject: [PATCH 2/8] Implement BOUND. --- .../x86/Implementation/FlowControl.hpp | 22 +++++++++++++++- .../Implementation/PerformImplementation.hpp | 2 +- InstructionSets/x86/Interrupts.hpp | 25 +++++++++++++++---- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/InstructionSets/x86/Implementation/FlowControl.hpp b/InstructionSets/x86/Implementation/FlowControl.hpp index a92a094a2..a247a42ed 100644 --- a/InstructionSets/x86/Implementation/FlowControl.hpp +++ b/InstructionSets/x86/Implementation/FlowControl.hpp @@ -213,7 +213,27 @@ void into( ContextT &context ) { if(context.flags.template flag()) { - interrupt(Interrupt::OnOverflow, context); + interrupt(Interrupt::Overflow, context); + } +} + +template +void bound( + const InstructionT &instruction, + read_t destination, + read_t source, + ContextT &context +) { + using sIntT = typename std::make_signed::type; + + const auto source_segment = instruction.data_segment(); + context.memory.preauthorise_read(source_segment, source, 2*sizeof(IntT)); + const sIntT lower_bound = sIntT(context.memory.template access(source_segment, source)); + source += 2; + const sIntT upper_bound = sIntT(context.memory.template access(source_segment, source)); + + if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) { + interrupt(Interrupt::BoundRangeExceeded, context); } } diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index cd4c1b48e..a0a1fe684 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -328,8 +328,8 @@ template < } break; } else { - // TODO: perform BOUND as of the 80186. static_assert(int(Operation::SETMOC) == int(Operation::BOUND)); + Primitive::bound(instruction, destination_r(), source_r(), context); } return; diff --git a/InstructionSets/x86/Interrupts.hpp b/InstructionSets/x86/Interrupts.hpp index 51366f11d..f47db4acf 100644 --- a/InstructionSets/x86/Interrupts.hpp +++ b/InstructionSets/x86/Interrupts.hpp @@ -12,11 +12,26 @@ namespace InstructionSet::x86 { enum Interrupt { - DivideError = 0, - SingleStep = 1, - NMI = 2, - OneByte = 3, - OnOverflow = 4, + DivideError = 0, + SingleStep = 1, + NMI = 2, + Breakpoint = 3, + Overflow = 4, + BoundRangeExceeded = 5, + InvalidOpcode = 6, + DeviceNotAvailable = 7, + DoubleFault = 8, + CoprocessorSegmentOverrun = 9, + InvalidTSS = 10, + SegmentNotPresent = 11, + StackSegmentFault = 12, + GeneralProtectionFault = 13, + PageFault = 14, + /* 15 is reserved */ + FloatingPointException = 16, + AlignmentCheck = 17, + MachineCheck = 18, + }; } From 6c405680f271289aa0212b3ef9d00568f2a539fb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 10:42:06 -0500 Subject: [PATCH 3/8] Implement PUSHA, POPA. --- .../Implementation/PerformImplementation.hpp | 7 ++- InstructionSets/x86/Implementation/Stack.hpp | 59 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index a0a1fe684..999aafe3f 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -348,8 +348,11 @@ template < Primitive::push(source_rmw(), context); // PUSH SP modifies SP before pushing it; // hence PUSH is sometimes read-modify-write. break; - case Operation::POPF: Primitive::popf(context); return; - case Operation::PUSHF: Primitive::pushf(context); return; + + case Operation::POPF: Primitive::popf(context); return; + case Operation::PUSHF: Primitive::pushf(context); return; + case Operation::POPA: Primitive::popa(context); return; + case Operation::PUSHA: Primitive::pusha(context); return; case Operation::CMPS: Primitive::cmps(instruction, eCX(), eSI(), eDI(), context); diff --git a/InstructionSets/x86/Implementation/Stack.hpp b/InstructionSets/x86/Implementation/Stack.hpp index b24619a15..d3d44dbc2 100644 --- a/InstructionSets/x86/Implementation/Stack.hpp +++ b/InstructionSets/x86/Implementation/Stack.hpp @@ -11,6 +11,8 @@ #include "../AccessType.hpp" +#include + namespace InstructionSet::x86::Primitive { // The below takes a reference in order properly to handle PUSH SP, @@ -89,6 +91,63 @@ void pushf( push(value, context); } +template +void popa( + ContextT &context +) { + if constexpr (!std::is_same_v) { + context.memory.preauthorise_stack_read(sizeof(IntT) * 8); + if constexpr (std::is_same_v) { + context.registers.edi() = pop(context); + context.registers.esi() = pop(context); + context.registers.ebp() = pop(context); + context.registers.esp() += 4; + context.registers.ebx() = pop(context); + context.registers.edx() = pop(context); + context.registers.ecx() = pop(context); + context.registers.eax() = pop(context); + } else { + context.registers.di() = pop(context); + context.registers.si() = pop(context); + context.registers.bp() = pop(context); + context.registers.sp() += 2; + context.registers.bx() = pop(context); + context.registers.dx() = pop(context); + context.registers.cx() = pop(context); + context.registers.ax() = pop(context); + } + } +} + +template +void pusha( + ContextT &context +) { + if constexpr (!std::is_same_v) { + context.memory.preauthorise_stack_read(sizeof(IntT) * 8); + IntT initial_sp = context.registers.sp(); + if constexpr (std::is_same_v) { + push(context.registers.eax(), context); + push(context.registers.ecx(), context); + push(context.registers.edx(), context); + push(context.registers.ebx(), context); + push(initial_sp, context); + push(context.registers.ebp(), context); + push(context.registers.esi(), context); + push(context.registers.esi(), context); + } else { + push(context.registers.ax(), context); + push(context.registers.cx(), context); + push(context.registers.dx(), context); + push(context.registers.bx(), context); + push(initial_sp, context); + push(context.registers.bp(), context); + push(context.registers.si(), context); + push(context.registers.si(), context); + } + } +} + } #endif /* Stack_hpp */ From ac826f90c372f7014e1a2408a249ed9e667e4bb1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 10:56:00 -0500 Subject: [PATCH 4/8] Formalise a separate manager of segments. --- .../Implementation/PerformImplementation.hpp | 32 +++++-- InstructionSets/x86/Implementation/Stack.hpp | 82 ++++++++-------- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 95 ++++++++++++------- 3 files changed, 125 insertions(+), 84 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 999aafe3f..988c9b774 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -256,13 +256,13 @@ template < case Operation::LDS: if constexpr (data_size == DataSize::Word) { Primitive::ld(instruction, destination_w(), context); - context.registers.did_update(Source::DS); + context.segments.did_update(Source::DS); } return; case Operation::LES: if constexpr (data_size == DataSize::Word) { Primitive::ld(instruction, destination_w(), context); - context.registers.did_update(Source::ES); + context.segments.did_update(Source::ES); } return; @@ -270,7 +270,7 @@ template < case Operation::MOV: Primitive::mov(destination_w(), source_r()); if constexpr (std::is_same_v) { - context.registers.did_update(instruction.destination().source()); + context.segments.did_update(instruction.destination().source()); } break; @@ -341,7 +341,7 @@ template < case Operation::POP: destination_w() = Primitive::pop(context); if constexpr (std::is_same_v) { - context.registers.did_update(instruction.destination().source()); + context.segments.did_update(instruction.destination().source()); } break; case Operation::PUSH: @@ -349,10 +349,26 @@ template < // hence PUSH is sometimes read-modify-write. break; - case Operation::POPF: Primitive::popf(context); return; - case Operation::PUSHF: Primitive::pushf(context); return; - case Operation::POPA: Primitive::popa(context); return; - case Operation::PUSHA: Primitive::pusha(context); return; + case Operation::POPF: + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::popf(context); + } + return; + case Operation::PUSHF: + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::pushf(context); + } + return; + case Operation::POPA: + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::popa(context); + } + return; + case Operation::PUSHA: + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::pusha(context); + } + return; case Operation::CMPS: Primitive::cmps(instruction, eCX(), eSI(), eDI(), context); diff --git a/InstructionSets/x86/Implementation/Stack.hpp b/InstructionSets/x86/Implementation/Stack.hpp index d3d44dbc2..760ac6d9a 100644 --- a/InstructionSets/x86/Implementation/Stack.hpp +++ b/InstructionSets/x86/Implementation/Stack.hpp @@ -95,27 +95,25 @@ template void popa( ContextT &context ) { - if constexpr (!std::is_same_v) { - context.memory.preauthorise_stack_read(sizeof(IntT) * 8); - if constexpr (std::is_same_v) { - context.registers.edi() = pop(context); - context.registers.esi() = pop(context); - context.registers.ebp() = pop(context); - context.registers.esp() += 4; - context.registers.ebx() = pop(context); - context.registers.edx() = pop(context); - context.registers.ecx() = pop(context); - context.registers.eax() = pop(context); - } else { - context.registers.di() = pop(context); - context.registers.si() = pop(context); - context.registers.bp() = pop(context); - context.registers.sp() += 2; - context.registers.bx() = pop(context); - context.registers.dx() = pop(context); - context.registers.cx() = pop(context); - context.registers.ax() = pop(context); - } + context.memory.preauthorise_stack_read(sizeof(IntT) * 8); + if constexpr (std::is_same_v) { + context.registers.edi() = pop(context); + context.registers.esi() = pop(context); + context.registers.ebp() = pop(context); + context.registers.esp() += 4; + context.registers.ebx() = pop(context); + context.registers.edx() = pop(context); + context.registers.ecx() = pop(context); + context.registers.eax() = pop(context); + } else { + context.registers.di() = pop(context); + context.registers.si() = pop(context); + context.registers.bp() = pop(context); + context.registers.sp() += 2; + context.registers.bx() = pop(context); + context.registers.dx() = pop(context); + context.registers.cx() = pop(context); + context.registers.ax() = pop(context); } } @@ -123,28 +121,26 @@ template void pusha( ContextT &context ) { - if constexpr (!std::is_same_v) { - context.memory.preauthorise_stack_read(sizeof(IntT) * 8); - IntT initial_sp = context.registers.sp(); - if constexpr (std::is_same_v) { - push(context.registers.eax(), context); - push(context.registers.ecx(), context); - push(context.registers.edx(), context); - push(context.registers.ebx(), context); - push(initial_sp, context); - push(context.registers.ebp(), context); - push(context.registers.esi(), context); - push(context.registers.esi(), context); - } else { - push(context.registers.ax(), context); - push(context.registers.cx(), context); - push(context.registers.dx(), context); - push(context.registers.bx(), context); - push(initial_sp, context); - push(context.registers.bp(), context); - push(context.registers.si(), context); - push(context.registers.si(), context); - } + context.memory.preauthorise_stack_read(sizeof(IntT) * 8); + IntT initial_sp = context.registers.sp(); + if constexpr (std::is_same_v) { + push(context.registers.eax(), context); + push(context.registers.ecx(), context); + push(context.registers.edx(), context); + push(context.registers.ebx(), context); + push(initial_sp, context); + push(context.registers.ebp(), context); + push(context.registers.esi(), context); + push(context.registers.esi(), context); + } else { + push(context.registers.ax(), context); + push(context.registers.cx(), context); + push(context.registers.dx(), context); + push(context.registers.bx(), context); + push(initial_sp, context); + push(context.registers.bp(), context); + push(context.registers.si(), context); + push(context.registers.si(), context); } } diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 069be119c..35841e1b5 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -68,7 +68,6 @@ struct Registers { uint16_t &di() { return di_; } uint16_t es_, cs_, ds_, ss_; - uint32_t es_base_, cs_base_, ds_base_, ss_base_; uint16_t ip_; uint16_t &ip() { return ip_; } @@ -78,17 +77,10 @@ struct Registers { uint16_t &ds() { return ds_; } uint16_t &ss() { return ss_; } - using Source = InstructionSet::x86::Source; - /// Posted by @c perform after any operation which *might* have affected a segment register. - void did_update(Source segment) { - switch(segment) { - default: break; - case Source::ES: es_base_ = es_ << 4; break; - case Source::CS: cs_base_ = cs_ << 4; break; - case Source::DS: ds_base_ = ds_ << 4; break; - case Source::SS: ss_base_ = ss_ << 4; break; - } - } + const uint16_t es() const { return es_; } + const uint16_t cs() const { return cs_; } + const uint16_t ds() const { return ds_; } + const uint16_t ss() const { return ss_; } bool operator ==(const Registers &rhs) const { return @@ -104,19 +96,52 @@ struct Registers { cs_ == rhs.cs_ && ds_ == rhs.ds_ && si_ == rhs.si_ && - ip_ == rhs.ip_ && - es_base_ == rhs.es_base_ && - cs_base_ == rhs.cs_base_ && - ds_base_ == rhs.ds_base_ && - ss_base_ == rhs.ss_base_; + ip_ == rhs.ip_; } }; +class Segments { + public: + Segments(const Registers ®isters) : registers_(registers) {} + + using Source = InstructionSet::x86::Source; + + /// Posted by @c perform after any operation which *might* have affected a segment register. + void did_update(Source segment) { + switch(segment) { + default: break; + case Source::ES: es_base_ = registers_.es() << 4; break; + case Source::CS: cs_base_ = registers_.cs() << 4; break; + case Source::DS: ds_base_ = registers_.ds() << 4; break; + case Source::SS: ss_base_ = registers_.ss() << 4; break; + } + } + + void reset() { + did_update(Source::ES); + did_update(Source::CS); + did_update(Source::DS); + did_update(Source::SS); + } + + uint32_t es_base_, cs_base_, ds_base_, ss_base_; + + bool operator ==(const Segments &rhs) const { + return + es_base_ != rhs.es_base_ && + cs_base_ != rhs.cs_base_ && + ds_base_ != rhs.ds_base_ && + ss_base_ != rhs.ss_base_; + } + + private: + const Registers ®isters_; +}; struct Memory { public: using AccessType = InstructionSet::x86::AccessType; // Constructor. - Memory(Registers ®isters) : registers_(registers) { + Memory(Registers ®isters, const Segments &segments) : registers_(registers), segments_(segments) { memory.resize(1024*1024); } @@ -237,6 +262,7 @@ struct Memory { std::unordered_map tags; std::vector memory; const Registers ®isters_; + const Segments &segments_; void preauthorise(uint32_t address) { preauthorisations.insert(address); @@ -256,10 +282,10 @@ struct Memory { uint32_t segment_base(InstructionSet::x86::Source segment) { using Source = InstructionSet::x86::Source; switch(segment) { - default: return registers_.ds_base_; - case Source::ES: return registers_.es_base_; - case Source::CS: return registers_.cs_base_; - case Source::SS: return registers_.ss_base_; + default: return segments_.ds_base_; + case Source::ES: return segments_.es_base_; + case Source::CS: return segments_.cs_base_; + case Source::SS: return segments_.ss_base_; } } @@ -339,8 +365,8 @@ struct IO { }; class FlowController { public: - FlowController(Memory &memory, Registers ®isters, Flags &flags) : - memory_(memory), registers_(registers), flags_(flags) {} + FlowController(Memory &memory, Registers ®isters, Segments &segments, Flags &flags) : + memory_(memory), registers_(registers), segments_(segments), flags_(flags) {} // Requirements for perform. void jump(uint16_t address) { @@ -349,7 +375,7 @@ class FlowController { void jump(uint16_t segment, uint16_t address) { registers_.cs_ = segment; - registers_.did_update(Registers::Source::CS); + segments_.did_update(Segments::Source::CS); registers_.ip_ = address; } @@ -371,6 +397,7 @@ class FlowController { private: Memory &memory_; Registers ®isters_; + Segments &segments_; Flags &flags_; bool should_repeat_ = false; }; @@ -378,12 +405,16 @@ class FlowController { struct ExecutionSupport { Flags flags; Registers registers; + Segments segments; Memory memory; FlowController flow_controller; IO io; static constexpr auto model = InstructionSet::x86::Model::i8086; - ExecutionSupport(): memory(registers), flow_controller(memory, registers, flags) {} + ExecutionSupport(): + memory(registers, segments), + segments(registers), + flow_controller(memory, registers, segments, flags) {} void clear() { memory.clear(); @@ -560,11 +591,6 @@ struct FailedExecution { registers.ss_ = [value[@"ss"] intValue]; registers.ip_ = [value[@"ip"] intValue]; - registers.did_update(Registers::Source::ES); - registers.did_update(Registers::Source::CS); - registers.did_update(Registers::Source::DS); - registers.did_update(Registers::Source::SS); - const uint16_t flags_value = [value[@"flags"] intValue]; flags.set(flags_value); @@ -610,6 +636,7 @@ struct FailedExecution { [self populate:initial_registers flags:initial_flags value:initial_state[@"regs"]]; execution_support.flags = initial_flags; execution_support.registers = initial_registers; + execution_support.segments.reset(); // Execute instruction. // @@ -657,8 +684,11 @@ struct FailedExecution { break; } + Segments intended_segments(intended_registers); [self populate:intended_registers flags:intended_flags value:final_state[@"regs"]]; - const bool registersEqual = intended_registers == execution_support.registers; + intended_segments.reset(); + + const bool registersEqual = intended_registers == execution_support.registers && intended_segments == execution_support.segments; const bool flagsEqual = (intended_flags.get() & flags_mask) == (execution_support.flags.get() & flags_mask); // Exit if no issues were found. @@ -703,7 +733,6 @@ struct FailedExecution { non_exception_registers.sp() = execution_support.registers.sp(); non_exception_registers.ax() = execution_support.registers.ax(); non_exception_registers.cs() = execution_support.registers.cs(); - non_exception_registers.cs_base_ = execution_support.registers.cs_base_; if(non_exception_registers == execution_support.registers) { failure_list = &permitted_failures; From 2533fd2da927176f0cccbea15e393c0449c7089e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 11:14:28 -0500 Subject: [PATCH 5/8] Fix segment comparisons. --- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 35841e1b5..25c6e7967 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -127,10 +127,10 @@ class Segments { bool operator ==(const Segments &rhs) const { return - es_base_ != rhs.es_base_ && - cs_base_ != rhs.cs_base_ && - ds_base_ != rhs.ds_base_ && - ss_base_ != rhs.ss_base_; + es_base_ == rhs.es_base_ && + cs_base_ == rhs.cs_base_ && + ds_base_ == rhs.ds_base_ && + ss_base_ == rhs.ss_base_; } private: From aafa7de536861b6f0362cec9b98126895ba9608f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 11:39:36 -0500 Subject: [PATCH 6/8] Implement LEAVE, almost. --- .../x86/Implementation/PerformImplementation.hpp | 4 +++- InstructionSets/x86/Implementation/Stack.hpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 988c9b774..e1ff68393 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -216,8 +216,10 @@ template < Primitive::idiv(pair_high(), pair_low(), source_r(), context); break; } else { - // TODO: perform LEAVE as of the 80186. static_assert(int(Operation::IDIV_REP) == int(Operation::LEAVE)); + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::leave(); + } } return; diff --git a/InstructionSets/x86/Implementation/Stack.hpp b/InstructionSets/x86/Implementation/Stack.hpp index 760ac6d9a..892b2f4e3 100644 --- a/InstructionSets/x86/Implementation/Stack.hpp +++ b/InstructionSets/x86/Implementation/Stack.hpp @@ -144,6 +144,21 @@ void pusha( } } +template +void leave( + ContextT &context +) { + // TODO: should use StackAddressSize to determine assignment of bp to sp. + // Probably make that a static constexpr on registers. + if constexpr (std::is_same_v) { + context.registers.esp() = context.registers.ebp(); + context.registers.ebp() = pop(context); + } else { + context.registers.sp() = context.registers.bp(); + context.registers.bp() = pop(context); + } +} + } #endif /* Stack_hpp */ From a22ac2f88be242eee02cb081b04c9e40b364ea8a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 11:39:44 -0500 Subject: [PATCH 7/8] Move towards privacy. --- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 25c6e7967..fcdd84851 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -33,71 +33,75 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" using Flags = InstructionSet::x86::Flags; struct Registers { - CPU::RegisterPair16 ax_; - uint8_t &al() { return ax_.halves.low; } - uint8_t &ah() { return ax_.halves.high; } - uint16_t &ax() { return ax_.full; } + public: + static constexpr bool is_32bit = false; - CPU::RegisterPair16 &axp() { return ax_; } + uint8_t &al() { return ax_.halves.low; } + uint8_t &ah() { return ax_.halves.high; } + uint16_t &ax() { return ax_.full; } - CPU::RegisterPair16 cx_; - uint8_t &cl() { return cx_.halves.low; } - uint8_t &ch() { return cx_.halves.high; } - uint16_t &cx() { return cx_.full; } + CPU::RegisterPair16 &axp() { return ax_; } - CPU::RegisterPair16 dx_; - uint8_t &dl() { return dx_.halves.low; } - uint8_t &dh() { return dx_.halves.high; } - uint16_t &dx() { return dx_.full; } + uint8_t &cl() { return cx_.halves.low; } + uint8_t &ch() { return cx_.halves.high; } + uint16_t &cx() { return cx_.full; } - CPU::RegisterPair16 bx_; - uint8_t &bl() { return bx_.halves.low; } - uint8_t &bh() { return bx_.halves.high; } - uint16_t &bx() { return bx_.full; } + uint8_t &dl() { return dx_.halves.low; } + uint8_t &dh() { return dx_.halves.high; } + uint16_t &dx() { return dx_.full; } - uint16_t sp_; - uint16_t &sp() { return sp_; } + uint8_t &bl() { return bx_.halves.low; } + uint8_t &bh() { return bx_.halves.high; } + uint16_t &bx() { return bx_.full; } - uint16_t bp_; - uint16_t &bp() { return bp_; } + uint16_t &sp() { return sp_; } + uint16_t &bp() { return bp_; } + uint16_t &si() { return si_; } + uint16_t &di() { return di_; } - uint16_t si_; - uint16_t &si() { return si_; } + uint16_t ip_; + uint16_t &ip() { return ip_; } - uint16_t di_; - uint16_t &di() { return di_; } + uint16_t &es() { return es_; } + uint16_t &cs() { return cs_; } + uint16_t &ds() { return ds_; } + uint16_t &ss() { return ss_; } - uint16_t es_, cs_, ds_, ss_; + const uint16_t es() const { return es_; } + const uint16_t cs() const { return cs_; } + const uint16_t ds() const { return ds_; } + const uint16_t ss() const { return ss_; } - uint16_t ip_; - uint16_t &ip() { return ip_; } + bool operator ==(const Registers &rhs) const { + return + ax_.full == rhs.ax_.full && + cx_.full == rhs.cx_.full && + dx_.full == rhs.dx_.full && + bx_.full == rhs.bx_.full && + sp_ == rhs.sp_ && + bp_ == rhs.bp_ && + si_ == rhs.si_ && + di_ == rhs.di_ && + es_ == rhs.es_ && + cs_ == rhs.cs_ && + ds_ == rhs.ds_ && + si_ == rhs.si_ && + ip_ == rhs.ip_; + } - uint16_t &es() { return es_; } - uint16_t &cs() { return cs_; } - uint16_t &ds() { return ds_; } - uint16_t &ss() { return ss_; } + // TODO: make the below private and use a friend class for test population, to ensure Perform + // is free of direct accesses. +// private: + CPU::RegisterPair16 ax_; + CPU::RegisterPair16 cx_; + CPU::RegisterPair16 dx_; + CPU::RegisterPair16 bx_; - const uint16_t es() const { return es_; } - const uint16_t cs() const { return cs_; } - const uint16_t ds() const { return ds_; } - const uint16_t ss() const { return ss_; } - - bool operator ==(const Registers &rhs) const { - return - ax_.full == rhs.ax_.full && - cx_.full == rhs.cx_.full && - dx_.full == rhs.dx_.full && - bx_.full == rhs.bx_.full && - sp_ == rhs.sp_ && - bp_ == rhs.bp_ && - si_ == rhs.si_ && - di_ == rhs.di_ && - es_ == rhs.es_ && - cs_ == rhs.cs_ && - ds_ == rhs.ds_ && - si_ == rhs.si_ && - ip_ == rhs.ip_; - } + uint16_t sp_; + uint16_t bp_; + uint16_t si_; + uint16_t di_; + uint16_t es_, cs_, ds_, ss_; }; class Segments { public: @@ -163,14 +167,14 @@ struct Memory { // Preauthorisation call-ins. // void preauthorise_stack_write(uint32_t length) { - uint16_t sp = registers_.sp_; + uint16_t sp = registers_.sp(); while(length--) { --sp; preauthorise(InstructionSet::x86::Source::SS, sp); } } void preauthorise_stack_read(uint32_t length) { - uint16_t sp = registers_.sp_; + uint16_t sp = registers_.sp(); while(length--) { preauthorise(InstructionSet::x86::Source::SS, sp); ++sp; @@ -261,7 +265,7 @@ struct Memory { std::unordered_set preauthorisations; std::unordered_map tags; std::vector memory; - const Registers ®isters_; + Registers ®isters_; const Segments &segments_; void preauthorise(uint32_t address) { From f83d2a8740b4c5a2431b1dae916ec7750f67b932 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 14 Nov 2023 16:23:24 -0500 Subject: [PATCH 8/8] Take a swing at ENTER. --- .../Implementation/PerformImplementation.hpp | 2 +- InstructionSets/x86/Implementation/Stack.hpp | 49 ++++++++++++++++--- InstructionSets/x86/Instruction.hpp | 14 +++++- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index e1ff68393..1d1422fe5 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -317,8 +317,8 @@ template < Primitive::setmo(destination_w(), context); break; } else { - // TODO: perform ENTER as of the 80186. static_assert(int(Operation::SETMO) == int(Operation::ENTER)); + Primitive::enter(instruction, context); } return; case Operation::SETMOC: diff --git a/InstructionSets/x86/Implementation/Stack.hpp b/InstructionSets/x86/Implementation/Stack.hpp index 892b2f4e3..dfefb18d2 100644 --- a/InstructionSets/x86/Implementation/Stack.hpp +++ b/InstructionSets/x86/Implementation/Stack.hpp @@ -22,13 +22,13 @@ void push( IntT &value, ContextT &context ) { - context.registers.sp_ -= sizeof(IntT); + context.registers.sp() -= sizeof(IntT); if constexpr (preauthorised) { - context.memory.template preauthorised_write(Source::SS, context.registers.sp_, value); + context.memory.template preauthorised_write(Source::SS, context.registers.sp(), value); } else { context.memory.template access( Source::SS, - context.registers.sp_) = value; + context.registers.sp()) = value; } context.memory.template write_back(); } @@ -39,8 +39,8 @@ IntT pop( ) { const auto value = context.memory.template access( Source::SS, - context.registers.sp_); - context.registers.sp_ += sizeof(IntT); + context.registers.sp()); + context.registers.sp() += sizeof(IntT); return value; } @@ -144,12 +144,49 @@ void pusha( } } +template +void enter( + const InstructionT &instruction, + ContextT &context +) { + // TODO: all non-16bit address sizes. + const auto alloc_size = instruction.dynamic_storage_size(); + const auto nesting_level = instruction.nesting_level() & 0x1f; + + // Preauthorse contents that'll be fetched via BP. + const auto copied_pointers = nesting_level - 2; + if(copied_pointers > 0) { + context.memory.preauthorise_read( + Source::SS, + context.registers.bp() - copied_pointers * sizeof(uint16_t), + copied_pointers * sizeof(uint16_t) + ); + } + + // Preauthorse stack activity. + context.memory.preauthorise_stack_write((1 + copied_pointers) * sizeof(uint16_t)); + + // Push BP and grab the end of frame. + push(context.registers.bp(), context); + const auto frame = context.registers.sp(); + + // Copy data as per the nesting level. + for(int c = 1; c < nesting_level; c++) { + context.registers.bp() -= 2; + + const auto value = context.memory.template preauthorised_read(Source::SS, context.registers.bp()); + push(value); + } + + // Set final BP. + context.registers.bp() = frame; +} + template void leave( ContextT &context ) { // TODO: should use StackAddressSize to determine assignment of bp to sp. - // Probably make that a static constexpr on registers. if constexpr (std::is_same_v) { context.registers.esp() = context.registers.ebp(); context.registers.ebp() = pop(context); diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index da62be4d1..baaf3ff6f 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -245,8 +245,8 @@ enum class Operation: uint8_t { /// Raises a bounds exception if not. BOUND = SETMOC, - /// Create stack frame. See operand() for the nesting level and offset() - /// for the dynamic storage size. + /// Create stack frame. See the Instruction getters `nesting_level()` + /// and `dynamic_storage_size()`. ENTER, /// Procedure exit; copies BP to SP, then pops a new BP from the stack. LEAVE, @@ -815,6 +815,11 @@ template class Instruction { return ops[has_operand()]; } + /// @returns The nesting level argument supplied to an ENTER. + constexpr ImmediateT nesting_level() const { + return operand(); + } + /// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will /// have a segment value of `1234h`. constexpr uint16_t segment() const { @@ -833,6 +838,11 @@ template class Instruction { return DisplacementT(offset()); } + /// @returns The dynamic storage size argument supplied to an ENTER. + constexpr ImmediateT dynamic_storage_size() const { + return displacement(); + } + // Standard comparison operator. constexpr bool operator ==(const Instruction &rhs) const { if( operation_ != rhs.operation_ ||