use super::decode::{resolve_modrm32, Operand};
use super::isa_int::{Cpu, StepOk};
use super::mmu::Mmu;
use super::regs::Reg32;
use super::Trap;
#[derive(Copy, Clone, Debug)]
struct Vex {
map: u8,
pp: u8,
l: u8,
#[allow(dead_code)]
w: bool,
vvvv: u8,
}
impl Vex {
fn from_c5(b1: u8) -> Self {
Vex {
map: 1, pp: b1 & 0x3,
l: (b1 >> 2) & 0x1,
w: false,
vvvv: (!(b1 >> 3)) & 0xF,
}
}
fn from_c4(b1: u8, b2: u8) -> Self {
Vex {
map: b1 & 0x1F,
pp: b2 & 0x3,
l: (b2 >> 2) & 0x1,
w: (b2 & 0x80) != 0,
vvvv: (!(b2 >> 3)) & 0xF,
}
}
}
fn vex_opcode_id(vex: &Vex, opcode: u8) -> u32 {
u32::from(opcode)
| (u32::from(vex.l) << 8)
| (u32::from(vex.pp) << 9)
| (u32::from(vex.map) << 11)
| (u32::from(vex.vvvv) << 14)
}
pub fn dispatch(
cpu: &mut Cpu,
mmu: &mut Mmu,
prefix_byte: u8,
entry_eip: u32,
) -> Result<StepOk, Trap> {
cpu.bump_avx_count();
let vex = match prefix_byte {
0xC5 => {
let b1 = cpu.fetch_imm8_pub(mmu)?;
Vex::from_c5(b1)
}
0xC4 => {
let b1 = cpu.fetch_imm8_pub(mmu)?;
let b2 = cpu.fetch_imm8_pub(mmu)?;
Vex::from_c4(b1, b2)
}
_ => unreachable!("dispatch called with non-VEX prefix {prefix_byte:#x}"),
};
let opcode = cpu.fetch_imm8_pub(mmu)?;
match (vex.map, vex.pp, vex.l, opcode) {
(1, 1, 0, 0xEF) => vpxor_128(cpu, mmu, &vex),
(1, 0, 0, 0x11) => vmovups_store_128(cpu, mmu),
(2, 1, 0, 0xF7) => bmi2_shift_x(cpu, mmu, &vex, ShiftKind::Shl),
(2, 2, 0, 0xF7) => bmi2_shift_x(cpu, mmu, &vex, ShiftKind::Sar),
(2, 3, 0, 0xF7) => bmi2_shift_x(cpu, mmu, &vex, ShiftKind::Shr),
_ => {
Err(Trap::UndefinedOpcode {
eip: entry_eip,
opcode: vex_opcode_id(&vex, opcode),
})
}
}
}
fn read_xmm_dst_and_src2(cpu: &mut Cpu, mmu: &Mmu) -> Result<(usize, u128), Trap> {
let mr = cpu.fetch_modrm(mmu)?;
let bytes = cpu.peek_after_modrm(mmu, 16)?;
let (op, consumed) = resolve_modrm32(mr, &bytes, &cpu.regs)?;
cpu.advance_eip(consumed as u32);
let dst = (mr.reg & 0x7) as usize;
let src2 = match op {
Operand::Reg32(_) => cpu.xmm[(mr.rm & 0x7) as usize],
Operand::Mem32(addr) => {
let bs = mmu.read(cpu.seg_translate(addr), 16)?;
let mut buf = [0u8; 16];
buf.copy_from_slice(&bs);
u128::from_le_bytes(buf)
}
};
Ok((dst, src2))
}
fn vpxor_128(cpu: &mut Cpu, mmu: &Mmu, vex: &Vex) -> Result<StepOk, Trap> {
let (dst, src2) = read_xmm_dst_and_src2(cpu, mmu)?;
let src1 = cpu.xmm[(vex.vvvv & 0x7) as usize];
cpu.xmm[dst] = src1 ^ src2;
cpu.ymm_high[dst] = 0;
Ok(StepOk::Continued)
}
#[derive(Copy, Clone)]
enum ShiftKind {
Shl,
Shr,
Sar,
}
fn bmi2_shift_x(cpu: &mut Cpu, mmu: &Mmu, vex: &Vex, kind: ShiftKind) -> Result<StepOk, Trap> {
let mr = cpu.fetch_modrm(mmu)?;
let bytes = cpu.peek_after_modrm(mmu, 16)?;
let (op, consumed) = resolve_modrm32(mr, &bytes, &cpu.regs)?;
cpu.advance_eip(consumed as u32);
let value = match op {
Operand::Reg32(r) => cpu.regs.get32(r),
Operand::Mem32(addr) => mmu.load32(cpu.seg_translate(addr))?,
};
let count = cpu.regs.get32(Reg32::from_bits(vex.vvvv & 0x7)) & 31;
let dst = Reg32::from_bits(mr.reg & 0x7);
let result = match kind {
ShiftKind::Shl => value.wrapping_shl(count),
ShiftKind::Shr => value.wrapping_shr(count),
ShiftKind::Sar => (value as i32).wrapping_shr(count) as u32,
};
cpu.regs.set32(dst, result);
Ok(StepOk::Continued)
}
fn vmovups_store_128(cpu: &mut Cpu, mmu: &mut Mmu) -> Result<StepOk, Trap> {
let mr = cpu.fetch_modrm(mmu)?;
let bytes = cpu.peek_after_modrm(mmu, 16)?;
let (op, consumed) = resolve_modrm32(mr, &bytes, &cpu.regs)?;
cpu.advance_eip(consumed as u32);
let src = cpu.xmm[(mr.reg & 0x7) as usize];
match op {
Operand::Reg32(_) => {
let dst = (mr.rm & 0x7) as usize;
cpu.xmm[dst] = src;
cpu.ymm_high[dst] = 0;
}
Operand::Mem32(addr) => {
mmu.write(cpu.seg_translate(addr), &src.to_le_bytes())?;
}
}
Ok(StepOk::Continued)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vex_c5_decode_pp_l_vvvv() {
let v = Vex::from_c5(0b1110_0001);
assert_eq!(v.map, 1);
assert_eq!(v.pp, 1);
assert_eq!(v.l, 0);
assert_eq!(v.vvvv, 3);
assert!(!v.w);
}
#[test]
fn vex_c4_decode_w_map() {
let v = Vex::from_c4(0b1110_0010, 0b1101_0111);
assert_eq!(v.map, 2);
assert_eq!(v.pp, 3);
assert_eq!(v.l, 1);
assert!(v.w);
assert_eq!(v.vvvv, 5);
}
#[test]
fn vex_opcode_id_round_trip() {
let v = Vex {
map: 3,
pp: 2,
l: 1,
w: true,
vvvv: 0xA,
};
let id = vex_opcode_id(&v, 0x58);
assert_eq!(id & 0xFF, 0x58);
assert_eq!((id >> 8) & 1, 1);
assert_eq!((id >> 9) & 0x3, 2);
assert_eq!((id >> 11) & 0x7, 3);
assert_eq!((id >> 14) & 0xF, 0xA);
}
}