use crate::rom::Rom; use std::fmt; #[derive(Debug, Clone, Copy)] pub enum Opname { Nop, Ajmp, Ljmp, Jnz, Mov, Mov16, MovxTo, MovxFrom, 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, Data16, Dptr, AtDptr, Indirect(Register), Register(Register), Reladdr, Addr11(u8), Addr16, } #[derive(Debug, Clone)] pub enum Operand { Acc, Direct(u8), Indirect(Register), Data(u8), Data16(u16), Dptr, AtDptr, 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], progmem: Rom, extmem: [u8; u16::max_value() as usize + 1], } const RAM_OFFSET_ACC: u8 = 0xE0; const RAM_OFFSET_DPH: u8 = 0x82; const RAM_OFFSET_DPL: u8 = 0x83; const RAM_OFFSET_SP: u8 = 0x80; 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 RESET_VALUE_ACC: u8 = 0x00; const RESET_VALUE_SP: 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_JNZ: u8 = 0x70; 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_MOV_DPTR_DATA16: u8 = 0x90; const OPCODE_AJMP_P5: u8 = 0xA1; const OPCODE_PUSH: u8 = 0xC0; const OPCODE_AJMP_P6: u8 = 0xC1; const OPCODE_POP: u8 = 0xD0; const OPCODE_MOV_A_AT_DPTR: u8 = 0xE0; 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_MOV_AT_DPTR_A: u8 = 0xF0; 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_JNZ as usize] = IncompleteOp { opname: Opname::Jnz, size: 2, operand1: Some(IncompleteOperand::Reladdr), 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_MOV_DPTR_DATA16 as usize] = IncompleteOp { opname: Opname::Mov16, size: 3, operand1: Some(IncompleteOperand::Dptr), operand2: Some(IncompleteOperand::Data16), }; lut[OPCODE_AJMP_P5 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(5)), operand2: None, }; lut[OPCODE_PUSH as usize] = IncompleteOp { opname: Opname::Push, size: 2, operand1: Some(IncompleteOperand::Direct), operand2: None, }; lut[OPCODE_AJMP_P6 as usize] = IncompleteOp { opname: Opname::Ajmp, size: 2, operand1: Some(IncompleteOperand::Addr11(6)), operand2: None, }; lut[OPCODE_POP as usize] = IncompleteOp { opname: Opname::Pop, size: 2, operand1: Some(IncompleteOperand::Direct), operand2: None, }; lut[OPCODE_MOV_A_AT_DPTR as usize] = IncompleteOp { opname: Opname::MovxFrom, size: 1, operand1: Some(IncompleteOperand::Acc), operand2: Some(IncompleteOperand::AtDptr), }; 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_MOV_AT_DPTR_A as usize] = IncompleteOp { opname: Opname::MovxTo, size: 1, operand1: Some(IncompleteOperand::AtDptr), operand2: Some(IncompleteOperand::Acc), }; 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::Data16(_) => 2, Operand::Dptr => 0, Operand::AtDptr => 0, 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 mut ram: [u8; u8::max_value() as usize + 1] = [0; u8::max_value() as usize + 1]; ram[RAM_OFFSET_SP as usize] = RESET_VALUE_SP; ram[RAM_OFFSET_ACC as usize] = RESET_VALUE_ACC; let progmem = match Rom::from_hex(hex) { Ok(value) => value, Err(err_string) => return Err(err_string), }; let extmem: [u8; u16::max_value() as usize + 1] = [0xFF; u16::max_value() as usize + 1]; Ok(Self { pc: 0, ram, progmem, extmem, }) } /// Fetch and execute one instruction pub fn step(&mut self) -> u16 { let op = self.fetch(); self.exec(op); self.pc } /// Get current Program Counter value pub fn pc(&self) -> u16 { self.pc } /// Get current instruction pub fn op(&self) -> Op { let op = IncompleteOp::from_u8(self.progmem.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(operand) => operand.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.expect("MOV has no first operand given"), op.operand2.expect("MOV has no second operand given"), ), Opname::Mov16 => self.mov16( op.operand1.expect("MOV has no first operand given"), op.operand2.expect("MOV has no second operand given"), ), Opname::MovxTo => self.movx_to( op.operand1.expect("MOVX has no first operand given"), op.operand2.expect("MOVX has no second operand given"), ), Opname::MovxFrom => self.movx_from( op.operand1.expect("MOVX has no first operand given"), op.operand2.expect("MOVX has no second operand given"), ), Opname::Illegal => (), Opname::Push => self.push(op.operand1.expect("PUSH has no operand given")), Opname::Pop => self.pop(op.operand1.expect("POP has no operand given")), Opname::Sjmp => self.sjmp(op.operand1.expect("SJMP has no operand given")), Opname::Ajmp => self.ajmp(op.operand1.expect("AJMP has no operand given")), Opname::Ljmp => self.ljmp(op.operand1.expect("LJMP has no operand given")), Opname::Jnz => self.jnz(op.operand1.expect("JNZ 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.progmem.array[(self.pc as usize + offset)]) } IncompleteOperand::Data => { Operand::Data(self.progmem.array[(self.pc as usize + offset)]) } IncompleteOperand::Data16 => Operand::Data16({ let high = self.progmem.array[(self.pc as usize + offset)]; let low = self.progmem.array[(self.pc as usize + offset + 1)]; u16::from(high) << 8 & u16::from(low) }), IncompleteOperand::Dptr => Operand::Dptr, IncompleteOperand::AtDptr => Operand::AtDptr, IncompleteOperand::Indirect(r) => Operand::Indirect(r), IncompleteOperand::Register(r) => Operand::Register(r), IncompleteOperand::Reladdr => { Operand::Reladdr(self.progmem.array[(self.pc as usize + offset)] as i8) } IncompleteOperand::Addr11(high) => Operand::Addr11({ let low = self.progmem.array[(self.pc as usize + offset)]; u16::from(high) << 8 & u16::from(low) }), IncompleteOperand::Addr16 => Operand::Addr16({ let high = self.progmem.array[(self.pc as usize + offset)]; let low = self.progmem.array[(self.pc as usize + offset + 1)]; u16::from(high) << 8 & u16::from(low) }), } } 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(data) => data, Operand::Register(r) => r.to_ram_offset(), other => panic!("MOV got incompatible second operand \"{:?}\"", other), }; self.ram[dest as usize] = value; } fn mov16(&mut self, o1: Operand, o2: Operand) { let dest = match o1 { Operand::Dptr => RAM_OFFSET_DPH, other => panic!("MOV (Mov16) got incompatible first operand \"{:?}\"", other), }; let value = match o2 { Operand::Data16(data) => data, other => panic!( "MOV (Mov16) got incompatible second operand \"{:?}\"", other ), }; self.ram[dest as usize] = (value >> 8) as u8; self.ram[dest as usize + 1] = value as u8; } fn movx_to(&mut self, o1: Operand, o2: Operand) { let dest = match o1 { Operand::AtDptr => { let dph = self.ram[RAM_OFFSET_DPH as usize]; let dpl = self.ram[RAM_OFFSET_DPL as usize]; u16::from(dph) << 8 & u16::from(dpl) } other => panic!( "MOVX (MovxTo) got incompatible first operand \"{:?}\"", other ), }; let value = match o2 { Operand::Acc => self.ram[RAM_OFFSET_ACC as usize], other => panic!( "MOVX (MovxTo) got incompatible second operand \"{:?}\"", other ), }; self.extmem[dest as usize] = value; } fn movx_from(&mut self, o1: Operand, o2: Operand) { let dest = match o1 { Operand::Acc => RAM_OFFSET_ACC, other => panic!( "MOVX (MovxFrom) got incompatible first operand \"{:?}\"", other ), }; let value = match o2 { Operand::AtDptr => { let dph = u16::from(self.ram[RAM_OFFSET_DPH as usize]); let dpl = u16::from(self.ram[RAM_OFFSET_DPL as usize]); let dptr = (dph << 8) + dpl; self.extmem[dptr as usize] } other => panic!( "MOVX (MovxFrom) got incompatible second operand \"{:?}\"", other ), }; self.extmem[dest as usize] = value; } fn push(&mut self, operand: Operand) { match operand { Operand::Direct(offset) => { self.ram[RAM_OFFSET_SP as usize] += 1; let value = self.ram[offset as usize]; let sp = self.ram[RAM_OFFSET_SP as usize]; self.ram[sp as usize] = value; } other => panic!("PUSH got incompatible operand \"{:?}\"", other), } } fn pop(&mut self, operand: Operand) { match operand { Operand::Direct(offset) => { let sp = self.ram[RAM_OFFSET_SP as usize]; self.ram[offset as usize] = self.ram[sp as usize]; self.ram[RAM_OFFSET_SP as usize] -= 1; } other => panic!("POP got incompatible operand \"{:?}\"", other), } } fn sjmp(&mut self, operand: Operand) { match operand { Operand::Reladdr(reladdr) => { self.pc = (self.pc as i16 + i16::from(reladdr)) as u16; } _ => panic!("SJMP got incompatible operand"), } } fn ajmp(&mut self, operand: Operand) { match operand { Operand::Addr11(addr11) => { assert!(addr11 <= 0x4FFu16); // Clear 11 low bits of PC and add address, that is within these 11 bits self.pc = (self.pc & !0x4FFu16) & addr11; } _ => panic!("AJMP got incompatible operand"), } } fn ljmp(&mut self, operand: Operand) { match operand { Operand::Addr16(addr) => self.pc = addr, _ => panic!("LJMP got incompatible operand"), } } fn jnz(&mut self, operand: Operand) { match operand { Operand::Reladdr(reladdr) => { if self.ram[usize::from(RAM_OFFSET_ACC)] != 0 { self.pc = (self.pc as i16 + i16::from(reladdr)) as u16; } } _ => panic!("JNZ got incompatible operand"), } } } 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::Data16(data) => format!("#{}", data), Operand::Dptr => "DPTR".to_string(), Operand::AtDptr => "@DPTR".to_string(), 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, "{}", match self { Opname::Nop => "NOP", Opname::Ajmp => "AJMP", Opname::Ljmp => "LJMP", Opname::Jnz => "JNZ", Opname::Mov => "MOV", Opname::Mov16 => "MOV", Opname::MovxTo => "MOVX", Opname::MovxFrom => "MOVX", Opname::Push => "PUSH", Opname::Pop => "POP", Opname::Illegal => "", Opname::Sjmp => "SJMP", } ) } }