Debugging mode
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1508,6 +1508,7 @@ name = "simu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"atomic-wait",
|
||||
"parse_int",
|
||||
"pixels",
|
||||
"winit",
|
||||
"winit_input_helper",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
149
simu/src/cpu.rs
149
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<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),
|
||||
|
||||
150
simu/src/main.rs
150
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::<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)
|
||||
";
|
||||
|
||||
Reference in New Issue
Block a user