use crate::resources::{Displacement, Encoding, GeneralPurposeRegister, Immediate, ModRM, Operand, REXPrefix, SIBByte, AddressingMode};
#[derive(Eq, Ord, PartialOrd, PartialEq, Debug, Clone)]
pub enum Opcode {
ADDRM64R64 { rm64: Operand, r64: GeneralPurposeRegister },
ADDR64RM64 { r64: GeneralPurposeRegister, rm64: Operand },
CALLFUNC(Operand),
CWD,
CDQ,
CQO,
CMPRAXIMM32 { imm: Immediate },
IDIVRM64 { rm64: Operand },
IMULR64RM64 { r64: GeneralPurposeRegister, rm64: Operand },
INCRM64 { rm64: Operand },
JMPLABEL { label: String },
JELABEL { label: String },
LEAR64FROMSTRADDR { r64: GeneralPurposeRegister, str_sym: String, addend: usize },
MOVRM8R8 { r8: GeneralPurposeRegister, rm8: Operand },
MOVRM64R64 { r64: GeneralPurposeRegister, rm64: Operand },
MOVR64RM64 { r64: GeneralPurposeRegister, rm64: Operand },
MOVRM64IMM32 { imm: Immediate, rm64: Operand },
NEGRM64 { rm64: Operand },
POPR64 { r64: GeneralPurposeRegister },
PUSHRM64 { rm64: Operand },
PUSHR64 { r64: GeneralPurposeRegister },
PUSHIMM32 { imm: Immediate },
RET,
SUBR64RM64 { r64: GeneralPurposeRegister, rm64: Operand },
SUBRM64R64 { rm64: Operand, r64: GeneralPurposeRegister },
SUBRM64IMM32 { rm64: Operand, imm: Immediate },
SYSCALL,
COMMENT(String),
}
impl Opcode {
pub fn to_bytes(&self) -> Vec<u8> {
match self {
Opcode::ADDRM64R64 { rm64: _, r64: _ } => vec![0x01],
Opcode::ADDR64RM64 { r64: _, rm64: _ } => vec![0x03],
Opcode::CALLFUNC(_func) => unimplemented!(),
Opcode::CWD => vec![0x66, 0x99],
Opcode::CDQ
| Opcode::CQO => vec![0x99],
Opcode::CMPRAXIMM32 { imm: _, } => vec![0x3d],
Opcode::IDIVRM64 { rm64: _ } => vec![0xf7],
Opcode::IMULR64RM64 { r64: _, rm64: _ } => vec![0x0f, 0xaf],
Opcode::INCRM64 { rm64: _ } => vec![0xff],
Opcode::JMPLABEL { label: _ } => vec![0xe9],
Opcode::JELABEL { label: _ } => vec![0x0f, 0x84],
Opcode::LEAR64FROMSTRADDR { r64: _, str_sym: _, addend: _ } => vec![0x8d],
Opcode::MOVRM8R8 { r8: _, rm8: _ } => vec![0x88],
Opcode::MOVRM64R64 { r64: _, rm64: _ } => vec![0x89],
Opcode::MOVR64RM64 { r64: _, rm64: _ } => vec![0x8b],
Opcode::MOVRM64IMM32 { imm: _, rm64: _ } => vec![0xc7],
Opcode::NEGRM64 { rm64: _ } => vec![0xf7],
Opcode::POPR64 { r64 } => vec![0x58 + r64.number()],
Opcode::PUSHRM64 { rm64: _ } => vec![0xff],
Opcode::PUSHR64 { r64 } => vec![0x50 + r64.number()],
Opcode::PUSHIMM32 { imm: _ } => vec![0x68],
Opcode::RET => vec![0xc3],
Opcode::SUBRM64IMM32 { rm64: _, imm: _ } => vec![0x81],
Opcode::SUBR64RM64 { r64: _, rm64: _ } => vec![0x2b],
Opcode::SUBRM64R64 { rm64: _, r64: _ } => vec![0x29],
Opcode::SYSCALL => vec![0x0f, 0x05],
Opcode::COMMENT(_com) => panic!("mustn't call 'to_bytes()' with COMMENT"),
}
}
pub fn encoding(&self) -> Encoding {
match self {
Opcode::ADDRM64R64 { rm64: _, r64: _ } => Encoding::MR,
Opcode::ADDR64RM64 { r64: _, rm64: _ } => Encoding::RM,
Opcode::CALLFUNC(_func) => unimplemented!(),
Opcode::CWD
| Opcode::CDQ
| Opcode::CQO => Encoding::ZO,
Opcode::CMPRAXIMM32 { imm: _, } => Encoding::I,
Opcode::IDIVRM64 { rm64: _ } => Encoding::M,
Opcode::IMULR64RM64 { r64: _, rm64: _ } => Encoding::RM,
Opcode::INCRM64 { rm64: _ } => Encoding::M,
Opcode::JMPLABEL { label: _ } => Encoding::D,
Opcode::JELABEL { label: _ } => Encoding::D,
Opcode::LEAR64FROMSTRADDR { r64: _, str_sym: _, addend: _ } => Encoding::RM,
Opcode::MOVRM8R8 { r8: _, rm8: _ } => Encoding::MR,
Opcode::MOVRM64R64 { r64: _, rm64: _ } => Encoding::MR,
Opcode::MOVR64RM64 { r64: _, rm64: _ } => Encoding::RM,
Opcode::MOVRM64IMM32 { rm64: _, imm: _ } => Encoding::MI,
Opcode::NEGRM64 { rm64: _ } => Encoding::M,
Opcode::POPR64 { r64: _ } => Encoding::O,
Opcode::PUSHRM64 { rm64: _ } => Encoding::M,
Opcode::PUSHR64 { r64: _ } => Encoding::O,
Opcode::PUSHIMM32 { imm: _ } => Encoding::I,
Opcode::RET => Encoding::ZO,
Opcode::SUBRM64IMM32 { rm64: _, imm: _ } => Encoding::MI,
Opcode::SUBR64RM64 { r64: _, rm64: _ } => Encoding::RM,
Opcode::SUBRM64R64 { rm64: _, r64: _ } => Encoding::MR,
Opcode::SYSCALL => Encoding::ZO,
Opcode::COMMENT(_com) => panic!("mustn't call 'encoding()' with COMMENT"),
}
}
pub fn rex_prefix(&self) -> Option<REXPrefix> {
match &self {
Opcode::ADDRM64R64 { rm64, r64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::ADDR64RM64 { r64, rm64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::CQO => Some(REXPrefix::new(true, false, false, false)),
Opcode::CMPRAXIMM32 { imm: _, } => Some(REXPrefix::new(true, false, false, false)),
Opcode::IMULR64RM64 { r64, rm64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::IDIVRM64 { rm64 } => {
Some(REXPrefix::new_from_mem(true, rm64))
}
Opcode::INCRM64 { rm64 } => {
Some(REXPrefix::new_from_mem(true, rm64))
}
Opcode::LEAR64FROMSTRADDR { r64, str_sym: _, addend: _ } => {
Some(REXPrefix::new(true, false, false, r64.is_expanded()))
}
Opcode::MOVRM64R64 { rm64, r64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::MOVR64RM64 { r64, rm64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::MOVRM64IMM32 { rm64, imm: _ } => {
Some(REXPrefix::new_from_mem(true, rm64))
}
Opcode::NEGRM64 { rm64 } => {
Some(REXPrefix::new_from_mem(true, rm64))
}
Opcode::POPR64 { r64 } => {
if r64.is_expanded() {
Some(REXPrefix::new(false, false, false, true))
} else {
None
}
}
Opcode::PUSHRM64 { rm64: _ } => None,
Opcode::PUSHR64 { r64 } => {
if r64.is_expanded() {
Some(REXPrefix::new(false, false, false, true))
} else {
None
}
}
Opcode::SUBRM64IMM32 { rm64, imm: _ } => {
Some(REXPrefix::new_from_mem(true, rm64))
}
Opcode::SUBR64RM64 { r64, rm64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
Opcode::SUBRM64R64 { rm64, r64 } => {
Some(REXPrefix::new_from_mem_and_reg(true, r64, rm64))
}
_ => None,
}
}
#[allow(unreachable_patterns)]
pub fn modrm(&self) -> Option<ModRM> {
match &self {
Opcode::ADDRM64R64 { rm64, r64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, r64))
}
Opcode::ADDR64RM64 { r64, rm64 } => {
Some(ModRM::new_rm(rm64.addressing_mode(), r64, rm64))
}
Opcode::IDIVRM64 { rm64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, &GeneralPurposeRegister::new_64bit_from_code(7)))
}
Opcode::IMULR64RM64 { r64, rm64 } => {
Some(ModRM::new_rm(rm64.addressing_mode(), r64, rm64))
}
Opcode::INCRM64 { rm64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, &GeneralPurposeRegister::new_64bit_from_code(0)))
}
Opcode::LEAR64FROMSTRADDR { r64, str_sym: _, addend: _ } => {
Some(ModRM::new_rm(AddressingMode::REGISTER, r64, &Operand::GENERALREGISTER(GeneralPurposeRegister::new_64bit_from_code(4))))
}
Opcode::MOVRM8R8 { rm8, r8 } => {
Some(ModRM::new_mr(rm8.addressing_mode(), rm8, r8))
}
Opcode::MOVRM64R64 { rm64, r64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, r64))
}
Opcode::MOVR64RM64 { r64, rm64 } => {
Some(ModRM::new_rm(rm64.addressing_mode(), r64, rm64))
}
Opcode::MOVRM64IMM32 { rm64, imm: _ } => {
Some(ModRM::new_mi(rm64.addressing_mode(), rm64))
}
Opcode::NEGRM64 { rm64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, &GeneralPurposeRegister::new_64bit_from_code(3)))
}
Opcode::PUSHRM64 { rm64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, &GeneralPurposeRegister::new_64bit_from_code(6)))
}
Opcode::SUBRM64IMM32 { rm64, imm: _ } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, &GeneralPurposeRegister::new_64bit_from_code(5)))
}
Opcode::SUBR64RM64 { r64, rm64 } => {
Some(ModRM::new_rm(rm64.addressing_mode(), r64, rm64))
}
Opcode::SUBRM64R64 { rm64, r64 } => {
Some(ModRM::new_mr(rm64.addressing_mode(), rm64, r64))
}
_ => None,
}
}
pub fn get_displacement(&self) -> Option<Displacement> {
match &self {
Opcode::ADDRM64R64 { rm64, r64: _ } => rm64.get_displacement(),
Opcode::ADDR64RM64 { r64: _, rm64 } => rm64.get_displacement(),
Opcode::IDIVRM64 { rm64 } => rm64.get_displacement(),
Opcode::IMULR64RM64 { r64: _, rm64 } => rm64.get_displacement(),
Opcode::INCRM64 { rm64 } => rm64.get_displacement(),
Opcode::MOVRM8R8 { rm8, r8: _ } => rm8.get_displacement(),
Opcode::MOVRM64R64 { rm64, r64: _ } => rm64.get_displacement(),
Opcode::NEGRM64 { rm64 } => rm64.get_displacement(),
Opcode::PUSHRM64 { rm64 } => rm64.get_displacement(),
Opcode::SUBRM64R64 { rm64, r64: _ } => rm64.get_displacement(),
Opcode::SUBRM64IMM32 { rm64, imm: _ } => rm64.get_displacement(),
Opcode::SUBR64RM64 { r64: _, rm64 } => rm64.get_displacement(),
_ => None,
}
}
pub fn get_immediate(&self) -> Option<Immediate> {
match &self {
Opcode::CMPRAXIMM32 { imm, } => Some(*imm),
Opcode::MOVRM64IMM32 { rm64: _, imm } => Some(*imm),
Opcode::PUSHIMM32 { imm } => Some(*imm),
Opcode::SUBRM64IMM32 { rm64: _, imm } => Some(*imm),
_ => None,
}
}
pub fn sib_bite(&self) -> Option<SIBByte> {
match &self {
Opcode::ADDRM64R64 { rm64, r64: _ } => rm64.sib_byte(),
Opcode::ADDR64RM64 { r64: _, rm64 } => rm64.sib_byte(),
Opcode::IDIVRM64 { rm64 } => rm64.sib_byte(),
Opcode::IMULR64RM64 { r64: _, rm64 } => rm64.sib_byte(),
Opcode::INCRM64 { rm64 } => rm64.sib_byte(),
Opcode::MOVRM8R8 { rm8, r8: _ } => rm8.sib_byte(),
Opcode::MOVRM64R64 { rm64, r64: _ } => rm64.sib_byte(),
Opcode::NEGRM64 { rm64 } => rm64.sib_byte(),
Opcode::PUSHRM64 { rm64 } => rm64.sib_byte(),
Opcode::SUBRM64IMM32 { rm64, imm: _ } => rm64.sib_byte(),
Opcode::SUBRM64R64 { rm64, r64: _ } => rm64.sib_byte(),
Opcode::SUBR64RM64 { r64: _, rm64 } => rm64.sib_byte(),
_ => None,
}
}
pub fn to_intel_string(&self) -> String {
match &self {
Opcode::CWD
| Opcode::CDQ
| Opcode::CQO
| Opcode::RET
| Opcode::SYSCALL => self.opcode_to_intel().to_string(),
Opcode::CALLFUNC(func) => format!("{} {}", self.opcode_to_intel(), func.to_intel_string()),
Opcode::CMPRAXIMM32 { imm, } => format!("{} rax, {}", self.opcode_to_intel(), imm.to_intel_string()),
Opcode::JMPLABEL { label } => format!("{} {}", self.opcode_to_intel(), label),
Opcode::JELABEL { label } => format!("{} {}", self.opcode_to_intel(), label),
Opcode::LEAR64FROMSTRADDR { r64, str_sym, addend: _ } => {
format!("{} {}, {}", self.opcode_to_intel(), str_sym, r64.to_intel_string())
}
Opcode::POPR64 { r64 }
| Opcode::PUSHR64 { r64 } => format!("{} {}", self.opcode_to_intel(), r64.to_intel_string()),
Opcode::PUSHIMM32 { imm } => format!("{} {}", self.opcode_to_intel(), imm.to_intel_string()),
Opcode::IDIVRM64 { rm64 }
| Opcode::INCRM64 { rm64 }
| Opcode::PUSHRM64 { rm64 }
| Opcode::NEGRM64 { rm64 } => {
format!("{} {}", self.opcode_to_intel(), rm64.to_intel_string())
}
Opcode::ADDR64RM64 { r64, rm64 }
| Opcode::IMULR64RM64 { r64, rm64 }
| Opcode::SUBR64RM64 { r64, rm64 }
| Opcode::MOVR64RM64 { r64, rm64 } => {
format!("{} {}, {}", self.opcode_to_intel(), r64.to_intel_string(), rm64.to_intel_string())
}
Opcode::MOVRM8R8 { rm8, r8 } => {
format!("{} {}, {}", self.opcode_to_intel(), rm8.to_intel_string(), r8.to_intel_string())
}
Opcode::ADDRM64R64 { rm64, r64 }
| Opcode::MOVRM64R64 { rm64, r64 }
| Opcode::SUBRM64R64 { rm64, r64 } => {
format!("{} {}, {}", self.opcode_to_intel(), rm64.to_intel_string(), r64.to_intel_string())
}
Opcode::MOVRM64IMM32 { rm64, imm }
| Opcode::SUBRM64IMM32 { rm64, imm } => {
format!("{} {}, {}", self.opcode_to_intel(), rm64.to_intel_string(), imm.to_intel_string())
}
Opcode::COMMENT(com) => format!("# {}", com),
}
}
pub fn to_at_string(&self) -> String {
match &self {
Opcode::CWD
| Opcode::CDQ
| Opcode::CQO
| Opcode::RET
| Opcode::SYSCALL => self.opcode_to_at().to_string(),
Opcode::CALLFUNC(func) => format!("{} {}", self.opcode_to_at(), func.to_at_string()),
Opcode::CMPRAXIMM32 { imm, } => format!("{} {}, %rax", self.opcode_to_at(), imm.to_at_string()),
Opcode::JMPLABEL { label } => format!("{} {}", self.opcode_to_at(), label),
Opcode::JELABEL { label } => format!("{} {}", self.opcode_to_at(), label),
Opcode::LEAR64FROMSTRADDR { r64, str_sym, addend: _ } => {
format!("{} {}, {}", self.opcode_to_at(), str_sym, r64.to_at_string())
}
Opcode::POPR64 { r64 }
| Opcode::PUSHR64 { r64 } => format!("{} {}", self.opcode_to_at(), r64.to_at_string()),
Opcode::PUSHIMM32 { imm } => format!("{} {}", self.opcode_to_at(), imm.to_at_string()),
Opcode::IDIVRM64 { rm64 }
| Opcode::INCRM64 { rm64 }
| Opcode::PUSHRM64 { rm64 }
| Opcode::NEGRM64 { rm64 } => {
format!("{} {}", self.opcode_to_at(), rm64.to_at_string())
}
Opcode::ADDR64RM64 { r64, rm64 }
| Opcode::IMULR64RM64 { r64, rm64 }
| Opcode::SUBR64RM64 { r64, rm64 }
| Opcode::MOVR64RM64 { r64, rm64 } => {
format!("{} {}, {}", self.opcode_to_at(), rm64.to_at_string(), r64.to_at_string())
}
Opcode::MOVRM8R8 { rm8, r8 } => {
format!("{} {}, {}", self.opcode_to_at(), r8.to_at_string(), rm8.to_at_string())
}
Opcode::ADDRM64R64 { rm64, r64 }
| Opcode::MOVRM64R64 { rm64, r64 }
| Opcode::SUBRM64R64 { rm64, r64 } => {
format!("{} {}, {}", self.opcode_to_at(), r64.to_at_string(), rm64.to_at_string())
}
Opcode::MOVRM64IMM32 { rm64, imm }
| Opcode::SUBRM64IMM32 { rm64, imm } => {
format!("{} {}, {}", self.opcode_to_at(), imm.to_at_string(), rm64.to_at_string())
}
Opcode::COMMENT(com) => format!("# {}", com),
}
}
fn opcode_to_intel(&self) -> &str {
match &self {
Opcode::ADDRM64R64 { rm64: _, r64: _ }
| Opcode::ADDR64RM64 { r64: _, rm64: _ } => "add",
Opcode::CALLFUNC(_func) => "call",
Opcode::CMPRAXIMM32 { imm: _, } => "cmp",
Opcode::JMPLABEL { label: _ } => "jmp",
Opcode::JELABEL { label: _ } => "je",
Opcode::LEAR64FROMSTRADDR { r64: _, str_sym: _, addend: _ } => "lea",
Opcode::CWD => "cwd",
Opcode::CDQ => "cdq",
Opcode::CQO => "cqo",
Opcode::IDIVRM64 { rm64: _ } => "idiv",
Opcode::IMULR64RM64 { r64: _, rm64: _ } => "imul",
Opcode::INCRM64 { rm64: _ } => "inc",
Opcode::MOVRM8R8 { rm8: _, r8: _ }
| Opcode::MOVRM64R64 { rm64: _, r64: _ }
| Opcode::MOVR64RM64 { r64: _, rm64: _ }
| Opcode::MOVRM64IMM32 { rm64: _, imm: _ } => "mov",
Opcode::NEGRM64 { rm64: _ } => "neg",
Opcode::POPR64 { r64: _ } => "pop",
Opcode::PUSHRM64 { rm64: _ }
| Opcode::PUSHR64 { r64: _ }
| Opcode::PUSHIMM32 { imm: _ } => "push",
Opcode::RET => "ret",
Opcode::SUBRM64IMM32 { rm64: _, imm: _ }
| Opcode::SUBR64RM64 { r64: _, rm64: _ }
| Opcode::SUBRM64R64 { rm64: _, r64: _ } => "sub",
Opcode::SYSCALL => "syscall",
Opcode::COMMENT(_com) => "",
}
}
fn opcode_to_at(&self) -> &str {
match &self {
Opcode::ADDRM64R64 { rm64: _, r64: _ }
| Opcode::ADDR64RM64 { r64: _, rm64: _ } => "addq",
Opcode::CALLFUNC(_func) => "call",
Opcode::CMPRAXIMM32 { imm: _, } => "cmpq",
Opcode::JMPLABEL { label: _ } => "jmp",
Opcode::JELABEL { label: _ } => "je",
Opcode::LEAR64FROMSTRADDR { r64: _, str_sym: _, addend: _ } => "leaq",
Opcode::CWD => "cwtd",
Opcode::CDQ => "cltd",
Opcode::CQO => "cqto",
Opcode::IDIVRM64 { rm64: _ } => "idivq",
Opcode::IMULR64RM64 { r64: _, rm64: _ } => "imulq",
Opcode::INCRM64 { rm64: _ } => "incq",
Opcode::MOVRM8R8 { rm8: _, r8: _ } => "movb",
Opcode::MOVRM64R64 { rm64: _, r64: _ }
| Opcode::MOVR64RM64 { r64: _, rm64: _ }
| Opcode::MOVRM64IMM32 { rm64: _, imm: _ } => "movq",
Opcode::NEGRM64 { rm64: _ } => "negq",
Opcode::POPR64 { r64: _ } => "popq",
Opcode::PUSHRM64 { rm64: _ }
| Opcode::PUSHR64 { r64: _ }
| Opcode::PUSHIMM32 { imm: _ } => "pushq",
Opcode::RET => "ret",
Opcode::SUBRM64IMM32 { rm64: _, imm: _ }
| Opcode::SUBR64RM64 { r64: _, rm64: _ }
| Opcode::SUBRM64R64 { rm64: _, r64: _ } => "subq",
Opcode::SYSCALL => "syscall",
Opcode::COMMENT(_com) => "",
}
}
}