1use egui::{Grid, Ui, Window};
2use std::borrow::Cow;
3use ti_sn76489::{NoiseMode, NoiseReload, Sn76489};
4
5pub const WINDOW_TITLE: &str = "SN76489 Registers";
6
7const NTSC_MCLK_FREQUENCY: f64 = 53_693_175.0;
8
9// Roughly 223722 Hz
10const SAMPLE_RATE: f64 = NTSC_MCLK_FREQUENCY / 15.0 / 16.0;
11
12pub fn render_debug_window(ctx: &egui::Context, psg: &Sn76489, open: &mut bool) {
13    Window::new(WINDOW_TITLE)
14        .open(open)
15        .constrain(false)
16        .resizable([true, true])
17        .default_pos(crate::rand_window_pos())
18        .show(ctx, |ui| {
19            for i in 0..3 {
20                ui.group(|ui| {
21                    render_tone_channel(i, psg, ui);
22                });
23            }
24
25            ui.group(|ui| {
26                render_noise_channel(psg, ui);
27            });
28        });
29}
30
31fn render_tone_channel(i: usize, psg: &Sn76489, ui: &mut Ui) {
32    let period = psg.tone_frequencies()[i];
33    let attenuation = psg.tone_attenuations()[i];
34
35    ui.heading(format!("Tone {i}"));
36
37    Grid::new(format!("psg_tone_{i}_grid")).show(ui, |ui| {
38        ui.label("Period");
39        ui.label(format!("{period} ({:.2} Hz)", tone_frequency(period)));
40        ui.end_row();
41
42        ui.label("Attenuation");
43        ui.label(format!("{attenuation} ({})", attenuation_str(attenuation)));
44        ui.end_row();
45    });
46}
47
48fn render_noise_channel(psg: &Sn76489, ui: &mut Ui) {
49    let mode = psg.noise_mode();
50    let reload = psg.noise_reload();
51    let attenuation = psg.noise_attenuation();
52
53    let mode_str = match mode {
54        NoiseMode::White => "White noise",
55        NoiseMode::Periodic => "Periodic noise",
56    };
57
58    let reload_str: Cow<'static, str> = match reload {
59        NoiseReload::Value(value) => format!("0x{value:02X}").into(),
60        NoiseReload::Tone2 => "Tone 2".into(),
61    };
62
63    ui.heading("Noise");
64
65    Grid::new("psg_noise_grid").show(ui, |ui| {
66        ui.label("Mode");
67        ui.label(mode_str);
68        ui.end_row();
69
70        ui.label("Counter reload");
71        ui.label(reload_str);
72        ui.end_row();
73
74        ui.label("Attenuation");
75        ui.label(format!("{attenuation} ({})", attenuation_str(attenuation)));
76        ui.end_row();
77    });
78}
79
80fn tone_frequency(period: u16) -> f64 {
81    if period == 0 {
82        return tone_frequency(1);
83    }
84
85    SAMPLE_RATE / 2.0 / f64::from(period)
86}
87
88fn attenuation_str(attenuation: u8) -> Cow<'static, str> {
89    match attenuation {
90        15 => "Muted".into(),
91        _ => format!("{} dB", 2 * attenuation).into(),
92    }
93}