footvm/src/vm.rs

578 lines
16 KiB
Rust
Raw Normal View History

2025-03-06 20:58:07 -05:00
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");
#[derive(Clone, Copy)]
enum ConditionalExecutionFlag
{
Always,
LessThan,
LessThanOrEqual,
Equal,
GreaterThanOrEqual,
GreaterThan,
NotEqual
}
impl TryFrom<u32> for ConditionalExecutionFlag
{
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error>
{
match value
{
0 => Ok(Always),
1 => Ok(LessThan),
2 => Ok(LessThanOrEqual),
3 => Ok(Equal),
4 => Ok(GreaterThanOrEqual),
5 => Ok(GreaterThan),
6 => Ok(NotEqual),
_ => Err(())
}
}
}
#[derive(Clone, Copy)]
enum AddressingMode
{
Immediate,
Direct,
Indirect(u8),
IndirectAutoIncrement,
IndirectAutoDecrement
}
impl TryFrom<u32> for AddressingMode
{
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error>
{
match value
{
0 => Ok(Immediate),
1 => Ok(Direct),
2 => Ok(IndirectAutoIncrement),
3 => Ok(IndirectAutoDecrement),
4..8 => Ok(Indirect((value - 4) as u8)),
_ => Err(())
}
}
}
#[derive(Clone, Copy)]
struct Operand
{
pub addressing_mode: AddressingMode,
pub value: u8
}
impl TryFrom<u32> for Operand
{
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error>
{
2025-03-07 02:58:45 -05:00
match ((value >> 5) & 0b111).try_into()
2025-03-06 20:58:07 -05:00
{
Ok(addressing_mode) => Ok(
Operand
{
addressing_mode,
value: (value as u8) & 0b00011111
},
),
Err(_) => Err(())
}
}
}
enum Instruction
{
CNST(ConditionalExecutionFlag, bool, Operand, u16),
CMPR(ConditionalExecutionFlag, bool, Operand, Operand),
BWNG(ConditionalExecutionFlag, bool, Operand, Operand),
ARNG(ConditionalExecutionFlag, bool, Operand, Operand),
LONG(ConditionalExecutionFlag, bool, Operand, Operand),
BWOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
BAND(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
BXOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
URSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
SRSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
ZLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
CLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
ADDI(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
SUBT(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MULT(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
DIVI(ConditionalExecutionFlag, bool, Operand, Operand, Operand),
MODU(ConditionalExecutionFlag, bool, Operand, Operand, Operand)
}
impl TryFrom<u32> for Instruction
{
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error>
{
let ceflag: ConditionalExecutionFlag = (value >> 29).try_into()?;
let rflag: bool = (value >> 28 & 1) == 1;
let dst: Operand = (value >> 16).try_into()?;
2025-03-07 02:58:45 -05:00
let opcode = (value >> 24) & 0xF;
match opcode
2025-03-06 20:58:07 -05:00
{
0 => Ok(CNST(ceflag, rflag, dst, (value & 0xFFFF) as u16)),
1 =>
{
let a: Operand = (value & 0xFF).try_into()?;
match (value >> 8) & 0xFF
{
0 => Ok(CMPR(ceflag, rflag, dst, a)),
1 => Ok(BWNG(ceflag, rflag, dst, a)),
2 => Ok(ARNG(ceflag, rflag, dst, a)),
3 => Ok(LONG(ceflag, rflag, dst, a)),
_ => Err(())
}
}
2..14 =>
{
let a: Operand = (value & 0xFF).try_into()?;
let b: Operand = ((value >> 8) & 0xFF).try_into()?;
2025-03-07 09:04:26 -05:00
match (value >> 24) & 0xF
2025-03-06 20:58:07 -05:00
{
2 => Ok(BWOR(ceflag, rflag, dst, b, a)),
3 => Ok(BAND(ceflag, rflag, dst, b, a)),
4 => Ok(BXOR(ceflag, rflag, dst, b, a)),
5 => Ok(URSH(ceflag, rflag, dst, b, a)),
6 => Ok(SRSH(ceflag, rflag, dst, b, a)),
7 => Ok(ZLSH(ceflag, rflag, dst, b, a)),
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(MULT(ceflag, rflag, dst, b, a)),
12 => Ok(DIVI(ceflag, rflag, dst, b, a)),
13 => Ok(MODU(ceflag, rflag, dst, b, a)),
_ => Err(())
}
},
_ => Err(())
}
}
}
#[derive(PartialEq, Eq)]
enum Status
{
Less,
Equal,
Greater
}
const MEMORY_SIZE: usize = 1 << 16;
pub struct FootVM
{
memory: [u16; MEMORY_SIZE],
2025-03-07 09:04:26 -05:00
pub registers: [u16; 32],
2025-03-06 20:58:07 -05:00
status: Status
}
2025-03-07 02:58:45 -05:00
impl Default for FootVM
{
fn default() -> Self
{
Self::new()
}
}
2025-03-06 20:58:07 -05:00
macro_rules! run_instruction
{
($self:ident, $cef:ident, $rep:ident, $name:ident, $d:ident, $a:ident) =>
{
if $rep
{
while $self.should_repeat()
{
if $self.check($cef)
{
$self.$name($d, $a)
}
}
}
else if $self.check($cef)
{
$self.$name($d, $a)
}
};
($self:ident, $cef:ident, $rep:ident, $name:ident, $d:ident, $a:ident, $b:ident) =>
{
if $rep
{
while $self.should_repeat()
{
if $self.check($cef)
{
$self.$name($d, $a, $b)
}
}
}
else if $self.check($cef)
{
$self.$name($d, $a, $b)
}
};
}
impl FootVM
{
pub fn new() -> FootVM
{
FootVM
{
memory: [0; MEMORY_SIZE],
registers: [0; 32],
status: Status::Equal
}
}
pub fn load(&mut self, asm_src: &str, write: Option<&mut dyn std::io::Write>) -> Result<(), ()>
{
let mut asm_src = asm_src.to_owned();
asm_src.insert_str(0, ASM_PREFIX);
let asm_src = asm_src;
let virtual_filename = "loaded";
let mut report = customasm::diagn::Report::new();
let mut fileserver = customasm::util::FileServerMock::new();
fileserver.add(virtual_filename, asm_src);
let opts = customasm::asm::AssemblyOptions::new();
let assembly = customasm::asm::assemble(
&mut report,
&opts,
&mut fileserver,
&[virtual_filename]
);
let binary = assembly.output.map(|o| o.format_binary()).ok_or_else(
|| {
match write
{
Some(w) => report.print_all(w, &mut fileserver, true),
None => ()
}
}
)?;
let mut cursor = Cursor::new(binary);
let mut index = 0;
loop
{
match cursor.read_u16::<BigEndian>()
{
Ok(value) =>
{
self[index] = value;
index += 1;
},
Err(_) => break
}
}
Ok(())
}
pub fn cycle(&mut self)
{
let first_word = self[self.program_counter()];
let second_word = self[self.program_counter() + 1];
2025-03-07 09:04:26 -05:00
let full_instruction_bits = (second_word as u32) << 16 | first_word as u32;
*self.mut_program_counter() = self.program_counter().wrapping_add(2);
match full_instruction_bits.try_into()
2025-03-06 20:58:07 -05:00
{
Ok(CNST(cef, rep, dst, imm)) =>
run_instruction!(self, cef, rep, cnst, dst, imm),
Ok(CMPR(cef, rep, a, b)) =>
run_instruction!(self, cef, rep, cmpr, a, b),
Ok(BWNG(cef, rep, dst, src)) =>
run_instruction!(self, cef, rep, bwng, dst, src),
Ok(ARNG(cef, rep, dst, src)) =>
run_instruction!(self, cef, rep, arng, dst, src),
Ok(LONG(cef, rep, dst, src)) =>
run_instruction!(self, cef, rep, long, 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)) =>
run_instruction!(self, cef, rep, band, dst, a, b),
Ok(BXOR(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, bxor, dst, a, b),
Ok(URSH(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, ursh, dst, a, b),
Ok(SRSH(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, srsh, dst, a, b),
Ok(ZLSH(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, zlsh, dst, a, b),
Ok(CLSH(cef, rep, dst, b, a)) =>
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)) =>
run_instruction!(self, cef, rep, subt, dst, a, b),
Ok(MULT(cef, rep, dst, b, a)) =>
run_instruction!(self, cef, rep, mult, 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)) =>
run_instruction!(self, cef, rep, modu, dst, a, b),
2025-03-07 09:04:26 -05:00
Err(_) => {}
2025-03-06 20:58:07 -05:00
}
}
fn cnst(&mut self, dst: Operand, imm: u16)
{
self.store_operand(dst, imm)
}
fn cmpr(&mut self, a: Operand, b: Operand)
{
let a = self.load_operand(a) as i16;
let b = self.load_operand(b) as i16;
self.status = match a - b
{
..0 => Status::Less,
0 => Status::Equal,
1.. => Status::Greater
}
}
fn bwng(&mut self, dst: Operand, src: Operand)
{
let value = self.load_operand(src);
self.store_operand(dst, !value)
}
fn arng(&mut self, dst: Operand, src: Operand)
{
let a = self.load_operand(src);
self.store_operand(dst, (-(a as i16)) as u16);
}
fn long(&mut self, dst: Operand, src: Operand)
{
let a = self.load_operand(src);
match a
{
0 => self.store_operand(dst, 1),
_ => self.store_operand(dst, 0)
}
}
fn bwor(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a | b);
}
fn band(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a & b);
}
fn bxor(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a ^ b);
}
fn ursh(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a >> b);
}
fn srsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, ((a as i16) >> (b as i16)) as u16);
}
fn zlsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a << b);
}
fn clsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
self.store_operand(dst, a.rotate_left(b as u32));
}
fn addi(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
2025-03-07 09:04:26 -05:00
self.store_operand(dst, a.wrapping_add(b));
2025-03-06 20:58:07 -05:00
}
fn subt(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
2025-03-07 09:04:26 -05:00
self.store_operand(dst, a.wrapping_sub(b));
2025-03-06 20:58:07 -05:00
}
fn mult(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
2025-03-07 09:04:26 -05:00
self.store_operand(dst, a.wrapping_mul(b));
2025-03-06 20:58:07 -05:00
}
fn divi(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
2025-03-07 09:04:26 -05:00
self.store_operand(dst, a.wrapping_div(b));
2025-03-06 20:58:07 -05:00
}
fn modu(&mut self, dst: Operand, src_a: Operand, src_b: Operand)
{
let a = self.load_operand(src_a);
let b = self.load_operand(src_b);
2025-03-07 09:04:26 -05:00
self.store_operand(dst, a.wrapping_rem(b));
2025-03-06 20:58:07 -05:00
}
fn store_operand(&mut self, dst: Operand, value: u16)
{
match dst.addressing_mode
{
Immediate => (),
Direct => self.registers[dst.value as usize] = value,
Indirect(offset) =>
{
let ptr = self.registers[dst.value as usize] + offset as u16;
self[ptr] = value
}
IndirectAutoIncrement =>
{
let ptr = self.registers[dst.value as usize];
self[ptr] = value;
self.registers[dst.value as usize] += 1;
},
IndirectAutoDecrement =>
{
let ptr = self.registers[dst.value as usize];
self[ptr] = value;
self.registers[dst.value as usize] -= 1;
}
}
}
fn load_operand(&mut self, src: Operand) -> u16
{
match src.addressing_mode
{
Immediate => src.value as u16,
Direct => self.registers[src.value as usize],
Indirect(offset) => self[self.registers[src.value as usize] + offset as u16],
IndirectAutoIncrement =>
{
let ptr = self.registers[src.value as usize];
let retval = self[ptr];
self.registers[src.value as usize] += 1;
retval
},
IndirectAutoDecrement =>
{
let ptr = self.registers[src.value as usize];
let retval = self[ptr];
self.registers[src.value as usize] -= 1;
retval
}
}
}
fn program_counter(&self) -> u16
{
self.registers[31]
}
fn mut_program_counter(&mut self) -> &mut u16
{
&mut self.registers[31]
}
fn loop_count(&self) -> u16
{
self.registers[29]
}
fn mut_loop_count(&mut self) -> &mut u16
{
&mut self.registers[29]
}
fn should_repeat(&mut self) -> bool
{
if self.loop_count() > 0
{
*self.mut_loop_count() -= 1;
true
}
else
{
false
}
}
fn check(&self, cef: ConditionalExecutionFlag) -> bool
{
match cef
{
Always => true,
LessThan => self.status == Status::Less,
LessThanOrEqual => self.status == Status::Less || self.status == Status::Equal,
Equal => self.status == Status::Equal,
GreaterThanOrEqual => self.status == Status::Greater || self.status == Status::Equal,
GreaterThan => self.status == Status::Greater,
NotEqual => self.status != Status::Equal
}
}
}
impl Index<u16> for FootVM
{
type Output = u16;
fn index(&self, index: u16) -> &Self::Output
{
&self.memory[index as usize]
}
}
impl IndexMut<u16> for FootVM
{
fn index_mut(&mut self, index: u16) -> &mut Self::Output
{
&mut self.memory[index as usize]
}
}