151 lines
4.9 KiB
Rust
151 lines
4.9 KiB
Rust
// use std::time::Duration;
|
|
|
|
mod tabs;
|
|
mod term;
|
|
use std::{io::Result, time::Duration};
|
|
|
|
use crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind};
|
|
use ratatui::{
|
|
prelude::*,
|
|
widgets::{Block, Cell, Paragraph, Row, Table, TableState},
|
|
};
|
|
|
|
use crate::cpu::Cpu;
|
|
|
|
pub struct App {
|
|
cpu: Cpu,
|
|
running: bool,
|
|
table_state: TableState,
|
|
}
|
|
|
|
impl App {
|
|
pub fn new(cpu: Cpu) -> Self {
|
|
Self {
|
|
cpu,
|
|
running: true,
|
|
table_state: TableState::default(),
|
|
}
|
|
}
|
|
pub fn init(&mut self) {
|
|
let _ = self.cpu.reset();
|
|
}
|
|
pub fn update(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
|
|
self.draw(terminal)?;
|
|
self.handle_events()?;
|
|
self.handle_cpu()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_cpu(&mut self) -> Result<()> {
|
|
if self.running {
|
|
self.cpu.cycle();
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Draw a single frame of the app.
|
|
fn draw(&self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
|
|
terminal
|
|
.draw(|frame| {
|
|
frame.render_widget(self, frame.size());
|
|
})
|
|
.unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_events(&mut self) -> Result<()> {
|
|
if poll(Duration::from_secs_f32(1.0 / 25.0))? {
|
|
match event::read()? {
|
|
Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key),
|
|
_ => {}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_key_press(&mut self, key: KeyEvent) {
|
|
match key.code {
|
|
KeyCode::Enter => {
|
|
if !self.running {
|
|
self.cpu.cycle()
|
|
}
|
|
}
|
|
KeyCode::Char(' ') => {
|
|
self.running = !self.running;
|
|
}
|
|
_ => {}
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Implement Widget for &App rather than for App as we would otherwise have to clone or copy the
|
|
/// entire app state on every frame. For this example, the app state is small enough that it doesn't
|
|
/// matter, but for larger apps this can be a significant performance improvement.
|
|
impl Widget for &App {
|
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
let [cpu_area, memory_area] =
|
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(area);
|
|
|
|
let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.cpu.a, x = self.cpu.x, y = self.cpu.y, pc = self.cpu.pc, s = self.cpu.s, p = self.cpu.p, irq = self.cpu.irq, nmi = self.cpu.nmi);
|
|
|
|
Paragraph::new(cpu_info)
|
|
.block(Block::bordered().title("cpu info!"))
|
|
.render(cpu_area, buf);
|
|
|
|
let memory = self.cpu.memory.lock().unwrap();
|
|
let table_height = memory_area.rows().count() - 2;
|
|
let rows: Vec<Row> = memory.data[0..table_height * 16]
|
|
.chunks(16)
|
|
.map(|chunk| {
|
|
chunk
|
|
.iter()
|
|
.map(|content| Cell::from(Text::from(format!("{content:#02}"))))
|
|
.collect::<Row>()
|
|
})
|
|
.collect();
|
|
let widths = vec![Constraint::Length(2); 16];
|
|
Widget::render(
|
|
Table::new(rows, widths)
|
|
.header(
|
|
Row::new(vec![
|
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e",
|
|
"f",
|
|
])
|
|
.style(Style::new().bold())
|
|
// To add space between the header and the rest of the rows, specify the margin
|
|
.bottom_margin(1),
|
|
)
|
|
.block(Block::bordered().title("memory!")),
|
|
memory_area,
|
|
buf,
|
|
);
|
|
}
|
|
}
|
|
|
|
// impl Widget for App {
|
|
// fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
|
|
// // let layout = Layout::horizontal([Constraint::Min(0), Constraint::Length(43)]);
|
|
// // let [title] = layout.areas(area);
|
|
|
|
// Span::styled("Ratatui", Style::default()).render(area, buf);
|
|
// // let titles = Tab::iter().map(Tab::title);
|
|
// // Tabs::new(titles)
|
|
// // .style(THEME.tabs)
|
|
// // .highlight_style(THEME.tabs_selected)
|
|
// // .select(self.tab as usize)
|
|
// // .divider("")
|
|
// // .padding("", "")
|
|
// // .render(tabs, buf);
|
|
// }
|
|
|
|
// // fn render_selected_tab(&self, area: Rect, buf: &mut Buffer) {
|
|
// // match self.tab {
|
|
// // Tab::About => self.about_tab.render(area, buf),
|
|
// // Tab::Recipe => self.recipe_tab.render(area, buf),
|
|
// // Tab::Email => self.email_tab.render(area, buf),
|
|
// // Tab::Traceroute => self.traceroute_tab.render(area, buf),
|
|
// // Tab::Weather => self.weather_tab.render(area, buf),
|
|
// // };
|
|
// // }
|
|
// }
|