1mod rom; 2 3use crate::common; 4use crate::cx4::{Cx4Ram, Cx4Registers}; 5use jgenesis_common::num::{GetBit, U16Ext, U24Ext}; 6 7#[derive(Debug, Clone, Default)] 8struct DspRegisters { 9 a: u32, 10 m: i64, 11 // Used for reading data from cartridge ROM 12 ext_buffer: u8, 13 ext_pointer: u32, 14 // Used for reading data from CX4 ROM 15 rom_buffer: u32, 16 // Used for reading data from CX4 RAM 17 ram_buffer: u32, 18 ram_pointer: u16, 19 page: u16, 20 zero: bool, 21 negative: bool, 22 carry: bool, 23 call_stack: [(u16, u8); 16], 24 call_stack_ptr: u8, 25} 26 27impl DspRegisters { 28 fn push_call_stack(&mut self, page: u16, pointer: u8) { 29 self.call_stack[self.call_stack_ptr as usize] = (page, pointer); 30 self.call_stack_ptr = (self.call_stack_ptr + 1) & 0xF; 31 } 32 33 fn pull_call_stack(&mut self) -> (u16, u8) { 34 self.call_stack_ptr = self.call_stack_ptr.wrapping_sub(1) & 0xF; 35 self.call_stack[self.call_stack_ptr as usize] 36 } 37 38 fn set_nz(&mut self, value: u32) { 39 self.zero = value & 0xFFFFFF == 0; 40 self.negative = value.bit(23); 41 } 42} 43 44#[allow(clippy::match_same_arms)] 45pub fn execute(cx4_registers: &mut Cx4Registers, rom: &[u8], ram: &mut Cx4Ram) { 46 let mut dsp_registers = DspRegisters::default(); 47 48 log::trace!( 49 "Beginning execution with page {:04X} and pointer {:02X}", 50 cx4_registers.instruction_page, 51 cx4_registers.instruction_pointer 52 ); 53 54 loop { 55 let opcode_addr = cx4_registers.risc_pc(); 56 let rom_addr = common::lorom_map_rom_address(opcode_addr, rom.len() as u32); 57 let opcode = u16::from_le_bytes([rom[rom_addr as usize], rom[(rom_addr + 1) as usize]]); 58 cx4_registers.increment_instruction_pointer(); 59 60 log::trace!("opcode={opcode:04X}, PC={opcode_addr:06X}"); 61 62 // The first 6 bits of opcode are enough to distinguish between instructions 63 match opcode & 0xFC00 { 64 0x0000 => { 65 // nop; do nothing 66 } 67 0x0800 => jmp(cx4_registers, &dsp_registers, opcode), 68 0x0C00 => jz(cx4_registers, &dsp_registers, opcode), 69 0x1000 => jc(cx4_registers, &dsp_registers, opcode), 70 0x1400 => jn(cx4_registers, &dsp_registers, opcode), 71 0x1C00 => { 72 // "finish"; effectively does nothing, exists for timing purposes only? 73 } 74 0x2400 => skip(cx4_registers, &dsp_registers, opcode), 75 0x2800 => call(cx4_registers, &mut dsp_registers, opcode), 76 0x2C00 => callz(cx4_registers, &mut dsp_registers, opcode), 77 0x3000 => callc(cx4_registers, &mut dsp_registers, opcode), 78 0x3400 => calln(cx4_registers, &mut dsp_registers, opcode), 79 0x3C00 => ret(cx4_registers, &mut dsp_registers), 80 0x4000 => inc_ext_ptr(&mut dsp_registers), 81 0x4800 => cmpr_a_op(cx4_registers, &mut dsp_registers, opcode), 82 0x4C00 => cmpr_a_imm(&mut dsp_registers, opcode), 83 0x5000 => cmp_a_op(cx4_registers, &mut dsp_registers, opcode), 84 0x5400 => cmp_a_imm(&mut dsp_registers, opcode), 85 0x5800 => sign_extend(&mut dsp_registers, opcode), 86 0x6000 => match opcode & 0x0300 { 87 0x0000 => mov_a_op(cx4_registers, &mut dsp_registers, opcode), 88 0x0100 => mov_mbr_op(cx4_registers, &mut dsp_registers, rom, opcode), 89 0x0300 => mov_page_op(cx4_registers, &mut dsp_registers, opcode), 90 _ => { 91 log::warn!("Unexpected mov opcode: {opcode:04X}"); 92 } 93 }, 94 0x6400 => match opcode & 0x0300 { 95 0x0000 => mov_a_imm(&mut dsp_registers, opcode), 96 0x0300 => mov_page_imm(&mut dsp_registers, opcode), 97 _ => { 98 log::warn!("Unexpected mov opcode: {opcode:04X}"); 99 } 100 }, 101 0x6800 => read_ram_op(cx4_registers, &mut dsp_registers, ram, opcode), 102 0x6C00 => read_ram_imm(&mut dsp_registers, ram, opcode), 103 0x7000 => read_rom(cx4_registers, &mut dsp_registers, opcode), 104 0x7800 => movb_page_op(cx4_registers, &mut dsp_registers, opcode), 105 0x7C00 => movb_page_imm(&mut dsp_registers, opcode), 106 0x8000 => add_a_op(cx4_registers, &mut dsp_registers, opcode), 107 0x8400 => add_a_imm(&mut dsp_registers, opcode), 108 0x8800 => subr_a_op(cx4_registers, &mut dsp_registers, opcode), 109 0x8C00 => subr_a_imm(&mut dsp_registers, opcode), 110 0x9000 => sub_a_op(cx4_registers, &mut dsp_registers, opcode), 111 0x9400 => sub_a_imm(&mut dsp_registers, opcode), 112 0x9800 => smul_op(cx4_registers, &mut dsp_registers, opcode), 113 0x9C00 => smul_imm(&mut dsp_registers, opcode), 114 0xA800 => xor_a_op(cx4_registers, &mut dsp_registers, opcode), 115 0xAC00 => xor_a_imm(&mut dsp_registers, opcode), 116 0xB000 => and_a_op(cx4_registers, &mut dsp_registers, opcode), 117 0xB400 => and_a_imm(&mut dsp_registers, opcode), 118 0xB800 => or_a_op(cx4_registers, &mut dsp_registers, opcode), 119 0xBC00 => or_a_imm(&mut dsp_registers, opcode), 120 0xC000 => shr_a_op(cx4_registers, &mut dsp_registers, opcode), 121 0xC400 => shr_a_imm(&mut dsp_registers, opcode), 122 0xC800 => sar_a_op(cx4_registers, &mut dsp_registers, opcode), 123 0xCC00 => sar_a_imm(&mut dsp_registers, opcode), 124 0xD000 => ror_a_op(cx4_registers, &mut dsp_registers, opcode), 125 0xD400 => ror_a_imm(&mut dsp_registers, opcode), 126 0xD800 => shl_a_op(cx4_registers, &mut dsp_registers, opcode), 127 0xDC00 => shl_a_imm(&mut dsp_registers, opcode), 128 0xE000 => mov_op_a(cx4_registers, &mut dsp_registers, opcode), 129 0xE800 => movb_ram_op(cx4_registers, &dsp_registers, ram, opcode), 130 0xEC00 => movb_ram_ptr(&dsp_registers, ram, opcode), 131 0xF000 => swap(cx4_registers, &mut dsp_registers, opcode), 132 0xFC00 => { 133 // stop; function is complete 134 return; 135 } 136 _ => { 137 log::warn!("Unexpected opcode: {opcode:04X}"); 138 } 139 } 140 } 141} 142 143fn mov_a_imm(registers: &mut DspRegisters, opcode: u16) { 144 log::trace!("mov A, #${:02X}", opcode & 0xFF); 145 146 registers.a = (opcode & 0xFF).into(); 147} 148 149fn mov_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 150 log::trace!("mov A, ${:02X}", opcode & 0xFF); 151 152 dsp_registers.a = read_register(cx4_registers, dsp_registers, opcode) & 0xFFFFFF; 153} 154 155fn mov_mbr_op( 156 cx4_registers: &Cx4Registers, 157 dsp_registers: &mut DspRegisters, 158 rom: &[u8], 159 opcode: u16, 160) { 161 log::trace!("mov MBR, ${:02X}", opcode & 0xFF); 162 163 let value = if opcode & 0xFF == 0x2E { 164 // $612E seems to be the only opcode that reads "register" $2E, which reads a byte from 165 // cartridge ROM using the current pointer 166 let rom_addr = common::lorom_map_rom_address(dsp_registers.ext_pointer, rom.len() as u32); 167 rom[rom_addr as usize] 168 } else { 169 read_register(cx4_registers, dsp_registers, opcode) as u8 170 }; 171 dsp_registers.ext_buffer = value; 172} 173 174fn mov_op_a(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 175 log::trace!("mov ${:02X}, A", opcode & 0xFF); 176 177 let a = dsp_registers.a; 178 write_register(cx4_registers, dsp_registers, opcode, a); 179} 180 181fn mov_page_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 182 log::trace!("mov page, {:02X}", opcode & 0xFF); 183 184 let value = read_register(cx4_registers, dsp_registers, opcode); 185 dsp_registers.page = value as u16; 186} 187 188fn mov_page_imm(registers: &mut DspRegisters, opcode: u16) { 189 log::trace!("mov page, #${:02X}", opcode & 0xFF); 190 191 registers.page = opcode & 0xFF; 192} 193 194fn read_rom(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 195 log::trace!("readrom ${:02X}", opcode & 0xFF); 196 197 let address = read_register(cx4_registers, dsp_registers, opcode); 198 dsp_registers.rom_buffer = rom::read(address); 199} 200 201fn read_ram_op( 202 cx4_registers: &Cx4Registers, 203 dsp_registers: &mut DspRegisters, 204 ram: &Cx4Ram, 205 opcode: u16, 206) { 207 log::trace!("readram ${:02X}", opcode & 0xFF); 208 209 let address = read_register(cx4_registers, dsp_registers, opcode); 210 let value = ram.get((address & 0xFFF) as usize).copied().unwrap_or(0); 211 read_ram(dsp_registers, opcode, value); 212} 213 214fn read_ram_imm(dsp_registers: &mut DspRegisters, ram: &Cx4Ram, opcode: u16) { 215 log::trace!("readram #${:02X}", opcode & 0xFF); 216 217 let address = dsp_registers.ram_pointer.wrapping_add(opcode & 0xFF); 218 let value = ram.get(address as usize).copied().unwrap_or(0); 219 read_ram(dsp_registers, opcode, value); 220} 221 222fn read_ram(registers: &mut DspRegisters, opcode: u16, ram_value: u8) { 223 match opcode & 0x0300 { 224 0x0000 => { 225 registers.ram_buffer = (registers.ram_buffer & 0xFFFF_FF00) | u32::from(ram_value); 226 } 227 0x0100 => { 228 registers.ram_buffer = 229 (registers.ram_buffer & 0xFFFF_00FF) | (u32::from(ram_value) << 8); 230 } 231 0x0200 => { 232 registers.ram_buffer = 233 (registers.ram_buffer & 0x0000_FFFF) | (u32::from(ram_value) << 16); 234 } 235 _ => { 236 log::warn!("Unexpected read RAM opcode: {opcode:04X}"); 237 } 238 } 239 log::trace!(" read RAM value {ram_value}; RAM buffer is {:06X}", registers.ram_buffer); 240} 241 242fn movb_ram_op( 243 cx4_registers: &Cx4Registers, 244 dsp_registers: &DspRegisters, 245 ram: &mut Cx4Ram, 246 opcode: u16, 247) { 248 log::trace!("movb ram[${:02X}], ram_buf", opcode & 0xFF); 249 250 let ram_addr = (read_register(cx4_registers, dsp_registers, opcode) & 0xFFF) as usize; 251 if ram_addr < ram.len() { 252 let value = match opcode & 0x0300 { 253 0x0000 => dsp_registers.ram_buffer.low_byte(), 254 0x0100 => dsp_registers.ram_buffer.mid_byte(), 255 0x0200 => dsp_registers.ram_buffer.high_byte(), 256 _ => { 257 log::warn!("Unexpected movb RAM[..] opcode: {opcode:02X}"); 258 0x00 259 } 260 }; 261 ram[ram_addr] = value; 262 } 263} 264 265fn movb_ram_ptr(registers: &DspRegisters, ram: &mut Cx4Ram, opcode: u16) { 266 log::trace!("movbram[ptr+#${:02X}], ram_buf", opcode & 0xFF); 267 268 let ram_addr = (registers.ram_pointer.wrapping_add(opcode & 0xFF) & 0xFFF) as usize; 269 if ram_addr < ram.len() { 270 let value = match opcode & 0x0300 { 271 0x0000 => registers.ram_buffer.low_byte(), 272 0x0100 => registers.ram_buffer.mid_byte(), 273 0x0200 => registers.ram_buffer.high_byte(), 274 _ => { 275 log::warn!("Unexpected movb RAM[..] opcode: {opcode:02X}"); 276 0x00 277 } 278 }; 279 ram[ram_addr] = value; 280 log::trace!(" wrote {value:02X} to {ram_addr:03X}"); 281 } 282} 283 284fn movb_page_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 285 log::trace!("movb P, ${:02X}", opcode & 0xFF); 286 287 let value = read_register(cx4_registers, dsp_registers, opcode); 288 mov_page(dsp_registers, opcode, value as u8); 289} 290 291fn movb_page_imm(registers: &mut DspRegisters, opcode: u16) { 292 log::trace!("movb P, #${:02X}", opcode & 0xFF); 293 294 mov_page(registers, opcode, opcode as u8); 295} 296 297fn mov_page(registers: &mut DspRegisters, opcode: u16, value: u8) { 298 if !opcode.bit(8) { 299 registers.page.set_lsb(value); 300 } else { 301 registers.page.set_msb(value); 302 } 303} 304 305fn swap(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 306 log::trace!("swap A, ${:02}", opcode & 0xFF); 307 308 let old_a = dsp_registers.a; 309 dsp_registers.a = read_register(cx4_registers, dsp_registers, opcode); 310 write_register(cx4_registers, dsp_registers, opcode, old_a); 311} 312 313fn jmp(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 314 log::trace!("jmp {:02X}", opcode & 0xFF); 315 316 execute_jump(cx4_registers, dsp_registers, opcode); 317} 318 319fn jz(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 320 log::trace!("jz {:02X}", opcode & 0xFF); 321 322 if dsp_registers.zero { 323 execute_jump(cx4_registers, dsp_registers, opcode); 324 } 325} 326 327fn jc(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 328 log::trace!("jc {:02X}", opcode & 0xFF); 329 330 if dsp_registers.carry { 331 execute_jump(cx4_registers, dsp_registers, opcode); 332 } 333} 334 335fn jn(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 336 log::trace!("jn {:02X}", opcode & 0xFF); 337 338 if dsp_registers.negative { 339 execute_jump(cx4_registers, dsp_registers, opcode); 340 } 341} 342 343fn call(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 344 log::trace!("call {:02X}", opcode & 0xFF); 345 346 dsp_registers 347 .push_call_stack(cx4_registers.instruction_page, cx4_registers.instruction_pointer); 348 execute_jump(cx4_registers, dsp_registers, opcode); 349} 350 351fn callz(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 352 log::trace!("callz {:02X}", opcode & 0xFF); 353 354 if dsp_registers.zero { 355 dsp_registers 356 .push_call_stack(cx4_registers.instruction_page, cx4_registers.instruction_pointer); 357 execute_jump(cx4_registers, dsp_registers, opcode); 358 } 359} 360 361fn callc(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 362 log::trace!("callc {:02X}", opcode & 0xFF); 363 364 if dsp_registers.carry { 365 dsp_registers 366 .push_call_stack(cx4_registers.instruction_page, cx4_registers.instruction_pointer); 367 execute_jump(cx4_registers, dsp_registers, opcode); 368 } 369} 370 371fn calln(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 372 log::trace!("calln {:02X}", opcode & 0xFF); 373 374 if dsp_registers.negative { 375 dsp_registers 376 .push_call_stack(cx4_registers.instruction_page, cx4_registers.instruction_pointer); 377 execute_jump(cx4_registers, dsp_registers, opcode); 378 } 379} 380 381fn ret(cx4_registers: &mut Cx4Registers, dsp_registers: &mut DspRegisters) { 382 log::trace!("ret"); 383 384 let (page, pointer) = dsp_registers.pull_call_stack(); 385 cx4_registers.instruction_page = page; 386 cx4_registers.instruction_pointer = pointer; 387} 388 389fn execute_jump(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 390 cx4_registers.instruction_pointer = opcode as u8; 391 if opcode.bit(9) { 392 cx4_registers.instruction_page = dsp_registers.page; 393 } 394} 395 396fn skip(cx4_registers: &mut Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) { 397 log::trace!("skip, opcode={opcode:04X}"); 398 399 let value = opcode.bit(0); 400 let flag = match opcode & 0x0300 { 401 0x0100 => dsp_registers.carry, 402 0x0200 => dsp_registers.zero, 403 0x0300 => dsp_registers.negative, 404 _ => { 405 log::warn!("Unexpected skip opcode: {opcode:02X}"); 406 false 407 } 408 }; 409 if flag == value { 410 cx4_registers.instruction_pointer = cx4_registers.instruction_pointer.wrapping_add(1); 411 } 412} 413 414fn add_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 415 log::trace!("add A, ${:02X}", opcode & 0xFF); 416 417 let value = read_register(cx4_registers, dsp_registers, opcode); 418 add(dsp_registers, opcode, value); 419} 420 421fn add_a_imm(registers: &mut DspRegisters, opcode: u16) { 422 log::trace!("add A, #${:02X}", opcode & 0xFF); 423 424 add(registers, opcode, (opcode & 0xFF).into()); 425} 426 427fn add(registers: &mut DspRegisters, opcode: u16, value: u32) { 428 let result = apply_a_shift(registers.a, opcode) + value; 429 registers.set_nz(result); 430 registers.carry = result > 0xFFFFFF; 431 432 registers.a = result & 0xFFFFFF; 433} 434 435fn subr_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 436 log::trace!("subr A, ${:02X}", opcode & 0xFF); 437 438 let value = read_register(cx4_registers, dsp_registers, opcode); 439 sub(dsp_registers, opcode, value, true); 440} 441 442fn subr_a_imm(registers: &mut DspRegisters, opcode: u16) { 443 log::trace!("subr A, #${:02X}", opcode & 0xFF); 444 445 sub(registers, opcode, (opcode & 0xFF).into(), true); 446} 447 448fn sub_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 449 log::trace!("sub A, ${:02X}", opcode & 0xFF); 450 451 let value = read_register(cx4_registers, dsp_registers, opcode); 452 sub(dsp_registers, opcode, value, false); 453} 454 455fn sub_a_imm(registers: &mut DspRegisters, opcode: u16) { 456 log::trace!("sub A, #${:02X}", opcode & 0xFF); 457 458 sub(registers, opcode, (opcode & 0xFF).into(), false); 459} 460 461fn sub(registers: &mut DspRegisters, opcode: u16, value: u32, reverse: bool) { 462 let a = apply_a_shift(registers.a, opcode); 463 let result = if reverse { value.wrapping_sub(a) } else { a.wrapping_sub(value) }; 464 registers.set_nz(result); 465 registers.carry = result <= 0xFFFFFF; 466 467 registers.a = result & 0xFFFFFF; 468} 469 470fn cmpr_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 471 log::trace!("cmpr A, ${:02X}", opcode & 0xFF); 472 473 let value = read_register(cx4_registers, dsp_registers, opcode); 474 cmp(dsp_registers, opcode, value, true); 475} 476 477fn cmpr_a_imm(registers: &mut DspRegisters, opcode: u16) { 478 log::trace!("cmpr A, #${:02X}", opcode & 0xFF); 479 480 cmp(registers, opcode, (opcode & 0xFF).into(), true); 481} 482 483fn cmp_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 484 log::trace!("cmp A, ${:02X}", opcode & 0xFF); 485 486 let value = read_register(cx4_registers, dsp_registers, opcode); 487 cmp(dsp_registers, opcode, value, false); 488} 489 490fn cmp_a_imm(registers: &mut DspRegisters, opcode: u16) { 491 log::trace!("cmp A, #${:02X}", opcode & 0xFF); 492 493 cmp(registers, opcode, (opcode & 0xFF).into(), false); 494} 495 496fn cmp(registers: &mut DspRegisters, opcode: u16, value: u32, reverse: bool) { 497 let a = apply_a_shift(registers.a, opcode); 498 let result = if reverse { value.wrapping_sub(a) } else { a.wrapping_sub(value) }; 499 registers.set_nz(result); 500 registers.carry = result <= 0xFFFFFF; 501} 502 503fn smul_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 504 log::trace!("smul ${:02X}", opcode & 0xFF); 505 506 let value = read_register(cx4_registers, dsp_registers, opcode); 507 smul(dsp_registers, value); 508} 509 510fn smul_imm(registers: &mut DspRegisters, opcode: u16) { 511 log::trace!("smul #${:02X}", opcode & 0xFF); 512 513 let value = (opcode & 0xFF).into(); 514 smul(registers, value); 515} 516 517fn smul(registers: &mut DspRegisters, value: u32) { 518 // smul multiplies two signed 24-bit integers to produce a signed 48-bit result 519 // Simulate this by sign extending the 24-bit numbers to 64 bits and doing the multiplication 520 // in 64 bits 521 let a = ((registers.a as i32) << 8) >> 8; 522 let b = ((value as i32) << 8) >> 8; 523 registers.m = i64::from(a) * i64::from(b); 524} 525 526fn and_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 527 log::trace!("and A, ${:02X}", opcode & 0xFF); 528 529 let value = read_register(cx4_registers, dsp_registers, opcode); 530 and(dsp_registers, opcode, value); 531} 532 533fn and_a_imm(registers: &mut DspRegisters, opcode: u16) { 534 log::trace!("and A, #${:02X}", opcode & 0xFF); 535 536 and(registers, opcode, (opcode & 0xFF).into()); 537} 538 539fn and(registers: &mut DspRegisters, opcode: u16, value: u32) { 540 let result = apply_a_shift(registers.a, opcode) & value; 541 registers.set_nz(result); 542 543 registers.a = result & 0xFFFFFF; 544} 545 546fn or_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 547 log::trace!("or A, ${:02X}", opcode & 0xFF); 548 549 let value = read_register(cx4_registers, dsp_registers, opcode); 550 or(dsp_registers, opcode, value); 551} 552 553fn or_a_imm(registers: &mut DspRegisters, opcode: u16) { 554 log::trace!("or A, #${:02X}", opcode & 0xFF); 555 556 or(registers, opcode, (opcode & 0xFF).into()); 557} 558 559fn or(registers: &mut DspRegisters, opcode: u16, value: u32) { 560 let result = apply_a_shift(registers.a, opcode) | value; 561 registers.set_nz(result); 562 563 registers.a = result & 0xFFFFFF; 564} 565 566fn xor_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 567 log::trace!("xor A, ${:02X}", opcode & 0xFF); 568 569 let value = read_register(cx4_registers, dsp_registers, opcode); 570 xor(dsp_registers, opcode, value); 571} 572 573fn xor_a_imm(dsp_registers: &mut DspRegisters, opcode: u16) { 574 log::trace!("xor A, #${:02X}", opcode & 0xFF); 575 576 xor(dsp_registers, opcode, (opcode & 0xFF).into()); 577} 578 579fn xor(registers: &mut DspRegisters, opcode: u16, value: u32) { 580 let result = apply_a_shift(registers.a, opcode) ^ value; 581 registers.set_nz(result); 582 583 registers.a = result & 0xFFFFFF; 584} 585 586fn shr_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 587 log::trace!("shr ${:02X}", opcode & 0xFF); 588 589 let value = read_register(cx4_registers, dsp_registers, opcode); 590 shr(dsp_registers, value); 591} 592 593fn shr_a_imm(registers: &mut DspRegisters, opcode: u16) { 594 log::trace!("shr #${:02X}", opcode & 0xFF); 595 596 shr(registers, (opcode & 0x1F).into()); 597} 598 599fn shr(registers: &mut DspRegisters, value: u32) { 600 if value <= 24 { 601 registers.a >>= value; 602 } 603 registers.set_nz(registers.a); 604} 605 606fn sar_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 607 log::trace!("sar ${:02X}", opcode & 0xFF); 608 609 let value = read_register(cx4_registers, dsp_registers, opcode); 610 sar(dsp_registers, value); 611} 612 613fn sar_a_imm(registers: &mut DspRegisters, opcode: u16) { 614 log::trace!("sar #${:02X}", opcode & 0xFF); 615 616 sar(registers, (opcode & 0x1F).into()); 617} 618 619fn sar(registers: &mut DspRegisters, value: u32) { 620 if value <= 24 { 621 registers.a = ((((registers.a as i32) << 8) >> (8 + value)) as u32) & 0xFFFFFF; 622 } 623 registers.set_nz(registers.a); 624} 625 626fn shl_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 627 log::trace!("shl ${:02X}", opcode & 0xFF); 628 629 let value = read_register(cx4_registers, dsp_registers, opcode); 630 shl(dsp_registers, value); 631} 632 633fn shl_a_imm(registers: &mut DspRegisters, opcode: u16) { 634 log::trace!("shl #${:02X}", opcode & 0xFF); 635 636 shl(registers, (opcode & 0x1F).into()); 637} 638 639fn shl(registers: &mut DspRegisters, value: u32) { 640 if value <= 24 { 641 registers.a = (registers.a << value) & 0xFFFFFF; 642 } 643 registers.set_nz(registers.a); 644} 645 646fn ror_a_op(cx4_registers: &Cx4Registers, dsp_registers: &mut DspRegisters, opcode: u16) { 647 log::trace!("ror ${:02X}", opcode & 0xFF); 648 649 let value = read_register(cx4_registers, dsp_registers, opcode); 650 ror(dsp_registers, value); 651} 652 653fn ror_a_imm(registers: &mut DspRegisters, opcode: u16) { 654 log::trace!("ror #${:02X}", opcode & 0xFF); 655 656 ror(registers, (opcode & 0x1F).into()); 657} 658 659fn ror(registers: &mut DspRegisters, value: u32) { 660 if value <= 24 { 661 registers.a = ((registers.a >> value) | (registers.a << (24 - value))) & 0xFFFFFF; 662 } 663 registers.set_nz(registers.a); 664} 665 666fn sign_extend(registers: &mut DspRegisters, opcode: u16) { 667 log::trace!("exts, opcode={opcode:04X}"); 668 669 match opcode & 0x0300 { 670 0x0100 => { 671 registers.a = ((registers.a as i8) as u32) & 0xFFFFFF; 672 } 673 0x0200 => { 674 registers.a = ((registers.a as i16) as u32) & 0xFFFFFF; 675 } 676 _ => { 677 log::warn!("Unexpected sign extension opcode: {opcode:04X}"); 678 } 679 } 680 registers.set_nz(registers.a); 681} 682 683fn inc_ext_ptr(registers: &mut DspRegisters) { 684 log::trace!("inc MAR"); 685 686 registers.ext_pointer = (registers.ext_pointer + 1) & 0xFFFFFF; 687} 688 689fn apply_a_shift(a: u32, opcode: u16) -> u32 { 690 match opcode & 0x0300 { 691 0x0000 => a, 692 0x0100 => (a << 1) & 0xFFFFFF, 693 0x0200 => (a << 8) & 0xFFFFFF, 694 0x0300 => (a << 16) & 0xFFFFFF, 695 _ => unreachable!("value & 0x0300 is always one of the above values"), 696 } 697} 698 699const CONSTANT_REGISTERS: &[u32; 16] = &[ 700 0x000000, 0xFFFFFF, 0x00FF00, 0xFF0000, 0x00FFFF, 0xFFFF00, 0x800000, 0x7FFFFF, 0x008000, 701 0x007FFF, 0xFF7FFF, 0xFFFF7F, 0x010000, 0xFEFFFF, 0x000100, 0x00FEFF, 702]; 703 704fn read_register(cx4_registers: &Cx4Registers, dsp_registers: &DspRegisters, opcode: u16) -> u32 { 705 match opcode & 0xFF { 706 0x00 => dsp_registers.a, 707 0x01 => ((dsp_registers.m >> 24) as u32) & 0xFFFFFF, 708 0x02 => (dsp_registers.m as u32) & 0xFFFFFF, 709 0x03 => dsp_registers.ext_buffer.into(), 710 0x08 => dsp_registers.rom_buffer, 711 0x0C => dsp_registers.ram_buffer, 712 0x13 => dsp_registers.ext_pointer, 713 0x1C => dsp_registers.ram_pointer.into(), 714 0x20 => cx4_registers.instruction_pointer.into(), 715 0x28 => dsp_registers.page.into(), 716 // $50-$5F are "fake" registers that always read constant values 717 0x50..=0x5F => CONSTANT_REGISTERS[(opcode & 0xF) as usize], 718 0x60..=0x6F => cx4_registers.gpr[(opcode & 0xF) as usize], 719 _ => { 720 log::warn!("Unhandled CX4 coprocessor register read: {:02X}", opcode & 0xFF); 721 0x00 722 } 723 } 724} 725 726fn write_register( 727 cx4_registers: &mut Cx4Registers, 728 dsp_registers: &mut DspRegisters, 729 opcode: u16, 730 value: u32, 731) { 732 match opcode & 0xFF { 733 0x00 => { 734 dsp_registers.a = value; 735 } 736 0x03 => { 737 dsp_registers.ext_buffer = value as u8; 738 } 739 0x0C => { 740 dsp_registers.ram_buffer = value & 0xFFFFFF; 741 } 742 0x13 => { 743 dsp_registers.ext_pointer = value & 0xFFFFFF; 744 } 745 0x1C => { 746 dsp_registers.ram_pointer = (value & 0xFFF) as u16; 747 } 748 0x20 => { 749 cx4_registers.instruction_pointer = value as u8; 750 } 751 0x28 => { 752 dsp_registers.page = value as u16; 753 } 754 0x60..=0x6F => { 755 let register_idx = (opcode & 0xF) as usize; 756 cx4_registers.gpr[register_idx] = value & 0xFFFFFF; 757 } 758 _ => { 759 log::warn!("Unhandled CX4 coprocessor register write: {:02X}", opcode & 0xFF); 760 } 761 } 762}