ft: abstract and implement ADD::* interpretation
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
use core::fmt;
|
||||
use std::ffi::{c_uchar, c_ushort};
|
||||
|
||||
use crate::operands::{Byte, Word};
|
||||
use crate::operands::Byte;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type c_long = i32; // we use a a.out with 32 byte
|
||||
|
||||
@@ -45,7 +45,7 @@ impl fmt::Display for Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
#[allow(non_camel_case_types)]
|
||||
/// All possible mnemonic variantions.
|
||||
/// These are sorted by type and are not in hex-encoding order.
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::operands::Byte;
|
||||
use crate::operands::{ModRmTarget, Operand};
|
||||
|
||||
use super::{flags::Flags, register::Register};
|
||||
use super::{flags::Flags, memory::Memory, register::Register};
|
||||
|
||||
/// Wrapper for easier argument passing of arithmetic operations.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Argument {
|
||||
Operand(crate::operands::Operand),
|
||||
ModRmTarget(ModRmTarget),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Computer {
|
||||
pub regs: Register,
|
||||
pub flags: Flags,
|
||||
pub memory: [Byte; 65536],
|
||||
pub memory: Memory,
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
@@ -16,7 +23,79 @@ impl Computer {
|
||||
Self {
|
||||
regs: Register::new(),
|
||||
flags: Flags::new(),
|
||||
memory: [0; 65536],
|
||||
memory: Memory::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds two [`Argument`]s, saves it to `dest` and sets flags, if necessary.
|
||||
/// A value can never be saved to an immediate Operand, so `dest` can only
|
||||
/// be a [`ModRmTarget`].
|
||||
pub fn add(&mut self, dest: ModRmTarget, src: Argument) {
|
||||
match src {
|
||||
Argument::ModRmTarget(src_target) => self.add_modrm(dest, src_target),
|
||||
Argument::Operand(src_operand) => self.add_imm(dest, src_operand),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds two [`ModRmTarget`]s togeher, saves it to `dest` and sets flags, if necessary.
|
||||
fn add_modrm(&mut self, dest: ModRmTarget, src: ModRmTarget) {
|
||||
match dest {
|
||||
ModRmTarget::Memory(dest_memory_idx) => match src {
|
||||
// mem, mem
|
||||
ModRmTarget::Memory(_) => panic!("Cannot add Memory to Memory!"),
|
||||
// mem, reg
|
||||
ModRmTarget::Register(src_register) => {
|
||||
let lhs = self.memory.read_from_memidx(&self.regs, dest_memory_idx);
|
||||
let rhs = self.regs.read(src_register);
|
||||
let result = lhs + rhs;
|
||||
self.memory
|
||||
.write_from_memidx(&self.regs, dest_memory_idx, result);
|
||||
// unsigned overflow
|
||||
self.flags.of = result < 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_from_memidx(&self.regs, src_memory_index);
|
||||
let result = lhs + rhs;
|
||||
self.regs.write(dest_register, result);
|
||||
// unsigned overflow
|
||||
self.flags.of = result < rhs;
|
||||
}
|
||||
// reg, reg
|
||||
ModRmTarget::Register(src_register) => {
|
||||
let lhs = self.regs.read(dest_register);
|
||||
let rhs = self.regs.read(src_register);
|
||||
let result = lhs + rhs;
|
||||
self.regs.write(dest_register, result);
|
||||
|
||||
// unsigned overflow
|
||||
self.flags.of = result < rhs
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an immediate [`Operand`] to a location pointed to by a
|
||||
/// [`ModRmTarget`], saves it to dest and sets flags, if necessary.
|
||||
fn add_imm(&mut self, dest: ModRmTarget, src: Operand) {
|
||||
match dest {
|
||||
ModRmTarget::Memory(dest_mem) => {
|
||||
let lhs = self.memory.read_from_memidx(&self.regs, dest_mem);
|
||||
let result = lhs + src;
|
||||
// unsigned overflow
|
||||
self.flags.of = result < src;
|
||||
self.memory.write_from_memidx(&self.regs, dest_mem, result);
|
||||
}
|
||||
ModRmTarget::Register(dest_reg) => {
|
||||
let lhs = self.regs.read(dest_reg);
|
||||
let result = lhs + src;
|
||||
// unsigned overflow
|
||||
self.flags.of = result < src;
|
||||
self.regs.write(dest_reg, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ use std::{fmt::Debug, process::exit};
|
||||
|
||||
use crate::{
|
||||
instructions::{Instruction, Mnemonic},
|
||||
operands::{Byte, Word},
|
||||
operands::{Byte, ModRmTarget, Operand, Word},
|
||||
};
|
||||
|
||||
use super::computer::Computer;
|
||||
use super::computer::{Argument, Computer};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterpreterError {
|
||||
@@ -44,16 +44,43 @@ impl Interpreter {
|
||||
pub fn interpret(&mut self) -> Result<(), InterpreterError> {
|
||||
for instr in self.instructions.iter() {
|
||||
log::info!(
|
||||
"IP({:04x})\t {:<15} | {}",
|
||||
"IP({:04x})\t {:<30} | {}",
|
||||
instr.start,
|
||||
instr.opcode.to_string(),
|
||||
self.computer
|
||||
);
|
||||
|
||||
match instr.opcode {
|
||||
/*
|
||||
* ADD
|
||||
*/
|
||||
Mnemonic::ADD_FromReg(dest, src) => self
|
||||
.computer
|
||||
.add(dest, Argument::ModRmTarget(ModRmTarget::Register(src))),
|
||||
Mnemonic::ADD_ToReg(src, dest) => self
|
||||
.computer
|
||||
.add(ModRmTarget::Register(dest), Argument::ModRmTarget(src)),
|
||||
Mnemonic::ADD_Ib(dest, src) => self
|
||||
.computer
|
||||
.add(dest, Argument::Operand(Operand::Byte(src))),
|
||||
Mnemonic::ADD_Iv(dest, src) => self
|
||||
.computer
|
||||
.add(dest, Argument::Operand(Operand::Word(src))),
|
||||
Mnemonic::ADD_ALIb(src_byte) => self.computer.add(
|
||||
ModRmTarget::Register(crate::register::Register::AL),
|
||||
Argument::Operand(Operand::Byte(src_byte)),
|
||||
),
|
||||
Mnemonic::ADD_AXIv(src_word) => self.computer.add(
|
||||
ModRmTarget::Register(crate::register::Register::AX),
|
||||
Argument::Operand(Operand::Word(src_word)),
|
||||
),
|
||||
|
||||
/*
|
||||
* MOV
|
||||
*/
|
||||
Mnemonic::MOV_BXIv(word) => self.computer.regs.bx.write(word),
|
||||
Mnemonic::INT(id) => self.handle_int(id)?,
|
||||
_ => todo!(),
|
||||
_ => log::info!("no action done"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,3 +154,18 @@ impl InterruptData {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InterruptData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Interrupt({}, {}, {}, {}, {}, {})",
|
||||
self.m_type,
|
||||
self.interrupt_id,
|
||||
self.proc_nr,
|
||||
self.count,
|
||||
self.position,
|
||||
self.data_position
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
83
src/interpreter/memory.rs
Normal file
83
src/interpreter/memory.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::operands::{Byte, Displacement, MemoryIndex, Operand, Word};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Memory {
|
||||
memory: [Byte; 65536],
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new() -> Self {
|
||||
Self { memory: [0; 65536] }
|
||||
}
|
||||
|
||||
// Write an [`Operand`] to a memory location indexed by a [`MemoryIndex`].
|
||||
pub fn write(
|
||||
&mut self,
|
||||
regs: &crate::interpreter::register::Register,
|
||||
idx: MemoryIndex,
|
||||
val: Operand,
|
||||
) {
|
||||
match Memory::calc_memidx(regs, idx) {
|
||||
Operand::Byte(idx_byte) => match val {
|
||||
Operand::Byte(value) => {
|
||||
log::debug!("Writing byte {value:#04x} to memory location {idx_byte:#04x}");
|
||||
self.memory[idx_byte as usize] = value
|
||||
}
|
||||
Operand::Word(_) => panic!("Cannot add Word to Byte Memory Index"),
|
||||
},
|
||||
Operand::Word(idx_word) => match val {
|
||||
Operand::Word(value) => {
|
||||
let byte1 = idx_word / 2;
|
||||
let byte2 = idx_word / 2 + 1;
|
||||
let [low, high]: [u8; 2] = value.to_le_bytes();
|
||||
log::debug!(
|
||||
"Writing bytes {low:#04x} and {high:#04x} to memory location {byte1:#04x} and {byte2:#04x}"
|
||||
);
|
||||
self.memory[byte1 as usize] = low;
|
||||
self.memory[byte1 as usize] = high;
|
||||
}
|
||||
Operand::Byte(value) => {
|
||||
self.memory[(idx_word * 2) as usize] = value;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Read into memory with a [`MemoryIndex`] as index.
|
||||
pub fn read(&self, regs: &crate::interpreter::register::Register, idx: MemoryIndex) -> Operand {
|
||||
match Memory::calc_memidx(regs, idx) {
|
||||
Operand::Byte(byte) => {
|
||||
log::debug!("Reading byte {byte:#04x} from memory");
|
||||
Operand::Byte(self.memory[byte as usize])
|
||||
}
|
||||
Operand::Word(word) => {
|
||||
let byte1 = word / 2;
|
||||
let byte2 = word / 2 + 1;
|
||||
log::debug!("Reading bytes {byte1:#04x} and {byte2:#04x} from memory");
|
||||
Operand::Word(Word::from_le_bytes([
|
||||
self.memory[byte1 as usize],
|
||||
self.memory[byte2 as usize],
|
||||
]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the absolute Memory Index from a [`MemoryIndex`] struct.
|
||||
fn calc_memidx(regs: &crate::interpreter::register::Register, idx: MemoryIndex) -> Operand {
|
||||
let mut base = Operand::Word(0);
|
||||
let mut index = Operand::Word(0);
|
||||
let mut disp = Displacement::IWord(0);
|
||||
|
||||
if let Some(base_reg) = idx.base {
|
||||
base = regs.read(base_reg);
|
||||
};
|
||||
if let Some(index_reg) = idx.index {
|
||||
index = regs.read(index_reg);
|
||||
};
|
||||
if let Some(displacement) = idx.displacement {
|
||||
disp = displacement;
|
||||
}
|
||||
|
||||
base + index + disp
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod computer;
|
||||
mod flags;
|
||||
pub mod interpreter;
|
||||
mod memory;
|
||||
mod register;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::operands::{Byte, Word};
|
||||
use crate::operands::{Byte, Operand, Word};
|
||||
use core::fmt;
|
||||
|
||||
use super::flags::Flags;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Register {
|
||||
pub ax: AX,
|
||||
@@ -26,6 +28,73 @@ impl Register {
|
||||
di: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read value from a [`crate::register::Register`].
|
||||
pub fn read(&self, reg: crate::register::Register) -> Operand {
|
||||
match reg {
|
||||
crate::register::Register::AX => Operand::Word(self.ax.read()),
|
||||
crate::register::Register::BX => Operand::Word(self.bx.read()),
|
||||
crate::register::Register::CX => Operand::Word(self.cx.read()),
|
||||
crate::register::Register::DX => Operand::Word(self.dx.read()),
|
||||
crate::register::Register::AH => Operand::Byte(self.ax.upper),
|
||||
crate::register::Register::AL => Operand::Byte(self.ax.lower),
|
||||
crate::register::Register::BH => Operand::Byte(self.bx.upper),
|
||||
crate::register::Register::BL => Operand::Byte(self.bx.lower),
|
||||
crate::register::Register::CH => Operand::Byte(self.cx.upper),
|
||||
crate::register::Register::CL => Operand::Byte(self.cx.lower),
|
||||
crate::register::Register::DH => Operand::Byte(self.dx.upper),
|
||||
crate::register::Register::DL => Operand::Byte(self.dx.lower),
|
||||
crate::register::Register::DI => Operand::Word(self.di),
|
||||
crate::register::Register::SI => Operand::Word(self.si),
|
||||
crate::register::Register::BP => Operand::Word(self.bp),
|
||||
crate::register::Register::SP => Operand::Word(self.sp),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write an [`Operand`] to a [`crate::register::Register`].
|
||||
pub fn write(&mut self, reg: crate::register::Register, op: Operand) {
|
||||
match op {
|
||||
Operand::Byte(byte) => match reg {
|
||||
crate::register::Register::AX => self.ax.lower = byte,
|
||||
crate::register::Register::BX => self.bx.lower = byte,
|
||||
crate::register::Register::CX => self.cx.lower = byte,
|
||||
crate::register::Register::DX => self.dx.lower = byte,
|
||||
crate::register::Register::AH => self.ax.upper = byte,
|
||||
crate::register::Register::AL => self.ax.lower = byte,
|
||||
crate::register::Register::BH => self.bx.upper = byte,
|
||||
crate::register::Register::BL => self.bx.lower = byte,
|
||||
crate::register::Register::CH => self.cx.upper = byte,
|
||||
crate::register::Register::CL => self.cx.lower = byte,
|
||||
crate::register::Register::DH => self.dx.upper = byte,
|
||||
crate::register::Register::DL => self.dx.lower = byte,
|
||||
crate::register::Register::DI => self.di = Word::from_le_bytes([0x0, byte]),
|
||||
crate::register::Register::SI => self.si = Word::from_le_bytes([0x0, byte]),
|
||||
crate::register::Register::BP => self.bp = Word::from_le_bytes([0x0, byte]),
|
||||
crate::register::Register::SP => self.sp = Word::from_le_bytes([0x0, byte]),
|
||||
},
|
||||
Operand::Word(word) => {
|
||||
match reg {
|
||||
crate::register::Register::AX => self.ax.write(word),
|
||||
crate::register::Register::BX => self.bx.write(word),
|
||||
crate::register::Register::CX => self.cx.write(word),
|
||||
crate::register::Register::DX => self.dx.write(word),
|
||||
// crate::register::Register::AH => {}
|
||||
// crate::register::Register::AL => {}
|
||||
// crate::register::Register::BL => {}
|
||||
// crate::register::Register::BH => {}
|
||||
// crate::register::Register::CH => {}
|
||||
// crate::register::Register::CL => {}
|
||||
// crate::register::Register::DH => {}
|
||||
// crate::register::Register::DL => {}
|
||||
crate::register::Register::DI => self.di = word,
|
||||
crate::register::Register::SI => self.si = word,
|
||||
crate::register::Register::BP => self.bp = word,
|
||||
crate::register::Register::SP => self.sp = word,
|
||||
_ => panic!("Tried writing Word to Byte-sized register"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Register {
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::register::SegmentRegister;
|
||||
|
||||
use crate::{disasm::DisasmError, register::Register};
|
||||
use core::fmt;
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
pub type Byte = u8; // b
|
||||
pub type IByte = i8; // used for displacements of memory access
|
||||
@@ -14,7 +15,7 @@ pub type Word = u16; // w or v
|
||||
pub type IWord = i16; // used for displacement of memory access
|
||||
pub type DWord = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Ord, Eq, PartialEq, PartialOrd, Copy)]
|
||||
/// Encodes either Byte- or Word-sized operands.
|
||||
/// Also sometimes used to decide if an instruction is Byte- or Word-sized,
|
||||
/// which is usually indicated by using a value of 0 and the disregarding
|
||||
@@ -24,6 +25,64 @@ pub enum Operand {
|
||||
Word(Word),
|
||||
}
|
||||
|
||||
impl Add for Operand {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
match self {
|
||||
Operand::Byte(lhsb) => match other {
|
||||
Operand::Byte(rhsb) => Operand::Byte(lhsb.wrapping_add(rhsb)),
|
||||
_ => panic!("Cannot add Word to Byte"),
|
||||
},
|
||||
Operand::Word(lhsw) => match other {
|
||||
Operand::Word(rhsw) => Operand::Word(lhsw.wrapping_add(rhsw)),
|
||||
Operand::Byte(rhsb) => Operand::Word(lhsw.wrapping_add(rhsb as Word)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Displacement> for Operand {
|
||||
type Output = Operand;
|
||||
|
||||
fn add(self, disp: Displacement) -> Self::Output {
|
||||
// XXX: ugly hack to add Operand with Displacement
|
||||
match disp {
|
||||
Displacement::IByte(byte) => {
|
||||
if byte < 0 {
|
||||
return self - Operand::Byte((byte * -1) as Byte);
|
||||
} else {
|
||||
return self + Operand::Byte(byte as Byte);
|
||||
}
|
||||
}
|
||||
Displacement::IWord(word) => {
|
||||
if word < 0 {
|
||||
return self - Operand::Word((word * -1) as Word);
|
||||
} else {
|
||||
return self + Operand::Word(word as Word);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Operand {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
match self {
|
||||
Operand::Byte(lhsb) => match other {
|
||||
Operand::Byte(rhsb) => Operand::Byte(lhsb.wrapping_sub(rhsb)),
|
||||
_ => panic!("Cannot substract Word from Byte"),
|
||||
},
|
||||
Operand::Word(lhsw) => match other {
|
||||
Operand::Word(rhsw) => Operand::Word(lhsw.wrapping_sub(rhsw)),
|
||||
Operand::Byte(rhsb) => Operand::Word(lhsw.wrapping_sub(rhsb as Word)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
@@ -42,7 +101,7 @@ impl fmt::LowerHex for Operand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// ModRM byte can either target a [`MemoryIndex`] (location in memory) or some
|
||||
/// [`Register`].
|
||||
pub enum ModRmTarget {
|
||||
@@ -59,7 +118,7 @@ impl std::fmt::Display for ModRmTarget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// Memory displacements are signed versions of Byte and Word operands.
|
||||
/// Encodes either Byte- or Word-sized operands.
|
||||
/// Generally, a [`Displacement`] is the result of a ModRM byte parse and
|
||||
@@ -105,7 +164,7 @@ impl std::fmt::Display for Displacement {
|
||||
|
||||
/// A memory index operand is usually created by ModRM bytes or words.
|
||||
/// e.g. [bx+si]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct MemoryIndex {
|
||||
pub base: Option<Register>,
|
||||
pub index: Option<Register>,
|
||||
@@ -141,7 +200,7 @@ impl fmt::Display for MemoryIndex {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// 16-bit pointer for access, usually with a [`SegmentRegister`] as segment
|
||||
/// and [`Pointer16`] as offset.
|
||||
/// Generally, this type only gets constructed in rare scenarios, when the
|
||||
@@ -185,7 +244,7 @@ impl TryFrom<ModRmTarget> for Pointer16 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// 32-bit segment:offset pointer for long jumps.
|
||||
/// Both [`Word`]s are immediately encoded after the instruction
|
||||
pub struct Pointer32 {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use crate::{disasm::DisasmError, operands::Operand};
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// Registers of a 8086 processor
|
||||
pub enum Register {
|
||||
// 8 bit
|
||||
@@ -86,7 +86,7 @@ impl fmt::Display for Register {
|
||||
}
|
||||
|
||||
/// Segment Registers of a 8086 processor
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum SegmentRegister {
|
||||
DS,
|
||||
ES,
|
||||
|
||||
Reference in New Issue
Block a user