Change to latest git comit of elf crate, add basic symbol support to run, and format code

This commit is contained in:
pjht 2022-10-12 08:04:27 -05:00
parent 1ecd6083a6
commit f4f8890a5d
3 changed files with 277 additions and 257 deletions

3
Cargo.lock generated
View File

@ -180,8 +180,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "elf" name = "elf"
version = "0.0.12" version = "0.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/cole14/rust-elf?rev=a4a747071316b1a900962dd83cbf2ab4ca4665fa#a4a747071316b1a900962dd83cbf2ab4ca4665fa"
checksum = "4b9c86cae88373e32c872967c296dcdd3b25771dcc7da1c4ba060b4a68fd8db7"
[[package]] [[package]]
name = "errno" name = "errno"

View File

@ -8,7 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
bitvec = "1.0.0" bitvec = "1.0.0"
derive-try-from-primitive = "1.0.0" derive-try-from-primitive = "1.0.0"
elf = "0.0.12" elf = { git = "https://github.com/cole14/rust-elf", rev = "a4a747071316b1a900962dd83cbf2ab4ca4665fa" }
human-repr = { version = "1.0.1", features = ["iec", "space"] } human-repr = { version = "1.0.1", features = ["iec", "space"] }
inventory = "0.3.1" inventory = "0.3.1"
itertools = "0.10.5" itertools = "0.10.5"

View File

@ -14,7 +14,7 @@ use crate::{
m68k::{BusError, M68K}, m68k::{BusError, M68K},
}; };
use disas::DisassemblyError; use disas::DisassemblyError;
use elf::types::Symbol; use elf::symbol::Symbol;
use itertools::Itertools; use itertools::Itertools;
use parse_int::parse; use parse_int::parse;
use reedline_repl_rs::{ use reedline_repl_rs::{
@ -22,7 +22,7 @@ use reedline_repl_rs::{
Error as ReplError, Repl, Error as ReplError, Repl,
}; };
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::{convert::TryFrom, fmt::Display, fs, num::ParseIntError, process}; use std::{convert::TryFrom, error, fmt::Display, fs, num::ParseIntError, process};
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
@ -33,6 +33,13 @@ enum Error {
InvalidPeekSize, InvalidPeekSize,
Disassembly(DisassemblyError<BusError>), Disassembly(DisassemblyError<BusError>),
Misc(&'static str), Misc(&'static str),
MiscDyn(Box<dyn error::Error>),
}
impl From<Box<dyn error::Error>> for Error {
fn from(v: Box<dyn error::Error>) -> Self {
Self::MiscDyn(v)
}
} }
impl From<DisassemblyError<BusError>> for Error { impl From<DisassemblyError<BusError>> for Error {
@ -69,6 +76,7 @@ impl Display for Error {
Self::InvalidPeekSize => f.write_str("Invalid peek size"), Self::InvalidPeekSize => f.write_str("Invalid peek size"),
Self::Disassembly(e) => e.fmt(f), Self::Disassembly(e) => e.fmt(f),
Self::Misc(s) => f.write_str(s), Self::Misc(s) => f.write_str(s),
Self::MiscDyn(e) => e.fmt(f),
} }
} }
} }
@ -184,269 +192,282 @@ fn main() -> Result<(), ReplError> {
Err(e) => panic!("{}", e), Err(e) => panic!("{}", e),
}; };
} }
Repl::<_, Error>::new(EmuState {cpu: M68K::new(backplane), symbols: Vec::new()}) Repl::<_, Error>::new(EmuState {
.with_name("68KEmu") cpu: M68K::new(backplane),
.with_version("0.1.0") symbols: Vec::new(),
.with_banner("68K Backplane Computer Emulator") })
.with_description("68K Backplane Computer Emulator") .with_name("68KEmu")
.with_command( .with_version("0.1.0")
Command::new("card") .with_banner("68K Backplane Computer Emulator")
.trailing_var_arg(true) .with_description("68K Backplane Computer Emulator")
.arg( .with_command(
Arg::new("num") Command::new("card")
.required(true) .trailing_var_arg(true)
.help("The card number to send the command to"), .arg(
) Arg::new("num")
.arg( .required(true)
Arg::new("args") .help("The card number to send the command to"),
.required(true) )
.multiple_values(true) .arg(
.takes_value(true), Arg::new("args")
) .required(true)
.about("Send a command to a card"), .multiple_values(true)
|args, state| { .takes_value(true),
let num = args.get_one::<String>("num").unwrap().parse::<u8>()?; )
state.cpu.bus_mut() .about("Send a command to a card"),
.cards_mut() |args, state| {
.get_mut(num as usize) let num = args.get_one::<String>("num").unwrap().parse::<u8>()?;
.ok_or(Error::InvalidCard(num))? state
.cmd( .cpu
&args .bus_mut()
.get_many::<String>("args") .cards_mut()
.unwrap() .get_mut(num as usize)
.map(String::as_str) .ok_or(Error::InvalidCard(num))?
.collect_vec(), .cmd(
); &args
Ok(None) .get_many::<String>("args")
}, .unwrap()
) .map(String::as_str)
.with_command( .collect_vec(),
Command::new("ls").about("List the cards in the system"), );
|_, state| { Ok(None)
#[allow(unstable_name_collisions)] },
Ok(Some( )
state.cpu.bus_mut() .with_command(
.cards() Command::new("ls").about("List the cards in the system"),
.iter() |_, state| {
.enumerate() #[allow(unstable_name_collisions)]
.map(|(i, card)| format!("Card {i}: {card}")) Ok(Some(
.intersperse('\n'.to_string()) state
.collect(), .cpu
)) .bus_mut()
}, .cards()
) .iter()
.with_command( .enumerate()
Command::new("regs").about("Show CPU registers"), .map(|(i, card)| format!("Card {i}: {card}"))
|_, state| Ok(Some(format!("{}", state.cpu))), .intersperse('\n'.to_string())
) .collect(),
.with_command( ))
Command::new("step") },
.arg( )
Arg::new("count") .with_command(
.takes_value(true) Command::new("regs").about("Show CPU registers"),
.help("Count of instructions to step by. Defaults to 1"), |_, state| Ok(Some(format!("{}", state.cpu))),
) )
.arg( .with_command(
Arg::new("print_ins") Command::new("step")
.long("print_ins") .arg(
.short('i') Arg::new("count")
.action(ArgAction::SetTrue) .takes_value(true)
.help("Print instructions") .help("Count of instructions to step by. Defaults to 1"),
) )
.arg( .arg(
Arg::new("print_regs") Arg::new("print_ins")
.long("print_regs") .long("print_ins")
.short('r') .short('i')
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.help("Print ending registers") .help("Print instructions"),
) )
.about("Step the CPU"), .arg(
|args, state| { Arg::new("print_regs")
let count = .long("print_regs")
parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?; .short('r')
let mut out = String::new(); .action(ArgAction::SetTrue)
for _ in 0..count { .help("Print ending registers"),
if state.cpu.stopped { )
out += &format!("CPU stopped at PC {:#x}\n", state.cpu.pc()); .about("Step the CPU"),
break; |args, state| {
} let count = parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?;
if args.get_flag("print_ins") { let mut out = String::new();
let pc = state.cpu.pc(); for _ in 0..count {
out += &disas_fmt(&mut state.cpu, pc).0; if state.cpu.stopped {
} out += &format!("CPU stopped at PC {:#x}\n", state.cpu.pc());
state.cpu.step(); break;
} }
if args.get_flag("print_regs") { if args.get_flag("print_ins") {
out += &format!("{}\n", state.cpu); let pc = state.cpu.pc();
} out += &disas_fmt(&mut state.cpu, pc).0;
if out.is_empty() {
Ok(None)
} else {
out.pop(); // Remove trailing newline
Ok(Some(out))
}
},
)
.with_command(
Command::new("run")
.arg(
Arg::new("stop_addr")
.takes_value(true)
.help("Optional address to stop execution at. Works as a breakpoint only for this run")
)
.arg(
Arg::new("print_ins")
.long("print_ins")
.short('p')
.action(ArgAction::SetTrue)
.help("Print all executed instructions")
)
.about("Run the CPU"),
|args, state| {
let mut out = String::new();
while !state.cpu.stopped {
let stop_addr = args
.get_one::<String>("stop_addr")
.map(|s| parse::<u32>(s))
.transpose()?;
if stop_addr.map(|a| state.cpu.pc() == a).unwrap_or(false) {
break;
}
if args.get_flag("print_ins") {
let pc = state.cpu.pc();
out += &disas_fmt(&mut state.cpu, pc).0;
}
state.cpu.step();
} }
state.cpu.step();
}
if args.get_flag("print_regs") {
out += &format!("{}\n", state.cpu); out += &format!("{}\n", state.cpu);
let pc = state.cpu.pc(); }
out += &disas_fmt(&mut state.cpu, pc).0; if out.is_empty() {
out.pop(); // Remove trailing newline
Ok(Some(out))
},
)
.with_command(
Command::new("reset").about("Reset the cards and CPU, in that order"),
|_, state| {
for card in state.cpu.bus_mut().cards_mut() {
card.reset();
}
state.cpu.reset();
Ok(None) Ok(None)
}, } else {
)
.with_command(
Command::new("peek")
.arg(Arg::new("count").short('c').takes_value(true))
.arg(Arg::new("fmt").short('f').required(true).takes_value(true))
.arg(Arg::new("addr").required(true))
.about("Peek a memory address"),
|args, state| {
let fmt_str = args.get_one::<String>("fmt").unwrap();
if fmt_str.len() != 2 {
return Err(Error::Misc("Peek format length must be 2"));
}
let fmt = PeekFormat::try_from(fmt_str.chars().next().unwrap())?;
let size = PeekSize::try_from(fmt_str.chars().nth(1).unwrap())?;
let count =
parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?;
let addr = parse::<u32>(args.get_one::<String>("addr").unwrap())?;
let mut data = Vec::new();
let bus = state.cpu.bus_mut();
for i in 0..count {
match size {
PeekSize::Byte => data.push(bus.read_byte(addr + i)? as u32),
PeekSize::Word => data.push(bus.read_word(addr + (i * 2))? as u32),
PeekSize::LongWord => data.push(
(bus.read_word(addr + (i * 4))? as u32) << 16
| (bus.read_word(addr + (i * 4) + 2)? as u32),
),
}
}
#[allow(unstable_name_collisions)]
Ok(Some(
data.chunks(size.chunk_size())
.enumerate()
.map(|(i, c)| {
format!(
"0x{:x}: ",
addr + (size.chunk_size() * size.byte_count() * i) as u32
) + &c
.iter()
.map(|d| fmt.format(*d, size))
.intersperse(" ".to_string())
.collect::<String>()
})
.intersperse("\n".to_string())
.collect::<String>(),
))
},
)
.with_command(
Command::new("disas")
.arg(
Arg::new("addr")
.help("Address to start disassembly at. Defaults to current PC"),
)
.arg(
Arg::new("count")
.short('c')
.takes_value(true)
.help("Count of instructions to disassemble. Defaults to 1"),
)
.about("Disassemble a region of memory"),
|args, state| {
let mut addr = args
.get_one::<String>("addr")
.map_or(Ok(state.cpu.pc()), |s| parse::<u32>(s))?;
let count =
parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?;
let mut out = String::new();
for _ in 0..count {
let (fmt, res) = disas_fmt(&mut state.cpu, addr);
out += &fmt;
match res {
Ok(new_addr) => {
addr = new_addr;
}
Err(_) => {
break;
}
}
}
out.pop(); // Remove trailing newline out.pop(); // Remove trailing newline
Ok(Some(out)) Ok(Some(out))
}, }
) },
.with_command( )
Command::new("sym") .with_command(
.arg( Command::new("run")
Arg::new("file") .arg(Arg::new("stop_addr").takes_value(true).help(
"Optional address to stop execution at. Works as a breakpoint only for this run",
))
.arg(
Arg::new("print_ins")
.long("print_ins")
.short('p')
.action(ArgAction::SetTrue)
.help("Print all executed instructions"),
)
.about("Run the CPU"),
|args, state| {
let mut out = String::new();
while !state.cpu.stopped {
let stop_addr = args.get_one::<String>("stop_addr");
let stop_addr = stop_addr
.map(|s| {
parse::<u32>(s).or_else(|_| {
state
.symbols
.iter()
.find(|sym| &sym.name == s)
.map(|sym| sym.value as u32)
.ok_or(Error::Misc("No such symbol"))
})
})
.transpose()?;
if stop_addr.map(|a| state.cpu.pc() == a).unwrap_or(false) {
break;
}
if args.get_flag("print_ins") {
let pc = state.cpu.pc();
out += &disas_fmt(&mut state.cpu, pc).0;
}
state.cpu.step();
}
out += &format!("{}\n", state.cpu);
let pc = state.cpu.pc();
out += &disas_fmt(&mut state.cpu, pc).0;
out.pop(); // Remove trailing newline
Ok(Some(out))
},
)
.with_command(
Command::new("reset").about("Reset the cards and CPU, in that order"),
|_, state| {
for card in state.cpu.bus_mut().cards_mut() {
card.reset();
}
state.cpu.reset();
Ok(None)
},
)
.with_command(
Command::new("peek")
.arg(Arg::new("count").short('c').takes_value(true))
.arg(Arg::new("fmt").short('f').required(true).takes_value(true))
.arg(Arg::new("addr").required(true))
.about("Peek a memory address"),
|args, state| {
let fmt_str = args.get_one::<String>("fmt").unwrap();
if fmt_str.len() != 2 {
return Err(Error::Misc("Peek format length must be 2"));
}
let fmt = PeekFormat::try_from(fmt_str.chars().next().unwrap())?;
let size = PeekSize::try_from(fmt_str.chars().nth(1).unwrap())?;
let count = parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?;
let addr = parse::<u32>(args.get_one::<String>("addr").unwrap())?;
let mut data = Vec::new();
let bus = state.cpu.bus_mut();
for i in 0..count {
match size {
PeekSize::Byte => data.push(bus.read_byte(addr + i)? as u32),
PeekSize::Word => data.push(bus.read_word(addr + (i * 2))? as u32),
PeekSize::LongWord => data.push(
(bus.read_word(addr + (i * 4))? as u32) << 16
| (bus.read_word(addr + (i * 4) + 2)? as u32),
),
}
}
#[allow(unstable_name_collisions)]
Ok(Some(
data.chunks(size.chunk_size())
.enumerate()
.map(|(i, c)| {
format!(
"0x{:x}: ",
addr + (size.chunk_size() * size.byte_count() * i) as u32
) + &c
.iter()
.map(|d| fmt.format(*d, size))
.intersperse(" ".to_string())
.collect::<String>()
})
.intersperse("\n".to_string())
.collect::<String>(),
))
},
)
.with_command(
Command::new("disas")
.arg(Arg::new("addr").help("Address to start disassembly at. Defaults to current PC"))
.arg(
Arg::new("count")
.short('c')
.takes_value(true)
.help("Count of instructions to disassemble. Defaults to 1"),
)
.about("Disassemble a region of memory"),
|args, state| {
let mut addr = args
.get_one::<String>("addr")
.map_or(Ok(state.cpu.pc()), |s| parse::<u32>(s))?;
let count = parse::<u32>(args.get_one::<String>("count").map_or("1", String::as_str))?;
let mut out = String::new();
for _ in 0..count {
let (fmt, res) = disas_fmt(&mut state.cpu, addr);
out += &fmt;
match res {
Ok(new_addr) => {
addr = new_addr;
}
Err(_) => {
break;
}
}
}
out.pop(); // Remove trailing newline
Ok(Some(out))
},
)
.with_command(
Command::new("sym")
.arg(
Arg::new("file")
.required(true) .required(true)
.help("The ELF file to load symbols from"), .help("The ELF file to load symbols from"),
) )
.about("Load symbols from an ELF file"), .about("Load symbols from an ELF file"),
|args, state| { |args, state| {
let file = args let file = args.get_one::<String>("file").unwrap();
.get_one::<String>("file").unwrap(); let file = elf::File::open_path(file).map_err(<Box<dyn error::Error>>::from)?;
let file = elf::File::open_path(file).unwrap(); let symtab = file
let symtab = file.get_section(".symtab").ok_or(Error::Misc("Could not find symbol table section"))?; .get_section(".symtab")
let symbols = file.get_symbols(symtab).unwrap(); .ok_or(Error::Misc("Could not find symbol table section"))?;
state.symbols = symbols; let symbols = file
Ok(None) .get_symbols(&symtab)
}, .map_err(<Box<dyn error::Error>>::from)?;
) state.symbols = symbols;
.with_command(Command::new("quit") Ok(None)
},
)
.with_command(
Command::new("quit")
.visible_alias("q") .visible_alias("q")
.visible_alias("exit") .visible_alias("exit")
.about("Quit"), .about("Quit"),
|_, _| process::exit(0) |_, _| process::exit(0),
) )
// Visible aliases don't actually work, so fake it with hidden subcommands // Visible aliases don't actually work, so fake it with hidden subcommands
.with_command(Command::new("q").hide(true), |_, _| process::exit(0)) .with_command(Command::new("q").hide(true), |_, _| process::exit(0))
.with_command(Command::new("exit").hide(true), |_, _| process::exit(0)) .with_command(Command::new("exit").hide(true), |_, _| process::exit(0))
.run() .run()
} }
fn disas_fmt(cpu: &mut M68K, addr: u32) -> (String, Result<u32, DisassemblyError<BusError>>) { fn disas_fmt(cpu: &mut M68K, addr: u32) -> (String, Result<u32, DisassemblyError<BusError>>) {