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}--] => 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

View File

@ -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<u32> 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<u32> for Instruction
fn try_from(value: u32) -> Result<Self, Self::Error>
{
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<u32> 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<u32> 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::<BigEndian>()
{
match cursor.read_u16::<BigEndian>()
{
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)
@ -592,3 +601,57 @@ impl IndexMut<u16> for FootVM
&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::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<String> = env::args().collect();
if args.len() < 2
{
_ = stderr().write("No filename supplied".as_bytes())
_ = stderr().write("No filename supplied\n".as_bytes())
}
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