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() {
|
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;
|
use crate::instruction::Opcode;
|
||||||
|
|
||||||
pub struct VM {
|
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,
|
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,
|
remainder: u32,
|
||||||
|
/// The result of the last comparison operation
|
||||||
equal_flag: bool,
|
equal_flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
|
/// Creates and returns a new VM
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
VM {
|
VM {
|
||||||
registers: [0; 32],
|
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 {
|
fn decode_opcode(&mut self) -> Opcode {
|
||||||
let opcode = Opcode::from(self.program[self.pc]);
|
let opcode = Opcode::from(self.program[self.pc]);
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
opcode
|
opcode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to decode the byte into an opcode
|
||||||
fn next_8_bits(&mut self) -> u8 {
|
fn next_8_bits(&mut self) -> u8 {
|
||||||
let result = self.program[self.pc];
|
let result = self.program[self.pc];
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Grabs the next 16 bits (2 bytes)
|
||||||
fn next_16_bits(&mut self) -> u16 {
|
fn next_16_bits(&mut self) -> u16 {
|
||||||
let result = ((self.program[self.pc] as u16) << 8) | self.program[self.pc + 1] as u16;
|
let result = ((self.program[self.pc] as u16) << 8) | self.program[self.pc + 1] as u16;
|
||||||
self.pc += 2;
|
self.pc += 2;
|
||||||
result
|
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) {
|
pub fn run(&mut self) {
|
||||||
let mut is_done = false;
|
let mut is_done = false;
|
||||||
while !is_done {
|
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) {
|
pub fn run_once(&mut self) {
|
||||||
self.execute_instruction();
|
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 {
|
fn execute_instruction(&mut self) -> bool {
|
||||||
if self.pc >= self.program.len() {
|
if self.pc >= self.program.len() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -208,7 +228,7 @@ mod vm_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opcode_hlt() {
|
fn test_hlt_opcode() {
|
||||||
let mut vm = get_test_vm();
|
let mut vm = get_test_vm();
|
||||||
let test_bytes = vec![6, 0, 0, 0];
|
let test_bytes = vec![6, 0, 0, 0];
|
||||||
vm.program = test_bytes;
|
vm.program = test_bytes;
|
||||||
|
@ -331,7 +351,7 @@ mod vm_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opcode_igl() {
|
fn test_igl_opcode() {
|
||||||
let mut vm = get_test_vm();
|
let mut vm = get_test_vm();
|
||||||
let test_bytes = vec![200, 0, 0, 0];
|
let test_bytes = vec![200, 0, 0, 0];
|
||||||
vm.program = test_bytes;
|
vm.program = test_bytes;
|
||||||
|
@ -385,5 +405,4 @@ mod vm_tests {
|
||||||
vm.run();
|
vm.run();
|
||||||
assert_eq!(vm.registers[0], 500);
|
assert_eq!(vm.registers[0], 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue