gameboy 0.1.5

Gameboy emulator written in Rust and WebAssembly
Documentation
use crate::mbc::{ram_banks, rom_banks, MemoryBankController};
pub type StrResult<T> = Result<T, &'static str>;

use std::fs::File;
use std::io::prelude::*;
use std::{io, path};

pub struct MBC5 {
    rom: Vec<u8>,
    ram: Vec<u8>,
    rombank: usize,
    rambank: usize,
    ram_on: bool,
    savepath: Option<path::PathBuf>,
    rombanks: usize,
    rambanks: usize,
}

impl MBC5 {
    pub fn new(data: Vec<u8>, file: path::PathBuf) -> StrResult<MBC5> {
        let subtype = data[0x147];
        let svpath = match subtype {
            0x1B | 0x1E => Some(file.with_extension("gbsave")),
            _ => None,
        };
        let rambanks = match subtype {
            0x1A | 0x1B | 0x1D | 0x1E => ram_banks(data[0x149]),
            _ => 0,
        };
        let ramsize = 0x2000 * rambanks;
        let rombanks = rom_banks(data[0x148]);

        let mut res = MBC5 {
            rom: data,
            ram: ::std::iter::repeat(0u8).take(ramsize).collect(),
            rombank: 1,
            rambank: 0,
            ram_on: false,
            savepath: svpath,
            rombanks,
            rambanks,
        };
        res.loadram().map(|_| res)
    }

    fn loadram(&mut self) -> StrResult<()> {
        match self.savepath {
            None => Ok(()),
            Some(ref savepath) => {
                let mut data = vec![];
                match File::open(savepath).and_then(|mut f| f.read_to_end(&mut data)) {
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
                    Err(_) => Err("Could not read RAM"),
                    Ok(..) => {
                        self.ram = data;
                        Ok(())
                    }
                }
            }
        }
    }
}

impl Drop for MBC5 {
    fn drop(&mut self) {
        match self.savepath {
            None => {}
            Some(ref path) => {
                let _ = File::create(path).and_then(|mut f| f.write_all(&self.ram));
            }
        };
    }
}

impl MemoryBankController for MBC5 {
    fn readrom(&self, a: u16) -> u8 {
        let idx = if a < 0x4000 {
            a as usize
        } else {
            (self.rombank * 0x4000) | ((a as usize) & 0x3FFF)
        };
        *self.rom.get(idx).unwrap_or(&0)
    }
    fn readram(&self, a: u16) -> u8 {
        if !self.ram_on {
            return 0;
        }
        self.ram[(self.rambank * 0x2000) | ((a as usize) & 0x1FFF)]
    }
    fn writerom(&mut self, a: u16, v: u8) {
        match a {
            0x0000..=0x1FFF => self.ram_on = v & 0x0F == 0x0A,
            0x2000..=0x2FFF => {
                self.rombank = ((self.rombank & 0x100) | (v as usize)) % self.rombanks
            }
            0x3000..=0x3FFF => {
                self.rombank =
                    ((self.rombank & 0x0FF) | (((v & 0x1) as usize) << 8)) % self.rombanks
            }
            0x4000..=0x5FFF => self.rambank = ((v & 0x0F) as usize) % self.rambanks,
            0x6000..=0x7FFF => { /* ? */ }
            _ => panic!("Could not write to {:04X} (MBC5)", a),
        }
    }
    fn writeram(&mut self, a: u16, v: u8) {
        if !self.ram_on {
            return;
        }
        self.ram[(self.rambank * 0x2000) | ((a as usize) & 0x1FFF)] = v;
    }
}