use crate::api::CodeSink;
fn low8_will_sign_extend_to_32(xs: i32) -> bool {
xs == ((xs << 24) >> 24)
}
#[inline]
pub(crate) fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 {
debug_assert!(m0d < 4);
debug_assert!(enc_reg_g < 8);
debug_assert!(rm_e < 8);
((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7)
}
#[inline]
pub(crate) fn encode_sib(scale: u8, enc_index: u8, enc_base: u8) -> u8 {
debug_assert!(scale < 4);
debug_assert!(enc_index < 8);
debug_assert!(enc_base < 8);
((scale & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7)
}
const fn is_special_if_8bit(enc: u8) -> bool {
enc >= 4 && enc <= 7
}
#[derive(Clone, Copy)]
pub struct RexPrefix {
byte: u8,
must_emit: bool,
}
impl RexPrefix {
#[inline]
#[must_use]
pub const fn one_op(enc: u8, w_bit: bool, uses_8bit: bool) -> Self {
let must_emit = uses_8bit && is_special_if_8bit(enc);
let w = if w_bit { 1 } else { 0 };
let r = 0;
let x = 0;
let b = (enc >> 3) & 1;
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
Self {
byte: flag,
must_emit,
}
}
#[inline]
#[must_use]
pub const fn two_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
let mut ret = RexPrefix::mem_op(enc_reg, enc_rm, w_bit, uses_8bit);
if uses_8bit && is_special_if_8bit(enc_rm) {
ret.must_emit = true;
}
ret
}
#[inline]
#[must_use]
pub const fn mem_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
let w = if w_bit { 1 } else { 0 };
let r = (enc_reg >> 3) & 1;
let x = 0;
let b = (enc_rm >> 3) & 1;
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
Self {
byte: flag,
must_emit,
}
}
#[inline]
#[must_use]
pub const fn with_digit(digit: u8, enc_reg: u8, w_bit: bool, uses_8bit: bool) -> Self {
Self::two_op(digit, enc_reg, w_bit, uses_8bit)
}
#[inline]
#[must_use]
pub const fn three_op(
enc_reg: u8,
enc_index: u8,
enc_base: u8,
w_bit: bool,
uses_8bit: bool,
) -> Self {
let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
let w = if w_bit { 1 } else { 0 };
let r = (enc_reg >> 3) & 1;
let x = (enc_index >> 3) & 1;
let b = (enc_base >> 3) & 1;
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
Self {
byte: flag,
must_emit,
}
}
#[inline]
pub fn encode(&self, sink: &mut impl CodeSink) {
if self.byte != 0x40 || self.must_emit {
sink.put1(self.byte);
}
}
}
#[derive(Copy, Clone)]
pub enum Disp {
None,
Imm8(i8),
Imm32(i32),
}
impl Disp {
pub fn new(val: i32, evex_scaling: Option<i8>) -> Disp {
if val == 0 {
return Disp::None;
}
match evex_scaling {
Some(scaling) => {
if val % i32::from(scaling) == 0 {
let scaled = val / i32::from(scaling);
if low8_will_sign_extend_to_32(scaled) {
return Disp::Imm8(scaled as i8);
}
}
Disp::Imm32(val)
}
None => match i8::try_from(val) {
Ok(val) => Disp::Imm8(val),
Err(_) => Disp::Imm32(val),
},
}
}
pub fn force_immediate(&mut self) {
if let Disp::None = self {
*self = Disp::Imm8(0);
}
}
pub fn m0d(self) -> u8 {
match self {
Disp::None => 0b00,
Disp::Imm8(_) => 0b01,
Disp::Imm32(_) => 0b10,
}
}
pub fn emit(self, sink: &mut impl CodeSink) {
match self {
Disp::None => {}
Disp::Imm8(n) => sink.put1(n as u8),
Disp::Imm32(n) => sink.put4(n as u32),
}
}
}