Add some tests; use .foot instead of .asm
This commit is contained in:
parent
96a8152170
commit
2ce34865af
@ -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
|
@ -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
|
||||
||
|
||||
{
|
||||
Some(w) => report.print_all(w, &mut fileserver, true),
|
||||
None => ()
|
||||
if let Some(write) = write_opt
|
||||
{
|
||||
report.print_all(write, &fileserver, true);
|
||||
}
|
||||
}
|
||||
)?;
|
||||
@ -286,17 +292,10 @@ impl FootVM
|
||||
let mut cursor = Cursor::new(binary);
|
||||
let mut index = 0;
|
||||
|
||||
loop
|
||||
{
|
||||
match cursor.read_u16::<BigEndian>()
|
||||
{
|
||||
Ok(value) =>
|
||||
while let Ok(value) = cursor.read_u16::<BigEndian>()
|
||||
{
|
||||
self[index] = value;
|
||||
index += 1;
|
||||
},
|
||||
Err(_) => break
|
||||
}
|
||||
}
|
||||
|
||||
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)) =>
|
||||
@ -338,12 +339,12 @@ impl FootVM
|
||||
run_instruction!(self, cef, rep, addi, dst, a, b),
|
||||
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(())
|
||||
}
|
||||
}
|
14
src/main.rs
14
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
|
||||
{
|
||||
StartCause::Poll =>
|
||||
if cause == StartCause::Poll
|
||||
{
|
||||
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
12
tests/integration_test.rs
Normal 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(())
|
||||
}
|
1
tests/programs/basic.foot
Normal file
1
tests/programs/basic.foot
Normal file
@ -0,0 +1 @@
|
||||
CNST. r0, #0xFEED
|
Loading…
x
Reference in New Issue
Block a user