gameboy_core/mmu/
mbc1.rs

1use super::cartridge::Cartridge;
2use super::mbc::Mbc;
3
4pub struct Mbc1 {
5    cartridge: Cartridge,
6    selected_rom_bank: usize,
7    selected_eram_bank: usize,
8    in_ram_banking_mode: bool,
9    external_ram_enabled: bool,
10    higher_rom_bank_bits: usize,
11    ram_change_callback: Box<dyn FnMut(usize, u8)>,
12}
13
14impl Mbc for Mbc1 {
15    fn read_byte(&self, index: u16) -> u8 {
16        match index {
17            0x0000..=0x3FFF => {
18                let rom = self.cartridge.get_rom();
19                rom[index as usize]
20            }
21            0x4000..=0x7FFF => {
22                let rom = self.cartridge.get_rom();
23                let offset = self.selected_rom_bank * 0x4000;
24                rom[index as usize - 0x4000 + offset]
25            }
26            0xA000..=0xBFFF => {
27                if self.external_ram_enabled {
28                    let selected_bank = if self.in_ram_banking_mode {
29                        self.selected_eram_bank
30                    } else {
31                        0
32                    };
33                    let offset = selected_bank * 0x2000;
34                    let address = index as usize - 0xA000 + offset;
35                    let ram = self.cartridge.get_ram();
36                    ram[address]
37                } else {
38                    0xFF
39                }
40            }
41            _ => panic!("index out of range: {:04X}", index),
42        }
43    }
44
45    fn write_byte(&mut self, index: u16, value: u8) {
46        match index {
47            0x0000..=0x1FFF => {
48                if self.cartridge.get_ram_size() > 0 {
49                    self.external_ram_enabled = (value & 0x0F) == 0x0A
50                }
51            }
52            0x2000..=0x3FFF => {
53                if self.in_ram_banking_mode {
54                    self.selected_rom_bank = usize::from(value & 0x1F);
55                } else {
56                    self.selected_rom_bank =
57                        usize::from(value & 0x1F) | (self.higher_rom_bank_bits << 5);
58                }
59
60                if self.selected_rom_bank == 0x00
61                    || self.selected_rom_bank == 0x20
62                    || self.selected_rom_bank == 0x40
63                    || self.selected_rom_bank == 0x60
64                {
65                    self.selected_rom_bank += 1;
66                }
67
68                self.selected_rom_bank &= self.cartridge.get_rom_banks() - 1;
69            }
70            0x4000..=0x5FFF => {
71                if self.in_ram_banking_mode {
72                    self.selected_eram_bank = usize::from(value & 0x03);
73                    self.selected_eram_bank &= self.cartridge.get_ram_banks() - 1;
74                } else {
75                    self.higher_rom_bank_bits = usize::from(value & 0x03);
76                    self.selected_rom_bank =
77                        (self.selected_rom_bank & 0x1F) | (self.higher_rom_bank_bits << 5);
78
79                    if self.selected_rom_bank == 0x00
80                        || self.selected_rom_bank == 0x20
81                        || self.selected_rom_bank == 0x40
82                        || self.selected_rom_bank == 0x60
83                    {
84                        self.selected_rom_bank += 1;
85                    }
86                    self.selected_rom_bank &= self.cartridge.get_rom_banks() - 1;
87                }
88            }
89            0x6000..=0x7FFF => {
90                if !(self.cartridge.get_ram_size() != 3 && (value & 0x01 != 0)) {
91                    self.in_ram_banking_mode = (value & 1) == 1;
92                }
93            }
94            0xA000..=0xBFFF => {
95                if self.external_ram_enabled {
96                    let selected_bank = if self.in_ram_banking_mode {
97                        self.selected_eram_bank
98                    } else {
99                        0
100                    };
101                    let offset = selected_bank * 0x2000;
102                    let address = index as usize - 0xA000 + offset;
103                    let ram = self.cartridge.get_ram_mut();
104                    ram[address] = value;
105
106                    (self.ram_change_callback)(address, value);
107                }
108            }
109            _ => panic!("index out of range: {:04X}", index),
110        }
111    }
112
113    fn get_cartridge(&self) -> &Cartridge {
114        &self.cartridge
115    }
116
117    fn get_cartridge_mut(&mut self) -> &mut Cartridge {
118        &mut self.cartridge
119    }
120
121    fn set_ram_change_callback(&mut self, f: Box<dyn FnMut(usize, u8)>) {
122        self.ram_change_callback = f;
123    }
124}
125
126impl Mbc1 {
127    pub fn new(cartridge: Cartridge) -> Mbc1 {
128        Mbc1 {
129            cartridge,
130            selected_rom_bank: 1,
131            selected_eram_bank: 0,
132            in_ram_banking_mode: false,
133            external_ram_enabled: false,
134            higher_rom_bank_bits: 0,
135            ram_change_callback: Box::new(|_, _| {}),
136        }
137    }
138}