use crate::rom::Rom; use std::fmt; #[derive(Debug, Clone)] pub enum Register { R0, R1, R2, R3, R4, R5, R6, R7, } 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, } } } #[derive(Debug, Clone)] pub enum Operand { Acc, Direct(u8), Indirect(Register), Data(u8), Register(Register), Reladdr(i8), } #[derive(Debug, Clone)] pub struct Op { opcode: Isa8051Opcode, size: usize, operand1: Option, operand2: Option, } 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_MOV_A_DATA: u8 = 0x74; const OPCODE_MOV_DIRECT_DATA: u8 = 0x75; 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 OPCODE_SJMP: u8 = 0xD2; 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), } ) } } impl fmt::Display for Op { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut output = String::new(); output.push_str(&format!("{} ", self.opcode)); 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 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, } } } pub struct Core { pc: u16, ram: [u8; u8::max_value() as usize], rom: Rom, } impl Core { /// /// Constructor /// pub fn new_with_rom_from_hex(hex: String) -> Result { let ram: [u8; u8::max_value() as usize] = [0; u8::max_value() as usize]; 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 = Isa8051Op::from_u8(self.rom.array[self.pc as usize]); let operand1 = if let Some(operand) = op.operand1 { Some(self.map_operand(operand, 1)) } else { None }; let operand2_offset = match &operand1 { Some(o) => o.size(), _ => 0, }; let operand2 = if let Some(operand) = op.operand2 { Some(self.map_operand(operand, operand2_offset)) } else { None }; Op { opcode: op.opcode, 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.opcode { Isa8051Opcode::Nop => (), Isa8051Opcode::Mov => (self.mov(op.operand1.unwrap(), op.operand2.unwrap())), Isa8051Opcode::Illegal => (), Isa8051Opcode::Push => (), Isa8051Opcode::Pop => (), Isa8051Opcode::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") } } } } 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 map_operand(&self, operand: Isa8051Operand, offset: usize) -> Operand { match operand { Isa8051Operand::Acc => Operand::Acc, Isa8051Operand::Direct => Operand::Direct(self.rom.array[(self.pc as usize + offset)]), Isa8051Operand::Data => Operand::Data(self.rom.array[(self.pc as usize + offset)]), Isa8051Operand::Indirect(r) => Operand::Indirect(r), Isa8051Operand::Register(r) => Operand::Register(r), Isa8051Operand::Reladdr => { Operand::Reladdr(self.rom.array[(self.pc as usize + offset)] as i8) } } } } #[derive(Debug, Clone)] pub enum Isa8051Operand { Acc, Direct, Data, Indirect(Register), Register(Register), Reladdr, } #[derive(Debug, Clone)] pub enum Isa8051Opcode { Nop, Mov, Push, Pop, Illegal, Sjmp, } impl fmt::Display for Isa8051Opcode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } #[derive(Debug, Clone)] pub struct Isa8051Op { opcode: Isa8051Opcode, size: usize, operand1: Option, operand2: Option, } impl Isa8051Op { fn from_u8(data: u8) -> Self { match data { OPCODE_NOP => Isa8051Op { opcode: Isa8051Opcode::Nop, size: 1, operand1: None, operand2: None, }, OPCODE_MOV_A_DATA => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 2, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Data), }, OPCODE_MOV_DIRECT_DATA => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 3, operand1: Some(Isa8051Operand::Direct), operand2: Some(Isa8051Operand::Data), }, OPCODE_MOV_A_DIRECT => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 2, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Direct), }, OPCODE_MOV_A_INDIRECT_R0 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Indirect(Register::R0)), }, OPCODE_MOV_A_INDIRECT_R1 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Indirect(Register::R1)), }, OPCODE_MOV_A_R0 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R0)), }, OPCODE_MOV_A_R1 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R1)), }, OPCODE_MOV_A_R2 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R2)), }, OPCODE_MOV_A_R3 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R3)), }, OPCODE_MOV_A_R4 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R4)), }, OPCODE_MOV_A_R5 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R5)), }, OPCODE_MOV_A_R6 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R6)), }, OPCODE_MOV_A_R7 => Isa8051Op { opcode: Isa8051Opcode::Mov, size: 1, operand1: Some(Isa8051Operand::Acc), operand2: Some(Isa8051Operand::Register(Register::R7)), }, OPCODE_PUSH => Isa8051Op { opcode: Isa8051Opcode::Push, size: 2, operand1: Some(Isa8051Operand::Direct), operand2: None, }, OPCODE_POP => Isa8051Op { opcode: Isa8051Opcode::Pop, size: 2, operand1: Some(Isa8051Operand::Direct), operand2: None, }, OPCODE_SJMP => Isa8051Op { opcode: Isa8051Opcode::Sjmp, size: 2, operand1: Some(Isa8051Operand::Reladdr), operand2: None, }, _ => Isa8051Op { opcode: Isa8051Opcode::Illegal, size: 1, operand1: None, operand2: None, }, } } }