Krolyxon
Tag v0.3.0

Branches

Tags

Refs →

bench51cratesbench51-guisrcpanestimers.rs

102 lines · 3.4 KB · Rust

1//! Timer/counter 0 and 1: mode, control bits and progress to overflow.
2
3use bench51_core::session::Session;
4use bench51_core::sfr::{TCON, TH0, TH1, TL0, TL1, TMOD};
5use eframe::egui::{self, Align, Layout, Ui};
6
7use super::{sfr_changed, sfr_chip};
8use crate::theme::{WARNING, palette};
9use crate::widgets;
10
11/// Count so far and the count at which the timer overflows.
12pub fn progress(mode: u8, th: u8, tl: u8) -> (u32, u32) {
13 match mode {
14 0 => (((th as u32) << 5) | (tl as u32 & 0x1F), 1 << 13),
15 1 => (((th as u32) << 8) | tl as u32, 1 << 16),
16 2 => {
17 let span = 256 - th as u32;
18 ((tl as u32).saturating_sub(th as u32).min(span), span.max(1))
19 }
20 _ => (tl as u32, 256),
21 }
22}
23
24fn timer(ui: &mut Ui, s: &mut Session, n: u8) {
25 let p = palette(ui.ctx());
26 let (th, tl) = if n == 0 { (TH0, TL0) } else { (TH1, TL1) };
27 let shift = 4 * n;
28 let ctl = (s.sim.sfr(TMOD) >> shift) & 0x0F;
29 let mut mode = ctl & 3;
30 let (tr, tf) = if n == 0 { (0x10, 0x20) } else { (0x40, 0x80) };
31 let running = s.sim.sfr(TCON) & tr != 0;
32
33 ui.horizontal(|ui| {
34 ui.label(p.label(format!("Timer {n}")).color(p.text_primary));
35 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
36 let desc = match mode {
37 0 => "13-bit",
38 1 => "16-bit",
39 2 => "8-bit auto-reload",
40 _ => "split 8-bit",
41 };
42 ui.label(p.label(desc).color(p.text_disabled));
43 });
44 });
45 ui.add_space(4.0);
46 ui.horizontal(|ui| {
47 let changed = sfr_changed(s, th) || sfr_changed(s, tl);
48 ui.label(
49 p.data(format!("{:02X} {:02X}", s.sim.sfr(th), s.sim.sfr(tl)))
50 .size(18.0)
51 .color(if changed { WARNING } else { p.text_primary }),
52 );
53 ui.label(p.label(format!("TH{n} TL{n}")).color(p.text_disabled));
54 });
55 let (count, max) = progress(mode, s.sim.sfr(th), s.sim.sfr(tl));
56 let segments = 24;
57 let filled = (count as u64 * segments as u64 / max as u64) as usize;
58 let color = if running {
59 p.text_display
60 } else {
61 p.text_disabled
62 };
63 let w = ui.available_width().min(360.0);
64 widgets::segment_bar(ui, w, 8.0, segments, filled, color, false);
65 ui.add_space(6.0);
66 ui.horizontal_wrapped(|ui| {
67 ui.spacing_mut().item_spacing.x = 4.0;
68 if widgets::segmented(ui, &mut mode, &[(0, "M0"), (1, "M1"), (2, "M2"), (3, "M3")]) {
69 let tmod = s.sim.sfr(TMOD) & !(3 << shift) | (mode << shift);
70 s.sim.set_sfr(TMOD, tmod);
71 }
72 ui.add_space(6.0);
73 sfr_chip(ui, s, "GATE", TMOD, 0x08 << shift);
74 sfr_chip(ui, s, "C/T", TMOD, 0x04 << shift);
75 sfr_chip(ui, s, &format!("TR{n}"), TCON, tr);
76 sfr_chip(ui, s, &format!("TF{n}"), TCON, tf);
77 });
78}
79
80pub fn show(ui: &mut Ui, s: &mut Session) {
81 egui::ScrollArea::vertical()
82 .auto_shrink([false; 2])
83 .show(ui, |ui| {
84 timer(ui, s, 0);
85 ui.add_space(24.0);
86 timer(ui, s, 1);
87 });
88}
89
90#[cfg(test)]
91mod tests {
92 use super::progress;
93
94 #[test]
95 fn overflow_progress_per_mode() {
96 assert_eq!(progress(1, 0x80, 0x00), (0x8000, 0x10000));
97 assert_eq!(progress(0, 0x01, 0xFF), (0x3F, 0x2000));
98 assert_eq!(progress(2, 0xFD, 0xFE), (1, 3));
99 assert_eq!(progress(2, 0xFF, 0xFF), (0, 1));
100 assert_eq!(progress(3, 0x12, 0x40), (0x40, 256));
101 }
102}