use alloc::vec::Vec;
use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::fpu::{Sse, X87};
use super::isa::{self, Arg, Bits, Fields, Op, Rep, seg};
use super::paging::{self, Tlb};
use super::prot::{self, Sys, cr0};
use super::{Config, Lines, Regs, Variant, flags, linear};
const RESET_CLOCKS: u32 = 7;
pub(super) const VEC_DIVIDE: u8 = 0;
pub(super) const VEC_DEBUG: u8 = 1;
pub(super) const VEC_NMI: u8 = 2;
pub(super) const VEC_BREAKPOINT: u8 = 3;
pub(super) const VEC_OVERFLOW: u8 = 4;
pub(super) const VEC_BOUND: u8 = 5;
pub(super) const VEC_UD: u8 = 6;
pub(super) const VEC_NM: u8 = 7;
pub(super) const VEC_DF: u8 = 8;
pub(super) const VEC_TS: u8 = 10;
pub(super) const VEC_NP: u8 = 11;
pub(super) const VEC_SS: u8 = 12;
pub(super) const VEC_GP: u8 = 13;
pub(super) const VEC_PF: u8 = 14;
pub(super) const VEC_MF: u8 = 16;
pub(super) const VEC_XM: u8 = 19;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Fault {
pub vector: u8,
pub error: Option<u32>,
}
impl Fault {
pub(super) const fn bare(vector: u8) -> Fault {
Fault {
vector,
error: None,
}
}
pub(super) const fn coded(vector: u8, error: u32) -> Fault {
Fault {
vector,
error: Some(error),
}
}
pub(super) const fn gp(error: u32) -> Fault {
Fault::coded(VEC_GP, error)
}
pub(super) const fn is_contributory(self) -> bool {
matches!(self.vector, VEC_DIVIDE | VEC_TS | VEC_NP | VEC_SS | VEC_GP)
}
}
pub(super) type Ex<T> = Result<T, Fault>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Queue {
bytes: [u8; 16],
len: u8,
depth: u8,
}
impl Queue {
pub(super) const fn new(variant: Variant) -> Queue {
Queue {
bytes: [0; 16],
len: 0,
depth: variant.queue_bytes(),
}
}
pub(super) const fn flush(&mut self) {
self.len = 0;
}
pub(super) const fn len(&self) -> u8 {
self.len
}
pub(super) const fn depth(&self) -> u8 {
self.depth
}
fn push(&mut self, byte: u8) {
if self.len < self.depth {
self.bytes[self.len as usize] = byte;
self.len += 1;
}
}
fn pop(&mut self) -> Option<u8> {
if self.len == 0 {
return None;
}
let byte = self.bytes[0];
let mut i = 1usize;
while i < self.len as usize {
self.bytes[i - 1] = self.bytes[i];
i += 1;
}
self.len -= 1;
Some(byte)
}
pub(super) fn contents(&self) -> Vec<u8> {
self.bytes[..self.len as usize].to_vec()
}
pub(super) fn install(&mut self, bytes: &[u8]) -> Result<(), ()> {
if bytes.len() > self.depth as usize {
return Err(());
}
self.len = bytes.len() as u8;
self.bytes[..bytes.len()].copy_from_slice(bytes);
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct State {
pub regs: Regs,
pub sys: Sys,
pub x87: X87,
pub sse: Sse,
pub tlb: Tlb,
pub cycles: u64,
pub halted: bool,
pub shutdown: bool,
pub reset_pending: bool,
pub wait_for_sipi: bool,
pub int_shadow: bool,
pub queue: Queue,
pub open_bus: u8,
pub faults: u64,
pub last_fault: u64,
pub debt: u64,
}
impl State {
pub(super) fn new(variant: Variant) -> State {
let (regs, sys) = if variant.is_32bit() {
let mut regs = Regs::new();
regs.cs = 0xf000;
regs.rip = 0xfff0;
regs.eflags = flags::ALWAYS_SET;
regs.rdx = u64::from(variant.reset_signature());
(regs, Sys::reset())
} else {
(Regs::new(), Sys::reset_8086())
};
State {
regs,
sys,
x87: X87::new(),
sse: Sse::new(),
tlb: Tlb::new(),
cycles: 0,
halted: false,
shutdown: false,
reset_pending: true,
wait_for_sipi: false,
int_shadow: false,
queue: Queue::new(variant),
open_bus: 0,
faults: 0,
last_fault: 0,
debt: 0,
}
}
}
pub(super) struct Exec<'a> {
pub(super) state: &'a mut State,
pub(super) mem: &'a AddressSpace,
pub(super) io: Option<&'a AddressSpace>,
pub(super) cfg: &'a Config,
pub(super) lines: &'a Lines,
pub(super) attrs: MemAttrs,
pub(super) ea: Option<(u8, u64)>,
pub(super) entry: Regs,
pub(super) start_ip: u64,
pub(super) used: u64,
pub(super) nesting: u8,
}
impl<'a> Exec<'a> {
pub(super) fn new(
state: &'a mut State,
mem: &'a AddressSpace,
io: Option<&'a AddressSpace>,
cfg: &'a Config,
lines: &'a Lines,
) -> Exec<'a> {
let attrs = MemAttrs::DEFAULT.with_requester(cfg.requester);
let entry = state.regs;
Exec {
state,
mem,
io,
cfg,
lines,
attrs,
ea: None,
entry,
start_ip: 0,
used: 0,
nesting: 0,
}
}
#[inline]
pub(super) fn variant(&self) -> Variant {
self.cfg.variant
}
#[inline]
pub(super) fn legacy(&self) -> bool {
!self.cfg.variant.is_32bit()
}
#[inline]
pub(super) fn protected(&self) -> bool {
!self.legacy() && self.state.sys.protected()
}
#[inline]
pub(super) fn cpl(&self) -> u8 {
if self.protected() {
(self.state.regs.cs & 3) as u8
} else {
0
}
}
#[inline]
pub(super) fn has_long(&self) -> bool {
self.cfg.features.long
}
#[inline]
pub(super) fn sixty_four(&self) -> bool {
self.has_long() && self.state.sys.sixty_four()
}
#[inline]
pub(super) fn code_bits(&self) -> Bits {
if self.legacy() {
return Bits::B16;
}
if self.sixty_four() {
return Bits::B64;
}
if self.state.sys.seg(seg::CS).big() {
Bits::B32
} else {
Bits::B16
}
}
pub(super) fn step(&mut self) -> u64 {
if self.state.reset_pending {
self.reset_sequence();
return self.used;
}
if self.lines.take_init_request() {
self.init_sequence();
return self.used;
}
if self.lines.init_held() {
return 0;
}
if let Some(page) = self.lines.take_startup()
&& self.state.wait_for_sipi
{
self.startup_sequence(page);
return self.used;
}
if self.state.wait_for_sipi {
return 0;
}
if self.state.shutdown {
return 0;
}
let shadow = self.state.int_shadow;
if !shadow {
if self.lines.take_nmi_pending() {
self.state.halted = false;
self.charge(Op::INT.clocks());
self.entry = self.state.regs;
self.deliver(Fault::bare(VEC_NMI));
return self.used;
}
if self.flag(flags::IF) && self.lines.intr_pending() {
self.state.halted = false;
self.charge(2 * self.variant().bus_clocks() + Op::INT.clocks());
let vector = self.lines.acknowledge();
self.entry = self.state.regs;
self.deliver(Fault::bare(vector));
return self.used;
}
}
if self.state.halted {
return 0;
}
let trap = self.flag(flags::TF) && !shadow;
self.state.int_shadow = false;
if let Err(fault) = self.instruction() {
if !self.legacy() {
self.state.regs = self.entry;
} else {
self.entry = self.state.regs;
}
self.state.queue.flush();
self.deliver(fault);
return self.used;
}
if trap && !self.state.int_shadow {
self.charge(Op::INT.clocks());
self.entry = self.state.regs;
self.deliver(Fault::bare(VEC_DEBUG));
}
self.used
}
fn deliver(&mut self, first: Fault) {
let mut current = first;
for _ in 0..3 {
self.nesting = self.nesting.saturating_add(1);
match self.take_interrupt(current.vector, current.error) {
Ok(()) => {
self.nesting = 0;
return;
}
Err(second) => {
if current.vector == VEC_DF {
self.state.shutdown = true;
self.state.halted = true;
self.nesting = 0;
return;
}
self.state.regs = self.entry;
self.state.queue.flush();
current = if Self::escalates(current, second) {
Fault::coded(VEC_DF, 0)
} else {
second
};
}
}
}
self.state.shutdown = true;
self.state.halted = true;
self.nesting = 0;
}
const fn escalates(first: Fault, second: Fault) -> bool {
if second.vector == VEC_PF {
return first.vector == VEC_PF;
}
if second.is_contributory() {
return first.is_contributory() || first.vector == VEC_PF;
}
false
}
pub(super) fn charge(&mut self, clocks: u32) {
let clocks = u64::from(clocks);
self.used += clocks;
self.state.cycles = self.state.cycles.wrapping_add(clocks);
}
pub(super) fn phys_read(&mut self, addr: u64, size: u8) -> u64 {
self.charge(self.variant().bus_clocks());
let width = Self::width_of(size);
let addr = self.masked(addr);
match self.mem.read(addr, width, self.attrs) {
Ok(value) => {
self.state.open_bus = (value >> ((size as u32 - 1) * 8)) as u8;
value
}
Err(_) => {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
let byte = u64::from(self.state.open_bus);
byte.wrapping_mul(0x0101_0101_0101_0101) & Self::mask(size)
}
}
}
pub(super) fn phys_write(&mut self, addr: u64, size: u8, value: u64) {
self.charge(self.variant().bus_clocks());
self.state.open_bus = (value >> ((size as u32 - 1) * 8)) as u8;
let width = Self::width_of(size);
let addr = self.masked(addr);
if self.mem.write(addr, width, value, self.attrs).is_err() {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
}
}
#[inline]
const fn width_of(size: u8) -> Width {
match size {
1 => Width::U8,
2 => Width::U16,
4 => Width::U32,
_ => Width::U64,
}
}
#[inline]
fn masked(&self, addr: u64) -> u64 {
(addr & !0xffff_ffffu64) | (addr & u64::from(self.lines.a20_mask()))
}
fn word_is_one_cycle(&self, base: u64, offset: u16) -> bool {
self.cfg.variant.bus_bytes() == 2 && base.is_multiple_of(2) && offset != 0xffff
}
fn legacy_read16_seg(&mut self, segment: u16, offset: u16) -> u16 {
let base = linear(segment, offset);
if self.word_is_one_cycle(base, offset) {
return self.phys_read(base, 2) as u16;
}
let lo = self.phys_read(base, 1) as u8;
let hi = self.phys_read(linear(segment, offset.wrapping_add(1)), 1) as u8;
u16::from(lo) | (u16::from(hi) << 8)
}
fn legacy_write16_seg(&mut self, segment: u16, offset: u16, value: u16) {
let base = linear(segment, offset);
if self.word_is_one_cycle(base, offset) {
self.phys_write(base, 2, u64::from(value));
return;
}
self.phys_write(base, 1, u64::from(value & 0xff));
self.phys_write(
linear(segment, offset.wrapping_add(1)),
1,
u64::from(value >> 8),
);
}
pub(super) fn read_mem(&mut self, sr: u8, offset: u64, size: u8) -> Ex<u64> {
if self.legacy() {
let segment = self.state.regs.segment(sr);
return Ok(match size {
1 => u64::from(self.phys_read(linear(segment, offset as u16), 1) as u8),
_ => u64::from(self.legacy_read16_seg(segment, offset as u16)),
});
}
let lin = self.seg_linear(sr, offset, u64::from(size), false)?;
self.linear_read(lin, size)
}
pub(super) fn write_mem(&mut self, sr: u8, offset: u64, size: u8, value: u64) -> Ex<()> {
if self.legacy() {
let segment = self.state.regs.segment(sr);
match size {
1 => self.phys_write(linear(segment, offset as u16), 1, value & 0xff),
_ => self.legacy_write16_seg(segment, offset as u16, value as u16),
}
return Ok(());
}
let lin = self.seg_linear(sr, offset, u64::from(size), true)?;
self.linear_write(lin, size, value)
}
pub(super) fn linear_read(&mut self, lin: u64, size: u8) -> Ex<u64> {
let user = self.cpl() == 3;
if !self.state.sys.paging() {
return Ok(self.phys_read(lin, size));
}
if Self::crosses_page(lin, size) {
let mut value = 0u64;
for i in 0..u64::from(size) {
let addr = lin.wrapping_add(i);
let phys = self.translate(addr, false, user)?;
value |= self.phys_read(phys, 1) << (8 * i);
}
return Ok(value);
}
let phys = self.translate(lin, false, user)?;
Ok(self.phys_read(phys, size))
}
pub(super) fn linear_write(&mut self, lin: u64, size: u8, value: u64) -> Ex<()> {
let user = self.cpl() == 3;
if !self.state.sys.paging() {
self.phys_write(lin, size, value);
return Ok(());
}
if Self::crosses_page(lin, size) {
let mut phys = [0u64; 8];
for i in 0..u64::from(size) {
phys[i as usize] = self.translate(lin.wrapping_add(i), true, user)?;
}
for i in 0..u64::from(size) {
self.phys_write(phys[i as usize], 1, (value >> (8 * i)) & 0xff);
}
return Ok(());
}
let phys = self.translate(lin, true, user)?;
self.phys_write(phys, size, value);
Ok(())
}
#[inline]
fn crosses_page(lin: u64, size: u8) -> bool {
(lin & 0xfff) + u64::from(size) > 0x1000
}
pub(super) fn sys_read(&mut self, lin: u64, size: u8) -> Ex<u64> {
if !self.state.sys.paging() {
return Ok(self.phys_read(lin, size));
}
if Self::crosses_page(lin, size) {
let mut value = 0u64;
for i in 0..u64::from(size) {
let phys = self.translate(lin.wrapping_add(i), false, false)?;
value |= self.phys_read(phys, 1) << (8 * i);
}
return Ok(value);
}
let phys = self.translate(lin, false, false)?;
Ok(self.phys_read(phys, size))
}
pub(super) fn sys_write(&mut self, lin: u64, size: u8, value: u64) -> Ex<()> {
if !self.state.sys.paging() {
self.phys_write(lin, size, value);
return Ok(());
}
if Self::crosses_page(lin, size) {
let mut phys = [0u64; 8];
for i in 0..u64::from(size) {
phys[i as usize] = self.translate(lin.wrapping_add(i), true, false)?;
}
for i in 0..u64::from(size) {
self.phys_write(phys[i as usize], 1, (value >> (8 * i)) & 0xff);
}
return Ok(());
}
let phys = self.translate(lin, true, false)?;
self.phys_write(phys, size, value);
Ok(())
}
pub(super) fn sys_read32(&mut self, lin: u64) -> Ex<u32> {
Ok(self.sys_read(lin, 4)? as u32)
}
pub(super) fn sys_write32(&mut self, lin: u64, value: u32) -> Ex<()> {
self.sys_write(lin, 4, u64::from(value))
}
pub(super) fn sys_read16(&mut self, lin: u64) -> Ex<u32> {
Ok(self.sys_read(lin, 2)? as u32)
}
#[inline]
const fn io_width(opsize: u8) -> u8 {
if opsize > 4 { 4 } else { opsize }
}
pub(super) fn io_read(&mut self, port: u16, size: u8) -> u32 {
self.charge(self.variant().bus_clocks());
let Some(io) = self.io else {
return match size {
1 => 0xff,
2 => 0xffff,
_ => 0xffff_ffff,
};
};
let width = match size {
1 => Width::U8,
2 => Width::U16,
_ => Width::U32,
};
match io.read(u64::from(port), width, self.attrs) {
Ok(value) => value as u32,
Err(_) => {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = u64::from(port);
match size {
1 => 0xff,
2 => 0xffff,
_ => 0xffff_ffff,
}
}
}
}
pub(super) fn io_write(&mut self, port: u16, size: u8, value: u32) {
self.charge(self.variant().bus_clocks());
let Some(io) = self.io else {
return;
};
let width = match size {
1 => Width::U8,
2 => Width::U16,
_ => Width::U32,
};
if io
.write(u64::from(port), width, u64::from(value), self.attrs)
.is_err()
{
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = u64::from(port);
}
}
fn io_read_sized(&mut self, port: u16, size: u8) -> u32 {
if self.legacy() && size == 2 {
let lo = self.io_read(port, 1);
let hi = self.io_read(port.wrapping_add(1), 1);
return lo | (hi << 8);
}
self.io_read(port, size)
}
fn io_write_sized(&mut self, port: u16, size: u8, value: u32) {
if self.legacy() && size == 2 {
self.io_write(port, 1, value & 0xff);
self.io_write(port.wrapping_add(1), 1, (value >> 8) & 0xff);
return;
}
self.io_write(port, size, value);
}
fn io_permitted(&mut self, port: u16, size: u8) -> Ex<()> {
if !self.protected() {
return Ok(());
}
if self.cpl() <= self.state.regs.iopl() {
return Ok(());
}
let tss = self.state.sys.task;
if !tss.present() {
return Err(Fault::gp(0));
}
let map_base = self.sys_read16(tss.base.wrapping_add(0x66))? & 0xffff;
for i in 0..u32::from(size) {
let bit = u32::from(port) + i;
let offset = map_base + (bit >> 3);
if offset > tss.limit {
return Err(Fault::gp(0));
}
let byte = {
let lin = tss.base.wrapping_add(u64::from(offset));
if self.state.sys.paging() {
let phys = self.translate(lin, false, false)?;
self.phys_read(phys, 1)
} else {
self.phys_read(lin, 1)
}
};
if byte & (1 << (bit & 7)) != 0 {
return Err(Fault::gp(0));
}
}
Ok(())
}
fn fetch_at(&mut self, offset: u64) -> Ex<u8> {
if self.legacy() {
let segment = self.state.regs.cs;
return Ok(self.phys_read(linear(segment, offset as u16), 1) as u8);
}
let cs = self.state.sys.seg(seg::CS);
let lin = if self.sixty_four() {
if !prot::canonical(offset) {
return Err(Fault::gp(0));
}
offset
} else {
if !cs.in_bounds(offset, 1) {
return Err(Fault::gp(0));
}
cs.base.wrapping_add(offset)
};
let user = self.cpl() == 3;
if !self.state.sys.paging() {
return Ok(self.phys_read(lin, 1) as u8);
}
let phys = self.translate_access(lin, paging::Access::fetch(user))?;
Ok(self.phys_read(phys, 1) as u8)
}
fn fill_queue(&mut self) -> Ex<()> {
if !self.legacy() {
return Ok(());
}
while self.state.queue.len() < self.state.queue.depth() {
let offset = self
.state
.regs
.rip
.wrapping_add(u64::from(self.state.queue.len()))
& 0xffff;
let byte = self.fetch_at(offset)?;
self.state.queue.push(byte);
}
Ok(())
}
fn fetch_byte(&mut self) -> Ex<u8> {
let byte = if self.state.queue.len() == 0 {
let offset = self.state.regs.rip;
let byte = self.fetch_at(offset)?;
if self.legacy() {
self.state.queue.push(byte);
self.state.queue.pop().unwrap_or(byte)
} else {
byte
}
} else {
self.state.queue.pop().unwrap_or(self.state.open_bus)
};
self.state.regs.rip = if self.legacy() {
(self.state.regs.rip & !0xffff) | u64::from(self.state.regs.rip.wrapping_add(1) as u16)
} else if self.sixty_four() {
self.state.regs.rip.wrapping_add(1)
} else {
(self.state.regs.rip & !0xffff_ffff)
| u64::from(self.state.regs.rip.wrapping_add(1) as u32)
};
Ok(byte)
}
pub(super) fn flag(&self, mask: u32) -> bool {
self.state.regs.eflags & mask != 0
}
pub(super) fn set_flag(&mut self, mask: u32, on: bool) {
if on {
self.state.regs.eflags |= mask;
} else {
self.state.regs.eflags &= !mask;
}
}
pub(super) fn set_flags(&mut self, value: u32) {
self.state.regs.eflags = Regs::normalise_flags(self.variant(), value);
}
const fn parity(value: u8) -> bool {
(value.count_ones() & 1) == 0
}
#[inline]
pub(super) const fn msb(size: u8) -> u64 {
1u64 << (size as u32 * 8 - 1)
}
#[inline]
pub(super) const fn mask(size: u8) -> u64 {
match size {
1 => 0xff,
2 => 0xffff,
4 => 0xffff_ffff,
_ => u64::MAX,
}
}
fn set_szp(&mut self, value: u64, size: u8) {
let value = value & Self::mask(size);
self.set_flag(flags::ZF, value == 0);
self.set_flag(flags::SF, value & Self::msb(size) != 0);
self.set_flag(flags::PF, Self::parity(value as u8));
}
pub(super) fn add(&mut self, a: u64, b: u64, carry: bool, size: u8) -> u64 {
let mask = Self::mask(size);
let a = a & mask;
let b = b & mask;
let (partial, c1) = a.overflowing_add(b);
let (sum, c2) = partial.overflowing_add(u64::from(carry));
let r = sum & mask;
let carry_out = if size == 8 { c1 || c2 } else { sum > mask };
self.set_flag(flags::CF, carry_out);
self.set_flag(flags::AF, (a ^ b ^ r) & 0x10 != 0);
let msb = Self::msb(size);
self.set_flag(flags::OF, (!(a ^ b)) & (a ^ r) & msb != 0);
self.set_szp(r, size);
r
}
pub(super) fn sub(&mut self, a: u64, b: u64, borrow: bool, size: u8) -> u64 {
let mask = Self::mask(size);
let a = a & mask;
let b = b & mask;
let (rhs, overflowed) = b.overflowing_add(u64::from(borrow));
let r = a.wrapping_sub(rhs) & mask;
self.set_flag(flags::CF, overflowed || a < rhs);
self.set_flag(flags::AF, (a ^ b ^ r) & 0x10 != 0);
let msb = Self::msb(size);
self.set_flag(flags::OF, (a ^ b) & (a ^ r) & msb != 0);
self.set_szp(r, size);
r
}
pub(super) fn logic_flags(&mut self, r: u64, size: u8) {
self.set_flag(flags::CF | flags::OF | flags::AF, false);
self.set_szp(r, size);
}
fn reset_sequence(&mut self) {
self.state.reset_pending = false;
self.state.halted = false;
self.state.shutdown = false;
self.state.int_shadow = false;
self.state.wait_for_sipi = false;
let variant = self.variant();
if variant.is_32bit() {
let keep = self.state.regs;
self.state.sys = Sys::reset();
self.state.tlb.flush();
let regs = &mut self.state.regs;
regs.cs = 0xf000;
regs.rip = 0xfff0;
regs.ds = 0;
regs.es = 0;
regs.ss = 0;
regs.fs = 0;
regs.gs = 0;
regs.eflags = flags::ALWAYS_SET;
regs.rdx = u64::from(variant.reset_signature());
let _ = keep;
} else {
self.state.sys = Sys::reset_8086();
let regs = &mut self.state.regs;
regs.cs = 0xffff;
regs.rip = 0;
regs.ds = 0;
regs.es = 0;
regs.ss = 0;
regs.eflags = flags::RESERVED_SET;
}
self.state.queue.flush();
self.charge(RESET_CLOCKS);
}
fn init_sequence(&mut self) {
self.reset_sequence();
self.state.wait_for_sipi = true;
}
fn startup_sequence(&mut self, page: u8) {
self.state.wait_for_sipi = false;
self.state.halted = false;
self.state.shutdown = false;
self.state.int_shadow = false;
let selector = u16::from(page) << 8;
self.state.regs.cs = selector;
self.state.regs.rip = 0;
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = selector;
entry.base = u64::from(page) << 12;
self.state.queue.flush();
self.charge(RESET_CLOCKS);
}
pub(super) fn stack_addr_size(&self) -> u8 {
if self.legacy() {
2
} else if self.sixty_four() {
8
} else if self.state.sys.seg(seg::SS).big() {
4
} else {
2
}
}
pub(super) fn sp(&self) -> u64 {
match self.stack_addr_size() {
2 => self.state.regs.rsp & 0xffff,
4 => self.state.regs.rsp & 0xffff_ffff,
_ => self.state.regs.rsp,
}
}
pub(super) fn set_sp(&mut self, value: u64) {
let rsp = self.state.regs.rsp;
self.state.regs.rsp = match self.stack_addr_size() {
2 => (rsp & !0xffff) | (value & 0xffff),
4 => value & 0xffff_ffff,
_ => value,
};
}
pub(super) fn push(&mut self, value: u64, size: u8) -> Ex<()> {
let sp = self.sp().wrapping_sub(u64::from(size)) & Self::mask(self.stack_addr_size());
self.set_sp(sp);
self.write_mem(seg::SS, sp, size, value)
}
pub(super) fn pop(&mut self, size: u8) -> Ex<u64> {
let sp = self.sp();
let value = self.read_mem(seg::SS, sp, size)?;
let next = sp.wrapping_add(u64::from(size)) & Self::mask(self.stack_addr_size());
self.set_sp(next);
Ok(value)
}
fn instruction(&mut self) -> Ex<()> {
self.entry = self.state.regs;
self.start_ip = self.state.regs.rip;
self.fill_queue()?;
let map = self.variant().map();
let bits = self.code_bits();
let mut fetch_fault: Option<Fault> = None;
let fields = {
let this = &mut *self;
isa::decode_stream_as(map, bits, &mut || {
if fetch_fault.is_some() {
return None;
}
match this.fetch_byte() {
Ok(byte) => Some(byte),
Err(fault) => {
fetch_fault = Some(fault);
None
}
}
})
};
if let Some(fault) = fetch_fault {
return Err(fault);
}
if !self.legacy() && fields.len > 15 {
return Err(Fault::gp(0));
}
self.prepare_ea(&fields);
self.charge(fields.insn.op.clocks());
self.execute(&fields)
}
fn prepare_ea(&mut self, f: &Fields) {
self.ea = None;
let insn = f.insn;
let escape = !f.two_byte && matches!(f.opcode, 0xd8..=0xdf);
let wants_memory = escape
|| [insn.dst, insn.src, insn.aux]
.iter()
.any(|a| a.uses_modrm_memory());
if let Some(m) = f.modrm
&& !m.is_register()
&& wants_memory
{
let offset = match f.addrsize {
2 => {
let regs = &self.state.regs;
let terms = match m.rm {
0 => regs.word(3).wrapping_add(regs.word(6)), 1 => regs.word(3).wrapping_add(regs.word(7)), 2 => regs.word(5).wrapping_add(regs.word(6)), 3 => regs.word(5).wrapping_add(regs.word(7)), 4 => regs.word(6), 5 => regs.word(7), 6 if m.md == 0 => 0,
6 => regs.word(5), _ => regs.word(3), };
let disp = f.disp as u16;
let value = if m.md == 0 && m.rm == 6 {
disp
} else {
terms.wrapping_add(disp)
};
u64::from(value)
}
4 => u64::from(self.ea32(f, m)),
_ => self.ea64(f, m),
};
self.ea = Some((f.mem_segment(), offset));
if self.legacy() {
self.charge(isa::ea_clocks(m.md, m.rm, f.seg_override.is_some()));
}
} else if [insn.dst, insn.src]
.iter()
.any(|a| matches!(a, Arg::Ob | Arg::Ov))
{
let offset = f.imm & Self::mask(f.addrsize);
self.ea = Some((f.segment(seg::DS), offset));
}
}
fn ea32(&self, f: &Fields, m: isa::ModRm) -> u32 {
let regs = &self.state.regs;
let mut value = 0u32;
if m.rm == 4 {
let sib = f.sib.unwrap_or(isa::Sib::new(0));
if !(sib.base == 5 && m.md == 0) {
value = value.wrapping_add(regs.dword(f.base_num()));
}
if f.has_index() {
value = value.wrapping_add(regs.dword(f.index_num()) << sib.scale);
}
} else if !(m.rm == 5 && m.md == 0) {
value = value.wrapping_add(regs.dword(f.rm_num()));
}
value.wrapping_add(f.disp as u32)
}
fn ea64(&self, f: &Fields, m: isa::ModRm) -> u64 {
if f.rip_relative {
return self.state.regs.rip.wrapping_add(f.disp as i64 as u64);
}
let regs = &self.state.regs;
let mut value = 0u64;
if m.rm == 4 {
let sib = f.sib.unwrap_or(isa::Sib::new(0));
if !(sib.base == 5 && m.md == 0) {
value = value.wrapping_add(regs.qword(f.base_num()));
}
if f.has_index() {
value = value.wrapping_add(regs.qword(f.index_num()) << sib.scale);
}
} else {
value = value.wrapping_add(regs.qword(f.rm_num()));
}
value.wrapping_add(f.disp as i64 as u64)
}
pub(super) fn ea(&self) -> (u8, u64) {
self.ea.unwrap_or((seg::DS, 0))
}
fn width(f: &Fields) -> u8 {
f.insn.width_bytes(f.opsize).unwrap_or(f.opsize)
}
pub(super) fn read_arg(&mut self, f: &Fields, arg: Arg, size: u8) -> Ex<u64> {
let regs = self.state.regs;
let rex = f.has_rex();
let value = match arg {
Arg::Eb | Arg::Ev | Arg::Ew | Arg::Ed | Arg::Ey => match f.modrm {
Some(m) if m.is_register() => regs.read(f.rm_num(), size, rex),
_ => {
let (sr, off) = self.ea();
self.read_mem(sr, off, size)?
}
},
Arg::Gb | Arg::Gv | Arg::Gw | Arg::Gy => regs.read(f.reg_num(), size, rex),
Arg::Rd => regs.read(f.rm_num(), self.system_reg_size(), rex),
Arg::Cd => self.read_control(f.reg_num())?,
Arg::Dd => self.read_debug(f.reg_num())?,
Arg::Td => self.read_test(f.reg_num())?,
Arg::Sw => {
let index = f.modrm.map_or(0, |m| m.reg);
let index = if self.legacy() { index & 3 } else { index };
u64::from(regs.segment(index))
}
Arg::Sr => u64::from(regs.segment((f.opcode >> 3) & 7)),
Arg::Ib | Arg::Iw | Arg::Iv | Arg::Iz | Arg::Ibs => f.imm & Self::mask(size),
Arg::Rb | Arg::Rv => regs.read(f.opcode_reg(), size, rex),
Arg::Al => u64::from(regs.byte(0)),
Arg::Ax => regs.read(0, size, rex),
Arg::Cl => u64::from(regs.byte(1)),
Arg::Dx => regs.read(2, 2, rex),
Arg::One => 1,
Arg::M | Arg::Mp | Arg::Ms => self.ea().1,
Arg::Ob | Arg::Ov => {
let (sr, off) = self.ea();
self.read_mem(sr, off, size)?
}
_ => 0,
};
Ok(value & Self::mask(size))
}
#[inline]
fn system_reg_size(&self) -> u8 {
if self.sixty_four() { 8 } else { 4 }
}
pub(super) fn write_arg(&mut self, f: &Fields, arg: Arg, size: u8, value: u64) -> Ex<()> {
let rex = f.has_rex();
match arg {
Arg::Eb | Arg::Ev | Arg::Ew | Arg::Ed | Arg::Ey => match f.modrm {
Some(m) if m.is_register() => self.state.regs.write(f.rm_num(), size, rex, value),
_ => {
let (sr, off) = self.ea();
self.write_mem(sr, off, size, value)?;
}
},
Arg::Gb | Arg::Gv | Arg::Gw | Arg::Gy => {
self.state.regs.write(f.reg_num(), size, rex, value);
}
Arg::Rd => {
let width = self.system_reg_size();
self.state.regs.write(f.rm_num(), width, rex, value);
}
Arg::Cd => self.write_control(f.reg_num(), value)?,
Arg::Dd => self.write_debug(f.reg_num(), value)?,
Arg::Td => self.write_test(f.reg_num(), value)?,
Arg::Sw => {
let index = f.modrm.map_or(0, |m| m.reg);
let index = if self.legacy() { index & 3 } else { index };
self.load_segment(index, value as u16)?;
}
Arg::Sr => {
let index = (f.opcode >> 3) & 7;
self.load_segment(index, value as u16)?;
}
Arg::Rb | Arg::Rv => self.state.regs.write(f.opcode_reg(), size, rex, value),
Arg::Al => self.state.regs.set_byte(0, value as u8),
Arg::Ax => self.state.regs.write(0, size, rex, value),
Arg::Cl => self.state.regs.set_byte(1, value as u8),
Arg::Dx => self.state.regs.write(2, 2, rex, value),
Arg::Ob | Arg::Ov => {
let (sr, off) = self.ea();
self.write_mem(sr, off, size, value)?;
}
_ => {}
}
Ok(())
}
#[allow(clippy::too_many_lines)]
fn execute(&mut self, f: &Fields) -> Ex<()> {
let insn = f.insn;
let size = Self::width(f);
if !self.cfg.features.fpu && !f.two_byte && matches!(f.opcode, 0xd8..=0xdf) {
return self.escape(f);
}
if insn.op.is_x87() {
return self.x87_instruction(f);
}
if insn.op.is_simd() {
return self.sse_instruction(f);
}
match insn.op {
Op::UD => return Err(Fault::bare(VEC_UD)),
Op::ADD | Op::ADC | Op::SUB | Op::SBB | Op::CMP | Op::AND | Op::OR | Op::XOR => {
self.arith(f, size)?;
}
Op::TEST => {
let a = self.read_arg(f, insn.dst, size)?;
let b = self.read_arg(f, insn.src, size)?;
self.logic_flags(a & b, size);
}
Op::INC | Op::DEC => {
let carry = self.flag(flags::CF);
let a = self.read_arg(f, insn.dst, size)?;
let r = if insn.op == Op::INC {
self.add(a, 1, false, size)
} else {
self.sub(a, 1, false, size)
};
self.write_arg(f, insn.dst, size, r)?;
self.set_flag(flags::CF, carry);
}
Op::NOT => {
let a = self.read_arg(f, insn.dst, size)?;
self.write_arg(f, insn.dst, size, !a)?;
}
Op::NEG => {
let a = self.read_arg(f, insn.dst, size)?;
let r = self.sub(0, a, false, size);
self.write_arg(f, insn.dst, size, r)?;
}
Op::MOV => self.mov(f, size)?,
Op::MOVZX | Op::MOVSX => {
let src_size = if insn.src == Arg::Eb { 1 } else { 2 };
let raw = self.read_arg(f, insn.src, src_size)?;
let value = if insn.op == Op::MOVZX {
raw
} else {
Self::sign_extend(raw, src_size)
};
self.write_arg(f, insn.dst, f.opsize, value)?;
}
Op::MOVSXD => {
let raw = self.read_arg(f, insn.src, 4)?;
let value = if f.opsize == 8 {
Self::sign_extend(raw, 4)
} else {
raw
};
self.write_arg(f, insn.dst, f.opsize, value)?;
}
Op::RDMSR => self.rdmsr()?,
Op::RDTSC => self.rdtsc()?,
Op::WRMSR => self.wrmsr()?,
Op::SYSCALL => self.syscall()?,
Op::SYSRET => self.sysret(f.opsize)?,
Op::SWAPGS => self.swapgs()?,
Op::REX => {}
op if op.is_cmov() => {
if !self.cfg.features.cmov {
return Err(Fault::bare(VEC_UD));
}
let cc = op.condition_code().unwrap_or(0);
let value = self.read_arg(f, insn.src, size)?;
let value = if self.condition(cc) {
value
} else {
self.read_arg(f, insn.dst, size)?
};
self.write_arg(f, insn.dst, size, value)?;
}
Op::XCHG => {
let a = self.read_arg(f, insn.dst, size)?;
let b = self.read_arg(f, insn.src, size)?;
self.write_arg(f, insn.dst, size, b)?;
self.write_arg(f, insn.src, size, a)?;
}
Op::XADD => {
let a = self.read_arg(f, insn.dst, size)?;
let b = self.read_arg(f, insn.src, size)?;
let sum = self.add(a, b, false, size);
self.write_arg(f, insn.src, size, a)?;
self.write_arg(f, insn.dst, size, sum)?;
}
Op::CMPXCHG => {
let dst = self.read_arg(f, insn.dst, size)?;
let acc = self.state.regs.read(0, size, false);
self.sub(acc, dst, false, size);
if acc & Self::mask(size) == dst {
let src = self.read_arg(f, insn.src, size)?;
self.write_arg(f, insn.dst, size, src)?;
} else {
self.state.regs.write(0, size, false, dst);
self.write_arg(f, insn.dst, size, dst)?;
}
}
Op::BSWAP => {
let index = f.opcode_reg();
if f.opsize == 8 {
let value = self.state.regs.qword(index);
self.state.regs.set_qword(index, value.swap_bytes());
} else {
let value = self.state.regs.dword(index);
self.state.regs.set_dword(index, value.swap_bytes());
}
}
Op::LEA => {
let offset = self.ea().1;
let offset = offset & Self::mask(f.addrsize);
self.write_arg(f, insn.dst, f.opsize, offset)?;
}
Op::LES | Op::LDS | Op::LSS | Op::LFS | Op::LGS => self.load_far_pointer(f)?,
Op::PUSH => self.push_op(f)?,
Op::POP => {
let value = self.pop(f.opsize)?;
self.write_arg(f, insn.dst, f.opsize, value)?;
}
Op::PUSHA => self.pusha(f)?,
Op::POPA => self.popa(f)?,
Op::ENTER => self.enter(f)?,
Op::LEAVE => {
let bp = match self.stack_addr_size() {
2 => u64::from(self.state.regs.word(5)),
4 => u64::from(self.state.regs.dword(5)),
_ => self.state.regs.rbp,
};
self.set_sp(bp);
let value = self.pop(f.opsize)?;
self.state.regs.write(5, f.opsize, false, value);
}
Op::PUSHF => {
let value = self.state.regs.eflags & !(flags::VM | flags::RF);
self.push(u64::from(value), f.opsize)?;
}
Op::POPF => self.popf(f)?,
Op::SAHF => {
let ah = u32::from(self.state.regs.byte(4));
let kept = self.state.regs.eflags & !flags::LOW_BYTE;
self.set_flags(kept | (ah & flags::LOW_BYTE));
}
Op::LAHF => {
let low = (self.state.regs.eflags & 0xff) as u8;
self.state.regs.set_byte(4, low);
}
Op::CBW => match f.opsize {
2 => {
let al = self.state.regs.byte(0);
self.state.regs.set_word(0, i16::from(al as i8) as u16);
}
4 => {
let ax = self.state.regs.word(0);
self.state.regs.set_dword(0, i32::from(ax as i16) as u32);
}
_ => {
let eax = self.state.regs.dword(0);
self.state.regs.set_qword(0, i64::from(eax as i32) as u64);
}
},
Op::CWD => match f.opsize {
2 => {
let fill = if self.state.regs.word(0) & 0x8000 != 0 {
0xffff
} else {
0
};
self.state.regs.set_word(2, fill);
}
4 => {
let fill = if self.state.regs.dword(0) & 0x8000_0000 != 0 {
0xffff_ffff
} else {
0
};
self.state.regs.set_dword(2, fill);
}
_ => {
let fill = if self.state.regs.rax & (1 << 63) != 0 {
u64::MAX
} else {
0
};
self.state.regs.set_qword(2, fill);
}
},
Op::ROL | Op::ROR | Op::RCL | Op::RCR | Op::SHL | Op::SHR | Op::SAR | Op::SETMO => {
self.shift(f, size)?;
}
Op::SHLD | Op::SHRD => self.double_shift(f, size)?,
Op::MUL | Op::IMUL => self.multiply(f, size)?,
Op::DIV | Op::IDIV => self.divide(f, size)?,
Op::AAM => self.aam(f)?,
Op::AAD => self.aad(f)?,
Op::DAA => self.decimal_adjust(false),
Op::DAS => self.decimal_adjust(true),
Op::AAA => self.ascii_adjust(false),
Op::AAS => self.ascii_adjust(true),
Op::CLC => self.set_flag(flags::CF, false),
Op::STC => self.set_flag(flags::CF, true),
Op::CMC => {
let cf = self.flag(flags::CF);
self.set_flag(flags::CF, !cf);
}
Op::CLD => self.set_flag(flags::DF, false),
Op::STD => self.set_flag(flags::DF, true),
Op::CLI => {
if self.protected() && self.cpl() > self.state.regs.iopl() {
return Err(Fault::gp(0));
}
self.set_flag(flags::IF, false);
}
Op::STI => {
if self.protected() && self.cpl() > self.state.regs.iopl() {
return Err(Fault::gp(0));
}
self.set_flag(flags::IF, true);
self.state.int_shadow = true;
}
Op::NOP => {
if f.rex & 1 != 0 {
let index = f.opcode_reg();
let a = self.state.regs.read(0, size, true);
let b = self.state.regs.read(index, size, true);
self.state.regs.write(0, size, true, b);
self.state.regs.write(index, size, true, a);
}
}
Op::WAIT => {
if self.cfg.features.fpu {
self.fwait()?;
}
}
Op::LOCK | Op::REP | Op::REPNE | Op::SEG => {}
Op::CMPXCHG8B => self.cmpxchg8b(f)?,
Op::HLT => {
if self.protected() && self.cpl() != 0 {
return Err(Fault::gp(0));
}
self.state.halted = true;
}
Op::ESC => self.escape(f)?,
Op::SALC => {
let value = if self.flag(flags::CF) { 0xff } else { 0x00 };
self.state.regs.set_byte(0, value);
}
Op::XLAT => {
let sr = f.segment(seg::DS);
let al = u64::from(self.state.regs.byte(0));
let base = match f.addrsize {
2 => u64::from(self.state.regs.word(3).wrapping_add(al as u16)),
4 => u64::from(self.state.regs.dword(3).wrapping_add(al as u32)),
_ => self.state.regs.rbx.wrapping_add(al),
};
let value = self.read_mem(sr, base, 1)?;
self.state.regs.set_byte(0, value as u8);
}
Op::IN => {
let port = self.port(f, insn.src);
let width = if insn.dst == Arg::Al {
1
} else {
Self::io_width(f.opsize)
};
self.io_permitted(port, width)?;
let value = self.io_read_sized(port, width);
self.state.regs.write(0, width, false, u64::from(value));
}
Op::OUT => {
let port = self.port(f, insn.dst);
let width = if insn.src == Arg::Al {
1
} else {
Self::io_width(f.opsize)
};
self.io_permitted(port, width)?;
let value = self.state.regs.read(0, width, false) as u32;
self.io_write_sized(port, width, value);
}
Op::CALL => self.call_near(f)?,
Op::CALLF => {
let (offset, selector) = self.far_target(f)?;
self.far_transfer(selector, offset, true, f.opsize)?;
}
Op::JMP => {
let target = match insn.dst {
Arg::Jv | Arg::Jb => self.relative_target(f),
_ => self.read_arg(f, insn.dst, f.opsize)?,
};
self.jump_near(target, f.opsize)?;
}
Op::JMPF => {
let (offset, selector) = self.far_target(f)?;
self.far_transfer(selector, offset, false, f.opsize)?;
}
Op::RET => {
let ip = self.pop(f.opsize)?;
let extra = if matches!(insn.dst, Arg::Iw | Arg::Iv | Arg::Iz) {
f.imm & 0xffff
} else {
0
};
self.jump_near(ip, f.opsize)?;
let sp = self.sp().wrapping_add(extra);
self.set_sp(sp);
}
Op::RETF => {
let extra = if matches!(insn.dst, Arg::Iw | Arg::Iv | Arg::Iz) {
f.imm & 0xffff
} else {
0
};
self.return_far(f.opsize, extra)?;
}
Op::IRET => self.iret(f.opsize)?,
Op::INT => {
let vector = f.imm as u8;
self.software_interrupt(vector)?;
}
Op::INT3 => self.software_interrupt(VEC_BREAKPOINT)?,
Op::ICEBP => {
self.take_interrupt(VEC_DEBUG, None)?;
}
Op::INTO => {
if self.flag(flags::OF) {
self.software_interrupt(VEC_OVERFLOW)?;
}
}
Op::BOUND => self.bound(f)?,
Op::LOOP | Op::LOOPE | Op::LOOPNE => {
let count = self.counter(f).wrapping_sub(1);
self.set_counter(f, count);
let zf = self.flag(flags::ZF);
let take = count != 0
&& match insn.op {
Op::LOOPE => zf,
Op::LOOPNE => !zf,
_ => true,
};
if take {
let target = self.relative_target(f);
self.jump_near(target, f.opsize)?;
}
}
Op::JCXZ => {
if self.counter(f) == 0 {
let target = self.relative_target(f);
self.jump_near(target, f.opsize)?;
}
}
Op::BT | Op::BTS | Op::BTR | Op::BTC => self.bit_test(f, size)?,
Op::BSF | Op::BSR => {
let src = self.read_arg(f, insn.src, size)?;
self.set_flag(flags::ZF, src == 0);
if src != 0 {
let bits = u32::from(size) * 8;
let index = if insn.op == Op::BSF {
src.trailing_zeros()
} else {
bits - 1 - (src.leading_zeros() - (64 - bits))
};
self.write_arg(f, insn.dst, size, u64::from(index))?;
}
}
Op::CPUID => self.cpuid()?,
Op::CLTS => {
if self.protected() && self.cpl() != 0 {
return Err(Fault::gp(0));
}
self.state.sys.cr0 &= !cr0::TS;
}
Op::INVD | Op::WBINVD => {
if self.protected() && self.cpl() != 0 {
return Err(Fault::gp(0));
}
}
Op::INVLPG => {
if f.rm_is_register() {
if self.sixty_four() && f.modrm.is_some_and(|m| m.rm == 0) {
self.swapgs()?;
return Ok(());
}
return Err(Fault::bare(VEC_UD));
}
if self.protected() && self.cpl() != 0 {
return Err(Fault::gp(0));
}
let (_, offset) = self.ea();
let base = self.state.sys.seg(f.mem_segment()).base;
self.state.tlb.invalidate(base.wrapping_add(offset));
}
Op::LGDT | Op::LIDT => self.load_table_register(f)?,
Op::SGDT | Op::SIDT => self.store_table_register(f)?,
Op::LLDT | Op::LTR => self.load_system_selector(f)?,
Op::SLDT | Op::STR => {
let selector = if insn.op == Op::SLDT {
self.state.sys.ldtr.selector
} else {
self.state.sys.task.selector
};
self.require_protected()?;
self.write_arg(f, insn.dst, 2, u64::from(selector))?;
}
Op::SMSW => {
let value = u64::from(self.state.sys.cr0);
if f.rm_is_register() {
self.write_arg(f, insn.dst, f.opsize, value)?;
} else {
self.write_arg(f, insn.dst, 2, value & 0xffff)?;
}
}
Op::LMSW => self.lmsw(f)?,
Op::LAR | Op::LSL => self.lar_lsl(f)?,
Op::VERR | Op::VERW => self.verify(f)?,
Op::ARPL => self.arpl(f)?,
op if op.is_setcc() => {
let cc = op.condition_code().unwrap_or(0);
let value = u64::from(self.condition(cc));
self.write_arg(f, insn.dst, 1, value)?;
}
op if op.is_conditional_jump() => {
let cc = op.condition_code().unwrap_or(0);
if self.condition(cc) {
let target = self.relative_target(f);
self.jump_near(target, f.opsize)?;
}
}
op if op.is_string() => self.string(f, size)?,
_ => {}
}
Ok(())
}
fn mov(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
if insn.src == Arg::Sw {
let value = self.read_arg(f, Arg::Sw, 2)?;
if f.rm_is_register() {
self.write_arg(f, insn.dst, f.opsize, value)?;
} else {
self.write_arg(f, insn.dst, 2, value)?;
}
return Ok(());
}
if !self.legacy() && (insn.dst == Arg::Sw || insn.src == Arg::Sw) {
let index = f.modrm.map_or(0, |m| m.reg);
if index > seg::GS || (index == seg::CS && insn.dst == Arg::Sw) {
return Err(Fault::bare(VEC_UD));
}
}
let value = self.read_arg(f, insn.src, size)?;
self.write_arg(f, insn.dst, size, value)
}
fn port(&self, f: &Fields, arg: Arg) -> u16 {
match arg {
Arg::Dx => self.state.regs.word(2),
_ => u16::from(f.imm as u8),
}
}
fn far_target(&mut self, f: &Fields) -> Ex<(u64, u16)> {
if f.insn.dst == Arg::Ap {
return Ok((f.imm_sized(), f.imm_seg()));
}
let (sr, off) = self.ea();
let offset = self.read_mem(sr, off, f.opsize)?;
let selector = self.read_mem(sr, off.wrapping_add(u64::from(f.opsize)), 2)?;
Ok((offset, selector as u16))
}
fn relative_target(&self, f: &Fields) -> u64 {
let next = self.state.regs.rip;
let target = next.wrapping_add(f.imm);
target & Self::mask(f.opsize)
}
fn counter(&self, f: &Fields) -> u64 {
match f.addrsize {
2 => u64::from(self.state.regs.word(1)),
4 => u64::from(self.state.regs.dword(1)),
_ => self.state.regs.rcx,
}
}
fn set_counter(&mut self, f: &Fields, value: u64) {
match f.addrsize {
2 => self.state.regs.set_word(1, value as u16),
4 => self.state.regs.set_dword(1, value as u32),
_ => self.state.regs.set_qword(1, value),
}
}
fn condition(&self, cc: u8) -> bool {
let cf = self.flag(flags::CF);
let zf = self.flag(flags::ZF);
let sf = self.flag(flags::SF);
let of = self.flag(flags::OF);
let pf = self.flag(flags::PF);
match cc & 15 {
0 => of,
1 => !of,
2 => cf,
3 => !cf,
4 => zf,
5 => !zf,
6 => cf || zf,
7 => !cf && !zf,
8 => sf,
9 => !sf,
10 => pf,
11 => !pf,
12 => sf != of,
13 => sf == of,
14 => zf || (sf != of),
_ => !zf && (sf == of),
}
}
fn arith(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let carry = self.flag(flags::CF);
let a = self.read_arg(f, insn.dst, size)?;
let b = self.read_arg(f, insn.src, size)?;
let r = match insn.op {
Op::ADD => self.add(a, b, false, size),
Op::ADC => self.add(a, b, carry, size),
Op::SUB | Op::CMP => self.sub(a, b, false, size),
Op::SBB => self.sub(a, b, carry, size),
Op::AND => {
let r = a & b;
self.logic_flags(r, size);
r
}
Op::OR => {
let r = a | b;
self.logic_flags(r, size);
r
}
_ => {
let r = a ^ b;
self.logic_flags(r, size);
r
}
};
if insn.op != Op::CMP {
self.write_arg(f, insn.dst, size, r)?;
}
Ok(())
}
fn push_op(&mut self, f: &Fields) -> Ex<()> {
let insn = f.insn;
let size = f.opsize;
if self.legacy() {
let sp = self.sp().wrapping_sub(u64::from(size)) & 0xffff;
self.set_sp(sp);
let value = self.read_arg(f, insn.dst, size)?;
return self.write_mem(seg::SS, sp, size, value);
}
let value = self.read_arg(f, insn.dst, size)?;
self.push(value, size)
}
fn pusha(&mut self, f: &Fields) -> Ex<()> {
let size = f.opsize;
let original_sp = self.sp();
for index in 0..8u8 {
let value = if index == 4 {
original_sp
} else {
self.state.regs.read(index, size, false)
};
self.push(value, size)?;
}
Ok(())
}
fn popa(&mut self, f: &Fields) -> Ex<()> {
let size = f.opsize;
for index in (0..8u8).rev() {
let value = self.pop(size)?;
if index != 4 {
self.state.regs.write(index, size, false, value);
}
}
Ok(())
}
fn enter(&mut self, f: &Fields) -> Ex<()> {
let size = f.opsize;
let frame = f.imm & 0xffff;
let level = (f.imm2 & 0x1f) as u8;
let bp = self.state.regs.read(5, size, false);
self.push(bp, size)?;
let frame_ptr = self.sp();
for _ in 1..level {
let bp = self.state.regs.read(5, size, false);
let bp = bp.wrapping_sub(u64::from(size));
self.state.regs.write(5, size, false, bp);
let value = self.read_mem(seg::SS, bp, size)?;
self.push(value, size)?;
}
if level > 0 {
self.push(frame_ptr, size)?;
}
self.state.regs.write(5, size, false, frame_ptr);
let sp = frame_ptr.wrapping_sub(frame);
self.set_sp(sp);
Ok(())
}
fn popf(&mut self, f: &Fields) -> Ex<()> {
let value = self.pop(f.opsize)? as u32;
let old = self.state.regs.eflags;
if self.legacy() {
self.set_flags(value);
return Ok(());
}
let cpl = self.cpl();
let iopl = self.state.regs.iopl();
let mut keep = flags::POPF_FORBIDDEN;
if self.protected() && cpl > iopl {
keep |= flags::IF;
}
if self.protected() && cpl > 0 {
keep |= flags::IOPL;
}
if f.opsize == 2 {
keep |= 0xffff_0000;
}
self.set_flags((value & !keep) | (old & keep));
Ok(())
}
fn escape(&mut self, f: &Fields) -> Ex<()> {
if self.legacy() {
if matches!(f.modrm, Some(m) if !m.is_register()) {
let (sr, off) = self.ea();
let _ = self.read_mem(sr, off, 2)?;
}
return Ok(());
}
let cr = self.state.sys.cr0;
if cr & (cr0::EM | cr0::TS) != 0 {
return Err(Fault::bare(VEC_NM));
}
if matches!(f.modrm, Some(m) if !m.is_register()) {
let (sr, off) = self.ea();
let _ = self.read_mem(sr, off, 2)?;
}
Ok(())
}
fn bound(&mut self, f: &Fields) -> Ex<()> {
let size = f.opsize;
let (sr, off) = self.ea();
let lower = self.read_mem(sr, off, size)?;
let upper = self.read_mem(sr, off.wrapping_add(u64::from(size)), size)?;
let index = self.read_arg(f, f.insn.dst, size)?;
let lower = Self::sign_extend(lower, size) as i64;
let upper = Self::sign_extend(upper, size) as i64;
let index = Self::sign_extend(index, size) as i64;
if index < lower || index > upper {
return Err(Fault::bare(VEC_BOUND));
}
Ok(())
}
pub(super) const fn sign_extend(value: u64, size: u8) -> u64 {
match size {
1 => ((value as u8) as i8) as i64 as u64,
2 => ((value as u16) as i16) as i64 as u64,
4 => ((value as u32) as i32) as i64 as u64,
_ => value,
}
}
fn bit_test(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let bits = u64::from(size) * 8;
let raw = self.read_arg(f, insn.src, if insn.src == Arg::Ib { 1 } else { size })?;
let bounded = f.rm_is_register() || insn.src == Arg::Ib;
let (index, offset_bytes) = if bounded {
(raw % bits, 0i64)
} else {
let signed = Self::sign_extend(raw, size) as i64;
let word = signed.div_euclid(bits as i64);
let bit = signed.rem_euclid(bits as i64) as u64;
(bit, word * i64::from(size))
};
let value = if bounded {
self.read_arg(f, insn.dst, size)?
} else {
let (sr, off) = self.ea();
let off = off.wrapping_add(offset_bytes as u64);
self.read_mem(sr, off, size)?
};
let bit = (value >> index) & 1;
self.set_flag(flags::CF, bit != 0);
let updated = match insn.op {
Op::BTS => value | (1 << index),
Op::BTR => value & !(1 << index),
Op::BTC => value ^ (1 << index),
_ => return Ok(()),
};
if bounded {
self.write_arg(f, insn.dst, size, updated)?;
} else {
let (sr, off) = self.ea();
let off = off.wrapping_add(offset_bytes as u64);
self.write_mem(sr, off, size, updated)?;
}
Ok(())
}
fn shift(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let raw = match insn.src {
Arg::One => 1,
Arg::Cl => u64::from(self.state.regs.byte(1)),
_ => f.imm & 0xff,
};
let count = if self.legacy() {
raw as u8
} else if size == 8 {
(raw & 0x3f) as u8
} else {
(raw & 0x1f) as u8
};
if count == 0 && !self.legacy() {
return Ok(());
}
let a = self.read_arg(f, insn.dst, size)?;
let r = self.shift_value(insn.op, a, count, size);
self.write_arg(f, insn.dst, size, r)
}
fn shift_value(&mut self, op: Op, value: u64, count: u8, size: u8) -> u64 {
if count == 0 {
return value;
}
let mask = Self::mask(size);
let msb = Self::msb(size);
if op == Op::SETMO {
self.logic_flags(mask, size);
return mask;
}
let mut v = value & mask;
let mut cf = self.flag(flags::CF);
let mut of = self.flag(flags::OF);
for _ in 0..count {
match op {
Op::ROL => {
cf = v & msb != 0;
v = ((v << 1) | u64::from(cf)) & mask;
of = (v & msb != 0) != cf;
}
Op::ROR => {
cf = v & 1 != 0;
v = ((v >> 1) | (u64::from(cf) * msb)) & mask;
of = (v & msb != 0) != (v & (msb >> 1) != 0);
}
Op::RCL => {
let carry_in = cf;
cf = v & msb != 0;
v = ((v << 1) | u64::from(carry_in)) & mask;
of = (v & msb != 0) != cf;
}
Op::RCR => {
let carry_in = cf;
of = (v & msb != 0) != carry_in;
cf = v & 1 != 0;
v = ((v >> 1) | (u64::from(carry_in) * msb)) & mask;
}
Op::SHL => {
cf = v & msb != 0;
v = (v << 1) & mask;
of = (v & msb != 0) != cf;
}
Op::SHR => {
of = v & msb != 0;
cf = v & 1 != 0;
v >>= 1;
}
_ => {
of = false;
cf = v & 1 != 0;
let filled = v | if v & msb != 0 { !mask } else { 0 };
v = ((filled as i64) >> 1) as u64 & mask;
}
}
}
self.set_flag(flags::CF, cf);
self.set_flag(flags::OF, of);
if matches!(op, Op::SHL | Op::SHR | Op::SAR) {
self.set_szp(v, size);
self.set_flag(flags::AF, op == Op::SHL && v & 0x10 != 0);
}
v
}
fn double_shift(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let raw = match insn.aux {
Arg::Cl => u64::from(self.state.regs.byte(1)),
_ => f.imm & 0xff,
};
let count = if size == 8 {
(raw & 0x3f) as u8
} else {
(raw & 0x1f) as u8
};
if count == 0 {
return Ok(());
}
let bits = u32::from(size) * 8;
if u32::from(count) >= bits {
return Ok(());
}
let dst = self.read_arg(f, insn.dst, size)?;
let src = self.read_arg(f, insn.src, size)?;
let mask = Self::mask(size);
let n = u32::from(count);
let (result, carry) = if insn.op == Op::SHLD {
let r = ((dst << n) | (src >> (bits - n))) & mask;
(r, (dst >> (bits - n)) & 1 != 0)
} else {
let r = ((dst >> n) | (src << (bits - n))) & mask;
(r, (dst >> (n - 1)) & 1 != 0)
};
self.set_flag(flags::CF, carry);
if count == 1 {
let msb = Self::msb(size);
self.set_flag(flags::OF, (dst ^ result) & msb != 0);
}
self.set_szp(result, size);
self.write_arg(f, insn.dst, size, result)
}
fn mul_flags(&mut self, high: u64, size: u8, overflow: bool) {
self.set_flag(flags::ZF, high == 0);
self.set_flag(flags::SF, high & Self::msb(size) != 0);
self.set_flag(flags::PF, Self::parity(high as u8));
self.set_flag(flags::AF, false);
self.set_flag(flags::CF | flags::OF, overflow);
}
fn multiply(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
if insn.dst == Arg::Gv {
return self.imul_short(f, size);
}
let signed = insn.op == Op::IMUL;
let src = self.read_arg(f, insn.dst, size)?;
let acc = self.state.regs.read(0, size, false);
let mask = Self::mask(size);
let bits = u32::from(size) * 8;
let product: u128 = if signed {
let a = Self::sign_extend(acc, size) as i64 as i128;
let b = Self::sign_extend(src, size) as i64 as i128;
(a.wrapping_mul(b)) as u128
} else {
u128::from(acc & mask) * u128::from(src & mask)
};
let low = (product as u64) & mask;
let high = ((product >> bits) as u64) & mask;
if size == 1 {
self.state.regs.set_word(0, (low | (high << 8)) as u16);
} else {
self.state.regs.write(0, size, false, low);
self.state.regs.write(2, size, false, high);
}
let overflow = if signed {
let sign_fill = if low & Self::msb(size) != 0 { mask } else { 0 };
high != sign_fill
} else {
high != 0
};
self.mul_flags(high, size, overflow);
Ok(())
}
fn imul_short(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let a = self.read_arg(f, insn.src, size)?;
let b = if insn.aux == Arg::None {
self.read_arg(f, insn.dst, size)?
} else {
let raw = self.read_arg(f, insn.aux, if insn.aux == Arg::Ibs { 1 } else { size })?;
if insn.aux == Arg::Ibs {
Self::sign_extend(raw, 1)
} else {
raw
}
};
let a = Self::sign_extend(a, size) as i64 as i128;
let b = Self::sign_extend(b, size) as i64 as i128;
let product = a.wrapping_mul(b);
let truncated = (product as u64) & Self::mask(size);
let fits = i128::from(Self::sign_extend(truncated, size) as i64) == product;
self.set_flag(flags::CF | flags::OF, !fits);
let high = ((product as u128) >> (u32::from(size) * 8)) as u64 & Self::mask(size);
self.set_flag(flags::ZF, high == 0);
self.set_flag(flags::SF, high & Self::msb(size) != 0);
self.set_flag(flags::PF, Self::parity(high as u8));
self.set_flag(flags::AF, false);
self.write_arg(f, insn.dst, size, truncated)
}
fn divide(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let signed = insn.op == Op::IDIV;
let negate = signed && f.rep.is_some() && self.legacy();
let bits = u32::from(size) * 8;
let source = self.read_arg(f, insn.dst, size)?;
let dividend: u128 = if size == 1 {
u128::from(self.state.regs.word(0))
} else {
let high = u128::from(self.state.regs.read(2, size, false));
let low = u128::from(self.state.regs.read(0, size, false));
(high << bits) | low
};
let (magnitude, divisor_magnitude) = if signed {
(
Self::sign_extend_wide(dividend, bits * 2).unsigned_abs(),
(Self::sign_extend(source, size) as i64 as i128).unsigned_abs(),
)
} else {
(dividend, u128::from(source))
};
let (quotient_magnitude, remainder_magnitude) =
self.cord(magnitude, divisor_magnitude, bits);
let mask = u128::from(Self::mask(size));
let (quotient, remainder, fault) = if divisor_magnitude == 0 {
(0, 0, true)
} else if signed {
let n = Self::sign_extend_wide(dividend, bits * 2);
let d = Self::sign_extend(source, size) as i64 as i128;
match (n.checked_div(d), n.checked_rem(d)) {
(Some(mut q), Some(r)) => {
if negate {
q = q.wrapping_neg();
}
let limit = 1i128 << (bits - 1);
(
(q as u128) & mask,
(r as u128) & mask,
!(-limit..limit).contains(&q),
)
}
_ => (0, 0, true),
}
} else {
let q = dividend / divisor_magnitude;
debug_assert!(q > mask || q == quotient_magnitude);
(
quotient_magnitude & mask,
remainder_magnitude & mask,
q > mask,
)
};
if fault {
return Err(Fault::bare(VEC_DIVIDE));
}
self.set_flag(flags::CF, quotient & (1 << (bits - 1)) == 0);
if size == 1 {
self.state
.regs
.set_word(0, ((quotient & 0xff) | ((remainder & 0xff) << 8)) as u16);
} else {
self.state.regs.write(0, size, false, quotient as u64);
self.state.regs.write(2, size, false, remainder as u64);
}
Ok(())
}
const fn sign_extend_wide(value: u128, bits: u32) -> i128 {
let shift = 128 - bits;
((value as i128) << shift) >> shift
}
fn cord(&mut self, dividend: u128, divisor: u128, bits: u32) -> (u128, u128) {
let mask = if bits >= 128 {
u128::MAX
} else {
(1u128 << bits) - 1
};
let top = 1u128 << (bits - 1);
let mut remainder = (dividend >> bits) & mask;
let mut quotient = dividend & mask;
for _ in 0..bits {
let carried = (quotient >> (bits - 1)) & 1;
quotient = (quotient << 1) & mask;
let overflowed = remainder & top != 0;
let shifted = ((remainder << 1) | carried) & mask;
let difference = shifted.wrapping_sub(divisor) & mask;
let borrow = shifted < divisor;
self.set_flag(flags::CF, borrow);
self.set_flag(flags::AF, (shifted ^ divisor ^ difference) & 0x10 != 0);
self.set_flag(
flags::OF,
(shifted ^ divisor) & (shifted ^ difference) & top != 0,
);
self.set_flag(flags::SF, difference & top != 0);
self.set_flag(flags::ZF, difference == 0);
self.set_flag(flags::PF, Self::parity(difference as u8));
if overflowed || !borrow {
remainder = difference;
quotient |= 1;
} else {
remainder = shifted;
}
}
(quotient, remainder)
}
fn aam(&mut self, f: &Fields) -> Ex<()> {
let base = f.imm as u8;
if base == 0 {
self.set_flag(flags::CF | flags::OF | flags::AF, false);
self.set_szp(0, 1);
return Err(Fault::bare(VEC_DIVIDE));
}
let al = self.state.regs.byte(0);
let quotient = al / base;
let remainder = al % base;
self.state
.regs
.set_word(0, u16::from(remainder) | (u16::from(quotient) << 8));
self.set_szp(u64::from(remainder), 1);
self.set_flag(flags::CF | flags::OF | flags::AF, false);
Ok(())
}
fn aad(&mut self, f: &Fields) -> Ex<()> {
let base = f.imm as u8;
let al = self.state.regs.byte(0);
let ah = self.state.regs.byte(4);
let product = ah.wrapping_mul(base);
let r = self.add(u64::from(product), u64::from(al), false, 1);
self.state.regs.set_word(0, r as u16);
Ok(())
}
fn decimal_adjust(&mut self, subtract: bool) {
let al = self.state.regs.byte(0);
let auxiliary = self.flag(flags::AF);
let low = (al & 0x0f) > 9 || auxiliary;
let threshold = if auxiliary { 0x9f } else { 0x99 };
let high = self.flag(flags::CF) || al > threshold;
let correction = u64::from(if low { 0x06u8 } else { 0x00 })
+ u64::from(if high { 0x60u8 } else { 0x00 });
let adjusted = if subtract {
self.sub(u64::from(al), correction, false, 1)
} else {
self.add(u64::from(al), correction, false, 1)
};
self.state.regs.set_byte(0, adjusted as u8);
self.set_flag(flags::CF, high);
self.set_flag(flags::AF, low);
}
fn ascii_adjust(&mut self, subtract: bool) {
let al = self.state.regs.byte(0);
let adjust = (al & 0x0f) > 9 || self.flag(flags::AF);
let operand = u64::from(if adjust { 6u8 } else { 0 });
let adjusted = if subtract {
self.sub(u64::from(al), operand, false, 1)
} else {
self.add(u64::from(al), operand, false, 1)
};
let ah = self.state.regs.byte(4);
let ah = match (adjust, subtract) {
(true, false) => ah.wrapping_add(1),
(true, true) => ah.wrapping_sub(1),
(false, _) => ah,
};
self.state
.regs
.set_word(0, (u16::from(ah) << 8) | u16::from(adjusted as u8 & 0x0f));
self.set_flag(flags::CF | flags::AF, adjust);
}
fn string(&mut self, f: &Fields, size: u8) -> Ex<()> {
let op = f.insn.op;
let delta = if self.flag(flags::DF) {
u64::from(size).wrapping_neg()
} else {
u64::from(size)
};
let Some(rep) = f.rep else {
return self.string_step(f, size, delta);
};
while self.counter(f) != 0 {
self.string_step(f, size, delta)?;
let count = self.counter(f).wrapping_sub(1);
self.set_counter(f, count);
if op.repeat_tests_zf() {
let zf = self.flag(flags::ZF);
let stop = match rep {
Rep::While => !zf,
Rep::WhileNot => zf,
};
if stop {
break;
}
}
if count == 0 {
break;
}
self.charge(op.clocks());
if self.lines.nmi_pending() || (self.flag(flags::IF) && self.lines.intr_pending()) {
self.state.regs.rip = self.start_ip;
self.state.queue.flush();
return Ok(());
}
}
Ok(())
}
fn si(&self, f: &Fields) -> u64 {
match f.addrsize {
2 => u64::from(self.state.regs.word(6)),
4 => u64::from(self.state.regs.dword(6)),
_ => self.state.regs.rsi,
}
}
fn di(&self, f: &Fields) -> u64 {
match f.addrsize {
2 => u64::from(self.state.regs.word(7)),
4 => u64::from(self.state.regs.dword(7)),
_ => self.state.regs.rdi,
}
}
fn string_step(&mut self, f: &Fields, size: u8, delta: u64) -> Ex<()> {
let op = f.insn.op;
let src_seg = f.segment(seg::DS);
let si = self.si(f);
let di = self.di(f);
let acc = self.state.regs.read(0, size, false);
match op {
Op::MOVSB | Op::MOVSW => {
let value = self.read_mem(src_seg, si, size)?;
self.write_mem(seg::ES, di, size, value)?;
self.advance(f, delta, true, true);
}
Op::CMPSB | Op::CMPSW => {
let a = self.read_mem(src_seg, si, size)?;
let b = self.read_mem(seg::ES, di, size)?;
self.sub(a, b, false, size);
self.advance(f, delta, true, true);
}
Op::STOSB | Op::STOSW => {
self.write_mem(seg::ES, di, size, acc)?;
self.advance(f, delta, false, true);
}
Op::LODSB | Op::LODSW => {
let value = self.read_mem(src_seg, si, size)?;
self.state.regs.write(0, size, false, value);
self.advance(f, delta, true, false);
}
Op::SCASB | Op::SCASW => {
let b = self.read_mem(seg::ES, di, size)?;
self.sub(acc, b, false, size);
self.advance(f, delta, false, true);
}
Op::INSB | Op::INSW => {
let port = self.state.regs.word(2);
let width = Self::io_width(size);
self.io_permitted(port, width)?;
let value = u64::from(self.io_read_sized(port, width));
self.write_mem(seg::ES, di, size, value)?;
self.advance(f, delta, false, true);
}
_ => {
let port = self.state.regs.word(2);
let width = Self::io_width(size);
self.io_permitted(port, width)?;
let value = self.read_mem(src_seg, si, size)?;
self.io_write_sized(port, width, value as u32);
self.advance(f, delta, true, false);
}
}
Ok(())
}
fn advance(&mut self, f: &Fields, delta: u64, si: bool, di: bool) {
for (want, index) in [(si, 6u8), (di, 7)] {
if !want {
continue;
}
match f.addrsize {
2 => {
let v = self.state.regs.word(index).wrapping_add(delta as u16);
self.state.regs.set_word(index, v);
}
4 => {
let v = self.state.regs.dword(index).wrapping_add(delta as u32);
self.state.regs.set_dword(index, v);
}
_ => {
let v = self.state.regs.qword(index).wrapping_add(delta);
self.state.regs.set_qword(index, v);
}
}
}
}
fn jump_near(&mut self, target: u64, opsize: u8) -> Ex<()> {
let target = target & Self::mask(opsize);
if self.sixty_four() {
if !prot::canonical(target) {
return Err(Fault::gp(0));
}
} else if !self.legacy() {
let cs = self.state.sys.seg(seg::CS);
if !cs.in_bounds(target, 1) {
return Err(Fault::gp(0));
}
}
self.state.regs.rip = target;
self.state.queue.flush();
Ok(())
}
fn call_near(&mut self, f: &Fields) -> Ex<()> {
let target = match f.insn.dst {
Arg::Jv | Arg::Jb => self.relative_target(f),
_ => self.read_arg(f, f.insn.dst, f.opsize)?,
};
let ret = self.state.regs.rip;
self.push(ret, f.opsize)?;
self.jump_near(target, f.opsize)
}
fn load_far_pointer(&mut self, f: &Fields) -> Ex<()> {
let size = f.opsize;
let (sr, off) = self.ea();
let offset = self.read_mem(sr, off, size)?;
let selector = self.read_mem(sr, off.wrapping_add(u64::from(size)), 2)? as u16;
let target = match f.insn.op {
Op::LES => seg::ES,
Op::LDS => seg::DS,
Op::LSS => seg::SS,
Op::LFS => seg::FS,
_ => seg::GS,
};
self.load_segment(target, selector)?;
self.write_arg(f, f.insn.dst, size, offset)
}
fn software_interrupt(&mut self, vector: u8) -> Ex<()> {
if self.protected() {
self.check_software_gate(vector)?;
}
self.take_interrupt(vector, None)
}
fn cpuid(&mut self) -> Ex<()> {
let features = self.cfg.features;
if !features.extras_486 {
return Err(Fault::bare(VEC_UD));
}
let mut edx1: u32 = 0;
if features.msr {
edx1 |= 1 << 5; edx1 |= 1 << 4; }
if features.pae {
edx1 |= 1 << 6; }
if self.lines.has_local_controller() {
edx1 |= 1 << 9;
}
if features.pse {
edx1 |= 1 << 3; }
if features.pge {
edx1 |= 1 << 13; }
if features.cmov {
edx1 |= 1 << 15; }
if features.cx8 {
edx1 |= 1 << 8; }
if features.fpu {
edx1 |= 1 << 0; }
if features.fxsr {
edx1 |= 1 << 24; }
if features.sse {
edx1 |= 1 << 25; }
if features.sse2 {
edx1 |= 1 << 26; }
let signature = self.cfg.variant.reset_signature();
let max_basic: u32 = 1;
let leaf = self.state.regs.rax as u32;
let regs = &mut self.state.regs;
let mut set = |a: u32, b: u32, c: u32, d: u32| {
regs.set_dword(0, a);
regs.set_dword(3, b);
regs.set_dword(1, c);
regs.set_dword(2, d);
};
match leaf {
0 => set(
max_basic,
u32::from_le_bytes(*b"Genu"),
u32::from_le_bytes(*b"ntel"),
u32::from_le_bytes(*b"ineI"),
),
1 => set(signature, 0, 0, edx1),
0x8000_0000 => set(if features.long { 0x8000_0008 } else { 0 }, 0, 0, 0),
0x8000_0001 if features.long => {
let mut edx: u32 = 1 << 29; if features.nx {
edx |= 1 << 20; }
if features.syscall {
edx |= 1 << 11; }
set(signature, 0, 0, edx);
}
0x8000_0008 if features.long => {
set(0x0000_3028, 0, 0, 0);
}
_ => set(0, 0, 0, 0),
}
Ok(())
}
}