1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:37:43 +00:00

LibX86+UserspaceEmulator: Introduce AddressSize and OperandSize enums

These replace the bools a32 and o32, which will make implementing
64-bit sizes possible. :^)
This commit is contained in:
Simon Wanner 2022-03-24 23:01:11 +01:00 committed by Andreas Kling
parent 7cd43deb28
commit a7268c3c74
6 changed files with 276 additions and 152 deletions

View file

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

View file

@ -258,9 +258,9 @@ void SoftCPU::do_once_or_repeat(const X86::Instruction& insn, Callback callback)
if (!insn.has_rep_prefix()) if (!insn.has_rep_prefix())
return callback(); return callback();
while (loop_index(insn.a32()).value()) { while (loop_index(insn.address_size()).value()) {
callback(); callback();
decrement_loop_index(insn.a32()); decrement_loop_index(insn.address_size());
if constexpr (check_zf) { if constexpr (check_zf) {
warn_if_flags_tainted("repz/repnz"); warn_if_flags_tainted("repz/repnz");
if (insn.rep_prefix() == X86::Prefix::REPZ && !zf()) 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)); auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
cpu.do_once_or_repeat<true>(insn, [&] { 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() });
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); op_sub(cpu, dest, src);
cpu.step_source_index(insn.a32(), sizeof(T)); cpu.step_source_index(insn.address_size(), sizeof(T));
cpu.step_destination_index(insn.a32(), 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) 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"); warn_if_uninitialized(ecx(), "jecxz imm8");
if (ecx().value() == 0) if (ecx().value() == 0)
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
} else { break;
case X86::AddressSize::Size16:
warn_if_uninitialized(cx(), "jcxz imm8"); warn_if_uninitialized(cx(), "jcxz imm8");
if (cx().value() == 0) if (cx().value() == 0)
set_eip(eip() + (i8)insn.imm8()); 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)); auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
cpu.do_once_or_repeat<true>(insn, [&] { 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.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) void SoftCPU::LOOPNZ_imm8(const X86::Instruction& insn)
{ {
warn_if_flags_tainted("loopnz"); warn_if_flags_tainted("loopnz");
if (insn.a32()) { switch (insn.address_size()) {
case X86::AddressSize::Size32:
set_ecx({ ecx().value() - 1, ecx().shadow() }); set_ecx({ ecx().value() - 1, ecx().shadow() });
if (ecx().value() != 0 && !zf()) if (ecx().value() != 0 && !zf())
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
} else { break;
case X86::AddressSize::Size16:
set_cx({ (u16)(cx().value() - 1), cx().shadow() }); set_cx({ (u16)(cx().value() - 1), cx().shadow() });
if (cx().value() != 0 && !zf()) if (cx().value() != 0 && !zf())
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
break;
default:
VERIFY_NOT_REACHED();
} }
} }
void SoftCPU::LOOPZ_imm8(const X86::Instruction& insn) void SoftCPU::LOOPZ_imm8(const X86::Instruction& insn)
{ {
warn_if_flags_tainted("loopz"); warn_if_flags_tainted("loopz");
if (insn.a32()) { switch (insn.address_size()) {
case X86::AddressSize::Size32:
set_ecx({ ecx().value() - 1, ecx().shadow() }); set_ecx({ ecx().value() - 1, ecx().shadow() });
if (ecx().value() != 0 && zf()) if (ecx().value() != 0 && zf())
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
} else { break;
case X86::AddressSize::Size16:
set_cx({ (u16)(cx().value() - 1), cx().shadow() }); set_cx({ (u16)(cx().value() - 1), cx().shadow() });
if (cx().value() != 0 && zf()) if (cx().value() != 0 && zf())
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
break;
default:
VERIFY_NOT_REACHED();
} }
} }
void SoftCPU::LOOP_imm8(const X86::Instruction& insn) 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() }); set_ecx({ ecx().value() - 1, ecx().shadow() });
if (ecx().value() != 0) if (ecx().value() != 0)
set_eip(eip() + (i8)insn.imm8()); set_eip(eip() + (i8)insn.imm8());
} else { break;
case X86::AddressSize::Size16:
set_cx({ (u16)(cx().value() - 1), cx().shadow() }); set_cx({ (u16)(cx().value() - 1), cx().shadow() });
if (cx().value() != 0) if (cx().value() != 0)
set_eip(eip() + (i8)insn.imm8()); 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)); auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS));
cpu.do_once_or_repeat<false>(insn, [&] { cpu.do_once_or_repeat<false>(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.write_memory<T>({ cpu.es(), cpu.destination_index(insn.a32()).value() }, src); cpu.write_memory<T>({ cpu.es(), cpu.destination_index(insn.address_size()).value() }, src);
cpu.step_source_index(insn.a32(), sizeof(T)); cpu.step_source_index(insn.address_size(), sizeof(T));
cpu.step_destination_index(insn.a32(), 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, [&] { cpu.do_once_or_repeat<true>(insn, [&] {
auto src = cpu.const_gpr<T>(X86::RegisterAL); 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); 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()) { if (insn.has_rep_prefix() && !df()) {
// Fast path for 8-bit forward memory fill. // 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 (m_emulator.mmu().fast_fill_memory8({ es(), destination_index(insn.address_size()).value() }, ecx().value(), al())) {
if (insn.a32()) { switch (insn.address_size()) {
case X86::AddressSize::Size32:
// FIXME: Should an uninitialized ECX taint EDI here? // FIXME: Should an uninitialized ECX taint EDI here?
set_edi({ (u32)(edi().value() + ecx().value()), edi().shadow() }); set_edi({ (u32)(edi().value() + ecx().value()), edi().shadow() });
set_ecx(shadow_wrap_as_initialized<u32>(0)); set_ecx(shadow_wrap_as_initialized<u32>(0));
} else { break;
case X86::AddressSize::Size16:
// FIXME: Should an uninitialized CX taint DI here? // FIXME: Should an uninitialized CX taint DI here?
set_di({ (u16)(di().value() + cx().value()), di().shadow() }); set_di({ (u16)(di().value() + cx().value()), di().shadow() });
set_cx(shadow_wrap_as_initialized<u16>(0)); set_cx(shadow_wrap_as_initialized<u16>(0));
break;
default:
VERIFY_NOT_REACHED();
} }
return; return;
} }
} }
do_once_or_repeat<false>(insn, [&] { do_once_or_repeat<false>(insn, [&] {
write_memory8({ es(), destination_index(insn.a32()).value() }, al()); write_memory8({ es(), destination_index(insn.address_size()).value() }, al());
step_destination_index(insn.a32(), 1); step_destination_index(insn.address_size(), 1);
}); });
} }
@ -2755,31 +2780,36 @@ void SoftCPU::STOSD(const X86::Instruction& insn)
{ {
if (insn.has_rep_prefix() && !df()) { if (insn.has_rep_prefix() && !df()) {
// Fast path for 32-bit forward memory fill. // 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 (m_emulator.mmu().fast_fill_memory32({ es(), destination_index(insn.address_size()).value() }, ecx().value(), eax())) {
if (insn.a32()) { switch (insn.address_size()) {
case X86::AddressSize::Size32:
// FIXME: Should an uninitialized ECX taint EDI here? // FIXME: Should an uninitialized ECX taint EDI here?
set_edi({ (u32)(edi().value() + (ecx().value() * sizeof(u32))), edi().shadow() }); set_edi({ (u32)(edi().value() + (ecx().value() * sizeof(u32))), edi().shadow() });
set_ecx(shadow_wrap_as_initialized<u32>(0)); set_ecx(shadow_wrap_as_initialized<u32>(0));
} else { break;
case X86::AddressSize::Size16:
// FIXME: Should an uninitialized CX taint DI here? // FIXME: Should an uninitialized CX taint DI here?
set_di({ (u16)(di().value() + (cx().value() * sizeof(u32))), di().shadow() }); set_di({ (u16)(di().value() + (cx().value() * sizeof(u32))), di().shadow() });
set_cx(shadow_wrap_as_initialized<u16>(0)); set_cx(shadow_wrap_as_initialized<u16>(0));
break;
default:
VERIFY_NOT_REACHED();
} }
return; return;
} }
} }
do_once_or_repeat<false>(insn, [&] { do_once_or_repeat<false>(insn, [&] {
write_memory32({ es(), destination_index(insn.a32()).value() }, eax()); write_memory32({ es(), destination_index(insn.address_size()).value() }, eax());
step_destination_index(insn.a32(), 4); step_destination_index(insn.address_size(), 4);
}); });
} }
void SoftCPU::STOSW(const X86::Instruction& insn) void SoftCPU::STOSW(const X86::Instruction& insn)
{ {
do_once_or_repeat<false>(insn, [&] { do_once_or_repeat<false>(insn, [&] {
write_memory16({ es(), destination_index(insn.a32()).value() }, ax()); write_memory16({ es(), destination_index(insn.address_size()).value() }, ax());
step_destination_index(insn.a32(), 2); 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) 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"); warn_if_uninitialized(ebx(), "xlat ebx");
else offset = ebx().value() + al().value();
break;
case X86::AddressSize::Size16:
warn_if_uninitialized(bx(), "xlat bx"); warn_if_uninitialized(bx(), "xlat bx");
offset = bx().value() + al().value();
break;
default:
VERIFY_NOT_REACHED();
}
warn_if_uninitialized(al(), "xlat al"); 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 })); set_al(read_memory8({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), offset }));
} }

View file

@ -175,64 +175,83 @@ public:
return gpr32((X86::RegisterIndex32)register_index); 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 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 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 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() }); set_ecx({ ecx().value() - 1, ecx().shadow() });
return ecx().value() == 0; 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())); VERIFY_NOT_REACHED();
return cx().value() == 0;
} }
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()) if (df())
set_esi({ esi().value() - step, esi().shadow() }); set_esi({ esi().value() - step, esi().shadow() });
else else
set_esi({ esi().value() + step, esi().shadow() }); set_esi({ esi().value() + step, esi().shadow() });
} else { break;
case X86::AddressSize::Size16:
if (df()) if (df())
set_si(ValueWithShadow<u16>(si().value() - step, si().shadow())); set_si(ValueWithShadow<u16>(si().value() - step, si().shadow()));
else else
set_si(ValueWithShadow<u16>(si().value() + step, si().shadow())); 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()) if (df())
set_edi({ edi().value() - step, edi().shadow() }); set_edi({ edi().value() - step, edi().shadow() });
else else
set_edi({ edi().value() + step, edi().shadow() }); set_edi({ edi().value() + step, edi().shadow() });
} else { break;
case X86::AddressSize::Size16:
if (df()) if (df())
set_di(ValueWithShadow<u16>(di().value() - step, di().shadow())); set_di(ValueWithShadow<u16>(di().value() - step, di().shadow()));
else else
set_di(ValueWithShadow<u16>(di().value() + step, di().shadow())); set_di(ValueWithShadow<u16>(di().value() + step, di().shadow()));
break;
default:
VERIFY_NOT_REACHED();
} }
} }

View file

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

View file

@ -14,10 +14,8 @@
namespace X86 { namespace X86 {
InstructionDescriptor s_table16[256]; InstructionDescriptor s_table[2][256];
InstructionDescriptor s_table32[256]; InstructionDescriptor s_0f_table[2][256];
InstructionDescriptor s_0f_table16[256];
InstructionDescriptor s_0f_table32[256];
InstructionDescriptor s_sse_table_np[256]; InstructionDescriptor s_sse_table_np[256];
InstructionDescriptor s_sse_table_66[256]; InstructionDescriptor s_sse_table_66[256];
InstructionDescriptor s_sse_table_f3[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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], op, mnemonic, format, impl, lock_prefix_allowed);
build(s_table32, 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) 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_table[to_underlying(OperandSize::Size16)], op, mnemonic, format16, impl16, lock_prefix_allowed);
build(s_table32, op, mnemonic, format32, impl32, 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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], op, mnemonic16, format16, impl16, lock_prefix_allowed);
build(s_table32, op, mnemonic32, format32, impl32, 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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], 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::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) 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_table[to_underlying(OperandSize::Size16)], 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::Size32)], op, slash, rm, mnemonic, format, impl);
} }
static void build_slash_reg(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler 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) 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); 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_np[op].format == InvalidFormat);
build(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed); 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) 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); build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
VERIFY(s_0f_table32[op].format == __SSE); VERIFY(s_0f_table[to_underlying(AddressSize::Size32)][op].format == __SSE);
VERIFY(s_sse_table_66[op].format == InvalidFormat);
build(s_sse_table_66, op, mnemonic, format, impl, lock_prefix_allowed); 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) 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); 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_f3[op].format == InvalidFormat);
build(s_sse_table_f3, op, mnemonic, format, impl, lock_prefix_allowed); 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) 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); 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); VERIFY(s_sse_table_f2[op].format == InvalidFormat);
build(s_sse_table_f2, op, mnemonic, format, impl, lock_prefix_allowed); 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) 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); 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); 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) 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); 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); 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 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_a32();
return to_string_a16(); case AddressSize::Size16:
return to_string_a16();
}
VERIFY_NOT_REACHED();
} }
String MemoryOrRegisterReference::to_string_a16() const String MemoryOrRegisterReference::to_string_a16() const
@ -1532,11 +1533,26 @@ String Instruction::to_string(u32 origin, SymbolProvider const* symbol_provider,
StringBuilder builder; StringBuilder builder;
if (has_segment_prefix()) if (has_segment_prefix())
builder.appendff("{}: ", register_name(segment_prefix().value())); builder.appendff("{}: ", register_name(segment_prefix().value()));
if (has_address_size_override_prefix()) if (has_address_size_override_prefix()) {
builder.append(m_a32 ? "a32 "sv : "a16 "sv); switch (m_address_size) {
// Note: SSE2 Uses this to change to doubles in SSE instruction or xmm registers in MMX instructions case AddressSize::Size16:
if (has_operand_size_override_prefix() && !(m_descriptor->format > __SSE && m_descriptor->format < __EndFormatsWithRMByte)) builder.append("a16"sv);
builder.append(m_o32 ? "o32 "sv : "o16 "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()) if (has_lock_prefix())
builder.append("lock "sv); builder.append("lock "sv);
// Note: SSE instructions use these to toggle between packed and single data // 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_seg = [&] { builder.append(register_name(segment_register())); };
auto append_creg = [&] { builder.appendff("cr{}", register_index()); }; auto append_creg = [&] { builder.appendff("cr{}", register_index()); };
auto append_dreg = [&] { builder.appendff("dr{}", 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_imm8 = [&] { formatted_address(origin + 2, x32, i8(imm8())); };
auto append_relative_imm16 = [&] { formatted_address(origin + 3, x32, i16(imm16())); }; auto append_relative_imm16 = [&] { formatted_address(origin + 3, x32, i16(imm16())); };
auto append_relative_imm32 = [&] { formatted_address(origin + 5, x32, i32(imm32())); }; 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 = [&](auto content) { builder.append(content); };
auto append_moff = [&] { auto append_moff = [&] {
builder.append('['); builder.append('[');
if (m_a32) { if (m_address_size == AddressSize::Size32) {
append_imm32(); append_imm32();
} else { } else if (m_address_size == AddressSize::Size16) {
append_imm16(); append_imm16();
} else {
VERIFY_NOT_REACHED();
} }
builder.append(']'); builder.append(']');
}; };
@ -2143,7 +2169,7 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
break; break;
case OP_reg: case OP_reg:
append_mnemonic_space(); append_mnemonic_space();
if (m_o32) if (m_operand_size == OperandSize::Size32)
append_reg32(); append_reg32();
else else
append_reg16(); append_reg16();

View file

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