summaryrefslogtreecommitdiff
path: root/src/cpu8051.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu8051.rs')
-rw-r--r--src/cpu8051.rs716
1 files changed, 716 insertions, 0 deletions
diff --git a/src/cpu8051.rs b/src/cpu8051.rs
new file mode 100644
index 0000000..2a0fbe8
--- /dev/null
+++ b/src/cpu8051.rs
@@ -0,0 +1,716 @@
+use crate::memory::Memory;
+use crate::progmem::ProgramMemory;
+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<IncompleteOperand>,
+ operand2: Option<IncompleteOperand>,
+ operand3: Option<IncompleteOperand>,
+}
+
+#[derive(Debug, Clone)]
+pub struct Op {
+ opname: Opname,
+ size: usize,
+ operand1: Option<Operand>,
+ operand2: Option<Operand>,
+ operand3: Option<Operand>,
+}
+
+pub struct Cpu8051 {
+ pc: u16,
+ ram: [u8; u8::max_value() as usize + 1],
+ progmem: ProgramMemory,
+ 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,
+ operand3: None,
+ }; u8::max_value() as usize + 1];
+ lut[OPCODE_NOP as usize] = IncompleteOp {
+ opname: Opname::Nop,
+ size: 1,
+ operand1: None,
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P0 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(0)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_LJMP as usize] = IncompleteOp {
+ opname: Opname::Ljmp,
+ size: 3,
+ operand1: Some(IncompleteOperand::Addr16),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P1 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(1)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P2 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(2)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P3 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(3)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_JNZ as usize] = IncompleteOp {
+ opname: Opname::Jnz,
+ size: 2,
+ operand1: Some(IncompleteOperand::Reladdr),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_DATA as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 2,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Data),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_DIRECT_DATA as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 3,
+ operand1: Some(IncompleteOperand::Direct),
+ operand2: Some(IncompleteOperand::Data),
+ operand3: None,
+ };
+ lut[OPCODE_SJMP as usize] = IncompleteOp {
+ opname: Opname::Sjmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Reladdr),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P4 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(4)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_MOV_DPTR_DATA16 as usize] = IncompleteOp {
+ opname: Opname::Mov16,
+ size: 3,
+ operand1: Some(IncompleteOperand::Dptr),
+ operand2: Some(IncompleteOperand::Data16),
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P5 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(5)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_PUSH as usize] = IncompleteOp {
+ opname: Opname::Push,
+ size: 2,
+ operand1: Some(IncompleteOperand::Direct),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P6 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(6)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_POP as usize] = IncompleteOp {
+ opname: Opname::Pop,
+ size: 2,
+ operand1: Some(IncompleteOperand::Direct),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_AT_DPTR as usize] = IncompleteOp {
+ opname: Opname::MovxFrom,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::AtDptr),
+ operand3: None,
+ };
+ lut[OPCODE_AJMP_P7 as usize] = IncompleteOp {
+ opname: Opname::Ajmp,
+ size: 2,
+ operand1: Some(IncompleteOperand::Addr11(7)),
+ operand2: None,
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_DIRECT as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 2,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Direct),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_INDIRECT_R0 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Indirect(Register::R0)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_INDIRECT_R1 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Indirect(Register::R1)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R0 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R0)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R1 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R1)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R2 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R2)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R3 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R3)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R4 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R4)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R5 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R5)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R6 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R6)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_A_R7 as usize] = IncompleteOp {
+ opname: Opname::Mov,
+ size: 1,
+ operand1: Some(IncompleteOperand::Acc),
+ operand2: Some(IncompleteOperand::Register(Register::R7)),
+ operand3: None,
+ };
+ lut[OPCODE_MOV_AT_DPTR_A as usize] = IncompleteOp {
+ opname: Opname::MovxTo,
+ size: 1,
+ operand1: Some(IncompleteOperand::AtDptr),
+ operand2: Some(IncompleteOperand::Acc),
+ operand3: 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::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 Cpu8051 {
+ /// Constructor
+ pub fn new() -> Self {
+ let mut ram = [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;
+ Self {
+ pc: 0,
+ ram,
+ progmem: ProgramMemory::new(),
+ extmem: [0xFF; u16::max_value() as usize + 1],
+ }
+ }
+
+ pub fn rom(mut self, rom: ProgramMemory) -> Self {
+ self.progmem = rom;
+ self
+ }
+
+ /// 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.get(self.pc));
+ let mut operands: Vec<Option<Operand>> = vec![op.operand1, op.operand2, op.operand3]
+ .iter()
+ .scan(1, |offset, &x| {
+ Some(if let Some(incomplete_operand) = x {
+ let operand = self.fetch_operand(incomplete_operand, *offset);
+ *offset += operand.size() as u16;
+ Some(operand)
+ } else {
+ None
+ })
+ })
+ .collect();
+ Op {
+ opname: op.opname,
+ size: op.size,
+ operand3: operands.remove(2),
+ operand2: operands.remove(1),
+ operand1: operands.remove(0),
+ }
+ }
+
+ /// 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: u16) -> Operand {
+ match operand {
+ IncompleteOperand::Acc => Operand::Acc,
+ IncompleteOperand::Direct => Operand::Direct(self.progmem.get(self.pc + offset)),
+ IncompleteOperand::Data => Operand::Data(self.progmem.get(self.pc + offset)),
+ IncompleteOperand::Data16 => Operand::Data16({
+ let high = self.progmem.get(self.pc + offset);
+ let low = self.progmem.get(self.pc + 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.get(self.pc + offset) as i8)
+ }
+ IncompleteOperand::Addr11(high) => Operand::Addr11({
+ let low = self.progmem.get(self.pc + offset);
+ u16::from(high) << 8 & u16::from(low)
+ }),
+ IncompleteOperand::Addr16 => Operand::Addr16({
+ let high = self.progmem.get(self.pc + offset);
+ let low = self.progmem.get(self.pc + 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 => "<Illegal>",
+ Opname::Sjmp => "SJMP",
+ }
+ )
+ }
+}