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 0016495bd..1d1422fe5 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -211,7 +211,17 @@ 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 { + static_assert(int(Operation::IDIV_REP) == int(Operation::LEAVE)); + if constexpr (std::is_same_v || std::is_same_v) { + Primitive::leave(); + } + } + return; case Operation::INC: Primitive::inc(destination_rmw(), context); break; case Operation::DEC: Primitive::dec(destination_rmw(), context); break; @@ -248,13 +258,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; @@ -262,7 +272,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; @@ -307,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: @@ -320,8 +330,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; @@ -333,15 +343,34 @@ 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: 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: + 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 b24619a15..dfefb18d2 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, @@ -20,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(); } @@ -37,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; } @@ -89,6 +91,111 @@ void pushf( push(value, context); } +template +void popa( + ContextT &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); + } +} + +template +void pusha( + ContextT &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); + } +} + +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. + 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 */ diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 4fc19a297..baaf3ff6f 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -240,13 +240,13 @@ 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. + /// 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, @@ -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. @@ -785,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 { @@ -803,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_ || 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, + }; } diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 069be119c..fcdd84851 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -33,90 +33,119 @@ 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_; - uint32_t es_base_, cs_base_, ds_base_, ss_base_; + 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_; } - - uint16_t &es() { return es_; } - uint16_t &cs() { return cs_; } - 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; + 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_; } - } - 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_ && - es_base_ == rhs.es_base_ && - cs_base_ == rhs.cs_base_ && - ds_base_ == rhs.ds_base_ && - ss_base_ == rhs.ss_base_; - } + // 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_; + + uint16_t sp_; + uint16_t bp_; + uint16_t si_; + uint16_t di_; + uint16_t es_, cs_, ds_, ss_; +}; +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); } @@ -138,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; @@ -236,7 +265,8 @@ 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) { preauthorisations.insert(address); @@ -256,10 +286,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 +369,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 +379,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 +401,7 @@ class FlowController { private: Memory &memory_; Registers ®isters_; + Segments &segments_; Flags &flags_; bool should_repeat_ = false; }; @@ -378,12 +409,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 +595,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 +640,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 +688,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 +737,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;