use core::fmt; use crate::operands::{ImmediateOperand, ModRmTarget, Word}; use super::{flags::Flags, interpreter::InterpreterError, memory::Memory, register::Register}; /// Wrapper for easier argument passing of polymorph arithmetic operations. #[derive(Debug, Clone)] pub enum ArithmeticOperand { Immediate(crate::operands::ImmediateOperand), ModRmTarget(ModRmTarget), } type ArithmeticResult = ImmediateOperand; type Lhs = ImmediateOperand; type Rhs = ImmediateOperand; #[derive(Debug, Clone)] pub struct Computer { pub regs: Register, pub flags: Flags, pub memory: Memory, } impl Computer { pub fn new() -> Self { Self { regs: Register::new(), flags: Flags::new(), memory: Memory::new(), } } /// Decrement stack pointer and write `val` onto the stack. pub fn push_stack(&mut self, val: ImmediateOperand) -> Result<(), InterpreterError> { self.regs.push()?; self.memory.write_raw(self.regs.sp, val.into()) } /// Retrieve value from stack and increment stack pointer. pub fn pop_stack(&mut self) -> Result { let word = self.memory.read_raw(self.regs.sp)?; self.regs.pop()?; Ok(word) } /// Perform binary `dest` = `dest` + `src`. Sets flags. pub fn add(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs + rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, lhs, rhs| { flags.cf = result < rhs; flags.of = (lhs.msb() && rhs.msb()) && (result.msb() != lhs.msb()); flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src); } /// Perform binary `dest` = `dest` - `src`. Sets flags. pub fn sub(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs - rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, lhs, rhs| { flags.cf = lhs < rhs; flags.of = lhs.msb() != rhs.msb() && lhs.msb() != result.msb(); flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src); } /// Perform binary `dest` = `dest` | `src`. Sets flags. pub fn or(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs | rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, _, _| { flags.cf = false; flags.of = false; flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src); } /// Perform binary `dest` = `dest` & `src`. Sets flags. pub fn and(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs & rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, _, _rhs| { flags.cf = false; flags.of = false; flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src); } /// Perform binary `dest` = `dest` ^ `src`. Sets flags. pub fn xor(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs ^ rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, _, _| { flags.cf = false; flags.of = false; flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src); } /// Perform compare operation, which acts like [`Self::sub()`], but without /// saving the result and only setting [`Self::flags`]. pub fn cmp(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs - rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, lhs, rhs| { flags.cf = lhs < rhs; flags.of = lhs.msb() != rhs.msb() && lhs.msb() != result.msb(); flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, false, dest, src); } /// Perform test operation, which acts like [`Self::and()`], but without /// saving the result and only setting [`Self::flags`]. pub fn test(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs & rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, _, _| { flags.cf = false; flags.of = false; flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, false, dest, src); } /// Perform `dest` = `dest` + `src` + CF. Sets flags. pub fn adc(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let cf = self.flags.cf as u8; // cheating, by flattening into immediates, but this allows to easily add the opt. carry let src_with_carry = match src { ArithmeticOperand::Immediate(immediate_operand) => { ArithmeticOperand::Immediate(immediate_operand + cf) } ArithmeticOperand::ModRmTarget(mod_rm_target) => { ArithmeticOperand::Immediate(match mod_rm_target { ModRmTarget::Memory(idx) => { (self.memory.read(&self.regs, idx) + cf as u16).into() } ModRmTarget::Register(reg) => self.regs.read(reg) + cf, }) } }; let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs + rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, lhs, rhs| { flags.cf = result < rhs; flags.of = lhs.msb() && rhs.msb() != result.msb(); flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src_with_carry); } /// Perform `dest` = `dest` - (`src` + CF). Sets flags. pub fn sbb(&mut self, dest: ModRmTarget, src: ArithmeticOperand) { let cf = self.flags.cf as u8; // cheating, by flattening into immediates, but this allows to easily add the opt. carry let src_with_carry = match src { ArithmeticOperand::Immediate(immediate_operand) => { ArithmeticOperand::Immediate(immediate_operand + cf) } ArithmeticOperand::ModRmTarget(mod_rm_target) => { ArithmeticOperand::Immediate(match mod_rm_target { ModRmTarget::Memory(idx) => { (self.memory.read(&self.regs, idx) + cf as u16).into() } ModRmTarget::Register(reg) => self.regs.read(reg) + cf, }) } }; let op: fn(Lhs, Rhs) -> ArithmeticResult = |lhs, rhs| lhs - rhs; let flag_set: fn(&mut Flags, ArithmeticResult, Lhs, Rhs) = |flags, result, lhs, rhs| { flags.cf = result < rhs; flags.of = lhs.msb() && rhs.msb() != result.msb(); flags.zf = result.zero(); flags.sf = result.msb(); flags.pf = result.parity(); }; self.op(op, flag_set, true, dest, src_with_carry); } /// Applies a binary operator [`O`] to the value of two [`Operand`]s, saves /// it to `dest`, if `write` is set, and sets flags, according to [`F`]. /// A result can never be saved to an immediate Operand, so `dest` can only /// be a [`ModRmTarget`]. fn op( &mut self, op: O, flag_set: F, write: bool, dest: ModRmTarget, src: ArithmeticOperand, ) where O: Fn(Lhs, Rhs) -> ArithmeticResult, F: Fn(&mut Flags, ArithmeticResult, Lhs, Rhs), { let lhs = self.read_modrm(dest); let rhs = match src { ArithmeticOperand::Immediate(imm) => imm, ArithmeticOperand::ModRmTarget(target) => self.read_modrm(target), }; let result = op(lhs, rhs); if write { self.write_modrm(dest, result); } flag_set(&mut self.flags, result, lhs, rhs); } /// Write an [`ImmediateOperand`] into [`Self::memory`] or [`Self::regs`]. pub fn write_modrm(&mut self, target: ModRmTarget, val: ImmediateOperand) { match target { ModRmTarget::Memory(idx) => self.memory.write(&self.regs, idx, val), ModRmTarget::Register(reg) => self.regs.write(reg, val), }; } /// Read an [`ImmediateOperand`] from [`Self::memory`] or [`Self::regs`]. pub fn read_modrm(&self, target: ModRmTarget) -> ImmediateOperand { match target { ModRmTarget::Memory(idx) => self.memory.read(&self.regs, idx).into(), ModRmTarget::Register(reg) => self.regs.read(reg), } } pub fn rotate( &mut self, target: ModRmTarget, rotations: usize, // how many rotations carry_usage: CarryUsage, // if carry should be included, or should just receive a copy rotation_direction: RotationDirection, // direction of rotation ) { let mut bits = self.read_modrm(target).bits(); match carry_usage { CarryUsage::FullRotation => bits.push(self.flags.cf), _ => {} } match rotation_direction { RotationDirection::Left => { bits.rotate_left(rotations); match carry_usage { CarryUsage::ReceiveCopy => self.flags.cf = bits[7], CarryUsage::FullRotation => self.flags.cf = bits.pop().unwrap(), } } RotationDirection::Right => { bits.rotate_right(rotations); match carry_usage { CarryUsage::ReceiveCopy => self.flags.cf = bits[0], CarryUsage::FullRotation => self.flags.cf = bits.pop().unwrap(), } } } self.write_modrm(target, ImmediateOperand::from(bits)); } } pub enum RotationDirection { Left, Right, } pub enum CarryUsage { ReceiveCopy, // dont add when rotating, but copy bit that was rotated to other side FullRotation, // include in full rotation } impl fmt::Display for Computer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} {}", self.regs, self.flags) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_push() { let mut c = Computer::new(); let val = ImmediateOperand::Word(0x1234); c.push_stack(val).unwrap(); assert_eq!(val, c.pop_stack().unwrap().into()) } }