use alloc::vec::Vec;
use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::isa::{self, Arg, Fields, Op, Rep, seg};
use super::paging::Tlb;
use super::prot::{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;
#[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 tlb: Tlb,
pub cycles: u64,
pub halted: bool,
pub shutdown: bool,
pub reset_pending: bool,
pub int_shadow: bool,
pub queue: Queue,
pub open_bus: u8,
pub faults: u64,
pub last_fault: u32,
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.eip = 0xfff0;
regs.eflags = flags::ALWAYS_SET;
regs.edx = variant.reset_signature();
(regs, Sys::reset())
} else {
(Regs::new(), Sys::reset_8086())
};
State {
regs,
sys,
tlb: Tlb::new(),
cycles: 0,
halted: false,
shutdown: false,
reset_pending: true,
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, u32)>,
pub(super) entry: Regs,
pub(super) start_ip: u32,
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
}
}
pub(super) fn step(&mut self) -> u64 {
if self.state.reset_pending {
self.reset_sequence();
return self.used;
}
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: u32, size: u8) -> u32 {
self.charge(self.variant().bus_clocks());
let width = match size {
1 => Width::U8,
2 => Width::U16,
_ => Width::U32,
};
let addr = addr & self.lines.a20_mask();
match self.mem.read(u64::from(addr), width, self.attrs) {
Ok(value) => {
self.state.open_bus = (value >> ((size as u32 - 1) * 8)) as u8;
value as u32
}
Err(_) => {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
let byte = u32::from(self.state.open_bus);
match size {
1 => byte,
2 => byte | (byte << 8),
_ => byte | (byte << 8) | (byte << 16) | (byte << 24),
}
}
}
}
pub(super) fn phys_write(&mut self, addr: u32, size: u8, value: u32) {
self.charge(self.variant().bus_clocks());
self.state.open_bus = (value >> ((size as u32 - 1) * 8)) as u8;
let width = match size {
1 => Width::U8,
2 => Width::U16,
_ => Width::U32,
};
let addr = addr & self.lines.a20_mask();
if self
.mem
.write(u64::from(addr), width, u64::from(value), self.attrs)
.is_err()
{
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
}
}
fn word_is_one_cycle(&self, base: u32, 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, u32::from(value));
return;
}
self.phys_write(base, 1, u32::from(value & 0xff));
self.phys_write(
linear(segment, offset.wrapping_add(1)),
1,
u32::from(value >> 8),
);
}
pub(super) fn read_mem(&mut self, sr: u8, offset: u32, size: u8) -> Ex<u32> {
if self.legacy() {
let segment = self.state.regs.segment(sr);
return Ok(match size {
1 => u32::from(self.phys_read(linear(segment, offset as u16), 1) as u8),
_ => u32::from(self.legacy_read16_seg(segment, offset as u16)),
});
}
let lin = self.seg_linear(sr, offset, u32::from(size), false)?;
self.linear_read(lin, size)
}
pub(super) fn write_mem(&mut self, sr: u8, offset: u32, size: u8, value: u32) -> 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, u32::from(size), true)?;
self.linear_write(lin, size, value)
}
pub(super) fn linear_read(&mut self, lin: u32, size: u8) -> Ex<u32> {
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 = 0u32;
for i in 0..u32::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: u32, size: u8, value: u32) -> 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 = [0u32; 4];
for i in 0..u32::from(size) {
phys[i as usize] = self.translate(lin.wrapping_add(i), true, user)?;
}
for i in 0..u32::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: u32, size: u8) -> bool {
(lin & 0xfff) + u32::from(size) > 0x1000
}
pub(super) fn sys_read32(&mut self, lin: u32) -> Ex<u32> {
if !self.state.sys.paging() {
return Ok(self.phys_read(lin, 4));
}
if Self::crosses_page(lin, 4) {
let mut value = 0u32;
for i in 0..4u32 {
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, 4))
}
pub(super) fn sys_write32(&mut self, lin: u32, value: u32) -> Ex<()> {
if !self.state.sys.paging() {
self.phys_write(lin, 4, value);
return Ok(());
}
if Self::crosses_page(lin, 4) {
let mut phys = [0u32; 4];
for i in 0..4u32 {
phys[i as usize] = self.translate(lin.wrapping_add(i), true, false)?;
}
for i in 0..4u32 {
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, 4, value);
Ok(())
}
pub(super) fn sys_read16(&mut self, lin: u32) -> Ex<u32> {
if !self.state.sys.paging() {
return Ok(self.phys_read(lin, 2));
}
if Self::crosses_page(lin, 2) {
let lo = {
let phys = self.translate(lin, false, false)?;
self.phys_read(phys, 1)
};
let phys = self.translate(lin.wrapping_add(1), false, false)?;
let hi = self.phys_read(phys, 1);
return Ok(lo | (hi << 8));
}
let phys = self.translate(lin, false, false)?;
Ok(self.phys_read(phys, 2))
}
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 = u32::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 = u32::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(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: u32) -> 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);
if !cs.in_bounds(offset, 1) {
return Err(Fault::gp(0));
}
let lin = 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(lin, false, 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
.eip
.wrapping_add(u32::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.eip;
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.eip = if self.legacy() {
(self.state.regs.eip & 0xffff_0000)
| u32::from(self.state.regs.eip.wrapping_add(1) as u16)
} else {
self.state.regs.eip.wrapping_add(1)
};
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]
const fn msb(size: u8) -> u32 {
1u32 << (size as u32 * 8 - 1)
}
#[inline]
const fn mask(size: u8) -> u32 {
match size {
1 => 0xff,
2 => 0xffff,
_ => 0xffff_ffff,
}
}
fn set_szp(&mut self, value: u32, 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: u32, b: u32, carry: bool, size: u8) -> u32 {
let mask = Self::mask(size);
let a = a & mask;
let b = b & mask;
let sum = u64::from(a) + u64::from(b) + u64::from(carry);
let r = (sum as u32) & mask;
self.set_flag(flags::CF, sum > u64::from(mask));
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: u32, b: u32, borrow: bool, size: u8) -> u32 {
let mask = Self::mask(size);
let a = a & mask;
let b = b & mask;
let rhs = u64::from(b) + u64::from(borrow);
let diff = u64::from(a).wrapping_sub(rhs);
let r = (diff as u32) & mask;
self.set_flag(flags::CF, u64::from(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: u32, 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;
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.eip = 0xfff0;
regs.ds = 0;
regs.es = 0;
regs.ss = 0;
regs.fs = 0;
regs.gs = 0;
regs.eflags = flags::ALWAYS_SET;
regs.edx = variant.reset_signature();
let _ = keep;
} else {
self.state.sys = Sys::reset_8086();
let regs = &mut self.state.regs;
regs.cs = 0xffff;
regs.eip = 0;
regs.ds = 0;
regs.es = 0;
regs.ss = 0;
regs.eflags = flags::RESERVED_SET;
}
self.state.queue.flush();
self.charge(RESET_CLOCKS);
}
pub(super) fn stack_addr_size(&self) -> u8 {
if self.legacy() {
2
} else if self.state.sys.seg(seg::SS).big() {
4
} else {
2
}
}
pub(super) fn sp(&self) -> u32 {
if self.stack_addr_size() == 2 {
self.state.regs.esp & 0xffff
} else {
self.state.regs.esp
}
}
pub(super) fn set_sp(&mut self, value: u32) {
if self.stack_addr_size() == 2 {
self.state.regs.esp = (self.state.regs.esp & 0xffff_0000) | (value & 0xffff);
} else {
self.state.regs.esp = value;
}
}
pub(super) fn push(&mut self, value: u32, size: u8) -> Ex<()> {
let sp = self.sp().wrapping_sub(u32::from(size));
let sp = if self.stack_addr_size() == 2 {
sp & 0xffff
} else {
sp
};
self.set_sp(sp);
self.write_mem(seg::SS, sp, size, value)
}
pub(super) fn pop(&mut self, size: u8) -> Ex<u32> {
let sp = self.sp();
let value = self.read_mem(seg::SS, sp, size)?;
let next = sp.wrapping_add(u32::from(size));
self.set_sp(if self.stack_addr_size() == 2 {
next & 0xffff
} else {
next
});
Ok(value)
}
fn instruction(&mut self) -> Ex<()> {
self.entry = self.state.regs;
self.start_ip = self.state.regs.eip;
self.fill_queue()?;
let map = self.variant().map();
let default32 = !self.legacy() && self.state.sys.seg(seg::CS).big();
let mut fetch_fault: Option<Fault> = None;
let fields = {
let this = &mut *self;
isa::decode_stream_as(map, default32, &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 wants_memory = [insn.dst, insn.src, insn.aux]
.iter()
.any(|a| matches!(a, Arg::Eb | Arg::Ev | Arg::Ew | Arg::M | Arg::Mp | Arg::Ms));
if let Some(m) = f.modrm
&& !m.is_register()
&& wants_memory
{
let offset = if 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)
};
u32::from(value)
} else {
self.ea32(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 = if f.addrsize == 2 {
f.imm & 0xffff
} else {
f.imm
};
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(sib.base));
}
if sib.has_index() {
value = value.wrapping_add(regs.dword(sib.index) << sib.scale);
}
} else if !(m.rm == 5 && m.md == 0) {
value = value.wrapping_add(regs.dword(m.rm));
}
value.wrapping_add(f.disp as u32)
}
pub(super) fn ea(&self) -> (u8, u32) {
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<u32> {
let regs = self.state.regs;
let value = match arg {
Arg::Eb | Arg::Ev | Arg::Ew => match f.modrm {
Some(m) if m.is_register() => regs.read(m.rm, size),
_ => {
let (sr, off) = self.ea();
self.read_mem(sr, off, size)?
}
},
Arg::Gb | Arg::Gv | Arg::Gw => regs.read(f.modrm.map_or(0, |m| m.reg), size),
Arg::Rd => regs.dword(f.modrm.map_or(0, |m| m.rm)),
Arg::Cd => self.read_control(f.modrm.map_or(0, |m| m.reg))?,
Arg::Dd => self.read_debug(f.modrm.map_or(0, |m| m.reg))?,
Arg::Td => self.read_test(f.modrm.map_or(0, |m| m.reg))?,
Arg::Sw => {
let index = f.modrm.map_or(0, |m| m.reg);
let index = if self.legacy() { index & 3 } else { index };
u32::from(regs.segment(index))
}
Arg::Sr => u32::from(regs.segment((f.opcode >> 3) & 7)),
Arg::Ib | Arg::Iw | Arg::Iv | Arg::Ibs => f.imm & Self::mask(size),
Arg::Rb | Arg::Rv => regs.read(f.opcode & 7, size),
Arg::Al => u32::from(regs.byte(0)),
Arg::Ax => regs.read(0, size),
Arg::Cl => u32::from(regs.byte(1)),
Arg::Dx => regs.read(2, 2),
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))
}
pub(super) fn write_arg(&mut self, f: &Fields, arg: Arg, size: u8, value: u32) -> Ex<()> {
match arg {
Arg::Eb | Arg::Ev | Arg::Ew => match f.modrm {
Some(m) if m.is_register() => self.state.regs.write(m.rm, size, value),
_ => {
let (sr, off) = self.ea();
self.write_mem(sr, off, size, value)?;
}
},
Arg::Gb | Arg::Gv | Arg::Gw => {
self.state
.regs
.write(f.modrm.map_or(0, |m| m.reg), size, value);
}
Arg::Rd => self
.state
.regs
.set_dword(f.modrm.map_or(0, |m| m.rm), value),
Arg::Cd => self.write_control(f.modrm.map_or(0, |m| m.reg), value)?,
Arg::Dd => self.write_debug(f.modrm.map_or(0, |m| m.reg), value)?,
Arg::Td => self.write_test(f.modrm.map_or(0, |m| m.reg), 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 & 7, size, value),
Arg::Al => self.state.regs.set_byte(0, value as u8),
Arg::Ax => self.state.regs.write(0, size, value),
Arg::Cl => self.state.regs.set_byte(1, value as u8),
Arg::Dx => self.state.regs.write(2, 2, 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);
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_extend32(raw, src_size)
};
self.write_arg(f, insn.dst, f.opsize, 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);
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, dst);
self.write_arg(f, insn.dst, size, dst)?;
}
}
Op::BSWAP => {
let index = f.opcode & 7;
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 = if f.addrsize == 2 {
offset & 0xffff
} else {
offset
};
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 = if self.stack_addr_size() == 2 {
self.state.regs.word(5) as u32
} else {
self.state.regs.ebp
};
self.set_sp(bp);
let value = self.pop(f.opsize)?;
self.state.regs.write(5, f.opsize, value);
}
Op::PUSHF => {
let value = self.state.regs.eflags & !(flags::VM | flags::RF);
self.push(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 => {
if f.opsize == 2 {
let al = self.state.regs.byte(0);
self.state.regs.set_word(0, i16::from(al as i8) as u16);
} else {
let ax = self.state.regs.word(0);
self.state.regs.set_dword(0, i32::from(ax as i16) as u32);
}
}
Op::CWD => {
if f.opsize == 2 {
let fill = if self.state.regs.word(0) & 0x8000 != 0 {
0xffff
} else {
0
};
self.state.regs.set_word(2, fill);
} else {
let fill = if self.state.regs.eax & 0x8000_0000 != 0 {
0xffff_ffff
} else {
0
};
self.state.regs.set_dword(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 | Op::WAIT | Op::LOCK | Op::REP | Op::REPNE | Op::SEG => {}
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 = u32::from(self.state.regs.byte(0));
let base = if f.addrsize == 2 {
u32::from(self.state.regs.word(3).wrapping_add(al as u16))
} else {
self.state.regs.ebx.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 { f.opsize };
self.io_permitted(port, width)?;
let value = self.io_read_sized(port, width);
self.state.regs.write(0, width, value);
}
Op::OUT => {
let port = self.port(f, insn.dst);
let width = if insn.src == Arg::Al { 1 } else { f.opsize };
self.io_permitted(port, width)?;
let value = self.state.regs.read(0, width);
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 insn.dst == Arg::Iw || insn.dst == Arg::Iv {
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 insn.dst == Arg::Iw || insn.dst == Arg::Iv {
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 index = if insn.op == Op::BSF {
src.trailing_zeros()
} else {
31 - src.leading_zeros()
};
self.write_arg(f, insn.dst, size, 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 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, u32::from(selector))?;
}
Op::SMSW => {
let value = 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 = u32::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<(u32, 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(u32::from(f.opsize)), 2)?;
Ok((offset, selector as u16))
}
fn relative_target(&self, f: &Fields) -> u32 {
let next = self.state.regs.eip;
let target = next.wrapping_add(f.imm);
if f.opsize == 2 {
target & 0xffff
} else {
target
}
}
fn counter(&self, f: &Fields) -> u32 {
if f.addrsize == 2 {
u32::from(self.state.regs.word(1))
} else {
self.state.regs.ecx
}
}
fn set_counter(&mut self, f: &Fields, value: u32) {
if f.addrsize == 2 {
self.state.regs.set_word(1, value as u16);
} else {
self.state.regs.ecx = 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(u32::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)
};
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, 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);
self.push(bp, size)?;
let frame_ptr = self.sp();
for _ in 1..level {
let bp = self.state.regs.read(5, size);
let bp = bp.wrapping_sub(u32::from(size));
self.state.regs.write(5, size, 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, 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)?;
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(u32::from(size)), size)?;
let index = self.read_arg(f, f.insn.dst, size)? as i32;
let lower = Self::sign_extend32(lower, size) as i32;
let upper = Self::sign_extend32(upper, size) as i32;
let index = if size == 2 {
index as i16 as i32
} else {
index
};
if index < lower || index > upper {
return Err(Fault::bare(VEC_BOUND));
}
Ok(())
}
const fn sign_extend32(value: u32, size: u8) -> u32 {
match size {
1 => ((value as u8) as i8) as u32,
2 => ((value as u16) as i16) as u32,
_ => value,
}
}
fn bit_test(&mut self, f: &Fields, size: u8) -> Ex<()> {
let insn = f.insn;
let bits = u32::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, 0i32)
} else {
let signed = Self::sign_extend32(raw, size) as i32;
let word = signed.div_euclid(bits as i32);
let bit = signed.rem_euclid(bits as i32) as u32;
(bit, word * i32::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 u32);
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 u32);
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 => u32::from(self.state.regs.byte(1)),
_ => f.imm & 0xff,
};
let count = if self.legacy() {
raw 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: u32, count: u8, size: u8) -> u32 {
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) | u32::from(cf)) & mask;
of = (v & msb != 0) != cf;
}
Op::ROR => {
cf = v & 1 != 0;
v = ((v >> 1) | (u32::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) | u32::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) | (u32::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;
v = ((v | if v & msb != 0 { !mask } else { 0 }) as i32 >> 1) as u32 & 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 => u32::from(self.state.regs.byte(1)),
_ => f.imm & 0xff,
};
let count = (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: u32, 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);
let mask = Self::mask(size);
let bits = u32::from(size) * 8;
let product: u64 = if signed {
let a = i64::from(Self::sign_extend32(acc, size) as i32);
let b = i64::from(Self::sign_extend32(src, size) as i32);
(a.wrapping_mul(b)) as u64
} else {
u64::from(acc & mask) * u64::from(src & mask)
};
let low = (product as u32) & mask;
let high = ((product >> bits) as u32) & mask;
if size == 1 {
self.state.regs.set_word(0, (low | (high << 8)) as u16);
} else {
self.state.regs.write(0, size, low);
self.state.regs.write(2, size, 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_extend32(raw, 1)
} else {
raw
}
};
let a = i64::from(Self::sign_extend32(a, size) as i32);
let b = i64::from(Self::sign_extend32(b, size) as i32);
let product = a.wrapping_mul(b);
let truncated = (product as u32) & Self::mask(size);
let fits = i64::from(Self::sign_extend32(truncated, size) as i32) == product;
self.set_flag(flags::CF | flags::OF, !fits);
let high = ((product as u64) >> (u32::from(size) * 8)) as u32 & 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: u64 = if size == 1 {
u64::from(self.state.regs.word(0))
} else {
let high = u64::from(self.state.regs.read(2, size));
let low = u64::from(self.state.regs.read(0, size));
(high << bits) | low
};
let (magnitude, divisor_magnitude) = if signed {
(
Self::sign_extend_wide(dividend, bits * 2).unsigned_abs(),
i64::from(Self::sign_extend32(source, size) as i32).unsigned_abs(),
)
} else {
(dividend, u64::from(source))
};
let (quotient_magnitude, remainder_magnitude) =
self.cord(magnitude, divisor_magnitude, bits);
let mask = u64::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 = i64::from(Self::sign_extend32(source, size) as i32);
match (n.checked_div(d), n.checked_rem(d)) {
(Some(mut q), Some(r)) => {
if negate {
q = q.wrapping_neg();
}
let limit = 1i64 << (bits - 1);
(
(q as u64) & mask,
(r as u64) & 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, quotient as u32);
self.state.regs.write(2, size, remainder as u32);
}
Ok(())
}
const fn sign_extend_wide(value: u64, bits: u32) -> i64 {
let shift = 64 - bits;
((value as i64) << shift) >> shift
}
fn cord(&mut self, dividend: u64, divisor: u64, bits: u32) -> (u64, u64) {
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let top = 1u64 << (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(u32::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(u32::from(product), u32::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 = u32::from(if low { 0x06u8 } else { 0x00 })
+ u32::from(if high { 0x60u8 } else { 0x00 });
let adjusted = if subtract {
self.sub(u32::from(al), correction, false, 1)
} else {
self.add(u32::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 = u32::from(if adjust { 6u8 } else { 0 });
let adjusted = if subtract {
self.sub(u32::from(al), operand, false, 1)
} else {
self.add(u32::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) {
u32::from(size).wrapping_neg()
} else {
u32::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.eip = self.start_ip;
self.state.queue.flush();
return Ok(());
}
}
Ok(())
}
fn si(&self, f: &Fields) -> u32 {
if f.addrsize == 2 {
u32::from(self.state.regs.word(6))
} else {
self.state.regs.esi
}
}
fn di(&self, f: &Fields) -> u32 {
if f.addrsize == 2 {
u32::from(self.state.regs.word(7))
} else {
self.state.regs.edi
}
}
fn string_step(&mut self, f: &Fields, size: u8, delta: u32) -> 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);
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, 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);
self.io_permitted(port, size)?;
let value = self.io_read_sized(port, size);
self.write_mem(seg::ES, di, size, value)?;
self.advance(f, delta, false, true);
}
_ => {
let port = self.state.regs.word(2);
self.io_permitted(port, size)?;
let value = self.read_mem(src_seg, si, size)?;
self.io_write_sized(port, size, value);
self.advance(f, delta, true, false);
}
}
Ok(())
}
fn advance(&mut self, f: &Fields, delta: u32, si: bool, di: bool) {
if f.addrsize == 2 {
if si {
let v = self.state.regs.word(6).wrapping_add(delta as u16);
self.state.regs.set_word(6, v);
}
if di {
let v = self.state.regs.word(7).wrapping_add(delta as u16);
self.state.regs.set_word(7, v);
}
} else {
if si {
self.state.regs.esi = self.state.regs.esi.wrapping_add(delta);
}
if di {
self.state.regs.edi = self.state.regs.edi.wrapping_add(delta);
}
}
}
fn jump_near(&mut self, target: u32, opsize: u8) -> Ex<()> {
let target = if opsize == 2 { target & 0xffff } else { target };
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.eip = 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.eip;
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(u32::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<()> {
if !self.variant().has_486_extras() {
return Err(Fault::bare(VEC_UD));
}
let leaf = self.state.regs.eax;
let regs = &mut self.state.regs;
match leaf {
0 => {
regs.eax = 1;
regs.ebx = u32::from_le_bytes(*b"Genu");
regs.edx = u32::from_le_bytes(*b"ineI");
regs.ecx = u32::from_le_bytes(*b"ntel");
}
_ => {
regs.eax = self.cfg.variant.reset_signature();
regs.ebx = 0;
regs.ecx = 0;
regs.edx = 0;
}
}
Ok(())
}
}