Bweh/src/lib.rs

97 lines
3.0 KiB
Rust

//! A very inaccurate gameboy emulator
#![feature(decl_macro)]
#![allow(clippy::unit_arg)]
/// A gameboy has:
/// - A 16-bit memory bus
/// - A CPU
/// - A picture processing unit
pub mod error {
use crate::cpu::disasm::Insn;
use std::fmt::Display;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Error {
kind: ErrorKind,
// any other information about an error
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind {
InvalidInstruction(u8),
InvalidAddress(u16),
UnimplementedInsn(Insn),
Unimplemented(u8),
HitBreak(u16, u8),
Todo,
}
impl From<ErrorKind> for Error {
fn from(value: ErrorKind) -> Self {
Self { kind: value }
}
}
impl<T> From<Error> for Result<T, Error> {
fn from(value: Error) -> Self {
Err(value)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::InvalidInstruction(i) => write!(f, "Invalid instruction: {i}"),
ErrorKind::InvalidAddress(addr) => write!(f, "Invalid address: {addr:04x}"),
ErrorKind::UnimplementedInsn(i) => write!(f, "Unimplemented instruction: {i}"),
ErrorKind::Unimplemented(i) => write!(f, "Unimplemented instruction: {i}"),
ErrorKind::HitBreak(addr, m) => {
write!(f, "Hit breakpoint {addr:x} (Took {m} cycles)")
}
ErrorKind::Todo => write!(f, "Not yet implemented"),
}
}
}
}
pub mod memory;
pub mod graphics;
pub mod cpu;
pub mod audio;
#[cfg(test)]
mod tests {
use crate::memory::{io::*, Cart};
const TEST_ROMS: &[&str] = &[
// "roms/Legend of Zelda, The - Link's Awakening DX (USA, Europe) (Rev B) (SGB Enhanced).gbc",
// "roms/Pokemon - Crystal Version (USA, Europe) (Rev A).gbc",
// "roms/Pokemon Pinball (USA) (Rumble Version) (SGB Enhanced).gbc",
// "roms/Pokemon - Red Version (USA, Europe) (SGB Enhanced).gb",
"roms/Super Mario Land 2 - 6 Golden Coins (USA, Europe) (Rev B).gb",
"roms/Super Mario Land (World) (Rev A).gb",
"roms/Tetris (World) (Rev A).gb",
];
#[test]
/// Checksums the cart headers of every cart defined in [TEST_ROMS],
/// and compares them against the checksum stored in the header
fn cart_header_checksum() {
for rom in TEST_ROMS {
let rom = std::fs::read(rom).unwrap();
let cart = Cart::from(rom);
let mut cksum = 0u8;
for index in 0x134..0x14d {
cksum = cksum
.wrapping_sub(cart.read(index).unwrap())
.wrapping_sub(1)
}
assert_eq!(cksum, cart.headercksum().unwrap())
}
}
}