Change small heuristics, add newlines between items

This commit is contained in:
John 2024-07-09 01:22:32 -05:00
parent 9106d9727f
commit 060a6b068b
9 changed files with 178 additions and 41 deletions

View File

@ -7,7 +7,9 @@ use boy::memory::io::BusIO;
pub trait BusIOTools: BusIO {
/// Prints all successful reads and writes
fn trace(&mut self) -> TracingBus<Self>;
fn ascii(&mut self) -> AsciiSerial<Self>;
fn read_file(&mut self, path: impl AsRef<Path>) -> IoResult<&mut Self>
where
Self: Sized;
@ -17,6 +19,7 @@ impl<T: BusIO> BusIOTools for T {
fn trace(&mut self) -> TracingBus<Self> {
TracingBus { bus: self }
}
fn ascii(&mut self) -> AsciiSerial<Self> {
AsciiSerial {
data: 0,
@ -24,6 +27,7 @@ impl<T: BusIO> BusIOTools for T {
bus: self,
}
}
fn read_file(&mut self, path: impl AsRef<Path>) -> 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();

View File

@ -6,6 +6,7 @@ use std::iter::{Enumerate, Peekable};
pub trait Disassemble: Iterator<Item = u8> + Sized {
fn disassemble(self) -> Disassembler<Self>;
}
impl<T: Iterator<Item = u8> + Sized> Disassemble for T {
fn disassemble(self) -> Disassembler<Self> {
Disassembler::new(self)
@ -29,27 +30,31 @@ impl<I: Iterator<Item = u8>> Iterator for Disassembler<I> {
impl<I: Iterator<Item = u8>> Disassembler<I> {
pub fn new(bytes: I) -> Self {
Disassembler {
bytes: bytes.enumerate().peekable(),
}
Disassembler { bytes: bytes.enumerate().peekable() }
}
pub fn index(&mut self) -> Option<usize> {
self.bytes.peek().map(|v| v.0)
}
pub fn imm8(&mut self) -> Option<u8> {
self.bytes.next().map(|v| v.1)
}
pub fn smm8(&mut self) -> Option<i8> {
self.imm8().map(|b| b as i8)
}
pub fn imm16(&mut self) -> Option<u16> {
let low = self.imm8()? as u16;
let high = self.imm8()? as u16;
Some(high << 8 | low)
}
pub fn smm16(&mut self) -> Option<i16> {
self.imm16().map(|w| w as i16)
}
pub fn print(&mut self, insn: Insn) -> Option<String> {
Some(match insn {
Insn::LdImm(reg) => format!("ld\t{reg}, {:02x}", self.imm8()?),
@ -79,6 +84,7 @@ impl<I: Iterator<Item = u8>> Disassembler<I> {
_ => format!("{insn}"),
})
}
pub fn print_prefix_cb(&mut self) -> Option<String> {
let prefixed: Prefixed = self.imm8()?.into();
Some(format!("{prefixed}"))

View File

@ -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<Token> {
Some(kind)
}
fn digits<const B: u32>(&mut self) -> Option<Token> {
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<Token> {
match self.peek() {
Some('b') => self.take().digits::<2>(),
@ -207,6 +213,7 @@ pub mod cli {
_ => self.digits::<16>(),
}
}
fn ident(&mut self) -> Option<Token> {
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<Token> {
@ -255,7 +263,7 @@ pub mod cli {
}
.emit(Token::Op(Range))
}
fn string(&mut self) -> Option<Token> {
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<I: Iterator<Item = char>> Iterator for Lexer<I> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
match self.sync().peek()? {
'[' => self.take().emit(Op(BrackOpen)),
@ -310,6 +320,7 @@ pub mod cli {
pub trait Parsible: Iterator<Item = Token> + Sized {
fn parse(self) -> Parser<Self>;
}
impl<T: Iterator<Item = Token> + Sized> Parsible for T {
fn parse(self) -> Parser<Self> {
Parser::new(self)
@ -330,9 +341,11 @@ pub mod cli {
fn peek(&mut self) -> Option<&Token> {
self.lexer.peek()
}
fn take(&mut self) -> Option<Token> {
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
}

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
use_small_heuristics = "Max"

View File

@ -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<u16, Error> {
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<u8, Error> {
// 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<u16, Error> {
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<u8, Error> {
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<u16, Error> {
// 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<usize, Error> {
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<u8, Error> {
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<R8> for CPU {
}
}
}
impl IndexMut<R8> for CPU {
/// Performs the mutable indexing (`container[index]`) operation.
/// # Panics
@ -1035,6 +1094,7 @@ impl IndexMut<R8> for CPU {
}
}
}
impl Index<R16> for CPU {
type Output = Wrapping<u16>;
/// Performs the indexing (`container[index]`) operation.
@ -1047,6 +1107,7 @@ impl Index<R16> for CPU {
}
}
}
impl IndexMut<R16> 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<R16Stack> for CPU {
}
}
}
impl IndexMut<R16Stack> for CPU {
fn index_mut(&mut self, index: R16Stack) -> &mut Self::Output {
match index {

View File

@ -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<u16>,
}
impl SplitRegister {
/// Read the low byte of the register
pub fn lo(&self) -> &Wrapping<u8> {
// 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<u8> {
// 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<u16> {
// 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<u8> {
// 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<u16> {
// 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<std::cmp::Ordering> {
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<H: std::hash::Hasher>(&self, state: &mut H) {
self.wide().hash(state)
}
}
impl From<u16> for SplitRegister {
fn from(value: u16) -> Self {
Self {
r16: Wrapping(value),
}
Self { r16: Wrapping(value) }
}
}
impl From<Wrapping<u16>> for SplitRegister {
fn from(value: Wrapping<u16>) -> 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<u8, u16> mul(),
Div<u8, u16> 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]);

View File

@ -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<u8> {
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 {

View File

@ -13,10 +13,12 @@ impl<const BASE: usize, const LEN: usize, const BANKS: usize> Banked<BASE, LEN,
pub fn new() -> 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<const BASE: usize, const LEN: usize, const BANKS: usize> UpperBanked<BASE,
pub fn new() -> 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();

View File

@ -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<Self::Item> {
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<u8>;
/// 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<const N: usize> BusIO for [u8; N] {
fn read(&self, addr: usize) -> Option<u8> {
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<u8> {
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<u8> {
fn read(&self, addr: usize) -> Option<u8> {
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<u8> {
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<u8> {
self.read(0x146)
}
/// Gets the Cartridge Type byte
fn carttype(&self) -> Option<u8> {
self.read(0x147)
}
/// Gets the size of the cartridge ROM (in 0x8000 byte banks)
fn romsize(&self) -> Option<usize> {
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<usize> {
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<u8> {
self.read(0x14a)
}
/// Gets the old licensee byte
fn oldlicencee(&self) -> Option<u8> {
self.read(0x14b)
}
/// Gets the Maskrom Version byte. Usually 0
fn maskromver(&self) -> Option<u8> {
self.read(0x14c)
}
/// Gets the header checksum
fn headercksum(&self) -> Option<u8> {
self.read(0x14d)
}
/// Gets the checksum of all bytes in ROM
fn globalcksum(&self) -> Option<u16> {
self.read_arr(0x14e).ok().map(u16::from_be_bytes)