ft(interpreter): impl push/pop
This commit is contained in:
@@ -1,16 +1,23 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::operands::{ImmediateOperand, ModRmTarget};
|
||||
use crate::operands::{ImmediateOperand, MemoryIndex, ModRmTarget};
|
||||
|
||||
use super::{flags::Flags, memory::Memory, register::Register};
|
||||
use super::{flags::Flags, interpreter::InterpreterError, memory::Memory, register::Register};
|
||||
|
||||
/// Wrapper for easier argument passing of multi-type operations.
|
||||
/// Wrapper for easier argument passing of polymorph arithmetic operations.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Operand {
|
||||
Immediate(crate::operands::ImmediateOperand),
|
||||
ModRmTarget(ModRmTarget),
|
||||
}
|
||||
|
||||
/// Wrapper for easier argument passing of polymorph pop operations.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PopTarget {
|
||||
Register(crate::register::Register),
|
||||
MemoryIndex(MemoryIndex),
|
||||
}
|
||||
|
||||
type ArithmeticResult = ImmediateOperand;
|
||||
type Lhs = ImmediateOperand;
|
||||
type Rhs = ImmediateOperand;
|
||||
@@ -31,6 +38,28 @@ impl Computer {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, target: PopTarget) -> Result<(), InterpreterError> {
|
||||
let word = self.memory.read_raw(self.regs.sp)?;
|
||||
|
||||
match target {
|
||||
PopTarget::Register(reg) => self.regs.write(reg, word.into()),
|
||||
PopTarget::MemoryIndex(mem_idx) => self
|
||||
.memory
|
||||
.write_raw(Memory::calc_memidx(&self.regs, mem_idx).into(), word.into())?,
|
||||
}
|
||||
|
||||
self.regs.pop()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::{fmt::Debug, process::exit};
|
||||
|
||||
use crate::{
|
||||
instructions::{Instruction, Mnemonic},
|
||||
interpreter::{interrupt::Mess1, memory::Memory},
|
||||
operands::{Byte, ImmediateOperand, ModRmTarget},
|
||||
interpreter::{computer::PopTarget, interrupt::Mess1, memory::Memory},
|
||||
operands::{Byte, ImmediateOperand, ModRmTarget, Word},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -17,8 +17,10 @@ type InstructionPointer<'a> = std::slice::Iter<'a, Instruction>;
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterpreterError {
|
||||
EndOfData,
|
||||
InvalidSyscall(u8),
|
||||
InstructionNotFound(usize),
|
||||
InvalidSyscall(Byte),
|
||||
InstructionNotFound(Word),
|
||||
MemoryOutOfBound(Word),
|
||||
InvalidRegisterState(crate::interpreter::register::Register),
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpreterError {
|
||||
@@ -31,6 +33,15 @@ impl fmt::Display for InterpreterError {
|
||||
InterpreterError::InstructionNotFound(addr) => {
|
||||
write!(f, "IP({addr}) points at invalid instruction")
|
||||
}
|
||||
InterpreterError::MemoryOutOfBound(addr) => {
|
||||
write!(
|
||||
f,
|
||||
"Attempted memory write out of physical bounds: ({addr:#04x})"
|
||||
)
|
||||
}
|
||||
InterpreterError::InvalidRegisterState(reg) => {
|
||||
write!(f, "Invalid Register State: {reg:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,10 +101,18 @@ impl Interpreter {
|
||||
/*
|
||||
* PUSH
|
||||
*/
|
||||
Mnemonic::PUSH_R(reg) => self.computer.push_stack(self.computer.regs.read(reg))?,
|
||||
Mnemonic::PUSH_Mod(target) => {
|
||||
self.computer.push_stack(self.computer.read_modrm(target))?
|
||||
}
|
||||
Mnemonic::PUSH_S(_) => todo!(),
|
||||
|
||||
/*
|
||||
* POP
|
||||
*/
|
||||
Mnemonic::POP_R(reg) => self.computer.pop_stack(PopTarget::Register(reg))?,
|
||||
Mnemonic::POP_M(idx) => self.computer.pop_stack(PopTarget::MemoryIndex(idx))?,
|
||||
Mnemonic::POP_S(_) => todo!(),
|
||||
|
||||
/*
|
||||
* OR
|
||||
|
||||
@@ -1,16 +1,47 @@
|
||||
use crate::operands::{Byte, Displacement, ImmediateOperand, MemoryIndex, Word};
|
||||
|
||||
use super::interpreter::InterpreterError;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Memory {
|
||||
memory: [Byte; 65536],
|
||||
memory: [Byte; Word::MAX as usize],
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new() -> Self {
|
||||
Self { memory: [0; 65536] }
|
||||
Self {
|
||||
memory: [0; Word::MAX as usize],
|
||||
}
|
||||
}
|
||||
|
||||
// Write an [`ImmediateOperand`] to a memory location indexed by a [`MemoryIndex`].
|
||||
/// Safely writes a [`Word`] into an index of memory.
|
||||
pub fn write_raw(&mut self, idx: Word, val: Word) -> Result<(), InterpreterError> {
|
||||
if idx + 1 > Word::MAX {
|
||||
return Err(InterpreterError::MemoryOutOfBound(idx));
|
||||
} else {
|
||||
let [low, high] = val.to_le_bytes();
|
||||
self.memory[idx as usize] = low;
|
||||
self.memory[(idx + 1) as usize] = high;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Safely reads a [`Word`] from an index of memory
|
||||
pub fn read_raw(&self, idx: Word) -> Result<Word, InterpreterError> {
|
||||
let b1 = self
|
||||
.memory
|
||||
.get(idx as usize)
|
||||
.ok_or(InterpreterError::MemoryOutOfBound(idx))?
|
||||
.to_owned();
|
||||
let b2 = self
|
||||
.memory
|
||||
.get((idx + 1) as usize)
|
||||
.ok_or(InterpreterError::MemoryOutOfBound(idx))?
|
||||
.to_owned();
|
||||
Ok(Word::from_be_bytes([b1, b2]))
|
||||
}
|
||||
|
||||
/// Write an [`ImmediateOperand`] to a memory location indexed by a [`MemoryIndex`].
|
||||
pub fn write(
|
||||
&mut self,
|
||||
regs: &crate::interpreter::register::Register,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::operands::{Byte, ImmediateOperand, Word};
|
||||
use core::fmt;
|
||||
|
||||
use super::interpreter::InterpreterError;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Register {
|
||||
pub ax: AX,
|
||||
@@ -20,13 +22,33 @@ impl Register {
|
||||
bx: BX::new(),
|
||||
cx: CX::new(),
|
||||
dx: DX::new(),
|
||||
sp: 0,
|
||||
sp: 0xffff,
|
||||
bp: 0,
|
||||
si: 0,
|
||||
di: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrement stack pointer
|
||||
pub fn push(&mut self) -> Result<(), InterpreterError> {
|
||||
if self.sp <= 2 {
|
||||
return Err(InterpreterError::InvalidRegisterState(*self));
|
||||
} else {
|
||||
self.sp -= 2;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment stack pointer
|
||||
pub fn pop(&mut self) -> Result<(), InterpreterError> {
|
||||
if self.sp >= 0xffff - 2 {
|
||||
return Err(InterpreterError::InvalidRegisterState(*self));
|
||||
} else {
|
||||
self.sp += 2;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Read value from a [`crate::register::Register`].
|
||||
pub fn read(&self, reg: crate::register::Register) -> ImmediateOperand {
|
||||
match reg {
|
||||
|
||||
@@ -34,9 +34,9 @@ impl ImmediateOperand {
|
||||
match self {
|
||||
Self::Byte(_) => {
|
||||
return if self.msb() {
|
||||
self.flip_sign().as_word().flip_sign()
|
||||
self.flip_sign().word().flip_sign()
|
||||
} else {
|
||||
self.as_word()
|
||||
self.word()
|
||||
};
|
||||
}
|
||||
Self::Word(_) => self,
|
||||
@@ -46,7 +46,7 @@ impl ImmediateOperand {
|
||||
/// Interprets [`Self::Byte`] as [`Self::Word`].
|
||||
/// Returns word, if already a [`Self::Word`].
|
||||
/// CAUTION: You probably want to use [`Self::sign_extend()`] instead.
|
||||
fn as_word(self) -> Self {
|
||||
fn word(self) -> Self {
|
||||
match self {
|
||||
Self::Byte(b) => Self::Word(b as Word),
|
||||
Self::Word(_) => self,
|
||||
@@ -566,7 +566,7 @@ mod tests {
|
||||
let b: u8 = 5;
|
||||
let byte = ImmediateOperand::Byte(b);
|
||||
let word = ImmediateOperand::Word(b as Word);
|
||||
assert_eq!(byte.as_word(), word);
|
||||
assert_eq!(byte.word(), word);
|
||||
assert_eq!(word, word);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user