Files
8086-rs/src/interpreter/computer.rs

219 lines
8.5 KiB
Rust

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 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(),
}
}
/// Perform binary `dest` = `dest` + `src`. Sets flags.
pub fn add(&mut self, dest: ModRmTarget, src: Operand) {
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);
}
/// Perform binary `dest` = `dest` - `src`. Sets flags.
pub fn sub(&mut self, dest: ModRmTarget, src: Operand) {
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: Operand) {
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: Operand) {
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: Operand) {
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: Operand) {
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: Operand) {
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: Operand) {
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 {
Operand::Immediate(immediate_operand) => Operand::Immediate(immediate_operand + cf),
Operand::ModRmTarget(mod_rm_target) => Operand::Immediate(match mod_rm_target {
ModRmTarget::Memory(idx) => self.memory.read(&self.regs, idx) + cf,
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: Operand) {
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 {
Operand::Immediate(immediate_operand) => Operand::Immediate(immediate_operand + cf),
Operand::ModRmTarget(mod_rm_target) => Operand::Immediate(match mod_rm_target {
ModRmTarget::Memory(idx) => self.memory.read(&self.regs, idx) + cf,
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: Operand)
where
O: Fn(Lhs, Rhs) -> ArithmeticResult,
F: Fn(&mut Flags, ArithmeticResult, Lhs, Rhs),
{
let lhs = self.read_modrm(dest);
let rhs = match src {
Operand::Immediate(imm) => imm,
Operand::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),
ModRmTarget::Register(reg) => self.regs.read(reg),
}
}
}
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::*;
}