1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 16:29:20 +00:00

Merge pull request #1205 from TomHarte/80286Preparation

Clear a path towards implementing the 80286
This commit is contained in:
Thomas Harte 2023-11-15 10:41:26 -05:00 committed by GitHub
commit 70a4d59517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 371 additions and 127 deletions

View File

@ -213,7 +213,27 @@ void into(
ContextT &context
) {
if(context.flags.template flag<Flag::Overflow>()) {
interrupt(Interrupt::OnOverflow, context);
interrupt(Interrupt::Overflow, context);
}
}
template <typename IntT, typename InstructionT, typename ContextT>
void bound(
const InstructionT &instruction,
read_t<IntT> destination,
read_t<IntT> source,
ContextT &context
) {
using sIntT = typename std::make_signed<IntT>::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<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
source += 2;
const sIntT upper_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) {
interrupt(Interrupt::BoundRangeExceeded, context);
}
}

View File

@ -211,7 +211,17 @@ template <
case Operation::IMUL_1: Primitive::imul<IntT>(pair_high(), pair_low(), source_r(), context); return;
case Operation::DIV: Primitive::div<IntT>(pair_high(), pair_low(), source_r(), context); return;
case Operation::IDIV: Primitive::idiv<false, IntT>(pair_high(), pair_low(), source_r(), context); return;
case Operation::IDIV_REP: Primitive::idiv<true, IntT>(pair_high(), pair_low(), source_r(), context); return;
case Operation::IDIV_REP:
if constexpr (ContextT::model == Model::i8086) {
Primitive::idiv<true, IntT>(pair_high(), pair_low(), source_r(), context);
break;
} else {
static_assert(int(Operation::IDIV_REP) == int(Operation::LEAVE));
if constexpr (std::is_same_v<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
Primitive::leave<IntT>();
}
}
return;
case Operation::INC: Primitive::inc<IntT>(destination_rmw(), context); break;
case Operation::DEC: Primitive::dec<IntT>(destination_rmw(), context); break;
@ -248,13 +258,13 @@ template <
case Operation::LDS:
if constexpr (data_size == DataSize::Word) {
Primitive::ld<Source::DS>(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<Source::ES>(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<IntT>(destination_w(), source_r());
if constexpr (std::is_same_v<IntT, uint16_t>) {
context.registers.did_update(instruction.destination().source());
context.segments.did_update(instruction.destination().source());
}
break;
@ -307,8 +317,8 @@ template <
Primitive::setmo<IntT>(destination_w(), context);
break;
} else {
// TODO: perform ENTER as of the 80186.
static_assert(int(Operation::SETMO) == int(Operation::ENTER));
Primitive::enter<IntT>(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<IntT>(instruction, destination_r(), source_r(), context);
}
return;
@ -333,15 +343,34 @@ template <
case Operation::POP:
destination_w() = Primitive::pop<IntT, false>(context);
if constexpr (std::is_same_v<IntT, uint16_t>) {
context.registers.did_update(instruction.destination().source());
context.segments.did_update(instruction.destination().source());
}
break;
case Operation::PUSH:
Primitive::push<IntT, false>(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<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
Primitive::popf(context);
}
return;
case Operation::PUSHF:
if constexpr (std::is_same_v<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
Primitive::pushf(context);
}
return;
case Operation::POPA:
if constexpr (std::is_same_v<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
Primitive::popa<IntT>(context);
}
return;
case Operation::PUSHA:
if constexpr (std::is_same_v<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
Primitive::pusha<IntT>(context);
}
return;
case Operation::CMPS:
Primitive::cmps<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), context);

View File

@ -11,6 +11,8 @@
#include "../AccessType.hpp"
#include <type_traits>
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<IntT>(Source::SS, context.registers.sp_, value);
context.memory.template preauthorised_write<IntT>(Source::SS, context.registers.sp(), value);
} else {
context.memory.template access<IntT, AccessType::Write>(
Source::SS,
context.registers.sp_) = value;
context.registers.sp()) = value;
}
context.memory.template write_back<IntT>();
}
@ -37,8 +39,8 @@ IntT pop(
) {
const auto value = context.memory.template access<IntT, preauthorised ? AccessType::PreauthorisedRead : AccessType::Read>(
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<uint16_t, false>(value, context);
}
template <typename IntT, typename ContextT>
void popa(
ContextT &context
) {
context.memory.preauthorise_stack_read(sizeof(IntT) * 8);
if constexpr (std::is_same_v<IntT, uint32_t>) {
context.registers.edi() = pop<uint32_t, true>(context);
context.registers.esi() = pop<uint32_t, true>(context);
context.registers.ebp() = pop<uint32_t, true>(context);
context.registers.esp() += 4;
context.registers.ebx() = pop<uint32_t, true>(context);
context.registers.edx() = pop<uint32_t, true>(context);
context.registers.ecx() = pop<uint32_t, true>(context);
context.registers.eax() = pop<uint32_t, true>(context);
} else {
context.registers.di() = pop<uint16_t, true>(context);
context.registers.si() = pop<uint16_t, true>(context);
context.registers.bp() = pop<uint16_t, true>(context);
context.registers.sp() += 2;
context.registers.bx() = pop<uint16_t, true>(context);
context.registers.dx() = pop<uint16_t, true>(context);
context.registers.cx() = pop<uint16_t, true>(context);
context.registers.ax() = pop<uint16_t, true>(context);
}
}
template <typename IntT, typename ContextT>
void pusha(
ContextT &context
) {
context.memory.preauthorise_stack_read(sizeof(IntT) * 8);
IntT initial_sp = context.registers.sp();
if constexpr (std::is_same_v<IntT, uint32_t>) {
push<uint32_t, true>(context.registers.eax(), context);
push<uint32_t, true>(context.registers.ecx(), context);
push<uint32_t, true>(context.registers.edx(), context);
push<uint32_t, true>(context.registers.ebx(), context);
push<uint32_t, true>(initial_sp, context);
push<uint32_t, true>(context.registers.ebp(), context);
push<uint32_t, true>(context.registers.esi(), context);
push<uint32_t, true>(context.registers.esi(), context);
} else {
push<uint16_t, true>(context.registers.ax(), context);
push<uint16_t, true>(context.registers.cx(), context);
push<uint16_t, true>(context.registers.dx(), context);
push<uint16_t, true>(context.registers.bx(), context);
push<uint16_t, true>(initial_sp, context);
push<uint16_t, true>(context.registers.bp(), context);
push<uint16_t, true>(context.registers.si(), context);
push<uint16_t, true>(context.registers.si(), context);
}
}
template <typename IntT, typename InstructionT, typename ContextT>
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<uint16_t, true>(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<uint16_t>(Source::SS, context.registers.bp());
push<uint16_t, true>(value);
}
// Set final BP.
context.registers.bp() = frame;
}
template <typename IntT, typename ContextT>
void leave(
ContextT &context
) {
// TODO: should use StackAddressSize to determine assignment of bp to sp.
if constexpr (std::is_same_v<IntT, uint32_t>) {
context.registers.esp() = context.registers.ebp();
context.registers.ebp() = pop<uint32_t, false>(context);
} else {
context.registers.sp() = context.registers.bp();
context.registers.bp() = pop<uint16_t, false>(context);
}
}
}
#endif /* Stack_hpp */

View File

@ -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<bool is_32bit> 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<bool is_32bit> 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<is_32bit> &rhs) const {
if( operation_ != rhs.operation_ ||

View File

@ -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,
};
}

View File

@ -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 &registers) : 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 &registers_;
};
struct Memory {
public:
using AccessType = InstructionSet::x86::AccessType;
// Constructor.
Memory(Registers &registers) : registers_(registers) {
Memory(Registers &registers, 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<uint32_t> preauthorisations;
std::unordered_map<uint32_t, Tag> tags;
std::vector<uint8_t> memory;
const Registers &registers_;
Registers &registers_;
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 &registers, Flags &flags) :
memory_(memory), registers_(registers), flags_(flags) {}
FlowController(Memory &memory, Registers &registers, 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 &registers_;
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;