Skip to content
Snippets Groups Projects
rom.rs 37.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •             // ROM bank selection 5 lower bits
    
                0x2000 | 0x3000 => {
    
                    let mut rom_bank = value as u16 & 0x1f;
                    rom_bank &= rom.rom_bank_count * 2 - 1;
    
                    if rom_bank == 0 {
                        rom_bank = 1;
                    }
                    rom.set_rom_bank(rom_bank);
                }
    
                // RAM bank selection and ROM bank selection upper bits
    
    João Magalhães's avatar
    João Magalhães committed
                0x4000 | 0x5000 => {
    
                    let ram_bank = value & 0x03;
    
                    if ram_bank as u16 >= rom.ram_bank_count {
                        return;
                    }
    
                    rom.set_ram_bank(ram_bank);
    
    João Magalhães's avatar
    João Magalhães committed
                0x6000 | 0x7000 => {
    
                    if value == 0x1 && rom.rom_bank_count > 32 {
                        unimplemented!("Advanced ROM banking mode for MBC1 is not implemented");
                    }
    
                _ => warnln!("Writing to unknown Cartridge ROM location 0x{:04x}", addr),
    
            }
        },
        read_ram: |rom: &Cartridge, addr: u16| -> u8 {
            if !rom.ram_enabled {
                return 0xff;
            }
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize]
        },
        write_ram: |rom: &mut Cartridge, addr: u16, value: u8| {
            if !rom.ram_enabled {
    
                warnln!("Attempt to write to ERAM while write protect is active");
    
                return;
            }
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize] = value;
        },
    };
    
    pub static MBC3: Mbc = Mbc {
        name: "MBC3",
        read_rom: |rom: &Cartridge, addr: u16| -> u8 {
            match addr & 0xf000 {
                0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize],
    
                0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom
                    .rom_data
                    .get(rom.rom_offset + (addr - 0x4000) as usize)
                    .unwrap_or(&0x0),
    
                _ => {
                    warnln!("Reading from unknown Cartridge ROM location 0x{:04x}", addr);
                    0xff
                }
    
            }
        },
        write_rom: |rom: &mut Cartridge, addr: u16, value: u8| {
            match addr & 0xf000 {
                // RAM enabled flag
                0x0000 | 0x1000 => {
                    rom.ram_enabled = (value & 0x0f) == 0x0a;
                }
                // ROM bank selection
                0x2000 | 0x3000 => {
    
                    let mut rom_bank = value as u16 & 0x7f;
                    rom_bank &= rom.rom_bank_count * 2 - 1;
    
                    if rom_bank == 0 {
                        rom_bank = 1;
                    }
                    rom.set_rom_bank(rom_bank);
                }
                // RAM bank selection
                0x4000 | 0x5000 => {
                    let ram_bank = value & 0x03;
    
                    if ram_bank as u16 >= rom.ram_bank_count {
                        return;
                    }
    
                    rom.set_ram_bank(ram_bank);
    
                _ => warnln!("Writing to unknown Cartridge ROM location 0x{:04x}", addr),
            }
        },
        read_ram: |rom: &Cartridge, addr: u16| -> u8 {
            if !rom.ram_enabled {
                return 0xff;
            }
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize]
        },
        write_ram: |rom: &mut Cartridge, addr: u16, value: u8| {
            if !rom.ram_enabled {
    
                warnln!("Attempt to write to ERAM while write protect is active");
    
                return;
            }
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize] = value;
        },
    };
    
    pub static MBC5: Mbc = Mbc {
        name: "MBC5",
        read_rom: |rom: &Cartridge, addr: u16| -> u8 {
            match addr & 0xf000 {
                0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize],
    
                0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom
                    .rom_data
                    .get(rom.rom_offset + (addr - 0x4000) as usize)
                    .unwrap_or(&0x0),
    
                _ => {
                    warnln!("Reading from unknown Cartridge ROM location 0x{:04x}", addr);
                    0xff
                }
            }
        },
        write_rom: |rom: &mut Cartridge, addr: u16, value: u8| {
            match addr & 0xf000 {
                // RAM enabled flag
                0x0000 | 0x1000 => {
                    rom.ram_enabled = (value & 0x0f) == 0x0a;
                }
    
                // ROM bank selection 8 lower bits
    
                0x2000 => {
    
                    let rom_bank = value as u16;
                    rom.set_rom_bank(rom_bank);
                }
                // ROM bank selection 9th bit
                0x3000 => {
    
    João Magalhães's avatar
    João Magalhães committed
                    let rom_bank = (rom.rom_bank() & 0x00ff) + (((value & 0x01) as u16) << 8);
    
                    rom.set_rom_bank(rom_bank);
                }
                // RAM bank selection
                0x4000 | 0x5000 => {
    
                    let mut ram_bank = value & 0x0f;
    
                    // handles the rumble flag for the cartridges
                    // that support the rumble operation
                    if rom.has_rumble() {
                        ram_bank = value & 0x07;
                        let rumble = (value & 0x08) == 0x08;
    
                        if rom.rumble_active != rumble {
    
                            rom.rumble_active = rumble;
                            rom.trigger_rumble();
                        }
                    }
    
    
                    if ram_bank as u16 >= rom.ram_bank_count {
                        return;
                    }
    
                    rom.set_ram_bank(ram_bank);
                }
                _ => warnln!("Writing to unknown Cartridge ROM location 0x{:04x}", addr),
    
        read_ram: |rom: &Cartridge, addr: u16| -> u8 {
    
            if !rom.ram_enabled {
                return 0xff;
            }
    
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize]
        },
    
        write_ram: |rom: &mut Cartridge, addr: u16, value: u8| {
    
            if !rom.ram_enabled {
    
                warnln!("Attempt to write to ERAM while write protect is active");
    
            rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize] = value;
    
    
    pub static GAME_GENIE: Mbc = Mbc {
        name: "GameGenie",
        read_rom: |rom: &Cartridge, addr: u16| -> u8 {
            let game_genie = rom.game_genie.as_ref().unwrap();
            if game_genie.contains_addr(addr) {
    
                // retrieves the Game Genie code that matches the current address
                // keep in mind that this assumes that no more that one code is
                // registered for the same memory address
    
                let genie_code = game_genie.get_addr(addr).unwrap();
    
                // obtains the current byte that is stored at the address using
                // the MBC, this value will probably be patched
                let data = (rom.mbc.read_rom)(rom, addr);
    
    
                // checks if the current data at the address is the same as the
                // one that is expected by the Game Genie code, if that's the case
                // applies the patch, otherwise returns the original strategy is
                // going to be used
    
                if genie_code.is_valid(data) {
    
                    debugln!("Applying Game Genie code: {}", genie_code);
    
                    return genie_code.patch_data(data);
    
                }
            }
            (rom.mbc.read_rom)(rom, addr)
        },
        write_rom: |rom: &mut Cartridge, addr: u16, value: u8| (rom.mbc.write_rom)(rom, addr, value),
        read_ram: |rom: &Cartridge, addr: u16| -> u8 { (rom.mbc.read_ram)(rom, addr) },
        write_ram: |rom: &mut Cartridge, addr: u16, value: u8| (rom.mbc.write_ram)(rom, addr, value),
    };
    
    #[cfg(test)]
    mod tests {
        use super::{Cartridge, RomType};
    
        #[test]
        fn test_has_rumble() {
            let mut rom = Cartridge::new();
    
            rom.set_data(&vec![0; 0x8000]).unwrap();
    
            assert!(!rom.has_rumble());
    
    
            rom.set_rom_type(RomType::Mbc5Rumble).unwrap();
    
            assert!(rom.has_rumble());
    
    
            rom.set_rom_type(RomType::Mbc5RumbleRam).unwrap();
    
            assert!(rom.has_rumble());
    
    
            rom.set_rom_type(RomType::Mbc5RumbleRamBattery).unwrap();
    
            assert!(rom.has_rumble());
    
    
            rom.set_rom_type(RomType::Mbc1).unwrap();
    
            assert!(!rom.has_rumble());
        }
    }