PSG channel 3 (wavetable / custom wave)
9const WAVE_RAM_BANK_LEN: usize = 16; 10 11define_bit_enum!(WaveRamSizeBanks, [One, Two]); 12 13#[derive(Debug, Clone, Copy, Encode, Decode)] 14pub struct WavetableChannel { 15 wave_ram: [[u8; WAVE_RAM_BANK_LEN]; 2], 16 sample_buffer: u8, 17 wave_ram_size: WaveRamSizeBanks, 18 selected_bank: bool, 19 timer: WavetableTimer, 20 length_counter: WavetableLengthCounter, 21 volume: u8, 22 force_volume_75: bool, 23 enabled: bool, 24 active: bool, 25} 26 27impl WavetableChannel { 28 pub fn new() -> Self { 29 Self { 30 wave_ram: array::from_fn(|_| array::from_fn(|_| 0)), 31 sample_buffer: 0, 32 wave_ram_size: WaveRamSizeBanks::default(), 33 selected_bank: false, 34 timer: WavetableTimer::new(), 35 length_counter: WavetableLengthCounter::new(), 36 volume: 0, 37 force_volume_75: false, 38 enabled: false, 39 active: false, 40 } 41 } 42 43 pub fn tick_2mhz(&mut self) { 44 if !self.active { 45 return; 46 } 47 48 if self.timer.tick() == TimerTickEffect::Clocked { 49 if self.wave_ram_size == WaveRamSizeBanks::Two && self.timer.phase == 0 { 50 self.selected_bank = !self.selected_bank; 51 } 52 53 self.sample_buffer = 54 self.wave_ram[usize::from(self.selected_bank)][(self.timer.phase >> 1) as usize]; 55 } 56 } 57 58 pub fn clock_length_counter(&mut self) { 59 let prev_active = self.active; 60 self.length_counter.clock(&mut self.active); 61 62 if prev_active && !self.active { 63 // Explicitly clear sample buffer when length counter disables channel 64 self.sample_buffer = 0; 65 } 66 } 67 68 pub fn sample(&self) -> u8 { 69 if !self.enabled { 70 return 0; 71 } 72 73 let sample = if !self.timer.phase.bit(0) { 74 self.sample_buffer >> 4 75 } else { 76 self.sample_buffer & 0xF 77 }; 78 79 if self.force_volume_75 { 80 (3 * sample) >> 2 81 } else if self.volume == 0 { 82 0 83 } else { 84 sample >> (self.volume - 1) 85 } 86 } 87 88 pub fn active(&self) -> bool { 89 self.active 90 } 91 92 pub fn write_nr30(&mut self, value: u8) { 93 self.wave_ram_size = WaveRamSizeBanks::from_bit(value.bit(5)); 94 self.selected_bank = value.bit(6); 95 self.enabled = value.bit(7); 96 97 self.active &= self.enabled; 98 99 log::trace!("NR30 write: {value:02X}"); 100 log::trace!(" Wave RAM banks: {:?}", self.wave_ram_size); 101 log::trace!(" Playback wave RAM bank: {}", u8::from(self.selected_bank)); 102 log::trace!(" Channel enabled: {}", self.enabled); 103 } 104 105 pub fn read_nr30(&self) -> u8 { 106 0x1F | ((self.wave_ram_size as u8) << 5) 107 | (u8::from(self.selected_bank) << 6) 108 | (u8::from(self.enabled) << 7) 109 } 110 111 pub fn write_nr31(&mut self, value: u8) { 112 self.length_counter.load(value); 113 114 log::trace!("NR31 write: {value:02X} (length counter load)"); 115 } 116 117 pub fn write_nr32(&mut self, value: u8) { 118 self.volume = (value >> 5) & 3; 119 self.force_volume_75 = value.bit(7); 120 121 log::trace!("NR32 write: {value:02X}"); 122 log::trace!(" Volume: {}", ["0%", "100%", "50%", "25%"][self.volume as usize]); 123 log::trace!(" Force volume to 75%: {}", self.volume); 124 } 125 126 pub fn read_nr32(&self) -> u8 { 127 0x1F | (self.volume << 5) | (u8::from(self.force_volume_75) << 7) 128 } 129 130 pub fn write_nr33(&mut self, value: u8) { 131 self.timer.write_frequency_low(value); 132 133 log::trace!("NR33 write: {value:02X}"); 134 log::trace!(" Timer frequency: {}", self.timer.frequency()); 135 } 136 137 pub fn read_nr34(&self) -> u8 { 138 0xBF | (u8::from(self.length_counter.enabled) << 6) 139 } 140 141 pub fn write_nr34(&mut self, value: u8, frame_sequencer_step: u8) { 142 self.timer.write_frequency_high(value); 143 self.length_counter.set_enabled(value.bit(4), frame_sequencer_step, &mut self.active); 144 145 if value.bit(7) { 146 // Channel triggered 147 self.timer.trigger(); 148 self.timer.phase = 0; 149 150 self.length_counter.trigger(frame_sequencer_step); 151 152 self.active = self.enabled; 153 } 154 155 log::trace!("NR34 write: {value:02X}"); 156 log::trace!(" Timer frequency: {}", self.timer.frequency()); 157 log::trace!(" Length counter enabled: {}", self.length_counter.enabled); 158 log::trace!(" Triggered: {}", value.bit(7)); 159 } 160 161 pub fn read_wave_ram(&self, address: u32) -> u8 { 162 // Reads always go to the inactive bank 163 let wave_ram_addr = (address & 0xF) as usize; 164 let bank: usize = (!self.selected_bank).into(); 165 self.wave_ram[bank][wave_ram_addr] 166 } 167 168 pub fn write_wave_ram(&mut self, address: u32, value: u8) { 169 // Writes always go to the inactive bank 170 let wave_ram_addr = (address & 0xF) as usize; 171 let bank: usize = (!self.selected_bank).into(); 172 self.wave_ram[bank][wave_ram_addr] = value; 173 } 174}