diff --git a/README.md b/README.md index 87ff5fe..677bd99 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ ![Rust](https://github.com/tonytins/corten/workflows/Rust/badge.svg) [![Build Status](https://travis-ci.org/tonytins/corten.svg?branch=master)](https://travis-ci.org/tonytins/corten) [![Build status](https://ci.appveyor.com/api/projects/status/ffru6ik26j2b87ko?svg=true)](https://ci.appveyor.com/project/tonytins/corten) [![codecov](https://codecov.io/gh/tonytins/corten/branch/master/graph/badge.svg)](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 diff --git a/docs/spec.md b/docs/spec.md new file mode 100644 index 0000000..06e2fa3 --- /dev/null +++ b/docs/spec.md @@ -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 | \ No newline at end of file diff --git a/src/instruction.rs b/src/instruction.rs index c99c802..7f3e701 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -6,6 +6,7 @@ pub enum Opcode { IGL, LOAD, ADD, + SUB, MUL, DIV, JMP, @@ -14,12 +15,13 @@ pub enum Opcode { impl From 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, } } diff --git a/src/vm.rs b/src/vm.rs index 5eabc11..5e77305 100644 --- a/src/vm.rs +++ b/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); + } }