jevsnes.git / third-party / rust / jgenesis / backend / gb-core / src / apu / wavetable.rs
1use crate::HardwareMode;
2use crate::apu::components::{TimerTickEffect, WavetableLengthCounter, WavetableTimer};
3use bincode::{Decode, Encode};
4use jgenesis_common::num::GetBit;
5
6#[derive(Debug, Clone, Encode, Decode)]
7pub struct WavetableChannel {
8    ram: [u8; 16],
9    sample_buffer: u8,
10    timer: WavetableTimer,
11    length_counter: WavetableLengthCounter,
12    volume: u8,
13    channel_enabled: bool,
14    dac_enabled: bool,
15}
16
17// From https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Power_Control
18const DMG_INITIAL_RAM: [u8; 16] = [
19    0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA,
20];
21
22const CGB_INITIAL_RAM: [u8; 16] = [
23    0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
24];
25
26impl WavetableChannel {
27    pub fn new(hardware_mode: HardwareMode) -> Self {
28        Self {
29            ram: match hardware_mode {
30                HardwareMode::Dmg => DMG_INITIAL_RAM,
31                HardwareMode::Cgb => CGB_INITIAL_RAM,
32            },
33            sample_buffer: 0,
34            timer: WavetableTimer::new(),
35            length_counter: WavetableLengthCounter::new(),
36            volume: 0,
37            channel_enabled: false,
38            dac_enabled: false,
39        }
40    }
41
42    pub fn read_ram(&self, address: u16) -> u8 {
43        // Reading from wavetable RAM while the channel is playing returns the contents of RAM at
44        // the current wave position rather than the requested address. Demotronic depends on this
45        // for its emulator check
46        // TODO on DMG this should only happen if the read occurs on specific cycles, otherwise it
47        // should return 0xFF
48        // TODO more accurate timing; this doesn't pass cgb_sound
49        if self.channel_enabled {
50            return self.ram[(self.timer.phase >> 1) as usize];
51        }
52
53        self.ram[(address & 0xF) as usize]
54    }
55
56    pub fn write_ram(&mut self, address: u16, value: u8) {
57        self.ram[(address & 0xF) as usize] = value;
58    }
59
60    pub fn read_register_0(&self) -> u8 {
61        0x7F | (u8::from(self.dac_enabled) << 7)
62    }
63
64    pub fn write_register_0(&mut self, value: u8) {
65        // NR30: Custom wave DAC enabled
66        self.dac_enabled = value.bit(7);
67
68        if !self.dac_enabled {
69            self.channel_enabled = false;
70        }
71
72        log::trace!("NR30 write, DAC enabled: {}", self.dac_enabled);
73    }
74
75    pub fn write_register_1(&mut self, value: u8) {
76        // NR31: Custom wave length counter reload
77        self.length_counter.load(value);
78
79        log::trace!("NR31 write, length counter: {}", self.length_counter.counter);
80    }
81
82    pub fn read_register_2(&self) -> u8 {
83        0x9F | (self.volume << 5)
84    }
85
86    pub fn write_register_2(&mut self, value: u8) {
87        // NR32: Custom wave volume
88        self.volume = (value >> 5) & 0x03;
89
90        log::trace!("NR32 write, volume: {}", self.volume);
91    }
92
93    pub fn write_register_3(&mut self, value: u8) {
94        // NR33: Custom wave frequency low bits
95        self.timer.write_frequency_low(value);
96
97        log::trace!("NR33 write, timer frequency: {}", self.timer.frequency());
98    }
99
100    pub fn read_register_4(&self) -> u8 {
101        0xBF | (u8::from(self.length_counter.enabled) << 6)
102    }
103
104    pub fn write_register_4(&mut self, value: u8, frame_sequencer_step: u8) {
105        // NR34: Custom wave frequency high bits + length counter enabled + trigger
106        self.timer.write_frequency_high(value);
107        self.length_counter.set_enabled(
108            value.bit(6),
109            frame_sequencer_step,
110            &mut self.channel_enabled,
111        );
112
113        if value.bit(7) {
114            // Channel triggered
115            self.timer.trigger();
116            self.timer.phase = 0;
117
118            self.length_counter.trigger(frame_sequencer_step);
119
120            self.channel_enabled = self.dac_enabled;
121        }
122
123        log::trace!("NR34 write");
124        log::trace!("  Timer frequency: {}", self.timer.frequency());
125        log::trace!("  Length counter enabled: {}", self.length_counter.enabled);
126        log::trace!("  Triggered: {}", value.bit(7));
127    }
128
129    pub fn tick_2mhz(&mut self) {
130        if !self.channel_enabled {
131            return;
132        }
133
134        if self.timer.tick() == TimerTickEffect::Clocked {
135            self.sample_buffer = self.ram[(self.timer.phase >> 1) as usize];
136        }
137    }
138
139    pub fn clock_length_counter(&mut self) {
140        let prev_enabled = self.channel_enabled;
141        self.length_counter.clock(&mut self.channel_enabled);
142
143        if prev_enabled && !self.channel_enabled {
144            // Explicitly clear the sample buffer when the length counter disables the channel.
145            // Necessary because the wavetable channel continues to output the current sample buffer
146            // when disabled, as long as the DAC is still enabled
147            self.sample_buffer = 0;
148        }
149    }
150
151    pub fn sample(&self) -> Option<u8> {
152        if !self.dac_enabled {
153            return None;
154        }
155
156        // A disabled wavetable channel with an enabled DAC outputs the current sample buffer, not 0!
157        // Some games depend on this, e.g. Cannon Fodder
158
159        if self.volume == 0 {
160            return Some(0);
161        }
162
163        // First sample in high nibble, second sample in low nibble
164        let sample = if !self.timer.phase.bit(0) {
165            self.sample_buffer >> 4
166        } else {
167            self.sample_buffer & 0xF
168        };
169        Some(sample >> (self.volume - 1))
170    }
171
172    pub fn enabled(&self) -> bool {
173        self.channel_enabled
174    }
175
176    pub fn reset(&mut self, hardware_mode: HardwareMode) {
177        *self = Self { ram: self.ram, ..Self::new(hardware_mode) };
178    }
179}