use crate::*;
use bitflags::bitflags;
use core::mem::size_of;
use rand_core::{Error, RngCore};
use static_assertions::const_assert;
use volatile_register::{RO, WO};
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)]
pub struct Voice {
pub frequency: WO<u16>,
pub pulse_width: WO<u16>,
pub control: WO<VoiceControlFlags>,
pub attack_decay: WO<u8>,
pub sustain_release: WO<u8>,
}
pub enum AttackTime {
Ms2 = 0,
Ms8 = 1,
Ms16 = 2,
Ms24 = 3,
Ms38 = 4,
Ms56 = 5,
Ms68 = 6,
Ms80 = 7,
Ms100 = 8,
Ms250 = 9,
Ms500 = 10,
Ms800 = 11,
Ms1000 = 12,
Ms3000 = 13,
Ms5000 = 14,
Ms8000 = 15,
}
pub enum DecayTime {
Ms6 = 0,
Ms24 = 1,
Ms48 = 2,
Ms72 = 3,
Ms114 = 4,
Ms168 = 5,
Ms204 = 6,
Ms240 = 7,
Ms300 = 8,
Ms750 = 9,
Ms1500 = 10,
Ms2400 = 11,
Ms3000 = 12,
Ms9000 = 13,
Ms15000 = 14,
Ms24000 = 15,
}
pub const fn combine_attack_decay(attack_time: AttackTime, decay_time: DecayTime) -> u8 {
(attack_time as u8 * 16) + (decay_time as u8)
}
impl Voice {
pub fn set_attack_decay(&self, attack_time: AttackTime, decay_time: DecayTime) {
let value = combine_attack_decay(attack_time, decay_time);
unsafe {
self.attack_decay.write(value);
}
}
}
const_assert!(size_of::<Voice>() == 7);
#[repr(C, packed)]
pub struct MOSSoundInterfaceDevice {
pub channel1: Voice,
pub channel2: Voice,
pub channel3: Voice,
pub filter_cutoff: WO<u16>, pub resonance_and_filter_setup: WO<u8>,
pub volume_filter_mode: WO<u8>,
pub potentiometer_x: RO<u8>, pub potentiometer_y: RO<u8>, pub channel3_oscillator: RO<u8>, pub channel3_envelope: RO<u8>, }
const_assert!(size_of::<MOSSoundInterfaceDevice>() == 0x1d);
impl MOSSoundInterfaceDevice {
pub fn start_random_generator(&self) {
unsafe {
self.channel3.frequency.write(u16::MAX);
self.channel3.control.write(VoiceControlFlags::NOISE);
}
}
pub fn rand8(&self, max_value: u8) -> u8 {
loop {
let r = self.channel3_oscillator.read();
if r <= max_value {
return r;
}
}
}
pub fn random_byte(&self) -> u8 {
self.channel3_oscillator.read()
}
pub fn rand16(&self, max_value: u16) -> u16 {
loop {
let r = ((self.channel3_oscillator.read() as u16) << 8)
| (self.channel3_oscillator.read() as u16);
if r <= max_value {
return r;
}
}
}
}
#[derive(Clone)]
pub struct SIDRng {
sid: &'static MOSSoundInterfaceDevice,
}
impl SIDRng {
pub fn new(sid_address: &'static MOSSoundInterfaceDevice) -> Self {
sid_address.start_random_generator();
Self { sid: sid_address }
}
}
impl RngCore for SIDRng {
fn next_u32(&mut self) -> u32 {
u32::from_ne_bytes([
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
])
}
fn next_u64(&mut self) -> u64 {
u64::from_ne_bytes([
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
self.sid.random_byte(),
])
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
dest.iter_mut()
.for_each(|byte| *byte = self.sid.random_byte());
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.fill_bytes(dest);
Ok(())
}
}
pub trait SidTune {
const BYTES: &'static [u8];
const HAS_BASIC_LOAD_ADDRESS: bool = matches!(
u16::from_be_bytes([Self::BYTES[0x08], Self::BYTES[0x09]]),
0
);
const DATA_OFFSET: usize = match Self::HAS_BASIC_LOAD_ADDRESS {
true => u16::from_be_bytes([Self::BYTES[0x06], Self::BYTES[0x07]]) as usize + 2,
false => u16::from_be_bytes([Self::BYTES[0x06], Self::BYTES[0x07]]) as usize,
};
const DATA_LEN: usize = Self::BYTES.len() - Self::DATA_OFFSET;
const INIT_ADDRESS: u16 = u16::from_be_bytes([Self::BYTES[0x0a], Self::BYTES[0x0b]]);
const INIT_PTR: *const unsafe extern "C" fn() -> () =
&Self::INIT_ADDRESS as *const u16 as *const unsafe extern "C" fn() -> ();
const PLAY_ADDRESS: u16 = u16::from_be_bytes([Self::BYTES[0x0c], Self::BYTES[0x0d]]);
const PLAY_PTR: *const unsafe extern "C" fn() -> () =
&Self::PLAY_ADDRESS as *const u16 as *const unsafe extern "C" fn() -> ();
const NUM_SONGS: usize = u16::from_be_bytes([Self::BYTES[0x0e], Self::BYTES[0x0f]]) as usize;
const LOAD_ADDRESS: u16 = match Self::HAS_BASIC_LOAD_ADDRESS {
true => u16::from_le_bytes([
Self::BYTES[Self::DATA_OFFSET - 2],
Self::BYTES[Self::DATA_OFFSET - 1],
]),
false => u16::from_be_bytes([Self::BYTES[0x08], Self::BYTES[0x09]]),
};
fn num_songs(&self) -> usize {
Self::NUM_SONGS
}
fn init(&self, song: u8) {
let [high, low] = Self::INIT_ADDRESS.to_be_bytes();
let address = Self::LOAD_ADDRESS as usize + Self::DATA_LEN;
let init_fn = &address as *const usize as *const unsafe extern "C" fn() -> ();
unsafe {
*(address as *mut [u8; 5]) = [0xa9, song, 0x4c, low, high];
(*init_fn)();
}
}
fn play(&self) {
unsafe { (*(Self::PLAY_PTR))() }
}
unsafe fn to_memory(&self)
where
[(); Self::DATA_LEN]:,
{
let dst = Self::LOAD_ADDRESS as *mut [u8; Self::DATA_LEN];
*dst = Self::BYTES[Self::DATA_OFFSET..Self::BYTES.len()]
.try_into()
.unwrap();
}
}