1use bincode::{Decode, Encode}; 2use jgenesis_common::num::{GetBit, U16Ext, U24Ext}; 3 4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 5pub enum DirectRomStep { 6 #[default] 7 One, 8 Step, 9} 10 11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 12pub enum DirectRomStepTarget { 13 #[default] 14 Base, 15 Offset, 16} 17 18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 19pub enum DirectRomSpecialAction { 20 #[default] 21 None, 22 Add8Bit, 23 Add16BitAfterWrite, 24 Add16BitAfterRead, 25} 26 27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 28pub struct DirectDataRomMode { 29 step: DirectRomStep, 30 step_target: DirectRomStepTarget, 31 offset_enabled: bool, 32 sign_extend_step: bool, 33 sign_extend_offset: bool, 34 special_action: DirectRomSpecialAction, 35} 36 37impl From<u8> for DirectDataRomMode { 38 fn from(value: u8) -> Self { 39 Self { 40 step: if value.bit(0) { DirectRomStep::Step } else { DirectRomStep::One }, 41 step_target: if value.bit(4) { 42 DirectRomStepTarget::Offset 43 } else { 44 DirectRomStepTarget::Base 45 }, 46 offset_enabled: value.bit(1), 47 sign_extend_step: value.bit(2), 48 sign_extend_offset: value.bit(3), 49 special_action: match (value >> 5) & 0x03 { 50 0x00 => DirectRomSpecialAction::None, 51 0x01 => DirectRomSpecialAction::Add8Bit, 52 0x02 => DirectRomSpecialAction::Add16BitAfterWrite, 53 0x03 => DirectRomSpecialAction::Add16BitAfterRead, 54 _ => unreachable!("value & 0x03 is always <= 0x03"), 55 }, 56 } 57 } 58} 59 60#[derive(Debug, Clone, Default, Encode, Decode)] 61pub struct MathRegisters { 62 pub dividend: u32, 63 pub multiplier: u16, 64 pub divisor: u16, 65 pub result: u32, 66 pub remainder: u16, 67 pub signed: bool, 68} 69 70impl MathRegisters { 71 fn execute_multiplication(&mut self) { 72 let product = if self.signed { 73 (self.dividend as i16 as u32).wrapping_mul(self.multiplier as i16 as u32) 74 } else { 75 u32::from(self.dividend as u16) * u32::from(self.multiplier) 76 }; 77 self.result = product; 78 } 79 80 fn execute_division(&mut self) { 81 if self.divisor == 0 { 82 // Divide by zero 83 self.result = 0; 84 self.remainder = self.dividend as u16; 85 return; 86 } 87 88 let (quotient, remainder) = if self.signed { 89 let dividend = self.dividend as i32; 90 let divisor: i32 = (self.divisor as i16).into(); 91 92 ((dividend / divisor) as u32, (dividend % divisor) as u16) 93 } else { 94 let divisor: u32 = self.divisor.into(); 95 (self.dividend / divisor, (self.dividend % divisor) as u16) 96 }; 97 self.result = quotient; 98 self.remainder = remainder; 99 } 100} 101 102#[derive(Debug, Clone, Encode, Decode)] 103pub struct Registers { 104 pub sram_enabled: bool, 105 pub rom_bank_d: u8, 106 pub rom_bank_e: u8, 107 pub rom_bank_f: u8, 108 pub direct_data_rom_initialized: bool, 109 pub direct_data_rom_base: u32, 110 pub direct_data_rom_offset: u16, 111 pub direct_data_rom_step: u16, 112 pub direct_data_rom_mode: DirectDataRomMode, 113 pub direct_data_rom_mode_byte: u8, 114 pub r4814_written: bool, 115 pub r4815_written: bool, 116 pub math: MathRegisters, 117 // Functionality unknown; treat as read/write register 118 pub sram_bank: u8, 119} 120 121impl Registers { 122 pub fn new() -> Self { 123 Self { 124 sram_enabled: false, 125 rom_bank_d: 0x00, 126 rom_bank_e: 0x01, 127 rom_bank_f: 0x02, 128 direct_data_rom_initialized: false, 129 direct_data_rom_base: 0, 130 direct_data_rom_offset: 0, 131 direct_data_rom_step: 0, 132 direct_data_rom_mode: DirectDataRomMode::default(), 133 direct_data_rom_mode_byte: 0, 134 r4814_written: false, 135 r4815_written: false, 136 math: MathRegisters::default(), 137 sram_bank: 0, 138 } 139 } 140 141 pub fn read_sram_enabled(&self) -> u8 { 142 u8::from(self.sram_enabled) << 7 143 } 144 145 pub fn write_sram_enabled(&mut self, value: u8) { 146 self.sram_enabled = value.bit(7); 147 } 148 149 pub fn read_direct_data_rom_r4810(&mut self, data_rom: &[u8]) -> u8 { 150 if !self.direct_data_rom_initialized { 151 return 0; 152 } 153 154 // Register $4810: Read from either ROM[Base] or ROM[Base+Offset] depending on mode, and then 155 // increment either Base or Offset depending on mode 156 let mode = self.direct_data_rom_mode; 157 let rom_addr = if mode.offset_enabled { 158 let offset = extend_u16(self.direct_data_rom_offset, mode.sign_extend_offset); 159 self.direct_data_rom_base.wrapping_add(offset) & 0xFFFFFF 160 } else { 161 self.direct_data_rom_base 162 }; 163 let byte = data_rom.get(rom_addr as usize).copied().unwrap_or(0); 164 165 let step = match mode.step { 166 DirectRomStep::One => 1, 167 DirectRomStep::Step => extend_u16(self.direct_data_rom_step, mode.sign_extend_step), 168 }; 169 170 match mode.step_target { 171 DirectRomStepTarget::Base => { 172 self.direct_data_rom_base = self.direct_data_rom_base.wrapping_add(step) & 0xFFFFFF; 173 } 174 DirectRomStepTarget::Offset => { 175 self.direct_data_rom_offset = self.direct_data_rom_offset.wrapping_add(step as u16); 176 } 177 } 178 179 byte 180 } 181 182 pub fn read_direct_data_rom_r481a(&mut self, data_rom: &[u8]) -> u8 { 183 if !self.direct_data_rom_initialized { 184 return 0; 185 } 186 187 // Register $481A: Read from ROM[Base+Offset], and potentially set Base = Base+Offset depending 188 // on mode 189 let offset = 190 extend_u16(self.direct_data_rom_offset, self.direct_data_rom_mode.sign_extend_offset); 191 let rom_addr = self.direct_data_rom_base.wrapping_add(offset) & 0xFFFFFF; 192 let byte = data_rom.get(rom_addr as usize).copied().unwrap_or(0); 193 194 if self.direct_data_rom_mode.special_action == DirectRomSpecialAction::Add16BitAfterRead { 195 self.direct_data_rom_base = rom_addr; 196 } 197 198 byte 199 } 200 201 pub fn write_direct_data_rom_base_low(&mut self, value: u8) { 202 self.direct_data_rom_base.set_low_byte(value); 203 } 204 205 pub fn write_direct_data_rom_base_mid(&mut self, value: u8) { 206 self.direct_data_rom_base.set_mid_byte(value); 207 } 208 209 pub fn write_direct_data_rom_base_high(&mut self, value: u8) { 210 self.direct_data_rom_base.set_high_byte(value); 211 212 // Direct data ROM reads are "initialized" after the first write to this register 213 // Pre-initialization reads from $4810 and $481A always return 0 214 self.direct_data_rom_initialized = true; 215 } 216 217 pub fn write_direct_data_rom_offset_low(&mut self, value: u8) { 218 self.direct_data_rom_offset.set_lsb(value); 219 self.r4814_written = true; 220 221 if self.r4814_written && self.r4815_written { 222 self.apply_mode_write(); 223 } 224 } 225 226 pub fn write_direct_data_rom_offset_high(&mut self, value: u8) { 227 self.direct_data_rom_offset.set_msb(value); 228 self.r4815_written = true; 229 230 if self.r4814_written && self.r4815_written { 231 self.apply_mode_write(); 232 } 233 } 234 235 fn apply_mode_write(&mut self) { 236 let mode = self.direct_data_rom_mode_byte.into(); 237 self.direct_data_rom_mode = mode; 238 self.r4814_written = false; 239 self.r4815_written = false; 240 241 // 2 of the 3 special actions apply after $4814 and $4815 are written 242 match mode.special_action { 243 DirectRomSpecialAction::Add8Bit => { 244 let offset = extend_u8(self.direct_data_rom_offset.lsb(), mode.sign_extend_offset); 245 self.direct_data_rom_base = 246 self.direct_data_rom_base.wrapping_add(offset) & 0xFFFFFF; 247 } 248 DirectRomSpecialAction::Add16BitAfterWrite => { 249 let offset = extend_u16(self.direct_data_rom_offset, mode.sign_extend_offset); 250 self.direct_data_rom_base = 251 self.direct_data_rom_base.wrapping_add(offset) & 0xFFFFFF; 252 } 253 DirectRomSpecialAction::None | DirectRomSpecialAction::Add16BitAfterRead => {} 254 } 255 } 256 257 pub fn write_direct_data_rom_step_low(&mut self, value: u8) { 258 self.direct_data_rom_step.set_lsb(value); 259 } 260 261 pub fn write_direct_data_rom_step_high(&mut self, value: u8) { 262 self.direct_data_rom_step.set_msb(value); 263 } 264 265 pub fn write_direct_data_rom_mode(&mut self, value: u8) { 266 // Direct data ROM mode changes are not applied immediately, only after $4814 and $4815 are 267 // written (direct data ROM offset) 268 self.direct_data_rom_mode_byte = value; 269 self.r4814_written = false; 270 self.r4815_written = false; 271 272 // Writing to this register seems to reset offset? Momotarou Dentetsu Happy has glitchy 273 // audio without doing this 274 self.direct_data_rom_offset = 0; 275 } 276 277 pub fn read_dividend(&self, address: u32) -> u8 { 278 // Dividend is at $4820-$4823 279 let shift = 8 * (address & 0x3); 280 (self.math.dividend >> shift) as u8 281 } 282 283 pub fn write_dividend(&mut self, address: u32, value: u8) { 284 // Divdend is at $4820-$4823 285 let shift = 8 * (address & 0x3); 286 self.math.dividend = (self.math.dividend & !(0xFF << shift)) | (u32::from(value) << shift); 287 } 288 289 pub fn write_multiplier_low(&mut self, value: u8) { 290 self.math.multiplier.set_lsb(value); 291 } 292 293 pub fn write_multiplier_high(&mut self, value: u8) { 294 self.math.multiplier.set_msb(value); 295 296 // Writing multiplier MSB initiates multiplication 297 self.math.execute_multiplication(); 298 } 299 300 pub fn write_divisor_low(&mut self, value: u8) { 301 self.math.divisor.set_lsb(value); 302 } 303 304 pub fn write_divisor_high(&mut self, value: u8) { 305 self.math.divisor.set_msb(value); 306 307 // Writing divisor MSB initiates division 308 self.math.execute_division(); 309 } 310 311 pub fn read_math_result(&self, address: u32) -> u8 { 312 // Result is at $4828-$482B 313 let shift = 8 * (address & 0x3); 314 (self.math.result >> shift) as u8 315 } 316 317 pub fn write_math_mode(&mut self, value: u8) { 318 self.math.signed = value.bit(0); 319 } 320} 321 322fn extend_u8(value: u8, sign_extend: bool) -> u32 { 323 if sign_extend { (value as i8 as u32) & 0xFFFFFF } else { value.into() } 324} 325 326fn extend_u16(value: u16, sign_extend: bool) -> u32 { 327 if sign_extend { (value as i16 as u32) & 0xFFFFFF } else { value.into() } 328}