1use bincode::{Decode, Encode}; 2use jgenesis_common::num::GetBit; 3 4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 5pub enum BwramMapSource { 6 #[default] 7 Normal, 8 Bitmap, 9} 10 11impl BwramMapSource { 12 fn from_bit(bit: bool) -> Self { 13 if bit { Self::Bitmap } else { Self::Normal } 14 } 15} 16 17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 18pub enum BwramBitmapBits { 19 Two, 20 #[default] 21 Four, 22} 23 24impl BwramBitmapBits { 25 fn from_bit(bit: bool) -> Self { 26 if bit { Self::Two } else { Self::Four } 27 } 28} 29 30const DEFAULT_BANK_C_ADDR: u32 = 0x000000; 31const DEFAULT_BANK_D_ADDR: u32 = 0x100000; 32const DEFAULT_BANK_E_ADDR: u32 = 0x200000; 33const DEFAULT_BANK_F_ADDR: u32 = 0x300000; 34 35#[derive(Debug, Clone, Encode, Decode)] 36pub struct Sa1Mmc { 37 pub bank_c_base_addr: u32, 38 pub bank_c_lorom_mapped: bool, 39 pub lorom_bank_c_addr: u32, 40 pub bank_d_base_addr: u32, 41 pub bank_d_lorom_mapped: bool, 42 pub lorom_bank_d_addr: u32, 43 pub bank_e_base_addr: u32, 44 pub bank_e_lorom_mapped: bool, 45 pub lorom_bank_e_addr: u32, 46 pub bank_f_base_addr: u32, 47 pub bank_f_lorom_mapped: bool, 48 pub lorom_bank_f_addr: u32, 49 pub snes_bwram_base_addr: u32, 50 pub sa1_bwram_base_addr: u32, 51 pub sa1_bwram_source: BwramMapSource, 52 pub bwram_bitmap_format: BwramBitmapBits, 53} 54 55impl Sa1Mmc { 56 pub fn new() -> Self { 57 Self { 58 bank_c_base_addr: DEFAULT_BANK_C_ADDR, 59 bank_c_lorom_mapped: false, 60 lorom_bank_c_addr: DEFAULT_BANK_C_ADDR, 61 bank_d_base_addr: DEFAULT_BANK_D_ADDR, 62 bank_d_lorom_mapped: false, 63 lorom_bank_d_addr: DEFAULT_BANK_D_ADDR, 64 bank_e_base_addr: DEFAULT_BANK_E_ADDR, 65 bank_e_lorom_mapped: false, 66 lorom_bank_e_addr: DEFAULT_BANK_E_ADDR, 67 bank_f_base_addr: DEFAULT_BANK_F_ADDR, 68 bank_f_lorom_mapped: false, 69 lorom_bank_f_addr: DEFAULT_BANK_F_ADDR, 70 snes_bwram_base_addr: 0, 71 sa1_bwram_base_addr: 0, 72 sa1_bwram_source: BwramMapSource::default(), 73 bwram_bitmap_format: BwramBitmapBits::default(), 74 } 75 } 76 77 pub fn map_rom_address(&self, address: u32) -> Option<u32> { 78 let bank = (address >> 16) & 0xFF; 79 let offset = address & 0xFFFF; 80 match (bank, offset) { 81 (0x00..=0x1F, 0x8000..=0xFFFF) => { 82 // LoROM bank C 83 Some(lorom_map_address(self.lorom_bank_c_addr, address)) 84 } 85 (0x20..=0x3F, 0x8000..=0xFFFF) => { 86 // LoROM bank D 87 Some(lorom_map_address(self.lorom_bank_d_addr, address)) 88 } 89 (0x80..=0x9F, 0x8000..=0xFFFF) => { 90 // LoROM bank E 91 Some(lorom_map_address(self.lorom_bank_e_addr, address)) 92 } 93 (0xA0..=0xBF, 0x8000..=0xFFFF) => { 94 // LoROM bank F 95 Some(lorom_map_address(self.lorom_bank_f_addr, address)) 96 } 97 (0xC0..=0xCF, _) => { 98 // HiROM bank C 99 Some(self.bank_c_base_addr | (address & 0xFFFFF)) 100 } 101 (0xD0..=0xDF, _) => { 102 // HiROM bank D 103 Some(self.bank_d_base_addr | (address & 0xFFFFF)) 104 } 105 (0xE0..=0xEF, _) => { 106 // HiROM bank E 107 Some(self.bank_e_base_addr | (address & 0xFFFFF)) 108 } 109 (0xF0..=0xFF, _) => { 110 // HiROM bank F 111 Some(self.bank_f_base_addr | (address & 0xFFFFF)) 112 } 113 _ => None, 114 } 115 } 116 117 pub fn write_cxb(&mut self, value: u8) { 118 self.bank_c_base_addr = u32::from(value & 0x07) << 20; 119 self.bank_c_lorom_mapped = value.bit(7); 120 121 self.lorom_bank_c_addr = 122 if self.bank_c_lorom_mapped { self.bank_c_base_addr } else { DEFAULT_BANK_C_ADDR }; 123 124 log::trace!(" MMC bank C base address: {:06X}", self.bank_c_base_addr); 125 log::trace!(" MMC bank C LoROM mapped: {}", self.bank_c_lorom_mapped); 126 } 127 128 pub fn write_dxb(&mut self, value: u8) { 129 self.bank_d_base_addr = u32::from(value & 0x07) << 20; 130 self.bank_d_lorom_mapped = value.bit(7); 131 132 self.lorom_bank_d_addr = 133 if self.bank_d_lorom_mapped { self.bank_d_base_addr } else { DEFAULT_BANK_D_ADDR }; 134 135 log::trace!(" MMC bank D base address: {:06X}", self.bank_d_base_addr); 136 log::trace!(" MMC bank D LoROM mapped: {}", self.bank_d_lorom_mapped); 137 } 138 139 pub fn write_exb(&mut self, value: u8) { 140 self.bank_e_base_addr = u32::from(value & 0x07) << 20; 141 self.bank_e_lorom_mapped = value.bit(7); 142 143 self.lorom_bank_e_addr = 144 if self.bank_e_lorom_mapped { self.bank_e_base_addr } else { DEFAULT_BANK_E_ADDR }; 145 146 log::trace!(" MMC bank E base address: {:06X}", self.bank_e_base_addr); 147 log::trace!(" MMC bank E LoROM mapped: {}", self.bank_e_lorom_mapped); 148 } 149 150 pub fn write_fxb(&mut self, value: u8) { 151 self.bank_f_base_addr = u32::from(value & 0x07) << 20; 152 self.bank_f_lorom_mapped = value.bit(7); 153 154 self.lorom_bank_f_addr = 155 if self.bank_f_lorom_mapped { self.bank_f_base_addr } else { DEFAULT_BANK_F_ADDR }; 156 157 log::trace!(" MMC bank F base address: {:06X}", self.bank_f_base_addr); 158 log::trace!(" MMC bank F LoROM mapped: {}", self.bank_f_lorom_mapped); 159 } 160 161 pub fn write_bmaps(&mut self, value: u8) { 162 self.snes_bwram_base_addr = u32::from(value & 0x1F) << 13; 163 164 log::trace!(" SNES BW-RAM base address: {:X}", self.snes_bwram_base_addr); 165 } 166 167 pub fn write_bmap(&mut self, value: u8) { 168 self.sa1_bwram_base_addr = u32::from(value & 0x7F) << 13; 169 self.sa1_bwram_source = BwramMapSource::from_bit(value.bit(7)); 170 171 log::trace!(" SA-1 BW-RAM base address: {:X}", self.sa1_bwram_base_addr); 172 log::trace!(" SA-1 BW-RAM block source: {:?}", self.sa1_bwram_source); 173 } 174 175 pub fn write_bbf(&mut self, value: u8) { 176 self.bwram_bitmap_format = BwramBitmapBits::from_bit(value.bit(7)); 177 178 log::trace!(" SA-1 BW-RAM bitmap format: {:?}", self.bwram_bitmap_format); 179 } 180} 181 182fn lorom_map_address(base_addr: u32, address: u32) -> u32 { 183 // A21-A23 come from MMC base address, but other than that this is standard LoROM mapping 184 // Ignore A15 and shift A16-A20 right by 1 185 base_addr | (address & 0x7FFF) | ((address & 0x1F0000) >> 1) 186}