1use bincode::{Decode, Encode}; 2use jgenesis_common::num::GetBit; 3 4#[derive(Debug, Clone, Copy, Encode, Decode)] 5pub struct LengthCounter<const MAX: u16> { 6 pub enabled: bool, 7 pub counter: u16, 8} 9 10impl<const MAX: u16> Default for LengthCounter<MAX> { 11 fn default() -> Self { 12 Self::new() 13 } 14} 15 16impl<const MAX: u16> LengthCounter<MAX> { 17 #[must_use] 18 pub fn new() -> Self { 19 Self { enabled: false, counter: MAX } 20 } 21 22 pub fn load(&mut self, value: u8) { 23 let masked_value = u16::from(value) & (MAX - 1); 24 self.counter = MAX - masked_value; 25 } 26 27 pub fn trigger(&mut self, frame_sequencer_step: u8) { 28 if self.counter == 0 { 29 self.counter = MAX; 30 31 // Quirk: Immediately clock if enabled during trigger and this is a length counter cycle 32 if self.enabled && !frame_sequencer_step.bit(0) { 33 self.counter -= 1; 34 } 35 } 36 } 37 38 pub fn clock(&mut self, channel_enabled: &mut bool) { 39 if !self.enabled || self.counter == 0 { 40 return; 41 } 42 43 self.counter -= 1; 44 if self.counter == 0 { 45 *channel_enabled = false; 46 } 47 } 48 49 pub fn set_enabled( 50 &mut self, 51 enabled: bool, 52 frame_sequencer_step: u8, 53 channel_enabled: &mut bool, 54 ) { 55 let prev_enabled = self.enabled; 56 self.enabled = enabled; 57 58 // Quirk: Immediately clock if newly enabled and this is a length counter cycle 59 if !prev_enabled && self.enabled && !frame_sequencer_step.bit(0) { 60 self.clock(channel_enabled); 61 } 62 } 63} 64 65pub type StandardLengthCounter = LengthCounter<64>; 66pub type WavetableLengthCounter = LengthCounter<256>; 67 68#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 69pub enum EnvelopeDirection { 70 Increasing, 71 #[default] 72 Decreasing, 73} 74 75impl EnvelopeDirection { 76 fn from_bit(bit: bool) -> Self { 77 if bit { Self::Increasing } else { Self::Decreasing } 78 } 79 80 fn to_bit(self) -> bool { 81 self == Self::Increasing 82 } 83} 84 85#[derive(Debug, Clone, Copy, Encode, Decode)] 86pub struct Envelope { 87 pub volume: u8, 88 enabled: bool, 89 period: u8, 90 counter: u8, 91 direction: EnvelopeDirection, 92 initial_volume: u8, 93 configured_direction: EnvelopeDirection, 94 configured_period: u8, 95} 96 97impl Default for Envelope { 98 fn default() -> Self { 99 Self::new() 100 } 101} 102 103impl Envelope { 104 #[must_use] 105 pub fn new() -> Self { 106 Self { 107 volume: 0, 108 enabled: false, 109 period: 0, 110 counter: 0, 111 direction: EnvelopeDirection::default(), 112 initial_volume: 0, 113 configured_direction: EnvelopeDirection::default(), 114 configured_period: 0, 115 } 116 } 117 118 #[must_use] 119 pub fn read_register(self) -> u8 { 120 (self.initial_volume << 4) 121 | (u8::from(self.configured_direction.to_bit()) << 3) 122 | self.configured_period 123 } 124 125 pub fn write_register(&mut self, value: u8) { 126 let direction = EnvelopeDirection::from_bit(value.bit(3)); 127 128 // "Zombie mode" hardware glitch: If the envelope register is written to with bit 3 set while the 129 // current period is 0, immediately increment volume while wrapping around from 15 to 0. 130 // Exact behavior seems to vary between hardware revisions, but this implementation seems to 131 // work for games that depend on "zombie mode" (e.g. Prehistorik Man) 132 if self.enabled && self.period == 0 && direction == EnvelopeDirection::Increasing { 133 self.volume = (self.volume + 1) & 0x0F; 134 } 135 136 self.initial_volume = value >> 4; 137 self.configured_direction = direction; 138 self.configured_period = value & 0x07; 139 } 140 141 pub fn trigger(&mut self) { 142 self.volume = self.initial_volume; 143 self.direction = self.configured_direction; 144 self.period = self.configured_period; 145 146 self.enabled = true; 147 self.counter = self.period; 148 } 149 150 pub fn clock(&mut self) { 151 if self.period == 0 || !self.enabled { 152 return; 153 } 154 155 self.counter -= 1; 156 if self.counter == 0 { 157 self.counter = self.period; 158 159 match (self.direction, self.volume) { 160 (EnvelopeDirection::Decreasing, 0) | (EnvelopeDirection::Increasing, 15) => { 161 // Volume cannot decrease past 0 or increase past 15 162 // Disable the envelope until next trigger 163 self.enabled = false; 164 } 165 (EnvelopeDirection::Decreasing, _) => { 166 self.volume -= 1; 167 } 168 (EnvelopeDirection::Increasing, _) => { 169 self.volume += 1; 170 } 171 } 172 } 173 } 174} 175 176#[derive(Debug, Clone, Copy, PartialEq, Eq)] 177pub enum TimerTickEffect { 178 None, 179 Clocked, 180} 181 182#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 183pub struct PhaseTimer<const MAX_PHASE: u8> { 184 pub phase: u8, 185 frequency: u16, 186 pub counter: u16, 187 period: u16, 188} 189 190impl<const MAX_PHASE: u8> Default for PhaseTimer<MAX_PHASE> { 191 fn default() -> Self { 192 Self::new() 193 } 194} 195 196impl<const MAX_PHASE: u8> PhaseTimer<MAX_PHASE> {
Panics
If (MAX_PHASE + 1) is not a power of 2
208 #[must_use] 209 pub fn just_reloaded(self) -> bool { 210 self.counter == self.period 211 } 212 213 #[must_use] 214 pub fn frequency(self) -> u16 { 215 self.frequency 216 } 217 218 pub fn write_frequency_low(&mut self, value: u8) { 219 self.write_frequency((self.frequency & 0xFF00) | u16::from(value)); 220 } 221 222 pub fn write_frequency_high(&mut self, value: u8) { 223 self.write_frequency((self.frequency & 0x00FF) | (u16::from(value & 0x07) << 8)); 224 } 225 226 pub fn write_frequency(&mut self, value: u16) { 227 self.frequency = value; 228 self.period = 2048 - value; 229 } 230 231 pub fn trigger(&mut self) { 232 self.counter = self.period; 233 } 234 235 pub fn tick(&mut self) -> TimerTickEffect { 236 self.counter -= 1; 237 if self.counter == 0 { 238 self.counter = self.period; 239 self.phase = (self.phase + 1) & MAX_PHASE; 240 return TimerTickEffect::Clocked; 241 } 242 243 TimerTickEffect::None 244 } 245} 246 247pub type PulseTimer = PhaseTimer<7>; 248pub type WavetableTimer = PhaseTimer<31>;