From 263686902be32de819a58f6d78ffca7f3690c8ba Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Thu, 6 Feb 2020 23:13:00 -0500 Subject: [PATCH] REPL! - Created a basic REPL that accepts hex code - Commented a lot of the code --- src/lib.rs | 2 -- src/main.rs | 9 ++++- src/repl.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vm.rs | 29 ++++++++++++++--- 4 files changed, 126 insertions(+), 8 deletions(-) delete mode 100644 src/lib.rs create mode 100644 src/repl.rs diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 0ff6fdb..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod instruction; -mod vm; diff --git a/src/main.rs b/src/main.rs index e7a11a9..f330e7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,10 @@ +mod instruction; +mod vm; +mod repl; + +use repl::REPL; + fn main() { - println!("Hello, world!"); + let mut repl = REPL::new(); + repl.run(); } diff --git a/src/repl.rs b/src/repl.rs new file mode 100644 index 0000000..fe470ca --- /dev/null +++ b/src/repl.rs @@ -0,0 +1,94 @@ +use std; +use std::io; +use std::num::ParseIntError; +use std::io::Write; + +use crate::vm::VM; + +pub struct REPL { + command_buffer: Vec, + vm: VM, +} + +impl REPL { + pub fn new() -> Self { + REPL { + vm: VM::new(), + command_buffer: vec![], + } + } + + /// Runs a similar VM execution loop but the instructions are taken from + /// the user directly at the terminal and not from pre-compiled byte code + pub fn run(&mut self) { + println!("Welcome to the Corten REPL"); + loop { + // Allocates a new string in which to store the user types each iteration + let mut buffer = String::new(); + + // Blocking call until the user types in a command + let stdin = io::stdin(); + print!(">>> "); + + // Look at the string the user gave us + io::stdout().flush().expect("Unable to flush stdout."); + stdin.read_line(&mut buffer).expect("Unable to read line."); + + let buffer = buffer.trim(); + + match buffer { + ".exit" => { + std::process::exit(0); + }, + ".history" => { + for command in &self.command_buffer { + println!("{}", command); + } + } + ".program" => { + println!("Listing instructions currently in VM's program:"); + for instruction in &self.vm.program { + println!("{}", instruction); + } + }, + ".registers" => { + println!("Listing registers and all contents:"); + println!("{:#?}", self.vm.registers); + }, + _ => { + let results = self.parse_hex(buffer); + match results { + Ok(bytes) => { + for byte in bytes { + self.vm.add_byte(byte); + } + } + Err(_er) => { + println!("Unable to decode hex string. Please enter 4 groups of 2 hex characters."); + } + } + self.vm.run_once(); + } + } + } + } + + /// Accepts the hexadecimal without a leading '0x' and returns a Vec of a + /// u8. Example: 00 01 03 E8 + fn parse_hex(&mut self, i: &str) -> Result, ParseIntError> { + let split = i.split(" ").collect::>(); + let mut results: Vec = vec![]; + for hex_string in split { + let byte = u8::from_str_radix(&hex_string, 16); + match byte { + Ok(result) => { + results.push(result); + }, + Err(err) => { + return Err(err) + } + } + } + Ok(results) + } +} \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs index 486ba3c..dd3acfa 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -2,14 +2,20 @@ use crate::instruction::Opcode; pub struct VM { - registers: [i32; 32], + /// Array that simulates the hardware register + pub registers: [i32; 32], + /// Program counter that tracks which byte is being executed pc: usize, - program: Vec, + /// The byte of the program being ran + pub program: Vec, + /// The remainer of the module used in the division opcode remainder: u32, + /// The result of the last comparison operation equal_flag: bool, } impl VM { + /// Creates and returns a new VM pub fn new() -> Self { VM { registers: [0; 32], @@ -20,24 +26,30 @@ impl VM { } } + /// Attempts to decode the byte the VM's program is pointing at + /// into an opcode fn decode_opcode(&mut self) -> Opcode { let opcode = Opcode::from(self.program[self.pc]); self.pc += 1; opcode } + /// Attempts to decode the byte into an opcode fn next_8_bits(&mut self) -> u8 { let result = self.program[self.pc]; self.pc += 1; result } + /// Grabs the next 16 bits (2 bytes) fn next_16_bits(&mut self) -> u16 { let result = ((self.program[self.pc] as u16) << 8) | self.program[self.pc + 1] as u16; self.pc += 2; result } + /// Wraps the execuation in a loop so it will continue to run until done or + /// there is an error executing the instructions. pub fn run(&mut self) { let mut is_done = false; while !is_done { @@ -45,10 +57,18 @@ impl VM { } } + /// Executes one instruction. Meant to allow for more controlled of the VM. pub fn run_once(&mut self) { self.execute_instruction(); } + /// Adds an byte to the VM's program + pub fn add_byte(&mut self, byte: u8) { + self.program.push(byte); + } + + /// Executes an instruction and returns a bool. Meant to be called by the + /// various public functions. fn execute_instruction(&mut self) -> bool { if self.pc >= self.program.len() { return false; @@ -208,7 +228,7 @@ mod vm_tests { } #[test] - fn test_opcode_hlt() { + fn test_hlt_opcode() { let mut vm = get_test_vm(); let test_bytes = vec![6, 0, 0, 0]; vm.program = test_bytes; @@ -331,7 +351,7 @@ mod vm_tests { } #[test] - fn test_opcode_igl() { + fn test_igl_opcode() { let mut vm = get_test_vm(); let test_bytes = vec![200, 0, 0, 0]; vm.program = test_bytes; @@ -385,5 +405,4 @@ mod vm_tests { vm.run(); assert_eq!(vm.registers[0], 500); } - }