gameboy_core 0.3.3

core library to be used for Gameboy emulation
Documentation
use super::mbc_type::MbcType;
use crate::rtc::Rtc;

pub struct Cartridge {
    rom_banks: usize,
    ram_banks: usize,
    ram_size: usize,
    has_rtc: bool,
    has_battery: bool,
    rom: Vec<u8>,
    ram: Vec<u8>,
    name: String,
    mbc_type: MbcType,
    is_cgb: bool,
    rtc: Rtc,
    last_time: u64,
}

impl Cartridge {
    pub fn from_rom(rom: Vec<u8>) -> Result<Cartridge, String> {
        let cartridge_type = i32::from(rom[0x0147]);
        let mbc_type = match cartridge_type {
            0x00 | 0x08 | 0x09 => Ok(MbcType::RomOnly),
            0x01 | 0x02 | 0x03 | 0xEA | 0xFF => Ok(MbcType::Mbc1),
            0x05 | 0x06 => Ok(MbcType::Mbc2),
            0x0F | 0x10 | 0x11 | 0x12 | 0x13 | 0xFC => Ok(MbcType::Mbc3),
            0x19 | 0x1A | 0x1B | 0x1C | 0x1D | 0x1E => Ok(MbcType::Mbc5),
            _ => Err(format!(
                "Unsupported cartridge type: 0x{:02X}",
                cartridge_type
            )),
        }?;

        let rom_banks = std::cmp::max(Cartridge::pow2ceil(rom.len() / 0x4000), 2);

        let ram_size = usize::from(rom[0x0149]);
        let ram_banks = match ram_size {
            0x0 => Ok(0),
            0x1 => Ok(1),
            0x2 => Ok(1),
            0x3 => Ok(4),
            0x4 => Ok(16),
            _ => Err(format!("Unknown number of RAM banks: 0x{:02X}", ram_size)),
        }?;

        let has_rtc = match cartridge_type {
            0x0F | 0x10 => true,
            _ => false,
        };
        let has_battery = match cartridge_type {
            0x03 | 0x06 | 0x09 | 0x0D | 0x0F | 0x10 | 0x13 | 0x17 | 0x1E | 0x1B | 0x22 | 0xFD
            | 0xFF => true,
            _ => false,
        };

        let mut name = String::new();
        let mut name_index = 0x0134;
        while rom[name_index] != 0x00 && name_index < 0x0143 {
            let c = rom[name_index] as char;
            name.push(c);
            name_index += 1;
        }

        let is_cgb = rom[0x0143] == 0xC0 || rom[0x0143] == 0x80;

        let ram = match mbc_type {
            MbcType::Mbc2 => vec![0x0F; 0x200],
            MbcType::Mbc5 => vec![0xFF; 0x20000],
            _ => vec![0xFF; 0x8000],
        };

        Ok(Cartridge {
            rom_banks,
            ram_banks,
            ram_size,
            has_rtc,
            has_battery,
            rom,
            ram,
            name,
            mbc_type,
            is_cgb,
            rtc: Rtc::new(),
            last_time: 0,
        })
    }

    fn pow2ceil(i: usize) -> usize {
        let mut i = i - 1;
        i |= i >> 1;
        i |= i >> 2;
        i |= i >> 4;
        i |= i >> 8;
        i += 1;
        i
    }

    pub fn get_rom_banks(&self) -> usize {
        self.rom_banks
    }

    pub fn get_ram_banks(&self) -> usize {
        self.ram_banks
    }

    pub fn get_ram_size(&self) -> usize {
        self.ram_size
    }

    pub fn has_rtc(&self) -> bool {
        self.has_rtc
    }

    pub fn has_battery(&self) -> bool {
        self.has_battery
    }

    pub fn get_rom(&self) -> &[u8] {
        self.rom.as_ref()
    }

    pub fn get_ram_mut(&mut self) -> &mut [u8] {
        self.ram.as_mut()
    }

    pub fn get_ram(&self) -> &[u8] {
        self.ram.as_ref()
    }

    pub fn set_ram(&mut self, ram: Vec<u8>) {
        self.ram = ram;
    }

    pub fn get_mbc_type(&self) -> MbcType {
        self.mbc_type
    }

    pub fn get_name(&self) -> &str {
        &self.name
    }

    pub fn is_cgb(&self) -> bool {
        self.is_cgb
    }

    pub fn get_last_timestamp(&self) -> (Rtc, u64) {
        (self.rtc, self.last_time)
    }

    pub fn set_last_timestamp(&mut self, rtc: Rtc, last_time: u64) {
        self.rtc.seconds = rtc.seconds;
        self.rtc.minutes = rtc.minutes;
        self.rtc.hours = rtc.hours;
        self.rtc.days_low = rtc.days_low;
        self.rtc.days_high = rtc.days_high;
        self.last_time = last_time;
    }
}