1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! A fantasy virtual machine with limits on resources.
//!
//! ## Instructions
//! | Name | Arguments | Description |
//! |---------------|---------------------------------|-------------------------------------------------------------------------------------|
//! | No Operation | None | Does nothing. |
//! | Push | u8 (8-bit value to push) | Pushes an 8-bit value onto the stack. |
//! | Pop Register | Register (destination register) | Pops a value from the stack into the specified register. |
//! | Push Register | Register (source register) | Pushes the value of the specified register onto the stack. |
//! | Add Stack | None | Adds the top two values on the stack. |
//! | Add Register | Two Registers (operands) | Adds the values of two registers and stores the result in the destination register. |
//! | Signal | u8 (signal value) | Sends a signal with an 8-bit value. |
//! | Jump | u8 (target address) | Jumps to the specified address in the program. |
//! ## Reserved symbols
//! | Symbol | Use |
//! |--------|-------------------|
//! | $ | Hexadecimal value |
//! | % | Binary value |
//! | ^ | Label value |
//!
//! ## *Hopes* for this project
//! - Turing completion
//! - Custom assembly
//! - Possible language implementation (C or Lua)
//! - Video card
//! - Ability to render games
//!
//! # Example usage:
//! ```rust
//! use strawberryvm::prelude::*;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut vm = Machine::new();
//!
//! vm.define_handler(0xF0, |machine| machine.machine_halted = true);
//!
//! write_memory!(vm,
//! 0 => 0x1,
//! 1 => 0xA,
//! 2 => 0x1,
//! 3 => 0x8,
//! 4 => 0x4,
//! 5 => 0x0,
//! 6 => 0x2,
//! 7 => 0x0,
//! 8 => 0x6,
//! 9 => 0xF0
//! );
//!
//! while !vm.machine_halted {
//! vm.step()?;
//! }
//!
//! println!("A = {}", vm.get_register(Register::A));
//!
//! Ok(())
//! }
//! ```
mod macros;
mod op;
mod register;
mod vm;
mod memory;
/// Can be included to get everything useful
/// for the machine
pub mod prelude {
pub use crate::write_memory;
pub use crate::op::*;
pub use crate::register::*;
pub use crate::vm::*;
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use crate::{
register::Register,
vm::{Machine, MEMORY_KILO_BYTES},
write_memory,
};
fn sig_halt(vm: &mut Machine) {
vm.machine_halted = true;
}
// Tests for failure (these should fail!)
#[test]
fn unknown_instruction() {
let mut machine = Machine::new();
machine.memory.write(0, 0xF).unwrap();
assert!(machine.step().is_err());
}
#[test]
fn out_of_bounds() -> Result<(), Box<dyn std::error::Error>> {
let mut machine = Machine::new();
assert!(machine
.memory
.write(u16::try_from(MEMORY_KILO_BYTES * 1024 + 1)?, 0xf)
.is_err());
Ok(())
}
#[test]
fn addition() -> Result<(), Box<dyn std::error::Error>> {
let mut machine = Machine::new();
machine.define_handler(0xf0, sig_halt);
write_memory!(machine,
0 => 0x01,
1 => 0x0a,
2 => 0x01,
3 => 0x08,
4 => 0x10,
5 => 0x00,
6 => 0x02,
7 => 0x00,
8 => 0x0f,
9 => 0xf0
);
machine.step().unwrap(); // PUSH 10
machine.step().unwrap(); // PUSH 8
machine.step().unwrap(); // ADDSTACK
machine.step().unwrap(); // POPREGISTER A
machine.step().unwrap(); // SIGNAL 0xF0
assert_eq!(machine.get_register(Register::A), 18);
Ok(())
}
#[test]
fn subsequent_addition() -> Result<(), Box<dyn std::error::Error>> {
let mut machine = Machine::new();
machine.define_handler(0xf0, sig_halt);
for _ in 1..=2 {
write_memory!(machine,
0 => 0x01,
1 => 0x0a,
2 => 0x01,
3 => 0x08,
4 => 0x10,
5 => 0x00,
6 => 0x02,
7 => 0x00,
8 => 0x0f,
9 => 0xf0
);
machine.step().unwrap(); // PUSH 10
machine.step().unwrap(); // PUSH 8
machine.step().unwrap(); // ADDSTACK
machine.step().unwrap(); // POPREGISTER A
machine.step().unwrap(); // SIGNAL 0xF0
assert_eq!(machine.get_register(Register::A), 18);
}
Ok(())
}
}