megadrive_sys/
psg.rs

1use core::ptr::write_volatile;
2
3const PSG_BASE: *mut u8 = 0xc00011 as _;
4const NUM_CHANNELS: u8 = 4;
5
6/// A frequency for use with the noise generator.
7#[repr(u8)]
8#[derive(Clone, Copy, Debug)]
9pub enum NoiseFrequency {
10    High = 0,
11    Mid = 1,
12    Low = 2,
13    Channel2 = 3,
14}
15
16/// A selection of note frequencies to use with the tone generators.
17#[repr(u16)]
18#[derive(Clone, Copy, Debug)]
19pub enum Note {
20    C3 = 851,
21    CSharp3 = 803,
22    D3 = 758,
23    DSharp3 = 715,
24    E3 = 675,
25    F3 = 637,
26    FSharp3 = 601,
27    G3 = 568,
28    GSharp3 = 536,
29    A3 = 506,
30    ASharp3 = 477,
31    B3 = 450,
32}
33
34impl Note {
35    /// Return a frequency value for the given note increased by the given
36    /// number of octaves.
37    pub fn increase_octave(self, o: usize) -> u16 {
38        (self as u16) >> o
39    }
40}
41
42impl From<Note> for u16 {
43    fn from(n: Note) -> Self {
44        n as u16
45    }
46}
47
48/// The programmable sound generator.
49///
50/// This chip can generate noise and some square waves. Mostly this is used for
51/// sound effects.
52pub struct PSG;
53
54impl PSG {
55    /// Initialise and return the PSG.
56    ///
57    /// This is not marked unsafe as it cannot lead to memory safety issues, however
58    /// creating two of these can cause conflicts in the generated sounds.
59    pub fn new() -> PSG {
60        let psg = PSG;
61
62        for c in 0..NUM_CHANNELS {
63            psg.set_volume(c, 0);
64        }
65
66        psg
67    }
68
69    fn write(&self, v: u8) {
70        unsafe { write_volatile(PSG_BASE, v) };
71    }
72
73    /// Set the volume of a channel.
74    pub fn set_volume(&self, channel: u8, volume: u8) {
75        self.write(0x90 | ((channel & 3) << 5) | (0x1f - (volume & 0x1f)));
76    }
77
78    /// Set the pitch of one of the channels.
79    ///
80    /// This is not valid for the noise generator on channel 3.
81    pub fn set_pitch(&self, channel: u8, frequency: impl Into<u16>) {
82        let frequency = frequency.into();
83        self.write(0x80 | ((channel & 3) << 5) | ((frequency as u8) & 0xf));
84        self.write((frequency >> 4) as u8);
85    }
86
87    /// Configure the noise channel.
88    pub fn set_noise(&self, white: bool, frequency: NoiseFrequency) {
89        let white = if white { 4 } else { 0 };
90        self.write(0xe0 | white | (frequency as u8));
91    }
92}