Jelajahi Sumber

LibX86+UserspaceEmulator: Introduce AddressSize and OperandSize enums

These replace the bools a32 and o32, which will make implementing
64-bit sizes possible. :^)
Simon Wanner 3 tahun lalu
induk
melakukan
a7268c3c74

+ 3 - 3
Userland/DevTools/UserspaceEmulator/Emulator.cpp

@@ -234,7 +234,7 @@ int Emulator::exec()
     while (!m_shutdown) {
         if (m_steps_til_pause) [[likely]] {
             m_cpu->save_base_eip();
-            auto insn = X86::Instruction::from_stream(*m_cpu, true, true);
+            auto insn = X86::Instruction::from_stream(*m_cpu, X86::OperandSize::Size32, X86::AddressSize::Size32);
             // Exec cycle
             if constexpr (trace) {
                 outln("{:p}  \033[33;1m{}\033[0m", m_cpu->base_eip(), insn.to_string(m_cpu->base_eip(), symbol_provider));
@@ -301,7 +301,7 @@ void Emulator::handle_repl()
     // FIXME: Function names (base, call, jump)
     auto saved_eip = m_cpu->eip();
     m_cpu->save_base_eip();
-    auto insn = X86::Instruction::from_stream(*m_cpu, true, true);
+    auto insn = X86::Instruction::from_stream(*m_cpu, X86::OperandSize::Size32, X86::AddressSize::Size32);
     // FIXME: This does not respect inlining
     //        another way of getting the current function is at need
     if (auto symbol = symbol_at(m_cpu->base_eip()); symbol.has_value()) {
@@ -311,7 +311,7 @@ void Emulator::handle_repl()
     outln("==> {}", create_instruction_line(m_cpu->base_eip(), insn));
     for (int i = 0; i < 7; ++i) {
         m_cpu->save_base_eip();
-        insn = X86::Instruction::from_stream(*m_cpu, true, true);
+        insn = X86::Instruction::from_stream(*m_cpu, X86::OperandSize::Size32, X86::AddressSize::Size32);
         outln("    {}", create_instruction_line(m_cpu->base_eip(), insn));
     }
     // We don't want to increase EIP here, we just want the instructions

+ 75 - 37
Userland/DevTools/UserspaceEmulator/SoftCPU.cpp

@@ -258,9 +258,9 @@ void SoftCPU::do_once_or_repeat(const X86::Instruction& insn, Callback callback)
     if (!insn.has_rep_prefix())
         return callback();
 
-    while (loop_index(insn.a32()).value()) {
+    while (loop_index(insn.address_size()).value()) {
         callback();
-        decrement_loop_index(insn.a32());
+        decrement_loop_index(insn.address_size());
         if constexpr (check_zf) {
             warn_if_flags_tainted("repz/repnz");
             if (insn.rep_prefix() == X86::Prefix::REPZ && !zf())
@@ -1259,11 +1259,11 @@ ALWAYS_INLINE static void do_cmps(SoftCPU& cpu, const X86::Instruction& insn)
 {
     auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
     cpu.do_once_or_repeat<true>(insn, [&] {
-        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.a32()).value() });
-        auto dest = cpu.read_memory<T>({ cpu.es(), cpu.destination_index(insn.a32()).value() });
+        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.address_size()).value() });
+        auto dest = cpu.read_memory<T>({ cpu.es(), cpu.destination_index(insn.address_size()).value() });
         op_sub(cpu, dest, src);
-        cpu.step_source_index(insn.a32(), sizeof(T));
-        cpu.step_destination_index(insn.a32(), sizeof(T));
+        cpu.step_source_index(insn.address_size(), sizeof(T));
+        cpu.step_destination_index(insn.address_size(), sizeof(T));
     });
 }
 
@@ -1771,14 +1771,19 @@ void SoftCPU::IRET(const X86::Instruction&) { TODO_INSN(); }
 
 void SoftCPU::JCXZ_imm8(const X86::Instruction& insn)
 {
-    if (insn.a32()) {
+    switch (insn.address_size()) {
+    case X86::AddressSize::Size32:
         warn_if_uninitialized(ecx(), "jecxz imm8");
         if (ecx().value() == 0)
             set_eip(eip() + (i8)insn.imm8());
-    } else {
+        break;
+    case X86::AddressSize::Size16:
         warn_if_uninitialized(cx(), "jcxz imm8");
         if (cx().value() == 0)
             set_eip(eip() + (i8)insn.imm8());
+        break;
+    default:
+        VERIFY_NOT_REACHED();
     }
 }
 
@@ -1865,9 +1870,9 @@ ALWAYS_INLINE static void do_lods(SoftCPU& cpu, const X86::Instruction& insn)
 {
     auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
     cpu.do_once_or_repeat<true>(insn, [&] {
-        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.a32()).value() });
+        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.address_size()).value() });
         cpu.gpr<T>(X86::RegisterAL) = src;
-        cpu.step_source_index(insn.a32(), sizeof(T));
+        cpu.step_source_index(insn.address_size(), sizeof(T));
     });
 }
 
@@ -1889,40 +1894,55 @@ void SoftCPU::LODSW(const X86::Instruction& insn)
 void SoftCPU::LOOPNZ_imm8(const X86::Instruction& insn)
 {
     warn_if_flags_tainted("loopnz");
-    if (insn.a32()) {
+    switch (insn.address_size()) {
+    case X86::AddressSize::Size32:
         set_ecx({ ecx().value() - 1, ecx().shadow() });
         if (ecx().value() != 0 && !zf())
             set_eip(eip() + (i8)insn.imm8());
-    } else {
+        break;
+    case X86::AddressSize::Size16:
         set_cx({ (u16)(cx().value() - 1), cx().shadow() });
         if (cx().value() != 0 && !zf())
             set_eip(eip() + (i8)insn.imm8());
+        break;
+    default:
+        VERIFY_NOT_REACHED();
     }
 }
 void SoftCPU::LOOPZ_imm8(const X86::Instruction& insn)
 {
     warn_if_flags_tainted("loopz");
-    if (insn.a32()) {
+    switch (insn.address_size()) {
+    case X86::AddressSize::Size32:
         set_ecx({ ecx().value() - 1, ecx().shadow() });
         if (ecx().value() != 0 && zf())
             set_eip(eip() + (i8)insn.imm8());
-    } else {
+        break;
+    case X86::AddressSize::Size16:
         set_cx({ (u16)(cx().value() - 1), cx().shadow() });
         if (cx().value() != 0 && zf())
             set_eip(eip() + (i8)insn.imm8());
+        break;
+    default:
+        VERIFY_NOT_REACHED();
     }
 }
 
 void SoftCPU::LOOP_imm8(const X86::Instruction& insn)
 {
-    if (insn.a32()) {
+    switch (insn.address_size()) {
+    case X86::AddressSize::Size32:
         set_ecx({ ecx().value() - 1, ecx().shadow() });
         if (ecx().value() != 0)
             set_eip(eip() + (i8)insn.imm8());
-    } else {
+        break;
+    case X86::AddressSize::Size16:
         set_cx({ (u16)(cx().value() - 1), cx().shadow() });
         if (cx().value() != 0)
             set_eip(eip() + (i8)insn.imm8());
+        break;
+    default:
+        VERIFY_NOT_REACHED();
     }
 }
 
@@ -1937,10 +1957,10 @@ ALWAYS_INLINE static void do_movs(SoftCPU& cpu, const X86::Instruction& insn)
 {
     auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
     cpu.do_once_or_repeat<false>(insn, [&] {
-        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.a32()).value() });
-        cpu.write_memory<T>({ cpu.es(), cpu.destination_index(insn.a32()).value() }, src);
-        cpu.step_source_index(insn.a32(), sizeof(T));
-        cpu.step_destination_index(insn.a32(), sizeof(T));
+        auto src = cpu.read_memory<T>({ src_segment, cpu.source_index(insn.address_size()).value() });
+        cpu.write_memory<T>({ cpu.es(), cpu.destination_index(insn.address_size()).value() }, src);
+        cpu.step_source_index(insn.address_size(), sizeof(T));
+        cpu.step_destination_index(insn.address_size(), sizeof(T));
     });
 }
 
@@ -2638,9 +2658,9 @@ ALWAYS_INLINE static void do_scas(SoftCPU& cpu, const X86::Instruction& insn)
 {
     cpu.do_once_or_repeat<true>(insn, [&] {
         auto src = cpu.const_gpr<T>(X86::RegisterAL);
-        auto dest = cpu.read_memory<T>({ cpu.es(), cpu.destination_index(insn.a32()).value() });
+        auto dest = cpu.read_memory<T>({ cpu.es(), cpu.destination_index(insn.address_size()).value() });
         op_sub(cpu, dest, src);
-        cpu.step_destination_index(insn.a32(), sizeof(T));
+        cpu.step_destination_index(insn.address_size(), sizeof(T));
     });
 }
 
@@ -2731,23 +2751,28 @@ void SoftCPU::STOSB(const X86::Instruction& insn)
 {
     if (insn.has_rep_prefix() && !df()) {
         // Fast path for 8-bit forward memory fill.
-        if (m_emulator.mmu().fast_fill_memory8({ es(), destination_index(insn.a32()).value() }, ecx().value(), al())) {
-            if (insn.a32()) {
+        if (m_emulator.mmu().fast_fill_memory8({ es(), destination_index(insn.address_size()).value() }, ecx().value(), al())) {
+            switch (insn.address_size()) {
+            case X86::AddressSize::Size32:
                 // FIXME: Should an uninitialized ECX taint EDI here?
                 set_edi({ (u32)(edi().value() + ecx().value()), edi().shadow() });
                 set_ecx(shadow_wrap_as_initialized<u32>(0));
-            } else {
+                break;
+            case X86::AddressSize::Size16:
                 // FIXME: Should an uninitialized CX taint DI here?
                 set_di({ (u16)(di().value() + cx().value()), di().shadow() });
                 set_cx(shadow_wrap_as_initialized<u16>(0));
+                break;
+            default:
+                VERIFY_NOT_REACHED();
             }
             return;
         }
     }
 
     do_once_or_repeat<false>(insn, [&] {
-        write_memory8({ es(), destination_index(insn.a32()).value() }, al());
-        step_destination_index(insn.a32(), 1);
+        write_memory8({ es(), destination_index(insn.address_size()).value() }, al());
+        step_destination_index(insn.address_size(), 1);
     });
 }
 
@@ -2755,31 +2780,36 @@ void SoftCPU::STOSD(const X86::Instruction& insn)
 {
     if (insn.has_rep_prefix() && !df()) {
         // Fast path for 32-bit forward memory fill.
-        if (m_emulator.mmu().fast_fill_memory32({ es(), destination_index(insn.a32()).value() }, ecx().value(), eax())) {
-            if (insn.a32()) {
+        if (m_emulator.mmu().fast_fill_memory32({ es(), destination_index(insn.address_size()).value() }, ecx().value(), eax())) {
+            switch (insn.address_size()) {
+            case X86::AddressSize::Size32:
                 // FIXME: Should an uninitialized ECX taint EDI here?
                 set_edi({ (u32)(edi().value() + (ecx().value() * sizeof(u32))), edi().shadow() });
                 set_ecx(shadow_wrap_as_initialized<u32>(0));
-            } else {
+                break;
+            case X86::AddressSize::Size16:
                 // FIXME: Should an uninitialized CX taint DI here?
                 set_di({ (u16)(di().value() + (cx().value() * sizeof(u32))), di().shadow() });
                 set_cx(shadow_wrap_as_initialized<u16>(0));
+                break;
+            default:
+                VERIFY_NOT_REACHED();
             }
             return;
         }
     }
 
     do_once_or_repeat<false>(insn, [&] {
-        write_memory32({ es(), destination_index(insn.a32()).value() }, eax());
-        step_destination_index(insn.a32(), 4);
+        write_memory32({ es(), destination_index(insn.address_size()).value() }, eax());
+        step_destination_index(insn.address_size(), 4);
     });
 }
 
 void SoftCPU::STOSW(const X86::Instruction& insn)
 {
     do_once_or_repeat<false>(insn, [&] {
-        write_memory16({ es(), destination_index(insn.a32()).value() }, ax());
-        step_destination_index(insn.a32(), 2);
+        write_memory16({ es(), destination_index(insn.address_size()).value() }, ax());
+        step_destination_index(insn.address_size(), 2);
     });
 }
 
@@ -2856,12 +2886,20 @@ void SoftCPU::XCHG_reg8_RM8(const X86::Instruction& insn)
 
 void SoftCPU::XLAT(const X86::Instruction& insn)
 {
-    if (insn.a32())
+    u32 offset;
+    switch (insn.address_size()) {
+    case X86::AddressSize::Size32:
         warn_if_uninitialized(ebx(), "xlat ebx");
-    else
+        offset = ebx().value() + al().value();
+        break;
+    case X86::AddressSize::Size16:
         warn_if_uninitialized(bx(), "xlat bx");
+        offset = bx().value() + al().value();
+        break;
+    default:
+        VERIFY_NOT_REACHED();
+    }
     warn_if_uninitialized(al(), "xlat al");
-    u32 offset = (insn.a32() ? ebx().value() : bx().value()) + al().value();
     set_al(read_memory8({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), offset }));
 }
 

+ 38 - 19
Userland/DevTools/UserspaceEmulator/SoftCPU.h

@@ -175,64 +175,83 @@ public:
             return gpr32((X86::RegisterIndex32)register_index);
     }
 
-    ValueWithShadow<u32> source_index(bool a32) const
+    ValueWithShadow<u32> source_index(X86::AddressSize address_size) const
     {
-        if (a32)
+        if (address_size == X86::AddressSize::Size32)
             return esi();
-        return { si().value(), (u32)si().shadow_as_value() & 0xffff };
+        if (address_size == X86::AddressSize::Size16)
+            return { si().value(), (u32)si().shadow_as_value() & 0xffff };
+        VERIFY_NOT_REACHED();
     }
 
-    ValueWithShadow<u32> destination_index(bool a32) const
+    ValueWithShadow<u32> destination_index(X86::AddressSize address_size) const
     {
-        if (a32)
+        if (address_size == X86::AddressSize::Size32)
             return edi();
-        return { di().value(), (u32)di().shadow_as_value() & 0xffff };
+        if (address_size == X86::AddressSize::Size16)
+            return { di().value(), (u32)di().shadow_as_value() & 0xffff };
+        VERIFY_NOT_REACHED();
     }
 
-    ValueWithShadow<u32> loop_index(bool a32) const
+    ValueWithShadow<u32> loop_index(X86::AddressSize address_size) const
     {
-        if (a32)
+        if (address_size == X86::AddressSize::Size32)
             return ecx();
-        return { cx().value(), (u32)cx().shadow_as_value() & 0xffff };
+        if (address_size == X86::AddressSize::Size16)
+            return { cx().value(), (u32)cx().shadow_as_value() & 0xffff };
+        VERIFY_NOT_REACHED();
     }
 
-    bool decrement_loop_index(bool a32)
+    bool decrement_loop_index(X86::AddressSize address_size)
     {
-        if (a32) {
+        switch (address_size) {
+        case X86::AddressSize::Size32:
             set_ecx({ ecx().value() - 1, ecx().shadow() });
             return ecx().value() == 0;
+        case X86::AddressSize::Size16:
+            set_cx(ValueWithShadow<u16>(cx().value() - 1, cx().shadow()));
+            return cx().value() == 0;
         }
-        set_cx(ValueWithShadow<u16>(cx().value() - 1, cx().shadow()));
-        return cx().value() == 0;
+        VERIFY_NOT_REACHED();
     }
 
-    ALWAYS_INLINE void step_source_index(bool a32, u32 step)
+    ALWAYS_INLINE void step_source_index(X86::AddressSize address_size, u32 step)
     {
-        if (a32) {
+        switch (address_size) {
+        case X86::AddressSize::Size32:
             if (df())
                 set_esi({ esi().value() - step, esi().shadow() });
             else
                 set_esi({ esi().value() + step, esi().shadow() });
-        } else {
+            break;
+        case X86::AddressSize::Size16:
             if (df())
                 set_si(ValueWithShadow<u16>(si().value() - step, si().shadow()));
             else
                 set_si(ValueWithShadow<u16>(si().value() + step, si().shadow()));
+            break;
+        default:
+            VERIFY_NOT_REACHED();
         }
     }
 
-    ALWAYS_INLINE void step_destination_index(bool a32, u32 step)
+    ALWAYS_INLINE void step_destination_index(X86::AddressSize address_size, u32 step)
     {
-        if (a32) {
+        switch (address_size) {
+        case X86::AddressSize::Size32:
             if (df())
                 set_edi({ edi().value() - step, edi().shadow() });
             else
                 set_edi({ edi().value() + step, edi().shadow() });
-        } else {
+            break;
+        case X86::AddressSize::Size16:
             if (df())
                 set_di(ValueWithShadow<u16>(di().value() - step, di().shadow()));
             else
                 set_di(ValueWithShadow<u16>(di().value() + step, di().shadow()));
+            break;
+        default:
+            VERIFY_NOT_REACHED();
         }
     }
 

+ 1 - 1
Userland/Libraries/LibX86/Disassembler.h

@@ -23,7 +23,7 @@ public:
         if (!m_stream.can_read())
             return {};
 #if ARCH(I386)
-        return Instruction::from_stream(m_stream, true, true);
+        return Instruction::from_stream(m_stream, OperandSize::Size32, AddressSize::Size32);
 #else
         dbgln("FIXME: Implement disassembly support for x86_64");
         return {};

+ 81 - 55
Userland/Libraries/LibX86/Instruction.cpp

@@ -14,10 +14,8 @@
 
 namespace X86 {
 
-InstructionDescriptor s_table16[256];
-InstructionDescriptor s_table32[256];
-InstructionDescriptor s_0f_table16[256];
-InstructionDescriptor s_0f_table32[256];
+InstructionDescriptor s_table[2][256];
+InstructionDescriptor s_0f_table[2][256];
 InstructionDescriptor s_sse_table_np[256];
 InstructionDescriptor s_sse_table_66[256];
 InstructionDescriptor s_sse_table_f3[256];
@@ -246,68 +244,68 @@ static void build_slash_rm(InstructionDescriptor* table, u8 op, u8 slash, u8 rm,
 
 static void build_0f(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_0f_table16, op, mnemonic, format, impl, lock_prefix_allowed);
-    build(s_0f_table32, op, mnemonic, format, impl, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size16)], op, mnemonic, format, impl, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size32)], op, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_table16, op, mnemonic, format, impl, lock_prefix_allowed);
-    build(s_table32, op, mnemonic, format, impl, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size16)], op, mnemonic, format, impl, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size32)], op, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build(u8 op, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_table16, op, mnemonic, format16, impl16, lock_prefix_allowed);
-    build(s_table32, op, mnemonic, format32, impl32, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size16)], op, mnemonic, format16, impl16, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size32)], op, mnemonic, format32, impl32, lock_prefix_allowed);
 }
 
 static void build_0f(u8 op, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_0f_table16, op, mnemonic, format16, impl16, lock_prefix_allowed);
-    build(s_0f_table32, op, mnemonic, format32, impl32, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size16)], op, mnemonic, format16, impl16, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size32)], op, mnemonic, format32, impl32, lock_prefix_allowed);
 }
 
 static void build(u8 op, char const* mnemonic16, InstructionFormat format16, InstructionHandler impl16, char const* mnemonic32, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_table16, op, mnemonic16, format16, impl16, lock_prefix_allowed);
-    build(s_table32, op, mnemonic32, format32, impl32, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size16)], op, mnemonic16, format16, impl16, lock_prefix_allowed);
+    build(s_table[to_underlying(OperandSize::Size32)], op, mnemonic32, format32, impl32, lock_prefix_allowed);
 }
 
 static void build_0f(u8 op, char const* mnemonic16, InstructionFormat format16, InstructionHandler impl16, char const* mnemonic32, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build(s_0f_table16, op, mnemonic16, format16, impl16, lock_prefix_allowed);
-    build(s_0f_table32, op, mnemonic32, format32, impl32, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size16)], op, mnemonic16, format16, impl16, lock_prefix_allowed);
+    build(s_0f_table[to_underlying(OperandSize::Size32)], op, mnemonic32, format32, impl32, lock_prefix_allowed);
 }
 
 static void build_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build_slash(s_table16, op, slash, mnemonic, format, impl, lock_prefix_allowed);
-    build_slash(s_table32, op, slash, mnemonic, format, impl, lock_prefix_allowed);
+    build_slash(s_table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
+    build_slash(s_table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build_slash(s_table16, op, slash, mnemonic, format16, impl16, lock_prefix_allowed);
-    build_slash(s_table32, op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
+    build_slash(s_table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format16, impl16, lock_prefix_allowed);
+    build_slash(s_table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
 }
 
 static void build_0f_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build_slash(s_0f_table16, op, slash, mnemonic, format16, impl16, lock_prefix_allowed);
-    build_slash(s_0f_table32, op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
+    build_slash(s_0f_table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format16, impl16, lock_prefix_allowed);
+    build_slash(s_0f_table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
 }
 
 static void build_0f_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    build_slash(s_0f_table16, op, slash, mnemonic, format, impl, lock_prefix_allowed);
-    build_slash(s_0f_table32, op, slash, mnemonic, format, impl, lock_prefix_allowed);
+    build_slash(s_0f_table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
+    build_slash(s_0f_table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_slash_rm(u8 op, u8 slash, u8 rm, char const* mnemonic, InstructionFormat format, InstructionHandler impl)
 {
-    build_slash_rm(s_table16, op, slash, rm, mnemonic, format, impl);
-    build_slash_rm(s_table32, op, slash, rm, mnemonic, format, impl);
+    build_slash_rm(s_table[to_underlying(OperandSize::Size16)], op, slash, rm, mnemonic, format, impl);
+    build_slash_rm(s_table[to_underlying(OperandSize::Size32)], op, slash, rm, mnemonic, format, impl);
 }
 
 static void build_slash_reg(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl)
@@ -318,40 +316,39 @@ static void build_slash_reg(u8 op, u8 slash, char const* mnemonic, InstructionFo
 
 static void build_sse_np(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format == InvalidFormat) {
+        build_0f(op, mnemonic, format, impl, lock_prefix_allowed);
+        build(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed);
+        return;
+    }
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
 
-    VERIFY(s_0f_table32[op].format == __SSE);
-    VERIFY(s_sse_table_np[op].format == InvalidFormat);
-
+    VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
     build(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_sse_66(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
-    VERIFY(s_0f_table32[op].format == __SSE);
-    VERIFY(s_sse_table_66[op].format == InvalidFormat);
-
+    VERIFY(s_0f_table[to_underlying(AddressSize::Size32)][op].format == __SSE);
     build(s_sse_table_66, op, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_sse_f3(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
-    VERIFY(s_0f_table32[op].format == __SSE);
-    VERIFY(s_sse_table_f3[op].format == InvalidFormat);
-
+    VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
     build(s_sse_table_f3, op, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_sse_f2(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
-    VERIFY(s_0f_table32[op].format == __SSE);
+    VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
     VERIFY(s_sse_table_f2[op].format == InvalidFormat);
 
     build(s_sse_table_f2, op, mnemonic, format, impl, lock_prefix_allowed);
@@ -359,18 +356,18 @@ static void build_sse_f2(u8 op, char const* mnemonic, InstructionFormat format,
 
 static void build_sse_np_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
 
-    VERIFY(s_0f_table32[op].format == __SSE);
+    VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
     build_slash(s_sse_table_np, op, slash, mnemonic, format, impl, lock_prefix_allowed);
 }
 
 static void build_sse_66_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
 {
-    if (s_0f_table32[op].format != __SSE)
+    if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
         build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
-    VERIFY(s_0f_table32[op].format == __SSE);
+    VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
     build_slash(s_sse_table_66, op, slash, mnemonic, format, impl, lock_prefix_allowed);
 }
 
@@ -1309,9 +1306,13 @@ String MemoryOrRegisterReference::to_string_xmm(Instruction const& insn) const
 
 String MemoryOrRegisterReference::to_string(Instruction const& insn) const
 {
-    if (insn.a32())
+    switch (insn.address_size()) {
+    case AddressSize::Size32:
         return to_string_a32();
-    return to_string_a16();
+    case AddressSize::Size16:
+        return to_string_a16();
+    }
+    VERIFY_NOT_REACHED();
 }
 
 String MemoryOrRegisterReference::to_string_a16() const
@@ -1532,11 +1533,26 @@ String Instruction::to_string(u32 origin, SymbolProvider const* symbol_provider,
     StringBuilder builder;
     if (has_segment_prefix())
         builder.appendff("{}: ", register_name(segment_prefix().value()));
-    if (has_address_size_override_prefix())
-        builder.append(m_a32 ? "a32 "sv : "a16 "sv);
-    // Note: SSE2 Uses this to change to doubles in SSE instruction or xmm registers in MMX instructions
-    if (has_operand_size_override_prefix() && !(m_descriptor->format > __SSE && m_descriptor->format < __EndFormatsWithRMByte))
-        builder.append(m_o32 ? "o32 "sv : "o16 "sv);
+    if (has_address_size_override_prefix()) {
+        switch (m_address_size) {
+        case AddressSize::Size16:
+            builder.append("a16"sv);
+            break;
+        case AddressSize::Size32:
+            builder.append("a32"sv);
+            break;
+        }
+    }
+    if (has_operand_size_override_prefix()) {
+        switch (m_operand_size) {
+        case OperandSize::Size16:
+            builder.append("o16"sv);
+            break;
+        case OperandSize::Size32:
+            builder.append("o32"sv);
+            break;
+        }
+    }
     if (has_lock_prefix())
         builder.append("lock "sv);
     // Note: SSE instructions use these to toggle between packed and single data
@@ -1597,7 +1613,15 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
     auto append_seg = [&] { builder.append(register_name(segment_register())); };
     auto append_creg = [&] { builder.appendff("cr{}", register_index()); };
     auto append_dreg = [&] { builder.appendff("dr{}", register_index()); };
-    auto append_relative_addr = [&] { formatted_address(origin + (m_a32 ? 6 : 4), x32, i32(m_a32 ? imm32() : imm16())); };
+    auto append_relative_addr = [&] {
+        if (m_address_size == AddressSize::Size32) {
+            formatted_address(origin + 6, x32, i32(imm32()));
+        } else if (m_address_size == AddressSize::Size16) {
+            formatted_address(origin + 4, x32, i32(imm16()));
+        } else {
+            VERIFY_NOT_REACHED();
+        }
+    };
     auto append_relative_imm8 = [&] { formatted_address(origin + 2, x32, i8(imm8())); };
     auto append_relative_imm16 = [&] { formatted_address(origin + 3, x32, i16(imm16())); };
     auto append_relative_imm32 = [&] { formatted_address(origin + 5, x32, i32(imm32())); };
@@ -1629,10 +1653,12 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
     auto append = [&](auto content) { builder.append(content); };
     auto append_moff = [&] {
         builder.append('[');
-        if (m_a32) {
+        if (m_address_size == AddressSize::Size32) {
             append_imm32();
-        } else {
+        } else if (m_address_size == AddressSize::Size16) {
             append_imm16();
+        } else {
+            VERIFY_NOT_REACHED();
         }
         builder.append(']');
     };
@@ -2143,7 +2169,7 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
         break;
     case OP_reg:
         append_mnemonic_space();
-        if (m_o32)
+        if (m_operand_size == OperandSize::Size32)
             append_reg32();
         else
             append_reg16();

+ 78 - 37
Userland/Libraries/LibX86/Instruction.h

@@ -42,6 +42,16 @@ constexpr T sign_extended_to(U value)
     return (TypeTrivia<T>::mask & ~TypeTrivia<U>::mask) | value;
 }
 
+enum class OperandSize : u8 {
+    Size16,
+    Size32,
+};
+
+enum class AddressSize : u8 {
+    Size16,
+    Size32,
+};
+
 enum IsLockPrefixAllowed {
     LockPrefixNotAllowed = 0,
     LockPrefixAllowed
@@ -214,27 +224,39 @@ struct InstructionDescriptor {
     // a non-null slashes member that's indexed by the three R/M bits.
     InstructionDescriptor* slashes { nullptr };
 
-    unsigned imm1_bytes_for_address_size(bool a32) const
+    unsigned imm1_bytes_for_address_size(AddressSize size) const
     {
-        if (imm1_bytes == CurrentAddressSize)
-            return a32 ? 4 : 2;
+        if (imm1_bytes == CurrentAddressSize) {
+            switch (size) {
+            case AddressSize::Size32:
+                return 4;
+            case AddressSize::Size16:
+                return 2;
+            }
+            VERIFY_NOT_REACHED();
+        }
         return imm1_bytes;
     }
 
-    unsigned imm2_bytes_for_address_size(bool a32) const
+    unsigned imm2_bytes_for_address_size(AddressSize size) const
     {
-        if (imm2_bytes == CurrentAddressSize)
-            return a32 ? 4 : 2;
+        if (imm2_bytes == CurrentAddressSize) {
+            switch (size) {
+            case AddressSize::Size32:
+                return 4;
+            case AddressSize::Size16:
+                return 2;
+            }
+            VERIFY_NOT_REACHED();
+        }
         return imm2_bytes;
     }
 
     IsLockPrefixAllowed lock_prefix_allowed { LockPrefixNotAllowed };
 };
 
-extern InstructionDescriptor s_table16[256];
-extern InstructionDescriptor s_table32[256];
-extern InstructionDescriptor s_0f_table16[256];
-extern InstructionDescriptor s_0f_table32[256];
+extern InstructionDescriptor s_table[2][256];
+extern InstructionDescriptor s_0f_table[2][256];
 extern InstructionDescriptor s_sse_table_np[256];
 extern InstructionDescriptor s_sse_table_66[256];
 extern InstructionDescriptor s_sse_table_f3[256];
@@ -469,7 +491,7 @@ private:
     String to_string_a32() const;
 
     template<typename InstructionStreamType>
-    void decode(InstructionStreamType&, bool a32);
+    void decode(InstructionStreamType&, AddressSize);
     template<typename InstructionStreamType>
     void decode16(InstructionStreamType&);
     template<typename InstructionStreamType>
@@ -497,7 +519,7 @@ private:
 class Instruction {
 public:
     template<typename InstructionStreamType>
-    static Instruction from_stream(InstructionStreamType&, bool o32, bool a32);
+    static Instruction from_stream(InstructionStreamType&, OperandSize, AddressSize);
     ~Instruction() = default;
 
     ALWAYS_INLINE MemoryOrRegisterReference& modrm() const { return m_modrm; }
@@ -537,7 +559,16 @@ public:
     u16 imm16_2() const { return m_imm2; }
     u32 imm32_1() const { return imm32(); }
     u32 imm32_2() const { return m_imm2; }
-    u32 imm_address() const { return m_a32 ? imm32() : imm16(); }
+    u32 imm_address() const
+    {
+        switch (m_address_size) {
+        case AddressSize::Size32:
+            return imm32();
+        case AddressSize::Size16:
+            return imm16();
+        }
+        VERIFY_NOT_REACHED();
+    }
 
     LogicalAddress imm_address16_16() const { return LogicalAddress(imm16_1(), imm16_2()); }
     LogicalAddress imm_address16_32() const { return LogicalAddress(imm16_1(), imm32_2()); }
@@ -556,13 +587,13 @@ public:
 
     u8 cc() const { return has_sub_op() ? m_sub_op & 0xf : m_op & 0xf; }
 
-    bool a32() const { return m_a32; }
+    AddressSize address_size() const { return m_address_size; }
 
     String to_string(u32 origin, SymbolProvider const* = nullptr, bool x32 = true) const;
 
 private:
     template<typename InstructionStreamType>
-    Instruction(InstructionStreamType&, bool o32, bool a32);
+    Instruction(InstructionStreamType&, OperandSize, AddressSize);
 
     void to_string_internal(StringBuilder&, u32 origin, SymbolProvider const*, bool x32) const;
 
@@ -580,8 +611,8 @@ private:
     u8 m_sub_op { 0 };
     u8 m_extra_bytes { 0 };
     u8 m_rep_prefix { 0 };
-    bool m_a32 : 1 { false };
-    bool m_o32 : 1 { false };
+    OperandSize m_operand_size { OperandSize::Size16 };
+    AddressSize m_address_size { AddressSize::Size16 };
     bool m_has_lock_prefix : 1 { false };
     bool m_has_operand_size_override_prefix : 1 { false };
     bool m_has_address_size_override_prefix : 1 { false };
@@ -819,9 +850,9 @@ ALWAYS_INLINE typename CPU::ValueWithShadowType256 MemoryOrRegisterReference::re
 }
 
 template<typename InstructionStreamType>
-ALWAYS_INLINE Instruction Instruction::from_stream(InstructionStreamType& stream, bool o32, bool a32)
+ALWAYS_INLINE Instruction Instruction::from_stream(InstructionStreamType& stream, OperandSize operand_size, AddressSize address_size)
 {
-    return Instruction(stream, o32, a32);
+    return Instruction(stream, operand_size, address_size);
 }
 
 ALWAYS_INLINE unsigned Instruction::length() const
@@ -860,20 +891,26 @@ ALWAYS_INLINE Optional<SegmentRegister> to_segment_prefix(u8 op)
 }
 
 template<typename InstructionStreamType>
-ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32, bool a32)
-    : m_a32(a32)
-    , m_o32(o32)
+ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSize operand_size, AddressSize address_size)
+    : m_operand_size(operand_size)
+    , m_address_size(address_size)
 {
     u8 prefix_bytes = 0;
     for (;; ++prefix_bytes) {
         u8 opbyte = stream.read8();
         if (opbyte == Prefix::OperandSizeOverride) {
-            m_o32 = !o32;
+            if (operand_size == OperandSize::Size32)
+                m_operand_size = OperandSize::Size16;
+            else if (operand_size == OperandSize::Size16)
+                m_operand_size = OperandSize::Size32;
             m_has_operand_size_override_prefix = true;
             continue;
         }
         if (opbyte == Prefix::AddressSizeOverride) {
-            m_a32 = !a32;
+            if (address_size == AddressSize::Size32)
+                m_address_size = AddressSize::Size16;
+            else if (address_size == AddressSize::Size16)
+                m_address_size = AddressSize::Size32;
             m_has_address_size_override_prefix = true;
             continue;
         }
@@ -896,9 +933,9 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
 
     if (m_op == 0x0f) {
         m_sub_op = stream.read8();
-        m_descriptor = m_o32 ? &s_0f_table32[m_sub_op] : &s_0f_table16[m_sub_op];
+        m_descriptor = &s_0f_table[to_underlying(m_operand_size)][m_sub_op];
     } else {
-        m_descriptor = m_o32 ? &s_table32[m_op] : &s_table16[m_op];
+        m_descriptor = &s_table[to_underlying(m_operand_size)][m_op];
     }
 
     if (m_descriptor->format == __SSE) {
@@ -906,7 +943,7 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
             m_descriptor = &s_sse_table_f3[m_sub_op];
         } else if (m_has_operand_size_override_prefix) {
             // This was unset while parsing the prefix initially
-            m_o32 = true;
+            m_operand_size = OperandSize::Size32;
             m_descriptor = &s_sse_table_66[m_sub_op];
         } else {
             m_descriptor = &s_sse_table_np[m_sub_op];
@@ -915,7 +952,7 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
 
     if (m_descriptor->has_rm) {
         // Consume ModR/M (may include SIB and displacement.)
-        m_modrm.decode(stream, m_a32);
+        m_modrm.decode(stream, m_address_size);
         m_register_index = m_modrm.reg();
     } else {
         if (has_sub_op())
@@ -947,8 +984,8 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
         return;
     }
 
-    auto imm1_bytes = m_descriptor->imm1_bytes_for_address_size(m_a32);
-    auto imm2_bytes = m_descriptor->imm2_bytes_for_address_size(m_a32);
+    auto imm1_bytes = m_descriptor->imm1_bytes_for_address_size(m_address_size);
+    auto imm2_bytes = m_descriptor->imm2_bytes_for_address_size(m_address_size);
 
     // Consume immediates if present.
     switch (imm2_bytes) {
@@ -992,11 +1029,11 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
 }
 
 template<typename InstructionStreamType>
-ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stream, bool a32)
+ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stream, AddressSize address_size)
 {
     m_rm_byte = stream.read8();
 
-    if (a32) {
+    if (address_size == AddressSize::Size32) {
         decode32(stream);
         switch (m_displacement_bytes) {
         case 0:
@@ -1009,9 +1046,8 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stre
             break;
         default:
             VERIFY_NOT_REACHED();
-            break;
         }
-    } else {
+    } else if (address_size == AddressSize::Size16) {
         decode16(stream);
         switch (m_displacement_bytes) {
         case 0:
@@ -1024,8 +1060,9 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stre
             break;
         default:
             VERIFY_NOT_REACHED();
-            break;
         }
+    } else {
+        VERIFY_NOT_REACHED();
     }
 }
 
@@ -1095,9 +1132,13 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& st
 template<typename CPU>
 ALWAYS_INLINE LogicalAddress MemoryOrRegisterReference::resolve(const CPU& cpu, Instruction const& insn)
 {
-    if (insn.a32())
+    switch (insn.address_size()) {
+    case AddressSize::Size16:
+        return resolve16(cpu, insn.segment_prefix());
+    case AddressSize::Size32:
         return resolve32(cpu, insn.segment_prefix());
-    return resolve16(cpu, insn.segment_prefix());
+    }
+    VERIFY_NOT_REACHED();
 }
 
 }