diff --git a/Libraries/LibX86/Instruction.h b/Libraries/LibX86/Instruction.h index 24818ddbf5..ea928ee3eb 100644 --- a/Libraries/LibX86/Instruction.h +++ b/Libraries/LibX86/Instruction.h @@ -204,12 +204,11 @@ public: RegisterIndex16 reg16() const { return static_cast(register_index()); } RegisterIndex8 reg8() const { return static_cast(register_index()); } - SegmentRegister segment() const - { - ASSERT(!is_register()); - return m_segment; - } - u32 offset(); + template + void write32(CPU&, const Instruction&, u32); + + template + u32 read32(CPU&, const Instruction&); private: MemoryOrRegisterReference() { } @@ -222,6 +221,14 @@ private: void decode16(InstructionStream&); void decode32(InstructionStream&); + template + LogicalAddress resolve16(const CPU&, Optional); + template + LogicalAddress resolve32(const CPU&, Optional); + + template + u32 evaluate_sib(const CPU&, SegmentRegister& default_segment) const; + unsigned m_register_index { 0xffffffff }; SegmentRegister m_segment; union { @@ -372,4 +379,206 @@ private: InstructionDescriptor* m_descriptor { nullptr }; }; +template +LogicalAddress MemoryOrRegisterReference::resolve16(const CPU& cpu, Optional segment_prefix) +{ + ASSERT(!m_a32); + + auto default_segment = SegmentRegister::DS; + u16 offset = 0; + + switch (m_rm & 7) { + case 0: + offset = cpu.bx() + cpu.si() + m_displacement16; + break; + case 1: + offset = cpu.bx() + cpu.di() + m_displacement16; + break; + case 2: + default_segment = SegmentRegister::SS; + offset = cpu.bp() + cpu.si() + m_displacement16; + break; + case 3: + default_segment = SegmentRegister::SS; + offset = cpu.bp() + cpu.di() + m_displacement16; + break; + case 4: + offset = cpu.si() + m_displacement16; + break; + case 5: + offset = cpu.di() + m_displacement16; + break; + case 6: + if ((m_rm & 0xc0) == 0) + offset = m_displacement16; + else { + default_segment = SegmentRegister::SS; + offset = cpu.bp() + m_displacement16; + } + break; + default: + offset = cpu.bx() + m_displacement16; + break; + } + + u16 segment = cpu.segment(segment_prefix.value_or(default_segment)); + return { segment, offset }; +} + +template +inline LogicalAddress MemoryOrRegisterReference::resolve32(const CPU& cpu, Optional segment_prefix) +{ + ASSERT(m_a32); + + auto default_segment = SegmentRegister::DS; + u32 offset = 0; + + switch (m_rm & 0x07) { + case 0: + offset = cpu.eax() + m_displacement32; + break; + case 1: + offset = cpu.ecx() + m_displacement32; + break; + case 2: + offset = cpu.edx() + m_displacement32; + break; + case 3: + offset = cpu.ebx() + m_displacement32; + break; + case 4: + offset = evaluate_sib(cpu, default_segment); + break; + case 6: + offset = cpu.esi() + m_displacement32; + break; + case 7: + offset = cpu.edi() + m_displacement32; + break; + default: // 5 + if ((m_rm & 0xc0) == 0x00) { + offset = m_displacement32; + break; + } else { + default_segment = SegmentRegister::SS; + offset = cpu.ebp() + m_displacement32; + break; + } + break; + } + u16 segment = cpu.segment(segment_prefix.value_or(default_segment)); + return { segment, offset }; +} + +template +inline u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, SegmentRegister& default_segment) const +{ + u32 scale = 0; + switch (m_sib & 0xc0) { + case 0x00: + scale = 1; + break; + case 0x40: + scale = 2; + break; + case 0x80: + scale = 4; + break; + case 0xc0: + scale = 8; + break; + } + u32 index = 0; + switch ((m_sib >> 3) & 0x07) { + case 0: + index = cpu.eax(); + break; + case 1: + index = cpu.ecx(); + break; + case 2: + index = cpu.edx(); + break; + case 3: + index = cpu.ebx(); + break; + case 4: + index = 0; + break; + case 5: + index = cpu.ebp(); + break; + case 6: + index = cpu.esi(); + break; + case 7: + index = cpu.edi(); + break; + } + + u32 base = m_displacement32; + switch (m_sib & 0x07) { + case 0: + base += cpu.eax(); + break; + case 1: + base += cpu.ecx(); + break; + case 2: + base += cpu.edx(); + break; + case 3: + base += cpu.ebx(); + break; + case 4: + default_segment = SegmentRegister::SS; + base += cpu.esp(); + break; + case 6: + base += cpu.esi(); + break; + case 7: + base += cpu.edi(); + break; + default: // 5 + switch ((m_rm >> 6) & 3) { + case 0: + break; + case 1: + case 2: + default_segment = SegmentRegister::SS; + base += cpu.ebp(); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + break; + } + + return (scale * index) + base; +} + +template +inline void MemoryOrRegisterReference::write32(CPU& cpu, const Instruction& insn, u32 value) +{ + if (is_register()) { + cpu.gpr32(reg32()) = value; + return; + } + + auto address = resolve32(cpu, insn.segment_prefix()); + cpu.write_memory32(address, value); +} + +template +inline u32 MemoryOrRegisterReference::read32(CPU& cpu, const Instruction& insn) +{ + if (is_register()) + return cpu.gpr32(reg32()); + + auto address = resolve32(cpu, insn.segment_prefix()); + return cpu.read_memory32(address); +} + }