| 1 | //! Symbol table of the build, with live values for memory symbols. |
| 2 | |
| 3 | use bench51_core::asm::SymKind; |
| 4 | use eframe::egui::{self, CursorIcon, Sense, Ui}; |
| 5 | |
| 6 | use crate::state::{Action, State}; |
| 7 | use crate::theme::{WARNING, palette}; |
| 8 | use crate::widgets; |
| 9 | |
| 10 | pub fn show(ui: &mut Ui, st: &mut State) { |
| 11 | let p = palette(ui.ctx()); |
| 12 | let Some(doc) = st.docs.get(st.active) else { |
| 13 | widgets::empty_state(ui, "No program", "Open or create a source file."); |
| 14 | return; |
| 15 | }; |
| 16 | let doc_id = doc.id; |
| 17 | let s = &doc.session; |
| 18 | let Some(build) = &s.build else { |
| 19 | widgets::empty_state(ui, "Not built", "Press F7 to assemble."); |
| 20 | return; |
| 21 | }; |
| 22 | let mut goto = None; |
| 23 | if build.out.symbols.is_empty() { |
| 24 | widgets::empty_state(ui, "No symbols", "Labels and EQU names appear here."); |
| 25 | return; |
| 26 | } |
| 27 | egui::ScrollArea::vertical() |
| 28 | .auto_shrink([false; 2]) |
| 29 | .show(ui, |ui| { |
| 30 | egui::Grid::new("symbols") |
| 31 | .num_columns(4) |
| 32 | .spacing([20.0, 6.0]) |
| 33 | .show(ui, |ui| { |
| 34 | for h in ["Name", "Kind", "Value", "Now"] { |
| 35 | ui.label(p.label(h).color(p.text_disabled)); |
| 36 | } |
| 37 | ui.end_row(); |
| 38 | for (name, sym) in &build.out.symbols { |
| 39 | let v = sym.value; |
| 40 | let name_label = egui::Label::new(p.data(name).color(p.text_display)) |
| 41 | .sense(Sense::click()); |
| 42 | let resp = ui |
| 43 | .add(name_label) |
| 44 | .on_hover_cursor(CursorIcon::PointingHand) |
| 45 | .on_hover_text("Go to definition"); |
| 46 | if resp.clicked() { |
| 47 | goto = Some(sym.line); |
| 48 | } |
| 49 | ui.label(p.label(sym.kind.name())); |
| 50 | let width = if (0..=0xFF).contains(&v) { 2 } else { 4 }; |
| 51 | ui.label( |
| 52 | p.data(format!("{:0width$X}", v & 0xFFFF)) |
| 53 | .color(p.text_secondary), |
| 54 | ); |
| 55 | let now = match sym.kind { |
| 56 | SymKind::Data | SymKind::Idata if (0..=0xFF).contains(&v) => { |
| 57 | let a = v as usize; |
| 58 | Some(( |
| 59 | format!("{:02X}", s.sim.iram[a]), |
| 60 | s.sim.iram[a] != s.prev_iram[a], |
| 61 | )) |
| 62 | } |
| 63 | SymKind::Xdata if (0..=0xFFFF).contains(&v) => { |
| 64 | Some((format!("{:02X}", s.sim.xram[v as usize]), false)) |
| 65 | } |
| 66 | SymKind::Bit if (0..=0xFF).contains(&v) => { |
| 67 | Some((u8::from(s.sim.read_bit(v as u8, true)).to_string(), false)) |
| 68 | } |
| 69 | _ => None, |
| 70 | }; |
| 71 | match now { |
| 72 | Some((t, changed)) => ui.label(p.data(t).color(if changed { |
| 73 | WARNING |
| 74 | } else { |
| 75 | p.text_primary |
| 76 | })), |
| 77 | None => ui.label(p.data("—").color(p.text_disabled)), |
| 78 | }; |
| 79 | ui.end_row(); |
| 80 | } |
| 81 | }); |
| 82 | }); |
| 83 | if let Some(line) = goto { |
| 84 | st.actions.push(Action::Goto { |
| 85 | doc: doc_id, |
| 86 | line, |
| 87 | col: 0, |
| 88 | }); |
| 89 | } |
| 90 | } |