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}