Skip to main content

megadrive_sys/
fm.rs

1use core::ptr::{write_volatile, read_volatile};
2
3const FM_BASE: *mut u8 = 0xa04000 as _;
4const FM_LFO: u8 = 0x22;
5const FM_TIMER_A_HI: u8 = 0x24;
6const FM_TIMER_A_LO: u8 = 0x25;
7const FM_TIMER_B: u8 = 0x25;
8const FM_TIMER_CTRL: u8 = 0x27;
9const FM_KEY_ON: u8 = 0x28;
10const FM_DAC_DATA: u8 = 0x2a;
11const FM_DAC_ENABLE: u8 = 0x2b;
12const FM_MULTIPLY: u8 = 0x30;
13const FM_TOTAL_LEVEL: u8 = 0x40;
14const FM_ATTACK_RATE: u8 = 0x50;
15const FM_DECAY_RATE: u8 = 0x60;
16const FM_SUSTAIN_RATE: u8 = 0x70;
17const FM_RELEASE_RATE: u8 = 0x80;
18const FM_SSGEG: u8 = 0x90;
19const FM_FREQUENCY_LO: u8 = 0xa0;
20const FM_FREQUENCY_HI: u8 = 0xa4;
21const FM_ALGORITHM: u8 = 0xb0;
22const FM_PANNING: u8 = 0xb4;
23
24static FM_SPECIAL_FREQUENCY_LO: [u8; 4] = [0xa9, 0xaa, 0xa8, 0xa2];
25static FM_SPECIAL_FREQUENCY_HI: [u8; 4] = [0xad, 0xae, 0xac, 0xa6];
26
27static ALL_CHANNELS: [u8; 6] = [0, 1, 2, 4, 5, 6];
28const NUM_CHANNELS: u8 = 6;
29const NUM_OPERATORS: u8 = 4;
30
31/// An enum representing the 4 supported panning values for a bank.
32#[repr(u8)]
33#[derive(Clone, Copy, Debug)]
34pub enum Panning {
35    None = 0b00,
36    Left = 0b10,
37    Right = 0b01,
38    Both = 0b11,
39}
40
41/// Note frequencies for use with the YM2612.
42///
43/// These are slightly inaccurate since the NTSC & PAL versions of the console
44/// operate the FM chip at slightly different frequencies.
45#[repr(u16)]
46#[derive(Clone, Copy, Debug)]
47pub enum Note {
48    C = 644,
49    CSharp = 681,
50    D = 722,
51    DSharp = 765,
52    E = 810,
53    F = 858,
54    FSharp = 910,
55    G = 964,
56    GSharp = 1021,
57    A = 1081,
58    ASharp = 1146,
59    B = 1214,
60}
61
62impl From<Note> for u16 {
63    fn from(v: Note) -> Self {
64        v as u16
65    }
66}
67
68/// Frequency enumeration for the Low-Frequency Oscillator.
69#[repr(u8)]
70#[derive(Clone, Copy, Debug)]
71pub enum LFORate {
72    F3_82Hz = 0b000,
73    F5_33Hz = 0b001,
74    F5_77Hz = 0b010,
75    F6_11Hz = 0b011,
76    F6_60Hz = 0b100,
77    F9_23Hz = 0b101,
78    F46_11Hz = 0b110,
79    F69_22Hz = 0b111,
80}
81
82impl From<LFORate> for u8 {
83    fn from(v: LFORate) -> Self {
84        v as u8
85    }
86}
87
88/// A struct for formatting the timer config used by the FM chip.
89#[derive(Clone, Copy, Debug)]
90pub struct TimerConfig(u8);
91
92impl TimerConfig {
93    /// Create a new timer config with CH3 in normal mode and both timers disabled.
94    pub fn new() -> TimerConfig {
95        TimerConfig(0)
96    }
97
98    /// Configure channel 3 'special mode'.
99    pub fn ch3_special_mode(self, v: bool) -> TimerConfig {
100        let f = if v { 0x40 } else { 0 };
101        TimerConfig((self.0 & 0x3f) | f)
102    }
103
104    /// Enable or disable timer A.
105    ///
106    /// If `e` is set, the timer is enabled and will count.
107    /// If `r` is set, the timer will set the completed flag when it wraps.
108    pub fn enable_timer_a(self, e: bool, r: bool) -> TimerConfig {
109        let fe = if e { 4 } else { 0 };
110        let fr = if r { 1 } else { 0 };
111        TimerConfig((self.0 & 0xfa) | fe | fr)
112    }
113
114    /// Enable or disable timer B.
115    ///
116    /// If `e` is set, the timer is enabled and will count.
117    /// If `r` is set, the timer will set the completed flag when it wraps.
118    pub fn enable_timer_b(self, e: bool, r: bool) -> TimerConfig {
119        let fe = if e { 8 } else { 0 };
120        let fr = if r { 2 } else { 0 };
121        TimerConfig((self.0 & 0xf5) | fe | fr)
122    }
123
124    /// Reset timer A.
125    pub fn reset_timer_a(self, v: bool) -> TimerConfig {
126        let v = if v {
127            self.0 | 0x10
128        } else {
129            self.0 &! 0x10
130        };
131        TimerConfig(v)
132    }
133
134    /// Reset timer B.
135    pub fn reset_timer_b(self, v: bool) -> TimerConfig {
136        let v = if v {
137            self.0 | 0x20
138        } else {
139            self.0 &! 0x20
140        };
141        TimerConfig(v)
142    }
143}
144
145/// A driver for the YM2612.
146///
147/// Whilst normally controlled by the Z80, this driver can be used to operate
148/// the YM2612 from the 68k.
149pub struct FM;
150
151impl FM {
152    /// Initialise the FM hardware and return an object for manipulating it.
153    ///
154    /// Whilst this is memory safe (and thus the function is not unsafe),
155    /// initialising multiple FMs will result in conflicts.
156    pub fn new() -> FM {
157        let fm = FM;
158        fm.enable_lfo(None);
159
160        // Disable special mode channel 3.
161        fm.configure_timers(TimerConfig::new());
162        fm.enable_dac(false);
163
164        for ch in fm.channels() {
165            ch.set_panning(Panning::None, 0, 0);
166            ch.set_key(false);
167
168            for op in ch.operators() {
169                op.set_ssgeg(0);
170            }
171        }
172
173        fm
174    }
175
176    fn write_reg_bank(&self, second: bool, addr: u8, value: u8) {
177        let reg_offset = if second { 2 } else { 0 };
178        unsafe {
179            let base = FM_BASE.offset(reg_offset);
180
181            // Busy spin until idle.
182            while (read_volatile(FM_BASE) & 0x80) != 0 {}
183
184            write_volatile(base, addr);
185            write_volatile(base.offset(1), value);
186        }
187    }
188
189    fn write_reg(&self, addr: u8, value: u8) {
190        self.write_reg_bank(false, addr, value);
191    }
192
193    /// Enable or disable the LFO unit.
194    ///
195    /// This enables and sets the frequency of the low-frequency-oscillator.
196    /// This is not enough to employ it - the LFO needs to be abled per
197    /// operator.
198    pub fn enable_lfo(&self, rate: Option<LFORate>) {
199        let v = rate.map_or(0, |v| 8 | (u8::from(v) & 7));
200        self.write_reg(FM_LFO, v);
201    }
202
203    /// Enables or disables the DAc, which replaces channel 6.
204    pub fn enable_dac(&self, enabled: bool) {
205        let v = if enabled { 0x80 } else { 0 };
206        self.write_reg(FM_DAC_ENABLE, v);
207    }
208
209    /// Write a single sample to the DAC.
210    pub fn dac_write(&self, v: u8) {
211        self.write_reg(FM_DAC_DATA, v);
212    }
213
214    /// Configure the frequency of timer A.
215    ///
216    /// The interval is calculated as `(1024 - f) * 18 us`.
217    pub fn set_timer_a(&self, f: u16) {
218        let hi = (f >> 2) as u8;
219        let lo = (f & 3) as u8;
220
221        self.write_reg(FM_TIMER_A_HI, hi);
222        self.write_reg(FM_TIMER_A_LO, lo);
223    }
224
225    /// Configure the frequency of timer B.
226    ///
227    /// The interval is calculated as `(256 - f) * 288 us`.
228    pub fn set_timer_b(&self, f: u8) {
229        self.write_reg(FM_TIMER_B, f);
230    }
231
232    /// Configure the timers.
233    pub fn configure_timers(&self, c: TimerConfig) {
234        self.write_reg(FM_TIMER_CTRL, c.0)
235    }
236
237    /// Check whether the timers have completed.
238    pub fn timer_status(&self) -> (bool, bool) {
239        let v = unsafe { read_volatile(FM_BASE) };
240        let a = (v & 1) != 0;
241        let b = (v & 2) != 0;
242        (a, b)
243    }
244
245    /// Fetch a single FM channel.
246    pub fn channel(&self, channel: u8) -> Channel {
247        Channel(FM, channel & 7)
248    }
249
250    /// Fetch all of the channels of the FM chip.
251    pub fn channels(&self) -> impl Iterator<Item=Channel> {
252        (0..NUM_CHANNELS).map(|c| Channel(FM, c))
253    }
254}
255
256/// A single FM channel.
257///
258/// This is a hardware voice. It can only be playing one note at a time.
259pub struct Channel(FM, u8);
260
261impl Channel {
262    fn write_reg(&self, base: u8, value: u8) {
263        let channel = self.1;
264        let (channel, second) = if channel > 3 {
265            (channel - 3, true)
266        } else {
267            (channel, false)
268        };
269        let addr = base + (channel & 3);
270        self.0.write_reg_bank(second, addr, value);
271    }
272
273    /// Set whether the key is down for a channel.
274    ///
275    /// This version allows setting key down individually per-operator, however
276    /// generally `set_key` alone is sufficient.
277    pub fn set_key_raw(&self, operator_mask: u8) {
278        let v = (ALL_CHANNELS[self.1 as usize] & 7) | (operator_mask << 4);
279        self.0.write_reg(FM_KEY_ON, v);
280    }
281
282    /// Set whether the key for a particular channel is down.
283    pub fn set_key(&self, down: bool) {
284        let mask = if down { 0xf } else { 0 };
285        self.set_key_raw(mask);
286    }
287
288    /// Set the frequency for a bank.
289    ///
290    /// The frequency is specified in chip-specific units. Use the `Note` enum for
291    /// pre-calculated values.
292    ///
293    /// Octave can be used to increase the octave (the maximum value is 7).
294    pub fn set_frequency(&self, frequency: impl Into<u16>, octave: u8) {
295        let frequency = frequency.into();
296        let lo = frequency as u8;
297        let hi = (((frequency >> 8) as u8) & 7) | ((octave & 7) << 3);
298
299        self.write_reg(FM_FREQUENCY_HI, hi);
300        self.write_reg(FM_FREQUENCY_LO, lo);
301    }
302
303    /// Set the algorithm and feedback for a bank.
304    pub fn set_algorithm(&self, algorithm: u8, feedback: u8) {
305        let v = (algorithm & 7) | ((feedback & 7) << 3);
306        self.write_reg(FM_ALGORITHM, v);
307    }
308
309    /// Set the panning, frequency modulation and amplituate modulation for a bank.
310    pub fn set_panning(&self, panning: Panning, ams: u8, fms: u8) {
311        let v = ((panning as u8) << 6) | ((ams & 3) << 4) | (fms & 7);
312        self.write_reg(FM_PANNING, v);
313    }
314
315    /// Get one of this channel's operators.
316    pub fn operator(&self, operator: u8) -> Operator {
317        Operator(FM, self.1, operator & 3)
318    }
319
320    /// Get all of the operators for this channel.
321    pub fn operators(&self) -> impl Iterator<Item=Operator> {
322        let ch = self.1;
323        (0..NUM_OPERATORS).map(move |i| Operator(FM, ch, i))
324    }
325}
326
327/// A single modulator in the channel.
328///
329/// These are chained together to produce the output waveform.
330pub struct Operator(FM, u8, u8);
331
332impl Operator {
333    fn write_reg(&self, base: u8, value: u8) {
334        let channel = self.1;
335        let op = self.2;
336        let (channel, second) = if channel > 3 {
337            (channel - 3, true)
338        } else {
339            (channel, false)
340        };
341        let addr = base | (channel & 3) | ((op & 3) << 2);
342        self.0.write_reg_bank(second, addr, value);
343    }
344
345    /// Set the operator multiplier & detuning.
346    ///
347    /// The multiply value is as written, except that 0 means 0.5.
348    pub fn set_multiplier(&self, multiply: u8, detune: u8) {
349        let v = ((detune & 7) << 4) | (multiply & 0xf);
350        self.write_reg(FM_MULTIPLY, v);
351    }
352
353    /// Set the total level (volume) of a single operator.
354    ///
355    /// The maximum value is 127.
356    pub fn set_total_level(&self, level: u8) {
357        self.write_reg(FM_TOTAL_LEVEL, level & 0x7f);
358    }
359
360    /// Set the attack rate and attack rate scaling for an operator.
361    ///
362    /// The attack rate defines how quickly the operator reaches maximum amplitude
363    /// (with 63 being the maximum).
364    ///
365    /// The attack rate scale is used to increase the attack rate at higher frequencies.
366    /// 0 indicates no increase, increasing the value increases the attack rate.
367    pub fn set_attack_rate(&self, attack_rate: u8, rate_scale: u8) {
368        let v = (attack_rate & 0x1f) | (rate_scale << 6);
369        self.write_reg(FM_ATTACK_RATE, v);
370    }
371
372    /// Set the decay rate and enable amplitude modulation.
373    ///
374    /// The higher the decay rate, the steeper the decay, with 31 being the maximum.
375    ///
376    /// If amon is true, the global amplitude modulation will be applied
377    pub fn set_decay_rate(&self, decay_rate: u8, amon: bool) {
378        let v = ((amon as u8) << 7) | (decay_rate & 0x1f);
379        self.write_reg(FM_DECAY_RATE, v);
380    }
381
382    /// Set the sustain rate of the operator (also known as the second decay rate).
383    ///
384    /// The maximum value is 31. Higher values mean steeper decay.
385    pub fn set_sustain_rate(&self, sustain_rate: u8) {
386        let v = sustain_rate & 0x1f;
387        self.write_reg(FM_SUSTAIN_RATE, v);
388    }
389
390    /// Set the release rate and sustain level of an operator.
391    pub fn set_release_rate(&self, release_rate: u8, sustain_level: u8) {
392        let v = (release_rate & 0xf) | ((sustain_level & 0xf) << 4);
393        self.write_reg(FM_RELEASE_RATE, v);
394    }
395
396    /// Set the frequency for a single operator.
397    ///
398    /// This is only valid for channel 3 in 'special' mode.
399    pub fn set_frequency_special(&self, frequency: impl Into<u16>, octave: u8) {
400        let frequency = frequency.into();
401        let lo = frequency as u8;
402        let hi = (((frequency >> 8) as u8) & 7) | ((octave & 7) << 3);
403        self.0.write_reg(FM_SPECIAL_FREQUENCY_HI[self.2 as usize], hi);
404        self.0.write_reg(FM_SPECIAL_FREQUENCY_LO[self.2 as usize], lo);
405    }
406
407    /// Set the proprietary field.
408    ///
409    /// According to the documentation, this should always be set to zero.
410    fn set_ssgeg(&self, value: u8) {
411        self.write_reg(FM_SSGEG, value);
412    }
413}