diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.cpp b/Userland/DevTools/UserspaceEmulator/Emulator.cpp index 133e32097f..2b795b8155 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.cpp +++ b/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 diff --git a/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp b/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp index f91f3840aa..f9f2b57724 100644 --- a/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp +++ b/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(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.a32()).value() }); - auto dest = cpu.read_memory({ cpu.es(), cpu.destination_index(insn.a32()).value() }); + auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.address_size()).value() }); + auto dest = cpu.read_memory({ 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(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.a32()).value() }); + auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.address_size()).value() }); cpu.gpr(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(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.a32()).value() }); - cpu.write_memory({ 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({ src_segment, cpu.source_index(insn.address_size()).value() }); + cpu.write_memory({ 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(insn, [&] { auto src = cpu.const_gpr(X86::RegisterAL); - auto dest = cpu.read_memory({ cpu.es(), cpu.destination_index(insn.a32()).value() }); + auto dest = cpu.read_memory({ 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(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(0)); + break; + default: + VERIFY_NOT_REACHED(); } return; } } do_once_or_repeat(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(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(0)); + break; + default: + VERIFY_NOT_REACHED(); } return; } } do_once_or_repeat(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(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 })); } diff --git a/Userland/DevTools/UserspaceEmulator/SoftCPU.h b/Userland/DevTools/UserspaceEmulator/SoftCPU.h index b2cfe4ab82..16982e6337 100644 --- a/Userland/DevTools/UserspaceEmulator/SoftCPU.h +++ b/Userland/DevTools/UserspaceEmulator/SoftCPU.h @@ -175,64 +175,83 @@ public: return gpr32((X86::RegisterIndex32)register_index); } - ValueWithShadow source_index(bool a32) const + ValueWithShadow 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 destination_index(bool a32) const + ValueWithShadow 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 loop_index(bool a32) const + ValueWithShadow 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(cx().value() - 1, cx().shadow())); + return cx().value() == 0; } - set_cx(ValueWithShadow(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(si().value() - step, si().shadow())); else set_si(ValueWithShadow(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(di().value() - step, di().shadow())); else set_di(ValueWithShadow(di().value() + step, di().shadow())); + break; + default: + VERIFY_NOT_REACHED(); } } diff --git a/Userland/Libraries/LibX86/Disassembler.h b/Userland/Libraries/LibX86/Disassembler.h index 8d453f318f..01cf39a075 100644 --- a/Userland/Libraries/LibX86/Disassembler.h +++ b/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 {}; diff --git a/Userland/Libraries/LibX86/Instruction.cpp b/Userland/Libraries/LibX86/Instruction.cpp index e1702209e2..f01468b89f 100644 --- a/Userland/Libraries/LibX86/Instruction.cpp +++ b/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(); diff --git a/Userland/Libraries/LibX86/Instruction.h b/Userland/Libraries/LibX86/Instruction.h index c2607a23c9..6c0903c92c 100644 --- a/Userland/Libraries/LibX86/Instruction.h +++ b/Userland/Libraries/LibX86/Instruction.h @@ -42,6 +42,16 @@ constexpr T sign_extended_to(U value) return (TypeTrivia::mask & ~TypeTrivia::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 - void decode(InstructionStreamType&, bool a32); + void decode(InstructionStreamType&, AddressSize); template void decode16(InstructionStreamType&); template @@ -497,7 +519,7 @@ private: class Instruction { public: template - 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 - 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 -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 to_segment_prefix(u8 op) } template -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 -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 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(); } }