97 lines
3.0 KiB
Rust
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())
|
|
}
|
|
}
|
|
}
|