//! Trait for transferring data to and from a CPU pub mod iter { //! Iterates over the bytes in a [BusIO] use super::BusIO; pub struct Iter<'a> { start: usize, bus: &'a dyn BusIO, } impl<'a> Iter<'a> { pub fn new(bus: &'a dyn BusIO) -> Self { Self { start: 0, bus } } pub fn start_at(bus: &'a dyn BusIO, start: usize) -> Self { Self { start, bus } } } impl<'a> Iterator for Iter<'a> { type Item = u8; fn next(&mut self) -> Option { let Self { start, bus } = self; let out = bus.read(*start); if out.is_some() { *start += 1; } out } } } /// Trait for transferring data to and from a CPU pub trait BusIO { // required functions /// Attempts to read a byte from self, returning None if the operation failed. fn read(&self, addr: usize) -> Option; /// Attempts to write a byte to self, returning None if the operation failed. fn write(&mut self, addr: usize, data: u8) -> Option<()>; /// Does some implementation-specific debug function fn diag(&mut self, _param: usize) {} /// Gets an iterator which reads the bytes from self fn read_iter(&self) -> iter::Iter where Self: Sized, { iter::Iter::new(self) } /// Gets an iterator which reads bytes in an exclusive range fn read_iter_from(&self, start: usize) -> iter::Iter where Self: Sized, { iter::Iter::start_at(self, start) } } impl BusIO for [u8; N] { fn read(&self, addr: usize) -> Option { self.get(addr).copied() } fn write(&mut self, addr: usize, data: u8) -> Option<()> { self.get_mut(addr).map(|v| *v = data) } } impl BusIO for [u8] { fn read(&self, addr: usize) -> Option { self.get(addr).copied() } fn write(&mut self, addr: usize, data: u8) -> Option<()> { self.get_mut(addr).map(|v| *v = data) } } impl BusIO for Vec { fn read(&self, addr: usize) -> Option { self.get(addr).copied() } fn write(&mut self, addr: usize, data: u8) -> Option<()> { self.get_mut(addr).map(|v| *v = data) } } impl BusAux for T {} pub trait BusAux: BusIO { // derived functions /// Copies a contiguous region from the bus. Returns an incomplete copy on error. fn read_arr(&self, addr: usize) -> Result<[u8; N], [u8; N]> { let mut out = [0; N]; for (idx, byte) in out.iter_mut().enumerate() { match self.read(addr + idx) { Some(value) => *byte = value, None => return Err(out), } } Ok(out) } /// Gets the cart entrypoint bytes (machine code starting at 0x100) fn entry(&self) -> Result<[u8; 4], [u8; 4]> { self.read_arr(0x100) } /// Gets the logo bytes (0x104..0x134) fn logo(&self) -> Result<[u8; 0x30], [u8; 0x30]> { self.read_arr(0x104) } /// Gets the title bytes as a String /// # Note /// The title area was repartitioned for other purposes during the Game Boy's life. /// See [mfcode](BusAux::mfcode) and [cgbflag](BusAux::cgbflag). fn title(&self) -> Result { // Safety: Chars are checked to be valid ascii before cast to char Ok(self .read_arr(0x134)? // to 0x144 .iter() .copied() .take_while(|c| *c != 0 && c.is_ascii()) .map(|c| unsafe { char::from_u32_unchecked(c as u32) }) .collect()) } /// Gets the manufacturer code bytes fn mfcode(&self) -> Result<[u8; 4], [u8; 4]> { self.read_arr(0x13f) } /// Gets the CGB Flag byte fn cgbflag(&self) -> Option { self.read(0x143) } /// Gets the New Licensee bytes fn newlicensee(&self) -> Result<[u8; 2], [u8; 2]> { self.read_arr(0x144) } /// Gets the SGB Flag byte fn sgbflag(&self) -> Option { self.read(0x146) } /// Gets the Cartridge Type byte fn carttype(&self) -> Option { self.read(0x147) } /// Gets the size of the cartridge ROM (in 0x8000 byte banks) fn romsize(&self) -> Option { self.read(0x148).map(|s| 1usize.wrapping_shl(s as u32 + 1)) } /// Gets the size of the cartridge RAM (in 0x8000 byte banks) fn ramsize(&self) -> Option { self.read(0x149).map(|s| match s { 0x02 => 1, 0x03 => 4, 0x04 => 16, 0x05 => 8, _ => 0, }) } /// Gets the destination region code fn destcode(&self) -> Option { self.read(0x14a) } /// Gets the old licensee byte fn oldlicencee(&self) -> Option { self.read(0x14b) } /// Gets the Maskrom Version byte. Usually 0 fn maskromver(&self) -> Option { self.read(0x14c) } /// Gets the header checksum fn headercksum(&self) -> Option { self.read(0x14d) } /// Gets the checksum of all bytes in ROM fn globalcksum(&self) -> Option { self.read_arr(0x14e).ok().map(u16::from_be_bytes) } } pub mod interrupt { use super::BusIO; use bitfield_struct::bitfield; /// Interrupt Enable register const IE: usize = 0xffff; /// Interrupt Flags register const IF: usize = 0xff0f; #[bitfield(u8)] #[derive(PartialEq, Eq)] pub struct Interrupt { vblank: bool, lcd: bool, timer: bool, serial: bool, joypad: bool, #[bits(3)] _reserved: (), } impl Interrupts for B {} /// Functionality for working with [Interrupt]s pub trait Interrupts: BusIO { /// Gets the set of enabled [Interrupt]s fn enabled(&self) -> Option { self.read(IE).map(Interrupt::from_bits) } /// Gets the set of raised [Interrupt]s #[inline] fn raised(&self) -> Option { self.read(IF).map(Interrupt::from_bits) } /// Raises [Interrupt]s with the provided mask. fn raise(&mut self, int: Interrupt) { let flags = self.raised().unwrap_or_default(); let _ = self.write(IF, flags.into_bits() | int.into_bits()); } /// Clears [Interrupt]s with the provided mask. fn clear(&mut self, int: Interrupt) { let flags = self.raised().unwrap_or_default(); let _ = self.write(IF, flags.into_bits() & !int.into_bits()); } } }