From 76af517a49cafe968ebfd620aaa0776b1d0f4806 Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Sat, 8 Feb 2020 17:00:18 -0500 Subject: [PATCH 1/5] Tweaked Github CI --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5a55ea5..0f5a05c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,6 @@ name: Rust -on: [push] +on: [push, pull_request] jobs: build: From 86f060e0f5ac181e2fb4e4ac467ae78966a3d5b4 Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Sun, 9 Feb 2020 15:16:38 -0500 Subject: [PATCH 2/5] Implemented HLT instruction - Instructions are now detected automatically based on context - Since MIPs can't decide if it wants to be open or not, Corten will be aiming to be more of a RISC-V VM in the long term. --- README.md | 2 +- docs/README.md | 2 +- docs/spec.md | 14 +++---- src/assembler.rs | 30 +++++++++++++- src/assembler/instruction_parser.rs | 61 ++++++++++++++++++++++++----- src/assembler/opcode_parser.rs | 20 +++++++--- src/assembler/operand_parser.rs | 2 +- src/assembler/program_parser.rs | 4 +- src/instruction.rs | 13 +++++- src/repl.rs | 3 +- 10 files changed, 120 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index eb2f85f..3976978 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Corten -Corten is a [MIPS64 Release 6](https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. +Corten is a [RISC-V](https://rv8.io/isa.html) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. ## Build Status diff --git a/docs/README.md b/docs/README.md index dc708f6..4cf0c4a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Welcome -Corten is a [MIPS64 Release 6](https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. +Corten is a [RISC-V](https://rv8.io/isa.html) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. ## Specifications diff --git a/docs/spec.md b/docs/spec.md index fc695a8..29b119b 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -1,14 +1,14 @@ # Specifications -## Instruction Set +## Instruction Set -| Opcode | Function | Comment | +| Code | Operation | Comment | | --- | --- | --- | | 0 | LOAD | Load program | -| 1 | ADD | -| 2 | SUB | -| 3 | MUL | -| 4 | DIV | +| 1 | ADD | Add | +| 2 | SUB | Subtract +| 3 | MUL | Multiply | +| 4 | DIV | Division | | 5 | HLT | Halt | | 6 | JMP | Jump | | 7 | JMPF | Jump forward | @@ -20,5 +20,5 @@ | 13 | LTE | Less then or equal to | | 14 | LT | Less then | | 15 | JMPE | Jump if equal | -| 16 | NOP | +| 16 | NOP | No Operation | | _ | IGL | Illegal action | \ No newline at end of file diff --git a/src/assembler.rs b/src/assembler.rs index 834bbe9..f4a9499 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -1,4 +1,7 @@ #![allow(dead_code)] + +use nom::types::CompleteStr; + pub mod instruction_parser; pub mod opcode_parser; pub mod operand_parser; @@ -92,4 +95,29 @@ impl From for u8 { Opcode::IGL => 100, } } -} \ No newline at end of file +} + +impl<'a> From> for Opcode { + fn from(v: CompleteStr<'a>) -> Self { + match v { + CompleteStr("load") => Opcode::LOAD, + CompleteStr("add") => Opcode::ADD, + CompleteStr("sub") => Opcode::SUB, + CompleteStr("mul") => Opcode::MUL, + CompleteStr("div") => Opcode::DIV, + CompleteStr("hlt") => Opcode::HLT, + CompleteStr("jmp") => Opcode::JMP, + CompleteStr("jmpf") => Opcode::JMPF, + CompleteStr("jmpb") => Opcode::JMPB, + CompleteStr("eq") => Opcode::EQ, + CompleteStr("neq") => Opcode::NEQ, + CompleteStr("gte") => Opcode::GTE, + CompleteStr("gt") => Opcode::GT, + CompleteStr("lte") => Opcode::LTE, + CompleteStr("lt") => Opcode::LT, + CompleteStr("jmpe") => Opcode::JMPE, + CompleteStr("nop") => Opcode::NOP, + _ => Opcode::IGL, + } + } +} diff --git a/src/assembler/instruction_parser.rs b/src/assembler/instruction_parser.rs index ddcdf11..21b1d83 100644 --- a/src/assembler/instruction_parser.rs +++ b/src/assembler/instruction_parser.rs @@ -1,9 +1,8 @@ -use nom::*; -use crate::assembler::Token; -use crate::assembler::opcode_parser::opcode_load; +use crate::assembler::opcode_parser::opcode; use crate::assembler::operand_parser::integer_operand; use crate::assembler::register_parser::register; - +use crate::assembler::Token; +use nom::*; use nom::types::CompleteStr; #[derive(Debug, PartialEq)] @@ -30,7 +29,6 @@ impl AssemblerInstruction { } }; - for operand in &[&self.operand1, &self.operand2, &self.operand3] { if let Some(token) = operand { AssemblerInstruction::extract_operand(token, &mut results) @@ -44,7 +42,7 @@ impl AssemblerInstruction { match t { Token::Register { reg_num } => { results.push(*reg_num); - }, + } Token::Number { value } => { let conv = *value as u16; let byte1 = conv; @@ -52,19 +50,45 @@ impl AssemblerInstruction { results.push(byte2 as u8); results.push(byte1 as u8); - }, + } _ => { println!("Opcode found in operand field"); std::process::exit(1); } }; } - } -named!(pub instruction_one, +named!(instruction_two, do_parse!( - o: opcode_load >> + o: opcode >> + opt!(multispace) >> + ( + AssemblerInstruction{ + opcode: o, + operand1: None, + operand2: None, + operand3: None + } + ) + ) +); + +named!(pub instruction, + do_parse!( + ins: alt!( + instruction_one | + instruction_two + ) >> + ( + ins + ) + ) +); + +named!(instruction_one, + do_parse!( + o: opcode >> r: register >> i: integer_operand >> ( @@ -99,4 +123,21 @@ mod instruction_parser_test { )) ); } + + #[test] + fn test_parse_instruction_form_two() { + let result = instruction_two(CompleteStr("hlt\n")); + assert_eq!( + result, + Ok(( + CompleteStr(""), + AssemblerInstruction { + opcode: Token::Opcode { code: Opcode::HLT }, + operand1: None, + operand2: None, + operand3: None + } + )) + ); + } } diff --git a/src/assembler/opcode_parser.rs b/src/assembler/opcode_parser.rs index 8d4406f..f19e178 100644 --- a/src/assembler/opcode_parser.rs +++ b/src/assembler/opcode_parser.rs @@ -1,11 +1,18 @@ #![allow(unused_imports)] use nom::*; -use nom::{digit, types::CompleteStr}; +use nom::{alpha1, digit, types::CompleteStr}; -use crate::assembler::{Token, Opcode}; +use crate::assembler::{Opcode, Token}; -named!(pub opcode_load, - do_parse!(tag!("load") >> (Token::Opcode{code: Opcode::LOAD})) +named!(pub opcode, + do_parse!( + opcode: alpha1 >> + ( + { + Token::Opcode{code: Opcode::from(opcode)} + } + ) + ) ); #[cfg(test)] @@ -15,10 +22,13 @@ mod opcode_parser_test { #[test] fn test_parser_op_load() { // Test that opcode is dected and parsed correctly - let result = opcode_load(CompleteStr("load")); + let result = opcode(CompleteStr("load")); assert_eq!(result.is_ok(), true); let (rest, token) = result.unwrap(); assert_eq!(token, Token::Opcode { code: Opcode::LOAD }); assert_eq!(rest, CompleteStr("")); + let result = opcode(CompleteStr("alod")); + let (_, token) = result.unwrap(); + assert_eq!(token, Token::Opcode { code: Opcode::IGL }); } } diff --git a/src/assembler/operand_parser.rs b/src/assembler/operand_parser.rs index 110d9fe..dddd28c 100644 --- a/src/assembler/operand_parser.rs +++ b/src/assembler/operand_parser.rs @@ -21,7 +21,7 @@ mod reg_parser_test { use super::*; #[test] - fn test_opcode_load() { + fn test_opcode() { let result = integer_operand(CompleteStr("#10")); let (rest, value) = result.unwrap(); assert_eq!(rest, CompleteStr("")); diff --git a/src/assembler/program_parser.rs b/src/assembler/program_parser.rs index a0ac7bf..ed19fc2 100644 --- a/src/assembler/program_parser.rs +++ b/src/assembler/program_parser.rs @@ -1,7 +1,7 @@ use nom::types::CompleteStr; use nom::*; -use crate::assembler::instruction_parser::{instruction_one, AssemblerInstruction}; +use crate::assembler::instruction_parser::{instruction, AssemblerInstruction}; #[derive(Debug, PartialEq)] pub struct Program { @@ -20,7 +20,7 @@ impl Program { named!(pub program, do_parse!( - instructions: many1!(instruction_one) >> + instructions: many1!(instruction) >> ( Program { instructions diff --git a/src/instruction.rs b/src/instruction.rs index e84f5af..ff68fa4 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -15,8 +15,9 @@ impl Instruction { #[cfg(test)] mod instruction_tests { - use crate::instruction::*; use crate::assembler::Opcode; + use crate::instruction::*; + use nom::types::CompleteStr; #[test] fn test_crate_hlt() { @@ -29,4 +30,14 @@ mod instruction_tests { let inst = Instruction::new(Opcode::HLT); assert_eq!(inst.opcode, Opcode::HLT); } + + #[test] + fn test_str_to_opcode() { + let opcode = Opcode::from(CompleteStr("load")); + assert_eq!(opcode, Opcode::LOAD); + let opcode = Opcode::from(CompleteStr("add")); + assert_eq!(opcode, Opcode::ADD); + let opcode = Opcode::from(CompleteStr("illegal")); + assert_eq!(opcode, Opcode::IGL); + } } diff --git a/src/repl.rs b/src/repl.rs index 2154bcc..1feb3ff 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -2,11 +2,10 @@ use std; use std::io; use std::io::Write; -use crate::vm::VM; use crate::assembler::program_parser::program; +use crate::vm::VM; use metacrate::crate_version; use rbtag::{BuildDateTime, BuildInfo}; -use nom::types::CompleteStr; #[derive(BuildDateTime, BuildInfo)] struct BuildTag; From b825931ce596080979b67575828f8f4a84a23b51 Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Sun, 9 Feb 2020 16:41:13 -0500 Subject: [PATCH 3/5] Support for memory allocation - Corten now has basic support for memory allocation through with the ALOC code. --- src/assembler.rs | 5 +++++ src/assembler/instruction_parser.rs | 2 +- src/vm.rs | 21 ++++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/assembler.rs b/src/assembler.rs index f4a9499..fd0a75d 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -44,6 +44,8 @@ pub enum Opcode { JMPF, /// Jump backward JMPB, + /// Allocate memory + ALOC, NOP, } @@ -67,6 +69,7 @@ impl From for Opcode { 14 => Opcode::LT, 15 => Opcode::JMPE, 16 => Opcode::NOP, + 17 => Opcode::ALOC, _ => Opcode::IGL, } } @@ -92,6 +95,7 @@ impl From for u8 { Opcode::GT => 14, Opcode::JMPE => 15, Opcode::NOP => 16, + Opcode::ALOC => 17, Opcode::IGL => 100, } } @@ -116,6 +120,7 @@ impl<'a> From> for Opcode { CompleteStr("lte") => Opcode::LTE, CompleteStr("lt") => Opcode::LT, CompleteStr("jmpe") => Opcode::JMPE, + CompleteStr("aloc") => Opcode::ALOC, CompleteStr("nop") => Opcode::NOP, _ => Opcode::IGL, } diff --git a/src/assembler/instruction_parser.rs b/src/assembler/instruction_parser.rs index 21b1d83..0c9cd1a 100644 --- a/src/assembler/instruction_parser.rs +++ b/src/assembler/instruction_parser.rs @@ -140,4 +140,4 @@ mod instruction_parser_test { )) ); } -} +} \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs index 6bb1595..9dfe1c8 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -8,7 +8,9 @@ pub struct VM { pc: usize, /// The byte of the program being ran pub program: Vec, - /// The remainer of the module used in the division opcode + /// Memory allocation + heap: Vec, + /// The remainder of the module used in the division opcode remainder: u32, /// The result of the last comparison operation equal_flag: bool, @@ -21,6 +23,7 @@ impl VM { registers: [0; 32], pc: 0, program: vec![], + heap: vec![], remainder: 0, equal_flag: false, } @@ -204,6 +207,12 @@ impl VM { self.next_8_bits(); self.next_8_bits(); } + Opcode::ALOC => { + let reg = self.next_8_bits() as usize; + let bytes = self.registers[reg]; + let new_end = self.heap.len() as i32 + bytes; + self.heap.resize(new_end as usize, 0); + } } true } @@ -260,6 +269,16 @@ mod vm_tests { assert_eq!(vm.registers[2], 2); } + #[test] + fn test_aloc_opcode() + { + let mut vm = get_test_vm(); + vm.registers[0] = 1024; + vm.program = vec![17, 0, 0, 0]; + vm.run_once(); + assert_eq!(vm.heap.len(), 1024); + } + #[test] fn test_eq_opcode() { let mut vm = get_test_vm(); From 13069b7ebded2c00e732a7f0e197beea72d541de Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Sun, 9 Feb 2020 18:43:23 -0500 Subject: [PATCH 4/5] Fixed a few old bugs - Fixed a where the VM excuted one loop regardless of instructions. - Use the full 32 bits if an instruction is less. --- src/assembler/instruction_parser.rs | 4 ++++ src/vm.rs | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/assembler/instruction_parser.rs b/src/assembler/instruction_parser.rs index 0c9cd1a..dbd5316 100644 --- a/src/assembler/instruction_parser.rs +++ b/src/assembler/instruction_parser.rs @@ -35,6 +35,10 @@ impl AssemblerInstruction { } } + while results.len() < 4 { + results.push(0); + } + results } diff --git a/src/vm.rs b/src/vm.rs index 9dfe1c8..9930d44 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -74,7 +74,7 @@ impl VM { /// various public functions. fn execute_instruction(&mut self) -> bool { if self.pc >= self.program.len() { - return false; + return true; } match self.decode_opcode() { Opcode::LOAD => { @@ -84,11 +84,11 @@ impl VM { } Opcode::HLT => { println!("HLT encountered"); - return false; + return true; } Opcode::IGL => { println!("Unrecognized opcode found! Terminating!"); - return false; + return true; } Opcode::ADD => { let reg1 = self.registers[self.next_8_bits() as usize]; @@ -214,7 +214,7 @@ impl VM { self.heap.resize(new_end as usize, 0); } } - true + false } } From 4dfb99568fe90472639425afb51161a1a5f2afb3 Mon Sep 17 00:00:00 2001 From: Anthony Foxclaw <35226681+tonytins@users.noreply.github.com> Date: Sun, 9 Feb 2020 19:33:00 -0500 Subject: [PATCH 5/5] We're just a RISC virtual machine --- README.md | 2 +- docs/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3976978..3228f7f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Corten -Corten is a [RISC-V](https://rv8.io/isa.html) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. +Corten is a RISC virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. ## Build Status diff --git a/docs/README.md b/docs/README.md index 4cf0c4a..2a558a4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Welcome -Corten is a [RISC-V](https://rv8.io/isa.html) virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. +Corten is a RISC virtual machine written in Rust as a hobby and based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. ## Specifications