Debugging mode

This commit is contained in:
Mwa
2026-03-16 04:36:59 +01:00
parent 2d50702acc
commit e385288e70
5 changed files with 281 additions and 23 deletions

1
Cargo.lock generated
View File

@@ -1508,6 +1508,7 @@ name = "simu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"atomic-wait", "atomic-wait",
"parse_int",
"pixels", "pixels",
"winit", "winit",
"winit_input_helper", "winit_input_helper",

View File

@@ -33,6 +33,7 @@ rajouter --features=[liste séparé par des virgules]
- div_mul: Support des instruction de multiplication / division - div_mul: Support des instruction de multiplication / division
- rgba: Écran au format RGBA plutot que 0BGR - rgba: Écran au format RGBA plutot que 0BGR
- rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO - rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO
- debug: repl similaire a la version python du simulateur
### instruction spéciale: ### instruction spéciale:
halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions

View File

@@ -8,8 +8,11 @@ atomic-wait = "1.1.0"
pixels = "0.15.0" pixels = "0.15.0"
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] } winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
winit_input_helper = "0.17.0" winit_input_helper = "0.17.0"
parse_int = { version = "0.9.0", optional = true }
[features] [features]
div_mul = [] div_mul = []
rgba = [] rgba = []
rich_keyboard = [] rich_keyboard = []
debug = ["dep:parse_int"]

View File

@@ -1,3 +1,5 @@
#[cfg(feature = "debug")]
use std::collections::HashMap;
use std::{ use std::{
hint::{likely, unlikely, unreachable_unchecked}, hint::{likely, unlikely, unreachable_unchecked},
io::Read, io::Read,
@@ -5,8 +7,7 @@ use std::{
ops::{Index, IndexMut}, ops::{Index, IndexMut},
process::exit, process::exit,
sync::atomic::AtomicU32, sync::atomic::AtomicU32,
thread::sleep, time::{self, Instant},
time::{self, Duration, Instant},
}; };
pub(crate) struct SharedState { pub(crate) struct SharedState {
@@ -44,7 +45,8 @@ enum Cond {
Ifule, Ifule,
} }
enum InteruptState { #[derive(Debug)]
pub enum InteruptState {
Disabled, Disabled,
Enabled, Enabled,
Serving(InteruptKind, u8), Serving(InteruptKind, u8),
@@ -52,7 +54,8 @@ enum InteruptState {
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)] #[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
#[repr(u32)] #[repr(u32)]
enum InteruptKind { #[allow(unused)] //some are there only based on features
pub(crate) enum InteruptKind {
MMIO = 1, MMIO = 1,
Swi = 2, Swi = 2,
DivByZero = 3, DivByZero = 3,
@@ -243,13 +246,98 @@ impl TryFrom<u32> for Instruction {
type Error = (InteruptKind, Reg, Reg, Op2, u32); type Error = (InteruptKind, Reg, Reg, Op2, u32);
} }
use std::fmt::Display;
impl Display for Op2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Op2::Direct(v) => write!(f, "{v}"),
Op2::Register(r) => write!(f, "r{r}"),
}
}
}
impl Display for Reg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "r{}", self.0)
}
}
impl Display for Cond {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Cond::Ifeq => write!(f, "ifeq"),
Cond::Ifne => write!(f, "ifne"),
Cond::Iflt => write!(f, "iflt"),
Cond::Ifge => write!(f, "ifge"),
Cond::Ifgt => write!(f, "ifgt"),
Cond::Ifle => write!(f, "ifle"),
Cond::Ifult => write!(f, "ifult"),
Cond::Ifuge => write!(f, "ifuge"),
Cond::Ifugt => write!(f, "ifugt"),
Cond::Ifule => write!(f, "ifule"),
}
}
}
#[cfg(feature = "debug")]
pub(crate) fn instr_to_text(i: u32, a: u32, book: &HashMap<u32, String>) -> String {
let addr = |addr: u32| {
let real = a.wrapping_add(addr * 4);
match book.get(&real) {
Some(s) => s.clone(),
None => format!("{addr}"),
}
};
match Instruction::try_from(i) {
Ok(i) => match i {
Instruction::Copy(reg, op2) => format!("copy {reg} {op2}"),
Instruction::Add(reg, reg1, op2) => format!("add {reg} {reg1} {op2}"),
Instruction::Sub(reg, reg1, op2) => format!("sub {reg} {reg1} {op2}"),
Instruction::Or(reg, reg1, op2) => format!("or {reg} {reg1} {op2}"),
Instruction::And(reg, reg1, op2) => format!("and {reg} {reg1} {op2}"),
Instruction::Xor(reg, reg1, op2) => format!("xor {reg} {reg1} {op2}"),
Instruction::Lsl(reg, reg1, op2) => format!("lsl {reg} {reg1} {op2}"),
Instruction::Lsr(reg, reg1, op2) => format!("lsr {reg} {reg1} {op2}"),
Instruction::Asr(reg, reg1, op2) => format!("asr {reg} {reg1} {op2}"),
Instruction::Smull(reg, reg1, op2) => format!("smull {reg} {reg1} {op2}"),
Instruction::Smulh(reg, reg1, op2) => format!("smulh {reg} {reg1} {op2}"),
Instruction::Umull(reg, reg1, op2) => format!("umull {reg} {reg1} {op2}"),
Instruction::Umulh(reg, reg1, op2) => format!("umulh {reg} {reg1} {op2}"),
Instruction::Div(reg, reg1, op2) => format!("div {reg} {reg1} {op2}"),
Instruction::Mod(reg, reg1, op2) => format!("mod {reg} {reg1} {op2}"),
Instruction::Store(reg, op2, reg1) => format!("store [{reg} + {op2}] {reg1}]"),
Instruction::Load(reg, reg1, op2) => format! {"load {reg} [{reg1} + {op2}]"},
Instruction::Push(op2) => format!("push {op2}"),
Instruction::Pop(reg) => format!("pop {reg}"),
Instruction::Skip(d, cond, reg, op2) => {
format!("skip {} {cond} {reg} {op2}", addr(d as u32))
}
Instruction::Jump(a) => format!("jump {}", addr(a)),
Instruction::Call(a) => format!("call {}", addr(a)),
Instruction::Ret() => format!("ret"),
Instruction::Reti() => format!("reti"),
Instruction::Swi() => format!("swi"),
Instruction::Eint() => format!("eint"),
Instruction::Dint() => format!("dint"),
Instruction::GetStack(reg) => format!("copy {reg} sp"),
Instruction::SetStack(op2) => format!("copy sp {op2}"),
},
Err(_) => {
format!("D 0x{:8x}", i)
}
}
}
pub struct Computer { pub struct Computer {
creation: Instant, creation: Instant,
ram: Box<[u32; 0x01000000 / 4]>, pub(crate) ram: Box<[u32; 0x01000000 / 4]>,
regs: [u32; 16], pub(crate) regs: [u32; 16],
pc: usize, pub(crate) pc: usize,
sp: usize, pub(crate) sp: usize,
interupts: InteruptState, pub(crate) interupts: InteruptState,
#[cfg(feature = "debug")]
pub(crate) error: bool,
#[cfg(feature = "debug")]
pub(crate) book: HashMap<u32, String>,
} }
impl Index<Reg> for Computer { impl Index<Reg> for Computer {
@@ -279,18 +367,32 @@ impl Computer {
pc: 0, pc: 0,
sp: 0, sp: 0,
interupts: InteruptState::Disabled, interupts: InteruptState::Disabled,
#[cfg(feature = "debug")]
error: false,
#[cfg(feature = "debug")]
book: HashMap::new(),
}; };
let mut buf = String::new(); let mut buf = String::new();
std::fs::File::open(filename) std::fs::File::open(filename)
.unwrap() .unwrap()
.read_to_string(&mut buf) .read_to_string(&mut buf)
.unwrap(); .unwrap();
for (i, line) in buf.lines().enumerate() { let mut lines = buf.lines().enumerate();
while let Some((i, line)) = lines.next() {
match u32::from_str_radix(line, 16) { match u32::from_str_radix(line, 16) {
Ok(val) => new.ram[i] = val, Ok(val) => new.ram[i] = val,
Err(_) => break, Err(_) => break,
} }
} }
#[cfg(feature = "debug")]
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) {
println!("adding label {s} at addr {i}");
new.book.insert(i, s.to_string());
}
}
}
new new
} }
#[inline(always)] #[inline(always)]
@@ -477,9 +579,19 @@ impl Computer {
if addr & (1 << 28) != 0 { if addr & (1 << 28) != 0 {
addr += 7 << 29; addr += 7 << 29;
} else if addr == 0 { } else if addr == 0 {
#[cfg(feature = "debug")]
{
match self.interupts {
InteruptState::Disabled => {
println!("program terminated");
self.error = true;
return;
}
_ => (),
}
println!("awaiting interupt..."); println!("awaiting interupt...");
}
atomic_wait::wait(&SHARED.external_interupts, 0); atomic_wait::wait(&SHARED.external_interupts, 0);
println!("waking!")
} }
self.pc = (addr + self.pc as u32) as usize; self.pc = (addr + self.pc as u32) as usize;
} }
@@ -490,6 +602,13 @@ impl Computer {
if addr & (1 << 28) != 0 { if addr & (1 << 28) != 0 {
addr += 7 << 29; addr += 7 << 29;
} else if unlikely(addr == 0) { } else if unlikely(addr == 0) {
#[cfg(feature = "debug")]
{
println!("program terminated");
self.error = true;
return;
}
#[cfg(not(feature = "debug"))]
exit(0); exit(0);
} }
self.pc = (addr + self.pc as u32) as usize; self.pc = (addr + self.pc as u32) as usize;
@@ -607,6 +726,12 @@ impl Computer {
match self.interupts { match self.interupts {
InteruptState::Disabled => { InteruptState::Disabled => {
println!("Illegal Instruction whith interupt disabled {kind:?} {arg:?}"); println!("Illegal Instruction whith interupt disabled {kind:?} {arg:?}");
#[cfg(feature = "debug")]
{
self.error = true;
return;
}
#[cfg(not(feature = "debug"))]
exit(1); exit(1);
} }
InteruptState::Enabled => self.interupts = InteruptState::Serving(kind, 0), InteruptState::Enabled => self.interupts = InteruptState::Serving(kind, 0),

View File

@@ -8,6 +8,7 @@
use std::env::args; use std::env::args;
use std::hint::unlikely; use std::hint::unlikely;
use std::io::stdin;
use std::process::exit; use std::process::exit;
use std::sync::{ use std::sync::{
Arc, Arc,
@@ -34,6 +35,7 @@ use cpu::SHARED;
fn wait_int() { fn wait_int() {
let mut v = (&SHARED.external_interupts).load(Acquire); let mut v = (&SHARED.external_interupts).load(Acquire);
while unlikely(v != 0) { while unlikely(v != 0) {
#[cfg(feature = "debug")]
println!("wating for interupt clear {v}"); println!("wating for interupt clear {v}");
atomic_wait::wait(&SHARED.external_interupts, v); atomic_wait::wait(&SHARED.external_interupts, v);
v = (&SHARED.external_interupts).load(Acquire); v = (&SHARED.external_interupts).load(Acquire);
@@ -87,33 +89,40 @@ impl<'a> ApplicationHandler for App<'a> {
if enabled { if enabled {
wait_int(); wait_int();
} }
#[cfg(feature = "debug")]
print!("Keyboard event: ");
#[cfg(feature = "rich_keyboard")] #[cfg(feature = "rich_keyboard")]
{ {
let txt = key_event let kb0 = key_event
.logical_key .logical_key
.to_text() .to_text()
.unwrap_or("") .unwrap_or("")
.as_bytes() .as_bytes()
.into_iter() .into_iter()
.fold(0, |a, e| a << 8 | (*e as u32)); .fold(0, |a, e| a << 8 | (*e as u32));
SHARED.keyboard[0].store(txt, Relaxed); SHARED.keyboard[0].store(kb0, Relaxed);
let txt = key_event let kb1 = key_event
.key_without_modifiers() .key_without_modifiers()
.to_text() .to_text()
.unwrap_or("") .unwrap_or("")
.as_bytes() .as_bytes()
.into_iter() .into_iter()
.fold(0, |a, e| a << 8 | (*e as u32)); .fold(0, |a, e| a << 8 | (*e as u32));
SHARED.keyboard[1].store(txt, Relaxed); SHARED.keyboard[1].store(kb1, Relaxed);
SHARED.keyboard[2].store( let kb2 =
key_event.state.is_pressed() as u32 | ((key_event.repeat as u32) << 1), key_event.state.is_pressed() as u32 | ((key_event.repeat as u32) << 1);
Relaxed, SHARED.keyboard[2].store(kb2, Relaxed);
); #[cfg(feature = "debug")]
print!("{kb0} {kb1} {kb2}")
} }
SHARED.keyboard[3] let kb3 = key_event.physical_key.to_scancode().unwrap_or(0);
.store(key_event.physical_key.to_scancode().unwrap_or(0), Relaxed); #[cfg(feature = "debug")]
println!(" {kb3}");
SHARED.keyboard[3].store(kb3, Relaxed);
if enabled { if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release); (&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release);
#[cfg(feature = "debug")]
println!("wake due to keyboard event");
atomic_wait::wake_all(&SHARED.external_interupts); atomic_wait::wake_all(&SHARED.external_interupts);
} }
} }
@@ -141,7 +150,8 @@ impl<'a> ApplicationHandler for App<'a> {
} }
if enabled { if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release); (&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release);
println!("wake mouse move"); #[cfg(feature = "debug")]
println!("wake due mouse move");
atomic_wait::wake_all(&SHARED.external_interupts); atomic_wait::wake_all(&SHARED.external_interupts);
} }
} }
@@ -174,6 +184,7 @@ impl<'a> ApplicationHandler for App<'a> {
}; };
if enabled { if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release); (&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release);
#[cfg(feature = "debug")]
println!("wake mouse click"); println!("wake mouse click");
atomic_wait::wake_all(&SHARED.external_interupts); atomic_wait::wake_all(&SHARED.external_interupts);
} }
@@ -286,12 +297,120 @@ fn main() -> Result<(), Error> {
let program = args() let program = args()
.nth(1) .nth(1)
.expect("you must supply the exec name as the first argument"); .expect("you must supply the exec name as the first argument");
scope(|sc| { scope(|sc| {
sc.spawn(|| { sc.spawn(|| {
let mut simulation = Computer::new(program); let mut simulation = Computer::new(program);
#[cfg(not(feature = "debug"))]
loop { loop {
simulation.step(); simulation.step();
} }
#[cfg(feature = "debug")]
{
let mut input = 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 + 1,
simulation.regs[i + 1]
);
}
println!("SP: {:08x} PC: {:08x}", simulation.sp, simulation.pc);
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();
}
false
}
"r" | "run" => {
while !simulation.error {
simulation.step();
}
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)
);
false
}
Err(e) => {
println!("{e}");
false
}
}
} else {
true
}
}
"c" | "context" => false,
_ => {
println!("{HELP_MSG}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
} {}
}
}
}); });
#[allow(deprecated)] #[allow(deprecated)]
@@ -305,3 +424,12 @@ 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)
- repeat last step (yes, do no enter anything and press Enter)
";