Add some tests; use .foot instead of .asm

This commit is contained in:
shylie 2025-03-23 01:40:00 -04:00
parent 96a8152170
commit 2ce34865af
7 changed files with 124 additions and 54 deletions

View File

@ -7,7 +7,7 @@
[r{register: u5}++] => 0b010 @ register [r{register: u5}++] => 0b010 @ register
[r{register: u5}--] => 0b011 @ register [r{register: u5}--] => 0b011 @ register
[r{register: u5}] => 0b100 @ register [r{register: u5}] => 0b100 @ register
[r{register: u5} + {offset: u2} => 0b1 @ offset @ register [r{register: u5} + {offset: u2}] => 0b1 @ offset @ register
} }
#subruledef crflags #subruledef crflags

View File

@ -1,13 +1,13 @@
use std::io::{Cursor}; use std::io::Cursor;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use AddressingMode::*; use AddressingMode::*;
use ConditionalExecutionFlag::*; use ConditionalExecutionFlag::*;
use Instruction::*; use Instruction::*;
use byteorder::{BigEndian, ReadBytesExt}; 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 enum ConditionalExecutionFlag
{ {
Always, Always,
@ -39,7 +39,7 @@ impl TryFrom<u32> for ConditionalExecutionFlag
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug, PartialEq)]
enum AddressingMode enum AddressingMode
{ {
Immediate, Immediate,
@ -101,6 +101,7 @@ enum Instruction
BWNG(ConditionalExecutionFlag, bool, Operand, Operand), BWNG(ConditionalExecutionFlag, bool, Operand, Operand),
ARNG(ConditionalExecutionFlag, bool, Operand, Operand), ARNG(ConditionalExecutionFlag, bool, Operand, Operand),
LONG(ConditionalExecutionFlag, bool, Operand, Operand), LONG(ConditionalExecutionFlag, bool, Operand, Operand),
CONF(ConditionalExecutionFlag, bool, Operand, Operand),
BWOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), BWOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
BAND(ConditionalExecutionFlag, bool, Operand, Operand, Operand), BAND(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
BXOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), BXOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
@ -110,9 +111,9 @@ enum Instruction
CLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), CLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
ADDI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), ADDI(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
SUBT(ConditionalExecutionFlag, bool, Operand, Operand, Operand), SUBT(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MULL(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MULS(ConditionalExecutionFlag, bool, Operand, Operand, Operand), MULS(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MULU(ConditionalExecutionFlag, bool, Operand, Operand, Operand), MULU(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MULR(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
DIVI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), DIVI(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MODU(ConditionalExecutionFlag, bool, Operand, Operand, Operand) MODU(ConditionalExecutionFlag, bool, Operand, Operand, Operand)
} }
@ -124,7 +125,7 @@ impl TryFrom<u32> for Instruction
fn try_from(value: u32) -> Result<Self, Self::Error> fn try_from(value: u32) -> Result<Self, Self::Error>
{ {
let ceflag: ConditionalExecutionFlag = (value >> 29).try_into()?; 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 dst: Operand = (value >> 16).try_into()?;
let opcode = (value >> 24) & 0xF; let opcode = (value >> 24) & 0xF;
match opcode match opcode
@ -139,10 +140,11 @@ impl TryFrom<u32> for Instruction
1 => Ok(BWNG(ceflag, rflag, dst, a)), 1 => Ok(BWNG(ceflag, rflag, dst, a)),
2 => Ok(ARNG(ceflag, rflag, dst, a)), 2 => Ok(ARNG(ceflag, rflag, dst, a)),
3 => Ok(LONG(ceflag, rflag, dst, a)), 3 => Ok(LONG(ceflag, rflag, dst, a)),
4 => Ok(CONF(ceflag, rflag, dst, a)),
_ => Err(()) _ => Err(())
} }
} }
2..14 => 2..15 =>
{ {
let a: Operand = (value & 0xFF).try_into()?; let a: Operand = (value & 0xFF).try_into()?;
let b: Operand = ((value >> 8) & 0xFF).try_into()?; let b: Operand = ((value >> 8) & 0xFF).try_into()?;
@ -157,9 +159,9 @@ impl TryFrom<u32> for Instruction
8 => Ok(CLSH(ceflag, rflag, dst, b, a)), 8 => Ok(CLSH(ceflag, rflag, dst, b, a)),
9 => Ok(ADDI(ceflag, rflag, dst, b, a)), 9 => Ok(ADDI(ceflag, rflag, dst, b, a)),
10 => Ok(SUBT(ceflag, rflag, dst, b, a)), 10 => Ok(SUBT(ceflag, rflag, dst, b, a)),
11 => Ok(MULL(ceflag, rflag, dst, b, a)), 11 => Ok(MULS(ceflag, rflag, dst, b, a)),
12 => Ok(MULS(ceflag, rflag, dst, b, a)), 12 => Ok(MULU(ceflag, rflag, dst, b, a)),
13 => Ok(MULU(ceflag, rflag, dst, b, a)), 13 => Ok(MULR(ceflag, rflag, dst, b, a)),
14 => Ok(DIVI(ceflag, rflag, dst, b, a)), 14 => Ok(DIVI(ceflag, rflag, dst, b, a)),
15 => Ok(MODU(ceflag, rflag, dst, b, a)), 15 => Ok(MODU(ceflag, rflag, dst, b, a)),
_ => Err(()) _ => Err(())
@ -184,7 +186,8 @@ pub struct FootVM
{ {
memory: [u16; MEMORY_SIZE], memory: [u16; MEMORY_SIZE],
pub registers: [u16; 32], pub registers: [u16; 32],
status: Status status: Status,
mult_conf: u8
} }
impl Default for FootVM impl Default for FootVM
@ -249,11 +252,12 @@ impl FootVM
{ {
memory: [0; MEMORY_SIZE], memory: [0; MEMORY_SIZE],
registers: [0; 32], 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(); let mut asm_src = asm_src.to_owned();
asm_src.insert_str(0, ASM_PREFIX); asm_src.insert_str(0, ASM_PREFIX);
@ -273,12 +277,14 @@ impl FootVM
&[virtual_filename] &[virtual_filename]
); );
let report = report;
let binary = assembly.output.map(|o| o.format_binary()).ok_or_else( 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), report.print_all(write, &fileserver, true);
None => ()
} }
} }
)?; )?;
@ -286,17 +292,10 @@ impl FootVM
let mut cursor = Cursor::new(binary); let mut cursor = Cursor::new(binary);
let mut index = 0; let mut index = 0;
loop while let Ok(value) = cursor.read_u16::<BigEndian>()
{ {
match cursor.read_u16::<BigEndian>() self[index] = value;
{ index += 1;
Ok(value) =>
{
self[index] = value;
index += 1;
},
Err(_) => break
}
} }
Ok(()) Ok(())
@ -306,7 +305,7 @@ impl FootVM
{ {
let first_word = self[self.program_counter()]; let first_word = self[self.program_counter()];
let second_word = self[self.program_counter() + 1]; 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); *self.mut_program_counter() = self.program_counter().wrapping_add(2);
match full_instruction_bits.try_into() match full_instruction_bits.try_into()
{ {
@ -320,6 +319,8 @@ impl FootVM
run_instruction!(self, cef, rep, arng, dst, src), run_instruction!(self, cef, rep, arng, dst, src),
Ok(LONG(cef, rep, dst, src)) => Ok(LONG(cef, rep, dst, src)) =>
run_instruction!(self, cef, rep, long, 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)) => Ok(BWOR(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, bwor, dst, a, b), run_instruction!(self, cef, rep, bwor, dst, a, b),
Ok(BAND(cef, rep, dst, b, a)) => Ok(BAND(cef, rep, dst, b, a)) =>
@ -336,14 +337,14 @@ impl FootVM
run_instruction!(self, cef, rep, clsh, dst, a, b), run_instruction!(self, cef, rep, clsh, dst, a, b),
Ok(ADDI(cef, rep, dst, b, a)) => Ok(ADDI(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, addi, dst, a, b), 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), 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)) => Ok(MULS(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, muls, dst, a, b), run_instruction!(self, cef, rep, muls, dst, a, b),
Ok(MULU(cef, rep, dst, b, a)) => Ok(MULU(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, mulu, dst, a, b), 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)) => Ok(DIVI(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, divi, dst, a, b), run_instruction!(self, cef, rep, divi, dst, a, b),
Ok(MODU(cef, rep, dst, b, a)) => 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) fn bwor(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{ {
let a = self.load_operand(src_a); let a = self.load_operand(src_a);
@ -455,25 +464,25 @@ impl FootVM
self.store_operand(dst, a.wrapping_sub(b)); 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) fn muls(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{ {
let a = i32::from(self.load_operand(src_a)); let a = i32::from(self.load_operand(src_a));
let b = i32::from(self.load_operand(src_b)); 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) fn mulu(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{ {
let a = u32::from(self.load_operand(src_a)); let a = u32::from(self.load_operand(src_a));
let b = u32::from(self.load_operand(src_b)); 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) fn divi(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
@ -592,3 +601,57 @@ impl IndexMut<u16> for FootVM
&mut self.memory[index as usize] &mut self.memory[index as usize]
} }
} }
#[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(())
}
}

View File

@ -1,9 +1,7 @@
mod vm;
use std::{env, fs}; use std::{env, fs};
use std::io::{stderr, Write}; use std::io::{stderr, Write};
use std::sync::Arc; use std::sync::Arc;
use vm::FootVM; use footvm::FootVM;
use pixels::{Pixels, SurfaceTexture}; use pixels::{Pixels, SurfaceTexture};
@ -58,7 +56,7 @@ impl App
{ {
let pixel_value = ((value & 0xF) * 17) as u8; let pixel_value = ((value & 0xF) * 17) as u8;
let pixel_index = ((ptr * PIXELS_PER_ADDRESS + k) * 4) as usize; 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 + 1] = pixel_value;
frame[pixel_index + 2] = pixel_value; frame[pixel_index + 2] = pixel_value;
frame[pixel_index + 3] = 0xFF; frame[pixel_index + 3] = 0xFF;
@ -75,14 +73,10 @@ impl ApplicationHandler for App
{ {
fn new_events(&mut self, _: &ActiveEventLoop, cause: StartCause) 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<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 if args.len() < 2
{ {
_ = stderr().write("No filename supplied".as_bytes()) _ = stderr().write("No filename supplied\n".as_bytes())
} }
else else
{ {

12
tests/integration_test.rs Normal file
View File

@ -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(())
}

View File

@ -0,0 +1 @@
CNST. r0, #0xFEED