148 lines
5.2 KiB
Rust
148 lines
5.2 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 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<O, F>(&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<O, F>(&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<O, F>(&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::*;
|
|
}
|