From 6c352396fa13b92b0c06509870ad3c543e4dd16c Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Mon, 10 Feb 2020 13:22:54 -0500 Subject: [PATCH] Add instruction visitor, which traverses memory and choosing instructions Signed-off-by: Alek Ratzloff --- src/vm/error.rs | 4 +- src/vm/mod.rs | 1 + src/vm/tick.rs | 308 ++++++++++++++++++++++++++++-------------------- src/vm/visit.rs | 95 +++++++++++++++ src/vm/vm.rs | 7 +- 5 files changed, 287 insertions(+), 128 deletions(-) create mode 100644 src/vm/visit.rs diff --git a/src/vm/error.rs b/src/vm/error.rs index f0841df..8e8e5c4 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -1,4 +1,4 @@ -use crate::vm::{vm::*, reg::Reg}; +use crate::vm::{inst::InstOp, reg::Reg, vm::*}; use snafu::Snafu; #[derive(Snafu, Debug, Clone)] @@ -7,6 +7,8 @@ pub enum VmError { IllegalReg { reg: Reg }, #[snafu(display("memory address out of bounds: 0x{:016x}", addr))] MemOutOfBounds { addr: Addr }, + #[snafu(display("illegal instruction opcode: 0x{:04x}", op))] + IllegalOp { op: InstOp }, } pub type Result = std::result::Result; diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 425f2f9..1f5f3e8 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -5,4 +5,5 @@ pub mod mem; pub mod obj; pub mod reg; mod tick; +pub mod visit; pub mod vm; diff --git a/src/vm/tick.rs b/src/vm/tick.rs index 5d00d08..deb5054 100644 --- a/src/vm/tick.rs +++ b/src/vm/tick.rs @@ -1,134 +1,190 @@ -use crate::vm::{error::*, flags::Flags, inst::*, reg::*, vm::*}; +use crate::vm::{error::*, flags::Flags, inst::*, reg::*, vm::*, visit::*, mem::MemCursor}; use std::io::stdin; impl Vm { pub fn tick(&mut self) -> Result<()> { - let mut cursor = self.mem_cursor(self.ip() as usize); - let op = cursor.next_u16()?; - let next_ip; - - macro_rules! math_inst { - ($mapping:expr) => {{ - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let value = ($mapping)(self.get_reg_checked(r1)?, self.get_reg_checked(r2)?); - self.set_reg(r1, value); - }}; - } - match op { - ADD => math_inst!(|w1: u64, w2: u64| w1.wrapping_add(w2)), - MUL => math_inst!(|w1: u64, w2: u64| w1.wrapping_mul(w2)), - DIV => math_inst!(|w1: u64, w2: u64| w1.wrapping_div(w2)), - MOD => math_inst!(|w1: u64, w2: u64| w1 % w2), - INEG => todo!(), - AND => math_inst!(|w1: u64, w2: u64| w1 & w2), - OR => math_inst!(|w1: u64, w2: u64| w1 | w2), - INV => { - let r1 = cursor.next_reg()?; - next_ip = cursor.position(); - let value = self.get_reg_checked(r1)?; - self.set_reg(r1, !value); - } - NOT => { - let r1 = cursor.next_reg()?; - next_ip = cursor.position(); - let value = self.get_reg(r1); - self.set_reg(r1, (value == 0) as Word); - } - XOR => math_inst!(|w1: u64, w2: u64| w1 ^ w2), - SHL => math_inst!(|w1: u64, w2: u64| w1 << w2), - SHR => math_inst!(|w1: u64, w2: u64| w1 >> w2), - CMPEQ => { - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let cmp = self.get_reg(r1) == self.get_reg(r2); - if cmp { - self.insert_flags(Flags::COMPARE); - } else { - self.remove_flags(Flags::COMPARE); - } - } - CMPLT => { - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let cmp = self.get_reg(r1) < self.get_reg(r2); - if cmp { - self.insert_flags(Flags::COMPARE); - } else { - self.remove_flags(Flags::COMPARE); - } - } - JMP => { - let r1 = cursor.next_reg()?; - next_ip = self.get_reg(r1); - } - JZ => { - let r1 = cursor.next_reg()?; - if !self.flags().contains(Flags::COMPARE) { - next_ip = self.get_reg(r1); - } else { - next_ip = cursor.position(); - } - } - JNZ => { - let r1 = cursor.next_reg()?; - if self.flags().contains(Flags::COMPARE) { - next_ip = self.get_reg(r1); - } else { - next_ip = cursor.position(); - } - } - LOAD => { - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let value = self.load(r2)?; - self.set_reg_checked(r1, value)?; - } - REGCOPY => math_inst!(|_: u64, w2: u64| w2), - STOREIMM64 => { - let r1 = cursor.next_reg()?; - // skip - cursor.next_u32()?; - let imm = cursor.next_u64()?; - next_ip = cursor.position(); - self.set_reg(r1, imm); - } - STOREIMM32 => { - let r1 = cursor.next_reg()?; - let imm = cursor.next_u32()?; - next_ip = cursor.position(); - self.set_reg(r1, imm as u64); - } - MEMCOPY => { - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let value = self.load(r2)?; - self.store(r1, value)?; - } - STORE => { - let (r1, r2) = cursor.next_regs()?; - next_ip = cursor.position(); - let value = self.get_reg(r1); - self.store(r2, value)?; - } - HALT => { - next_ip = cursor.position(); - self.insert_flags(Flags::HALT); - } - NOP => { - next_ip = cursor.position(); - } - _ => panic!("unknown instruction opcode: 0x{:04x}", op), - } - - println!("op: {:04x} {}", op, inst_name(op).unwrap()); - println!("ip: {:05x}", self.ip()); - println!("next_ip: {:05x}", next_ip); - let mut _line = String::new(); - stdin().read_line(&mut _line).unwrap(); - + let next_ip = visit_inst(self)?; self.set_reg(IP, next_ip); - Ok(()) } + + fn next_ip(&self) -> Result { + let ip = self.ip(); + let op = self.get_inst_op(ip)?; + Ok(ip + (inst_len(op) as u64)) + } + + fn with_regs(&mut self, r1: Reg, r2: Reg, mapping: F) -> B + where F: FnOnce(Word, Word) -> B + { + let w1 = self.get_reg(r1); + let w2 = self.get_reg(r2); + (mapping)(w1, w2) + } +} + +impl InstAcceptor for Vm { + type Out = Addr; + + fn cursor(&self) -> MemCursor { + self.mem_cursor(self.ip() as usize) + } + + fn add(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_add(w2)); + self.set_reg(r1, out); + self.next_ip() + } + + fn mul(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1.wrapping_mul(w2)); + self.set_reg(r1, out); + self.next_ip() + } + + fn div(&mut self, r1: Reg, r2: Reg) -> Result { + // TODO : check w2 == 0 and throw error/exception + let out = self.with_regs(r1, r2, |w1, w2| w1 / w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn mod_(&mut self, r1: Reg, r2: Reg) -> Result { + // TODO : check w2 == 0 and throw error/exception + let out = self.with_regs(r1, r2, |w1, w2| w1 % w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn ineg(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, (!w1).wrapping_add(1)); + self.next_ip() + } + + fn and(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 & w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn or(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 | w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn inv(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, !w1); + self.next_ip() + } + + fn not(&mut self, r1: Reg) -> Result { + let w1 = self.get_reg(r1); + self.set_reg(r1, (w1 == 0) as Word); + self.next_ip() + } + + fn xor(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 ^ w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn shl(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 << w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn shr(&mut self, r1: Reg, r2: Reg) -> Result { + let out = self.with_regs(r1, r2, |w1, w2| w1 >> w2); + self.set_reg(r1, out); + self.next_ip() + } + + fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result { + let cmp = self.with_regs(r1, r2, |w1, w2| w1 == w2); + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + self.next_ip() + } + + fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result { + let cmp = self.with_regs(r1, r2, |w1, w2| w1 < w2); + if cmp { + self.insert_flags(Flags::COMPARE); + } else { + self.remove_flags(Flags::COMPARE); + } + self.next_ip() + } + + fn jmp(&mut self, r1: Reg) -> Result { + let addr = self.get_reg(r1); + Ok(addr) + } + + fn jz(&mut self, r1: Reg) -> Result { + if !self.flags().contains(Flags::COMPARE) { + Ok(self.get_reg(r1)) + } else { + self.next_ip() + } + } + + fn jnz(&mut self, r1: Reg) -> Result { + if self.flags().contains(Flags::COMPARE) { + Ok(self.get_reg(r1)) + } else { + self.next_ip() + } + } + + fn load(&mut self, r1: Reg, r2: Reg) -> Result { + let value = Vm::load(self, r2)?; + self.set_reg_checked(r1, value)?; + self.next_ip() + } + + fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result { + let value = self.get_reg_checked(r2)?; + self.set_reg_checked(r1, value)?; + self.next_ip() + } + + fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result { + self.set_reg_checked(r1, w1)?; + self.next_ip() + } + + fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result { + self.set_reg_checked(r1, w1 as Word)?; + self.next_ip() + } + + fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result { + let value = Vm::load(self, r2)?; + self.store(r1, value)?; + self.next_ip() + } + + fn store(&mut self, r1: Reg, r2: Reg) -> Result { + let value = self.get_reg_checked(r1)?; + self.store(r2, value)?; + self.next_ip() + } + + fn halt(&mut self) -> Result { + self.insert_flags(Flags::HALT); + self.next_ip() + } + + fn nop(&mut self) -> Result { + self.next_ip() + } } diff --git a/src/vm/visit.rs b/src/vm/visit.rs new file mode 100644 index 0000000..c0803bc --- /dev/null +++ b/src/vm/visit.rs @@ -0,0 +1,95 @@ +use crate::vm::{ + error::*, + inst::*, + mem::MemCursor, + reg::Reg, + vm::{HalfWord, Word}, +}; + +pub trait InstAcceptor { + type Out; + + fn cursor(&self) -> MemCursor; + fn add(&mut self, r1: Reg, r2: Reg) -> Result; + fn mul(&mut self, r1: Reg, r2: Reg) -> Result; + fn div(&mut self, r1: Reg, r2: Reg) -> Result; + fn mod_(&mut self, r1: Reg, r2: Reg) -> Result; + fn ineg(&mut self, r1: Reg) -> Result; + fn and(&mut self, r1: Reg, r2: Reg) -> Result; + fn or(&mut self, r1: Reg, r2: Reg) -> Result; + fn inv(&mut self, r1: Reg) -> Result; + fn not(&mut self, r1: Reg) -> Result; + fn xor(&mut self, r1: Reg, r2: Reg) -> Result; + fn shl(&mut self, r1: Reg, r2: Reg) -> Result; + fn shr(&mut self, r1: Reg, r2: Reg) -> Result; + fn cmpeq(&mut self, r1: Reg, r2: Reg) -> Result; + fn cmplt(&mut self, r1: Reg, r2: Reg) -> Result; + fn jmp(&mut self, r1: Reg) -> Result; + fn jz(&mut self, r1: Reg) -> Result; + fn jnz(&mut self, r1: Reg) -> Result; + fn load(&mut self, r1: Reg, r2: Reg) -> Result; + fn regcopy(&mut self, r1: Reg, r2: Reg) -> Result; + fn storeimm64(&mut self, r1: Reg, w1: Word) -> Result; + fn storeimm32(&mut self, r1: Reg, w1: HalfWord) -> Result; + fn memcopy(&mut self, r1: Reg, r2: Reg) -> Result; + fn store(&mut self, r1: Reg, r2: Reg) -> Result; + fn halt(&mut self) -> Result; + fn nop(&mut self) -> Result; +} + +pub fn visit_inst(acceptor: &mut A) -> Result { + let mut cursor = acceptor.cursor(); + let op = cursor.next_u16()?; + + macro_rules! r1_r2_inst { + ($fun:ident) => {{ + let (r1, r2) = cursor.next_regs()?; + acceptor.$fun(r1, r2) + }}; + } + macro_rules! r1_inst { + ($fun:ident) => {{ + let r1 = cursor.next_reg()?; + acceptor.$fun(r1) + }}; + } + + match op { + ADD => r1_r2_inst!(add), + MUL => r1_r2_inst!(mul), + DIV => r1_r2_inst!(div), + MOD => r1_r2_inst!(mod_), + INEG => r1_inst!(ineg), + AND => r1_r2_inst!(and), + OR => r1_r2_inst!(or), + INV => r1_inst!(inv), + NOT => r1_inst!(not), + XOR => r1_r2_inst!(xor), + SHL => r1_r2_inst!(shl), + SHR => r1_r2_inst!(shr), + CMPEQ => r1_r2_inst!(cmpeq), + CMPLT => r1_r2_inst!(cmplt), + JMP => r1_inst!(jmp), + JZ => r1_inst!(jz), + JNZ => r1_inst!(jnz), + LOAD => r1_r2_inst!(load), + REGCOPY => r1_r2_inst!(regcopy), + STOREIMM64 => { + let r1 = cursor.next_reg()?; + // skip + cursor.next_u32()?; + let imm = cursor.next_u64()?; + acceptor.storeimm64(r1, imm) + } + STOREIMM32 => { + let r1 = cursor.next_reg()?; + let imm = cursor.next_u32()?; + acceptor.storeimm32(r1, imm) + } + MEMCOPY => r1_r2_inst!(memcopy), + STORE => r1_r2_inst!(store), + HALT => acceptor.halt(), + NOP => acceptor.nop(), + _ => Err(VmError::IllegalOp { op }), + } +} diff --git a/src/vm/vm.rs b/src/vm/vm.rs index e2e28b7..d1dbac6 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -1,4 +1,4 @@ -use crate::vm::{error::*, flags::*, mem::*, obj::obj::*, reg::*}; +use crate::vm::{error::*, flags::*, inst::InstOp, mem::*, obj::obj::*, reg::*}; use byteorder::{WriteBytesExt, LE}; use std::{io::Cursor, mem}; @@ -101,6 +101,11 @@ impl Vm { Ok(self.mem_cursor(addr as usize).next_u32().unwrap()) } + pub fn get_inst_op(&self, addr: Addr) -> Result { + self.check_read(addr, 2)?; + Ok(self.mem_cursor(addr as usize).next_u16().unwrap()) + } + pub fn get_byte(&self, addr: Addr) -> Result { self.check_addr(addr)?; Ok(self.mem_cursor(addr as usize).next_u8().unwrap())