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:
parent
76af517a49
commit
86f060e0f5
10 changed files with 120 additions and 31 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
14
docs/spec.md
14
docs/spec.md
|
@ -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 |
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(""));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Reference in a new issue