1//! Samsung SSP1601 DSP, the primary component of the SVP 2 3use crate::svp::{PmcWaitingFor, StatusRegister, Svp}; 4use jgenesis_common::num::GetBit; 5use std::fmt::{Display, Formatter}; 6 7#[derive(Debug, Clone, Copy, PartialEq, Eq)] 8enum AluOp { 9 Add, 10 Subtract, 11 Compare, 12 And, 13 Or, 14 ExclusiveOr, 15} 16 17impl AluOp { 18 fn from_opcode(opcode: u16) -> Option<Self> { 19 match opcode & 0xE000 { 20 0x2000 => Some(Self::Subtract), 21 0x6000 => Some(Self::Compare), 22 0x8000 => Some(Self::Add), 23 0xA000 => Some(Self::And), 24 0xC000 => Some(Self::Or), 25 0xE000 => Some(Self::ExclusiveOr), 26 _ => None, 27 } 28 } 29} 30 31#[derive(Debug, Clone, Copy, PartialEq, Eq)] 32enum AccumulateOp { 33 Zero, 34 Add, 35 Subtract, 36} 37 38#[derive(Debug, Clone, Copy, PartialEq, Eq)] 39enum Condition { 40 True, 41 Zero, 42 NotZero, 43 Negative, 44 NotNegative, 45} 46 47impl Condition { 48 fn from_opcode(opcode: u16) -> Self { 49 match opcode & 0x01F0 { 50 0x0000 => Self::True, 51 0x0050 => Self::NotZero, 52 0x0150 => Self::Zero, 53 0x0070 => Self::NotNegative, 54 0x0170 => Self::Negative, 55 _ => panic!("Invalid SVP opcode (invalid condition): {opcode:04X}"), 56 } 57 } 58 59 fn check(self, status: StatusRegister) -> bool { 60 match self { 61 Self::True => true, 62 Self::Zero => status.zero, 63 Self::NotZero => !status.zero, 64 Self::Negative => status.negative, 65 Self::NotNegative => !status.negative, 66 } 67 } 68} 69 70#[derive(Debug, Clone, Copy, PartialEq, Eq)] 71enum RamBank { 72 Zero, 73 One, 74} 75 76impl RamBank { 77 fn from_opcode(opcode: u16) -> Self { 78 if opcode.bit(8) { Self::One } else { Self::Zero } 79 } 80} 81 82impl Display for RamBank { 83 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 84 match self { 85 Self::Zero => write!(f, "RAM0"), 86 Self::One => write!(f, "RAM1"), 87 } 88 } 89} 90 91#[derive(Debug, Clone, Copy, PartialEq, Eq)] 92enum AddressingMode { 93 // d / s 94 GeneralRegister(u16), 95 // ri / rj 96 PointerRegister(RamBank, u16), 97 // (ri) / (rj) 98 Indirect { bank: RamBank, pointer: u16, modifier: u16 }, 99 // ((ri)) / ((rj)) 100 DoubleIndirect { bank: RamBank, pointer: u16, modifier: u16 }, 101 // addr 102 Direct { bank: RamBank, address: u8 }, 103 // imm 104 Immediate, 105 // simm 106 ShortImmediate(u8), 107 // (A) 108 AccumulatorIndirect, 109} 110 111impl AddressingMode { 112 const ACCUMULATOR_REGISTER: Self = Self::GeneralRegister(3); 113} 114 115impl Display for AddressingMode { 116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 117 match self { 118 Self::GeneralRegister(0) => write!(f, "NULL"), 119 Self::GeneralRegister(1) => write!(f, "X"), 120 Self::GeneralRegister(2) => write!(f, "Y"), 121 Self::GeneralRegister(3) => write!(f, "AH"), 122 Self::GeneralRegister(4) => write!(f, "ST"), 123 Self::GeneralRegister(5) => write!(f, "STACK"), 124 Self::GeneralRegister(6) => write!(f, "PC"), 125 Self::GeneralRegister(7) => write!(f, "P"), 126 Self::GeneralRegister(8) => write!(f, "PM0/XSTStatus"), 127 Self::GeneralRegister(9) => write!(f, "PM1"), 128 Self::GeneralRegister(10) => write!(f, "PM2"), 129 Self::GeneralRegister(11) => write!(f, "PM3/XST"), 130 Self::GeneralRegister(12) => write!(f, "PM4"), 131 Self::GeneralRegister(14) => write!(f, "PMC"), 132 Self::GeneralRegister(15) => write!(f, "AL"), 133 Self::GeneralRegister(_) => write!(f, "(invalid register)"), 134 Self::PointerRegister(bank, pointer) => write!(f, "RIJ[{bank}, {pointer}]"), 135 Self::Indirect { bank, pointer, modifier } => { 136 write!(f, "(RIJ)[{bank}, {pointer}]/mod={modifier}") 137 } 138 Self::DoubleIndirect { bank, pointer, modifier } => { 139 write!(f, "((RIJ))[{bank}, {pointer}]/mod={modifier}") 140 } 141 Self::Direct { bank, address } => write!(f, "{bank}[{address:02X}]"), 142 Self::Immediate => write!(f, "#<imm>"), 143 Self::ShortImmediate(value) => write!(f, "#<{value:02X}>"), 144 Self::AccumulatorIndirect => write!(f, "(AH)"), 145 } 146 } 147} 148 149pub fn execute_instruction(svp: &mut Svp, rom: &[u16]) { 150 let opcode = fetch_operand(svp, rom); 151 152 log::trace!("PC={:04X}, opcode={opcode:04X}", svp.registers.pc.wrapping_sub(1)); 153 154 // The first 8 bits are enough to distinguish between all opcodes 155 match opcode & 0xFF00 { 156 0x0000 => { 157 // ld d, s 158 ld_d_s(svp, rom, opcode); 159 } 160 0x0200 | 0x0300 => { 161 // ld d, (ri) 162 ld_d_ri_indirect(svp, rom, opcode); 163 } 164 0x0400 | 0x0500 => { 165 // ld (ri), s 166 ld_ri_s_indirect(svp, rom, opcode); 167 } 168 0x0600 | 0x0700 => { 169 // ld A, addr 170 ld_a_addr(svp, rom, opcode); 171 } 172 0x0800 => { 173 // ldi d, imm 174 ldi_d_imm(svp, rom, opcode); 175 } 176 0x0A00 | 0x0B00 => { 177 // ld d, ((ri)) 178 ld_d_ri_double_indirect(svp, rom, opcode); 179 } 180 0x0C00 | 0x0D00 => { 181 // ldi (ri), imm 182 ldi_ri_imm(svp, rom, opcode); 183 } 184 0x0E00 | 0x0F00 => { 185 // ld addr, A 186 ld_addr_a(svp, rom, opcode); 187 } 188 0x1200 | 0x1300 => { 189 // ld d, ri 190 ld_d_ri(svp, rom, opcode); 191 } 192 0x1400 | 0x1500 => { 193 // ld ri, s 194 ld_ri_s(svp, rom, opcode); 195 } 196 0x1800..=0x1F00 => { 197 // ldi ri, simm 198 ldi_ri_simm(svp, rom, opcode); 199 } 200 0x3700 => { 201 // mpys (rj), (ri) 202 execute_multiply_accumulate(svp, opcode, AccumulateOp::Subtract); 203 } 204 0x4800 | 0x4900 => { 205 // call cond, addr 206 execute_call(svp, rom, opcode); 207 } 208 0x4A00 => { 209 // ld d, (a) 210 ld_d_a_indirect(svp, rom, opcode); 211 } 212 0x4C00 | 0x4D00 => { 213 // bra cond, addr 214 execute_bra(svp, rom, opcode); 215 } 216 0x9000 | 0x9100 => { 217 // mod cond, op 218 execute_mod(svp, opcode); 219 } 220 0x9700 => { 221 // mpya (rj), (ri) 222 execute_multiply_accumulate(svp, opcode, AccumulateOp::Add); 223 } 224 0xB700 => { 225 // mld (rj), (ri) 226 execute_multiply_accumulate(svp, opcode, AccumulateOp::Zero); 227 } 228 0xFF00 => { 229 // Treat as a no-op; do nothing 230 } 231 _ => { 232 // ALU ops; highest 3 bits determine op, next 5 bits determine addressing mode 233 execute_alu(svp, rom, opcode); 234 } 235 } 236} 237 238fn fetch_operand(svp: &mut Svp, rom: &[u16]) -> u16 { 239 let operand = svp.read_program_memory(svp.registers.pc, rom); 240 svp.registers.pc = svp.registers.pc.wrapping_add(1); 241 operand 242} 243 244fn execute_load(svp: &mut Svp, rom: &[u16], source: AddressingMode, dest: AddressingMode) { 245 log::trace!(" LD source={source}, dest={dest}"); 246 247 match (source, dest) { 248 (AddressingMode::GeneralRegister(7), AddressingMode::GeneralRegister(3)) => { 249 // P to A; copy all 32 bits 250 svp.registers.accumulator = svp.registers.product(); 251 log::trace!(" A = {:08X}", svp.registers.accumulator); 252 } 253 ( 254 AddressingMode::GeneralRegister(0), 255 AddressingMode::GeneralRegister(register @ 8..=15), 256 ) => { 257 // Blind write; program PM register (if applicable) and reset PMC state 258 log::trace!("Blind write to register {register}"); 259 260 if register <= 12 { 261 let pm_idx = (register - 8) as usize; 262 svp.registers.pm_write[pm_idx] 263 .initialize(svp.registers.pmc.address, svp.registers.pmc.mode); 264 265 log::trace!( 266 "Initialized PM{pm_idx} for writes: {:X?}", 267 svp.registers.pm_write[pm_idx] 268 ); 269 } 270 271 if register != 14 { 272 svp.registers.pmc.waiting_for = PmcWaitingFor::Address; 273 } else { 274 // Blind writes to PMC toggle state instead of resetting 275 svp.registers.pmc.waiting_for = svp.registers.pmc.waiting_for.toggle(); 276 } 277 } 278 ( 279 AddressingMode::GeneralRegister(register @ 8..=15), 280 AddressingMode::GeneralRegister(0), 281 ) => { 282 // Blind read; program PM register (if applicable) and reset PMC state 283 log::trace!("Blind read to register {register}"); 284 285 if register <= 12 { 286 let pm_idx = (register - 8) as usize; 287 svp.registers.pm_read[pm_idx] 288 .initialize(svp.registers.pmc.address, svp.registers.pmc.mode); 289 290 log::trace!( 291 "Initialized PM{pm_idx} for reads: {:X?}", 292 svp.registers.pm_read[pm_idx] 293 ); 294 } 295 296 if register != 14 { 297 svp.registers.pmc.waiting_for = PmcWaitingFor::Address; 298 } else { 299 // Blind reads to PMC toggle state instead of resetting 300 svp.registers.pmc.waiting_for = svp.registers.pmc.waiting_for.toggle(); 301 } 302 } 303 _ => { 304 // Normal 16-bit load 305 let value = read_addressing_mode(svp, rom, source); 306 write_addressing_mode(svp, dest, value); 307 308 log::trace!(" Wrote value {value:04X}"); 309 } 310 } 311} 312 313fn execute_alu(svp: &mut Svp, rom: &[u16], opcode: u16) { 314 let Some(op) = AluOp::from_opcode(opcode) else { 315 panic!("Invalid SSP1601 opcode: {opcode:04X}"); 316 }; 317 318 let source = parse_alu_addressing_mode(opcode); 319 320 log::trace!(" ALU op={op:?}, source={source}"); 321 322 // ALU operations are 32-bit, but most sources are 16-bit. 323 // If A or P is the source, use all 32 bits; otherwise shift the 16-bit value into the high 324 // word of a 32-bit value 325 let operand = match source { 326 AddressingMode::GeneralRegister(3) => svp.registers.accumulator, 327 AddressingMode::GeneralRegister(7) => svp.registers.product(), 328 _ => u32::from(read_addressing_mode(svp, rom, source)) << 16, 329 }; 330 331 let accumulator = svp.registers.accumulator; 332 let result = match op { 333 AluOp::Add => accumulator.wrapping_add(operand), 334 AluOp::Subtract | AluOp::Compare => accumulator.wrapping_sub(operand), 335 AluOp::And => accumulator & operand, 336 AluOp::Or => accumulator | operand, 337 AluOp::ExclusiveOr => accumulator ^ operand, 338 }; 339 340 update_flags(svp, result); 341 if op != AluOp::Compare { 342 svp.registers.accumulator = result; 343 } 344 345 log::trace!(" ALU result {result:08X}"); 346} 347 348fn update_flags(svp: &mut Svp, accumulator: u32) { 349 svp.registers.status.zero = accumulator == 0; 350 svp.registers.status.negative = accumulator.bit(31); 351} 352 353fn execute_mod(svp: &mut Svp, opcode: u16) { 354 let condition = Condition::from_opcode(opcode); 355 if !condition.check(svp.registers.status) { 356 log::trace!(" MODIFY cond={condition:?}, condition false"); 357 return; 358 } 359 360 // Lowest 3 bits determine operation 361 match opcode & 0x0007 { 362 0x0002 => { 363 // Arithmetic right shift 364 log::trace!(" ASR cond={condition:?}"); 365 366 svp.registers.accumulator = ((svp.registers.accumulator as i32) >> 1) as u32; 367 } 368 0x0003 => { 369 // Left shift 370 log::trace!(" SL cond={condition:?}"); 371 372 svp.registers.accumulator <<= 1; 373 } 374 0x0006 => { 375 // Negate 376 log::trace!(" NEG cond={condition:?}"); 377 378 svp.registers.accumulator = (!svp.registers.accumulator).wrapping_add(1); 379 } 380 0x0007 => { 381 // Absolute value 382 log::trace!(" ABS cond={condition:?}"); 383 384 if svp.registers.accumulator.bit(31) { 385 svp.registers.accumulator = (!svp.registers.accumulator).wrapping_add(1); 386 } 387 } 388 _ => panic!("Invalid SVP opcode: {opcode:04X}"), 389 } 390 391 update_flags(svp, svp.registers.accumulator); 392} 393 394fn execute_call(svp: &mut Svp, rom: &[u16], opcode: u16) { 395 let address = fetch_operand(svp, rom); 396 let condition = Condition::from_opcode(opcode); 397 398 log::trace!(" CALL cond={condition:?}, address={address:04X}"); 399 400 if condition.check(svp.registers.status) { 401 svp.registers.stack.push(svp.registers.pc); 402 svp.registers.pc = address; 403 } 404} 405 406fn execute_bra(svp: &mut Svp, rom: &[u16], opcode: u16) { 407 let address = fetch_operand(svp, rom); 408 let condition = Condition::from_opcode(opcode); 409 410 log::trace!(" BRA cond={condition:?}, address={address:04X}"); 411 412 if condition.check(svp.registers.status) { 413 svp.registers.pc = address; 414 } 415} 416 417fn execute_multiply_accumulate(svp: &mut Svp, opcode: u16, op: AccumulateOp) { 418 // Accumulation is performed before changing the multiply result via X and Y 419 match op { 420 AccumulateOp::Zero => { 421 svp.registers.accumulator = 0; 422 } 423 AccumulateOp::Add => { 424 svp.registers.accumulator = 425 svp.registers.accumulator.wrapping_add(svp.registers.product()); 426 } 427 AccumulateOp::Subtract => { 428 svp.registers.accumulator = 429 svp.registers.accumulator.wrapping_sub(svp.registers.product()); 430 } 431 } 432 433 update_flags(svp, svp.registers.accumulator); 434 435 // X is always set to a value from RAM0 436 let x_pointer = opcode & 0x03; 437 let x_modifier = (opcode >> 2) & 0x03; 438 let ram0_addr = read_pointer(svp, RamBank::Zero, x_pointer, x_modifier); 439 svp.registers.x = svp.ram0[ram0_addr as usize]; 440 441 // Y is always set to a value from RAM1 442 let y_pointer = (opcode >> 4) & 0x03; 443 let y_modifier = (opcode >> 6) & 0x03; 444 let ram1_addr = read_pointer(svp, RamBank::One, y_pointer, y_modifier); 445 svp.registers.y = svp.ram1[ram1_addr as usize]; 446 447 log::trace!( 448 " MUL op={op:?}, X=RAM0[p{x_pointer}]/mod={x_modifier}, Y=RAM1[p{y_pointer}]/mod={y_modifier}" 449 ); 450 log::trace!(" A={:08X}, P={:08X}", svp.registers.accumulator, svp.registers.product()); 451} 452 453fn ld_d_s(svp: &mut Svp, rom: &[u16], opcode: u16) { 454 let source = AddressingMode::GeneralRegister(opcode & 0xF); 455 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 456 execute_load(svp, rom, source, dest); 457} 458 459fn ld_d_ri(svp: &mut Svp, rom: &[u16], opcode: u16) { 460 let bank = RamBank::from_opcode(opcode); 461 let pointer = opcode & 0x03; 462 let source = AddressingMode::PointerRegister(bank, pointer); 463 464 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 465 466 execute_load(svp, rom, source, dest); 467} 468 469fn ld_ri_s(svp: &mut Svp, rom: &[u16], opcode: u16) { 470 let bank = RamBank::from_opcode(opcode); 471 let pointer = opcode & 0x03; 472 let dest = AddressingMode::PointerRegister(bank, pointer); 473 474 let source = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 475 476 execute_load(svp, rom, source, dest); 477} 478 479fn ld_d_ri_indirect(svp: &mut Svp, rom: &[u16], opcode: u16) { 480 let bank = RamBank::from_opcode(opcode); 481 let pointer = opcode & 0x03; 482 let modifier = (opcode >> 2) & 0x03; 483 let source = AddressingMode::Indirect { bank, pointer, modifier }; 484 485 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 486 487 execute_load(svp, rom, source, dest); 488} 489 490fn ld_ri_s_indirect(svp: &mut Svp, rom: &[u16], opcode: u16) { 491 let bank = RamBank::from_opcode(opcode); 492 let pointer = opcode & 0x03; 493 let modifier = (opcode >> 2) & 0x03; 494 let dest = AddressingMode::Indirect { bank, pointer, modifier }; 495 496 let source = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 497 498 execute_load(svp, rom, source, dest); 499} 500 501fn ld_d_ri_double_indirect(svp: &mut Svp, rom: &[u16], opcode: u16) { 502 let bank = RamBank::from_opcode(opcode); 503 let pointer = opcode & 0x03; 504 let modifier = (opcode >> 2) & 0x03; 505 let source = AddressingMode::DoubleIndirect { bank, pointer, modifier }; 506 507 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 508 509 execute_load(svp, rom, source, dest); 510} 511 512fn ld_a_addr(svp: &mut Svp, rom: &[u16], opcode: u16) { 513 let bank = RamBank::from_opcode(opcode); 514 let address = opcode as u8; 515 let source = AddressingMode::Direct { bank, address }; 516 517 let dest = AddressingMode::ACCUMULATOR_REGISTER; 518 519 execute_load(svp, rom, source, dest); 520} 521 522fn ld_addr_a(svp: &mut Svp, rom: &[u16], opcode: u16) { 523 let bank = RamBank::from_opcode(opcode); 524 let address = opcode as u8; 525 let dest = AddressingMode::Direct { bank, address }; 526 527 let source = AddressingMode::ACCUMULATOR_REGISTER; 528 529 execute_load(svp, rom, source, dest); 530} 531 532fn ldi_d_imm(svp: &mut Svp, rom: &[u16], opcode: u16) { 533 let source = AddressingMode::Immediate; 534 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 535 execute_load(svp, rom, source, dest); 536} 537 538fn ldi_ri_imm(svp: &mut Svp, rom: &[u16], opcode: u16) { 539 let bank = RamBank::from_opcode(opcode); 540 let pointer = opcode & 0x03; 541 let modifier = (opcode >> 2) & 0x03; 542 let dest = AddressingMode::Indirect { bank, pointer, modifier }; 543 544 let source = AddressingMode::Immediate; 545 546 execute_load(svp, rom, source, dest); 547} 548 549fn ldi_ri_simm(svp: &mut Svp, rom: &[u16], opcode: u16) { 550 let source = AddressingMode::ShortImmediate(opcode as u8); 551 552 let bank = if opcode.bit(10) { RamBank::One } else { RamBank::Zero }; 553 let pointer = (opcode >> 8) & 0x03; 554 let dest = AddressingMode::PointerRegister(bank, pointer); 555 556 execute_load(svp, rom, source, dest); 557} 558 559fn ld_d_a_indirect(svp: &mut Svp, rom: &[u16], opcode: u16) { 560 let source = AddressingMode::AccumulatorIndirect; 561 let dest = AddressingMode::GeneralRegister((opcode >> 4) & 0xF); 562 execute_load(svp, rom, source, dest); 563} 564 565fn parse_alu_addressing_mode(opcode: u16) -> AddressingMode { 566 match opcode & 0x1F00 { 567 0x0000 => { 568 // OP A, s 569 AddressingMode::GeneralRegister(opcode & 0xF) 570 } 571 0x0200 | 0x0300 => { 572 // OP A, (ri) 573 let bank = RamBank::from_opcode(opcode); 574 let pointer = opcode & 0x03; 575 let modifier = (opcode >> 2) & 0x03; 576 AddressingMode::Indirect { bank, pointer, modifier } 577 } 578 0x0600 | 0x0700 => { 579 // OP A, addr 580 let bank = RamBank::from_opcode(opcode); 581 let address = opcode as u8; 582 AddressingMode::Direct { bank, address } 583 } 584 0x0800 => { 585 // OPi A, imm 586 AddressingMode::Immediate 587 } 588 0x0A00 | 0x0B00 => { 589 // OP A, ((ri)) 590 let bank = RamBank::from_opcode(opcode); 591 let pointer = opcode & 0x03; 592 let modifier = (opcode >> 2) & 0x03; 593 AddressingMode::DoubleIndirect { bank, pointer, modifier } 594 } 595 0x1200 | 0x1300 => { 596 // OP A, ri 597 let bank = RamBank::from_opcode(opcode); 598 let pointer = opcode & 0x03; 599 AddressingMode::PointerRegister(bank, pointer) 600 } 601 0x1800 => { 602 // OPi A, simm 603 let immediate_value = opcode as u8; 604 AddressingMode::ShortImmediate(immediate_value) 605 } 606 _ => panic!("Invalid SVP opcode: {opcode:04X}"), 607 } 608} 609 610fn read_addressing_mode(svp: &mut Svp, rom: &[u16], source: AddressingMode) -> u16 { 611 match source { 612 AddressingMode::GeneralRegister(register) => read_register(svp, rom, register), 613 AddressingMode::PointerRegister(bank, register) => match (bank, register) { 614 (RamBank::Zero, 0..=2) => svp.registers.ram0_pointers[register as usize].into(), 615 (RamBank::One, 0..=2) => svp.registers.ram1_pointers[register as usize].into(), 616 (RamBank::Zero | RamBank::One, 3) => 0, 617 _ => panic!("invalid pointer register: {register}"), 618 }, 619 AddressingMode::Indirect { bank, pointer, modifier } => { 620 // Read a value from internal RAM using a pointer register 621 let ram_addr = read_pointer(svp, bank, pointer, modifier); 622 match bank { 623 RamBank::Zero => svp.ram0[ram_addr as usize], 624 RamBank::One => svp.ram1[ram_addr as usize], 625 } 626 } 627 AddressingMode::DoubleIndirect { bank, pointer, modifier } => { 628 // Read a value from program memory, with the address determined by an indirect read 629 // The address stored in internal RAM is incremented after the read 630 let ram_addr = read_pointer(svp, bank, pointer, modifier); 631 let ram = match bank { 632 RamBank::Zero => &mut svp.ram0, 633 RamBank::One => &mut svp.ram1, 634 }; 635 636 let indirect_addr = ram[ram_addr as usize]; 637 ram[ram_addr as usize] = indirect_addr.wrapping_add(1); 638 639 log::trace!(" Indirect addr = {indirect_addr:04X}"); 640 641 svp.read_program_memory(indirect_addr, rom) 642 } 643 AddressingMode::Direct { bank, address } => match bank { 644 // Read a value from internal RAM using a direct address 645 RamBank::Zero => svp.ram0[address as usize], 646 RamBank::One => svp.ram1[address as usize], 647 }, 648 AddressingMode::Immediate => { 649 // 16-bit immediate value, specified in the next word in program memory 650 let value = fetch_operand(svp, rom); 651 log::trace!(" Immediate value: {value:04X}"); 652 value 653 } 654 AddressingMode::ShortImmediate(value) => { 655 // 8-bit immediate value embedded in the opcode, zero extended to 16 bits 656 value.into() 657 } 658 AddressingMode::AccumulatorIndirect => { 659 // Read program memory, using the high word of the accumulator as the address 660 let address = (svp.registers.accumulator >> 16) as u16; 661 svp.read_program_memory(address, rom) 662 } 663 } 664} 665 666fn write_addressing_mode(svp: &mut Svp, dest: AddressingMode, value: u16) { 667 match dest { 668 AddressingMode::GeneralRegister(register) => { 669 write_register(svp, register, value); 670 } 671 AddressingMode::PointerRegister(bank, pointer) => { 672 // Pointer registers 3/7 are not writable 673 if pointer < 3 { 674 match bank { 675 RamBank::Zero => { 676 svp.registers.ram0_pointers[pointer as usize] = value as u8; 677 } 678 RamBank::One => { 679 svp.registers.ram1_pointers[pointer as usize] = value as u8; 680 } 681 } 682 } 683 } 684 AddressingMode::Indirect { bank, pointer, modifier } => { 685 // Write a value to internal RAM using a pointer register 686 let ram_addr = read_pointer(svp, bank, pointer, modifier); 687 match bank { 688 RamBank::Zero => { 689 svp.ram0[ram_addr as usize] = value; 690 } 691 RamBank::One => { 692 svp.ram1[ram_addr as usize] = value; 693 } 694 } 695 } 696 AddressingMode::Direct { bank, address } => match bank { 697 // Write a value to internal RAM using a direct address 698 RamBank::Zero => { 699 svp.ram0[address as usize] = value; 700 } 701 RamBank::One => { 702 svp.ram1[address as usize] = value; 703 } 704 }, 705 AddressingMode::DoubleIndirect { .. } 706 | AddressingMode::Immediate 707 | AddressingMode::ShortImmediate(..) 708 | AddressingMode::AccumulatorIndirect => panic!("Invalid write addressing mode: {dest:?}"), 709 } 710} 711 712#[allow(clippy::match_same_arms)] 713fn read_register(svp: &mut Svp, rom: &[u16], register: u16) -> u16 { 714 match register { 715 0 => { 716 // Dummy/null register; always reads $FFFF 717 0xFFFF 718 } 719 1 => { 720 // X register 721 svp.registers.x 722 } 723 2 => { 724 // Y register 725 svp.registers.y 726 } 727 3 => { 728 // Accumulator, high word 729 (svp.registers.accumulator >> 16) as u16 730 } 731 4 => { 732 // Status register 733 svp.registers.status.into() 734 } 735 5 => { 736 // Stack register; reads pop 737 svp.registers.stack.pop() 738 } 739 6 => { 740 // PC 741 svp.registers.pc 742 } 743 7 => { 744 // P register 745 (svp.registers.product() >> 16) as u16 746 } 747 8 => { 748 // PM0 / XST status 749 if svp.registers.status.st_bits_set() { 750 // PM0 751 pm_read(svp, rom, 0) 752 } else { 753 // XST status 754 svp.registers.xst.ssp_read_status() 755 } 756 } 757 9 => { 758 // PM1 759 pm_read(svp, rom, 1) 760 } 761 10 => { 762 // PM2 763 pm_read(svp, rom, 2) 764 } 765 11 => { 766 // PM3 / XST register 767 if svp.registers.status.st_bits_set() { 768 // PM3 769 pm_read(svp, rom, 3) 770 } else { 771 // XST register 772 svp.registers.xst.value 773 } 774 } 775 12 => { 776 // PM4 777 pm_read(svp, rom, 4) 778 } 779 13 => { 780 // Unknown; unused by SVP code 781 0xFFFF 782 } 783 14 => { 784 // PMC register 785 log::trace!("PMC read"); 786 svp.registers.pmc.read() 787 } 788 15 => { 789 // Accumulator, low word 790 svp.registers.accumulator as u16 791 } 792 _ => panic!("Invalid SVP register number: {register}"), 793 } 794} 795 796#[allow(clippy::match_same_arms)] 797fn write_register(svp: &mut Svp, register: u16, value: u16) { 798 match register { 799 0 => { 800 // Dummy/null register; writes do nothing 801 } 802 1 => { 803 // X register 804 svp.registers.x = value; 805 } 806 2 => { 807 // Y register 808 svp.registers.y = value; 809 } 810 3 => { 811 // Accumulator, high word 812 svp.registers.accumulator = 813 (svp.registers.accumulator & 0x0000_FFFF) | (u32::from(value) << 16); 814 } 815 4 => { 816 // Status register 817 svp.registers.status.write(value); 818 } 819 5 => { 820 // Stack; writes push 821 svp.registers.stack.push(value); 822 } 823 6 => { 824 // PC 825 svp.registers.pc = value; 826 } 827 7 => { 828 // P register; writes do nothing because P always equals 2 * X * Y 829 } 830 8 => { 831 // PM0 / XST status 832 if svp.registers.status.st_bits_set() { 833 // PM0 834 pm_write(svp, 0, value); 835 } else { 836 // XST status; is this even writable? SVP code seems to write to it 837 svp.registers.xst.m68k_written = value.bit(1); 838 svp.registers.xst.ssp_written = value.bit(0); 839 } 840 } 841 9 => { 842 // PM1 843 pm_write(svp, 1, value); 844 } 845 10 => { 846 // PM2 847 pm_write(svp, 2, value); 848 } 849 11 => { 850 // PM3 / XST register 851 if svp.registers.status.st_bits_set() { 852 // PM3 853 pm_write(svp, 3, value); 854 } else { 855 // XST register 856 svp.registers.xst.ssp_write(value); 857 } 858 } 859 12 => { 860 // PM4 861 pm_write(svp, 4, value); 862 } 863 13 => { 864 // Unknown; unused by SVP 865 } 866 14 => { 867 // PMC 868 log::trace!("PMC write: {value:04X}"); 869 870 svp.registers.pmc.write(value); 871 } 872 15 => { 873 // Accumulator, low word 874 svp.registers.accumulator = 875 (svp.registers.accumulator & 0xFFFF_0000) | u32::from(value); 876 } 877 _ => panic!("Invalid SVP register number: {register}"), 878 } 879} 880 881fn pm_read(svp: &mut Svp, rom: &[u16], pm_idx: usize) -> u16 { 882 log::trace!("PM{pm_idx} read"); 883 884 let pm_register = &mut svp.registers.pm_read[pm_idx]; 885 let address = pm_register.get_and_increment_address(); 886 887 // Reading a PM register always updates the address/mode words stored in PMC 888 svp.registers.pmc.update_from(pm_register); 889 890 svp.read_external_memory(address, rom) 891} 892 893fn pm_write(svp: &mut Svp, pm_idx: usize, value: u16) { 894 log::trace!("PM{pm_idx} write {value:04X}"); 895 896 let pm_register = &mut svp.registers.pm_write[pm_idx]; 897 let address = pm_register.get_and_increment_address(); 898 let overwrite_mode = pm_register.overwrite_mode; 899 900 // Writing a PM register always updates the address/mode words stored in PMC 901 svp.registers.pmc.update_from(pm_register); 902 903 if overwrite_mode { 904 // This is a read-modify-write, so verify that the SSP isn't attempting to write to ROM 905 if !(0x000000..=0x0FFFFF).contains(&address) { 906 let existing_value = svp.read_external_memory(address, &[]); 907 908 // Overwrite mode splits words into 4 nibbles and only writes the non-0 nibbles 909 let new_value = [0x000F, 0x00F0, 0x0F00, 0xF000] 910 .into_iter() 911 .map(|mask| if value & mask != 0 { value & mask } else { existing_value & mask }) 912 .reduce(|a, b| a | b) 913 .unwrap(); 914 svp.write_external_memory(address, new_value); 915 } 916 } else { 917 svp.write_external_memory(address, value); 918 } 919} 920 921fn read_pointer(svp: &mut Svp, bank: RamBank, pointer: u16, modifier: u16) -> u8 { 922 if pointer < 3 { 923 // Pointer register 924 let registers = match bank { 925 RamBank::Zero => &mut svp.registers.ram0_pointers, 926 RamBank::One => &mut svp.registers.ram1_pointers, 927 }; 928 929 let ram_addr = registers[pointer as usize]; 930 increment_pointer_register( 931 &mut registers[pointer as usize], 932 modifier, 933 svp.registers.status.loop_modulo(), 934 ); 935 ram_addr 936 } else { 937 // "Fake" pointer register (p3/p7) used for access to internal RAM addresses 0-3 938 // Modifier bits are used as the address 939 modifier as u8 940 } 941} 942 943fn increment_pointer_register(register: &mut u8, modifier: u16, loop_modulo: u8) { 944 match modifier { 945 0 => { 946 // No auto-increment/decrement; do nothing 947 } 948 1 => { 949 // Auto-increment with no modulo 950 *register = (*register).wrapping_add(1); 951 } 952 2 => { 953 // Modulo decrement 954 *register = modulo_decrement(*register, loop_modulo); 955 } 956 3 => { 957 // Modulo increment 958 *register = modulo_increment(*register, loop_modulo); 959 } 960 _ => panic!("invalid pointer register modifier: {modifier}"), 961 } 962} 963 964fn modulo_increment(value: u8, modulo: u8) -> u8 { 965 let mask = modulo.wrapping_sub(1); 966 (value & !mask) | (value.wrapping_add(1) & mask) 967} 968 969fn modulo_decrement(value: u8, modulo: u8) -> u8 { 970 let mask = modulo.wrapping_sub(1); 971 (value & !mask) | (value.wrapping_sub(1) & mask) 972}