Rearranaged opcode order
- Rearranaged opcodes to match Iridium's - Grabbed the get_test_vm() function from the Iridium 1 source to keep up with the tutorial - Specifications page with details regarding Iridium and Corten's instruction set list
This commit is contained in:
parent
ee08374252
commit
c1bdbc78c5
4 changed files with 68 additions and 28 deletions
|
@ -2,7 +2,11 @@
|
|||
|
||||
 [](https://travis-ci.org/tonytins/corten) [](https://ci.appveyor.com/project/tonytins/corten) [](https://codecov.io/gh/tonytins/corten)
|
||||
|
||||
A stack-based virtual machine initially based on [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial.
|
||||
Corten is a stack-based virtual machine written in Rust.
|
||||
|
||||
## Specifications
|
||||
|
||||
See [specifications](docs/spec.md) page.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
|
20
docs/spec.md
Normal file
20
docs/spec.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Specifications
|
||||
|
||||
## Iridium
|
||||
|
||||
Corten is based on Fletcher Haynes's [So you want to build a language VM](https://blog.subnetzero.io/post/building-language-vm-part-01/) tutorial. His virtual machine used for the tutorial is known as [Iridium](https://github.com/fhaynes/iridium). Despite it's origins, it does aim to be full a fledged virtual machine and is already on it's [third iteration](https://gitlab.com/fletchercp/iridium3) with support for SSH, PIDs and Strings. Corten aims to be Iridium 1-compatible.
|
||||
|
||||
## Instruction Set
|
||||
|
||||
| Register | Opcode |
|
||||
| --- | --- |
|
||||
| 0 | LOAD |
|
||||
| 1 | ADD |
|
||||
| 2 | SUB |
|
||||
| 3 | MUL |
|
||||
| 4 | DIV |
|
||||
| 6 | HLT |
|
||||
| 5 | JMP |
|
||||
| 8 | JMPF |
|
||||
| 9 | JMPB |
|
||||
| _ | IGL |
|
|
@ -6,6 +6,7 @@ pub enum Opcode {
|
|||
IGL,
|
||||
LOAD,
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
JMP,
|
||||
|
@ -14,12 +15,13 @@ pub enum Opcode {
|
|||
impl From<u8> for Opcode {
|
||||
fn from(vm: u8) -> Self {
|
||||
match vm {
|
||||
0 => Opcode::HLT,
|
||||
1 => Opcode::LOAD,
|
||||
2 => Opcode::JMP,
|
||||
3 => Opcode::ADD,
|
||||
4 => Opcode::MUL,
|
||||
5 => Opcode::DIV,
|
||||
0 => Opcode::LOAD,
|
||||
1 => Opcode::ADD,
|
||||
2 => Opcode::SUB,
|
||||
3 => Opcode::MUL,
|
||||
4 => Opcode::DIV,
|
||||
6 => Opcode::HLT,
|
||||
7 => Opcode::JMP,
|
||||
_ => Opcode::IGL,
|
||||
}
|
||||
}
|
||||
|
|
56
src/vm.rs
56
src/vm.rs
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
use crate::instruction::Opcode;
|
||||
|
||||
pub struct VM {
|
||||
|
@ -53,7 +54,7 @@ impl VM {
|
|||
match self.decode_opcode() {
|
||||
Opcode::LOAD => {
|
||||
let reg = self.next_8_bits() as usize;
|
||||
let num = self.next_16_bits() as u32;
|
||||
let num = self.next_16_bits() as u16;
|
||||
self.registers[reg] = num as i32;
|
||||
}
|
||||
Opcode::HLT => {
|
||||
|
@ -83,6 +84,11 @@ impl VM {
|
|||
let target = self.registers[self.next_8_bits() as usize];
|
||||
self.pc = target as usize;
|
||||
}
|
||||
Opcode::SUB => {
|
||||
let reg1 = self.registers[self.next_8_bits() as usize];
|
||||
let reg2 = self.registers[self.next_8_bits() as usize];
|
||||
self.registers[self.next_8_bits() as usize] = reg1 - reg2;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -92,6 +98,14 @@ impl VM {
|
|||
mod vm_tests {
|
||||
use crate::vm::*;
|
||||
|
||||
fn get_test_vm() -> VM {
|
||||
let mut test_vm = VM::new();
|
||||
test_vm.registers[0] = 5;
|
||||
test_vm.registers[1] = 10;
|
||||
|
||||
test_vm
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crate_vm() {
|
||||
let test_vm = VM::new();
|
||||
|
@ -100,37 +114,37 @@ mod vm_tests {
|
|||
|
||||
#[test]
|
||||
fn test_opcode_hlt() {
|
||||
let mut test_vm = VM::new();
|
||||
let test_bytes = vec![0, 0, 0, 0];
|
||||
test_vm.program = test_bytes;
|
||||
test_vm.run_once();
|
||||
assert_eq!(test_vm.pc, 1);
|
||||
let mut vm = get_test_vm();
|
||||
let test_bytes = vec![6, 0, 0, 0];
|
||||
vm.program = test_bytes;
|
||||
vm.run_once();
|
||||
assert_eq!(vm.pc, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opcode_igl() {
|
||||
let mut test_vm = VM::new();
|
||||
let mut vm = get_test_vm();
|
||||
let test_bytes = vec![200, 0, 0, 0];
|
||||
test_vm.program = test_bytes;
|
||||
test_vm.run_once();
|
||||
assert_eq!(test_vm.pc, 1);
|
||||
vm.program = test_bytes;
|
||||
vm.run_once();
|
||||
assert_eq!(vm.pc, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jmp_opcode() {
|
||||
let mut test_vm = VM::new();
|
||||
test_vm.registers[0] = 1;
|
||||
test_vm.program = vec![7, 0, 0, 0];
|
||||
test_vm.run_once();
|
||||
assert_eq!(test_vm.pc, 1);
|
||||
let mut vm = get_test_vm();
|
||||
vm.registers[0] = 1;
|
||||
vm.program = vec![7, 0, 0, 0];
|
||||
vm.run_once();
|
||||
assert_eq!(vm.pc, 1);
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
#[test]
|
||||
fn test_load_opcode() {
|
||||
let mut test_vm = VM::new();
|
||||
let mut vm = get_test_vm();
|
||||
// 500 is represented this way in little endian format
|
||||
test_vm.program = vec![0, 0, 1, 244];
|
||||
test_vm.run();
|
||||
assert_eq!(test_vm.registers[0], 500);
|
||||
}*/
|
||||
vm.program = vec![0, 0, 1, 244];
|
||||
vm.run();
|
||||
assert_eq!(vm.registers[0], 500);
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue