use core::fmt; use crate::operands::{ImmediateOperand, ModRmTarget}; use super::{flags::Flags, memory::Memory, register::Register}; /// Wrapper for easier argument passing of multi-type operations. #[derive(Debug, Clone)] pub enum Operand { Immediate(crate::operands::ImmediateOperand), ModRmTarget(ModRmTarget), } type Result = 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(), } } /// Perform binary `dest` = `dest` + `src`. Sets flags. pub fn add(&mut self, dest: ModRmTarget, src: Operand) { let op: fn(Lhs, Rhs) -> Result = |lhs, rhs| lhs + rhs; let flag_set: fn(&mut Flags, Result, 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(); // flags.af = }; self.op(op, flag_set, dest, src); } /// Perform binary `dest` = `dest` - `src`. Sets flags. pub fn sub(&mut self, dest: ModRmTarget, src: Operand) { let op: fn(Lhs, Rhs) -> Result = |lhs, rhs| lhs - rhs; let flag_set: fn(&mut Flags, Result, Lhs, Rhs) = |_, _, _, _| (); self.op(op, flag_set, dest, src); } /// Applies a binary operator [`O`] to the value of two [`Operand`]s, saves /// it to `dest` 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, dest: ModRmTarget, src: Operand) where O: Fn(Lhs, Rhs) -> Result, F: Fn(&mut Flags, Result, Lhs, Rhs), { match src { Operand::ModRmTarget(src_target) => self.op_modrm(op, flag_set, dest, src_target), Operand::Immediate(src_operand) => self.op_imm(op, flag_set, dest, src_operand), } } /// Applies a binary operator [`O`] to two memory locations, pointed to by a /// [`ModRmTargets`]s, saves it to dest and sets flags, according to [`F`]. fn op_modrm(&mut self, op: O, flag_set: F, dest: ModRmTarget, src: ModRmTarget) where O: Fn(Lhs, Rhs) -> Result, F: Fn(&mut Flags, Result, Lhs, Rhs), { match dest { ModRmTarget::Memory(dest_memory_idx) => match src { // mem, mem ModRmTarget::Memory(_) => { panic!("Cannot apply binary operator from Memory to Memory!") } // mem, reg ModRmTarget::Register(src_register) => { let lhs = self.memory.read(&self.regs, dest_memory_idx); let rhs = self.regs.read(src_register); let result = op(lhs, rhs); self.memory.write(&self.regs, dest_memory_idx, result); flag_set(&mut self.flags, result, lhs, rhs); } }, ModRmTarget::Register(dest_register) => match src { // reg, mem ModRmTarget::Memory(src_memory_index) => { let lhs = self.regs.read(dest_register); let rhs = self.memory.read(&self.regs, src_memory_index); let result = op(lhs, rhs); self.regs.write(dest_register, result); flag_set(&mut self.flags, result, lhs, rhs); } // reg, reg ModRmTarget::Register(src_register) => { let lhs = self.regs.read(dest_register); let rhs = self.regs.read(src_register); let result = op(lhs, rhs); self.regs.write(dest_register, result); flag_set(&mut self.flags, result, lhs, rhs); } }, } } /// Applies a binary operator [`O`] to [`ImmediateOperand`] and a location /// pointed to by a [`ModRmTarget`], saves it to dest and sets flags, /// according to [`F`]. fn op_imm(&mut self, op: O, flag_set: F, dest: ModRmTarget, src: ImmediateOperand) where O: Fn(Lhs, Rhs) -> Result, F: Fn(&mut Flags, Result, Lhs, Rhs), { match dest { ModRmTarget::Memory(dest_mem) => { let lhs = self.memory.read(&self.regs, dest_mem); let result = op(lhs, src); self.memory.write(&self.regs, dest_mem, result); flag_set(&mut self.flags, result, lhs, src); } ModRmTarget::Register(dest_reg) => { let lhs = self.regs.read(dest_reg); let result = op(lhs, src); self.regs.write(dest_reg, result); flag_set(&mut self.flags, result, lhs, src); } } } } 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::*; }