use anyhow::{bail, Result}; use itertools::Itertools; use radix_fmt::radix; use std::cmp::Ordering; use std::io::{self, Write}; #[derive(Debug)] pub struct Data { program: [u8; 32 * 1024], pub tmp: u16, pub reg_pc: u16, pub reg_opc: u8, pub reg_arg: u16, pub reg_s: u8, pub reg_sp: u16, pub reg_a: u16, pub reg_b: u16, pub reg_c: u16, pub reg_d: u16, memory: [u16; 16 * 1024], term: console::Term, } impl Data { pub fn new(program: [u8; 32 * 1024]) -> Self { Self { program, tmp: 0, reg_pc: 0, reg_opc: 0, reg_arg: 0, reg_s: 0, reg_sp: 0, reg_a: 0, reg_b: 0, reg_c: 0, reg_d: 0, memory: [0; 16 * 1024], term: console::Term::stdout(), } } pub fn get_memory(&self, address: u16) -> Result { if address < (32 * 1024) { match self.program.get(address as usize) { Some(val) => Ok(*val as u16), None => Ok(0), } // } else if address < (40 * 1024) { // Ok(self.stack[(address - (32 * 1024)) as usize]) } else if address < (48 * 1024) { Ok(self.memory[(address - (32 * 1024)) as usize]) } else if address == (48 * 1024 + 2) { Ok(self.term.read_char()? as u16) } else { Ok(0) } } pub fn set_memory(&mut self, address: u16, value: u16) -> Result<()> { // if ((32 * 1024)..(40 * 1024)).contains(&address) { // self.stack[(address - (32 * 1024)) as usize] = value; if ((32 * 1024)..(48 * 1024)).contains(&address) { self.memory[(address - (32 * 1024)) as usize] = value; } else if address == (48 * 1024) { print!("{}", value); io::stdout().flush()?; } else if address == (48 * 1024 + 1) { print!("{}", char::from(value as u8)); io::stdout().flush()?; } Ok(()) } pub fn get_register(&self, register: u8) -> u16 { match register { 0x0 => self.reg_pc, 0x1 => self.reg_opc as u16, 0x2 => self.reg_arg, 0x3 => self.reg_s as u16, 0x4 => self.reg_sp, 0x5 => self.reg_a, 0x6 => self.reg_b, 0x7 => self.reg_c, 0x8 => self.reg_d, _ => 0, } } pub fn set_register(&mut self, register: u8, value: u16) -> u16 { match register { 0x0 => { self.reg_pc = value; value } 0x1 => { self.reg_opc = value as u8; value & 0xff } 0x2 => { self.reg_arg = value; value } 0x3 => { self.reg_s = value as u8; value & 0xff } 0x4 => { self.reg_sp = value; value } 0x5 => { self.reg_a = value; value } 0x6 => { self.reg_b = value; value } 0x7 => { self.reg_c = value; value } 0x8 => { self.reg_d = value; value } _ => 0, } } fn alu(&mut self, operation: u8) -> Result<()> { match operation { 0x00 => { self.tmp = !self.reg_a; } 0x01 => { self.tmp = self.reg_a & self.reg_b; } 0x02 => { self.tmp = self.reg_a | self.reg_b; } 0x03 => { self.tmp = self.reg_a ^ self.reg_b; } 0x04 => { self.tmp = self.reg_a << self.reg_b; } 0x05 => { self.tmp = self.reg_a >> self.reg_b; } 0x06 => { self.tmp = self .reg_a .wrapping_add(self.reg_b) .wrapping_add((self.reg_s & 0b00000001) as u16); self.reg_s &= 0b11111110; if self.tmp < self.reg_a { self.tmp |= 0b00000001; } } 0x07 => { self.tmp = self .reg_a .wrapping_sub(self.reg_b) .wrapping_sub((self.reg_s & 0b00000001) as u16); self.reg_s &= 0b11111110; if self.tmp > self.reg_a { self.tmp |= 0b00000001; } } 0x08 => { self.tmp = self.reg_a.wrapping_mul(self.reg_b); self.reg_s &= 0b11111110; } 0x09 => { self.tmp = self.reg_a / self.reg_b; self.reg_s &= 0b11111110; } 0x0a => { self.tmp = match self.reg_a.cmp(&self.reg_b) { Ordering::Equal => 0, Ordering::Less => 1, Ordering::Greater => 2, } } 0x0b => { self.tmp = (self.reg_a == self.reg_b) as u16; } 0x0c => { self.tmp = (self.reg_a < self.reg_b) as u16; } 0x0d => { self.tmp = (self.reg_a > self.reg_b) as u16; } 0x0e => { self.tmp = (self.reg_a <= self.reg_b) as u16; } 0x0f => { self.tmp = (self.reg_a >= self.reg_b) as u16; } 0x10 => { self.tmp = (self.reg_a & 1 == 1) as u16; } 0x11 => { self.tmp = (self.reg_a & 1 != 1) as u16; } 0x12 => { self.tmp = rand::random(); } _ => bail!("Invalid ALU operation: 0x{}", radix(operation, 16)), } Ok(()) } } pub fn emulate(data: &mut Data) -> Result<()> { while data.reg_pc != 0xffff { data.reg_opc = data.get_memory(data.reg_pc)? as u8; data.reg_pc = data.reg_pc.wrapping_add(1); if data.reg_opc >> 7 == 1 { data.reg_opc &= 0b0111111; data.reg_arg = 0; } else { data.reg_arg = data.get_memory(data.reg_pc)? << 8; data.reg_pc = data.reg_pc.wrapping_add(1); data.reg_arg |= data.get_memory(data.reg_pc)?; data.reg_pc = data.reg_pc.wrapping_add(1); } match data.reg_opc { 0x00 => {} 0x01 => { data.tmp = data.reg_arg; data.memory[data.reg_sp as usize] = data.reg_arg; data.reg_sp = data.reg_sp.wrapping_add(1); } 0x02 => { data.reg_sp = data.reg_sp.wrapping_sub(1); data.memory[data.reg_sp as usize] = 0; } 0x03 => { data.tmp = data.reg_arg; } 0x04 => { data.tmp = data.get_register(data.reg_arg as u8); } 0x05 => { data.tmp = data.memory[data.reg_sp.wrapping_sub(1) as usize]; } 0x06 => { data.set_register(data.reg_arg as u8, data.tmp); } 0x07 => { if data.reg_a & 1 == 1 { data.set_register(data.reg_arg as u8, data.tmp); } } 0x08 => { data.memory[data.reg_sp as usize] = data.tmp; data.reg_sp = data.reg_sp.wrapping_add(1); } 0x09 => { println!( "[DEBUG]: [{}]", data.memory.iter().take(data.reg_sp as usize).join(", ") ); print!("Press enter to continue execution...",); io::stdout().flush()?; data.term.read_line()?; } 0x0a => { data.alu(data.tmp as u8)?; } 0x0b => { data.tmp = data.get_memory(data.tmp)?; } 0x0c => { data.set_memory(data.tmp, data.reg_a)?; } _ => bail!("Invalid opcode: 0x{}", radix(data.reg_opc, 16)), } } Ok(()) }