use std::collections::HashMap; use std::vec::Vec; use crate::instruction::{Instruction, NormalInstruction, SpecialInstruction}; pub trait Console { fn read(&mut self) -> Option; fn write(&mut self, c: u8); } #[derive(Debug)] pub enum CpuError { ProgramEnd, InvalidInstruction, UnexpectedInput, InvalidArray, ArrayOutOfBounds, DivideByZero, Halt, LargeAlloc, InvalidOutput } #[derive(Debug)] pub struct Cpu { reg: [u32; 8], arr: HashMap>, iarr: u32, finger: u32, out: Option, in_reg: Option, debug: u32 } impl Cpu { pub fn new(pgm: Vec) -> Cpu { let mut arrays = HashMap::new(); arrays.insert(0, pgm); Cpu { reg: [0;8], arr: arrays, iarr: 1, finger: 0, in_reg: None, out: None, debug: 0 } } pub fn set_debug(&mut self, debug: u32) { self.debug = debug; } fn gr(&self, r: u8) -> u32{ self.reg[r as usize] } fn sr(&mut self, r: u8, v:u32) { self.reg[r as usize] = v } fn ins(&self) -> Result { if self.finger as usize >= self.arr[&0].len() { return Err(CpuError::ProgramEnd); } let d = self.arr[&0][self.finger as usize]; if let Ok(i) = Instruction::parse(d) { Ok(i) } else { Err(CpuError::InvalidInstruction) } } pub fn output(&self) -> Option { self.out } pub fn expecting_input(&self) -> bool { match self.in_reg { None => false, Some(_) => true } } pub fn set_in(&mut self, value: u8) -> Result<(), CpuError> { match self.in_reg { None => Err(CpuError::UnexpectedInput), Some(i) => { self.reg[i as usize] = value as u32; Ok(()) } } } pub fn state(&self) -> String { let istr = match self.ins() { Ok(i) => i.fmt(), Err(e) => format!("{:?}", e) }; format!("CPU Reg {:?}\nCPU Finger {}\nCPU Ins {}", self.reg, self.finger, istr) } fn handle_ortho(&mut self, i:SpecialInstruction) -> Result<(), CpuError>{ self.sr(i.a, i.v); Ok(()) } fn handle_cmov(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ if self.gr(i.c) > 0 { let v = self.gr(i.b); self.sr(i.a, v); } Ok(()) } fn handle_aix(&mut self, i: NormalInstruction) -> Result<(), CpuError> { let aidx = self.gr(i.b); let aoff = self.gr(i.c) as usize; if let Some(arr) = self.arr.get(&aidx){ if arr.len() > aoff { self.sr(i.a, arr[aoff]); Ok(()) } else { Err(CpuError::ArrayOutOfBounds) } } else { Err(CpuError::InvalidArray) } } fn handle_amd(&mut self, i: NormalInstruction) -> Result<(), CpuError> { let aidx = self.gr(i.a); let aoff = self.gr(i.b) as usize; let v = self.gr(i.c); // println!("amd {}[{}]={}", aidx, aoff, v); if let Some(arr) = self.arr.get_mut(&aidx){ if arr.len() > aoff { arr[aoff] = v; Ok(()) } else { Err(CpuError::ArrayOutOfBounds) } } else { Err(CpuError::InvalidArray) } } fn handle_add(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let b = self.gr(i.b); let c = self.gr(i.c); let (a, _) = b.overflowing_add(c); self.sr(i.a, a); Ok(()) } fn handle_mul(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let b = self.gr(i.b); let c = self.gr(i.c); let (a, _) = b.overflowing_mul(c); self.sr(i.a, a); Ok(()) } fn handle_div(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let b = self.gr(i.b); let c = self.gr(i.c); if c == 0 { return Err(CpuError::DivideByZero); } let (a, _) = b.overflowing_div(c); self.sr(i.a, a); Ok(()) } fn handle_nand(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let b = self.gr(i.b); let c = self.gr(i.c); let a = !(b & c); self.sr(i.a, a); Ok(()) } fn handle_halt(&mut self, _i:NormalInstruction) -> Result<(), CpuError>{ Err(CpuError::Halt) } fn handle_alc(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let c = self.gr(i.c) as usize; if c > 1048576 { return Err(CpuError::LargeAlloc); } let aid = self.iarr; self.iarr += 1; let v = vec![0; c]; self.arr.insert(aid, v); self.sr(i.b, aid); //println!("alc {} -> {}", c, aid); Ok(()) } fn handle_abn(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let c = self.gr(i.c); if c == 0 || ! self.arr.contains_key(&c){ return Err(CpuError::InvalidArray); } self.arr.remove(&c); Ok(()) } fn handle_out(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let c = self.gr(i.c); if c > 255 { return Err(CpuError::InvalidOutput); } self.out = Some(c as u8); Ok(()) } fn handle_inp(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ self.sr(i.c, 0xffffffff); self.in_reg = Some(i.c); Ok(()) } fn handle_lod(&mut self, i:NormalInstruction) -> Result<(), CpuError>{ let aidx = self.gr(i.b); let aoff = self.gr(i.c); if let Some(arr) = self.arr.get(&aidx){ if arr.len() > (aoff as usize) { if aidx > 0 { // println!("cloning {}", aidx); self.arr.insert(0, arr.clone()); } self.finger = aoff; Ok(()) } else { Err(CpuError::ArrayOutOfBounds) } } else { Err(CpuError::InvalidArray) } } fn exec(&mut self, instruction: Instruction)-> Result<(), CpuError>{ //println!("{:?}", instruction); match instruction { Instruction::Ortho(i) => self.handle_ortho(i), Instruction::ConditionalMove(i) => self.handle_cmov(i), Instruction::ArrayIndex(i) => self.handle_aix(i), Instruction::ArrayAmend(i) => self.handle_amd(i), Instruction::Add(i) => self.handle_add(i), Instruction::Multiply(i) => self.handle_mul(i), Instruction::Divide(i) => self.handle_div(i), Instruction::NotAnd(i) => self.handle_nand(i), Instruction::Halt(i) => self.handle_halt(i), Instruction::Allocate(i) => self.handle_alc(i), Instruction::Abandon(i) => self.handle_abn(i), Instruction::Output(i) => self.handle_out(i), Instruction::Input(i) => self.handle_inp(i), Instruction::Load(i) => self.handle_lod(i), //_ => Err(CpuError::InvalidInstruction) } } pub fn step(&mut self) -> Result<(), CpuError>{ self.out = None; self.in_reg = None; let i = self.ins()?; self.finger += 1; self.exec(i)?; Ok(()) } pub fn run(&mut self, console: &mut impl Console) -> Result<(), CpuError> { let mut cont = true; while cont { if self.debug > 5 { println!("{:?}", self.ins()); println!("{}", self.state()); } cont = match self.step() { Ok(_) => true, Err(CpuError::ProgramEnd) => false, Err(e) => return Err(e) }; if self.expecting_input() { if let Some(c) = console.read() { self.set_in(c)?; } }; if let Some(c) = self.output() { console.write(c); } }; Ok(()) } } #[cfg(test)] mod tests{ use super::*; struct NoConsole; impl Console for NoConsole { fn read(&mut self) -> Option { None } fn write(&mut self, _c: u8) {} } struct BufConsole { inb: Vec, inptr: usize, outb: Vec } impl BufConsole { fn make() -> BufConsole { BufConsole { inb: vec![], inptr: 0, outb: vec![] } } } impl Console for BufConsole { fn read(&mut self) -> Option { if self.inptr >= self.inb.len() { None } else { let r = self.inb[self.inptr]; self.inptr += 1; Some(r) } } fn write(&mut self, c: u8) { self.outb.push(c); } } fn ezop(op:u32, a:u32, b:u32, c:u32) -> u32 { match op { 13 => (op & 0xf) << 28 | (a & 7) << 25 | (b & 0x1ffffff), _ => (op & 0xf) << 28 | (a & 7) << 6 | (b & 7) << 3 | (c & 7) } } #[test] fn test_ortho() -> Result<(), CpuError> { let pgm = vec!( ezop(13, 3, 1337, 0) ); let mut c = Cpu::new(pgm); let exp = [0, 0, 0, 1337, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_cmov() -> Result<(), CpuError> { let pgm = vec!( ezop(0, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1337, 1, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 1337, 1337, 1, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_cmov_neg() -> Result<(), CpuError> { let pgm = vec!( ezop(0, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1337, 0, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 0, 1337, 0, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_aix() -> Result<(), CpuError> { let pgm = vec!( ezop(1, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.arr.insert(1338, vec![0,0,0,1337]); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 1337, 1338, 3, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_aix_noarr() -> Result<(), String> { let pgm = vec!( ezop(1, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; match c.run(&mut NoConsole{}) { Err(CpuError::InvalidArray) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_aix_nooff() -> Result<(), String> { let pgm = vec!( ezop(1, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; c.arr.insert(1338, vec![0]); match c.run(&mut NoConsole{}) { Err(CpuError::ArrayOutOfBounds) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_amd() -> Result<(), CpuError> { let pgm = vec!( ezop(2, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.arr.insert(1338, vec![0,0,0,0]); c.reg = [0, 1338, 1, 1336, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 1338, 1, 1336, 0, 0, 0, 0]; assert_eq!(c.reg, exp); if let Some(arr) = c.arr.get(&1338){ assert_eq!(arr,&vec![0,1336,0,0] ); Ok(()) } else { Err(CpuError::InvalidArray) } } #[test] fn test_amd_noarr() -> Result<(), String> { let pgm = vec!( ezop(2, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 1338, 3, 0, 0, 0, 0, 0]; match c.run(&mut NoConsole{}) { Err(CpuError::InvalidArray) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_amd_nooff() -> Result<(), String> { let pgm = vec!( ezop(2, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 1338, 3, 0, 0, 0, 0, 0]; c.arr.insert(1338, vec![0]); match c.run(&mut NoConsole{}) { Err(CpuError::ArrayOutOfBounds) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_add() -> Result<(), CpuError> { let pgm = vec!( ezop(3, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1337, 1, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 1338, 1337, 1, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_add_ovf() -> Result<(), CpuError> { let pgm = vec!( ezop(3, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1337, 0xffffffff, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 1336, 1337, 0xffffffff, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_mul() -> Result<(), CpuError> { let pgm = vec!( ezop(4, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1337, 2, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 2674, 1337, 2, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_mul_ovf() -> Result<(), CpuError> { let pgm = vec!( ezop(4, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 2, 0xffffffff, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 0xfffffffe, 2, 0xffffffff, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_div() -> Result<(), CpuError> { let pgm = vec!( ezop(5, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 2048, 1024, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 2, 2048, 1024, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_div_zero() -> Result<(), String> { let pgm = vec!( ezop(5, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 1338, 3, 0, 0, 0, 0, 0]; match c.run(&mut NoConsole{}) { Err(CpuError::DivideByZero) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_nand() -> Result<(), CpuError> { let pgm = vec!( ezop(6, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0xff00ff00, 0xffff0000, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; let exp = [0, 0x00ffff00, 0xff00ff00, 0xffff0000, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_halt() -> Result<(), String> { let pgm = vec!( ezop(7, 1, 2, 3) ); let mut c = Cpu::new(pgm); match c.run(&mut NoConsole{}) { Err(CpuError::Halt) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_alc_large() -> Result<(), String> { let pgm = vec!( ezop(8, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 1338, 33, 0xffffffff, 0, 0, 0, 0]; match c.run(&mut NoConsole{}) { Err(CpuError::LargeAlloc) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_alc() -> Result<(), CpuError> { let pgm = vec!( ezop(8, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 1338, 33, 5, 0, 0, 0, 0]; if let Err(e) = c.run(&mut NoConsole{}) { return Err(e) } let exp = [0, 1338, 1, 5, 0, 0, 0, 0]; assert_eq!(c.reg, exp); if let Some(arr) = c.arr.get(&1){ assert_eq!(arr,&vec![0,0,0,0,0] ); Ok(()) } else { Err(CpuError::InvalidArray) } } #[test] fn test_abn() -> Result<(), CpuError> { let pgm = vec!( ezop(9, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.arr.insert(1338, vec![0,0,0,0]); c.reg = [0, 0, 0, 1338, 0, 0, 0, 0]; c.run(&mut NoConsole{})?; if c.arr.contains_key(&1338) { Err(CpuError::InvalidArray) } else { Ok(()) } } #[test] fn test_abn_zero() -> Result<(), String> { let pgm = vec!( ezop(9, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0, 0, 0, 0, 0, 0]; match c.run(&mut NoConsole{}) { Err(CpuError::InvalidArray) => Ok(()), Err(_) => Err(String::from("Invalid error")), Ok(_) => Err(String::from("Expected err got ok")) } } #[test] fn test_out() -> Result<(), String> { let pgm = vec!( ezop(10, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0, 128, 0, 0, 0, 0]; let mut cons = BufConsole::make(); if let Err(e) = c.run(&mut cons) { return Err(format!("unexpected err {:?}", e)); }; assert_eq!(cons.outb, vec!(128)); Ok(()) } #[test] fn test_out_inv() -> Result<(), String> { let pgm = vec!( ezop(10, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0, 1337, 0, 0, 0, 0]; let mut cons = BufConsole::make(); match c.run(&mut cons) { Err(CpuError::InvalidOutput) => Ok(()), Ok(_) => Err(format!("unexpected ok")), Err(e) => Err(format!("unexpected err {:?}", e)) } } #[test] fn test_inp() -> Result<(), String> { let pgm = vec!( ezop(11, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0, 0, 0, 0, 0, 0]; let mut cons = BufConsole::make(); cons.inb.push(128); if let Err(e) = c.run(&mut cons) { return Err(format!("unexpected err {:?}", e)); }; let exp = [0, 0, 0, 128, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_inp_fail() -> Result<(), String> { let pgm = vec!( ezop(11, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 0, 0, 0, 0, 0, 0]; let mut cons = BufConsole::make(); if let Err(e) = c.run(&mut cons) { return Err(format!("unexpected err {:?}", e)); }; let exp = [0, 0, 0, 0xffffffff, 0, 0, 0, 0]; assert_eq!(c.reg, exp); Ok(()) } #[test] fn test_lod() -> Result<(), CpuError> { let pgm = vec!( ezop(12, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.arr.insert(1338, vec![0,0,0,1339]); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; c.step()?; assert_eq!(c.finger, 3); if let Some(arr) = c.arr.get(&0){ assert_eq!(arr,&vec![0,0,0,1339] ); Ok(()) } else { Err(CpuError::InvalidArray) } } #[test] fn test_lod_inv() -> Result<(), String> { let pgm = vec!( ezop(12, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; match c.step() { Err(CpuError::InvalidArray) => Ok(()), Ok(_) => Err(format!("unexpected ok")), Err(e) => Err(format!("unexpected err {:?}", e)) } } #[test] fn test_lod_off() -> Result<(), String> { let pgm = vec!( ezop(12, 1, 2, 3) ); let mut c = Cpu::new(pgm); c.reg = [0, 0, 1338, 3, 0, 0, 0, 0]; c.arr.insert(1338, vec![0]); match c.step() { Err(CpuError::ArrayOutOfBounds) => Ok(()), Ok(_) => Err(format!("unexpected ok")), Err(e) => Err(format!("unexpected err {:?}", e)) } } }