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 for ConditionalExecutionFlag { type Error = (); fn try_from(value: u32) -> Result { 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 for AddressingMode { type Error = (); fn try_from(value: u32) -> Result { 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 for Operand { type Error = (); fn try_from(value: u32) -> Result { match (value >> 5).try_into() { 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 for Instruction { type Error = (); fn try_from(value: u32) -> Result { let ceflag: ConditionalExecutionFlag = (value >> 29).try_into()?; let rflag: bool = (value >> 28 & 1) == 1; let dst: Operand = (value >> 16).try_into()?; match value >> 24 { 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()?; match value >> 24 { 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], pub registers: [u16; 32], status: Status } 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::() { 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]; match ((second_word as u32) << 16 | first_word as u32).try_into() { 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), Err(_) => () } *self.mut_program_counter() += 2; } 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); self.store_operand(dst, ((a as i16) + (b as i16)) as u16); } 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); self.store_operand(dst, ((a as i16) - (b as i16)) as u16); } 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); self.store_operand(dst, ((a as i16) * (b as i16)) as u16); } 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); self.store_operand(dst, ((a as i16) / (b as i16)) as u16); } 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); self.store_operand(dst, ((a as i16) % (b as i16)) as u16); } 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 stack_pointer(&self) -> u16 { self.registers[30] } 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 for FootVM { type Output = u16; fn index(&self, index: u16) -> &Self::Output { &self.memory[index as usize] } } impl IndexMut for FootVM { fn index_mut(&mut self, index: u16) -> &mut Self::Output { &mut self.memory[index as usize] } }