memory.rs (8676B)
1 use eframe::egui; 2 use egui::{Align2, FontId, Key, Rect, Sense, Stroke, TextEdit, Ui, pos2, vec2}; 3 use perilus_core::perilus::Perilus; 4 5 use crate::app::{ 6 ADDR_W, BYTES_PER_ROW, DIVIDER_GAP, FONT_SIZE, GROUP_GAP, PC_COLOR, ROW_H, SimApp, 7 WORDS_PER_ROW, char_width, 8 }; 9 use crate::state::{Focus, ViewMode, hex_char, printable, row_digits}; 10 11 fn divider(ui: &mut Ui) { 12 let (rect, _) = ui.allocate_exact_size(vec2(DIVIDER_GAP * 2.0, ROW_H), Sense::hover()); 13 let color = ui.visuals().weak_text_color(); 14 let spacing = ui.spacing().item_spacing.y; 15 ui.painter().line_segment( 16 [ 17 pos2(rect.center().x, rect.top() - spacing * 0.5), 18 pos2(rect.center().x, rect.bottom() + spacing * 0.5), 19 ], 20 Stroke::new(1.0, color), 21 ); 22 } 23 24 impl SimApp { 25 pub(crate) fn memory_ui(&mut self, ui: &mut Ui) { 26 let font = FontId::monospace(FONT_SIZE); 27 let nib_w = char_width(ui, &font); 28 29 ui.horizontal_wrapped(|ui| { 30 ui.strong("Memory"); 31 ui.toggle_value(&mut self.show_aliases, "ABI names [x]"); 32 let mut byte_view = self.view == ViewMode::Byte; 33 if ui.toggle_value(&mut byte_view, "Byte view [v]").changed() { 34 self.view = if byte_view { 35 ViewMode::Byte 36 } else { 37 ViewMode::Word 38 }; 39 } 40 let mut follow = self.follow_pc; 41 if ui 42 .toggle_value(&mut follow, "Follow PC [shift+p]") 43 .changed() 44 { 45 self.follow_pc = follow; 46 self.follow_pc_last = self.current_pc; 47 self.status = format!("Follow PC {}", if follow { "on" } else { "off" }); 48 } 49 ui.separator(); 50 ui.label("addr"); 51 let response = ui.add( 52 TextEdit::singleline(&mut self.address_input) 53 .desired_width(90.0) 54 .hint_text("hex"), 55 ); 56 if std::mem::take(&mut self.focus_address) { 57 response.request_focus(); 58 } 59 if response.lost_focus() && ui.input(|i| i.key_pressed(Key::Enter)) { 60 self.jump_to_address(); 61 } 62 if ui.button("Go [g]").clicked() { 63 self.jump_to_address(); 64 } 65 ui.separator(); 66 ui.label(format!("selected 0x{:08x}", self.mem_addr)); 67 }); 68 ui.separator(); 69 70 if self.follow_pc && self.current_pc != self.follow_pc_last { 71 self.follow_pc_last = self.current_pc; 72 let pc_row = self.current_pc as usize / BYTES_PER_ROW; 73 if !self.mem_viewport.contains(&pc_row) { 74 self.scroll_to_row = Some(pc_row); 75 } 76 } 77 78 let row_pitch = ROW_H + ui.spacing().item_spacing.y; 79 let mut area = egui::ScrollArea::both().auto_shrink([false, false]); 80 if let Some(row) = self.scroll_to_row.take() { 81 area = area.vertical_scroll_offset(row as f32 * row_pitch); 82 } 83 let total_rows = Perilus::MEMORY_SIZE_WORDS / WORDS_PER_ROW; 84 area.show_rows(ui, ROW_H, total_rows, |ui, rows| { 85 self.mem_viewport = rows.clone(); 86 for row in rows { 87 self.memory_row(ui, row, nib_w, &font); 88 } 89 }); 90 } 91 92 fn memory_row(&mut self, ui: &mut Ui, row: usize, nib_w: f32, font: &FontId) { 93 let row_addr = (row * BYTES_PER_ROW) as u32; 94 ui.horizontal(|ui| { 95 let text_color = ui.visuals().text_color(); 96 let background = ui.visuals().panel_fill; 97 let pc = self.current_pc; 98 let focus_memory = self.focus == Focus::Memory; 99 let cursor_in_row = 100 focus_memory && (self.mem_addr / BYTES_PER_ROW as u32) as usize == row; 101 let cursor_index = (self.mem_addr % BYTES_PER_ROW as u32) as usize; 102 103 let (addr_rect, _) = ui.allocate_exact_size(vec2(ADDR_W, ROW_H), Sense::hover()); 104 ui.painter().text( 105 addr_rect.left_center(), 106 Align2::LEFT_CENTER, 107 format!("{row_addr:08x}"), 108 font.clone(), 109 text_color, 110 ); 111 divider(ui); 112 113 let group = match self.view { 114 ViewMode::Word => 8, 115 ViewMode::Byte => 2, 116 }; 117 let gaps = (64 / group - 1) as f32; 118 let digit_w = 64.0 * nib_w + gaps * GROUP_GAP; 119 let (digits_rect, digits_response) = 120 ui.allocate_exact_size(vec2(digit_w, ROW_H), Sense::click()); 121 122 let order = row_digits(self.view); 123 let cursor = (self.mem_addr, self.mem_nib); 124 let mut cells: Vec<(Rect, u32, u8)> = Vec::with_capacity(64); 125 let mut x = digits_rect.left(); 126 for (i, &(offset, nib)) in order.iter().enumerate() { 127 if i > 0 && i % group == 0 { 128 x += GROUP_GAP; 129 } 130 let cell = Rect::from_min_size(pos2(x, digits_rect.top()), vec2(nib_w, ROW_H)); 131 let addr = row_addr + offset; 132 let byte = self.perilus.get_memory_byte(addr); 133 let digit = if nib == 0 { byte >> 4 } else { byte & 0xf }; 134 if focus_memory && (addr, nib) == cursor { 135 ui.painter().rect_filled(cell, 0.0, text_color); 136 ui.painter().text( 137 cell.center(), 138 Align2::CENTER_CENTER, 139 hex_char(digit), 140 font.clone(), 141 background, 142 ); 143 } else { 144 let color = if (addr & !3) == (pc & !3) { 145 PC_COLOR 146 } else { 147 text_color 148 }; 149 ui.painter().text( 150 cell.center(), 151 Align2::CENTER_CENTER, 152 hex_char(digit), 153 font.clone(), 154 color, 155 ); 156 } 157 cells.push((cell, addr, nib)); 158 x += nib_w; 159 } 160 161 if let Some(pos) = digits_response.interact_pointer_pos() 162 && (digits_response.clicked() || digits_response.double_clicked()) 163 && let Some(&(_, addr, nib)) = cells.iter().find(|(rect, _, _)| rect.contains(pos)) 164 { 165 self.mem_addr = addr; 166 self.mem_nib = nib; 167 self.focus = Focus::Memory; 168 if digits_response.double_clicked() { 169 self.set_pc_to_cursor(); 170 } 171 } 172 173 divider(ui); 174 175 let ascii: Vec<char> = match self.view { 176 ViewMode::Word => (0..WORDS_PER_ROW) 177 .flat_map(|word| { 178 self.perilus.get_memory()[row * WORDS_PER_ROW + word].to_le_bytes() 179 }) 180 .map(printable) 181 .collect(), 182 ViewMode::Byte => (0..BYTES_PER_ROW) 183 .map(|i| printable(self.perilus.get_memory_byte(row_addr + i as u32))) 184 .collect(), 185 }; 186 let ascii_width = ascii.len() as f32 * nib_w; 187 let (ascii_rect, _) = ui.allocate_exact_size(vec2(ascii_width, ROW_H), Sense::hover()); 188 let painter = ui.painter(); 189 for (i, ch) in ascii.iter().enumerate() { 190 let cell = Rect::from_min_size( 191 pos2(ascii_rect.left() + i as f32 * nib_w, ascii_rect.top()), 192 vec2(nib_w, ROW_H), 193 ); 194 if cursor_in_row && i == cursor_index { 195 painter.rect_filled(cell, 0.0, text_color); 196 painter.text( 197 cell.center(), 198 Align2::CENTER_CENTER, 199 *ch, 200 font.clone(), 201 background, 202 ); 203 } else { 204 let color = if (row_addr + i as u32) & !3 == pc & !3 { 205 PC_COLOR 206 } else { 207 text_color 208 }; 209 painter.text( 210 cell.center(), 211 Align2::CENTER_CENTER, 212 *ch, 213 font.clone(), 214 color, 215 ); 216 } 217 } 218 }); 219 } 220 }