diff --git a/Cargo.lock b/Cargo.lock index 7b71a93..c27e6fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,6 +1508,7 @@ name = "simu" version = "0.1.0" dependencies = [ "atomic-wait", + "parse_int", "pixels", "winit", "winit_input_helper", diff --git a/README.md b/README.md index c50b957..00761be 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/simu/Cargo.toml b/simu/Cargo.toml index eb6384a..3faa461 100644 --- a/simu/Cargo.toml +++ b/simu/Cargo.toml @@ -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"] diff --git a/simu/src/cpu.rs b/simu/src/cpu.rs index 4191ff0..ea7ce2f 100644 --- a/simu/src/cpu.rs +++ b/simu/src/cpu.rs @@ -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 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) -> 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, } impl Index 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::>().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), diff --git a/simu/src/main.rs b/simu/src/main.rs index abbcf54..e711ed7 100644 --- a/simu/src/main.rs +++ b/simu/src/main.rs @@ -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::(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) +";