mirror of
https://github.com/kaesaecracker/echse.git
synced 2025-09-04 15:07:16 +02:00
initial commit
This commit is contained in:
commit
5ad6baa30d
35 changed files with 2877 additions and 0 deletions
9
krabbeltier/Cargo.toml
Normal file
9
krabbeltier/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "krabbeltier"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap.workspace = true
|
||||
shellwords.workspace = true
|
||||
echse = { path = "../echse" }
|
214
krabbeltier/src/main.rs
Normal file
214
krabbeltier/src/main.rs
Normal file
|
@ -0,0 +1,214 @@
|
|||
use echse::machine::{Machine, MachineBuilder};
|
||||
use echse::parser::EiParser;
|
||||
use std::collections::HashSet;
|
||||
use std::io::{BufRead, StdinLock, Write};
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Cli {
|
||||
ei_file: String,
|
||||
#[arg(short, long, default_value_t = 2)]
|
||||
registers: usize,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
#[command(
|
||||
disable_help_flag = true,
|
||||
disable_version_flag = true,
|
||||
no_binary_name = true,
|
||||
bin_name = ""
|
||||
)]
|
||||
enum DebugCommand {
|
||||
#[command(visible_alias = "q")]
|
||||
/// exit the debugger
|
||||
Quit,
|
||||
|
||||
#[command(visible_alias = "p")]
|
||||
/// prints the current state
|
||||
Print {
|
||||
#[arg(value_enum, default_value_t = PrintMode::All)]
|
||||
mode: PrintMode,
|
||||
},
|
||||
|
||||
#[command(visible_alias = "s")]
|
||||
/// executes a single instruction
|
||||
Step,
|
||||
|
||||
#[command(visible_alias = "j")]
|
||||
/// set the instruction pointer
|
||||
Jump { ip: usize },
|
||||
|
||||
#[command(visible_alias = "b")]
|
||||
/// toggle breakpoints
|
||||
Break { ip: Option<usize> },
|
||||
|
||||
#[command(visible_alias = "bl")]
|
||||
BreakList,
|
||||
|
||||
/// continue running instructions
|
||||
#[command(visible_alias = "c")]
|
||||
Continue,
|
||||
|
||||
/// Write a value into the specified register
|
||||
#[command(visible_alias = "w")]
|
||||
Write {
|
||||
register: usize,
|
||||
#[arg(allow_negative_numbers = true)]
|
||||
value: isize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum, Default, Debug)]
|
||||
enum PrintMode {
|
||||
#[default]
|
||||
#[clap(alias = "a")]
|
||||
All,
|
||||
#[clap(alias = "r")]
|
||||
Registers,
|
||||
#[clap(alias = "i")]
|
||||
Instructions,
|
||||
}
|
||||
|
||||
struct App {
|
||||
machine: Machine,
|
||||
breakpoints: HashSet<usize>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub(crate) fn exec(&mut self) {
|
||||
println!("Welcome to the debugger. Run 'help' to see available commands.");
|
||||
|
||||
let mut stdin = std::io::stdin().lock();
|
||||
let mut input_buf = String::new();
|
||||
|
||||
loop {
|
||||
Self::read_command_line(&mut stdin, &mut input_buf);
|
||||
let command = shellwords::split(input_buf.trim()).map(clap::Parser::try_parse_from);
|
||||
|
||||
match command {
|
||||
Ok(Ok(command)) => {
|
||||
if !self.run_command(command) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
}
|
||||
Ok(Err(e)) => println!("{e}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn read_command_line(stdin: &mut StdinLock, input_buf: &mut String) {
|
||||
input_buf.clear();
|
||||
let mut stdout = std::io::stdout().lock();
|
||||
stdout.write_fmt(format_args!("> ")).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
drop(stdout);
|
||||
stdin.read_line(input_buf).unwrap();
|
||||
}
|
||||
|
||||
fn run_command(&mut self, command: DebugCommand) -> bool {
|
||||
match command {
|
||||
DebugCommand::Quit => return false,
|
||||
DebugCommand::Print { mode } => match mode {
|
||||
PrintMode::All => {
|
||||
println!("{}", self.machine);
|
||||
}
|
||||
PrintMode::Registers => {
|
||||
println!("{}", self.machine.registers);
|
||||
}
|
||||
PrintMode::Instructions => {
|
||||
println!("next instruction is {}", self.machine.ip);
|
||||
println!("{}", self.machine.instructions);
|
||||
}
|
||||
},
|
||||
DebugCommand::Step => {
|
||||
if self.machine.instructions.len() > self.machine.ip {
|
||||
println!(
|
||||
"{:03}: {}\n",
|
||||
self.machine.ip, self.machine.instructions[self.machine.ip]
|
||||
);
|
||||
self.machine.step();
|
||||
} else {
|
||||
println!("reached end of program\n");
|
||||
}
|
||||
}
|
||||
DebugCommand::Jump { ip } => {
|
||||
self.machine.ip = ip;
|
||||
println!("ip now at {ip}\n");
|
||||
}
|
||||
DebugCommand::Break { ip } => {
|
||||
let ip = ip.unwrap_or(self.machine.ip);
|
||||
if self.breakpoints.contains(&ip) {
|
||||
self.breakpoints.remove(&ip);
|
||||
println!("removed breakpoint at {ip}\n");
|
||||
} else {
|
||||
self.breakpoints.insert(ip);
|
||||
println!("added breakpoint at {ip}\n");
|
||||
}
|
||||
}
|
||||
DebugCommand::BreakList => {
|
||||
println!("current breakpoints: {:?}\n", self.breakpoints);
|
||||
}
|
||||
DebugCommand::Continue => {
|
||||
let mut count = 0;
|
||||
while self.machine.step() {
|
||||
count += 1;
|
||||
if self.breakpoints.contains(&self.machine.ip) {
|
||||
println!(
|
||||
"breakpoint hit before executing {:03}: {}",
|
||||
self.machine.ip, self.machine.instructions[self.machine.ip]
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"stepped {count} instructions, ip now at {}\n",
|
||||
self.machine.ip
|
||||
);
|
||||
}
|
||||
DebugCommand::Write { value, register } => {
|
||||
if self.machine.registers.len() <= register {
|
||||
eprintln!(
|
||||
"Cannot write to register r{register}, as this machine only has {}\n",
|
||||
self.machine.registers.len()
|
||||
);
|
||||
} else {
|
||||
self.machine.registers[register].write(value);
|
||||
println!("r{register}: {value}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Cli = clap::Parser::parse();
|
||||
|
||||
let parser = EiParser::from_file(&args.ei_file).unwrap();
|
||||
let instructions = match parser.parse() {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
parser
|
||||
.write_error_report(e, &mut std::io::stderr())
|
||||
.expect("failed to generate error report");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let machine = MachineBuilder::new()
|
||||
.with_registers(args.registers)
|
||||
.with_instructions(instructions)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut app = App {
|
||||
machine,
|
||||
breakpoints: HashSet::new(),
|
||||
};
|
||||
|
||||
app.exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue