300 lines
11 KiB
Rust
300 lines
11 KiB
Rust
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<Word, InterpreterError> {
|
|
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<O, F>(
|
|
&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())
|
|
}
|
|
}
|