1use crate::superfx::gsu::instructions::{ 2 MemoryType, clear_prefix_flags, read_register, write_register, 3}; 4use crate::superfx::gsu::{GraphicsSupportUnit, MultiplierSpeed}; 5use jgenesis_common::num::{GetBit, SignBit}; 6 7pub(super) fn add( 8 opcode: u8, 9 memory_type: MemoryType, 10 gsu: &mut GraphicsSupportUnit, 11 rom: &[u8], 12 ram: &[u8], 13) -> u8 { 14 // ADD/ADC: Add / Add with carry 15 // ALT1 controls ADD vs. ADC 16 // ALT2 controls register operand vs. immediate operand 17 let operand = 18 if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) }; 19 20 let existing_carry = if gsu.alt1 { u16::from(gsu.carry_flag) } else { 0 }; 21 22 let source = read_register(gsu, gsu.sreg); 23 let (partial_sum, carry1) = source.overflowing_add(operand); 24 let (sum, carry2) = partial_sum.overflowing_add(existing_carry); 25 let carry = carry1 || carry2; 26 27 let bit_14_carry = (source & 0x7FFF) + (operand & 0x7FFF) + existing_carry >= 0x8000; 28 let overflow = bit_14_carry != carry; 29 30 gsu.zero_flag = sum == 0; 31 gsu.carry_flag = carry; 32 gsu.sign_flag = sum.sign_bit(); 33 gsu.overflow_flag = overflow; 34 35 let cycles = write_register(gsu, gsu.dreg, sum, rom, ram); 36 37 clear_prefix_flags(gsu); 38 cycles + memory_type.access_cycles(gsu.clock_speed) 39} 40 41pub(super) fn sub( 42 opcode: u8, 43 memory_type: MemoryType, 44 gsu: &mut GraphicsSupportUnit, 45 rom: &[u8], 46 ram: &[u8], 47) -> u8 { 48 // SUB/SBC/CMP: Subtract / Subtract with carry / Compare 49 // ALT1 controls SUB vs. SBC/CMP 50 // For SUB, ALT2 controls register operand vs. immediate operand 51 // For SBC/CMP, ALT2 controls SBC vs. CMP 52 let operand = if !gsu.alt1 && gsu.alt2 { 53 u16::from(opcode & 0x0F) 54 } else { 55 read_register(gsu, opcode & 0x0F) 56 }; 57 58 let sbc = gsu.alt1 && !gsu.alt2; 59 let existing_borrow = if sbc { u16::from(!gsu.carry_flag) } else { 0 }; 60 61 let source = read_register(gsu, gsu.sreg); 62 let (partial_diff, borrow1) = source.overflowing_sub(operand); 63 let (difference, borrow2) = partial_diff.overflowing_sub(existing_borrow); 64 let borrow = borrow1 || borrow2; 65 66 let bit_14_borrow = source & 0x7FFF < (operand & 0x7FFF) + existing_borrow; 67 let overflow = bit_14_borrow != borrow; 68 69 gsu.zero_flag = difference == 0; 70 gsu.carry_flag = !borrow; 71 gsu.sign_flag = difference.sign_bit(); 72 gsu.overflow_flag = overflow; 73 74 let mut cycles = 0; 75 if !(gsu.alt1 && gsu.alt2) { 76 // Only write if not CMP 77 cycles = write_register(gsu, gsu.dreg, difference, rom, ram); 78 } 79 80 clear_prefix_flags(gsu); 81 cycles + memory_type.access_cycles(gsu.clock_speed) 82} 83 84pub(super) fn fmult( 85 memory_type: MemoryType, 86 gsu: &mut GraphicsSupportUnit, 87 rom: &[u8], 88 ram: &[u8], 89) -> u8 { 90 // FMULT/LMULT: Fractional signed multiplication / Long signed multiplication 91 // ALT1 controls FMULT vs. LMULT 92 let source: i32 = (read_register(gsu, gsu.sreg) as i16).into(); 93 let operand: i32 = (gsu.r[6] as i16).into(); 94 let product = source * operand; 95 let high_word = (product >> 16) as u16; 96 97 if gsu.alt1 { 98 // LMULT: Write low word to R4 99 gsu.r[4] = product as u16; 100 } 101 102 let cycles = write_register(gsu, gsu.dreg, high_word, rom, ram); 103 104 gsu.zero_flag = high_word == 0; 105 gsu.carry_flag = product.bit(15); 106 gsu.sign_flag = high_word.sign_bit(); 107 108 clear_prefix_flags(gsu); 109 cycles 110 + match (memory_type, gsu.multiplier_speed) { 111 (MemoryType::CodeCache, MultiplierSpeed::Standard) => 8, 112 (MemoryType::CodeCache, MultiplierSpeed::High) => 4, 113 (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::Standard) => 11, 114 (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::High) => 7, 115 } 116} 117 118pub(super) fn mult( 119 opcode: u8, 120 memory_type: MemoryType, 121 gsu: &mut GraphicsSupportUnit, 122 rom: &[u8], 123 ram: &[u8], 124) -> u8 { 125 // MULT/UMULT: Signed multiplication / Unsigned multiplication 126 // ALT1 controls MULT vs. UMULT 127 // ALT2 controls register operand vs. immediate operand 128 let operand = 129 if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) & 0xFF }; 130 131 let source = read_register(gsu, gsu.sreg) & 0xFF; 132 133 let product = if gsu.alt1 { 134 // UMULT: Unsigned 8-bit x 8-bit -> 16-bit 135 source * operand 136 } else { 137 // MULT: Signed 8-bit x 8-bit -> 16-bit 138 (i16::from(source as i8) * i16::from(operand as i8)) as u16 139 }; 140 141 let cycles = write_register(gsu, gsu.dreg, product, rom, ram); 142 143 gsu.zero_flag = product == 0; 144 gsu.sign_flag = product.sign_bit(); 145 146 clear_prefix_flags(gsu); 147 // TODO ROM/RAM should take more cycles at 21.47 MHz? 148 cycles 149 + match (memory_type, gsu.multiplier_speed) { 150 (MemoryType::CodeCache, MultiplierSpeed::Standard) => 2, 151 (MemoryType::CodeCache, MultiplierSpeed::High) => 1, 152 (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::Standard) => 5, 153 (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::High) => 3, 154 } 155} 156 157pub(super) fn inc( 158 opcode: u8, 159 memory_type: MemoryType, 160 gsu: &mut GraphicsSupportUnit, 161 rom: &[u8], 162 ram: &[u8], 163) -> u8 { 164 // INC Rn: Increment register 165 let register = opcode & 0x0F; 166 let incremented = gsu.r[register as usize].wrapping_add(1); 167 let cycles = write_register(gsu, register, incremented, rom, ram); 168 169 gsu.zero_flag = incremented == 0; 170 gsu.sign_flag = incremented.sign_bit(); 171 172 clear_prefix_flags(gsu); 173 cycles + memory_type.access_cycles(gsu.clock_speed) 174} 175 176pub(super) fn dec( 177 opcode: u8, 178 memory_type: MemoryType, 179 gsu: &mut GraphicsSupportUnit, 180 rom: &[u8], 181 ram: &[u8], 182) -> u8 { 183 // DEC Rn: Decrement register 184 let register = opcode & 0x0F; 185 let decremented = gsu.r[register as usize].wrapping_sub(1); 186 let cycles = write_register(gsu, register, decremented, rom, ram); 187 188 gsu.zero_flag = decremented == 0; 189 gsu.sign_flag = decremented.sign_bit(); 190 191 clear_prefix_flags(gsu); 192 cycles + memory_type.access_cycles(gsu.clock_speed) 193} 194 195pub(super) fn and( 196 opcode: u8, 197 memory_type: MemoryType, 198 gsu: &mut GraphicsSupportUnit, 199 rom: &[u8], 200 ram: &[u8], 201) -> u8 { 202 // AND/BIC: And / And with complement 203 // ALT1 controls AND vs. BIC 204 // ALT2 controls register operand vs. immediate operand 205 let operand = 206 if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) }; 207 208 let source = read_register(gsu, gsu.sreg); 209 let result = if gsu.alt1 { source & !operand } else { source & operand }; 210 211 let cycles = write_register(gsu, gsu.dreg, result, rom, ram); 212 213 gsu.zero_flag = result == 0; 214 gsu.sign_flag = result.sign_bit(); 215 216 clear_prefix_flags(gsu); 217 cycles + memory_type.access_cycles(gsu.clock_speed) 218} 219 220pub(super) fn or( 221 opcode: u8, 222 memory_type: MemoryType, 223 gsu: &mut GraphicsSupportUnit, 224 rom: &[u8], 225 ram: &[u8], 226) -> u8 { 227 // OR/XOR: Or / Exclusive or 228 // ALT1 controls OR vs. XOR 229 // ALT2 controls register operand vs. immediate operand 230 let operand = 231 if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) }; 232 233 let source = read_register(gsu, gsu.sreg); 234 let result = if gsu.alt1 { source ^ operand } else { source | operand }; 235 236 let cycles = write_register(gsu, gsu.dreg, result, rom, ram); 237 238 gsu.zero_flag = result == 0; 239 gsu.sign_flag = result.sign_bit(); 240 241 clear_prefix_flags(gsu); 242 cycles + memory_type.access_cycles(gsu.clock_speed) 243} 244 245pub(super) fn not( 246 memory_type: MemoryType, 247 gsu: &mut GraphicsSupportUnit, 248 rom: &[u8], 249 ram: &[u8], 250) -> u8 { 251 // NOT: Bitwise not 252 let source = read_register(gsu, gsu.sreg); 253 let inverted = !source; 254 let cycles = write_register(gsu, gsu.dreg, inverted, rom, ram); 255 256 gsu.zero_flag = inverted == 0; 257 gsu.sign_flag = inverted.sign_bit(); 258 259 clear_prefix_flags(gsu); 260 cycles + memory_type.access_cycles(gsu.clock_speed) 261} 262 263pub(super) fn asr( 264 memory_type: MemoryType, 265 gsu: &mut GraphicsSupportUnit, 266 rom: &[u8], 267 ram: &[u8], 268) -> u8 { 269 // ASR/DIV2: Arithmetic shift right / Divide by 2 270 // ALT1 controls ASR vs. DIV2 271 let source = read_register(gsu, gsu.sreg); 272 273 // DIV2 is equivalent to ASR unless Sreg == -1, in which case DIV2 produces 0 and ASR produces -1 274 let shifted = 275 if gsu.alt1 && source == u16::MAX { 0 } else { (source >> 1) | (source & 0x8000) }; 276 277 let cycles = write_register(gsu, gsu.dreg, shifted, rom, ram); 278 279 gsu.zero_flag = shifted == 0; 280 gsu.carry_flag = source.bit(0); 281 gsu.sign_flag = shifted.sign_bit(); 282 283 clear_prefix_flags(gsu); 284 cycles + memory_type.access_cycles(gsu.clock_speed) 285} 286 287pub(super) fn lsr( 288 memory_type: MemoryType, 289 gsu: &mut GraphicsSupportUnit, 290 rom: &[u8], 291 ram: &[u8], 292) -> u8 { 293 // LSR: Logical shift right 294 let source = read_register(gsu, gsu.sreg); 295 let shifted = source >> 1; 296 let cycles = write_register(gsu, gsu.dreg, shifted, rom, ram); 297 298 gsu.zero_flag = shifted == 0; 299 gsu.carry_flag = source.bit(0); 300 gsu.sign_flag = false; 301 302 clear_prefix_flags(gsu); 303 cycles + memory_type.access_cycles(gsu.clock_speed) 304} 305 306pub(super) fn rol( 307 memory_type: MemoryType, 308 gsu: &mut GraphicsSupportUnit, 309 rom: &[u8], 310 ram: &[u8], 311) -> u8 { 312 // ROL: Rotate left 313 let source = read_register(gsu, gsu.sreg); 314 let rotated = (source << 1) | u16::from(gsu.carry_flag); 315 let cycles = write_register(gsu, gsu.dreg, rotated, rom, ram); 316 317 gsu.zero_flag = rotated == 0; 318 gsu.carry_flag = source.sign_bit(); 319 gsu.sign_flag = rotated.sign_bit(); 320 321 clear_prefix_flags(gsu); 322 cycles + memory_type.access_cycles(gsu.clock_speed) 323} 324 325pub(super) fn ror( 326 memory_type: MemoryType, 327 gsu: &mut GraphicsSupportUnit, 328 rom: &[u8], 329 ram: &[u8], 330) -> u8 { 331 // ROR: Rotate right 332 let source = read_register(gsu, gsu.sreg); 333 let rotated = (source >> 1) | (u16::from(gsu.carry_flag) << 15); 334 let cycles = write_register(gsu, gsu.dreg, rotated, rom, ram); 335 336 gsu.zero_flag = rotated == 0; 337 gsu.carry_flag = source.bit(0); 338 gsu.sign_flag = rotated.sign_bit(); 339 340 clear_prefix_flags(gsu); 341 cycles + memory_type.access_cycles(gsu.clock_speed) 342} 343 344pub(super) fn sex( 345 memory_type: MemoryType, 346 gsu: &mut GraphicsSupportUnit, 347 rom: &[u8], 348 ram: &[u8], 349) -> u8 { 350 // SEX: Sign extend 351 let source = read_register(gsu, gsu.sreg); 352 let extended = (source as i8) as u16; 353 let cycles = write_register(gsu, gsu.dreg, extended, rom, ram); 354 355 gsu.zero_flag = extended == 0; 356 gsu.sign_flag = extended.sign_bit(); 357 358 clear_prefix_flags(gsu); 359 cycles + memory_type.access_cycles(gsu.clock_speed) 360}