1//! Ricoh RF5C164 PCM sound chip
2
3use bincode::{Decode, Encode};
4use genesis_config::{PcmInterpolation, SegaCdEmulatorConfig};
5use jgenesis_common::boxedarray::BoxedByteArray;
6use jgenesis_common::debug::{DebugBytesView, DebugMemoryView};
7use jgenesis_common::num::{GetBit, U16Ext};
8use std::array;
9
10// Divider of sub CPU cycles
11const RF5C164_DIVIDER: u64 = 384;
12
13const ADDRESS_FRACT_BITS: u32 = 11;
14const ADDRESS_FRACT_MASK: u32 = (1 << ADDRESS_FRACT_BITS) - 1;
15
16const WAVEFORM_RAM_LEN: usize = 64 * 1024;
17const WAVEFORM_ADDRESS_MASK: u32 = WAVEFORM_RAM_LEN as u32 - 1;
18
19const ADDRESS_FIXED_POINT_MASK: u32 = (1 << (16 + ADDRESS_FRACT_BITS)) - 1;
20
21type WaveformRam = [u8; WAVEFORM_RAM_LEN];
22
23#[derive(Debug, Clone, Default, Encode, Decode)]
24struct InterpolationBuffer {
25    buffer: [i8; 6],
26}
27
28impl InterpolationBuffer {
29    fn clear(&mut self) {
30        self.buffer.fill(0);
31    }
32
33    fn push(&mut self, sample: i8) {
34        for i in 0..5 {
35            self.buffer[i] = self.buffer[i + 1];
36        }
37        self.buffer[5] = sample;
38    }
39
40    fn sample(&self, interpolation: PcmInterpolation, current_address: u32) -> f64 {
41        match interpolation {
42            PcmInterpolation::None => self.buffer[5].into(),
43            PcmInterpolation::Linear => {
44                interpolate_linear(self.buffer[4], self.buffer[5], interpolation_x(current_address))
45            }
46            PcmInterpolation::CubicHermite => interpolate_cubic_4p(
47                self.buffer[2..6].try_into().unwrap(),
48                interpolation_x(current_address),
49            ),
50            PcmInterpolation::CubicHermite6Point => {
51                interpolate_cubic_6p(self.buffer, interpolation_x(current_address))
52            }
53        }
54    }
55}
56
57fn interpolation_x(address: u32) -> f64 {
58    f64::from(address & ADDRESS_FRACT_MASK) / f64::from(1 << ADDRESS_FRACT_BITS)
59}
60
61fn interpolate_linear(y0: i8, y1: i8, x: f64) -> f64 {
62    let y0: f64 = y0.into();
63    let y1: f64 = y1.into();
64
65    y0 * (1.0 - x) + y1 * x
66}
67
68// Clamp to [-127, 126] because samples are sign+magnitude, not signed 8-bit
69// +127 is not a valid sample value because 0xFF is the loop end marker
70const MIN_SAMPLE: f64 = -127.0;
71const MAX_SAMPLE: f64 = 126.0;
72
73fn interpolate_cubic_4p(samples: [i8; 4], x: f64) -> f64 {
74    let result = jgenesis_common::audio::interpolate_cubic_hermite_4p(samples.map(f64::from), x);
75    result.clamp(MIN_SAMPLE, MAX_SAMPLE)
76}
77
78fn interpolate_cubic_6p(samples: [i8; 6], x: f64) -> f64 {
79    let result = jgenesis_common::audio::interpolate_cubic_hermite_6p(samples.map(f64::from), x);
80    result.clamp(MIN_SAMPLE, MAX_SAMPLE)
81}
82
83#[derive(Debug, Clone, Default, Encode, Decode)]
84struct Channel {
85    enabled: bool,
86    start_address: u16,
87    loop_address: u16,
88    master_volume: u8,
89    l_volume: u8,
90    r_volume: u8,
91    // Fixed point 16.11
92    current_address: u32,
93    // Fixed point 5.11
94    address_increment: u16,
95    interpolation_buffer: InterpolationBuffer,
96}
97
98impl Channel {
99    fn enable(&mut self, waveform_ram: &WaveformRam) {
100        if !self.enabled {
101            self.current_address = u32::from(self.start_address) << ADDRESS_FRACT_BITS;
102            self.interpolation_buffer.clear();
103            self.enabled = true;
104
105            // Immediately read the first sample when a channel is enabled; otherwise it will get skipped
106            let first_sample = waveform_ram[self.start_address as usize];
107            if first_sample != 0xFF {
108                self.interpolation_buffer.push(sign_magnitude_to_pcm(first_sample));
109            }
110        }
111    }
112
113    fn disable(&mut self) {
114        self.enabled = false;
115    }
116
117    fn clock(&mut self, waveform_ram: &WaveformRam) {
118        if !self.enabled {
119            return;
120        }
121
122        let address_increment: u32 = self.address_increment.into();
123        let incremented_address = self.current_address + address_increment;
124
125        let mut address = self.current_address >> ADDRESS_FRACT_BITS;
126        let steps = (incremented_address >> ADDRESS_FRACT_BITS) - address;
127        if steps == 0 {
128            // Only the fractional bits changed; no new samples read
129            self.current_address = incremented_address & ADDRESS_FIXED_POINT_MASK;
130            return;
131        }
132
133        // All steps but last
134        for _ in 0..steps - 1 {
135            address = (address + 1) & WAVEFORM_ADDRESS_MASK;
136            let sample = waveform_ram[address as usize];
137            if sample == 0xFF {
138                // Loop signal
139                // Actual hardware would skip over this, so just ignore it.
140                // This shouldn't really happen in practice unless a game puts multiple loop markers
141                // at the end of a sample while playing at >32552 Hz to guarantee that the chip
142                // doesn't miss the loop.
143                continue;
144            }
145
146            self.interpolation_buffer.push(sign_magnitude_to_pcm(sample));
147        }
148
149        // Last step
150        address = (address + 1) & WAVEFORM_ADDRESS_MASK;
151        let sample = waveform_ram[address as usize];
152        if sample == 0xFF {
153            // Loop signal; jump to start of loop and immediately read the next sample
154            address = self.loop_address.into();
155            let loop_start_sample = waveform_ram[self.loop_address as usize];
156            if loop_start_sample == 0xFF {
157                // Infinite loop
158                // TODO what does actual hardware do when there's an infinite loop?
159                self.interpolation_buffer.push(0);
160            } else {
161                self.interpolation_buffer.push(sign_magnitude_to_pcm(loop_start_sample));
162            }
163        } else {
164            self.interpolation_buffer.push(sign_magnitude_to_pcm(sample));
165        }
166
167        let new_address_int = address & WAVEFORM_ADDRESS_MASK;
168        let new_address_fract = incremented_address & ADDRESS_FRACT_MASK;
169        self.current_address = (new_address_int << ADDRESS_FRACT_BITS) | new_address_fract;
170    }
171
172    fn sample(&self, interpolation: PcmInterpolation) -> (i32, i32) {
173        if !self.enabled {
174            return (0, 0);
175        }
176
177        let sample = self.interpolation_buffer.sample(interpolation, self.current_address);
178        let sign = sample.signum() as i32;
179        let magnitude = sample.abs();
180
181        // Apply volume
182        let amplified = magnitude * f64::from(self.master_volume);
183        let panned_l = amplified * f64::from(self.l_volume);
184        let panned_r = amplified * f64::from(self.r_volume);
185
186        // Drop the lowest 5 bits and apply sign
187        // Per the RF5C164 datasheet, the truncation is done purely on the magnitude, before taking
188        // sign into account
189        let output_l = sign * ((panned_l.round() as i32) >> 5);
190        let output_r = sign * ((panned_r.round() as i32) >> 5);
191
192        (output_l, output_r)
193    }
194}
195
196fn sign_magnitude_to_pcm(sample: u8) -> i8 {
197    // RF5C164 samples have a sign bit and a 7-bit magnitude
198    // Sign bit 1 = Positive, 0 = Negative
199    let magnitude = (sample & 0x7F) as i8;
200    if sample.bit(7) { magnitude } else { -magnitude }
201}
202
203#[derive(Debug, Clone, Encode, Decode)]
204pub struct Rf5c164 {
205    enabled: bool,
206    channels: [Channel; 8],
207    waveform_ram: BoxedByteArray<WAVEFORM_RAM_LEN>,
208    waveform_ram_bank: u8,
209    selected_channel: u8,
210    divider: u64,
211    interpolation: PcmInterpolation,
212}
213
214impl Rf5c164 {
215    pub fn new(config: &SegaCdEmulatorConfig) -> Self {
216        Self {
217            enabled: false,
218            channels: array::from_fn(|_| Channel::default()),
219            waveform_ram: BoxedByteArray::new(),
220            waveform_ram_bank: 0,
221            selected_channel: 0,
222            divider: RF5C164_DIVIDER,
223            interpolation: config.pcm_interpolation,
224        }
225    }
226
227    pub fn read(&self, address: u32) -> u8 {
228        match address {
229            0x0000..=0x0007 | 0x0009..=0x000F | 0x0020..=0x0FFF => {
230                // Unused for reads
231                0x00
232            }
233            0x0008 => self.read_channel_on_register(),
234            0x0010..=0x001F => self.read_channel_address(address),
235            0x1000..=0x1FFF => {
236                // Reading waveform RAM is only allowed while the chip is not running
237                if !self.enabled {
238                    let waveform_ram_addr =
239                        (u32::from(self.waveform_ram_bank) << 12) | (address & 0x0FFF);
240                    self.waveform_ram[waveform_ram_addr as usize]
241                } else {
242                    0x00
243                }
244            }
245            _ => panic!("invalid RF5C164 address: {address:06X}"),
246        }
247    }
248
249    pub fn write(&mut self, address: u32, value: u8) {
250        match address {
251            0x0000..=0x0008 => {
252                self.write_register(address, value);
253            }
254            0x0009..=0x0FFF => {
255                // Unused
256            }
257            0x1000..=0x1FFF => {
258                let waveform_ram_addr =
259                    (u32::from(self.waveform_ram_bank) << 12) | (address & 0x0FFF);
260                self.waveform_ram[waveform_ram_addr as usize] = value;
261            }
262            _ => panic!("invalid RF5C164 address: {address:06X}"),
263        }
264    }
265
266    pub fn dma_write(&mut self, address: u32, value: u8) {
267        let waveform_ram_addr = (u32::from(self.waveform_ram_bank) << 12) | address;
268        self.waveform_ram[waveform_ram_addr as usize] = value;
269    }
270
271    pub fn disable(&mut self) {
272        self.enabled = false;
273    }
274
275    fn read_channel_on_register(&self) -> u8 {
276        log::trace!("Channel on/off register read");
277
278        self.channels
279            .iter()
280            .enumerate()
281            .map(|(i, channel)| u8::from(channel.enabled) << i)
282            .reduce(|a, b| a | b)
283            .unwrap()
284    }
285
286    fn read_channel_address(&self, address: u32) -> u8 {
287        let channel_idx = (address & 0xF) >> 1;
288        let channel = &self.channels[channel_idx as usize];
289        let channel_address = if channel.enabled {
290            (channel.current_address >> ADDRESS_FRACT_BITS) as u16
291        } else {
292            channel.start_address
293        };
294
295        log::trace!("Channel {channel_idx} address read; current address = {channel_address:04X}");
296
297        if address.bit(0) {
298            // High byte
299            channel_address.msb()
300        } else {
301            // Low byte
302            channel_address.lsb()
303        }
304    }
305
306    fn write_register(&mut self, address: u32, value: u8) {
307        log::trace!(
308            "PCM register: Wrote {value:02X} to {address:04X}, current channel is {}",
309            self.selected_channel
310        );
311
312        match address {
313            0x0000 => {
314                // Envelope
315                self.channels[self.selected_channel as usize].master_volume = value;
316
317                log::trace!("  Master volume = {value:02X}");
318            }
319            0x0001 => {
320                // Pan
321                let channel = &mut self.channels[self.selected_channel as usize];
322                channel.l_volume = value & 0x0F;
323                channel.r_volume = value >> 4;
324
325                log::trace!(
326                    "  L volume = {:02X}, R volume = {:02X}",
327                    channel.l_volume,
328                    channel.r_volume
329                );
330            }
331            0x0002 => {
332                // Address increment, low byte
333                let channel = &mut self.channels[self.selected_channel as usize];
334                channel.address_increment.set_lsb(value);
335
336                log::trace!("  Address increment = {:04X}", channel.address_increment);
337            }
338            0x0003 => {
339                // Address increment, high byte
340                let channel = &mut self.channels[self.selected_channel as usize];
341                channel.address_increment.set_msb(value);
342
343                log::trace!("  Address increment = {:04X}", channel.address_increment);
344            }
345            0x0004 => {
346                // Loop address, low byte
347                let channel = &mut self.channels[self.selected_channel as usize];
348                channel.loop_address.set_lsb(value);
349
350                log::trace!("  Loop address = {:04X}", channel.loop_address);
351            }
352            0x0005 => {
353                // Loop address, high byte
354                let channel = &mut self.channels[self.selected_channel as usize];
355                channel.loop_address.set_msb(value);
356
357                log::trace!("  Loop address = {:04X}", channel.loop_address);
358            }
359            0x0006 => {
360                // Start address (low byte is always $00)
361                self.channels[self.selected_channel as usize].start_address =
362                    u16::from_be_bytes([value, 0x00]);
363
364                log::trace!("  Start address = {value:02X}00");
365            }
366            0x0007 => {
367                // Control register
368                self.enabled = value.bit(7);
369
370                log::trace!("Chip enabled = {}", self.enabled);
371
372                // Bits 3-0 have different effects depending on the value of bit 6
373                if value.bit(6) {
374                    // Change selected channel (3 bits)
375                    self.selected_channel = value & 0x07;
376
377                    log::trace!("  Selected channel = {}", self.selected_channel);
378                } else {
379                    // Change waveform RAM bank (4 bits)
380                    self.waveform_ram_bank = value & 0x0F;
381
382                    log::trace!("  PCM waveform RAM bank = {:X}", self.waveform_ram_bank);
383                }
384            }
385            0x0008 => {
386                // Channel on/off register
387                // 1 = Disabled, 0 = Enabled
388                for (i, channel) in self.channels.iter_mut().enumerate() {
389                    if value.bit(i as u8) {
390                        channel.disable();
391                    } else {
392                        channel.enable(&self.waveform_ram);
393                    }
394                }
395            }
396            _ => panic!("invalid RF5C164 register address: {address:06X}"),
397        }
398    }
399
400    pub fn tick(&mut self, mut sub_cpu_cycles: u64, mut audio_callback: impl FnMut((f64, f64))) {
401        while sub_cpu_cycles >= self.divider {
402            sub_cpu_cycles -= self.divider;
403            self.divider = RF5C164_DIVIDER;
404
405            if self.enabled {
406                self.clock();
407            }
408
409            audio_callback(self.sample());
410        }
411        self.divider -= sub_cpu_cycles;
412    }
413
414    fn clock(&mut self) {
415        for channel in &mut self.channels {
416            channel.clock(&self.waveform_ram);
417        }
418    }
419
420    pub fn sample(&self) -> (f64, f64) {
421        if !self.enabled {
422            return (0.0, 0.0);
423        }
424
425        let (sample_l, sample_r) = self
426            .channels
427            .iter()
428            .map(|channel| channel.sample(self.interpolation))
429            .fold((0, 0), |(sum_l, sum_r), (sample_l, sample_r)| {
430                (sum_l + sample_l, sum_r + sample_r)
431            });
432
433        // Individual channel samples are effectively signed 15-bit after applying volume (and
434        // dropping the lowest 5 bits)
435        // Mixed output is clamped to signed 16-bit
436        let sample_l = sample_l.clamp(i16::MIN.into(), i16::MAX.into());
437        let sample_r = sample_r.clamp(i16::MIN.into(), i16::MAX.into());
438
439        let sample_l = f64::from(sample_l) / -f64::from(i16::MIN);
440        let sample_r = f64::from(sample_r) / -f64::from(i16::MIN);
441        (sample_l, sample_r)
442    }
443
444    pub fn reload_config(&mut self, config: &SegaCdEmulatorConfig) {
445        self.interpolation = config.pcm_interpolation;
446    }
447
448    pub fn debug_ram_view(&mut self) -> impl DebugMemoryView {
449        DebugBytesView(self.waveform_ram.as_mut_slice())
450    }
451}