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.
This commit is contained in:
Anthony Foxclaw 2020-02-09 15:16:38 -05:00
parent 76af517a49
commit 86f060e0f5
10 changed files with 120 additions and 31 deletions

View file

@ -1,6 +1,6 @@
# Corten # 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 ## Build Status

View file

@ -1,6 +1,6 @@
# Welcome # 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 ## Specifications

View file

@ -1,14 +1,14 @@
# Specifications # Specifications
## Instruction Set ## Instruction Set
| Opcode | Function | Comment | | Code | Operation | Comment |
| --- | --- | --- | | --- | --- | --- |
| 0 | LOAD | Load program | | 0 | LOAD | Load program |
| 1 | ADD | | 1 | ADD | Add |
| 2 | SUB | | 2 | SUB | Subtract
| 3 | MUL | | 3 | MUL | Multiply |
| 4 | DIV | | 4 | DIV | Division |
| 5 | HLT | Halt | | 5 | HLT | Halt |
| 6 | JMP | Jump | | 6 | JMP | Jump |
| 7 | JMPF | Jump forward | | 7 | JMPF | Jump forward |
@ -20,5 +20,5 @@
| 13 | LTE | Less then or equal to | | 13 | LTE | Less then or equal to |
| 14 | LT | Less then | | 14 | LT | Less then |
| 15 | JMPE | Jump if equal | | 15 | JMPE | Jump if equal |
| 16 | NOP | | 16 | NOP | No Operation |
| _ | IGL | Illegal action | | _ | IGL | Illegal action |

View file

@ -1,4 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use nom::types::CompleteStr;
pub mod instruction_parser; pub mod instruction_parser;
pub mod opcode_parser; pub mod opcode_parser;
pub mod operand_parser; pub mod operand_parser;
@ -92,4 +95,29 @@ impl From<Opcode> for u8 {
Opcode::IGL => 100, Opcode::IGL => 100,
} }
} }
} }
impl<'a> From<CompleteStr<'a>> 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,
}
}
}

View file

@ -1,9 +1,8 @@
use nom::*; use crate::assembler::opcode_parser::opcode;
use crate::assembler::Token;
use crate::assembler::opcode_parser::opcode_load;
use crate::assembler::operand_parser::integer_operand; use crate::assembler::operand_parser::integer_operand;
use crate::assembler::register_parser::register; use crate::assembler::register_parser::register;
use crate::assembler::Token;
use nom::*;
use nom::types::CompleteStr; use nom::types::CompleteStr;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -30,7 +29,6 @@ impl AssemblerInstruction {
} }
}; };
for operand in &[&self.operand1, &self.operand2, &self.operand3] { for operand in &[&self.operand1, &self.operand2, &self.operand3] {
if let Some(token) = operand { if let Some(token) = operand {
AssemblerInstruction::extract_operand(token, &mut results) AssemblerInstruction::extract_operand(token, &mut results)
@ -44,7 +42,7 @@ impl AssemblerInstruction {
match t { match t {
Token::Register { reg_num } => { Token::Register { reg_num } => {
results.push(*reg_num); results.push(*reg_num);
}, }
Token::Number { value } => { Token::Number { value } => {
let conv = *value as u16; let conv = *value as u16;
let byte1 = conv; let byte1 = conv;
@ -52,19 +50,45 @@ impl AssemblerInstruction {
results.push(byte2 as u8); results.push(byte2 as u8);
results.push(byte1 as u8); results.push(byte1 as u8);
}, }
_ => { _ => {
println!("Opcode found in operand field"); println!("Opcode found in operand field");
std::process::exit(1); std::process::exit(1);
} }
}; };
} }
} }
named!(pub instruction_one<CompleteStr, AssemblerInstruction>, named!(instruction_two<CompleteStr, AssemblerInstruction>,
do_parse!( do_parse!(
o: opcode_load >> o: opcode >>
opt!(multispace) >>
(
AssemblerInstruction{
opcode: o,
operand1: None,
operand2: None,
operand3: None
}
)
)
);
named!(pub instruction<CompleteStr, AssemblerInstruction>,
do_parse!(
ins: alt!(
instruction_one |
instruction_two
) >>
(
ins
)
)
);
named!(instruction_one<CompleteStr, AssemblerInstruction>,
do_parse!(
o: opcode >>
r: register >> r: register >>
i: integer_operand >> 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
}
))
);
}
} }

View file

@ -1,11 +1,18 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use nom::*; 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<CompleteStr, Token>, named!(pub opcode<CompleteStr, Token>,
do_parse!(tag!("load") >> (Token::Opcode{code: Opcode::LOAD})) do_parse!(
opcode: alpha1 >>
(
{
Token::Opcode{code: Opcode::from(opcode)}
}
)
)
); );
#[cfg(test)] #[cfg(test)]
@ -15,10 +22,13 @@ mod opcode_parser_test {
#[test] #[test]
fn test_parser_op_load() { fn test_parser_op_load() {
// Test that opcode is dected and parsed correctly // 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); assert_eq!(result.is_ok(), true);
let (rest, token) = result.unwrap(); let (rest, token) = result.unwrap();
assert_eq!(token, Token::Opcode { code: Opcode::LOAD }); assert_eq!(token, Token::Opcode { code: Opcode::LOAD });
assert_eq!(rest, CompleteStr("")); assert_eq!(rest, CompleteStr(""));
let result = opcode(CompleteStr("alod"));
let (_, token) = result.unwrap();
assert_eq!(token, Token::Opcode { code: Opcode::IGL });
} }
} }

View file

@ -21,7 +21,7 @@ mod reg_parser_test {
use super::*; use super::*;
#[test] #[test]
fn test_opcode_load() { fn test_opcode() {
let result = integer_operand(CompleteStr("#10")); let result = integer_operand(CompleteStr("#10"));
let (rest, value) = result.unwrap(); let (rest, value) = result.unwrap();
assert_eq!(rest, CompleteStr("")); assert_eq!(rest, CompleteStr(""));

View file

@ -1,7 +1,7 @@
use nom::types::CompleteStr; use nom::types::CompleteStr;
use nom::*; use nom::*;
use crate::assembler::instruction_parser::{instruction_one, AssemblerInstruction}; use crate::assembler::instruction_parser::{instruction, AssemblerInstruction};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Program { pub struct Program {
@ -20,7 +20,7 @@ impl Program {
named!(pub program<CompleteStr, Program>, named!(pub program<CompleteStr, Program>,
do_parse!( do_parse!(
instructions: many1!(instruction_one) >> instructions: many1!(instruction) >>
( (
Program { Program {
instructions instructions

View file

@ -15,8 +15,9 @@ impl Instruction {
#[cfg(test)] #[cfg(test)]
mod instruction_tests { mod instruction_tests {
use crate::instruction::*;
use crate::assembler::Opcode; use crate::assembler::Opcode;
use crate::instruction::*;
use nom::types::CompleteStr;
#[test] #[test]
fn test_crate_hlt() { fn test_crate_hlt() {
@ -29,4 +30,14 @@ mod instruction_tests {
let inst = Instruction::new(Opcode::HLT); let inst = Instruction::new(Opcode::HLT);
assert_eq!(inst.opcode, 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);
}
} }

View file

@ -2,11 +2,10 @@ use std;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use crate::vm::VM;
use crate::assembler::program_parser::program; use crate::assembler::program_parser::program;
use crate::vm::VM;
use metacrate::crate_version; use metacrate::crate_version;
use rbtag::{BuildDateTime, BuildInfo}; use rbtag::{BuildDateTime, BuildInfo};
use nom::types::CompleteStr;
#[derive(BuildDateTime, BuildInfo)] #[derive(BuildDateTime, BuildInfo)]
struct BuildTag; struct BuildTag;