- Created a basic REPL that accepts hex code
- Commented a lot of the code
This commit is contained in:
Anthony Foxclaw 2020-02-06 23:13:00 -05:00
parent 717c99a4a6
commit 263686902b
4 changed files with 126 additions and 8 deletions

View file

@ -1,2 +0,0 @@
mod instruction;
mod vm;

View file

@ -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();
}

94
src/repl.rs Normal file
View file

@ -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<String>,
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<Vec<u8>, ParseIntError> {
let split = i.split(" ").collect::<Vec<&str>>();
let mut results: Vec<u8> = 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)
}
}

View file

@ -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<u8>,
/// The byte of the program being ran
pub program: Vec<u8>,
/// 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);
}
}