use crate::rom::Rom; use std::fmt; #[derive(Debug, Clone, Copy)] pub enum Opname { Nop, Ajmp, Ljmp, Mov, Push, Pop, Illegal, Sjmp, } #[derive(Debug, Clone, Copy)] pub enum Register { R0, R1, R2, R3, R4, R5, R6, R7, } #[derive(Debug, Clone, Copy)] enum IncompleteOperand { Acc, Direct, Data, Indirect(Register), Register(Register), Reladdr, Addr11(u8), Addr16, } #[derive(Debug, Clone)] pub enum Operand { Acc, Direct(u8), Indirect(Register), Data(u8), Register(Register), Reladdr(i8), Addr11(u16), Addr16(u16), } #[derive(Debug, Clone, Copy)] struct IncompleteOp { opname: Opname, size: usize, operand1: Option, operand2: Option, } #[derive(Debug, Clone)] pub struct Op { opname: Opname, size: usize, operand1: Option, operand2: Option, } pub struct Core { pc: u16, ram: [u8; u8::max_value() as usize + 1], rom: Rom, } const RAM_OFFSET_ACC: u8 = 0xE0; const RAM_OFFSET_R0: u8 = 0x00; const RAM_OFFSET_R1: u8 = 0x01; const RAM_OFFSET_R2: u8 = 0x02; const RAM_OFFSET_R3: u8 = 0x03; const RAM_OFFSET_R4: u8 = 0x04; const RAM_OFFSET_R5: u8 = 0x05; const RAM_OFFSET_R6: u8 = 0x06; const RAM_OFFSET_R7: u8 = 0x07; const OPCODE_NOP: u8 = 0x00; const OPCODE_AJMP_P0: u8 = 0x01; const OPCODE_LJMP: u8 = 0x02; const OPCODE_AJMP_P1: u8 = 0x21; const OPCODE_AJMP_P2: u8 = 0x41; const OPCODE_AJMP_P3: u8 = 0x61; const OPCODE_MOV_A_DATA: u8 = 0x74; const OPCODE_MOV_DIRECT_DATA: u8 = 0x75; const OPCODE_SJMP: u8 = 0x80; const OPCODE_AJMP_P4: u8 = 0x81; const OPCODE_AJMP_P5: u8 = 0xA1; const OPCODE_AJMP_P6: u8 = 0xC1; const OPCODE_AJMP_P7: u8 = 0xE1; const OPCODE_MOV_A_DIRECT: u8 = 0xE5; const OPCODE_MOV_A_INDIRECT_R0: u8 = 0xE6; const OPCODE_MOV_A_INDIRECT_R1: u8 = 0xE7; const OPCODE_MOV_A_R0: u8 = 0xE8; const OPCODE_MOV_A_R1: u8 = 0xE9; const OPCODE_MOV_A_R2: u8 = 0xEA; const OPCODE_MOV_A_R3: u8 = 0xEB; const OPCODE_MOV_A_R4: u8 = 0xEC; const OPCODE_MOV_A_R5: u8 = 0xED; const OPCODE_MOV_A_R6: u8 = 0xEE; const OPCODE_MOV_A_R7: u8 = 0xEF; const OPCODE_PUSH: u8 = 0xC0; const OPCODE_POP: u8 = 0xD0; const OP_LUT: [IncompleteOp; u8::max_value() as usize + 1] = { let mut lut = [IncompleteOp { opname: Opname::Illegal, size: 1, operand1: None, operand2: None, }; u8::max_value() as usize + 1]; lut[OPCODE_NOP as usize] = IncompleteOp { opname: Opname::Nop, size: 1, operand1: None, operand2: None, }; lut[OPCODE_AJMP_P0 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(0)), operand2: None, }; lut[OPCODE_LJMP as usize] = IncompleteOp { opname: Opname::Ljmp, size: 3, operand1: Some(IncompleteOperand::Addr16), operand2: None, }; lut[OPCODE_AJMP_P1 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(1)), operand2: None, }; lut[OPCODE_AJMP_P2 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(2)), operand2: None, }; lut[OPCODE_AJMP_P3 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(3)), operand2: None, }; lut[OPCODE_MOV_A_DATA as usize] = IncompleteOp { opname: Opname::Mov, size: 2, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Data), }; lut[OPCODE_MOV_DIRECT_DATA as usize] = IncompleteOp { opname: Opname::Mov, size: 3, operand1: Some(IncompleteOperand::Direct), operand2: Some(IncompleteOperand::Data), }; lut[OPCODE_SJMP as usize] = IncompleteOp { opname: Opname::Sjmp, size: 2, operand1: Some(IncompleteOperand::Reladdr), operand2: None, }; lut[OPCODE_AJMP_P4 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(4)), operand2: None, }; lut[OPCODE_AJMP_P5 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(5)), operand2: None, }; lut[OPCODE_AJMP_P6 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(6)), operand2: None, }; lut[OPCODE_AJMP_P7 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(7)), operand2: None, }; lut[OPCODE_MOV_A_DIRECT as usize] = IncompleteOp { opname: Opname::Mov, size: 2, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Direct), }; lut[OPCODE_MOV_A_INDIRECT_R0 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Indirect(Register::R0)), }; lut[OPCODE_MOV_A_INDIRECT_R1 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Indirect(Register::R1)), }; lut[OPCODE_MOV_A_R0 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R0)), }; lut[OPCODE_MOV_A_R1 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R1)), }; lut[OPCODE_MOV_A_R2 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R2)), }; lut[OPCODE_MOV_A_R3 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R3)), }; lut[OPCODE_MOV_A_R4 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R4)), }; lut[OPCODE_MOV_A_R5 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R5)), }; lut[OPCODE_MOV_A_R6 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R6)), }; lut[OPCODE_MOV_A_R7 as usize] = IncompleteOp { opname: Opname::Mov, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::Register(Register::R7)), }; lut[OPCODE_PUSH as usize] = IncompleteOp { opname: Opname::Push, size: 2, operand1: Some(IncompleteOperand::Direct), operand2: None, }; lut[OPCODE_POP as usize] = IncompleteOp { opname: Opname::Pop, size: 2, operand1: Some(IncompleteOperand::Direct), operand2: None, }; lut }; impl Register { fn to_ram_offset(&self) -> u8 { match self { Register::R0 => RAM_OFFSET_R0, Register::R1 => RAM_OFFSET_R1, Register::R2 => RAM_OFFSET_R2, Register::R3 => RAM_OFFSET_R3, Register::R4 => RAM_OFFSET_R4, Register::R5 => RAM_OFFSET_R5, Register::R6 => RAM_OFFSET_R6, Register::R7 => RAM_OFFSET_R7, } } } impl Operand { fn size(&self) -> usize { match self { Operand::Acc => 0, Operand::Direct(_) => 1, Operand::Indirect(_) => 0, Operand::Data(_) => 1, Operand::Register(_) => 0, Operand::Reladdr(_) => 1, Operand::Addr11(_) => 1, Operand::Addr16(_) => 2, } } } impl IncompleteOp { fn from_u8(data: u8) -> Self { OP_LUT[data as usize] } } impl Core { /// Constructor pub fn new_with_rom_from_hex(hex: String) -> Result { let ram: [u8; u8::max_value() as usize + 1] = [0; u8::max_value() as usize + 1]; let rom = match Rom::from_hex(hex) { Ok(value) => value, Err(err_string) => return Err(err_string), }; Ok(Self { pc: 0, ram, rom }) } /// Fetch and execute one instruction pub fn step(&mut self) -> u16 { let op = self.fetch(); self.exec(op); self.pc } /// Get current instruction pub fn op(&self) -> Op { let op = IncompleteOp::from_u8(self.rom.array[self.pc as usize]); let operand1 = if let Some(incomplete_operand) = op.operand1 { Some(self.fetch_operand(incomplete_operand, 1)) } else { None }; let operand2_offset = match &operand1 { Some(o) => o.size(), _ => 0, }; let operand2 = if let Some(incomplete_operand) = op.operand2 { Some(self.fetch_operand(incomplete_operand, operand2_offset)) } else { None }; Op { opname: op.opname, size: op.size, operand1, operand2, } } /// Increment PC, return the whole operation fn fetch(&mut self) -> Op { let op = self.op(); self.pc += op.size as u16; op } /// Execute one instruction without incrementing PC fn exec(&mut self, op: Op) { match op.opname { Opname::Nop => (), Opname::Mov => (self.mov(op.operand1.unwrap(), op.operand2.unwrap())), Opname::Illegal => (), Opname::Push => (), Opname::Pop => (), Opname::Sjmp => { if let Some(operand) = op.operand1 { match operand { Operand::Reladdr(reladdr) => self.sjmp(reladdr), _ => panic!("SJMP got incompatible operand"), } } else { panic!("SJMP has no operand given") } } Opname::Ajmp => { if let Some(operand) = op.operand1 { match operand { Operand::Addr11(addr) => self.ajmp(addr), _ => panic!("AJMP got incompatible operand"), } } else { panic!("AJMP has no operand given") } } Opname::Ljmp => { if let Some(operand) = op.operand1 { match operand { Operand::Addr16(addr) => self.ljmp(addr), _ => panic!("LJMP got incompatible operand"), } } else { panic!("LJMP has no operand given") } } } } /// Get operand by offset, based on meta information fn fetch_operand(&self, operand: IncompleteOperand, offset: usize) -> Operand { match operand { IncompleteOperand::Acc => Operand::Acc, IncompleteOperand::Direct => { Operand::Direct(self.rom.array[(self.pc as usize + offset)]) } IncompleteOperand::Data => Operand::Data(self.rom.array[(self.pc as usize + offset)]), IncompleteOperand::Indirect(r) => Operand::Indirect(r), IncompleteOperand::Register(r) => Operand::Register(r), IncompleteOperand::Reladdr => { Operand::Reladdr(self.rom.array[(self.pc as usize + offset)] as i8) } IncompleteOperand::Addr11(high) => Operand::Addr11( self.rom.array[(self.pc as usize + offset)] as u16 + (u16::from(high) << 8), ), IncompleteOperand::Addr16 => Operand::Addr16( (u16::from(self.rom.array[(self.pc as usize + offset)]) << 8) + u16::from(self.rom.array[(self.pc as usize + offset + 1)]), ), } } fn mov(&mut self, o1: Operand, o2: Operand) { let dest = match o1 { Operand::Acc => RAM_OFFSET_ACC, Operand::Direct(dir) => dir, Operand::Indirect(r) => self.ram[r.to_ram_offset() as usize], Operand::Register(r) => r.to_ram_offset(), other => panic!("Mov: got incompatible first operand \"{:?}\"", other), }; let value = match o2 { Operand::Acc => RAM_OFFSET_ACC, Operand::Direct(dir) => dir, Operand::Indirect(r) => self.ram[r.to_ram_offset() as usize], Operand::Data(r) => r, Operand::Register(r) => r.to_ram_offset(), other => panic!("Mov: got incompatible second operand \"{:?}\"", other), }; self.ram[dest as usize] = value; } fn sjmp(&mut self, reladdr: i8) { self.pc = (self.pc as i16 + i16::from(reladdr)) as u16; } fn ajmp(&mut self, pageaddr: u16) { self.pc = (self.pc & !0x4FFu16) + pageaddr; } fn ljmp(&mut self, pageaddr: u16) { self.pc = pageaddr; } } impl fmt::Display for Operand { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}", match self { Operand::Acc => "A".to_string(), Operand::Direct(dir) => format!("{:x}h", dir), Operand::Indirect(r) => format!("@{:?}", r), Operand::Data(data) => format!("#{}", data), Operand::Register(r) => format!("{:?}", r), Operand::Reladdr(rel) => format!("{}", rel), Operand::Addr11(rel) => format!("{}", rel), Operand::Addr16(rel) => format!("{}", rel), } ) } } impl fmt::Display for Op { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut output = String::new(); output.push_str(&format!("{} ", self.opname)); if let Some(op1) = &self.operand1 { output.push_str(&format!("{}", op1)); } if let Some(op2) = &self.operand2 { output.push_str(&format!(",{}", op2)); } write!(f, "{}", output) } } impl fmt::Display for Opname { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } }