use std::collections::VecDeque;
use crate::dc_filter::DcFilter;
use crate::generators::{EnvelopeGenerator, NUM_CHANNELS, NoiseGenerator, ToneGenerator};
use crate::mixer::Mixer;
use crate::tables::REG_MASK;
use ym2149_common::{MASTER_GAIN, Ym2149Backend};
const DEFAULT_MASTER_CLOCK: u32 = 2_000_000;
const DEFAULT_SAMPLE_RATE: u32 = 44_100;
const NUM_REGISTERS: usize = 14;
const CPU_CYCLES_PER_PSG_CYCLE: u64 = 4;
#[derive(Clone, Debug)]
struct PendingWrite {
cpu_cycle: u64,
register: u8,
value: u8,
}
fn random_seed(seed: &mut u32) -> u16 {
*seed = seed.wrapping_mul(214013).wrapping_add(2531011);
((*seed >> 16) & 0x7fff) as u16
}
#[derive(Clone)]
pub struct Ym2149 {
internal_clock: u32,
sample_rate: u32,
cycle_accumulator: u32,
registers: [u8; NUM_REGISTERS],
selected_register: usize,
tone_generators: [ToneGenerator; NUM_CHANNELS],
noise_generator: NoiseGenerator,
envelope_generator: EnvelopeGenerator,
mixer: Mixer,
dc_filter: DcFilter,
last_sample: f32,
in_timer_irq: bool,
write_queue: VecDeque<PendingWrite>,
current_cpu_cycle: u64,
last_select_cycle: u64,
cpu_cycles_per_sample: u64,
sample_start_cycle: u64,
}
impl Ym2149 {
pub fn new() -> Self {
Self::with_clocks(DEFAULT_MASTER_CLOCK, DEFAULT_SAMPLE_RATE)
}
pub fn with_clocks(master_clock: u32, sample_rate: u32) -> Self {
let cpu_clock = master_clock * CPU_CYCLES_PER_PSG_CYCLE as u32;
let cpu_cycles_per_sample = cpu_clock as u64 / sample_rate as u64;
let mut chip = Self {
internal_clock: master_clock / 8,
sample_rate,
cycle_accumulator: 0,
registers: [0; NUM_REGISTERS],
selected_register: 0,
tone_generators: [
ToneGenerator::new(),
ToneGenerator::new(),
ToneGenerator::new(),
],
noise_generator: NoiseGenerator::new(),
envelope_generator: EnvelopeGenerator::new(),
mixer: Mixer::new(),
dc_filter: DcFilter::new(),
last_sample: 0.0,
in_timer_irq: false,
write_queue: VecDeque::new(),
current_cpu_cycle: 0,
last_select_cycle: 0,
cpu_cycles_per_sample,
sample_start_cycle: 0,
};
chip.reset();
chip
}
pub fn reset(&mut self) {
let mut seed = 1u32;
let random_edges = (random_seed(&mut seed) as u32 & ((1 << 10) | (1 << 5) | 1)) * 0x1f;
for (i, tone) in self.tone_generators.iter_mut().enumerate() {
tone.reset();
tone.set_edge_bits(random_edges & (0x1f << (i * 5)));
}
self.noise_generator.reset();
self.envelope_generator.reset();
self.mixer.reset();
self.dc_filter.reset();
self.registers = [0; NUM_REGISTERS];
self.apply_register(7, 0x3F);
self.selected_register = 0;
self.cycle_accumulator = 0;
self.in_timer_irq = false;
self.last_sample = 0.0;
self.write_queue.clear();
self.current_cpu_cycle = 0;
self.last_select_cycle = 0;
self.sample_start_cycle = 0;
}
#[inline]
pub fn set_cpu_cycle(&mut self, cycle: u64) {
self.current_cpu_cycle = cycle;
}
#[inline]
#[must_use]
pub fn cpu_cycle(&self) -> u64 {
self.current_cpu_cycle
}
pub fn write_port(&mut self, port: u8, value: u8) {
if (port & 2) != 0 {
self.write_queue.push_back(PendingWrite {
cpu_cycle: self.current_cpu_cycle,
register: self.selected_register as u8,
value,
});
} else {
self.selected_register = (value as usize) & 0x0F;
self.last_select_cycle = self.current_cpu_cycle;
}
}
pub fn write_port_immediate(&mut self, port: u8, value: u8) {
if (port & 2) != 0 {
self.apply_register(self.selected_register, value);
} else {
self.selected_register = (value as usize) & 0x0F;
}
}
fn process_pending_writes(&mut self, up_to_cycle: u64) {
while let Some(write) = self.write_queue.front() {
if write.cpu_cycle <= up_to_cycle {
let w = self.write_queue.pop_front().unwrap();
self.apply_register(w.register as usize, w.value);
} else {
break;
}
}
}
pub fn flush_pending_writes(&mut self) {
while let Some(write) = self.write_queue.pop_front() {
self.apply_register(write.register as usize, write.value);
}
}
#[inline]
#[must_use]
pub fn pending_write_count(&self) -> usize {
self.write_queue.len()
}
#[must_use]
pub fn read_port(&self, port: u8) -> u8 {
if (port & 2) == 0 && self.selected_register < NUM_REGISTERS {
self.registers[self.selected_register]
} else {
0xFF
}
}
pub fn write_register(&mut self, register: u8, value: u8) {
self.apply_register(register as usize, value);
}
#[must_use]
pub fn read_register(&self, register: u8) -> u8 {
let reg = register as usize;
if reg < NUM_REGISTERS {
self.registers[reg]
} else {
0
}
}
fn apply_register(&mut self, register: usize, value: u8) {
if register >= NUM_REGISTERS {
return;
}
let value = value & REG_MASK[register];
self.registers[register] = value;
match register {
0..=5 => {
let channel = register / 2;
let period = self.read_tone_period(channel);
self.tone_generators[channel].set_period(period);
if period <= 1 && self.in_timer_irq {
self.tone_generators[channel].mark_pending_reset();
}
}
6 => {
self.noise_generator.set_period(value as u32);
}
7 => {
self.mixer.config.set_from_register(value);
}
11 | 12 => {
let period = self.read_envelope_period();
self.envelope_generator.set_period(period);
}
13 => {
self.envelope_generator.set_shape(value);
}
_ => {}
}
}
#[inline]
fn read_tone_period(&self, channel: usize) -> u32 {
let base = channel * 2;
((self.registers[base + 1] as u32) << 8) | (self.registers[base] as u32)
}
#[inline]
fn read_envelope_period(&self) -> u32 {
((self.registers[12] as u32) << 8) | (self.registers[11] as u32)
}
fn tick_generators(&mut self) -> u32 {
let mut tone_edges = 0u32;
for (i, tone) in self.tone_generators.iter_mut().enumerate() {
tone_edges |= tone.tick(i as u32 * 5);
}
let noise_mask = self.noise_generator.tick();
self.envelope_generator.tick();
self.mixer.config.compute_gate_mask(tone_edges, noise_mask)
}
pub fn compute_next_sample(&mut self) -> i16 {
let sample_end_cycle = self.sample_start_cycle + self.cpu_cycles_per_sample;
self.process_pending_writes(sample_end_cycle);
self.sample_start_cycle = sample_end_cycle;
let mut accumulated_mask: u16 = 0;
loop {
accumulated_mask |= self.tick_generators() as u16;
self.cycle_accumulator += self.sample_rate;
if self.cycle_accumulator >= self.internal_clock {
break;
}
}
self.cycle_accumulator -= self.internal_clock;
let envelope_level = self.envelope_generator.level();
let volume_regs = [self.registers[8], self.registers[9], self.registers[10]];
let (gated_levels, ungated_levels) =
self.mixer
.compute_levels(volume_regs, envelope_level, accumulated_mask as u32);
let mut total_output = 0u32;
for channel in 0..NUM_CHANNELS {
let level_index = (gated_levels >> (channel * 5)) & 0x1F;
let ungated_level_index = (ungated_levels >> (channel * 5)) & 0x1F;
let half_amplitude = self.tone_generators[channel].is_half_amplitude();
total_output += self
.mixer
.compute_channel_output(channel, level_index, ungated_level_index, half_amplitude);
}
self.dc_filter.process(total_output as u16)
}
#[inline]
pub fn sync_sample_cycle(&mut self, cpu_cycle: u64) {
self.sample_start_cycle = cpu_cycle;
self.current_cpu_cycle = cpu_cycle;
}
pub fn set_timer_irq_state(&mut self, in_irq: bool) {
if !in_irq {
for (i, tone) in self.tone_generators.iter_mut().enumerate() {
tone.apply_pending_reset(i as u32 * 5);
}
}
self.in_timer_irq = in_irq;
}
#[inline]
pub fn inside_timer_irq(&mut self, inside: bool) {
self.set_timer_irq_state(inside);
}
}
impl Default for Ym2149 {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Ym2149 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ym2149")
.field("registers", &self.registers)
.field("sample_rate", &self.sample_rate)
.field("internal_clock", &self.internal_clock)
.finish_non_exhaustive()
}
}
impl Ym2149Backend for Ym2149 {
fn new() -> Self {
Ym2149::new()
}
fn with_clocks(master_clock: u32, sample_rate: u32) -> Self {
Ym2149::with_clocks(master_clock, sample_rate)
}
fn reset(&mut self) {
Ym2149::reset(self)
}
fn write_register(&mut self, addr: u8, value: u8) {
Ym2149::write_register(self, addr, value)
}
fn read_register(&self, addr: u8) -> u8 {
Ym2149::read_register(self, addr)
}
fn load_registers(&mut self, regs: &[u8; 16]) {
for (i, &value) in regs.iter().take(NUM_REGISTERS).enumerate() {
self.write_register(i as u8, value);
}
}
fn dump_registers(&self) -> [u8; 16] {
let mut out = [0u8; 16];
out[..NUM_REGISTERS].copy_from_slice(&self.registers);
out
}
fn clock(&mut self) {
let sample_i16 = self.compute_next_sample();
self.last_sample = (sample_i16 as f32 / 32767.0 * MASTER_GAIN).clamp(-1.0, 1.0);
}
fn get_sample(&self) -> f32 {
self.last_sample
}
fn get_channel_outputs(&self) -> (f32, f32, f32) {
self.mixer.channel_outputs()
}
fn set_channel_mute(&mut self, channel: usize, mute: bool) {
self.mixer.set_mute(channel, mute);
}
fn is_channel_muted(&self, channel: usize) -> bool {
self.mixer.is_muted(channel)
}
fn set_color_filter(&mut self, _enabled: bool) {
}
fn trigger_envelope(&mut self) {
self.envelope_generator.trigger();
}
fn set_drum_sample_override(&mut self, channel: usize, sample: Option<f32>) {
self.mixer.set_drum_override(channel, sample);
}
fn set_mixer_overrides(&mut self, _force_tone: [bool; 3], _force_noise_mute: [bool; 3]) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_chip_has_default_state() {
let chip = Ym2149::new();
assert_eq!(chip.sample_rate, DEFAULT_SAMPLE_RATE);
assert_eq!(chip.internal_clock, DEFAULT_MASTER_CLOCK / 8);
}
#[test]
fn test_register_read_write() {
let mut chip = Ym2149::new();
chip.write_register(0, 0x55);
assert_eq!(chip.read_register(0), 0x55);
chip.write_register(1, 0xFF);
assert_eq!(chip.read_register(1), 0x0F); }
#[test]
fn test_reset_clears_state() {
let mut chip = Ym2149::new();
chip.write_register(0, 0x55);
chip.write_register(8, 0x0F);
chip.reset();
assert_eq!(chip.read_register(0), 0);
assert_eq!(chip.read_register(8), 0);
assert_eq!(chip.read_register(7), 0x3F);
}
#[test]
fn test_sample_generation() {
let mut chip = Ym2149::new();
chip.write_register(0, 0x00);
chip.write_register(1, 0x01);
chip.write_register(8, 0x0F);
chip.write_register(7, 0x3E);
for _ in 0..100 {
chip.clock();
}
let sample = chip.get_sample();
assert!(sample.abs() > 0.0 || chip.last_sample.abs() >= 0.0);
}
#[test]
fn test_channel_mute() {
let mut chip = Ym2149::new();
assert!(!chip.is_channel_muted(0));
chip.set_channel_mute(0, true);
assert!(chip.is_channel_muted(0));
assert!(!chip.is_channel_muted(1));
}
#[test]
fn test_port_access() {
let mut chip = Ym2149::new();
chip.write_port(0, 5);
chip.write_port(2, 0x0A);
chip.flush_pending_writes();
assert_eq!(chip.read_register(5), 0x0A);
}
#[test]
fn test_port_access_immediate() {
let mut chip = Ym2149::new();
chip.write_port_immediate(0, 5);
chip.write_port_immediate(2, 0x0A);
assert_eq!(chip.read_register(5), 0x0A);
}
#[test]
fn test_write_queue_timing() {
let mut chip = Ym2149::new();
chip.set_cpu_cycle(100);
chip.write_port(0, 8); chip.write_port(2, 0x0F);
assert_eq!(chip.pending_write_count(), 1);
assert_eq!(chip.read_register(8), 0x00);
chip.sync_sample_cycle(0);
chip.compute_next_sample();
assert_eq!(chip.pending_write_count(), 0);
assert_eq!(chip.read_register(8), 0x0F);
}
}