REPL!
- Created a basic REPL that accepts hex code - Commented a lot of the code
This commit is contained in:
parent
717c99a4a6
commit
263686902b
4 changed files with 126 additions and 8 deletions
|
@ -1,2 +0,0 @@
|
|||
mod instruction;
|
||||
mod vm;
|
|
@ -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
94
src/repl.rs
Normal 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)
|
||||
}
|
||||
}
|
29
src/vm.rs
29
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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Reference in a new issue