diff --git a/boy-debug/src/bus.rs b/boy-debug/src/bus.rs index 1d2ada5..1d826f5 100644 --- a/boy-debug/src/bus.rs +++ b/boy-debug/src/bus.rs @@ -7,7 +7,9 @@ use boy::memory::io::BusIO; pub trait BusIOTools: BusIO { /// Prints all successful reads and writes fn trace(&mut self) -> TracingBus; + fn ascii(&mut self) -> AsciiSerial; + fn read_file(&mut self, path: impl AsRef) -> IoResult<&mut Self> where Self: Sized; @@ -17,6 +19,7 @@ impl BusIOTools for T { fn trace(&mut self) -> TracingBus { TracingBus { bus: self } } + fn ascii(&mut self) -> AsciiSerial { AsciiSerial { data: 0, @@ -24,6 +27,7 @@ impl BusIOTools for T { bus: self, } } + fn read_file(&mut self, path: impl AsRef) -> IoResult<&mut Self> { let data = std::fs::read(path)?; eprintln!("Read {} bytes.", data.len()); @@ -49,6 +53,7 @@ impl<'t, T: BusIO> BusIO for TracingBus<'t, T> { // } self.bus.read(addr) } + fn write(&mut self, addr: usize, data: u8) -> Option<()> { eprintln!("set [{addr:04x}], {data:02x}"); self.bus.write(addr, data) @@ -60,11 +65,13 @@ impl<'t, T: BusIO> From<&'t mut T> for TracingBus<'t, T> { Self { bus: value } } } + impl<'t, T: BusIO + AsRef<[u8]>> AsRef<[u8]> for TracingBus<'t, T> { fn as_ref(&self) -> &[u8] { self.bus.as_ref() } } + impl<'t, T: BusIO + AsMut<[u8]>> AsMut<[u8]> for TracingBus<'t, T> { fn as_mut(&mut self) -> &mut [u8] { self.bus.as_mut() @@ -93,6 +100,7 @@ impl<'t, T: BusIO + ?Sized> BusIO for AsciiSerial<'t, T> { _ => self.bus.read(addr), } } + fn write(&mut self, addr: usize, data: u8) -> Option<()> { match addr { 0xff01 => { @@ -111,6 +119,7 @@ impl<'t, T: BusIO + ?Sized> BusIO for AsciiSerial<'t, T> { } self.bus.write(addr, data) } + fn diag(&mut self, param: usize) { println!("debug: '{}'", self.string()); self.buf.clear(); diff --git a/boy-debug/src/disassembler.rs b/boy-debug/src/disassembler.rs index 622e302..ee5d84c 100644 --- a/boy-debug/src/disassembler.rs +++ b/boy-debug/src/disassembler.rs @@ -6,6 +6,7 @@ use std::iter::{Enumerate, Peekable}; pub trait Disassemble: Iterator + Sized { fn disassemble(self) -> Disassembler; } + impl + Sized> Disassemble for T { fn disassemble(self) -> Disassembler { Disassembler::new(self) @@ -29,27 +30,31 @@ impl> Iterator for Disassembler { impl> Disassembler { pub fn new(bytes: I) -> Self { - Disassembler { - bytes: bytes.enumerate().peekable(), - } + Disassembler { bytes: bytes.enumerate().peekable() } } + pub fn index(&mut self) -> Option { self.bytes.peek().map(|v| v.0) } + pub fn imm8(&mut self) -> Option { self.bytes.next().map(|v| v.1) } + pub fn smm8(&mut self) -> Option { self.imm8().map(|b| b as i8) } + pub fn imm16(&mut self) -> Option { let low = self.imm8()? as u16; let high = self.imm8()? as u16; Some(high << 8 | low) } + pub fn smm16(&mut self) -> Option { self.imm16().map(|w| w as i16) } + pub fn print(&mut self, insn: Insn) -> Option { Some(match insn { Insn::LdImm(reg) => format!("ld\t{reg}, {:02x}", self.imm8()?), @@ -79,6 +84,7 @@ impl> Disassembler { _ => format!("{insn}"), }) } + pub fn print_prefix_cb(&mut self) -> Option { let prefixed: Prefixed = self.imm8()?.into(); Some(format!("{prefixed}")) diff --git a/boy-debug/src/lib.rs b/boy-debug/src/lib.rs index 6478f2c..3b7d144 100644 --- a/boy-debug/src/lib.rs +++ b/boy-debug/src/lib.rs @@ -77,6 +77,7 @@ pub mod cli { /// `=`: Equals Sign Eq, } + impl Display for Op { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -180,16 +181,20 @@ pub mod cli { } self } + fn peek(&mut self) -> Option<&char> { self.text.peek() } + fn take(&mut self) -> &mut Self { self.text.next(); self } + fn emit(&mut self, kind: Token) -> Option { Some(kind) } + fn digits(&mut self) -> Option { let mut out = 0; while let Some(Some(next)) = self.peek().map(|c| c.to_digit(B)) { @@ -198,6 +203,7 @@ pub mod cli { } Some(Num(out)) } + fn base(&mut self) -> Option { match self.peek() { Some('b') => self.take().digits::<2>(), @@ -207,6 +213,7 @@ pub mod cli { _ => self.digits::<16>(), } } + fn ident(&mut self) -> Option { let mut out = String::new(); while let Some(&c) = self.peek().filter(|&&c| c.is_alphanumeric() || c == '_') { @@ -239,6 +246,7 @@ pub mod cli { }, }) } + /// Checks the following character, producing the provided [Token] /// if it matches the expected [char]. Otherwise, prodices [Token::Other]. fn chain(&mut self, expect: char, becomes: Token) -> Option { @@ -255,7 +263,7 @@ pub mod cli { } .emit(Token::Op(Range)) } - + fn string(&mut self) -> Option { let mut value = String::new(); while let Some(c) = self.text.next_if(|&c| c != '"') { @@ -264,8 +272,10 @@ pub mod cli { self.take().emit(Token::Ident(value)) } } + impl> Iterator for Lexer { type Item = Token; + fn next(&mut self) -> Option { match self.sync().peek()? { '[' => self.take().emit(Op(BrackOpen)), @@ -310,6 +320,7 @@ pub mod cli { pub trait Parsible: Iterator + Sized { fn parse(self) -> Parser; } + impl + Sized> Parsible for T { fn parse(self) -> Parser { Parser::new(self) @@ -330,9 +341,11 @@ pub mod cli { fn peek(&mut self) -> Option<&Token> { self.lexer.peek() } + fn take(&mut self) -> Option { self.lexer.next() } + #[must_use] fn then(&mut self, tok: Token) -> Option<&mut Self> { (*self.peek()? == tok).then(|| { @@ -472,6 +485,7 @@ pub mod cli { }; continue; } + if let Some(prec) = op.infix() { if prec.before() < level { break; @@ -501,13 +515,16 @@ pub mod cli { Term, Sign, } + impl Level { fn level(self) -> u8 { (self as u8) << 1 } + pub fn before(self) -> u8 { self.level() } + pub fn after(self) -> u8 { self.level() + 1 } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..2a35f02 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +use_small_heuristics = "Max" diff --git a/src/cpu.rs b/src/cpu.rs index e3db379..28dc533 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -35,6 +35,7 @@ pub struct Flags { fn half_carry_add(a: u8, b: u8) -> bool { (a & 0xf) + (b & 0xf) > 0xf } + /// Performs the half-carry calculation for subtraction fn half_carry_sub(a: u8, b: u8) -> bool { (a & 0xf) < (b & 0xf) @@ -145,18 +146,22 @@ impl CPU { pub fn pc(&self) -> u16 { self.pc.0 } + /// Sets the address of the next instruction pub fn set_pc(&mut self, addr: u16) { self.pc.0 = addr } + /// Gets the current instruction pub fn ir(&self) -> Insn { self.ir } + /// Sets a breakpoint. Returns true if the breakpoint was not already set. pub fn set_break(&mut self, addr: u16) -> bool { self.breakpoints.insert(addr) } + /// Removes a breakpoint. Returns true if the breakpoint was set. pub fn unset_break(&mut self, addr: u16) -> bool { self.breakpoints.remove(&addr) @@ -171,18 +176,21 @@ impl CPU { self.pc += 1; Ok(out) } + /// Read a 16-bit immediate at the program counter, post-incrementing it twice (adds two cycles) pub fn imm16(&mut self, bus: &impl BusIO) -> Result { let out = self.read16(self.pc.0, bus)?; self.pc += 2; Ok(out) } + /// Read an 8-bit value at address `addr` on the `bus` (adds one cycle) pub fn read(&mut self, addr: u16, bus: &impl BusIO) -> Result { // Reading a byte takes one cycle self.wait(); bus.read(addr as usize).ok_or(InvalidAddress(addr).into()) } + /// Read a 16-bit LE value at address `addr` on the `bus` (adds two cycles) pub fn read16(&mut self, addr: u16, bus: &impl BusIO) -> Result { let out = self.read(addr, bus)? as u16; @@ -193,9 +201,9 @@ impl CPU { pub fn write(&mut self, addr: u16, data: u8, bus: &mut impl BusIO) -> Result<(), Error> { // Writing a byte takes one cycle self.wait(); - bus.write(addr as usize, data) - .ok_or(InvalidAddress(addr).into()) + bus.write(addr as usize, data).ok_or(InvalidAddress(addr).into()) } + /// Write a 16-bit LE value to address `addr..=addr+1` on the `bus` (adds two cycles) pub fn write16(&mut self, addr: u16, data: u16, bus: &mut impl BusIO) -> Result<(), Error> { let data = data.to_le_bytes(); @@ -208,18 +216,21 @@ impl CPU { self.sp -= 1; self.write(self.sp.0, data, bus) } + /// Push a 16-bit value onto the stack (adds two cycles) pub fn push16(&mut self, data: u16, bus: &mut impl BusIO) -> Result<(), Error> { let data = data.to_le_bytes(); self.push(data[0], bus)?; self.push(data[1], bus) } + /// Pop a byte off the stack (adds one cycle) pub fn pop(&mut self, bus: &mut impl BusIO) -> Result { let out = self.read(self.sp.0, bus); self.sp += 1; out } + /// Pop a double word off the stack (adds two cycles) pub fn pop16(&mut self, bus: &mut impl BusIO) -> Result { // pushes LH, pops HL @@ -268,23 +279,28 @@ mod instructions { pub fn nop(&mut self) -> IResult { Ok(()) } + pub fn stop(&mut self) -> IResult { println!("Stop requested.\n{self}"); Ok(()) } + pub fn halt(&mut self) -> IResult { Ok(()) } + pub fn di(&mut self) -> IResult { self.ime = Ime::Disabled; Ok(()) } + pub fn ei(&mut self) -> IResult { // TODO: this is incorrect self.ime = Ime::ShouldEnable; Ok(()) } } + /// Loads and stores impl CPU { pub fn ld(&mut self, dst: R8, src: R8, bus: &mut impl BusIO) -> IResult { @@ -292,70 +308,84 @@ mod instructions { self.set_r8(dst, src, bus); Ok(()) } + pub fn ld_imm(&mut self, dst: R8, bus: &mut impl BusIO) -> IResult { // TODO: open bus behavior let src = self.imm8(bus)?; self.set_r8(dst, src, bus); Ok(()) } + pub fn ld_imm16(&mut self, dst: R16, bus: &mut impl BusIO) -> IResult { let imm16 = self.imm16(bus)?; self[dst].0 = imm16; Ok(()) } + pub fn ld_ind(&mut self, src: R16Indirect, bus: &mut impl BusIO) -> IResult { self[R8::A].0 = self.read_r16ind(src, bus)?; Ok(()) } + pub fn st_ind(&mut self, dst: R16Indirect, bus: &mut impl BusIO) -> IResult { self.write_r16ind(dst, self[R8::A].0, bus); Ok(()) } + pub fn ldc(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self[R8::C].0 as u16 | 0xff00; self[R8::A].0 = self.read(addr, bus)?; Ok(()) } + pub fn stc(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self[R8::C].0 as u16 | 0xff00; self.write(addr, self[R8::A].0, bus)?; Ok(()) } + pub fn ld_abs(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.imm16(bus)?; self[R8::A].0 = self.read(addr, bus)?; Ok(()) } + pub fn st_abs(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.imm16(bus)?; self.write(addr, self[R8::A].0, bus)?; Ok(()) } + pub fn st_sp_abs(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.imm16(bus)?; self.write16(addr, self[R16::SP].0, bus)?; Ok(()) } + pub fn ld_sp_hl(&mut self) -> IResult { self[R16::HL].0 = self[R16::SP].0; Ok(()) } + pub fn ld_hl_sp_rel(&mut self, bus: &mut impl BusIO) -> IResult { let offset = self.imm8(bus)? as i8 as i16 as u16; self[R16::HL].0 = self.sp.0.wrapping_add(offset); Ok(()) } + pub fn ldh(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.imm8(bus)? as u16 | 0xff00; self[R8::A].0 = self.read(addr, bus)?; Ok(()) } + pub fn sth(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.imm8(bus)? as u16 | 0xff00; self.write(addr, self[R8::A].0, bus)?; Ok(()) } } + /// Inc and Dec impl CPU { pub fn inc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult { @@ -364,21 +394,25 @@ mod instructions { *self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry); Ok(()) } + pub fn dec(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult { let (data, carry) = self.get_r8(reg, bus).overflowing_sub(1); self.set_r8(reg, data, bus); *self.flags_mut() = Flags::new().with_z(data == 0).with_c(carry); Ok(()) } + pub fn inc16(&mut self, reg: R16) -> IResult { self[reg] += 1; Ok(()) } + pub fn dec16(&mut self, reg: R16) -> IResult { self[reg] -= 1; Ok(()) } } + /// Rotates and shifts impl CPU { pub fn rlnca(&mut self) -> IResult { @@ -387,12 +421,14 @@ mod instructions { self.set_z(false).set_n(false).set_h(false).set_c(carry); Ok(()) } + pub fn rrnca(&mut self) -> IResult { let carry = self[R8::A].0 & 1 != 0; self[R8::A].0 = self[R8::A].0.rotate_right(1); self.set_z(false).set_n(false).set_h(false).set_c(carry); Ok(()) } + pub fn rla(&mut self) -> IResult { // TODO: optimize RLA/RRA let carry = self[R8::A].0 & 0x80 != 0; @@ -402,6 +438,7 @@ mod instructions { self.set_z(false).set_n(false).set_h(false).set_c(carry); Ok(()) } + pub fn rra(&mut self) -> IResult { let carry = self[R8::A].0 & 1 != 0; self[R8::A] >>= 1; @@ -411,6 +448,7 @@ mod instructions { Ok(()) } } + /// Funky instructions impl CPU { pub fn daa(&mut self) -> IResult { @@ -435,22 +473,26 @@ mod instructions { Ok(()) } + pub fn cpl(&mut self) -> IResult { self[R8::A] ^= 0xff; self.set_n(false).set_h(false); Ok(()) } + pub fn scf(&mut self) -> IResult { let flags = self.flags_mut(); *flags = flags.with_n(false).with_h(false).with_c(true); Ok(()) } + pub fn ccf(&mut self) -> IResult { let flags = self.flags_mut(); *flags = flags.with_n(false).with_h(false).with_c(!flags.c()); Ok(()) } } + /// Addition impl CPU { fn add_impl(&mut self, value: u8) -> IResult { @@ -469,6 +511,7 @@ mod instructions { self.add_impl(src)?; Ok(()) } + pub fn add16(&mut self, reg: R16) -> IResult { let (value, h) = (self[reg], self[R8::H].0); let value_h = (value.0 >> 8) as u8; @@ -479,13 +522,14 @@ mod instructions { *self.hl.wide_mut() += value; Ok(()) } + #[allow(unused)] pub fn add16_spi(&mut self, bus: &mut impl BusIO) -> IResult { let offset = self.imm8(bus)? as i8 as i16; // sign extend - Err(UnimplementedInsn(self.ir).into()) } } + /// Addition with carry impl CPU { fn addc_impl(&mut self, value: u8) -> IResult { @@ -493,10 +537,7 @@ mod instructions { let hc = half_carry_add(self[R8::A].0, src); let (out, carry) = self[R8::A].0.overflowing_add(src); self[R8::A].0 = out; - self.set_z(out == 0) - .set_n(false) - .set_h(hc) - .set_c(carry | carry_in); + self.set_z(out == 0).set_n(false).set_h(hc).set_c(carry | carry_in); Ok(()) } pub fn adc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult { @@ -508,6 +549,7 @@ mod instructions { self.addc_impl(src) } } + /// Subtraction impl CPU { fn sub_impl(&mut self, value: u8) -> IResult { @@ -526,6 +568,7 @@ mod instructions { self.sub_impl(value) } } + /// Subtraction with carry impl CPU { #[inline] @@ -534,10 +577,7 @@ mod instructions { let (out, carry) = self[R8::A].0.overflowing_sub(src); let hc = half_carry_sub(self[R8::A].0, src); self[R8::A].0 = out; - self.set_z(out == 0) - .set_n(true) - .set_h(hc) - .set_c(carry | cin); + self.set_z(out == 0).set_n(true).set_h(hc).set_c(carry | cin); Ok(()) } pub fn sbc(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult { @@ -549,14 +589,12 @@ mod instructions { self.subc_impl(value) } } + /// Bitwise AND impl CPU { fn and_impl(&mut self, value: u8) -> IResult { self[R8::A] &= value; - self.set_z(self[R8::A].0 == 0) - .set_n(false) - .set_h(true) - .set_c(false); + self.set_z(self[R8::A].0 == 0).set_n(false).set_h(true).set_c(false); Ok(()) } pub fn and(&mut self, reg: R8, bus: &mut impl BusIO) -> IResult { @@ -568,6 +606,7 @@ mod instructions { self.and_impl(value) } } + /// Bitwise XOR impl CPU { fn xor_impl(&mut self, value: u8) -> IResult { @@ -584,6 +623,7 @@ mod instructions { self.xor_impl(value) } } + /// Bitwise OR impl CPU { fn or_impl(&mut self, value: u8) -> IResult { @@ -600,6 +640,7 @@ mod instructions { self.or_impl(value) } } + /// Comparison impl CPU { fn cmp_impl(&mut self, src: u8) -> IResult { @@ -617,11 +658,13 @@ mod instructions { self.cmp_impl(src) } } + /// Stack ops impl CPU { pub fn insn_push(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult { self.push16(self[reg].0, bus) } + pub fn insn_pop(&mut self, reg: R16Stack, bus: &mut impl BusIO) -> IResult { self[reg].0 = self.pop16(bus)?; if let R16Stack::AF = reg { @@ -630,6 +673,7 @@ mod instructions { Ok(()) } } + /// Jumps, branches, calls, and returns impl CPU { pub fn jr(&mut self, bus: &mut impl BusIO) -> IResult { @@ -680,6 +724,7 @@ mod instructions { false => Ok(()), } } + pub fn ret(&mut self, bus: &mut impl BusIO) -> IResult { let addr = self.pop16(bus)?; self.jumpto(addr); @@ -696,10 +741,12 @@ mod instructions { self.ime = Ime::Enabled; // Technically this should go after ret's pop self.ret(bus) } + pub fn reset(&mut self, addr: u8, bus: &mut impl BusIO) -> IResult { self.call_addr(addr as u16 * 8, bus) } } + /// Prefixed instructions impl CPU { /// Rotates left, setting the carry flag to the highest bit @@ -786,7 +833,7 @@ impl CPU { pub fn run(&mut self, bus: &mut impl BusIO) -> Result { self.m_cycles = 0; // Run the current instruction - self.execute(bus)?; + let out = self.execute(bus); let prefetch_pc = self.pc.0; // Handle interrupts @@ -802,9 +849,10 @@ impl CPU { if self.breakpoints.contains(&prefetch_pc) { Err(HitBreak(prefetch_pc, self.m_cycles).into()) } else { - Ok(self.m_cycles as usize) + out.map(|_| self.m_cycles as usize) } } + pub fn fetch(&mut self, bus: &mut impl BusIO) -> Result<(), Error> { self.ir = match self.ir { // HALT fetches the current instruction, rather than the next @@ -815,6 +863,7 @@ impl CPU { }; Ok(()) } + pub fn execute(&mut self, bus: &mut impl BusIO) -> Result<(), Error> { #[allow(unused_variables)] match self.ir { @@ -874,6 +923,7 @@ impl CPU { Insn::OrI => self.ori(bus), Insn::Cp(reg) => self.cmp(reg, bus), Insn::CpI => self.cmpi(bus), + Insn::Push(reg) => self.insn_push(reg, bus), Insn::Pop(reg) => self.insn_pop(reg, bus), // Jumps @@ -894,6 +944,7 @@ impl CPU { Insn::Invalid(b) => Err(InvalidInstruction(b).into()), } } + pub fn prefixed(&mut self, bus: &mut impl BusIO) -> Result<(), Error> { match Prefixed::from(self.imm8(bus)?) { Prefixed::Rlc(reg) => self.rlc(reg, bus), @@ -919,6 +970,7 @@ impl CPU { _ => self[r8].0, } } + /// Sets an 8-bit register. This requires bus access, since `[HL]` is a valid location /// /// Panic-free alternative to [IndexMut] @@ -947,21 +999,26 @@ impl CPU { } } } + /// Reads data at an R16-indirect address pub fn read_r16ind(&mut self, r16: R16Indirect, bus: &impl BusIO) -> Result { let addr = self.get_r16ind_addr(r16); self.read(addr, bus) } + pub fn write_r16ind(&mut self, r16: R16Indirect, data: u8, bus: &mut impl BusIO) { let addr = self.get_r16ind_addr(r16); let _ = self.write(addr, data, bus); } + pub fn flags_mut(&mut self) -> &mut Flags { self.af.flags_mut() } + pub fn flags(&self) -> Flags { self.af.flags().to_owned() } + flags_getters_setters! { /// the `Zero` Flag z, set_z; @@ -972,6 +1029,7 @@ impl CPU { /// the `Carry` Flag c, set_c; } + /// Compares the given [Cond] to the CPU's condition flags pub fn cond(&self, cond: Cond) -> bool { match cond { @@ -1018,6 +1076,7 @@ impl Index for CPU { } } } + impl IndexMut for CPU { /// Performs the mutable indexing (`container[index]`) operation. /// # Panics @@ -1035,6 +1094,7 @@ impl IndexMut for CPU { } } } + impl Index for CPU { type Output = Wrapping; /// Performs the indexing (`container[index]`) operation. @@ -1047,6 +1107,7 @@ impl Index for CPU { } } } + impl IndexMut for CPU { /// Performs the mutable indexing (`container[index]`) operation. fn index_mut(&mut self, index: R16) -> &mut Self::Output { @@ -1070,6 +1131,7 @@ impl Index for CPU { } } } + impl IndexMut for CPU { fn index_mut(&mut self, index: R16Stack) -> &mut Self::Output { match index { diff --git a/src/cpu/split_register.rs b/src/cpu/split_register.rs index e826a72..0c25039 100644 --- a/src/cpu/split_register.rs +++ b/src/cpu/split_register.rs @@ -20,6 +20,7 @@ use super::Flags; /// The low-byte index constant const LO: usize = if 1u16.to_be() != 1u16 { 0 } else { 1 }; + /// The high-byte index constant const HI: usize = 1 - LO; @@ -30,22 +31,26 @@ pub union SplitRegister { af: [Flags; 2], r16: Wrapping, } + impl SplitRegister { /// Read the low byte of the register pub fn lo(&self) -> &Wrapping { // SAFETY: The GB's registers store POD types, so it's safe to transmute unsafe { &self.lh[LO] } } + /// Read the high byte of the register pub fn hi(&self) -> &Wrapping { // SAFETY: See [SplitRegister::lo] unsafe { &self.lh[HI] } } + /// Read the [flags byte](Flags) of the register pub fn flags(&self) -> &Flags { // SAFETY: See [SplitRegister::lo] unsafe { &self.af[LO] } } + /// Read the contents of the combined register pub fn wide(&self) -> &Wrapping { // SAFETY: See [SplitRegister::lo] @@ -57,16 +62,19 @@ impl SplitRegister { // SAFETY: See [SplitRegister::lo] unsafe { &mut self.lh[LO] } } + /// Gets a mutable reference to the high byte of the register pub fn hi_mut(&mut self) -> &mut Wrapping { // SAFETY: See [SplitRegister::lo] unsafe { &mut self.lh[HI] } } + /// Gets a mutable reference to the [flags byte](Flags) of the register pub fn flags_mut(&mut self) -> &mut Flags { // SAFETY: See [SplitRegister::lo] unsafe { &mut self.af[LO] } } + /// Gets a mutable reference to the combined register pub fn wide_mut(&mut self) -> &mut Wrapping { // SAFETY: See [SplitRegister::lo] @@ -75,61 +83,64 @@ impl SplitRegister { /// Constructs a SplitRegister from an array of big-endian bytes. pub fn from_be_bytes(value: [u8; 2]) -> Self { - Self { - lh: [Wrapping(value[HI]), Wrapping(value[LO])], - } + Self { lh: [Wrapping(value[HI]), Wrapping(value[LO])] } } + /// Constructs a SplitRegister from an array of little-endian bytes. pub fn from_le_bytes(value: [u8; 2]) -> Self { - Self { - lh: [Wrapping(value[LO]), Wrapping(value[HI])], - } + Self { lh: [Wrapping(value[LO]), Wrapping(value[HI])] } } + /// Constructs a SplitRegister from an array of host-endian bytes. pub fn from_ne_bytes(value: [u8; 2]) -> Self { - Self { - lh: [Wrapping(value[0]), Wrapping(value[1])], - } + Self { lh: [Wrapping(value[0]), Wrapping(value[1])] } } } + impl Debug for SplitRegister { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "[{:02x}, {:02x}]", self.lo(), self.hi()) } } + impl Default for SplitRegister { fn default() -> Self { Self { r16: Wrapping(0) } } } + impl PartialEq for SplitRegister { fn eq(&self, other: &Self) -> bool { self.wide().eq(other.wide()) } } + impl Eq for SplitRegister {} + impl PartialOrd for SplitRegister { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } + impl Ord for SplitRegister { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.wide().cmp(other.wide()) } } + impl Hash for SplitRegister { fn hash(&self, state: &mut H) { self.wide().hash(state) } } + impl From for SplitRegister { fn from(value: u16) -> Self { - Self { - r16: Wrapping(value), - } + Self { r16: Wrapping(value) } } } + impl From> for SplitRegister { fn from(value: Wrapping) -> Self { Self { r16: value } @@ -158,6 +169,7 @@ macro impl_ops(for $base:ident {$($trait:ident<$($ty:ty),*$(,)?> $func:ident()), })* )* } + impl_ops!(for SplitRegister { Mul mul(), Div div(), @@ -171,18 +183,16 @@ impl_ops!(for SplitRegister { #[test] fn low_is_low() { - let reg = SplitRegister { - r16: Wrapping(0x00ff), - }; + let reg = SplitRegister { r16: Wrapping(0x00ff) }; assert_eq!(*reg.lo(), Wrapping(0xff)) } + #[test] fn hi_is_hi() { - let reg = SplitRegister { - r16: Wrapping(0xff00), - }; + let reg = SplitRegister { r16: Wrapping(0xff00) }; assert_eq!(*reg.hi(), Wrapping(0xff)) } + #[test] fn from_bytes() { let be = SplitRegister::from_be_bytes([0xc5, 0x77]); diff --git a/src/memory.rs b/src/memory.rs index c0ce813..8e261af 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -30,6 +30,7 @@ pub enum Mode { DMG, CGB, } + impl Mode { pub fn from_bits(value: u8) -> Self { match value & 0x40 { @@ -37,6 +38,7 @@ impl Mode { _ => Self::CGB, } } + pub fn to_bits(self) -> u8 { (self as u8) << 7 } @@ -124,6 +126,7 @@ impl Bus { hram: [0; 128], } } + pub fn mmio_read(&self, addr: usize) -> Option { match addr { // Joypad I/O @@ -306,6 +309,7 @@ mod cart { Self::new(value) } } + impl From<&[u8]> for Cart { fn from(value: &[u8]) -> Self { Self::new(Vec::from(value)) @@ -366,8 +370,10 @@ pub mod mapper { pub mod no_mbc { use super::*; + #[derive(Clone, Copy, Debug, Default)] pub struct NoMBC; + impl Mapper for NoMBC { fn read(&self, addr: usize) -> Response { match addr { diff --git a/src/memory/banked.rs b/src/memory/banked.rs index 422dc71..a29614b 100644 --- a/src/memory/banked.rs +++ b/src/memory/banked.rs @@ -13,10 +13,12 @@ impl Banked Self { Self::default() } + /// Selects a bank between 0 and `LEN` - 1, saturating pub fn select(&mut self, bank: usize) { self.selected = bank.min(BANKS - 1) } + pub fn selected(&self) -> usize { self.selected } @@ -79,10 +81,12 @@ impl UpperBanked Self { Self::default() } + /// Selects a bank between 1 and `LEN` - 1, saturating to `LEN` - 1 pub fn select(&mut self, bank: usize) { self.selected = bank.clamp(1, BANKS - 1) } + /// Gets the currently selected upper bank pub fn selected(&self) -> usize { self.selected @@ -159,6 +163,7 @@ mod test { assert_eq!(idx, *data as usize) } } + #[test] fn upper_banked_bounds() { let ub = UpperBanked::<0, 8, 32>::new(); diff --git a/src/memory/io.rs b/src/memory/io.rs index d100cb3..6dedbc5 100644 --- a/src/memory/io.rs +++ b/src/memory/io.rs @@ -8,6 +8,7 @@ pub mod iter { start: usize, bus: &'a dyn BusIO, } + impl<'a> Iter<'a> { pub fn new(bus: &'a dyn BusIO) -> Self { Self { start: 0, bus } @@ -16,8 +17,10 @@ pub mod iter { 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); @@ -34,6 +37,7 @@ 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<()>; @@ -47,6 +51,7 @@ pub trait BusIO { { iter::Iter::new(self) } + /// Gets an iterator which reads bytes in an exclusive range fn read_iter_from(&self, start: usize) -> iter::Iter where @@ -60,6 +65,7 @@ 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) } @@ -69,6 +75,7 @@ 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) } @@ -78,6 +85,7 @@ 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) } @@ -97,14 +105,17 @@ pub trait BusAux: BusIO { } 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. @@ -119,31 +130,36 @@ pub trait BusAux: BusIO { .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, @@ -153,22 +169,27 @@ pub trait BusAux: BusIO { _ => 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)