Skip to content

Commit

Permalink
sapemu: implement DSP register access
Browse files Browse the repository at this point in the history
this leads to a ton of new broken tests...
  • Loading branch information
kleinesfilmroellchen committed Oct 14, 2024
1 parent c53b406 commit bede66d
Show file tree
Hide file tree
Showing 7 changed files with 2,202 additions and 699 deletions.
Empty file added mismatches
Empty file.
17 changes: 17 additions & 0 deletions sapemu/src/dsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,24 @@ pub mod registers;
mod tables;

/// State of the S-DSP.
#[derive(Clone, Default)]
pub struct Dsp {
/// Public DSP registers.
pub registers: DspRegisters,
}

impl Dsp {
/// Create a new DSP instance.
#[must_use]
pub fn new() -> Self {
Self { registers: DspRegisters::default() }
}

/// Load the register state from a bank of registers (e.g. a memory dump).
#[allow(clippy::cast_possible_truncation)]
pub fn load_register_bank(&mut self, register_bank: &[u8; 128]) {
for (address, value) in register_bank.iter().enumerate() {
self.registers.write(address as u8, *value);
}
}
}
5 changes: 3 additions & 2 deletions sapemu/src/dsp/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ impl Default for DspRegisters {

impl DspRegisters {
/// Read from a DSP register.
pub fn read(&mut self, address: u8) -> u8 {
#[must_use]
pub fn read(&self, address: u8) -> u8 {
let lower_nibble = address & 0xf;
let upper_nibble = (address >> 4) & 0x7;
match (upper_nibble, lower_nibble) {
Expand Down Expand Up @@ -133,7 +134,7 @@ impl DspRegisters {
(0x4, 0xC) => self.key_on.0 = value,
(0x5, 0xC) => self.key_off.0 = value,
(0x6, 0xC) => self.flags.0 = value,
(0x7, 0xC) => self.voice_end.0 = value,
(0x7, 0xC) => self.voice_end = PerVoiceFlag::default(),
(0x0, 0xD) => self.echo_feedback_volume = value,
(0x2, 0xD) => self.pitch_mod_enable.0 = value,
(0x3, 0xD) => self.noise_enable.0 = value,
Expand Down
20 changes: 13 additions & 7 deletions sapemu/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::time::Instant;
use ::log::{debug, info, warn, LevelFilter};
use anyhow::Result;
use clap::{Parser, ValueEnum};
use dsp::Dsp;
use smp::peripherals::{ProgramStatusWord, TestRegister};
use spcfile::parser::parse_from_bytes;
use time::macros::format_description;
Expand Down Expand Up @@ -54,28 +55,30 @@ fn try_all_formats(
file_data: &[u8],
smp: &mut Smp,
memory: &mut Memory,
dsp: &mut Dsp,
arguments: &CliArguments,
ticks: &mut usize,
) -> Result<()> {
if object::read::elf::ElfFile32::<object::LittleEndian>::parse(file_data).is_ok() {
upload_from_elf(file_data, smp, memory, arguments, ticks)
upload_from_elf(file_data, smp, memory, dsp, arguments, ticks)
} else {
upload_from_spc(file_data, smp, memory, arguments, ticks)
upload_from_spc(file_data, smp, memory, dsp, arguments, ticks)
}
}

fn upload_from_elf(
file_data: &[u8],
smp: &mut Smp,
memory: &mut Memory,
dsp: &mut Dsp,
arguments: &CliArguments,
ticks: &mut usize,
) -> Result<()> {
let mut uploader = Uploader::from_elf(&object::read::elf::ElfFile32::parse(file_data)?)?;

while !smp.is_halted() && arguments.cycles.map_or(true, |cycles| *ticks < cycles) {
uploader.perform_step(&mut smp.ports);
smp.tick(memory);
smp.tick(memory, &mut dsp.registers);
*ticks += 1;
if uploader.is_finished() {
break;
Expand All @@ -88,6 +91,7 @@ fn upload_from_spc(
file_data: &[u8],
smp: &mut Smp,
memory: &mut Memory,
dsp: &mut Dsp,
_arguments: &CliArguments,
_ticks: &mut usize,
) -> Result<()> {
Expand All @@ -101,6 +105,7 @@ fn upload_from_spc(
smp.psw = ProgramStatusWord(file.header.psw);

memory.ram = *file.memory.ram;
dsp.load_register_bank(&file.memory.dsp_registers);

smp.copy_mapped_registers_from_memory(memory);
// SHENANIGANS! Many ROMs crash the SPC700 to be able to extract the ROM state easily.
Expand Down Expand Up @@ -133,24 +138,25 @@ fn main() -> Result<()> {

let mut memory = Box::new(Memory::new());
let mut smp = Smp::new(&mut memory);
let mut dsp = Dsp::new();

let file_data = fs::read(&arguments.input)?;

let start_time = Instant::now();
let mut ticks = 0;

match arguments.format {
Some(InputFormat::Elf) => upload_from_elf(&file_data, &mut smp, &mut memory, &arguments, &mut ticks),
Some(InputFormat::Spc) => upload_from_spc(&file_data, &mut smp, &mut memory, &arguments, &mut ticks),
None => try_all_formats(&file_data, &mut smp, &mut memory, &arguments, &mut ticks),
Some(InputFormat::Elf) => upload_from_elf(&file_data, &mut smp, &mut memory, &mut dsp, &arguments, &mut ticks),
Some(InputFormat::Spc) => upload_from_spc(&file_data, &mut smp, &mut memory, &mut dsp, &arguments, &mut ticks),
None => try_all_formats(&file_data, &mut smp, &mut memory, &mut dsp, &arguments, &mut ticks),
}?;

debug!("Memory state after upload:\n{}", spcasm::pretty_hex(&memory.ram, None));

trace!("{}", smp.is_halted());

while !smp.is_halted() && arguments.cycles.map_or(true, |cycles| ticks < cycles) {
smp.tick(&mut memory);
smp.tick(&mut memory, &mut dsp.registers);
ticks += 1;
}

Expand Down
28 changes: 20 additions & 8 deletions sapemu/src/smp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use spcasm::sema::Register;

use self::ops::{InstructionImpl, InstructionInternalState, OPCODE_TABLE};
use self::peripherals::{ControlRegister, CpuIOPorts, ProgramStatusWord, RunState, TestRegister, Timers};
use crate::dsp::registers::DspRegisters;
use crate::memory::Memory;
use crate::trace;

Expand Down Expand Up @@ -92,6 +93,10 @@ pub const CPUIO1: u16 = 0x00F5;
pub const CPUIO2: u16 = 0x00F6;
/// CPUIO3 register.
pub const CPUIO3: u16 = 0x00F7;
/// DSP register read/write port.
pub const DSPADDR: u16 = 0x00F2;
/// DSP register address port.
pub const DSPDATA: u16 = 0x00F3;

/// Vector for software interrupts.
pub const BREAK_VECTOR: u16 = 0xFFDE;
Expand All @@ -116,7 +121,7 @@ impl Smp {
}

/// Run a single CPU cycle.
pub fn tick(&mut self, memory: &mut Memory) {
pub fn tick(&mut self, memory: &mut Memory, dsp: &mut DspRegisters) {
self.cycle_counter += 1;
if self.run_state != RunState::Running {
return;
Expand All @@ -127,7 +132,7 @@ impl Smp {

// Fetch next instruction
if self.instruction_cycle == 0 {
self.current_opcode = self.read_next_pc(memory);
self.current_opcode = self.read_next_pc(memory, dsp);
self.instruction_function = OPCODE_TABLE[self.current_opcode as usize];
trace!(
"(@{}) fetch instruction [{:04x}] = {:02x}",
Expand All @@ -139,7 +144,7 @@ impl Smp {

// Execute tick
let instruction_result =
(self.instruction_function)(self, memory, self.instruction_cycle, self.last_instruction_state);
(self.instruction_function)(self, memory, dsp, self.instruction_cycle, self.last_instruction_state);
// Decide whether to advance to next instruction or not
match instruction_result {
ops::MicroArchAction::Continue(new_state) => {
Expand Down Expand Up @@ -244,30 +249,37 @@ impl Smp {
self.psw.set(ProgramStatusWord::Break, break_);
}

#[allow(unused)]
#[track_caller]
fn write(&mut self, address: u16, value: u8, memory: &mut Memory) {
fn write(&mut self, address: u16, value: u8, memory: &mut Memory, dsp: &mut DspRegisters) {
match address {
TEST => self.test_write(value),
CONTROL => self.control_write(value),
CPUIO0 | CPUIO1 | CPUIO2 | CPUIO3 => self.ports.write(address - CPUIO0, value),
DSPDATA => {
let dsp_address = memory.read(DSPADDR, self.control.contains(ControlRegister::BootRomEnable));
dsp.write(dsp_address, value);
},
_ => self.memory_write(address, value, memory),
}
}

#[track_caller]
fn read(&mut self, address: u16, memory: &Memory) -> u8 {
fn read(&mut self, address: u16, memory: &Memory, dsp: &DspRegisters) -> u8 {
match address {
TEST => self.test.0,
CONTROL => self.control.0,
CPUIO0 | CPUIO1 | CPUIO2 | CPUIO3 => self.ports.read(address - CPUIO0),
DSPDATA => {
let dsp_address = memory.read(DSPADDR, self.control.contains(ControlRegister::BootRomEnable));
dsp.read(dsp_address)
},
_ => memory.read(address, self.control.contains(ControlRegister::BootRomEnable)),
}
}

/// Reads memory at the current program counter and advances it afterwards.
fn read_next_pc(&mut self, memory: &Memory) -> u8 {
let data = self.read(self.pc, memory);
fn read_next_pc(&mut self, memory: &Memory, dsp: &DspRegisters) -> u8 {
let data = self.read(self.pc, memory, dsp);
self.pc = self.pc.wrapping_add(1);
data
}
Expand Down
Loading

0 comments on commit bede66d

Please sign in to comment.