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"
dependencies = [
"atomic-wait",
"parse_int",
"pixels",
"winit",
"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
- rgba: Écran au format RGBA plutot que 0BGR
- rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO
- debug: repl similaire a la version python du simulateur
### instruction spéciale:
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"
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
winit_input_helper = "0.17.0"
parse_int = { version = "0.9.0", optional = true }
[features]
div_mul = []
rgba = []
rich_keyboard = []
debug = ["dep:parse_int"]

View File

@@ -1,3 +1,5 @@
#[cfg(feature = "debug")]
use std::collections::HashMap;
use std::{
hint::{likely, unlikely, unreachable_unchecked},
io::Read,
@@ -5,8 +7,7 @@ use std::{
ops::{Index, IndexMut},
process::exit,
sync::atomic::AtomicU32,
thread::sleep,
time::{self, Duration, Instant},
time::{self, Instant},
};
pub(crate) struct SharedState {
@@ -44,7 +45,8 @@ enum Cond {
Ifule,
}
enum InteruptState {
#[derive(Debug)]
pub enum InteruptState {
Disabled,
Enabled,
Serving(InteruptKind, u8),
@@ -52,7 +54,8 @@ enum InteruptState {
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
#[repr(u32)]
enum InteruptKind {
#[allow(unused)] //some are there only based on features
pub(crate) enum InteruptKind {
MMIO = 1,
Swi = 2,
DivByZero = 3,
@@ -243,13 +246,98 @@ impl TryFrom<u32> for Instruction {
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 {
creation: Instant,
ram: Box<[u32; 0x01000000 / 4]>,
regs: [u32; 16],
pc: usize,
sp: usize,
interupts: InteruptState,
pub(crate) ram: Box<[u32; 0x01000000 / 4]>,
pub(crate) regs: [u32; 16],
pub(crate) pc: usize,
pub(crate) sp: usize,
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 {
@@ -279,18 +367,32 @@ impl Computer {
pc: 0,
sp: 0,
interupts: InteruptState::Disabled,
#[cfg(feature = "debug")]
error: false,
#[cfg(feature = "debug")]
book: HashMap::new(),
};
let mut buf = String::new();
std::fs::File::open(filename)
.unwrap()
.read_to_string(&mut buf)
.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) {
Ok(val) => new.ram[i] = val,
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
}
#[inline(always)]
@@ -477,9 +579,19 @@ impl Computer {
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if addr == 0 {
println!("awaiting interupt...");
#[cfg(feature = "debug")]
{
match self.interupts {
InteruptState::Disabled => {
println!("program terminated");
self.error = true;
return;
}
_ => (),
}
println!("awaiting interupt...");
}
atomic_wait::wait(&SHARED.external_interupts, 0);
println!("waking!")
}
self.pc = (addr + self.pc as u32) as usize;
}
@@ -490,6 +602,13 @@ impl Computer {
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if unlikely(addr == 0) {
#[cfg(feature = "debug")]
{
println!("program terminated");
self.error = true;
return;
}
#[cfg(not(feature = "debug"))]
exit(0);
}
self.pc = (addr + self.pc as u32) as usize;
@@ -607,6 +726,12 @@ impl Computer {
match self.interupts {
InteruptState::Disabled => {
println!("Illegal Instruction whith interupt disabled {kind:?} {arg:?}");
#[cfg(feature = "debug")]
{
self.error = true;
return;
}
#[cfg(not(feature = "debug"))]
exit(1);
}
InteruptState::Enabled => self.interupts = InteruptState::Serving(kind, 0),

View File

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