many improvements

- fix a decode bug for C.ADDI16SP
- improve test suite (to test that bug)
- improve debugging
- clean up code
This commit is contained in:
nora 2025-03-14 20:58:18 +01:00
parent fdb4968c8b
commit b2c3c9fc80
8 changed files with 290 additions and 64 deletions

View file

@ -1,4 +1,4 @@
use crate::inst::{AmoOp, Inst, InstCode, IsCompressed};
use crate::inst::{AmoOp, DecodeError, Inst, IsCompressed};
use std::{
fmt::{Debug, Display},
io::Write,
@ -66,7 +66,7 @@ impl Memory {
#[derive(Debug)]
pub enum Status {
Trap(&'static str),
IllegalInstruction(InstCode, &'static str),
IllegalInstruction(DecodeError),
InvalidMemoryAccess(u32),
UnalignedPc(u32),
UnaligneMemoryAccess { addr: u32, required_align: u32 },
@ -74,6 +74,12 @@ pub enum Status {
Exit { code: i32 },
}
impl From<DecodeError> for Status {
fn from(value: DecodeError) -> Self {
Status::IllegalInstruction(value)
}
}
pub struct Emulator {
pub mem: Memory,
pub xreg: [u32; 32],
@ -87,6 +93,7 @@ pub struct Emulator {
pub is_breaking: bool,
pub debug: bool,
pub break_pc: u32,
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> Result<(), Status>>,
}
@ -219,7 +226,6 @@ impl Emulator {
// TODO: add some auxv for the poor process.
self.mem.store_u32(next_word(), 0)?;
Ok(())
}
@ -238,16 +244,16 @@ impl Emulator {
fn step(&mut self) -> Result<(), Status> {
let code = self.mem.load_u32(self.pc)?;
let (inst, was_compressed) = Inst::decode(code)?;
if self.is_breaking {
self.debug_interactive();
if self.debug {
print!("0x{:x} ", self.pc);
}
let (inst, was_compressed) = Inst::decode(code)?;
if self.debug {
println!(
"0x{:x} {} (sp: {}) {inst:?}",
self.pc,
"{} (sp: {}) {inst:?}",
match was_compressed {
IsCompressed::Yes => "C",
IsCompressed::No => " ",
@ -256,6 +262,14 @@ impl Emulator {
);
}
if self.pc == self.break_pc {
self.is_breaking = true;
}
if self.is_breaking {
self.debug_interactive();
}
let next_pc = self.pc.wrapping_add(match was_compressed {
IsCompressed::Yes => 2,
IsCompressed::No => 4,
@ -515,8 +529,8 @@ impl Emulator {
std::io::stdout().flush().unwrap();
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
let input = input.trim();
match input {
let mut input = input.trim().split_whitespace();
match input.next().unwrap_or_default() {
"c" => {
self.is_breaking = false;
return;
@ -525,7 +539,43 @@ impl Emulator {
return;
}
"q" => std::process::exit(0),
"p" => {
"m" => {
let Some(size) = input.next() else {
eprintln!("require b/h/w argument for m command");
continue;
};
let Some(addr) = input.next() else {
eprintln!("require address argument for m command");
continue;
};
let Ok(addr) = (if let Some(addr) = addr.strip_prefix("0x") {
u32::from_str_radix(addr, 16)
} else {
u32::from_str_radix(&addr, 10)
}) else {
eprintln!("invalid address");
continue;
};
let value = match size {
"w" => self.mem.load_u32(addr),
"h" => self.mem.load_u16(addr).map(Into::into),
"b" => self.mem.load_u8(addr).map(Into::into),
_ => {
eprintln!("require b/h/w argument for m command");
continue;
}
};
let value = match value {
Ok(v) => v,
Err(e) => {
eprintln!("failed to load value: {e:?}");
continue;
}
};
println!("{value} (0x{value:x})");
}
"r" => {
let format_value = |v: u32| {
if v == 0 {
format!("{:0>8x}", v.black())
@ -626,7 +676,8 @@ impl Emulator {
_ => println!(
"commands:
- ?: help
- p: print registers
- r: print registers
- m <b|h|w> <addr>: read a byte/half/word of memory at an address
- c: continue until next breakpoint
- s: step one instruction
- q: quit"