From 2ce34865af5fd383f0cab1a27d053d4afe3cbbe4 Mon Sep 17 00:00:00 2001 From: shylie Date: Sun, 23 Mar 2025 01:40:00 -0400 Subject: [PATCH] Add some tests; use .foot instead of .asm --- examples/{first.asm => first.foot} | 0 examples/{mandelbrot.asm => mandelbrot.foot} | 0 src/{foot.asm => foot.foot} | 2 +- src/{vm.rs => lib.rs} | 145 +++++++++++++------ src/main.rs | 18 +-- tests/integration_test.rs | 12 ++ tests/programs/basic.foot | 1 + 7 files changed, 124 insertions(+), 54 deletions(-) rename examples/{first.asm => first.foot} (100%) rename examples/{mandelbrot.asm => mandelbrot.foot} (100%) rename src/{foot.asm => foot.foot} (97%) rename src/{vm.rs => lib.rs} (83%) create mode 100644 tests/integration_test.rs create mode 100644 tests/programs/basic.foot diff --git a/examples/first.asm b/examples/first.foot similarity index 100% rename from examples/first.asm rename to examples/first.foot diff --git a/examples/mandelbrot.asm b/examples/mandelbrot.foot similarity index 100% rename from examples/mandelbrot.asm rename to examples/mandelbrot.foot diff --git a/src/foot.asm b/src/foot.foot similarity index 97% rename from src/foot.asm rename to src/foot.foot index 782a647..d691e7b 100644 --- a/src/foot.asm +++ b/src/foot.foot @@ -7,7 +7,7 @@ [r{register: u5}++] => 0b010 @ register [r{register: u5}--] => 0b011 @ register [r{register: u5}] => 0b100 @ register - [r{register: u5} + {offset: u2} => 0b1 @ offset @ register + [r{register: u5} + {offset: u2}] => 0b1 @ offset @ register } #subruledef crflags diff --git a/src/vm.rs b/src/lib.rs similarity index 83% rename from src/vm.rs rename to src/lib.rs index 1395890..8d2df1a 100644 --- a/src/vm.rs +++ b/src/lib.rs @@ -1,13 +1,13 @@ -use std::io::{Cursor}; +use std::io::Cursor; use std::ops::{Index, IndexMut}; use AddressingMode::*; use ConditionalExecutionFlag::*; use Instruction::*; use byteorder::{BigEndian, ReadBytesExt}; -const ASM_PREFIX: &str = include_str!("foot.asm"); +const ASM_PREFIX: &str = include_str!("foot.foot"); -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] enum ConditionalExecutionFlag { Always, @@ -39,7 +39,7 @@ impl TryFrom for ConditionalExecutionFlag } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] enum AddressingMode { Immediate, @@ -101,6 +101,7 @@ enum Instruction BWNG(ConditionalExecutionFlag, bool, Operand, Operand), ARNG(ConditionalExecutionFlag, bool, Operand, Operand), LONG(ConditionalExecutionFlag, bool, Operand, Operand), + CONF(ConditionalExecutionFlag, bool, Operand, Operand), BWOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), BAND(ConditionalExecutionFlag, bool, Operand, Operand, Operand), BXOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), @@ -110,9 +111,9 @@ enum Instruction CLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), ADDI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), SUBT(ConditionalExecutionFlag, bool, Operand, Operand, Operand), - MULL(ConditionalExecutionFlag, bool, Operand, Operand, Operand), MULS(ConditionalExecutionFlag, bool, Operand, Operand, Operand), MULU(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + MULR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), DIVI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), MODU(ConditionalExecutionFlag, bool, Operand, Operand, Operand) } @@ -124,7 +125,7 @@ impl TryFrom for Instruction fn try_from(value: u32) -> Result { let ceflag: ConditionalExecutionFlag = (value >> 29).try_into()?; - let rflag: bool = (value >> 28 & 1) == 1; + let rflag: bool = ((value >> 28) & 1) == 1; let dst: Operand = (value >> 16).try_into()?; let opcode = (value >> 24) & 0xF; match opcode @@ -139,10 +140,11 @@ impl TryFrom for Instruction 1 => Ok(BWNG(ceflag, rflag, dst, a)), 2 => Ok(ARNG(ceflag, rflag, dst, a)), 3 => Ok(LONG(ceflag, rflag, dst, a)), + 4 => Ok(CONF(ceflag, rflag, dst, a)), _ => Err(()) } } - 2..14 => + 2..15 => { let a: Operand = (value & 0xFF).try_into()?; let b: Operand = ((value >> 8) & 0xFF).try_into()?; @@ -157,9 +159,9 @@ impl TryFrom for Instruction 8 => Ok(CLSH(ceflag, rflag, dst, b, a)), 9 => Ok(ADDI(ceflag, rflag, dst, b, a)), 10 => Ok(SUBT(ceflag, rflag, dst, b, a)), - 11 => Ok(MULL(ceflag, rflag, dst, b, a)), - 12 => Ok(MULS(ceflag, rflag, dst, b, a)), - 13 => Ok(MULU(ceflag, rflag, dst, b, a)), + 11 => Ok(MULS(ceflag, rflag, dst, b, a)), + 12 => Ok(MULU(ceflag, rflag, dst, b, a)), + 13 => Ok(MULR(ceflag, rflag, dst, b, a)), 14 => Ok(DIVI(ceflag, rflag, dst, b, a)), 15 => Ok(MODU(ceflag, rflag, dst, b, a)), _ => Err(()) @@ -184,7 +186,8 @@ pub struct FootVM { memory: [u16; MEMORY_SIZE], pub registers: [u16; 32], - status: Status + status: Status, + mult_conf: u8 } impl Default for FootVM @@ -249,11 +252,12 @@ impl FootVM { memory: [0; MEMORY_SIZE], registers: [0; 32], - status: Status::Equal + status: Status::Equal, + mult_conf: 0 } } - pub fn load(&mut self, asm_src: &str, write: Option<&mut dyn std::io::Write>) -> Result<(), ()> + pub fn load(&mut self, asm_src: &str, write_opt: Option<&mut dyn std::io::Write>) -> Result<(), ()> { let mut asm_src = asm_src.to_owned(); asm_src.insert_str(0, ASM_PREFIX); @@ -273,12 +277,14 @@ impl FootVM &[virtual_filename] ); + let report = report; + let binary = assembly.output.map(|o| o.format_binary()).ok_or_else( - || { - match write + || + { + if let Some(write) = write_opt { - Some(w) => report.print_all(w, &mut fileserver, true), - None => () + report.print_all(write, &fileserver, true); } } )?; @@ -286,17 +292,10 @@ impl FootVM let mut cursor = Cursor::new(binary); let mut index = 0; - loop + while let Ok(value) = cursor.read_u16::() { - match cursor.read_u16::() - { - Ok(value) => - { - self[index] = value; - index += 1; - }, - Err(_) => break - } + self[index] = value; + index += 1; } Ok(()) @@ -306,7 +305,7 @@ impl FootVM { let first_word = self[self.program_counter()]; let second_word = self[self.program_counter() + 1]; - let full_instruction_bits = u32::from(second_word) << 16 | u32::from(first_word); + let full_instruction_bits = (u32::from(second_word) << 16) | u32::from(first_word); *self.mut_program_counter() = self.program_counter().wrapping_add(2); match full_instruction_bits.try_into() { @@ -320,6 +319,8 @@ impl FootVM run_instruction!(self, cef, rep, arng, dst, src), Ok(LONG(cef, rep, dst, src)) => run_instruction!(self, cef, rep, long, dst, src), + Ok(CONF(cef, rep, dst, src)) => + run_instruction!(self, cef, rep, conf, dst, src), Ok(BWOR(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, bwor, dst, a, b), Ok(BAND(cef, rep, dst, b, a)) => @@ -336,14 +337,14 @@ impl FootVM run_instruction!(self, cef, rep, clsh, dst, a, b), Ok(ADDI(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, addi, dst, a, b), - Ok(SUBT(cef, rep, dst, b, a)) => + Ok(SUBT(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, subt, dst, a, b), - Ok(MULL(cef, rep, dst, b, a)) => - run_instruction!(self, cef, rep, mull, dst, a, b), Ok(MULS(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, muls, dst, a, b), Ok(MULU(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, mulu, dst, a, b), + Ok(MULR(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, mulr, dst, a, b), Ok(DIVI(cef, rep, dst, b, a)) => run_instruction!(self, cef, rep, divi, dst, a, b), Ok(MODU(cef, rep, dst, b, a)) => @@ -392,6 +393,14 @@ impl FootVM } } + fn conf(&mut self, dst: Operand, src: Operand) + { + self.store_operand(dst, u16::from(self.mult_conf)); + + let a = self.load_operand(src); + self.mult_conf = (a & 0xF) as u8; + } + fn bwor(&mut self, dst: Operand, src_a: Operand, src_b: Operand) { let a = self.load_operand(src_a); @@ -455,25 +464,25 @@ impl FootVM self.store_operand(dst, a.wrapping_sub(b)); } - fn mull(&mut self, dst: Operand, src_a: Operand, src_b: Operand) - { - let a = u32::from(self.load_operand(src_a)); - let b = u32::from(self.load_operand(src_b)); - self.store_operand(dst, (a * b) as u16); - } - fn muls(&mut self, dst: Operand, src_a: Operand, src_b: Operand) { let a = i32::from(self.load_operand(src_a)); let b = i32::from(self.load_operand(src_b)); - self.store_operand(dst, ((a * b) >> 16) as u16); + self.store_operand(dst, ((a * b) >> self.mult_conf) as u16); } fn mulu(&mut self, dst: Operand, src_a: Operand, src_b: Operand) { let a = u32::from(self.load_operand(src_a)); let b = u32::from(self.load_operand(src_b)); - self.store_operand(dst,((a * b) >> 16) as u16); + self.store_operand(dst, ((a * b) >> self.mult_conf) as u16); + } + + fn mulr(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = u32::from(self.load_operand(src_a)); + let b = u32::from(self.load_operand(src_b)); + self.store_operand(dst, (a * b) as u16); } fn divi(&mut self, dst: Operand, src_a: Operand, src_b: Operand) @@ -591,4 +600,58 @@ impl IndexMut for FootVM { &mut self.memory[index as usize] } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests +{ + use super::*; + + #[test] + fn test_conditional_execution_flag_try_from_u32() -> Result<(), ()> + { + let flag = ConditionalExecutionFlag::try_from(0)?; + assert_eq!(flag, Always); + + let flag = ConditionalExecutionFlag::try_from(1)?; + assert_eq!(flag, LessThan); + + let flag = ConditionalExecutionFlag::try_from(2)?; + assert_eq!(flag, LessThanOrEqual); + + let flag = ConditionalExecutionFlag::try_from(3)?; + assert_eq!(flag, Equal); + + let flag = ConditionalExecutionFlag::try_from(4)?; + assert_eq!(flag, GreaterThanOrEqual); + + let flag = ConditionalExecutionFlag::try_from(5)?; + assert_eq!(flag, GreaterThan); + + Ok(()) + } + + #[test] + fn test_addressing_mode_try_from_u32() -> Result<(), ()> + { + let mode = AddressingMode::try_from(0)?; + assert_eq!(mode, Immediate); + + let mode = AddressingMode::try_from(1)?; + assert_eq!(mode, Direct); + + let mode = AddressingMode::try_from(2)?; + assert_eq!(mode, IndirectAutoIncrement); + + let mode = AddressingMode::try_from(3)?; + assert_eq!(mode, IndirectAutoDecrement); + + for i in 4..8 + { + let mode = AddressingMode::try_from(i)?; + assert_eq!(mode, Indirect((i - 4) as u8)); + } + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 3ce0515..87224c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ -mod vm; - use std::{env, fs}; use std::io::{stderr, Write}; use std::sync::Arc; -use vm::FootVM; +use footvm::FootVM; use pixels::{Pixels, SurfaceTexture}; @@ -58,7 +56,7 @@ impl App { let pixel_value = ((value & 0xF) * 17) as u8; let pixel_index = ((ptr * PIXELS_PER_ADDRESS + k) * 4) as usize; - frame[pixel_index + 0] = pixel_value; + frame[pixel_index ] = pixel_value; frame[pixel_index + 1] = pixel_value; frame[pixel_index + 2] = pixel_value; frame[pixel_index + 3] = 0xFF; @@ -75,14 +73,10 @@ impl ApplicationHandler for App { fn new_events(&mut self, _: &ActiveEventLoop, cause: StartCause) { - match cause + if cause == StartCause::Poll { - StartCause::Poll => - { - self.update(); - self.draw() - }, - _ => {} + self.update(); + self.draw() } } @@ -119,7 +113,7 @@ fn main() let args: Vec = env::args().collect(); if args.len() < 2 { - _ = stderr().write("No filename supplied".as_bytes()) + _ = stderr().write("No filename supplied\n".as_bytes()) } else { diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..c252ee4 --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,12 @@ +#[test] +fn test_basic() -> Result<(), ()> +{ + let mut vm: footvm::FootVM = Default::default(); + vm.load(include_str!("programs/basic.foot"), None)?; + + vm.cycle(); + + assert_eq!(vm.registers[0], 0xFEED); + + Ok(()) +} diff --git a/tests/programs/basic.foot b/tests/programs/basic.foot new file mode 100644 index 0000000..57f720e --- /dev/null +++ b/tests/programs/basic.foot @@ -0,0 +1 @@ +CNST. r0, #0xFEED