improved debug interface

This commit is contained in:
Mwa
2026-03-23 19:19:38 +01:00
parent c6b58dbc21
commit ced0e13f6b
4 changed files with 780 additions and 182 deletions

View File

@@ -9,6 +9,8 @@ winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "b
winit_input_helper = "0.17.0"
parse_int = { version = "0.9.0", optional = true }
wait_on_address = { version = "0.1.4", optional = true }
clap-repl = { version = "0.3.2", optional = true }
clap = { version = "4.6.0", optional = true }
[features]
@@ -16,9 +18,7 @@ default = ["futex"]
div_mul = []
rgba = []
rich_keyboard = []
debug = ["dep:parse_int"]
debug = ["dep:parse_int","dep:clap-repl","dep:clap"]
futex = ["dep:wait_on_address"]
# [[bench]]
# name = "bench"
# harness = false
clap-repl = ["dep:clap-repl"]
clap = ["dep:clap"]

View File

@@ -6,7 +6,6 @@ use std::{
io::Read,
mem::transmute,
ops::{Index, IndexMut},
process::exit,
sync::atomic::AtomicU32,
time::{self, Instant},
};
@@ -314,7 +313,7 @@ pub(crate) fn instr_to_text(i: u32, a: u32, book: &HashMap<u32, String>) -> Stri
format!("skip {} {cond} {reg} {op2}", addr(d as u32))
}
Instruction::Jump(a) => format!("jump {}", addr(a)),
Instruction::Call(a) => format!("call {}", addr(a)),
Instruction::Call(a) => format!("call {}", addr(a % 0x1000_0000)),
Instruction::Ret() => format!("ret"),
Instruction::Reti() => format!("reti"),
Instruction::Swi() => format!("swi"),
@@ -339,7 +338,7 @@ pub struct Computer {
#[cfg(feature = "debug")]
pub(crate) error: bool,
#[cfg(feature = "debug")]
pub(crate) book: HashMap<u32, String>,
pub(crate) book: (HashMap<u32, String>,HashMap<String,u32>),
}
impl Index<Reg> for Computer {
@@ -368,7 +367,7 @@ impl Computer {
#[cfg(feature = "debug")]
error: false,
#[cfg(feature = "debug")]
book: HashMap::new(),
book: (HashMap::new(),HashMap::new()),
};
let mut buf = String::new();
std::fs::File::open(filename)
@@ -386,12 +385,15 @@ impl Computer {
while let Some((_, line)) = lines.next() {
if let Some([addr, s]) = line.split_ascii_whitespace().collect::<Vec<_>>().as_array() {
if let Ok(i) = u32::from_str_radix(addr, 16) {
new.book.insert(i, s.to_string());
new.book.0.insert(i, s.to_string());
new.book.1.insert(s.to_string(), i);
}
}
}
new
}
#[inline(always)]
pub fn step(&mut self, s: usize) {
match self.interupts {
@@ -745,6 +747,10 @@ impl Computer {
}
}
}
#[cfg(feature = "debug")]
pub fn debug_step(&mut self,s: usize){
self.step(s);
}
#[inline(always)]
fn resolve(&self, op2: Op2) -> u32 {
match op2 {

View File

@@ -20,7 +20,7 @@ use winit::event_loop::EventLoop;
use winit::platform::scancode::PhysicalKeyExtScancode;
use winit::window::Window;
use crate::cpu::{Computer, MMIOInterupt};
use crate::cpu::{Computer, MMIOInterupt, instr_to_text};
mod wait;
use wait::WaitOnAtomic;
mod cpu;
@@ -304,143 +304,7 @@ fn main() -> Result<(), Error> {
//ugly debug code, I should improve that using a real TUI crate
#[cfg(feature = "debug")]
{
let mut input = std::io::stdin().lines();
loop {
{
println!(
"interrupts are {:?}, with mmio interupts flags {}",
simulation.interupts,
SHARED.external_enabled_interupts.load(Relaxed)
);
for i in 0..8 {
println!(
"r{i} 0x{:08x} r{} 0x{:08x}",
simulation.regs[i],
i + 8,
simulation.regs[i + 8]
);
}
println!(
"SP: {:08x} PC: {:08x}",
simulation.sp * 4,
simulation.pc * 4
);
println!("RAM near SP");
let min_pc = (simulation.pc).min(0x0100_0000 / 4 - 8);
let min_sp = (simulation.sp).min(0x0100_0000 / 4 - 8);
for i in 0..8 {
println!(
"{:8x}: 0x{:08x}",
(min_sp + i) * 4,
simulation.ram[min_sp + i],
);
}
println!("Ram near PC");
for i in 0..8 {
let idx = min_pc + i;
let istr = simulation.ram[idx];
if let Some(s) = simulation.book.get(&(idx as u32 * 4)) {
println!("{s}:")
};
println!(
"{:8x}: 0x{:08x} {}",
idx * 4,
istr,
cpu::instr_to_text(istr, idx as u32 * 4, &simulation.book)
)
}
}
while {
let next = input.next().unwrap().unwrap();
let next: Vec<_> = next.split_ascii_whitespace().collect();
if next.len() > 0 {
match next[0] {
"s" | "step" => {
let n: usize = {
if next.len() >= 2 {
parse_int::parse(next[1]).unwrap_or(1)
} else {
1
}
};
for _ in 0..n {
if simulation.error {
println!("cannot step, cpu killed");
break;
}
simulation.step(1);
}
false
}
"r" | "run" => {
while !simulation.error {
simulation.step(64);
}
false
}
"p" | "print" => {
if next.len() >= 2 {
match parse_int::parse::<u32>(next[1]) {
Ok(i) => {
let v = simulation.ram[i as usize / 4];
println!(
"0x{:8x} -- {}",
v,
cpu::instr_to_text(v, i, &simulation.book)
);
true
}
Err(e) => {
println!("{e}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
}
"c" | "context" => false,
"u" | "up" => {
while !simulation.error
&& simulation.ram[simulation.pc] != 0x8800_0000
{
simulation.step(1);
}
false
}
"t" | "to" => {
if next.len() >= 2 {
match parse_int::parse::<u32>(next[1]) {
Ok(v) => {
while !simulation.error
&& simulation.pc != (v as usize / 4)
{
simulation.step(1);
}
false
}
Err(e) => {
println!("{e}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
}
_ => {
println!("{HELP_MSG}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
} {}
}
debug_loop(&mut simulation);
}
});
@@ -457,11 +321,209 @@ fn main() -> Result<(), Error> {
}
#[cfg(feature = "debug")]
const HELP_MSG: &str = "
step n - step trough n instructions (alias s)
run - run program until exit / error (alias r)
context - print context (alias c)
print n - print ram content at address n and next 8 (alias p)
up - run until the nex ret is reached (alias u)
to n - tun until PC = n (alias t)
";
fn debug_loop(com: &mut Computer) {
struct Wrap(DefaultCompleter, DefaultCompleter);
use clap::Parser;
use clap_repl::ClapEditor;
use clap_repl::reedline::{
Completer, DefaultCompleter, DefaultPrompt, DefaultPromptSegment, FileBackedHistory,
};
use crate::cpu::instr_to_text;
#[derive(Debug, Parser)]
#[command(name = "")] // This name will show up in clap's error messages, so it is important to set it to "".
enum Commands {
/// Step by single instrcution
#[command(alias = "s")]
Step {
///number of instruction to step (default one)
num: Option<usize>,
},
/// Run until program halt, or specified instruction is reached
#[command(alias = "r")]
Run {
/// Can be either a label or a address (support hex format)
desigantor: Option<String>,
},
/// run until the current function return.
#[command(alias = "u")]
Up,
/// Print memory at address. Support hexa format
#[command(alias = "p")]
Print { address: String },
/// Print the address associated with a label
#[command(alias = "l")]
Label { label: String },
/// Print context
#[command(alias = "c")]
Context,
}
let prompt = DefaultPrompt {
left_prompt: DefaultPromptSegment::Basic(">>".to_owned()),
right_prompt: DefaultPromptSegment::Empty,
};
let commands_comp = DefaultCompleter::new_with_wordlen(
["step", "run", "up", "print", "label", "help", "context"]
.map(|s| s.to_string())
.to_vec(),
0,
);
let mut labels_comp =
DefaultCompleter::with_inclusions("_0123456789".chars().collect::<Vec<_>>().as_slice())
.set_min_word_len(0);
labels_comp.insert(com.book.0.values().cloned().collect());
let editor = ClapEditor::<Commands>::builder()
.with_prompt(Box::new(prompt))
.with_editor_hook(|reed| {
reed.with_history(Box::new(
FileBackedHistory::with_file(1000, "debug_cmd.hist".into()).unwrap(),
))
.with_completer(Box::new(Wrap(commands_comp, labels_comp)))
.with_quick_completions(true)
.with_partial_completions(true)
})
.build();
debug_context(com);
editor.repl(|command| match command {
Commands::Step { num } => {
let steps = num.unwrap_or(1);
com.debug_step(steps);
debug_context(com);
}
Commands::Run { desigantor } => match desigantor {
Some(s) => match parse_int::parse::<usize>(s.as_str()) {
Ok(addr) => {
while com.pc != (addr / 4) {
com.debug_step(1);
}
debug_context(com);
}
Err(_) => match com.book.1.get(s.as_str()).cloned() {
Some(addr) => {
while com.pc != (addr as usize / 4) {
com.debug_step(1);
}
debug_context(com);
}
None => {
println!("Error, {s} cannot be interpreted as addr nor label")
}
},
},
None => {
while !com.error {
com.debug_step(64);
}
debug_context(com);
}
},
Commands::Up => {
let curr_sp = com.sp;
while (com.sp > curr_sp)
|| ((com.ram[com.pc] != (0b10001000 << 24))
&& (com.ram[com.pc] != (0b10101000 << 24)))
{
com.debug_step(1);
}
debug_context(com);
}
Commands::Print { address } => match parse_int::parse::<usize>(address.as_str()) {
Ok(addr) => match com.ram.get(addr / 4) {
Some(i) => {
println!(
"RAM at {addr:8x}: {:8x} {}",
i,
instr_to_text(*i, u32::MAX, &com.book.0)
)
}
None => println!("Cannot index RAM at address {addr:8x}"),
},
Err(_) => match com.book.1.get(address.as_str()).cloned() {
Some(addr) => println!(
"RAM at {addr:8x}: {:8x} {}",
com.ram[addr as usize / 4],
instr_to_text(com.ram[addr as usize / 4], addr, &com.book.0)
),
None => {
println!("Error, {address} cannot be interpreted as addr nor label")
}
},
},
Commands::Label { label } => match com.book.1.get(label.as_str()) {
Some(addr) => println!("label is at addr {addr}"),
None => println!("error: label not found"),
},
Commands::Context => debug_context(com),
});
exit(0);
impl Completer for Wrap {
fn complete(&mut self, line: &str, pos: usize) -> Vec<clap_repl::reedline::Suggestion> {
let trimmed = line.trim_start();
let line_parts = trimmed.splitn(2, ' ').collect::<Vec<_>>();
if line_parts.len() <= 1 {
self.0.complete(line, pos)
} else {
match line_parts[0] {
"r" | "run" | "p" | "print" | "l" | "label" => {
let trimmed_2 = line_parts[1].trim_start();
let offset = line.len() - trimmed_2.len();
let mut sub = self.1.complete(trimmed_2, pos - offset);
for sug in sub.iter_mut() {
use clap_repl::reedline::Span;
sug.span = Span {
start: sug.span.start + offset,
end: sug.span.end + offset,
}
}
sub
}
_ => Vec::new(),
}
}
}
}
}
fn debug_context(com: &Computer) {
println!("Interupt state: {:?}", com.interupts);
for i in 0..8 {
println!(
"r{i} = {:8x} r{:<2} = {:8x}",
com.regs[i],
i + 8,
com.regs[i + 8]
);
}
println!("SP={:08x} PC={:08x}", com.sp, com.pc);
println!("RAM at SP | Ram at PC:");
let mut pc_lines = Vec::new();
for i in 0..16 {
match com.book.0.get(&((com.pc + i) as u32 * 4)) {
Some(label) => pc_lines.push(format!(" {label}:")),
None => {}
};
pc_lines.push(format!(
"{:08x} {}",
com.ram[com.pc + i],
instr_to_text(com.ram[com.pc + i], (com.pc + i) as u32 * 4, &com.book.0)
));
}
for (i, pc_l) in pc_lines.iter().enumerate() {
if com.sp + i < com.ram.len() {
println!("{:08x} | {pc_l}", com.ram[com.sp + i])
} else {
println!(" -- | {pc_l}")
}
}
}