1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// copyright 2022 mikael lund aka wombat
// 
// licensed under the apache license, version 2.0 (the "license");
// you may not use this file except in compliance with the license.
// you may obtain a copy of the license at
// 
//     http://www.apache.org/licenses/license-2.0
// 
// unless required by applicable law or agreed to in writing, software
// distributed under the license is distributed on an "as is" basis,
// without warranties or conditions of any kind, either express or implied.
// see the license for the specific language governing permissions and
// limitations under the license.

use bitflags::bitflags;
use core::mem::size_of;
use volatile_register::{RO, WO};
use static_assertions::const_assert;

bitflags! {
    pub struct VoiceControlFlags: u8 {
        const GATE     = 0b0000_0001;
        const SYNC     = 0b0000_0010;
        const RING_MODULATION = 0b0000_0100;
        const TEST     = 0b0000_1000;
        const TRIANGLE = 0b0001_0000;
        const SAWTOOTH = 0b0010_0000;
        const PULSE    = 0b0100_0000;
        const NOISE    = 0b1000_0000;
    }
}

#[repr(C, packed)]
/// Registers for a single SID voice/channel
pub struct Voice {
    pub frequency: WO<u16>,             // 0x00
    pub pulse_width: WO<u16>,           // 0x02
    pub control: WO<VoiceControlFlags>, // 0x04
    pub attack_decay: WO<u8>,           // 0x05
    pub sustain_release: WO<u8>,        // 0x06
}

const_assert!(size_of::<Voice>() == 7);

#[repr(C, packed)]
/// MOS Technology Sound Interface Device (SID)
pub struct MOSSoundInterfaceDevice {
    pub channel1: Voice,
    pub channel2: Voice,
    pub channel3: Voice,
    pub filter_cutoff: WO<u16>,             // 0x15
    pub resonance_and_filter_setup: WO<u8>, // 0x17
    pub volume_filter_mode: WO<u8>,         // 0x18
    pub potentiometer_x: RO<u8>,            // 0x19
    pub potentiometer_y: RO<u8>,            // 0x1a
    pub channel3_oscillator: RO<u8>,        // 0x1b
    pub channel3_envelope: RO<u8>,          // 0x1c
}

const_assert!(size_of::<MOSSoundInterfaceDevice>() == 0x1d);

impl MOSSoundInterfaceDevice {
    /**
     * Start noise generation on SID channel 3.
     *
     * Example:
     *
     *    (*c64::SID).start_random_generator();
     *    let random_byte = rand8!(*c64::SID);
     *
     * More information [here](https://www.atarimagazines.com/compute/issue72/random_numbers.php).
     */
    pub unsafe fn start_random_generator(&self) {
        self.channel3.frequency.write(0xffff);
        self.channel3.control.write(VoiceControlFlags::NOISE);
    }
}

/**
 * Use SID entropy to generate a random byte in the interval.
 *
 * Example:
 *
 *    (*c64::SID).start_random_generator();
 *    let random_byte = rand8!(*c64::SID);
 *
 * More information [here](https://www.atarimagazines.com/compute/issue72/random_numbers.php).
 */
#[macro_export]
macro_rules! rand8 {
    ($sid_pointer:expr) => {{
        (*$sid_pointer).channel3_oscillator.read()
    }};
}