use core::fmt;
use super::flags::Flags;
use super::reg::{Reg, Size};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Scale {
X1,
X2,
X4,
X8,
}
impl Scale {
#[inline]
#[must_use]
pub const fn factor(self) -> u8 {
1 << self.bits()
}
pub(crate) const fn bits(self) -> u8 {
match self {
Scale::X1 => 0,
Scale::X2 => 1,
Scale::X4 => 2,
Scale::X8 => 3,
}
}
pub(crate) const fn from_bits(bits: u8) -> Scale {
match bits & 3 {
0 => Scale::X1,
1 => Scale::X2,
2 => Scale::X4,
_ => Scale::X8,
}
}
}
const NO_REG: u8 = 0xFF;
const RIP: u8 = 0xFE;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Mem {
disp: i32,
size: Size,
base: u8,
index: u8,
scale: Scale,
}
impl Mem {
#[inline]
#[must_use]
pub const fn new(size: Size, base: Reg) -> Mem {
Mem {
disp: 0,
size,
base: base.bits(),
index: NO_REG,
scale: Scale::X1,
}
}
#[inline]
#[must_use]
pub const fn rip(size: Size, disp: i32) -> Mem {
Mem {
disp,
size,
base: RIP,
index: NO_REG,
scale: Scale::X1,
}
}
#[inline]
#[must_use]
pub const fn absolute(size: Size, addr: i32) -> Mem {
Mem {
disp: addr,
size,
base: NO_REG,
index: NO_REG,
scale: Scale::X1,
}
}
#[inline]
#[must_use]
pub const fn with_disp(self, disp: i32) -> Mem {
Mem { disp, ..self }
}
#[inline]
#[must_use]
pub const fn with_index(self, index: Reg, scale: Scale) -> Mem {
Mem {
index: index.bits(),
scale,
..self
}
}
#[inline]
#[must_use]
pub const fn with_size(self, size: Size) -> Mem {
Mem { size, ..self }
}
#[inline]
#[must_use]
pub const fn size(&self) -> Size {
self.size
}
#[inline]
#[must_use]
pub const fn base(&self) -> Option<Reg> {
if self.base & 0x80 == 0 {
Some(Reg::from_bits(self.base))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn index(&self) -> Option<Reg> {
if self.index & 0x80 == 0 {
Some(Reg::from_bits(self.index))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn scale(&self) -> Scale {
self.scale
}
#[inline]
#[must_use]
pub const fn disp(&self) -> i32 {
self.disp
}
#[inline]
#[must_use]
pub const fn is_rip_relative(&self) -> bool {
self.base == RIP
}
pub(crate) const fn decoded(
size: Size,
base: Option<Reg>,
index: Option<Reg>,
scale: Scale,
disp: i32,
) -> Mem {
let (index, scale) = match index {
Some(r) => (r.bits(), scale),
None => (NO_REG, Scale::X1),
};
let base = match base {
Some(r) => r.bits(),
None => NO_REG,
};
Mem {
disp,
size,
base,
index,
scale,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Operand {
Reg(Reg),
Mem(Mem),
Imm(i64),
Rel(i64),
}
impl From<Reg> for Operand {
#[inline]
fn from(reg: Reg) -> Operand {
Operand::Reg(reg)
}
}
impl From<Mem> for Operand {
#[inline]
fn from(mem: Mem) -> Operand {
Operand::Mem(mem)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Cond {
O,
No,
B,
Ae,
E,
Ne,
Be,
A,
S,
Ns,
P,
Np,
L,
Ge,
Le,
G,
}
const CONDS: [Cond; 16] = [
Cond::O,
Cond::No,
Cond::B,
Cond::Ae,
Cond::E,
Cond::Ne,
Cond::Be,
Cond::A,
Cond::S,
Cond::Ns,
Cond::P,
Cond::Np,
Cond::L,
Cond::Ge,
Cond::Le,
Cond::G,
];
const COND_NAMES: [&str; 16] = [
"o", "no", "b", "ae", "e", "ne", "be", "a", "s", "ns", "p", "np", "l", "ge", "le", "g",
];
impl Cond {
#[inline]
#[must_use]
pub const fn code(self) -> u8 {
self as u8
}
#[inline]
#[must_use]
pub const fn from_code(code: u8) -> Option<Cond> {
if code < 16 {
Some(CONDS[code as usize])
} else {
None
}
}
#[inline]
#[must_use]
pub const fn negate(self) -> Cond {
CONDS[(self.code() ^ 1) as usize]
}
#[must_use]
pub const fn reads(self) -> Flags {
match self {
Cond::O | Cond::No => Flags::OF,
Cond::B | Cond::Ae => Flags::CF,
Cond::E | Cond::Ne => Flags::ZF,
Cond::Be | Cond::A => Flags::CF.union(Flags::ZF),
Cond::S | Cond::Ns => Flags::SF,
Cond::P | Cond::Np => Flags::PF,
Cond::L | Cond::Ge => Flags::SF.union(Flags::OF),
Cond::Le | Cond::G => Flags::ZF.union(Flags::SF).union(Flags::OF),
}
}
#[inline]
#[must_use]
pub const fn suffix(self) -> &'static str {
COND_NAMES[self as usize]
}
}
impl fmt::Display for Cond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.suffix())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cond_codes_round_trip_and_negate() {
for code in 0..16u8 {
let c = Cond::from_code(code);
assert_eq!(c.map(Cond::code), Some(code));
let c = c.unwrap_or(Cond::O);
assert_eq!(c.negate().code(), code ^ 1);
assert_eq!(c.negate().reads(), c.reads());
}
assert_eq!(Cond::from_code(16), None);
}
#[test]
fn test_scale_bits_round_trip() {
for s in [Scale::X1, Scale::X2, Scale::X4, Scale::X8] {
assert_eq!(Scale::from_bits(s.bits()), s);
}
assert_eq!(Scale::X4.factor(), 4);
}
#[test]
fn test_mem_accessors() {
let m = Mem::new(Size::Qword, Reg::RBX)
.with_index(Reg::RSI, Scale::X8)
.with_disp(-5);
assert_eq!(m.base(), Some(Reg::RBX));
assert_eq!(m.index(), Some(Reg::RSI));
assert_eq!(m.scale(), Scale::X8);
assert_eq!(m.disp(), -5);
assert!(!m.is_rip_relative());
let a = Mem::absolute(Size::Byte, 7);
assert!(!a.is_rip_relative() && a.base().is_none() && a.index().is_none());
let r = Mem::rip(Size::Byte, 7);
assert!(r.is_rip_relative() && r.base().is_none());
}
#[test]
fn test_mem_decoded_canonical_scale_without_index() {
let m = Mem::decoded(Size::Dword, Some(Reg::RAX), None, Scale::X8, 0);
assert_eq!(m, Mem::new(Size::Dword, Reg::RAX));
}
}