Skip to content

Commit

Permalink
UI: Improve CPU handler controls
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessandroGrassi99 authored and guerinoni committed Mar 26, 2024
1 parent 348b6cb commit 0c79823
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 75 deletions.
11 changes: 8 additions & 3 deletions emu/src/cpu/arm7tdmi.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::convert::TryInto;

use serde::{Deserialize, Serialize};

#[cfg(feature = "logger")]
use logger::log;

use serde::{Deserialize, Serialize};
#[cfg(feature = "disassembler")]
use vecfixed::VecFixed;

Expand Down Expand Up @@ -38,6 +38,8 @@ pub struct Arm7tdmi {
decoded_arm: Option<ArmModeOpcode>,
fetched_thumb: Option<u16>,
decoded_thumb: Option<ThumbModeOpcode>,

pub current_cycle: u128,
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -124,6 +126,7 @@ impl Default for Arm7tdmi {
decoded_arm: None,
fetched_thumb: None,
decoded_thumb: None,
current_cycle: u128::default(),
};

// Setting ARM mode at startup
Expand Down Expand Up @@ -449,6 +452,7 @@ impl Arm7tdmi {
}

pub fn step(&mut self) {
self.current_cycle += 1;
match self.cpsr.cpu_state() {
CpuState::Thumb => {
let to_execute = self.decoded_thumb;
Expand Down Expand Up @@ -705,11 +709,12 @@ impl std::fmt::Display for HalfwordTransferKind {

#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;

use crate::cpu::condition::Condition;
use crate::cpu::flags::{HalfwordDataTransferOffsetKind, Indexing, LoadStoreKind, Offsetting};
use crate::cpu::registers::{REG_LR, REG_PROGRAM_COUNTER, REG_SP};
use crate::cpu::thumb::instruction::ThumbModeInstruction;
use pretty_assertions::assert_eq;

use super::*;

Expand Down
238 changes: 166 additions & 72 deletions ui/src/cpu_handler.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
use emu::gba::Gba;

use crate::ui_traits::UiTool;

use std::collections::BTreeSet;
use std::ops::{Deref, DerefMut, Range};
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use std::thread;

use egui::text_selection::text_cursor_state::byte_index_from_char_index;
use egui::{TextBuffer, TextEdit};

use emu::gba::Gba;

use crate::ui_traits::UiTool;

pub struct CpuHandler {
gba: Arc<Mutex<Gba>>,
play: Arc<AtomicBool>,
thread_handle: Option<thread::JoinHandle<()>>,
breakpoints: Arc<Mutex<BTreeSet<Breakpoint>>>,
b_address: String,

b_address: UpperHexString,
breakpoint_combo: BreakpointType,
cycle_to_skip_custom_value: u64,
}

impl CpuHandler {
Expand All @@ -24,8 +28,9 @@ impl CpuHandler {
play: Arc::new(AtomicBool::new(false)),
thread_handle: None,
breakpoints: Arc::new(Mutex::new(BTreeSet::new())),
b_address: String::default(),
b_address: UpperHexString::default(),
breakpoint_combo: BreakpointType::Equal,
cycle_to_skip_custom_value: 5000,
}
}
}
Expand All @@ -42,6 +47,56 @@ enum BreakpointType {
Greater,
}

#[derive(Default)]
struct UpperHexString(String);

impl Deref for UpperHexString {
type Target = String;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for UpperHexString {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl TextBuffer for UpperHexString {
fn is_mutable(&self) -> bool {
true
}

fn as_str(&self) -> &str {
&self.0
}

fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
let mut text_string = text.to_string();
text_string.retain(|c| c.is_ascii_hexdigit());
text_string.make_ascii_uppercase();

let byte_idx = byte_index_from_char_index(self.as_str(), char_index);

self.insert_str(byte_idx, text_string.as_str());

text.chars().count()
}

fn delete_char_range(&mut self, char_range: Range<usize>) {
assert!(char_range.start <= char_range.end);

// Get both byte indices
let byte_start = byte_index_from_char_index(self.as_str(), char_range.start);
let byte_end = byte_index_from_char_index(self.as_str(), char_range.end);

// Then drain all characters within this range
self.drain(byte_start..byte_end);
}
}

impl UiTool for CpuHandler {
fn name(&self) -> &'static str {
"Cpu Handler"
Expand Down Expand Up @@ -118,89 +173,128 @@ impl UiTool for CpuHandler {
self.play.swap(false, std::sync::atomic::Ordering::Relaxed);
self.thread_handle = None;
}
});

ui.collapsing("CPU Advanced controls", |ui| {
ui.label(format!(
"Current CPU cycle: {}",
&mut self.gba.lock().unwrap().cpu.current_cycle
));

if ui.button("⏭x1").clicked() {
if let Ok(mut gba) = self.gba.lock() {
gba.step()
ui.horizontal(|ui| {
ui.label("Step CPU cycles:");

if ui.button("⏭x1").clicked() {
if let Ok(mut gba) = self.gba.lock() {
gba.step()
}
}
}

if ui.button("⏭x10").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..10).for_each(|_| gba.step());
if ui.button("⏭x10").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..10).for_each(|_| gba.step());
}
}
}

if ui.button("⏭x100").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..100).for_each(|_| gba.step());
if ui.button("⏭x100").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..100).for_each(|_| gba.step());
}
}
}

if ui.button("⏭x500").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..500).for_each(|_| gba.step());
if ui.button("⏭x500").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..500).for_each(|_| gba.step());
}
}
}
});

ui.add_space(20.0);
if ui.button("⏭x1000").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..1000).for_each(|_| gba.step());
}
}
});

ui.heading("Breakpoints");
ui.add_space(12.0);
ui.horizontal(|ui| {
ui.label("Step (custom) CPU cycles:");
ui.add(egui::DragValue::new(&mut self.cycle_to_skip_custom_value).speed(100));

ui.horizontal(|ui| {
ui.label("Address (hex) : ");
ui.text_edit_singleline(&mut self.b_address);

egui::ComboBox::from_label("Select breakpoint type")
.selected_text(if self.breakpoint_combo == BreakpointType::Equal {
"="
} else {
">"
})
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_min_width(60.0);
ui.selectable_value(&mut self.breakpoint_combo, BreakpointType::Equal, "=");
ui.selectable_value(&mut self.breakpoint_combo, BreakpointType::Greater, ">");
});

if ui.button("Add").clicked() {
if self.b_address.is_empty() {
return;
if ui.button("Step").clicked() {
if let Ok(mut gba) = self.gba.lock() {
(0..self.cycle_to_skip_custom_value).for_each(|_| gba.step());
}
}
})
});

let a = if self.b_address.starts_with("0x") {
self.b_address[2..].to_string()
} else {
self.b_address.clone()
};
ui.collapsing("Breakpoints", |ui| {
ui.horizontal(|ui| {
egui::ComboBox::from_id_source("breakpoint-type")
.selected_text(if self.breakpoint_combo == BreakpointType::Equal {
"Equal to"
} else {
"Greater than"
})
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_width(40.0);
ui.set_max_width(100.0);
ui.selectable_value(
&mut self.breakpoint_combo,
BreakpointType::Equal,
"Equal to",
);
ui.selectable_value(
&mut self.breakpoint_combo,
BreakpointType::Greater,
"Greater than",
);
});

let address = u32::from_str_radix(&a, 16).unwrap();
let b = Breakpoint {
address,
kind: self.breakpoint_combo,
};
ui.label("address (HEX):");

self.breakpoints.lock().unwrap().insert(b);
ui.add(
TextEdit::singleline(&mut self.b_address)
.desired_width(150.0)
.char_limit(16),
);

self.b_address.clear();
}
});
if ui.button("Set").clicked() {
if self.b_address.is_empty() {
return;
}

ui.add_space(10.0);
egui::containers::ScrollArea::new([false, true]).show(ui, |ui| {
let breakpoints = self.breakpoints.lock().unwrap().clone();
let a = if self.b_address.starts_with("0x") {
self.b_address[2..].to_string()
} else {
self.b_address.clone()
};

for b in breakpoints.iter() {
ui.horizontal(|ui| {
ui.label(format!("0x{:08X}", b.address));
if ui.button("x").clicked() {
self.breakpoints.lock().unwrap().remove(b);
}
});
}
let address = u32::from_str_radix(&a, 16).unwrap();
let b = Breakpoint {
address,
kind: self.breakpoint_combo,
};

self.breakpoints.lock().unwrap().insert(b);

self.b_address.clear();
}
});

egui::containers::ScrollArea::new([false, true]).show(ui, |ui| {
ui.label("Active breakpoints:");
let breakpoints = self.breakpoints.lock().unwrap().clone();

for b in breakpoints.iter() {
ui.horizontal(|ui| {
ui.label(format!("0x{:08X}", b.address));
if ui.button("X").clicked() {
self.breakpoints.lock().unwrap().remove(b);
}
});
}
});
});
}
}

0 comments on commit 0c79823

Please sign in to comment.