-- X86 disassembler.
-- Copyright (C) 2006 Tristan Gingold
--
-- GHDL is free software; you can redistribute it and/or modify it under
-- the terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 2, or (at your option) any later
-- version.
--
-- GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or
-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-- for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with GCC; see the file COPYING. If not, write to the Free
-- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
with System.Address_To_Access_Conversions;
package body Disa_X86 is
type Byte is new Interfaces.Unsigned_8;
type Bf_2 is mod 2 ** 2;
type Bf_3 is mod 2 ** 3;
type Byte_Vector is array (Natural) of Byte;
package Bv_Addr2acc is new System.Address_To_Access_Conversions
(Object => Byte_Vector);
use Bv_Addr2acc;
type Cstring_Acc is access constant String;
type Index_Type is
(
N_None,
N_Push,
N_Pop,
N_Ret,
N_Mov,
N_Add,
N_Or,
N_Adc,
N_Sbb,
N_And,
N_Sub,
N_Xor,
N_Cmp,
N_Into,
N_Jmp,
N_Jcc,
N_Setcc,
N_Call,
N_Int,
N_Cdq,
N_Imul,
N_Mul,
N_Leave,
N_Test,
N_Lea,
N_O,
N_No,
N_B,
N_AE,
N_E,
N_Ne,
N_Be,
N_A,
N_S,
N_Ns,
N_P,
N_Np,
N_L,
N_Ge,
N_Le,
N_G,
N_Not,
N_Neg,
N_Cbw,
N_Div,
N_Idiv,
N_Movsx,
N_Movzx,
N_Nop,
N_Hlt,
N_Inc,
N_Dec,
N_Rol,
N_Ror,
N_Rcl,
N_Rcr,
N_Shl,
N_Shr,
N_Sar,
N_Fadd,
N_Fmul,
N_Fcom,
N_Fcomp,
N_Fsub,
N_Fsubr,
N_Fdiv,
N_Fdivr,
G_1,
G_2,
G_3,
G_5
);
type Names_Type is array (Index_Type range <>) of Cstring_Acc;
subtype S is String;
Names : constant Names_Type :=
(N_None => new S'("none"),
N_Push => new S'("push"),
N_Pop => new S'("pop"),
N_Ret => new S'("ret"),
N_Mov => new S'("mov"),
N_Add => new S'("add"),
N_Or => new S'("or"),
N_Adc => new S'("adc"),
N_Sbb => new S'("sbb"),
N_And => new S'("and"),
N_Sub => new S'("sub"),
N_Xor => new S'("xor"),
N_Cmp => new S'("cmp"),
N_Into => new S'("into"),
N_Jmp => new S'("jmp"),
N_Jcc => new S'("j"),
N_Int => new S'("int"),
N_Cdq => new S'("cdq"),
N_Call => new S'("call"),
N_Imul => new S'("imul"),
N_Mul => new S'("mul"),
N_Leave => new S'("leave"),
N_Test => new S'("test"),
N_Setcc => new S'("set"),
N_Lea => new S'("lea"),
N_O => new S'("o"),
N_No => new S'("no"),
N_B => new S'("b"),
N_AE => new S'("ae"),
N_E => new S'("e"),
N_Ne => new S'("ne"),
N_Be => new S'("be"),
N_A => new S'("a"),
N_S => new S'("s"),
N_Ns => new S'("ns"),
N_P => new S'("p"),
N_Np => new S'("np"),
N_L => new S'("l"),
N_Ge => new S'("ge"),
N_Le => new S'("le"),
N_G => new S'("g"),
N_Not => new S'("not"),
N_Neg => new S'("neg"),
N_Cbw => new S'("cbw"),
N_Div => new S'("div"),
N_Idiv => new S'("idiv"),
N_Movsx => new S'("movsx"),
N_Movzx => new S'("movzx"),
N_Nop => new S'("nop"),
N_Hlt => new S'("hlt"),
N_Inc => new S'("inc"),
N_Dec => new S'("dec"),
N_Rol => new S'("rol"),
N_Ror => new S'("ror"),
N_Rcl => new S'("rcl"),
N_Rcr => new S'("rcr"),
N_Shl => new S'("shl"),
N_Shr => new S'("shr"),
N_Sar => new S'("sar"),
N_Fadd => new S'("fadd"),
N_Fmul => new S'("fmul"),
N_Fcom => new S'("fcom"),
N_Fcomp => new S'("fcomp"),
N_Fsub => new S'("fsub"),
N_Fsubr => new S'("fsubr"),
N_Fdiv => new S'("fdiv"),
N_Fdivr => new S'("fdivr")
);
-- Format of an instruction.
-- MODRM_SRC_8 : modrm byte follow, and modrm is source, witdh = 8bits
-- MODRM_DST_8 : modrm byte follow, and modrm is dest, width = 8 bits.
-- MODRM_SRC_W : modrm byte follow, and modrm is source, width = 16/32 bits
-- MODRM_DST_W : modrm byte follow, and modrm is dest, width =16/32 bits.
-- MODRM_IMM_W : modrm byte follow, with an opcode in the reg field,
-- followed by an immediat, width = 16/32 bits.
-- MODRM_IMM_8 : modrm byte follow, with an opcode in the reg field,
-- followed by an immediat, width = 8 bits.
-- IMM : the opcode is followed by an immediate value.
-- PREFIX : the opcode is a prefix (1 byte).
-- OPCODE : inherent addressing.
-- OPCODE2 : a second byte specify the instruction.
-- REG_IMP : register is in the 3 LSB of the opcode.
-- REG_IMM_W : register is in the 3 LSB of the opcode, followed by an
-- immediat, width = 16/32 bits.
-- DISP_W : a wide displacement (16/32 bits).
-- DISP_8 : short displacement (8 bits).
-- INVALID : bad opcode.
type Format_Type is (Modrm_Src, Modrm_Dst,
Modrm_Imm, Modrm_Imm_S,
Modrm,
Modrm_Ax,
Modrm_Imm8,
Imm, Imm_S, Imm_8,
Eax_Imm,
Prefix, Opcode, Opcode2, Reg_Imp,
Reg_Imm,
Imp,
Disp_W, Disp_8,
Cond_Disp_W, Cond_Disp_8,
Cond_Modrm,
Ax_Off_Src, Ax_Off_Dst,
Invalid);
type Width_Type is (W_None, W_8, W_16, W_32, W_Data);
-- Description for one instruction.
type Insn_Desc_Type is record
-- Name of the operation.
Name : Index_Type;
-- Width of the instruction.
-- This is used to add a suffix (b,w,l) to the instruction.
-- This may also be the size of a data.
Width : Width_Type;
-- Format of the instruction.
Format : Format_Type;
end record;
Desc_Invalid : constant Insn_Desc_Type := (N_None, W_None, Invalid);
type Insn_Desc_Array_Type is array (Byte) of Insn_Desc_Type;
type Group_Desc_Array_Type is array (Bf_3) of Insn_Desc_Type;
Insn_Desc : constant Insn_Desc_Array_Type :=
(
2#00_000_000# => (N_Add, W_8, Modrm_Dst),
2#00_000_001# => (N_Add, W_Data, Modrm_Dst),
2#00_000_010# => (N_Add, W_8, Modrm_Src),
2#00_000_011# => (N_Add, W_Data, Modrm_Src),
2#00_001_000# => (N_Or, W_8, Modrm_Dst),
2#00_001_001# => (N_Or, W_Data, Modrm_Dst),
2#00_001_010# => (N_Or, W_8, Modrm_Src),
2#00_001_011# => (N_Or, W_Data, Modrm_Src),
2#00_011_000# => (N_Sbb, W_8, Modrm_Dst),
2#00_011_001# => (N_Sbb, W_Data, Modrm_Dst),
2#00_011_010# => (N_Sbb, W_8, Modrm_Src),
2#00_011_011# => (N_Sbb, W_Data, Modrm_Src),
2#00_100_000# => (N_And, W_8, Modrm_Dst),
2#00_100_001# => (N_And, W_Data, Modrm_Dst),
2#00_100_010# => (N_And, W_8, Modrm_Src),
2#00_100_011# => (N_And, W_Data, Modrm_Src),
2#00_101_000# => (N_Sub, W_8, Modrm_Dst),
2#00_101_001# => (N_Sub, W_Data, Modrm_Dst),
2#00_101_010# => (N_Sub, W_8, Modrm_Src),
2#00_101_011# => (N_Sub, W_Data, Modrm_Src),
2#00_110_000# => (N_Xor, W_8, Modrm_Dst),
2#00_110_001# => (N_Xor, W_Data, Modrm_Dst),
2#00_110_010# => (N_Xor, W_8, Modrm_Src),
2#00_110_011# => (N_Xor, W_Data, Modrm_Src),
2#00_111_000# => (N_Cmp, W_8, Modrm_Dst),
2#00_111_001# => (N_Cmp, W_Data, Modrm_Dst),
2#00_111_010# => (N_Cmp, W_8, Modrm_Src),
2#00_111_011# => (N_Cmp, W_Data, Modrm_Src),
2#00_111_100# => (N_Cmp, W_8, Eax_Imm),
2#00_111_101# => (N_Cmp, W_Data, Eax_Imm),
2#0101_0_000# => (N_Push, W_Data, Reg_Imp),
2#0101_0_001# => (N_Push, W_Data, Reg_Imp),
2#0101_0_010# => (N_Push, W_Data, Reg_Imp),
2#0101_0_011# => (N_Push, W_Data, Reg_Imp),
2#0101_0_100# => (N_Push, W_Data, Reg_Imp),
2#0101_0_101# => (N_Push, W_Data, Reg_Imp),
2#0101_0_110# => (N_Push, W_Data, Reg_Imp),
2#0101_0_111# => (N_Push, W_Data, Reg_Imp),
2#0101_1_000# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_001# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_010# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_011# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_100# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_101# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_110# => (N_Pop, W_Data, Reg_Imp),
2#0101_1_111# => (N_Pop, W_Data, Reg_Imp),
2#0110_1000# => (N_Push, W_Data, Imm),
2#0110_1010# => (N_Push, W_Data, Imm_S),
2#0111_0000# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0001# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0010# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0011# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0100# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0101# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0110# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_0111# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1000# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1001# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1010# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1011# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1100# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1101# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1110# => (N_Jcc, W_None, Cond_Disp_8),
2#0111_1111# => (N_Jcc, W_None, Cond_Disp_8),
2#1000_0000# => (G_1, W_8, Modrm_Imm),
2#1000_0001# => (G_1, W_Data, Modrm_Imm),
2#1000_0011# => (G_1, W_Data, Modrm_Imm_S),
2#1000_0101# => (N_Test, W_Data, Modrm_Src),
2#1000_1101# => (N_Lea, W_Data, Modrm_Src),
2#1000_1010# => (N_Mov, W_8, Modrm_Src),
2#1000_1011# => (N_Mov, W_Data, Modrm_Src),
2#1000_1000# => (N_Mov, W_8, Modrm_Dst),
2#1000_1001# => (N_Mov, W_Data, Modrm_Dst),
2#1001_0000# => (N_Nop, W_None, Opcode),
2#1001_1001# => (N_Cdq, W_Data, Imp),
2#1010_0000# => (N_Mov, W_8, Ax_Off_Src),
2#1010_0001# => (N_Mov, W_Data, Ax_Off_Src),
2#1010_0010# => (N_Mov, W_8, Ax_Off_Dst),
2#1010_0011# => (N_Mov, W_Data, Ax_Off_Dst),
2#1011_0000# => (N_Mov, W_8, Reg_Imm),
2#1011_1000# => (N_Mov, W_Data, Reg_Imm),
2#1011_1001# => (N_Mov, W_Data, Reg_Imm),
2#1011_1010# => (N_Mov, W_Data, Reg_Imm),
2#1011_1011# => (N_Mov, W_Data, Reg_Imm),
2#1011_1100# => (N_Mov, W_Data, Reg_Imm),
2#1011_1101# => (N_Mov, W_Data, Reg_Imm),
2#1011_1110# => (N_Mov, W_Data, Reg_Imm),
2#1011_1111# => (N_Mov, W_Data, Reg_Imm),
2#1100_0000# => (G_2, W_8, Modrm_Imm8),
2#1100_0001# => (G_2, W_Data, Modrm_Imm8),
2#1100_0011# => (N_Ret, W_None, Opcode),
2#1100_0110# => (N_Mov, W_8, Modrm_Imm),
2#1100_0111# => (N_Mov, W_Data, Modrm_Imm),
2#1100_1001# => (N_Leave, W_None, Opcode),
2#1100_1101# => (N_Int, W_None, Imm_8),
2#1100_1110# => (N_Into, W_None, Opcode),
2#1110_1000# => (N_Call, W_None, Disp_W),
2#1110_1001# => (N_Jmp, W_None, Disp_W),
2#1110_1011# => (N_Jmp, W_None, Disp_8),
2#1111_0100# => (N_Hlt, W_None, Opcode),
2#1111_0110# => (G_3, W_None, Invalid),
2#1111_0111# => (G_3, W_None, Invalid),
2#1111_1111# => (G_5, W_None, Invalid),
--2#1111_1111# => (N_Push, W_Data, Modrm),
others => (N_None, W_None, Invalid));
Insn_Desc_0F : constant Insn_Desc_Array_Type :=
(2#1000_0000# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0001# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0010# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0011# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0100# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0101# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0110# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_0111# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1000# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1001# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1010# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1011# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1100# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1101# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1110# => (N_Jcc, W_None, Cond_Disp_W),
2#1000_1111# => (N_Jcc, W_None, Cond_Disp_W),
2#1001_0000# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0001# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0010# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0011# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0100# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0101# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0110# => (N_Setcc, W_8, Cond_Modrm),
2#1001_0111# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1000# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1001# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1010# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1011# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1100# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1101# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1110# => (N_Setcc, W_8, Cond_Modrm),
2#1001_1111# => (N_Setcc, W_8, Cond_Modrm),
2#1011_0110# => (N_Movzx, W_Data, Modrm_Dst),
2#1011_1110# => (N_Movsx, W_Data, Modrm_Dst),
others => (N_None, W_None, Invalid));
-- 16#F7#
Insn_Desc_G3 : constant Group_Desc_Array_Type :=
(2#000# => (N_Test, W_Data, Reg_Imm),
2#010# => (N_Not, W_Data, Modrm_Dst),
2#011# => (N_Neg, W_Data, Modrm_Dst),
2#100# => (N_Mul, W_Data, Modrm_Ax),
2#101# => (N_Imul, W_Data, Modrm_Ax),
2#110# => (N_Div, W_Data, Modrm_Ax),
2#111# => (N_Idiv, W_Data, Modrm_Ax),
others => (N_None, W_None, Invalid));
Insn_Desc_G5 : constant Group_Desc_Array_Type :=
(2#000# => (N_Inc, W_Data, Modrm),
2#001# => (N_Dec, W_Data, Modrm),
2#010# => (N_Call, W_Data, Modrm),
--2#011# => (N_Call, W_Data, Modrm_Ax),
2#100# => (N_Jmp, W_Data, Modrm),
--2#101# => (N_Jmp, W_Data, Modrm_Ax),
2#110# => (N_Push, W_Data, Modrm_Ax),
others => (N_None, W_None, Invalid));
type Group_Name_Array_Type is array (Index_Type range G_1 .. G_2, Bf_3)
of Index_Type;
Group_Name : constant Group_Name_Array_Type :=
(
G_1 => (N_Add, N_Or, N_Adc, N_Sbb, N_And, N_Sub, N_Xor, N_Cmp),
G_2 => (N_Rol, N_Ror, N_Rcl, N_Rcr, N_Shl, N_Shr, N_None, N_Sar)
);
-- Standard widths of operations.
type Width_Array_Type is array (Width_Type) of Character;
Width_Char : constant Width_Array_Type :=
(W_None => '-', W_8 => 'b', W_16 => 'w', W_32 => 'l', W_Data => '?');
type Width_Len_Type is array (Width_Type) of Natural;
Width_Len : constant Width_Len_Type :=
(W_None => 0, W_8 => 1, W_16 => 2, W_32 => 4, W_Data => 0);
-- Registers.
-- type Reg_Type is (Reg_Ax, Reg_Bx, Reg_Cx, Reg_Dx,
-- Reg_Bp, Reg_Sp, Reg_Si, Reg_Di,
-- Reg_Al, Reg_Ah, Reg_Bl, Reg_Bh,
-- Reg_Cl, Reg_Ch, Reg_Dl, Reg_Dh);
-- Bits extraction from byte functions.
-- For a byte, MSB (most significant bit) is bit 7 while
-- LSB (least significant bit) is bit 0.
-- Extract bits 2, 1 and 0.
function Ext_210 (B : Byte) return Bf_3;
pragma Inline (Ext_210);
-- Extract bits 5-3 of byte B.
function Ext_543 (B : Byte) return Bf_3;
pragma Inline (Ext_543);
-- Extract bits 7-6 of byte B.
function Ext_76 (B : Byte) return Bf_2;
pragma Inline (Ext_76);
function Ext_210 (B : Byte) return Bf_3 is
begin
return Bf_3 (B and 2#111#);
end Ext_210;
function Ext_543 (B : Byte) return Bf_3 is
begin
return Bf_3 (Shift_Right (B, 3) and 2#111#);
end Ext_543;
function Ext_76 (B : Byte) return Bf_2 is
begin
return Bf_2 (Shift_Right (B, 6) and 2#11#);
end Ext_76;
function Ext_Modrm_Mod (B : Byte) return Bf_2 renames Ext_76;
function Ext_Modrm_Rm (B : Byte) return Bf_3 renames Ext_210;
function Ext_Modrm_Reg (B : Byte) return Bf_3 renames Ext_543;
function Ext_Sib_Base (B : Byte) return Bf_3 renames Ext_210;
function Ext_Sib_Index (B : Byte) return Bf_3 renames Ext_543;
function Ext_Sib_Scale (B : Byte) return Bf_2 renames Ext_76;
procedure Disassemble_Insn (Addr : System.Address;
Pc : Unsigned_32;
Line : in out String;
Line_Len : out Natural;
Insn_Len : out Natural;
Proc_Cb : Symbol_Proc_Type)
is
-- Index in LINE of the next character to be written.
Lo : Natural;
-- Default width.
W_Default : constant Width_Type := W_32;
-- The instruction memory, 0 based.
Mem : Bv_Addr2acc.Object_Pointer;
-- Add NAME to the line.
procedure Add_Name (Name : Index_Type);
pragma Inline (Add_Name);
-- Add CHAR to the line.
procedure Add_Char (C : Character);
pragma Inline (Add_Char);
-- Add STR to the line.
procedure Add_String (Str : String) is
begin
Line (Lo .. Lo + Str'Length - 1) := Str;
Lo := Lo + Str'Length;
end Add_String;
-- Add BYTE to the line.
procedure Add_Byte (V : Byte) is
type My_Str is array (Natural range 0 .. 15) of Character;
Hex_Digit : constant My_Str := "0123456789abcdef";
begin
Add_Char (Hex_Digit (Natural (Shift_Right (V, 4) and 16#0f#)));
Add_Char (Hex_Digit (Natural (Shift_Right (V, 0) and 16#0f#)));
end Add_Byte;
procedure Add_Name (Name : Index_Type) is
begin
Add_String (Names (Name).all);
end Add_Name;
procedure Add_Char (C : Character) is
begin
Line (Lo) := C;
Lo := Lo + 1;
end Add_Char;
procedure Add_Comma is
begin
Add_String (", ");
end Add_Comma;
procedure Name_Align (Orig : Natural) is
begin
Add_Char (' ');
while Lo - Orig < 8 loop
Add_Char (' ');
end loop;
end Name_Align;
procedure Add_Opcode (Name : Index_Type; Width : Width_Type)
is
L : constant Natural := Lo;
begin
Add_Name (Name);
if False and Width /= W_None then
Add_Char (Width_Char (Width));
end if;
Name_Align (L);
end Add_Opcode;
procedure Add_Cond_Opcode (Name : Index_Type; B : Byte)
is
L : constant Natural := Lo;
begin
Add_Name (Name);
Add_Name (Index_Type'Val (Index_Type'Pos (N_O)
+ Byte'Pos (B and 16#0f#)));
Name_Align (L);
end Add_Cond_Opcode;
procedure Decode_Reg_Field (F : Bf_3; W : Width_Type) is
type Reg_Name2_Array is array (Bf_3) of String (1 .. 2);
type Reg_Name3_Array is array (Bf_3) of String (1 .. 3);
Regs_8 : constant Reg_Name2_Array :=
("al", "cl", "dl", "bl", "ah", "ch", "dh", "bh");
Regs_16 : constant Reg_Name2_Array :=
("ax", "cx", "dx", "bx", "sp", "bp", "si", "di");
Regs_32 : constant Reg_Name3_Array :=
("eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi");
begin
Add_Char ('%');
case W is
when W_8 =>
Add_String (Regs_8 (F));
when W_16 =>
Add_String (Regs_16 (F));
when W_32 =>
Add_String (Regs_32 (F));
when W_None
| W_Data =>
raise Program_Error;
end case;
end Decode_Reg_Field;
procedure Decode_Val (Off : Natural; Width : Width_Type)
is
begin
case Width is
when W_8 =>
Add_Byte (Mem (Off));
when W_16 =>
Add_Byte (Mem (Off + 1));
Add_Byte (Mem (Off));
when W_32 =>
Add_Byte (Mem (Off + 3));
Add_Byte (Mem (Off + 2));
Add_Byte (Mem (Off + 1));
Add_Byte (Mem (Off + 0));
when W_None
| W_Data =>
raise Program_Error;
end case;
end Decode_Val;
function Decode_Val (Off : Natural; Width : Width_Type)
return Unsigned_32
is
V : Unsigned_32;
begin
case Width is
when W_8 =>
V := Unsigned_32 (Mem (Off));
-- Sign extension.
if V >= 16#80# then
V := 16#Ffff_Ff00# or V;
end if;
return V;
when W_16 =>
return Shift_Left (Unsigned_32 (Mem (Off + 1)), 8)
or Unsigned_32 (Mem (Off));
when W_32 =>
return Shift_Left (Unsigned_32 (Mem (Off + 3)), 24)
or Shift_Left (Unsigned_32 (Mem (Off + 2)), 16)
or Shift_Left (Unsigned_32 (Mem (Off + 1)), 8)
or Shift_Left (Unsigned_32 (Mem (Off + 0)), 0);
when W_None
| W_Data =>
raise Program_Error;
end case;
end Decode_Val;
procedure Decode_Imm (Off : in out Natural; Width : Width_Type)
is
begin
Add_String ("$0x");
Decode_Val (Off, Width);
Off := Off + Width_Len (Width);
end Decode_Imm;
procedure Decode_Disp (Off : in out Natural;
Width : Width_Type;
Offset : Unsigned_32 := 0)
is
L : Natural;
V : Unsigned_32;
Off_Orig : constant Natural := Off;
begin
L := Lo;
V := Decode_Val (Off, Width) + Offset;
Off := Off + Width_Len (Width);
if Proc_Cb /= null then
Proc_Cb.all (Mem (Off)'Address,
Line (Lo .. Line'Last), Lo);
end if;
if L /= Lo then
if V = 0 then
return;
end if;
Add_String (" + ");
end if;
Add_String ("0x");
if Offset = 0 then
Decode_Val (Off_Orig, Width);
else
Add_Byte (Byte (Shift_Right (V, 24) and 16#Ff#));
Add_Byte (Byte (Shift_Right (V, 16) and 16#Ff#));
Add_Byte (Byte (Shift_Right (V, 8) and 16#Ff#));
Add_Byte (Byte (Shift_Right (V, 0) and 16#Ff#));
end if;
end Decode_Disp;
procedure Decode_Modrm_Reg (B : Byte; Width : Width_Type) is
begin
Decode_Reg_Field (Ext_Modrm_Reg (B), Width);
end Decode_Modrm_Reg;
procedure Decode_Sib (Sib : Byte; B_Mod : Bf_2)
is
S : Bf_2;
I : Bf_3;
B : Bf_3;
begin
S := Ext_Sib_Scale (Sib);
B := Ext_Sib_Base (Sib);
I := Ext_Sib_Index (Sib);
Add_Char ('(');
if B = 2#101# and then B_Mod /= 0 then
Decode_Reg_Field (B, W_32);
Add_Char (',');
end if;
if I /= 2#100# then
Decode_Reg_Field (I, W_32);
case S is
when 2#00# =>
null;
when 2#01# =>
Add_String (",2");
when 2#10# =>
Add_String (",4");
when 2#11# =>
Add_String (",8");
end case;
end if;
Add_Char (')');
end Decode_Sib;
procedure Decode_Modrm_Mem (Off : in out Natural; Width : Width_Type)
is
B : Byte;
B_Mod : Bf_2;
B_Rm : Bf_3;
Off_Orig : Natural;
begin
B := Mem (Off);
B_Mod := Ext_Modrm_Mod (B);
B_Rm := Ext_Modrm_Rm (B);
Off_Orig := Off;
case B_Mod is
when 2#11# =>
Decode_Reg_Field (B_Rm, Width);
Off := Off + 1;
when 2#10# =>
if B_Rm = 2#100# then
Off := Off + 2;
Decode_Disp (Off, W_32);
Decode_Sib (Mem (Off_Orig + 1), B_Mod);
else
Off := Off + 1;
Decode_Disp (Off, W_32);
Add_Char ('(');
Decode_Reg_Field (B_Rm, W_32);
Add_Char (')');
end if;
when 2#01# =>
if B_Rm = 2#100# then
Off := Off + 2;
Decode_Disp (Off, W_8);
Decode_Sib (Mem (Off_Orig + 1), B_Mod);
else
Off := Off + 1;
Decode_Disp (Off, W_8);
Add_Char ('(');
Decode_Reg_Field (B_Rm, W_32);
Add_Char (')');
end if;
when 2#00# =>
if B_Rm = 2#100# then
Off := Off + 2;
Decode_Sib (Mem (Off_Orig + 1), B_Mod);
elsif B_Rm = 2#101# then
Off := Off + 1;
Decode_Disp (Off, W_32);
else
Add_Char ('(');
Decode_Reg_Field (B_Rm, W_32);
Add_Char (')');
Off := Off + 1;
end if;
end case;
end Decode_Modrm_Mem;
-- Return the length of the modrm bytes.
-- At least 1 (mod/rm), at most 6 (mod/rm + SUB + disp32).
function Decode_Modrm_Len (Off : Natural) return Natural
is
B : Byte;
M_Mod : Bf_2;
M_Rm : Bf_3;
begin
B := Mem (Off);
M_Mod := Ext_Modrm_Mod (B);
M_Rm := Ext_Modrm_Rm (B);
case M_Mod is
when 2#11# =>
return 1;
when 2#10# =>
if M_Rm = 2#100# then
return 1 + 1 + 4;
else
return 1 + 4;
end if;
when 2#01# =>
if M_Rm = 2#100# then
return 1 + 1 + 1;
else
return 1 + 1;
end if;
when 2#00# =>
if M_Rm = 2#101# then
-- disp32.
return 1 + 4;
elsif M_Rm = 2#100# then
-- SIB
return 1 + 1;
else
return 1;
end if;
end case;
end Decode_Modrm_Len;
Off : Natural;
B : Byte;
B1 : Byte;
Desc : Insn_Desc_Type;
Name : Index_Type;
W : Width_Type;
begin
Mem := To_Pointer (Addr);
Off := 0;
Lo := Line'First;
B := Mem (0);
if B = 2#0000_1111# then
B := Mem (1);
Off := 2;
Insn_Len := 2;
Desc := Insn_Desc_0F (B);
else
Off := 1;
Insn_Len := 1;
Desc := Insn_Desc (B);
end if;
if Desc.Name >= G_1 then
B1 := Mem (Off);
case Desc.Name is
when G_1
| G_2 =>
Name := Group_Name (Desc.Name, Ext_543 (B1));
when G_3 =>
Desc := Insn_Desc_G3 (Ext_543 (B1));
Name := Desc.Name;
when G_5 =>
Desc := Insn_Desc_G5 (Ext_543 (B1));
Name := Desc.Name;
when others =>
Desc := Desc_Invalid;
end case;
else
Name := Desc.Name;
end if;
case Desc.Width is
when W_Data =>
W := W_Default;
when W_8
| W_16
| W_32 =>
W := Desc.Width;
when W_None =>
case Desc.Format is
when Disp_8
| Cond_Disp_8
| Imm_8 =>
W := W_8;
when Disp_W
| Cond_Disp_W =>
W := W_Default;
when Invalid
| Opcode =>
W := W_None;
when others =>
raise Program_Error;
end case;
end case;
case Desc.Format is
when Reg_Imp =>
Add_Opcode (Desc.Name, W_Default);
Decode_Reg_Field (Ext_210 (B), W_Default);
when Opcode =>
Add_Opcode (Desc.Name, W_None);
when Modrm =>
Add_Opcode (Desc.Name, W);
Decode_Modrm_Mem (Insn_Len, W);
when Modrm_Src =>
Add_Opcode (Desc.Name, W);
-- Disp source first.
Decode_Modrm_Mem (Insn_Len, W);
Add_Comma;
B := Mem (Off);
Decode_Modrm_Reg (Mem (Off), W);
when Modrm_Dst =>
Add_Opcode (Desc.Name, W);
-- Disp source first.
B := Mem (Off);
Decode_Modrm_Reg (B, W);
Add_Comma;
Decode_Modrm_Mem (Insn_Len, W);
when Modrm_Imm =>
Add_Opcode (Name, W);
Insn_Len := Off + Decode_Modrm_Len (Off);
Decode_Imm (Insn_Len, W);
Add_Comma;
Decode_Modrm_Mem (Off, W);
when Modrm_Imm_S =>
Add_Opcode (Name, W);
Insn_Len := Off + Decode_Modrm_Len (Off);
Decode_Imm (Insn_Len, W_8);
Add_Comma;
Decode_Modrm_Mem (Off, W);
when Modrm_Imm8 =>
Add_Opcode (Name, W);
Decode_Modrm_Mem (Off, W);
Add_Comma;
Decode_Imm (Off, W_8);
when Reg_Imm =>
Add_Opcode (Desc.Name, W);
Decode_Imm (Insn_Len, W);
Add_Comma;
Decode_Reg_Field (Ext_210 (B), W);
when Eax_Imm =>
Add_Opcode (Desc.Name, W);
Decode_Imm (Insn_Len, W);
Add_Comma;
Decode_Reg_Field (2#000#, W);
when Disp_W
| Disp_8 =>
Add_Opcode (Desc.Name, W_None);
Decode_Disp (Insn_Len, W,
Pc + Unsigned_32 (Insn_Len + Width_Len (W)));
when Cond_Disp_8
| Cond_Disp_W =>
Add_Cond_Opcode (Desc.Name, B);
Decode_Disp (Insn_Len, W,
Pc + Unsigned_32 (Insn_Len + Width_Len (W)));
when Cond_Modrm =>
Add_Cond_Opcode (Desc.Name, B);
Decode_Modrm_Mem (Insn_Len, W);
when Imm =>
Add_Opcode (Desc.Name, W);
Decode_Imm (Insn_Len, W);
when Imm_S
| Imm_8 =>
Add_Opcode (Desc.Name, W);
Decode_Imm (Insn_Len, W_8);
when Modrm_Ax =>
if (B and 2#1#) = 2#0# then
W := W_8;
else
W := W_Default;
end if;
Add_Opcode (Desc.Name, W);
Decode_Reg_Field (0, W);
Add_Comma;
Decode_Modrm_Mem (Off, W);
when Ax_Off_Src =>
Add_Opcode (Desc.Name, W);
Decode_Disp (Insn_Len, W);
Add_Comma;
Decode_Reg_Field (0, W);
when Ax_Off_Dst =>
Add_Opcode (Desc.Name, W);
Decode_Reg_Field (0, W);
Add_Comma;
Decode_Disp (Insn_Len, W);
when Imp =>
Add_Opcode (Desc.Name, W_Default);
when Invalid
| Prefix
| Opcode2 =>
Add_String ("invalid ");
if Insn_Len = 2 then
Add_Byte (Mem (0));
end if;
Add_Byte (B);
Insn_Len := 1;
end case;
Line_Len := Lo - Line'First;
end Disassemble_Insn;
end Disa_X86;