Skip to content

Commit

Permalink
emu: Add simulated clock which updates the screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Granddave committed Jul 18, 2024
1 parent c86cb3e commit 7cc4132
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/emulator/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub fn exec(bytes: &[u8], program_start: u16) -> anyhow::Result<()> {
Event::Key(key_event) => update(&mut app, key_event),
Event::Mouse(_mouse_event) => {}
Event::Resize(_w, _h) => {}
Event::Clock => app.clock(),
};
}

Expand Down
33 changes: 29 additions & 4 deletions src/emulator/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
disassembler::{disassemble_code, listing},
emulator::{
bus::{Bus, Readable, Writeable},
cpu::{self, Cpu, RunOption, STACK_BASE, STACK_PAGE},
cpu::{self, Cpu, STACK_BASE, STACK_PAGE},
},
};
use anyhow::Result;
Expand All @@ -15,6 +15,13 @@ pub mod widget;
const MEMORY_SCROLL_PAGE: usize = 0x10;
const MEMORY_SCROLL_MAX: usize = 0xff;

#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum RunMode {
#[default]
Step,
Run,
}

#[derive(Default)]
pub struct App {
/// Emulated CPU
Expand All @@ -40,6 +47,8 @@ pub struct App {

pub selected_widget: AppWidget,

pub run_mode: RunMode,

/// If the app should quit
should_quit: bool,
}
Expand Down Expand Up @@ -91,22 +100,38 @@ impl App {
self.cpu = Cpu::new();
self.cpu.reset();
self.state.invalidate();

self.run_mode = RunMode::Step;
}

/// Quits the application.
pub fn quit(&mut self) {
self.should_quit = true;
}

/// Clock the CPU by one cycle.
pub fn clock(&mut self) {
match self.run_mode {
RunMode::Run => self.step_cpu(),
RunMode::Step => (),
}
}

/// Steps the CPU by one instruction.
pub fn step_cpu(&mut self) {
self.cpu.step(&mut self.memory);
match self.run_mode {
RunMode::Run => self.cpu.clock(&mut self.memory),
RunMode::Step => self.cpu.step(&mut self.memory),
}
self.state.invalidate();
}

/// Run CPU execution until a break instruction is reached.
pub fn continue_execution(&mut self) {
self.cpu.run(&mut self.memory, RunOption::StopOnJumpToSelf);
pub fn toggle_run_step_mode(&mut self) {
match self.run_mode {
RunMode::Run => self.run_mode = RunMode::Step,
RunMode::Step => self.run_mode = RunMode::Run,
}
}

/// Get the last and current state of the emulation
Expand Down
15 changes: 13 additions & 2 deletions src/emulator/tui/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum Event {
Mouse(MouseEvent),
/// Terminal resize.
Resize(u16, u16),
/// Clock tick.
Clock,
}

/// Terminal event handler.
Expand All @@ -37,14 +39,16 @@ impl EventHandler {
/// Constructs a new instance of [`EventHandler`].
pub fn new(tick_rate: u64) -> Self {
let tick_rate = Duration::from_millis(tick_rate);
let clock_rate = Duration::from_millis(10);
let (sender, receiver) = mpsc::channel();
let handler = {
let sender = sender.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
let mut last_clock = Instant::now();
loop {
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
let timeout = clock_rate
.checked_sub(last_clock.elapsed())
.unwrap_or(tick_rate);

if event::poll(timeout).expect("unable to poll for event") {
Expand All @@ -67,6 +71,13 @@ impl EventHandler {
sender.send(Event::Tick).expect("failed to send tick event");
last_tick = Instant::now();
}

if last_clock.elapsed() >= clock_rate {
sender
.send(Event::Clock)
.expect("failed to send clock event");
last_clock = Instant::now();
}
}
})
};
Expand Down
31 changes: 28 additions & 3 deletions src/emulator/tui/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use ratatui::{prelude::*, widgets::*};

use crate::emulator::cpu::STACK_BASE;

use super::app::{widget::AppWidget, App};
use super::app::{widget::AppWidget, App, RunMode};

fn is_selected_style(selected: AppWidget, current_widget: AppWidget) -> Style {
if selected == current_widget {
Expand Down Expand Up @@ -246,7 +246,7 @@ fn render_top_bar(_app: &mut App, frame: &mut Frame, layout: Rect) {
);
}

fn render_bottom_bar(_app: &mut App, frame: &mut Frame, layout: Rect) {
fn render_help_text_widget(_app: &mut App, frame: &mut Frame, layout: Rect) {
frame.render_widget(
Paragraph::new(vec![
"Press `Esc`, `Ctrl-C` or `q` to stop running.".into(),
Expand All @@ -260,6 +260,23 @@ fn render_bottom_bar(_app: &mut App, frame: &mut Frame, layout: Rect) {
);
}

fn render_simulation_state_widget(app: &mut App, frame: &mut Frame, layout: Rect) {
let is_step_mode = app.run_mode == RunMode::Step;
let text: Vec<Line<'_>> = vec![vec![
Span::raw("Run mode: "),
Span::styled("Step", style_active_text(is_step_mode)),
Span::raw("/"),
Span::styled("Continuous", style_active_text(!is_step_mode)),
]
.into()];
frame.render_widget(
Paragraph::new(text)
.style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Left),
layout,
);
}

pub fn render(app: &mut App, frame: &mut Frame) {
let main_layout = Layout::default()
.direction(Direction::Vertical)
Expand Down Expand Up @@ -292,11 +309,19 @@ pub fn render(app: &mut App, frame: &mut Frame) {
Constraint::Max(18), // Memory viewer
])
.split(app_layout[2]);
let bottom_bar_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(2), // Help text
Constraint::Fill(1), // Simulation state
])
.split(main_layout[2]);

render_top_bar(app, frame, main_layout[0]);
render_registers_widget(app, frame, left_app_layout[0]);
render_stack_widget(app, frame, left_app_layout[1]);
render_disassembly_widget(app, frame, app_layout[1]);
render_memory_widget(app, frame, right_app_layout[0]);
render_bottom_bar(app, frame, main_layout[2])
render_help_text_widget(app, frame, bottom_bar_layout[0]);
render_simulation_state_widget(app, frame, bottom_bar_layout[1]);
}
2 changes: 1 addition & 1 deletion src/emulator/tui/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn update(app: &mut App, key_event: KeyEvent) {
if key_event.modifiers == KeyModifiers::CONTROL {
app.quit()
} else {
app.continue_execution();
app.toggle_run_step_mode();
}
}
KeyCode::Char('s') => app.step_cpu(),
Expand Down

0 comments on commit 7cc4132

Please sign in to comment.