initial
This commit is contained in:
commit
cf59d6f88c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "forte"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
84
src/asm.rs
Normal file
84
src/asm.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use crate::{OpKind, Word};
|
||||
use log::*;
|
||||
|
||||
/// Assembler
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum AsmError {
|
||||
ExpectedNum,
|
||||
InvalidOp,
|
||||
}
|
||||
|
||||
fn parse_num(input: &str) -> Result<Word, AsmError> {
|
||||
debug!("Parsing {} as number", input);
|
||||
if let Ok(n) = input.parse::<Word>() {
|
||||
return Ok(n);
|
||||
}
|
||||
if let Ok(n) = Word::from_str_radix(input.trim_start_matches("0x"), 16) {
|
||||
return Ok(n);
|
||||
}
|
||||
Err(AsmError::ExpectedNum)
|
||||
}
|
||||
|
||||
fn parse_op(input: &str) -> Result<Word, AsmError> {
|
||||
debug!("Parsing {} as opcode", input);
|
||||
let op = match input {
|
||||
"push" => OpKind::Push,
|
||||
"pop" => OpKind::Pop,
|
||||
"dup" => OpKind::Dup,
|
||||
"add" => OpKind::Add,
|
||||
"dif" => OpKind::Dif,
|
||||
"mul" => OpKind::Mul,
|
||||
"div" => OpKind::Div,
|
||||
"rem" => OpKind::Rem,
|
||||
"and" => OpKind::And,
|
||||
"or" => OpKind::Or,
|
||||
"xor" => OpKind::Xor,
|
||||
"shr" => OpKind::Shr,
|
||||
"shl" => OpKind::Shl,
|
||||
"beq" => OpKind::Beq,
|
||||
"bne" => OpKind::Bne,
|
||||
"bgt" => OpKind::Bgt,
|
||||
"blt" => OpKind::Blt,
|
||||
"fun" => OpKind::Fun,
|
||||
"call" => OpKind::Call,
|
||||
"ret" => OpKind::Ret,
|
||||
"loop" => OpKind::Loop,
|
||||
"iter" => OpKind::Iter,
|
||||
"exe" => OpKind::Exe,
|
||||
"sto" => OpKind::Sto,
|
||||
"lod" => OpKind::Lod,
|
||||
"len" => OpKind::Len,
|
||||
_ => return Err(AsmError::InvalidOp),
|
||||
};
|
||||
Ok(op as Word)
|
||||
}
|
||||
|
||||
pub fn assemble(input: &str) -> Result<Vec<Word>, AsmError> {
|
||||
let mut last: Option<Word> = None;
|
||||
|
||||
let split = input.split_whitespace();
|
||||
let mut output = Vec::with_capacity(split.size_hint().0);
|
||||
for part in split {
|
||||
// If just parsed op, next should be num
|
||||
if last.is_some() {
|
||||
let num = parse_num(part)?;
|
||||
let op = last.take().unwrap();
|
||||
let word = (op << 64) | num;
|
||||
output.push(word);
|
||||
}
|
||||
// Otherwise, could be op or num
|
||||
else {
|
||||
if let Ok(op) = parse_op(part) {
|
||||
last = Some(op);
|
||||
} else {
|
||||
output.push(parse_num(part)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
if last.is_some() {
|
||||
Err(AsmError::ExpectedNum)
|
||||
} else {
|
||||
Ok(output)
|
||||
}
|
||||
}
|
407
src/main.rs
Normal file
407
src/main.rs
Normal file
|
@ -0,0 +1,407 @@
|
|||
mod asm;
|
||||
mod op;
|
||||
mod util;
|
||||
|
||||
use op::*;
|
||||
use util::*;
|
||||
|
||||
use log::*;
|
||||
pub type Byte = u32;
|
||||
pub type Word = u128;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Phase {
|
||||
Warmup,
|
||||
Recital,
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
ip: Word,
|
||||
ebp: Word,
|
||||
phase: Phase,
|
||||
pub cstack: Vec<Word>,
|
||||
pub pstack: Vec<Word>,
|
||||
pub fstack: Vec<Word>,
|
||||
pub instructions: Vec<Word>,
|
||||
pub mem: Vec<Word>,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ip: 0,
|
||||
ebp: 0,
|
||||
phase: Phase::Warmup,
|
||||
cstack: vec![],
|
||||
pstack: vec![],
|
||||
fstack: vec![],
|
||||
instructions: vec![],
|
||||
mem: vec![0; 1024],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, src: Vec<u128>) {
|
||||
self.instructions = src;
|
||||
}
|
||||
|
||||
fn memset(&mut self, address: Word, val: Word) -> CResult {
|
||||
let address: usize = match address.try_into() {
|
||||
Ok(a) => a,
|
||||
Err(_) => return Err(Error::WriteOob),
|
||||
};
|
||||
let reference = self.mem.get_mut(address).ok_or(Error::WriteOob)?;
|
||||
*reference = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn memget(&mut self, address: Word) -> Result<Word, Error> {
|
||||
let address: usize = match address.try_into() {
|
||||
Ok(a) => a,
|
||||
Err(_) => return Err(Error::WriteOob),
|
||||
};
|
||||
self.mem.get(address).ok_or(Error::WriteOob).copied()
|
||||
}
|
||||
|
||||
fn c_pop(&mut self) -> Result<Word, Error> {
|
||||
match self.cstack.pop() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(Error::StackUnderflow),
|
||||
}
|
||||
}
|
||||
|
||||
fn p_pop(&mut self) -> Result<Word, Error> {
|
||||
match self.pstack.pop() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(Error::StackUnderflow),
|
||||
}
|
||||
}
|
||||
|
||||
fn p_peak(&mut self) -> Result<Word, Error> {
|
||||
match self.pstack.last() {
|
||||
Some(i) => Ok(*i),
|
||||
None => Err(Error::StackUnderflow),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, address: Word) -> CResult {
|
||||
let address: usize = address.try_into().map_err(|_| Error::BadCall)?;
|
||||
let address = *self.fstack.get(address).ok_or(Error::BadCall)?;
|
||||
|
||||
self.cstack.push(self.ip);
|
||||
self.cstack.push(self.cstack.len() as Word);
|
||||
if address >= self.instructions.len() as Word {
|
||||
return Err(Error::BadCall);
|
||||
}
|
||||
self.ip = address;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _return(&mut self) -> CResult {
|
||||
let ebp = self.c_pop()?;
|
||||
let ip = self.c_pop()?;
|
||||
self.ip = ip;
|
||||
self.ebp = ebp;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step(&mut self) -> CResult {
|
||||
let word = self
|
||||
.instructions
|
||||
.get(self.ip as usize)
|
||||
.expect("Ran out of instructions");
|
||||
let instr: Op = match word.try_into() {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
error!("Invalid opcode: {:#x}", word);
|
||||
panic!()
|
||||
},
|
||||
};
|
||||
debug!(
|
||||
"[{:?}]{}: {:?} {}",
|
||||
self.phase, self.ip, instr.kind, instr.var
|
||||
);
|
||||
self.ip += 1;
|
||||
match self.phase {
|
||||
Phase::Warmup => self.step_warmup(instr),
|
||||
Phase::Recital => self.step_recital(instr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step_recital(&mut self, instr: Op) -> CResult {
|
||||
match instr.kind {
|
||||
OpKind::Push => {
|
||||
for _ in 0..instr.var {
|
||||
let num = match self.instructions.get(self.ip as usize) {
|
||||
Some(n) => n,
|
||||
None => return Err(Error::StackUnderflow),
|
||||
};
|
||||
debug!("{}: _ {}", self.ip, num);
|
||||
self.pstack.push(*num);
|
||||
self.ip += 1;
|
||||
}
|
||||
},
|
||||
OpKind::Pop => {
|
||||
for _ in 0..instr.var {
|
||||
self.p_pop()?;
|
||||
}
|
||||
},
|
||||
OpKind::Dup => {
|
||||
let d = self.p_peak().unwrap_or(0);
|
||||
for _ in 0..instr.var {
|
||||
self.pstack.push(d);
|
||||
}
|
||||
},
|
||||
OpKind::Add => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(a.saturating_add(b));
|
||||
}
|
||||
},
|
||||
OpKind::Dif => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(abs_sub(a, b));
|
||||
}
|
||||
},
|
||||
OpKind::Mul => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(a.saturating_mul(b));
|
||||
}
|
||||
},
|
||||
OpKind::Div => {
|
||||
if instr.var == 0 {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(b.checked_div(a).unwrap_or(Word::MAX));
|
||||
} else {
|
||||
let numerator = self.p_pop()?;
|
||||
self.pstack.push(
|
||||
numerator
|
||||
.checked_div(instr.var as Word)
|
||||
.unwrap_or(Word::MAX),
|
||||
)
|
||||
}
|
||||
},
|
||||
OpKind::Rem => {
|
||||
if instr.var == 0 {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self
|
||||
.pstack
|
||||
.push(b.checked_rem_euclid(a).unwrap_or(Word::MAX));
|
||||
} else {
|
||||
let numerator = self.p_pop()?;
|
||||
self.pstack.push(
|
||||
numerator
|
||||
.checked_rem_euclid(instr.var as Word)
|
||||
.unwrap_or(Word::MAX),
|
||||
)
|
||||
}
|
||||
},
|
||||
OpKind::And => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(a & b);
|
||||
}
|
||||
},
|
||||
OpKind::Or => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(a | b);
|
||||
}
|
||||
},
|
||||
OpKind::Xor => {
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(a ^ b);
|
||||
}
|
||||
},
|
||||
OpKind::Shl => {
|
||||
if instr.var == 0 {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(b << a);
|
||||
} else {
|
||||
let top = self.p_pop()?;
|
||||
self.pstack.push(top << instr.var as Word);
|
||||
}
|
||||
},
|
||||
OpKind::Shr => {
|
||||
if instr.var == 0 {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
self.pstack.push(b >> a);
|
||||
} else {
|
||||
let top = self.p_pop()?;
|
||||
self.pstack.push(top >> instr.var as Word);
|
||||
}
|
||||
},
|
||||
OpKind::Beq => {
|
||||
let mut should_branch = false;
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
should_branch |= a != b;
|
||||
self.pstack.push(a);
|
||||
}
|
||||
// Remove extraneous
|
||||
self.p_pop()?;
|
||||
if should_branch {
|
||||
self.ip += 1;
|
||||
self.call(instr.var as Word)?;
|
||||
}
|
||||
},
|
||||
OpKind::Bne => {
|
||||
let mut should_branch = false;
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
should_branch |= a == b;
|
||||
self.pstack.push(a);
|
||||
}
|
||||
// Remove extraneous
|
||||
self.p_pop()?;
|
||||
if should_branch {
|
||||
self.ip += 1;
|
||||
self.call(instr.var as Word)?;
|
||||
}
|
||||
},
|
||||
|
||||
OpKind::Bgt => {
|
||||
let mut should_branch = false;
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
should_branch |= a <= b;
|
||||
self.pstack.push(a);
|
||||
}
|
||||
// Remove extraneous
|
||||
self.p_pop()?;
|
||||
if should_branch {
|
||||
self.ip += 1;
|
||||
self.call(instr.var as Word)?;
|
||||
}
|
||||
},
|
||||
|
||||
OpKind::Blt => {
|
||||
let mut should_branch = false;
|
||||
for _ in 0..instr.var {
|
||||
let a = self.p_pop()?;
|
||||
let b = self.p_pop()?;
|
||||
should_branch |= a <= b;
|
||||
self.pstack.push(a);
|
||||
}
|
||||
// Remove extraneous
|
||||
self.p_pop()?;
|
||||
if should_branch {
|
||||
self.ip += 1;
|
||||
self.call(instr.var as Word)?;
|
||||
}
|
||||
},
|
||||
OpKind::Fun => {
|
||||
// Ignore during execution
|
||||
},
|
||||
OpKind::Call => {
|
||||
self.call(instr.var as Word)?;
|
||||
},
|
||||
OpKind::Ret => {
|
||||
self._return()?;
|
||||
},
|
||||
|
||||
OpKind::Loop => {
|
||||
self.cstack.push(self.ip);
|
||||
self.cstack.push(instr.var as Word);
|
||||
},
|
||||
|
||||
OpKind::Iter => {
|
||||
let counter = self.c_pop()?;
|
||||
let address = self.c_pop()?;
|
||||
if counter.saturating_sub(1) != 0 {
|
||||
self.cstack.push(address);
|
||||
self.cstack.push(counter - 1);
|
||||
}
|
||||
},
|
||||
OpKind::Exe => {
|
||||
// Ignore in execution
|
||||
self.phase = Phase::Recital;
|
||||
// TODO: Maybe break program here?
|
||||
},
|
||||
|
||||
OpKind::Sto => {
|
||||
let word = self.p_pop()?;
|
||||
self.memset(instr.var as Word, word)?;
|
||||
},
|
||||
|
||||
OpKind::Lod => {
|
||||
let word = self.memget(instr.var as Word)?;
|
||||
self.pstack.push(word);
|
||||
},
|
||||
OpKind::Len => {
|
||||
self.pstack.push(self.pstack.len() as Word - self.ebp);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
debug!("{:?}", self.pstack);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step_warmup(&mut self, instr: Op) -> CResult {
|
||||
match instr.kind {
|
||||
OpKind::Fun => self.fstack.push(self.ip),
|
||||
OpKind::Exe => {
|
||||
self.phase = Phase::Recital;
|
||||
self.ip = match self.fstack.last() {
|
||||
Some(i) => *i,
|
||||
None => return Err(Error::StackUnderflow),
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn steps(&mut self, n: usize) -> CResult {
|
||||
for _ in 0..n {
|
||||
self.step()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
flexi_logger::Logger::try_with_str("rh24")
|
||||
.unwrap()
|
||||
.start()
|
||||
.unwrap();
|
||||
|
||||
let mut m = Machine::new();
|
||||
// let program = vec![
|
||||
// //
|
||||
// Op::new(OpKind::Fun, 0),
|
||||
// Op::new(OpKind::Push, 2),
|
||||
// 15,
|
||||
// 30,
|
||||
// Op::new(OpKind::Dif, 1),
|
||||
// Op::new(OpKind::Ret, 0),
|
||||
// Op::new(OpKind::Fun, 0),
|
||||
// Op::new(OpKind::Push, 2),
|
||||
// 16,
|
||||
// 4,
|
||||
// Op::new(OpKind::Dif, 1),
|
||||
// Op::new(OpKind::Call, 0),
|
||||
// Op::new(OpKind::Add, 1),
|
||||
// Op::new(OpKind::Exe, 0),
|
||||
// ];
|
||||
let program = include_str!("./../demo.asm");
|
||||
let program = asm::assemble(program).unwrap();
|
||||
m.read(program);
|
||||
m.steps(32).unwrap();
|
||||
println!("RESULT = {:?}", m.pstack);
|
||||
}
|
112
src/op.rs
Normal file
112
src/op.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use crate::Word;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u64)]
|
||||
pub enum OpKind {
|
||||
// P-Stack
|
||||
Push,
|
||||
Pop,
|
||||
Dup,
|
||||
// Math
|
||||
Add,
|
||||
Dif,
|
||||
Mul,
|
||||
Div,
|
||||
Rem,
|
||||
// Bool
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Shr,
|
||||
Shl,
|
||||
// C-Stack
|
||||
Beq,
|
||||
Bne,
|
||||
Bgt,
|
||||
Blt,
|
||||
// I-Stack
|
||||
/// Begin function def
|
||||
Fun,
|
||||
Call,
|
||||
Ret,
|
||||
/// Begin subroutine
|
||||
Loop,
|
||||
/// Iterate the loop
|
||||
Iter,
|
||||
/// Break loop early
|
||||
Exe,
|
||||
// Memory
|
||||
Sto,
|
||||
Lod,
|
||||
// Misc
|
||||
// Length of the stack currently
|
||||
Len,
|
||||
|
||||
LAST,
|
||||
}
|
||||
|
||||
impl TryFrom<u64> for OpKind {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => Self::Push,
|
||||
1 => Self::Pop,
|
||||
2 => Self::Dup,
|
||||
3 => Self::Add,
|
||||
4 => Self::Dif,
|
||||
5 => Self::Mul,
|
||||
6 => Self::Div,
|
||||
7 => Self::Rem,
|
||||
8 => Self::And,
|
||||
9 => Self::Or,
|
||||
10 => Self::Xor,
|
||||
11 => Self::Shr,
|
||||
12 => Self::Shl,
|
||||
13 => Self::Beq,
|
||||
14 => Self::Bne,
|
||||
15 => Self::Bgt,
|
||||
16 => Self::Blt,
|
||||
17 => Self::Fun,
|
||||
18 => Self::Call,
|
||||
19 => Self::Ret,
|
||||
20 => Self::Loop,
|
||||
21 => Self::Iter,
|
||||
22 => Self::Exe,
|
||||
23 => Self::Sto,
|
||||
24 => Self::Lod,
|
||||
25 => Self::Len,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Op {
|
||||
pub op: u64,
|
||||
pub kind: OpKind,
|
||||
pub var: u64,
|
||||
}
|
||||
|
||||
impl Op {
|
||||
pub fn new(kind: OpKind, var: u64) -> Word {
|
||||
let top = (kind as u64 as u128) << 64;
|
||||
let bottom = var as u128;
|
||||
top | bottom
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&u128> for Op {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &u128) -> Result<Self, Self::Error> {
|
||||
let top = value >> 64;
|
||||
let bottom = value & 0xFFFF_FFFF;
|
||||
let kind: OpKind = (top as u64).try_into()?;
|
||||
Ok(Self {
|
||||
kind,
|
||||
op: top as u64,
|
||||
var: bottom as u64,
|
||||
})
|
||||
}
|
||||
}
|
43
src/util.rs
Normal file
43
src/util.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
|
||||
use crate::{op::Op, Word};
|
||||
|
||||
pub fn abs_sub(a: Word, b: Word) -> Word {
|
||||
let max = u128::max(a, b);
|
||||
let min = u128::min(a, b);
|
||||
max - min
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
StackOverflow,
|
||||
StackUnderflow,
|
||||
StackUnbalanced,
|
||||
WriteOob,
|
||||
ReadOob,
|
||||
BadCall,
|
||||
NoExec,
|
||||
EndReached,
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::StackOverflow => write!(f, "Stack Overflow"),
|
||||
Self::StackUnderflow => write!(f, "Stack Underflow"),
|
||||
Self::StackUnbalanced => write!(f, "Stack Unbalanced"),
|
||||
Self::WriteOob => write!(f, "Memory write out of bounds"),
|
||||
Self::ReadOob => write!(f, "Memory read out of bounds"),
|
||||
Self::BadCall => {
|
||||
write!(f, "Attempted to call function which does not exist")
|
||||
},
|
||||
Self::NoExec => write!(
|
||||
f,
|
||||
"No exec instruction found. Are you trying to execute a library?"
|
||||
),
|
||||
Self::EndReached => write!(f, "Successfully terminated"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type CResult = Result<(), Error>;
|
Loading…
Reference in a new issue