1pub mod huc3; 2pub mod mbc3; 3 4use crate::cartridge::HasBasicRamMapping; 5use crate::cartridge::mappers::mbc3::Mbc3Rtc; 6use bincode::{Decode, Encode}; 7use jgenesis_common::frontend::SaveWriter; 8use jgenesis_common::num::{GetBit, U16Ext}; 9 10#[derive(Debug, Clone, Copy, PartialEq, Encode, Decode)] 11enum BankingMode { 12 Simple, 13 Complex, 14} 15 16#[derive(Debug, Clone, Encode, Decode)] 17pub struct Mbc1 { 18 rom_bank: u8, 19 rom_addr_mask: u32, 20 ram_bank: u8, 21 ram_addr_mask: u32, 22 ram_enabled: bool, 23 banking_mode: BankingMode, 24} 25 26impl Mbc1 { 27 pub fn new(rom_len: u32, ram_len: u32) -> Self { 28 Self { 29 rom_bank: 1, 30 rom_addr_mask: rom_len - 1, 31 ram_bank: 0, 32 ram_addr_mask: ram_len.saturating_sub(1), 33 ram_enabled: false, 34 banking_mode: BankingMode::Simple, 35 } 36 } 37 38 pub fn map_rom_address(&self, address: u16) -> u32 { 39 if !address.bit(14) { 40 // $0000-$3FFF mapping depends on banking mode 41 match self.banking_mode { 42 BankingMode::Simple => { 43 // $0000-$3FFF is always mapped to the first 16KB of ROM 44 (address & 0x3FFF).into() 45 } 46 BankingMode::Complex => { 47 // $0000-$3FFF uses the highest 2 bits of ROM bank 48 let rom_bank = self.rom_bank & 0x60; 49 ((u32::from(rom_bank) << 14) | u32::from(address & 0x3FFF)) & self.rom_addr_mask 50 } 51 } 52 } else { 53 // $4000-$7FFF is mapped to the currently selected ROM bank 54 ((u32::from(self.rom_bank) << 14) | u32::from(address & 0x3FFF)) & self.rom_addr_mask 55 } 56 } 57 58 pub fn write_rom_address(&mut self, address: u16, value: u8) { 59 log::trace!("MBC1 register write: {address:04X} {value:02X}"); 60 61 match address { 62 0x0000..=0x1FFF => { 63 self.ram_enabled = value & 0x0F == 0x0A; 64 log::trace!(" RAM enabled: {}", self.ram_enabled); 65 } 66 0x2000..=0x3FFF => { 67 let mut rom_bank_low = value & 0x1F; 68 if rom_bank_low == 0 { 69 rom_bank_low = 1; 70 } 71 self.rom_bank = (self.rom_bank & !0x1F) | rom_bank_low; 72 log::trace!(" ROM bank: {:02X}", self.rom_bank); 73 } 74 0x4000..=0x5FFF => { 75 self.rom_bank = (self.rom_bank & 0x1F) | ((value & 0x03) << 5); 76 self.ram_bank = value & 0x03; 77 log::trace!(" ROM bank: {:02X}", self.rom_bank); 78 log::trace!(" RAM bank: {:02X}", self.ram_bank); 79 } 80 0x6000..=0x7FFF => { 81 self.banking_mode = 82 if value.bit(0) { BankingMode::Complex } else { BankingMode::Simple }; 83 log::trace!(" Banking mode: {:?}", self.banking_mode); 84 } 85 _ => panic!("Invalid cartridge address: {address:04X}"), 86 } 87 } 88} 89 90impl HasBasicRamMapping for Mbc1 { 91 fn map_ram_address(&self, address: u16) -> Option<u32> { 92 if !self.ram_enabled { 93 return None; 94 } 95 96 let ram_addr = match self.banking_mode { 97 BankingMode::Simple => { 98 // RAM is not banked in simple mode; always mapped to the first 8KB of RAM 99 (address & 0x1FFF).into() 100 } 101 BankingMode::Complex => { 102 ((u32::from(self.ram_bank) << 13) | u32::from(address & 0x1FFF)) 103 & self.ram_addr_mask 104 } 105 }; 106 107 Some(ram_addr) 108 } 109}
Every MBC2 cartridge has 512x4 bits of RAM
112pub const MBC2_RAM_LEN: usize = 512;
114#[derive(Debug, Clone, Encode, Decode)] 115pub struct Mbc2 { 116 rom_bank: u8, 117 rom_addr_mask: u32, 118 ram: Box<[u8; MBC2_RAM_LEN]>, 119 ram_enabled: bool, 120} 121 122impl Mbc2 { 123 pub fn new(rom_len: u32, initial_ram: Vec<u8>) -> Self { 124 let ram = 125 if initial_ram.len() == MBC2_RAM_LEN { initial_ram } else { vec![0; MBC2_RAM_LEN] }; 126 127 Self { 128 rom_bank: 0, 129 rom_addr_mask: rom_len - 1, 130 ram: ram.into_boxed_slice().try_into().unwrap(), 131 ram_enabled: false, 132 } 133 } 134 135 pub fn map_rom_address(&self, address: u16) -> u32 { 136 basic_map_rom_address(address, self.rom_bank.into(), false, self.rom_addr_mask) 137 } 138 139 pub fn read_ram(&self, address: u16) -> u8 { 140 if !self.ram_enabled { 141 return 0xFF; 142 } 143 144 // MBC2 RAM is nibble-sized 145 self.ram[(address & 0x1FF) as usize] | 0xF0 146 } 147 148 pub fn write_ram(&mut self, address: u16, value: u8) { 149 if !self.ram_enabled { 150 return; 151 } 152 153 // MBC2 RAM is nibble-sized 154 self.ram[(address & 0x1FF) as usize] = value & 0x0F; 155 } 156 157 pub fn ram(&self) -> &[u8] { 158 self.ram.as_ref() 159 } 160 161 pub fn write_rom_address(&mut self, address: u16, value: u8) { 162 // MBC2 only has two registers, both mapped to $0000-$3FFF 163 if !(0x0000..0x4000).contains(&address) { 164 return; 165 } 166 167 // Address bit 8 determines whether this sets RAM enabled (clear) or ROM bank (set) 168 if !address.bit(8) { 169 // Set RAM enabled 170 self.ram_enabled = value & 0x0F == 0x0A; 171 } else { 172 // Set ROM bank 173 self.rom_bank = value & 0x0F; 174 } 175 } 176} 177 178#[derive(Debug, Clone, Encode, Decode)] 179pub struct Mbc3 { 180 rom_bank: u8, 181 rom_addr_mask: u32, 182 ram_bank: u8, 183 ram_addr_mask: u32, 184 ram_enabled: bool, 185 rtc: Option<Mbc3Rtc>, 186} 187 188impl Mbc3 { 189 pub fn new(rom_len: u32, ram_len: u32, rtc: Option<Mbc3Rtc>) -> Self { 190 Self { 191 rom_bank: 0, 192 rom_addr_mask: rom_len - 1, 193 ram_bank: 0, 194 ram_addr_mask: if ram_len != 0 { ram_len - 1 } else { 0 }, 195 ram_enabled: false, 196 rtc, 197 } 198 } 199 200 pub fn map_rom_address(&self, address: u16) -> u32 { 201 basic_map_rom_address(address, self.rom_bank.into(), false, self.rom_addr_mask) 202 } 203 204 pub fn write_rom_address(&mut self, address: u16, value: u8) { 205 match address { 206 0x0000..=0x1FFF => { 207 // RAM/RTC enabled 208 self.ram_enabled = value & 0x0F == 0x0A; 209 } 210 0x2000..=0x3FFF => { 211 // ROM bank 212 self.rom_bank = value; 213 } 214 0x4000..=0x5FFF => { 215 // RAM bank number / RTC register select 216 self.ram_bank = value & 0x0F; 217 } 218 0x6000..=0x7FFF => { 219 // RTC latch 220 if let Some(rtc) = &mut self.rtc { 221 rtc.write_latch(value); 222 } 223 } 224 0x8000..=0xFFFF => panic!("Invalid cartridge address: {address:06X}"), 225 } 226 } 227 228 pub fn read_ram(&self, address: u16, sram: &[u8]) -> u8 { 229 match self.ram_bank { 230 0x00..=0x07 => { 231 // SRAM 232 basic_map_ram_address( 233 self.ram_enabled, 234 address, 235 self.ram_bank.into(), 236 self.ram_addr_mask, 237 ) 238 .map_or(0xFF, |ram_addr| sram[ram_addr as usize]) 239 } 240 0x08..=0x0C => { 241 // RTC registers 242 self.rtc.as_ref().map_or(0xFF, |rtc| rtc.read_register(self.ram_bank)) 243 } 244 _ => 0xFF, 245 } 246 } 247 248 pub fn write_ram(&mut self, address: u16, value: u8, sram: &mut [u8]) { 249 match self.ram_bank { 250 0x00..=0x07 => { 251 // SRAM 252 let ram_addr = basic_map_ram_address( 253 self.ram_enabled, 254 address, 255 self.ram_bank.into(), 256 self.ram_addr_mask, 257 ); 258 if let Some(ram_addr) = ram_addr { 259 sram[ram_addr as usize] = value; 260 } 261 } 262 0x08..=0x0C => { 263 // RTC registers 264 if let Some(rtc) = &mut self.rtc { 265 rtc.write_register(self.ram_bank, value); 266 } 267 } 268 _ => {} 269 } 270 } 271 272 pub fn update_rtc_time(&mut self) { 273 if let Some(rtc) = &mut self.rtc { 274 rtc.update_time(); 275 } 276 } 277 278 pub fn save_rtc_state<S: SaveWriter>(&self, save_writer: &mut S) -> Result<(), S::Err> { 279 if let Some(rtc) = &self.rtc { 280 save_writer.persist_serialized("rtc", rtc)?; 281 } 282 283 Ok(()) 284 } 285} 286 287#[derive(Debug, Clone, Encode, Decode)] 288pub struct Mbc5 { 289 rom_bank: u16, 290 rom_addr_mask: u32, 291 ram_bank: u8, 292 ram_addr_mask: u32, 293 ram_enabled: bool, 294} 295 296impl Mbc5 { 297 pub fn new(rom_len: u32, ram_len: u32) -> Self { 298 Self { 299 rom_bank: 1, 300 rom_addr_mask: rom_len - 1, 301 ram_bank: 0, 302 ram_addr_mask: if ram_len != 0 { ram_len - 1 } else { 0 }, 303 ram_enabled: false, 304 } 305 } 306 307 pub fn map_rom_address(&self, address: u16) -> u32 { 308 basic_map_rom_address(address, self.rom_bank.into(), true, self.rom_addr_mask) 309 } 310 311 pub fn write_rom_address(&mut self, address: u16, value: u8) { 312 log::trace!("MBC5 register write: {address:04X} {value:02X}"); 313 314 match address { 315 0x0000..=0x1FFF => { 316 // RAM enabled 317 self.ram_enabled = value & 0x0F == 0x0A; 318 log::trace!("RAM enabled: {}", self.ram_enabled); 319 } 320 0x2000..=0x2FFF => { 321 // Low 8 bits of ROM bank 322 self.rom_bank.set_lsb(value); 323 log::trace!("ROM bank: {:03X}", self.rom_bank); 324 } 325 0x3000..=0x3FFF => { 326 // Highest bit of ROM bank 327 self.rom_bank.set_msb(value & 0x01); 328 log::trace!("ROM bank: {:03X}", self.rom_bank); 329 } 330 0x4000..=0x5FFF => { 331 // RAM bank / rumble motor 332 // TODO support rumble 333 self.ram_bank = value & 0x0F; 334 log::trace!("RAM bank: {:02X}", self.ram_bank); 335 } 336 0x6000..=0x7FFF => {} 337 _ => panic!("Invalid cartridge address: {address:06X}"), 338 } 339 } 340} 341 342impl HasBasicRamMapping for Mbc5 { 343 fn map_ram_address(&self, address: u16) -> Option<u32> { 344 basic_map_ram_address(self.ram_enabled, address, self.ram_bank.into(), self.ram_addr_mask) 345 } 346}
MBC2 / MBC3 / MBC5
349fn basic_map_rom_address( 350 address: u16, 351 rom_bank: u32, 352 allow_bank_0: bool, 353 rom_addr_mask: u32, 354) -> u32 { 355 if address < 0x4000 { 356 // First 16KB of ROM 357 address.into() 358 } else { 359 // 16KB ROM bank 360 let rom_bank = if !allow_bank_0 && rom_bank == 0 { 1 } else { rom_bank }; 361 ((rom_bank << 14) | u32::from(address & 0x3FFF)) & rom_addr_mask 362 } 363}