tgbr/mbc/
mbc2.rs

1use bitvec::prelude::*;
2use log::warn;
3use serde::{Deserialize, Serialize};
4use std::cmp::max;
5
6#[derive(Serialize, Deserialize)]
7pub struct Mbc2 {
8    rom_bank: u8,
9    rom_bank_mask: u8,
10    #[serde(with = "serde_bytes")]
11    ram: Vec<u8>,
12    ram_enable: bool,
13}
14
15impl Mbc2 {
16    pub fn new(rom: &crate::rom::Rom, internal_ram: Option<Vec<u8>>) -> Self {
17        if let Some(ram) = &internal_ram {
18            assert_eq!(ram.len(), 256);
19        }
20        let rom_bank_num = rom.rom_size / 0x4000;
21        assert!(rom_bank_num.is_power_of_two());
22        Self {
23            rom_bank: 1,
24            rom_bank_mask: rom_bank_num.saturating_sub(1) as u8,
25            ram: internal_ram.unwrap_or_else(|| vec![0; 0x100]),
26            ram_enable: false,
27        }
28    }
29}
30
31impl super::MbcTrait for Mbc2 {
32    fn read(&mut self, ctx: &mut impl super::Context, addr: u16) -> u8 {
33        match addr {
34            0x0000..=0x3FFF => ctx.rom().data[addr as usize],
35            0x4000..=0x7FFF => {
36                let offset = (self.rom_bank & self.rom_bank_mask) as usize * 0x4000;
37                ctx.rom().data[offset + (addr & 0x3FFF) as usize]
38            }
39            0xA000..=0xBFFF => {
40                if self.ram_enable {
41                    let addr = (addr & 0x1FF) as usize;
42                    let data = self.ram[addr / 2];
43                    if addr % 2 == 0 {
44                        data & 0xF
45                    } else {
46                        data >> 4
47                    }
48                } else {
49                    !0
50                }
51            }
52            _ => panic!("MBC2: Read ${addr:04X}"),
53        }
54    }
55    fn write(&mut self, _ctx: &mut impl super::Context, addr: u16, data: u8) {
56        match addr {
57            0x0000..=0x3FFF => {
58                if addr.view_bits::<Lsb0>()[8] {
59                    self.rom_bank = max(1, data & 0xF);
60                } else {
61                    self.ram_enable = data == 0x0A;
62                }
63            }
64            0xA000..=0xBFFF => {
65                if self.ram_enable {
66                    let addr = (addr & 0x1FF) as usize;
67                    let v = self.ram[addr / 2].view_bits_mut::<Lsb0>();
68                    if addr % 2 == 0 {
69                        v[0..=3].store(data & 0xF);
70                    } else {
71                        v[4..=7].store(data & 0xF);
72                    }
73                }
74            }
75            _ => warn!("MBC2: Write ${addr:04X} = ${data:02X}"),
76        }
77    }
78    fn internal_ram(&self) -> Option<&[u8]> {
79        Some(&self.ram)
80    }
81}