commit cf59d6f88c6e25ef76c364c9abfc4f3e478ac805 Author: voidNUL <50534996+Xterminate1818@users.noreply.github.com> Date: Sat Feb 24 23:00:45 2024 -0600 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5dec02f --- /dev/null +++ b/Cargo.toml @@ -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] diff --git a/src/asm.rs b/src/asm.rs new file mode 100644 index 0000000..ccc64e6 --- /dev/null +++ b/src/asm.rs @@ -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 { + debug!("Parsing {} as number", input); + if let Ok(n) = input.parse::() { + 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 { + 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, AsmError> { + let mut last: Option = 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) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5feced3 --- /dev/null +++ b/src/main.rs @@ -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, + pub pstack: Vec, + pub fstack: Vec, + pub instructions: Vec, + pub mem: Vec, +} + +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) { + 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 { + 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 { + match self.cstack.pop() { + Some(i) => Ok(i), + None => Err(Error::StackUnderflow), + } + } + + fn p_pop(&mut self) -> Result { + match self.pstack.pop() { + Some(i) => Ok(i), + None => Err(Error::StackUnderflow), + } + } + + fn p_peak(&mut self) -> Result { + 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); +} diff --git a/src/op.rs b/src/op.rs new file mode 100644 index 0000000..9282889 --- /dev/null +++ b/src/op.rs @@ -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 for OpKind { + type Error = (); + + fn try_from(value: u64) -> Result { + 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 { + 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, + }) + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..fce496c --- /dev/null +++ b/src/util.rs @@ -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>;