diff --git a/src/chip.rs b/src/chip.rs index a42d088..72f7ef7 100644 --- a/src/chip.rs +++ b/src/chip.rs @@ -7,7 +7,6 @@ pub struct Chip { pub cpu: CPU, pub rom: Box<[u8]>, pub ram: Box<[u8]>, - // TODO: List of devices } diff --git a/src/cpu.rs b/src/cpu.rs index 3beeb76..f81d386 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -3,14 +3,13 @@ use decoder; use decoder::{IncrementMode, Instruction}; -use regs::{StatusFlag, GeneralPurposeRegister, GeneralPurposeRegisterPair}; +use regs::{GeneralPurposeRegister, GeneralPurposeRegisterPair, StatusFlag}; use chip_definitions; use std::fmt; use slog; - #[derive(Debug)] pub enum CPUError { UnimplementedInstruction, @@ -36,10 +35,14 @@ pub struct CPU { logger: slog::Logger, } - impl fmt::Display for CPU { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "CPU @ 0x{:X} (file offset = 0x{:X}) ", self.pc, 2 * self.pc)?; + write!( + f, + "CPU @ 0x{:X} (file offset = 0x{:X}) ", + self.pc, + 2 * self.pc + )?; write!(f, "SREG: 0x{:02X}: ", self.sreg)?; for i in 0..7 { let t = self.sreg & (1 << i); @@ -52,7 +55,7 @@ impl fmt::Display for CPU { write!(f, "\n")?; for i in 0..32 { write!(f, " R{:-2}={:02X} ", i, self.registers[i])?; - if (i + 1) % 10 == 0{ + if (i + 1) % 10 == 0 { write!(f, "\n")?; } } @@ -64,14 +67,15 @@ impl CPU { pub fn new(logger: slog::Logger) -> Self { CPU { registers: [0u8; 32], - pc: 0, // Reset vector - sreg: 0, // Uninitialized as well - logger: logger + pc: 0, // Reset vector + sreg: 0, // Uninitialized as well + logger: logger, } } pub fn get_sp(&self, mem: &[u8]) -> u16 { - mem[chip_definitions::IOAdress::SPL as usize] as u16 | ((mem[chip_definitions::IOAdress::SPH as usize] as u16) << 8) + mem[chip_definitions::IOAdress::SPL as usize] as u16 + | ((mem[chip_definitions::IOAdress::SPH as usize] as u16) << 8) } fn set_sp(&self, mem: &mut [u8], val: u16) { @@ -79,7 +83,6 @@ impl CPU { mem[chip_definitions::IOAdress::SPH as usize] = (val >> 8) as u8; } - fn test_flag(&self, flag: StatusFlag) -> bool { (self.sreg & (flag as u8)) > 0 } @@ -124,13 +127,20 @@ impl CPU { Err(CPUError::OutOfBoundsException) } else { if addr < 0x2000 { - if addr != chip_definitions::IOAdress::SPH as u16 && addr != chip_definitions::IOAdress::SPL as u16 { + if addr != chip_definitions::IOAdress::SPH as u16 + && addr != chip_definitions::IOAdress::SPL as u16 + { // info!(self.logger, "Writing to IOR: {:04X} = {:02X}", addr, val); } if addr == chip_definitions::IOAdress::USARTC0_DATA as u16 { // print!("USART_OUT:{: <3} ({}) ", val, (val as char).escape_debug()); - info!(self.logger, "UART output: {: <3} ({})", val, (val as char).escape_debug()); - }/* else if addr == chip_definitions::IOAdress::SPL as u16 { + info!( + self.logger, + "UART output: {: <3} ({})", + val, + (val as char).escape_debug() + ); + } /* else if addr == chip_definitions::IOAdress::SPL as u16 { } else if addr == chip_definitions::IOAdress::SPH as u16 { @@ -190,9 +200,9 @@ impl CPU { // TODO: Hooks if addr == chip_definitions::IOAdress::USARTC0_DATA as _ { info!(self.logger, "Read from USART"); - // return Err(CPUError::Exit); - // return Ok(b'\r') - // return Ok(0); + // return Err(CPUError::Exit); + // return Ok(b'\r') + // return Ok(0); } else if addr == chip_definitions::IOAdress::USARTC0_STATUS as _ { // info!(self.logger, "Read from USART status"); if self.pc == 0x5AC { @@ -205,7 +215,9 @@ impl CPU { // HACK: Osci is set right.. return Ok(0x02); } else if addr < 0x2000 { - if addr != chip_definitions::IOAdress::SPH as u16 && addr != chip_definitions::IOAdress::SPL as u16 { + if addr != chip_definitions::IOAdress::SPH as u16 + && addr != chip_definitions::IOAdress::SPL as u16 + { info!(self.logger, "Reading from IOR: {:04X}", addr); } /* @@ -288,7 +300,10 @@ impl CPU { let add_carry = (d & r) | (r & !result) | (!result & d); self.set_clear_flag(StatusFlag::HalfCarry, add_carry & 0b1000 != 0); self.set_clear_flag(StatusFlag::Carry, add_carry & 0b1000_0000 != 0); - self.set_clear_flag(StatusFlag::TwosComplementOverflow, ((d & r & !result) | (!d & !r & result)) & 0x80 == 0x80); + self.set_clear_flag( + StatusFlag::TwosComplementOverflow, + ((d & r & !result) | (!d & !r & result)) & 0x80 == 0x80, + ); self.update_flags_zns_8(result); } @@ -296,7 +311,10 @@ impl CPU { let sub_carry = (!d & r) | (r & result) | (result & !d); self.set_clear_flag(StatusFlag::HalfCarry, sub_carry & 0b1000 != 0); self.set_clear_flag(StatusFlag::Carry, sub_carry & 0b1000_0000 != 0); - self.set_clear_flag(StatusFlag::TwosComplementOverflow, ((d & !r & !result) | (!d & r & result)) & 0x80 == 0x80); + self.set_clear_flag( + StatusFlag::TwosComplementOverflow, + ((d & !r & !result) | (!d & r & result)) & 0x80 == 0x80, + ); self.update_flags_zns_8(result); } @@ -313,7 +331,10 @@ impl CPU { let sub_carry = (!d & r) | (r & result) | (result & !d); self.set_clear_flag(StatusFlag::HalfCarry, sub_carry & 0b1000 != 0); self.set_clear_flag(StatusFlag::Carry, sub_carry & 0b1000_0000 != 0); - self.set_clear_flag(StatusFlag::TwosComplementOverflow, ((d & !r & !result) | (!d & r & result)) & 0x80 == 0x80); + self.set_clear_flag( + StatusFlag::TwosComplementOverflow, + ((d & !r & !result) | (!d & r & result)) & 0x80 == 0x80, + ); self.update_flags_Rzns(result); } @@ -355,7 +376,13 @@ impl CPU { Ok(v) => v, Err(e) => return Err(CPUError::DecodingError(e)), }; - debug!(self.logger, "CPU: pc={:06X} sp={:04X} Fetch: {: <40}", self.pc, self.get_sp(ram), format!("{}", ins)); + debug!( + self.logger, + "CPU: pc={:06X} sp={:04X} Fetch: {: <40}", + self.pc, + self.get_sp(ram), + format!("{}", ins) + ); self.pc += (ins.size() / 2) as u32; @@ -365,12 +392,12 @@ impl CPU { Instruction::CLR(ref r) => { self.set_register(r, 0); self.update_flags_znv0s(0); - }, + } Instruction::EOR(ref d, ref r) => { self.registers[d.as_usize()] ^= self.registers[r.as_usize()]; let r = self.registers[d.as_usize()]; self.update_flags_znv0s(r); - }, + } Instruction::LDI(ref r, v) => self.set_register(r, v), Instruction::SER(ref r) => self.set_register(r, 0xFF), Instruction::RJMP(v) => { @@ -379,18 +406,18 @@ impl CPU { info!(self.logger, "HALTED "); return Err(CPUError::Exit); } - }, + } Instruction::CLR_FLAG(v) => self.clear_flag(v), Instruction::SET_FLAG(v) => self.set_flag(v), Instruction::CPI(ref r, v) => { let rv = self.get_register(r); self.update_flags_sub_zns(rv.wrapping_sub(v), rv, v); - }, + } Instruction::CP(ref r, ref i) => { let rv = self.get_register(r); let iv = self.get_register(i); self.update_flags_sub_zns(rv.wrapping_sub(iv), rv, iv); - }, + } Instruction::CPC(ref d, ref r) => { let rd: u8 = self.get_register(d); let rr: u8 = self.get_register(r); @@ -402,12 +429,12 @@ impl CPU { }; self.update_flags_sub_Rzns(s, rd, rr); - }, + } Instruction::BR_IF(offset, flag, test) => { if self.test_flag(flag) == test { self.pc = self.pc.wrapping_add(offset as _); } - }, + } Instruction::ST(ref ptr, ref src_reg, ref inc_mode) => { let base = self.get_register_pair(ptr); let addr = match *inc_mode { @@ -415,15 +442,15 @@ impl CPU { IncrementMode::PreDecrement => { self.set_register_pair(ptr, base.wrapping_sub(1)); base.wrapping_sub(1) - }, + } IncrementMode::PostIncrement => { self.set_register_pair(ptr, base.wrapping_add(1)); base - }, + } IncrementMode::ConstantOffset(o) => base.wrapping_add(o as _), }; self.ram_write(ram, addr, self.get_register(src_reg))?; - }, + } Instruction::LD(ref dst_reg, ref ptr, ref inc_mode) => { let base = self.get_register_pair(ptr); if ptr.low() == 26 && ram[chip_definitions::IOAdress::RAMPX as usize] > 0 { @@ -438,41 +465,45 @@ impl CPU { IncrementMode::PreDecrement => { self.set_register_pair(ptr, base.wrapping_sub(1)); base.wrapping_sub(1) - }, + } IncrementMode::PostIncrement => { self.set_register_pair(ptr, base.wrapping_add(1)); base - }, + } IncrementMode::ConstantOffset(o) => base.wrapping_add(o as _), }; let v = self.ram_read(ram, addr)?; self.set_register(dst_reg, v); - }, + } Instruction::ELPM(ref dst_reg, ref inc_mode) => { - let Zb = self.get_register_pair(&30u8.into()); // TODO: Only use required bits, other read as zero (according to datasheet) let Z = - Zb as usize | - (ram[chip_definitions::IOAdress::RAMPZ as usize] as usize) << 16 - ; + Zb as usize | (ram[chip_definitions::IOAdress::RAMPZ as usize] as usize) << 16; match *inc_mode { IncrementMode::PostIncrement => { self.set_register_pair(&30u8.into(), Zb.wrapping_add(1)); - ram[chip_definitions::IOAdress::RAMPZ as usize] = (Z.wrapping_add(1) >> 16) as u8; - }, + ram[chip_definitions::IOAdress::RAMPZ as usize] = + (Z.wrapping_add(1) >> 16) as u8; + } _ => { // This instruction does only support None + PostIncrement - }, + } } if Z >= rom.len() { - warn!(self.logger, "ELPM OOB: RAMPZ={:02X} Z={:04X} len={:06X} ", Z >> 16, Zb, rom.len()); + warn!( + self.logger, + "ELPM OOB: RAMPZ={:02X} Z={:04X} len={:06X} ", + Z >> 16, + Zb, + rom.len() + ); // return Err(CPUError::OutOfBoundsException); return Ok(0xFF); // Hack I kno but emulator does it like that } let d = rom[Z as usize]; self.set_register(dst_reg, d); - }, + } Instruction::LPM(ref dst_reg, ref inc_mode) => { let Z = self.get_register_pair(&30u8.into()); let d = rom[Z as usize]; @@ -480,16 +511,16 @@ impl CPU { match *inc_mode { IncrementMode::PostIncrement => { self.set_register_pair(&30u8.into(), Z.wrapping_add(1)); - }, + } _ => { // This instruction does only support None + PostIncrement - }, + } } - }, + } Instruction::OUT(ref addr, ref val) => { let val = self.get_register(val); self.ram_write(ram, *addr, val)?; - }, + } Instruction::IN(ref reg, ref addr) => { let v = self.ram_read(ram, *addr)?; self.set_register(reg, v); @@ -500,35 +531,35 @@ impl CPU { self.push(ram, ((ret_to >> 8) & 0xFF) as u8)?; self.push(ram, (ret_to & 0xFF) as u8)?; self.pc = *addr; - }, + } Instruction::RCALL(ref addr) => { let ret_to = self.pc; self.push(ram, ((ret_to >> 16) & 0xFF) as u8)?; self.push(ram, ((ret_to >> 8) & 0xFF) as u8)?; self.push(ram, (ret_to & 0xFF) as u8)?; self.pc = (self.pc as i32 + *addr as i32) as u32; - }, + } Instruction::RET => { let mut ret_to = self.pop(ram)? as u32; ret_to += (self.pop(ram)? as u32) << 8; ret_to += (self.pop(ram)? as u32) << 16; self.pc = ret_to as _; - }, + } Instruction::POP(ref reg) => { let v = self.pop(ram)?; self.registers[reg.as_usize()] = v; - }, + } Instruction::PUSH(ref reg) => { let v = self.registers[reg.as_usize()]; self.push(ram, v)?; - }, + } Instruction::SUBI(ref d, ref v) => { let dv = self.get_register(d); let vres = dv.wrapping_sub(*v); self.update_flags_sub_zns(vres, dv, *v); self.set_register(d, vres); - }, + } Instruction::SBC(ref d, ref r) => { let dv = self.get_register(d); let rv = self.get_register(r); @@ -539,7 +570,7 @@ impl CPU { self.update_flags_sub_Rzns(vres, dv, rv); self.set_register(d, vres); - }, + } Instruction::SBCI(ref d, ref v) => { let dv = self.get_register(d); let mut vres = dv.wrapping_sub(*v); @@ -549,11 +580,11 @@ impl CPU { self.update_flags_sub_Rzns(vres, dv, *v); self.set_register(d, vres); - }, + } Instruction::MOVW(ref d, ref r) => { let t = self.get_register_pair(r); self.set_register_pair(d, t); - }, + } Instruction::OR(ref d, ref r) => { let t = self.get_register(d) | self.get_register(r); self.set_register(d, t); @@ -563,28 +594,28 @@ impl CPU { let t = self.get_register(d) | *v; self.set_register(d, t); self.update_flags_znv0s(t); - }, + } Instruction::AND(ref d, ref r) => { let t = self.get_register(d) & self.get_register(r); self.set_register(d, t); self.update_flags_znv0s(t); - }, + } Instruction::TST(ref r) => { let t = self.get_register(r); self.update_flags_znv0s(t); - }, + } Instruction::ANDI(ref d, ref v) => { let t = self.get_register(d) & *v; self.set_clear_flag(StatusFlag::Zero, t == 0); self.set_register(d, t); - }, + } Instruction::ADD(ref d, ref r) => { let t = self.get_register(d).wrapping_add(self.get_register(r)); let dd = self.get_register(d); self.set_clear_flag(StatusFlag::Carry, t < dd); self.set_clear_flag(StatusFlag::Zero, t == 0); self.set_register(d, t); - }, + } Instruction::ADC(ref d, ref r) => { let mut t = self.get_register(d).wrapping_add(self.get_register(r)); if self.test_flag(StatusFlag::Carry) { @@ -594,41 +625,47 @@ impl CPU { self.set_clear_flag(StatusFlag::Carry, t < dd); self.set_clear_flag(StatusFlag::Zero, t == 0); self.set_register(d, t); - }, + } Instruction::SUB(ref d, ref r) => { let dv = self.get_register(d); let rv = self.get_register(r); let res = dv.wrapping_sub(rv); self.update_flags_sub_zns(res, dv, rv); self.set_register(d, res); - }, + } Instruction::MOV(ref d, ref r) => { let v = self.get_register(r); self.set_register(d, v); - }, + } Instruction::ADIW(ref d, ref v) => { let dv = self.get_register_pair(d); let res = dv.wrapping_add(*v); self.set_register_pair(d, res); - self.set_clear_flag(StatusFlag::TwosComplementOverflow, (dv & !res) & 0x8000 == 0x8000); + self.set_clear_flag( + StatusFlag::TwosComplementOverflow, + (dv & !res) & 0x8000 == 0x8000, + ); self.set_clear_flag(StatusFlag::Carry, (!dv & res) & 0x8000 == 0x8000); self.update_flags_zns_16(res); - }, + } Instruction::SBIW(ref d, ref v) => { let dv = self.get_register_pair(d); let res = dv.wrapping_sub(*v as _); - self.set_clear_flag(StatusFlag::TwosComplementOverflow, (!dv & res) & 0x8000 == 0x8000); + self.set_clear_flag( + StatusFlag::TwosComplementOverflow, + (!dv & res) & 0x8000 == 0x8000, + ); self.set_clear_flag(StatusFlag::Carry, (dv & !res) & 0x8000 == 0x8000); self.update_flags_zns_16(res); self.set_register_pair(d, res); - }, + } Instruction::DEC(ref r) => { let rv = self.get_register(r); let res = rv.wrapping_sub(1); self.set_register(r, res); self.set_clear_flag(StatusFlag::TwosComplementOverflow, res == 0x7F); self.update_flags_zns_8(res); - }, + } Instruction::INC(ref r) => { let rv = self.get_register(r); let res = rv.wrapping_add(1); @@ -636,82 +673,94 @@ impl CPU { self.set_clear_flag(StatusFlag::TwosComplementOverflow, res == 0x80); self.update_flags_zns_8(res); - }, + } Instruction::STS16(ref addr, ref r) => { let rampd = ram[chip_definitions::IOAdress::RAMPD as usize] as u32; - if rampd != 0 { panic!("This is unexpected (for now)"); } + if rampd != 0 { + panic!("This is unexpected (for now)"); + } self.ram_write(ram, *addr, self.get_register(r))?; - }, + } Instruction::STS8(ref addr, ref r) => { self.ram_write(ram, *addr as u16, self.get_register(r))?; - }, + } Instruction::LDS16(ref r, ref addr) => { let rampd = ram[chip_definitions::IOAdress::RAMPD as usize] as u32; - if rampd != 0 { panic!("This is unexpected (for now)"); } + if rampd != 0 { + panic!("This is unexpected (for now)"); + } let v = self.ram_read(ram, *addr)?; self.set_register(r, v); - }, + } Instruction::LDS8(ref r, ref addr) => { let v = self.ram_read(ram, *addr as u16)?; self.set_register(r, v); - }, + } Instruction::LSL(ref r) => { let v = self.get_register(r); self.set_clear_flag(StatusFlag::Carry, v & 0x80 == 0x80); self.set_register(r, v << 1); self.set_clear_flag(StatusFlag::Zero, (v << 1) == 0); - }, + } Instruction::ROL(ref r) => { let v = self.get_register(r); - let c = if self.test_flag(StatusFlag::Carry) { 1 } else { 0 }; + let c = if self.test_flag(StatusFlag::Carry) { + 1 + } else { + 0 + }; self.set_clear_flag(StatusFlag::Carry, v & 0x80 == 0x80); self.set_register(r, v << 1 | c); self.set_clear_flag(StatusFlag::Zero, (v << 1) | c == 0); - }, + } Instruction::ASR(ref r) => { let rv = self.get_register(r); let res = rv >> 1 | (rv & 0x80); self.set_register(r, res); self.update_flags_zcnvs(res, rv); - }, + } Instruction::LSR(ref r) => { let rv = self.get_register(r); self.set_register(r, rv >> 1); self.clear_flag(StatusFlag::Negative); self.update_flags_zcvs(rv >> 1, rv); - }, + } Instruction::ROR(ref r) => { let rv = self.get_register(r); - let c = if self.test_flag(StatusFlag::Carry) { 0x80 } else { 0 }; + let c = if self.test_flag(StatusFlag::Carry) { + 0x80 + } else { + 0 + }; let res = rv >> 1 | c; self.set_register(r, res); self.update_flags_zcnvs(res, rv); - }, + } Instruction::SBRS(ref r, ref bit) => { let r = self.get_register(r); if (r & (1 << *bit)) > 0 { self.pc += 1; } - }, + } Instruction::SBRC(ref r, ref bit) => { let r = self.get_register(r); - if (r & (1 << *bit)) == 0{ + if (r & (1 << *bit)) == 0 { self.pc += 1; } - }, + } Instruction::CPSE(ref r, ref d) => { if self.get_register(r) == self.get_register(d) { // TODO: assume 2b instruction after this one. self.pc += 1; } - }, + } Instruction::COM(ref r) => { let rv = self.get_register(r); let res = 0xFFu8.wrapping_sub(rv); self.set_register(r, res); self.update_flags_znv0s(res); self.set_flag(StatusFlag::Carry); - }, + } Instruction::NEG(ref r) => { let rv = self.get_register(r); let res = 0u8.wrapping_sub(rv); @@ -720,7 +769,7 @@ impl CPU { self.set_clear_flag(StatusFlag::TwosComplementOverflow, res == 0x80); self.set_clear_flag(StatusFlag::Carry, res != 0); self.update_flags_zns_8(rv); - }, + } Instruction::MUL(ref r, ref d) => { // R1:R0 ← Rd × Rr(unsigned ← unsigned × unsigned) let r = self.get_register(r) as u16; @@ -730,20 +779,20 @@ impl CPU { self.registers[1] = ((v >> 8) & 0xFF) as u8; self.set_clear_flag(StatusFlag::Carry, v & 0x80 == 0x80); self.set_clear_flag(StatusFlag::Zero, v == 0); - }, + } Instruction::BST(ref r, ref v) => { let r = self.get_register(r); self.set_clear_flag(StatusFlag::BitCopyStorage, r & (1 << *v) != 0); - }, + } Instruction::SWAP(ref r) => { let rv = self.get_register(r); self.set_register(r, rv >> 4 | ((rv << 4) & 0xF0)); - }, + } Instruction::BREAK => { return Err(CPUError::Breakpoint); } - Instruction::NOP => {}, - _ => return Err(CPUError::UnimplementedInstruction) + Instruction::NOP => {} + _ => return Err(CPUError::UnimplementedInstruction), } Ok(ins.cycles()) diff --git a/src/decoder.rs b/src/decoder.rs index a8db18f..d4dded0 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -1,5 +1,4 @@ -use regs::{GeneralPurposeRegister, GeneralPurposeRegisterPair, IORegister, - StatusFlag, PC}; +use regs::{GeneralPurposeRegister, GeneralPurposeRegisterPair, IORegister, StatusFlag, PC}; use std::fmt; @@ -11,10 +10,10 @@ pub enum DecodingError { #[derive(Debug)] pub enum IncrementMode { - None, // [Z] - PreDecrement, // [-Z] - PostIncrement, // [Z+] - ConstantOffset(u8), // e.g. [Z+q] + None, // [Z] + PreDecrement, // [-Z] + PostIncrement, // [Z+] + ConstantOffset(u8), // e.g. [Z+q] } #[derive(Debug)] @@ -42,7 +41,7 @@ pub enum Instruction { DEC(GeneralPurposeRegister), EICALL, EIJMP, - ELPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement + ELPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement EOR(GeneralPurposeRegister, GeneralPurposeRegister), FMUL(GeneralPurposeRegister, GeneralPurposeRegister), FMULS(GeneralPurposeRegister, GeneralPurposeRegister), @@ -55,11 +54,15 @@ pub enum Instruction { LAC(GeneralPurposeRegister), LAS(GeneralPurposeRegister), LAT(GeneralPurposeRegister), - LD(GeneralPurposeRegister, GeneralPurposeRegisterPair, IncrementMode), + LD( + GeneralPurposeRegister, + GeneralPurposeRegisterPair, + IncrementMode, + ), LDI(GeneralPurposeRegister, u8), - LDS8(GeneralPurposeRegister, u8), // Two variants, with u8 and u16 + LDS8(GeneralPurposeRegister, u8), // Two variants, with u8 and u16 LDS16(GeneralPurposeRegister, u16), - LPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement + LPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement LSL(GeneralPurposeRegister), LSR(GeneralPurposeRegister), MOV(GeneralPurposeRegister, GeneralPurposeRegister), @@ -74,10 +77,10 @@ pub enum Instruction { OUT(IORegister, GeneralPurposeRegister), POP(GeneralPurposeRegister), PUSH(GeneralPurposeRegister), - RCALL(i16), // These are relative and have only 12 bits. + RCALL(i16), // These are relative and have only 12 bits. RET, RETI, - RJMP(i16), // These are relative and have only 12 bits. + RJMP(i16), // These are relative and have only 12 bits. ROL(GeneralPurposeRegister), ROR(GeneralPurposeRegister), SBC(GeneralPurposeRegister, GeneralPurposeRegister), @@ -92,9 +95,13 @@ pub enum Instruction { SER(GeneralPurposeRegister), SET_FLAG(StatusFlag), // TODO how is it really called? (CLR) SLEEP, - SPM(IncrementMode), // Only supports PostIncrement - ST(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), - STS8(u8, GeneralPurposeRegister), // Two variants, u8/u16 + SPM(IncrementMode), // Only supports PostIncrement + ST( + GeneralPurposeRegisterPair, + GeneralPurposeRegister, + IncrementMode, + ), + STS8(u8, GeneralPurposeRegister), // Two variants, u8/u16 STS16(u16, GeneralPurposeRegister), SUB(GeneralPurposeRegister, GeneralPurposeRegister), SUBI(GeneralPurposeRegister, u8), @@ -104,7 +111,6 @@ pub enum Instruction { XCH(GeneralPurposeRegister), } - impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -116,7 +122,9 @@ impl fmt::Display for Instruction { Instruction::ASR(ref r) => write!(f, "ASR {}", r), Instruction::BLD(ref r, v) => write!(f, "BLD {}, {}", r, v), Instruction::BREAK => write!(f, "BREAK"), - Instruction::BR_IF(rel, ref flag, tgt) => write!(f, "BR_IF {}, {:?}=={}", rel, flag, tgt), + Instruction::BR_IF(rel, ref flag, tgt) => { + write!(f, "BR_IF {}, {:?}=={}", rel, flag, tgt) + } Instruction::BST(ref r, v) => write!(f, "BST {}, {}", r, v), Instruction::CALL(pc) => write!(f, "CALL {:06X}", pc), Instruction::CBI(ref ior, v) => write!(f, "CBI {}, {}", ior, v), @@ -193,7 +201,6 @@ impl fmt::Display for Instruction { } } - impl Instruction { // TODO: There are more // LDS32 STS32 @@ -201,7 +208,7 @@ impl Instruction { match *self { Instruction::JMP(_) | Instruction::CALL(_) => 4, Instruction::STS16(_, _) | Instruction::LDS16(_, _) => 4, - _ => 2 + _ => 2, } } @@ -230,12 +237,16 @@ pub fn decode(data: &[u8]) -> Result { 0b1001_0100_1000_1000 => { // CLR_FLAG TODO HOW IS IT CALLED let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; - return Ok(Instruction::CLR_FLAG(StatusFlag::try_from_idx(idx).unwrap())); - }, + return Ok(Instruction::CLR_FLAG( + StatusFlag::try_from_idx(idx).unwrap(), + )); + } 0b1001_0100_0000_1000 => { // SET_FLAG TODO HOW IS IT CALLED let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; - return Ok(Instruction::SET_FLAG(StatusFlag::try_from_idx(idx).unwrap())); + return Ok(Instruction::SET_FLAG( + StatusFlag::try_from_idx(idx).unwrap(), + )); } _ => {} } @@ -261,7 +272,7 @@ pub fn decode(data: &[u8]) -> Result { // Now things will get ugly, we have a lot of different prefix sizes for // different instructions. - + /* 0000_0011_1: 0000_0011_0: @@ -301,16 +312,22 @@ pub fn decode(data: &[u8]) -> Result { let A = ((v & 0b1111_1000) >> 3) as u16; let b = (v & 0b0111) as u8; match v & 0b1111_1111_0000_0000 { - 0b1001_0110_0000_0000 => return Ok(Instruction::ADIW(((d & 0b11) * 2 + 24).into(), K as u16)), + 0b1001_0110_0000_0000 => { + return Ok(Instruction::ADIW(((d & 0b11) * 2 + 24).into(), K as u16)) + } 0b0000_0001_0000_0000 => return Ok(Instruction::MOVW((d * 2).into(), (r * 2).into())), 0b0000_0010_0000_0000 => return Ok(Instruction::MULS((d + 16).into(), (r + 16).into())), - 0b1001_0111_0000_0000 => return Ok(Instruction::SBIW(((d & 0b11) * 2 + 24).into(), K as u16)), - 0b1110_1111_0000_0000 => if r == 0b1111 { return Ok(Instruction::SER((d + 16).into())) } + 0b1001_0111_0000_0000 => { + return Ok(Instruction::SBIW(((d & 0b11) * 2 + 24).into(), K as u16)) + } + 0b1110_1111_0000_0000 => if r == 0b1111 { + return Ok(Instruction::SER((d + 16).into())); + }, 0b1001_1010_0000_0000 => return Ok(Instruction::SBI(A, b)), 0b1001_1011_0000_0000 => return Ok(Instruction::SBIS(A, b)), 0b1001_1001_0000_0000 => return Ok(Instruction::SBIC(A, b)), 0b1001_1000_0000_0000 => return Ok(Instruction::CBI(A, b)), - _ => {}, + _ => {} } } @@ -325,31 +342,72 @@ pub fn decode(data: &[u8]) -> Result { // mode: 00 -> None, 01 -> PostIncrement, 10 -> PreDecrement let mode = v & 0b11; let do_store = v & 0b0010_0000_0000 != 0; - match (do_store, v & 0b0001_0000_0000_0000 > 0, (v >> 2) & 0b11, mode) { + match ( + do_store, + v & 0b0001_0000_0000_0000 > 0, + (v >> 2) & 0b11, + mode, + ) { // LD X - (false, true, 0b11, 0b00) => return Ok(Instruction::LD(dr, 26.into(), IncrementMode::None)), - (false, true, 0b11, 0b01) => return Ok(Instruction::LD(dr, 26.into(), IncrementMode::PostIncrement)), - (false, true, 0b11, 0b10) => return Ok(Instruction::LD(dr, 26.into(), IncrementMode::PreDecrement)), + (false, true, 0b11, 0b00) => { + return Ok(Instruction::LD(dr, 26.into(), IncrementMode::None)) + } + (false, true, 0b11, 0b01) => { + return Ok(Instruction::LD(dr, 26.into(), IncrementMode::PostIncrement)) + } + (false, true, 0b11, 0b10) => { + return Ok(Instruction::LD(dr, 26.into(), IncrementMode::PreDecrement)) + } // LD Y - (false, false, 0b10, 0b00) => return Ok(Instruction::LD(dr, 28.into(), IncrementMode::None)), - (false, true, 0b10, 0b01) => return Ok(Instruction::LD(dr, 28.into(), IncrementMode::PostIncrement)), - (false, true, 0b10, 0b10) => return Ok(Instruction::LD(dr, 28.into(), IncrementMode::PreDecrement)), + (false, false, 0b10, 0b00) => { + return Ok(Instruction::LD(dr, 28.into(), IncrementMode::None)) + } + (false, true, 0b10, 0b01) => { + return Ok(Instruction::LD(dr, 28.into(), IncrementMode::PostIncrement)) + } + (false, true, 0b10, 0b10) => { + return Ok(Instruction::LD(dr, 28.into(), IncrementMode::PreDecrement)) + } // LD Z - (false, false, 0b00, 0b00) => return Ok(Instruction::LD(dr, 30.into(), IncrementMode::None)), - (false, true, 0b00, 0b01) => return Ok(Instruction::LD(dr, 30.into(), IncrementMode::PostIncrement)), - (false, true, 0b00, 0b10) => return Ok(Instruction::LD(dr, 30.into(), IncrementMode::PreDecrement)), + (false, false, 0b00, 0b00) => { + return Ok(Instruction::LD(dr, 30.into(), IncrementMode::None)) + } + (false, true, 0b00, 0b01) => { + return Ok(Instruction::LD(dr, 30.into(), IncrementMode::PostIncrement)) + } + (false, true, 0b00, 0b10) => { + return Ok(Instruction::LD(dr, 30.into(), IncrementMode::PreDecrement)) + } // ST X - (true, true, 0b11, 0b00) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::None)), - (true, true, 0b11, 0b01) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PostIncrement)), - (true, true, 0b11, 0b10) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PreDecrement)), + (true, true, 0b11, 0b00) => { + return Ok(Instruction::ST(26.into(), dr, IncrementMode::None)) + } + (true, true, 0b11, 0b01) => { + return Ok(Instruction::ST(26.into(), dr, IncrementMode::PostIncrement)) + } + (true, true, 0b11, 0b10) => { + return Ok(Instruction::ST(26.into(), dr, IncrementMode::PreDecrement)) + } // ST Y - (true, false, 0b10, 0b00) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::None)), - (true, true, 0b10, 0b01) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PostIncrement)), - (true, true, 0b10, 0b10) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PreDecrement)), + (true, false, 0b10, 0b00) => { + return Ok(Instruction::ST(28.into(), dr, IncrementMode::None)) + } + (true, true, 0b10, 0b01) => { + return Ok(Instruction::ST(28.into(), dr, IncrementMode::PostIncrement)) + } + (true, true, 0b10, 0b10) => { + return Ok(Instruction::ST(28.into(), dr, IncrementMode::PreDecrement)) + } // ST Z - (true, false, 0b00, 0b00) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::None)), - (true, true, 0b00, 0b01) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PostIncrement)), - (true, true, 0b00, 0b10) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PreDecrement)), + (true, false, 0b00, 0b00) => { + return Ok(Instruction::ST(30.into(), dr, IncrementMode::None)) + } + (true, true, 0b00, 0b01) => { + return Ok(Instruction::ST(30.into(), dr, IncrementMode::PostIncrement)) + } + (true, true, 0b00, 0b10) => { + return Ok(Instruction::ST(30.into(), dr, IncrementMode::PreDecrement)) + } // POP: 1001 000d dddd 1111 (false, true, 0b11, 0b11) => return Ok(Instruction::POP(dr)), @@ -366,11 +424,15 @@ pub fn decode(data: &[u8]) -> Result { // ELPM (2) 1001 000d dddd 0110 (false, true, 0b01, 0b10) => return Ok(Instruction::ELPM(dr, IncrementMode::None)), // ELPM (3) 1001 000d dddd 0111 - (false, true, 0b01, 0b11) => return Ok(Instruction::ELPM(dr, IncrementMode::PostIncrement)), + (false, true, 0b01, 0b11) => { + return Ok(Instruction::ELPM(dr, IncrementMode::PostIncrement)) + } // LPM (2): 1001 000d dddd 0100 (false, true, 0b01, 0b00) => return Ok(Instruction::LPM(dr, IncrementMode::None)), // LPM (3): 1001 000d dddd 0101 - (false, true, 0b01, 0b01) => return Ok(Instruction::LPM(dr, IncrementMode::PostIncrement)), + (false, true, 0b01, 0b01) => { + return Ok(Instruction::LPM(dr, IncrementMode::PostIncrement)) + } // TODO: LDS32 STS32 // STS32 1001 001r rrrr 0000 kkkk kkkk kkkk kkkk (true, true, 0, 0) => { @@ -379,7 +441,7 @@ pub fn decode(data: &[u8]) -> Result { Some(k) => return Ok(Instruction::STS16(k, dr)), None => return Err(DecodingError::TruncatedInstruction), } - }, + } // LDS32 1001 000d dddd 0000 kkkk kkkk kkkk kkkk (false, true, 0, 0) => { //LDS32 @@ -422,7 +484,7 @@ pub fn decode(data: &[u8]) -> Result { return Ok(Instruction::SBRS(r, b)); } } - }, + } // BLD '1111 100d dddd 0bbb' // BST '1111 101d dddd 0bbb' 0b1111_1000_0000_0000 | 0b1111_1010_0000_0000 => { @@ -475,10 +537,10 @@ pub fn decode(data: &[u8]) -> Result { } } } - _ => {}, + _ => {} } } - _ => {}, + _ => {} } { @@ -492,14 +554,14 @@ pub fn decode(data: &[u8]) -> Result { } else { return Ok(Instruction::ADC(d5, r5)); } - }, + } 0b0000_1100_0000_0000 => { if d5 == r5 { return Ok(Instruction::LSL(d5)); } else { return Ok(Instruction::ADD(d5, r5)); } - }, + } 0b0000_1000_0000_0000 => return Ok(Instruction::SBC(d5, r5)), 0b0000_0100_0000_0000 => return Ok(Instruction::CPC(d5, r5)), 0b1001_1100_0000_0000 => return Ok(Instruction::MUL(d5, r5)), @@ -608,11 +670,35 @@ pub fn decode(data: &[u8]) -> Result { let dr = (((v >> 4) & 0b11111) as u8).into(); let q = (((v >> 8) & 0b10_0000) | ((v >> 7) & 0b1_1000) | (v & 0b111)) as u8; match (v & 0b1000, w) { - (0b1000, false) => return Ok(Instruction::LD(dr, 28.into(), IncrementMode::ConstantOffset(q))), - (0b1000, true) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::ConstantOffset(q))), - (0b0000, false) => return Ok(Instruction::LD(dr, 30.into(), IncrementMode::ConstantOffset(q))), - (0b0000, true) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::ConstantOffset(q))), - _ => {}, + (0b1000, false) => { + return Ok(Instruction::LD( + dr, + 28.into(), + IncrementMode::ConstantOffset(q), + )) + } + (0b1000, true) => { + return Ok(Instruction::ST( + 28.into(), + dr, + IncrementMode::ConstantOffset(q), + )) + } + (0b0000, false) => { + return Ok(Instruction::LD( + dr, + 30.into(), + IncrementMode::ConstantOffset(q), + )) + } + (0b0000, true) => { + return Ok(Instruction::ST( + 30.into(), + dr, + IncrementMode::ConstantOffset(q), + )) + } + _ => {} } } Err(DecodingError::UndefinedInstruction(v)) diff --git a/src/gdbstub.rs b/src/gdbstub.rs index d9c2df3..baf11f4 100644 --- a/src/gdbstub.rs +++ b/src/gdbstub.rs @@ -57,11 +57,11 @@ impl GDBPacket { } let checksum = u8::from_str_radix( - std::str::from_utf8(&checksum) - .map_err(|_| (GDBPacketError::InvalidFormat))?, 16) - .map_err(|_| (GDBPacketError::InvalidFormat))?; + std::str::from_utf8(&checksum).map_err(|_| (GDBPacketError::InvalidFormat))?, + 16, + ).map_err(|_| (GDBPacketError::InvalidFormat))?; let p = GDBPacket { - raw_data: decoded_data.clone() + raw_data: decoded_data.clone(), }; if p.gen_checksum() == checksum { @@ -79,8 +79,11 @@ impl GDBPacket { let mut r = Vec::new(); for e in self.raw_data.iter() { match *e { - b'$' | b'#' | b'}' | b'*' => { r.push(b'}'); r.push(*e ^ 0x20); } - _ => r.push(*e) + b'$' | b'#' | b'}' | b'*' => { + r.push(b'}'); + r.push(*e ^ 0x20); + } + _ => r.push(*e), } } r @@ -117,10 +120,11 @@ struct GDBStub<'a> { impl<'a> GDBStub<'a> { fn stop_reply(&self) -> String { // Send SIGTRAP along with SREG/SP/PC. - format!("T0520:{:02X};21:{:04X};22:{:08X};", - self.chip.cpu.sreg, - self.chip.cpu.get_sp(&self.chip.ram).swap_bytes(), - (2 * self.chip.cpu.pc).swap_bytes() + format!( + "T0520:{:02X};21:{:04X};22:{:08X};", + self.chip.cpu.sreg, + self.chip.cpu.get_sp(&self.chip.ram).swap_bytes(), + (2 * self.chip.cpu.pc).swap_bytes() ).to_string() } @@ -135,7 +139,9 @@ impl<'a> GDBStub<'a> { panic!("Connection closed?") } // Wait for beginning of pkg. - if buf.len() == 0 && s[0] != b'$' { continue; } + if buf.len() == 0 && s[0] != b'$' { + continue; + } buf.push(s[0]); if !has_checksum { @@ -145,7 +151,10 @@ impl<'a> GDBStub<'a> { if n_checksum_bytes == 2 { // Can we parse the packet? match GDBPacket::from_packet(&buf) { - Ok(p) => { pkg = Some(p); break 'rx_loop; }, + Ok(p) => { + pkg = Some(p); + break 'rx_loop; + } Err(_) => panic!("Could not parse pkg: {:?}", buf), } } @@ -154,17 +163,22 @@ impl<'a> GDBStub<'a> { let pkg = pkg.unwrap(); // Try to parse the string. let response = std::str::from_utf8(&pkg.raw_data) - .map_err(|_| ())?.to_string(); + .map_err(|_| ())? + .to_string(); // Split it into command and values. if response.chars().nth(0).ok_or(())? == 'v' { // Multibyte word, up to the first ; (or others?). let word_payload = response.split(";").collect::>(); - Ok((word_payload[0].to_string(), - word_payload[1..].join(";").to_string())) + Ok(( + word_payload[0].to_string(), + word_payload[1..].join(";").to_string(), + )) } else { - Ok((response.chars().nth(0).ok_or(())?.to_string(), - response.chars().skip(1).collect::())) + Ok(( + response.chars().nth(0).ok_or(())?.to_string(), + response.chars().skip(1).collect::(), + )) } } @@ -194,53 +208,53 @@ impl<'a> GDBStub<'a> { let query_data = split_definitions(&payload); match &*query_data[0].0 { "Supported" => { - response = "qXfer:memory-map:read+;PacketSize=1024".into(); - }, + response = + "qXfer:memory-map:read+;PacketSize=1024,hwbreak+,swbreak+".into(); + } "Attached" => { response = "1".into(); - }, + } "Xfer" => { response = format!( "l\n\ - \n\ - \n\ - 0x80\n\ - ", - self.chip.ram.len(), self.chip.rom.len() - ).to_string().into(); - }, + \n\ + \n\ + 0x80\n\ + ", + self.chip.ram.len(), + self.chip.rom.len() + ).to_string() + .into(); + } "C" => response = "01".into(), query => { warn!(self.log, "Unknown query: '{}'", query); response = "".into(); - }, + } } } "H" => response = "OK".into(), - "?" => response = "S05".into(), + "?" => response = self.stop_reply().into(), "g" => { // Read registers. let mut reg_vals = Vec::new(); // R0-R31 for i in 0..32 { - reg_vals.push( - format!("{:02X}", self.chip.cpu.registers[i])); + reg_vals.push(format!("{:02X}", self.chip.cpu.registers[i])); } // SREG reg_vals.push(format!("{:02X}", self.chip.cpu.sreg)); // SP - reg_vals.push( - format!( - "{:04X}", - self.chip.cpu.get_sp(&self.chip.ram).swap_bytes() - ) - ); + reg_vals.push(format!( + "{:04X}", + self.chip.cpu.get_sp(&self.chip.ram).swap_bytes() + )); // PC - reg_vals.push( - format!("{:08X}", (2 * self.chip.cpu.pc).swap_bytes())); + reg_vals.push(format!("{:08X}", (2 * self.chip.cpu.pc).swap_bytes())); response = reg_vals.join("").into(); - }, + } + // TODO: G (for setting all regs). "s" | "c" => { // Step / Continue // Parse resume from. @@ -265,7 +279,7 @@ impl<'a> GDBStub<'a> { } // Send a stop reply. response = self.stop_reply().into(); - }, + } "m" => { // Read memory. let parts = payload.split(",").collect::>(); @@ -275,7 +289,6 @@ impl<'a> GDBStub<'a> { let mut err = false; for i in 0..len { let addr = mem_addr.wrapping_add(i); - // TODO: Overflow checks. if addr & 0x00f00000 > 0 { // RAM: let addr_i = addr & 0xFFFFF; @@ -304,7 +317,7 @@ impl<'a> GDBStub<'a> { } else { response = data.join("").into(); } - }, + } "M" => { // Write memory. let addrlen_content = payload.split(":").collect::>(); @@ -315,7 +328,6 @@ impl<'a> GDBStub<'a> { let mut err = false; for i in 0..len { let addr = mem_addr.wrapping_add(i); - // TODO: Overflow checks. if addr & 0x00f00000 > 0 { // RAM: let addr_i = addr & 0xFFFFF; @@ -323,14 +335,16 @@ impl<'a> GDBStub<'a> { err = true; break; } - self.chip.ram[addr_i] = u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap_or(0); + self.chip.ram[addr_i] = + u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap_or(0); } else { // ROM if addr >= self.chip.rom.len() { err = true; break; } - self.chip.rom[addr] = u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap_or(0); + self.chip.rom[addr] = + u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap_or(0); } } if err { @@ -338,7 +352,7 @@ impl<'a> GDBStub<'a> { } else { response = "OK".into(); } - }, + } "z" | "Z" => { // insert(Z)/remove(z) breakpoint. let values = payload.split(",").collect::>(); @@ -357,7 +371,7 @@ impl<'a> GDBStub<'a> { } else { response = "E05".into(); // TODO: Which error would be correct? } - }, + } "vMustReplyEmpty" => response = "".into(), _ => { info!(self.log, "Unknown cmd: {} {}", cmd, payload); @@ -365,7 +379,9 @@ impl<'a> GDBStub<'a> { } } info!(self.log, "-> {}", response); - let response = GDBPacket { raw_data: response.as_bytes().to_vec() }; + let response = GDBPacket { + raw_data: response.as_bytes().to_vec(), + }; conn.write_all(&response.to_packet_format()).unwrap(); } } diff --git a/src/regs.rs b/src/regs.rs index a5037f1..17be577 100644 --- a/src/regs.rs +++ b/src/regs.rs @@ -22,19 +22,22 @@ pub enum StatusFlag { Carry = 1 << 0, } - impl fmt::Display for StatusFlag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - StatusFlag::GlobalInterruptEnable => "I", - StatusFlag::BitCopyStorage => "T", - StatusFlag::HalfCarry => "H", - StatusFlag::SignBit => "S", - StatusFlag::TwosComplementOverflow => "V", - StatusFlag::Negative => "N", - StatusFlag::Zero => "Z", - StatusFlag::Carry => "C", - }) + write!( + f, + "{}", + match *self { + StatusFlag::GlobalInterruptEnable => "I", + StatusFlag::BitCopyStorage => "T", + StatusFlag::HalfCarry => "H", + StatusFlag::SignBit => "S", + StatusFlag::TwosComplementOverflow => "V", + StatusFlag::Negative => "N", + StatusFlag::Zero => "Z", + StatusFlag::Carry => "C", + } + ) } } @@ -81,7 +84,7 @@ impl StatusFlag { StatusFlag::SignBit, StatusFlag::HalfCarry, StatusFlag::BitCopyStorage, - StatusFlag::GlobalInterruptEnable + StatusFlag::GlobalInterruptEnable, ][i as usize]) } } @@ -95,7 +98,7 @@ impl From for GeneralPurposeRegister { if v > 31 { unreachable!(); } - Self{0: v} + Self { 0: v } } } @@ -123,14 +126,12 @@ impl PartialEq for GeneralPurposeRegister { } } - impl fmt::Display for GeneralPurposeRegister { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "R{}", self.0) } } - // Mostly the same as above, but we want to make it a different type. // type GeneralPurposeRegisterPair = struct(u8); #[derive(Debug)] @@ -141,7 +142,7 @@ impl From for GeneralPurposeRegisterPair { println!("v={}", v); unreachable!(); } - Self{0: v} + Self { 0: v } } } @@ -173,8 +174,7 @@ impl fmt::Display for GeneralPurposeRegisterPair { } } - pub type PC = u32; // TODO: Struct :) -pub type IORegister = u16; \ No newline at end of file +pub type IORegister = u16;