summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml2
-rw-r--r--src/bin/br.rs85
-rw-r--r--src/debug.rs79
-rw-r--r--src/emulators.rs4
-rw-r--r--src/emulators/graphical_emulator.rs2
-rw-r--r--src/emulators/headless_emulator.rs2
7 files changed, 143 insertions, 35 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0b35118..0f55e51 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -62,8 +62,8 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bedrock-asm"
-version = "4.0.0"
-source = "git+git://benbridle.com/bedrock_asm?tag=v4.0.0#db2c08220e95729c1880953d00aaded693dc43c7"
+version = "4.0.1"
+source = "git+git://benbridle.com/bedrock_asm?tag=v4.0.1#2cd0c86659479774d092de727e0f0c31e27e49f2"
dependencies = [
"vagabond",
"xflags",
diff --git a/Cargo.toml b/Cargo.toml
index f65ef48..72b6127 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
description = "Emulator for running Bedrock programs"
[dependencies]
-bedrock-asm = { git = "git://benbridle.com/bedrock_asm", tag = "v4.0.0" }
+bedrock-asm = { git = "git://benbridle.com/bedrock_asm", tag = "v4.0.1" }
bedrock-core = { git = "git://benbridle.com/bedrock_core", tag = "v5.0.0" }
phosphor = { git = "git://benbridle.com/phosphor", tag = "v3.1.0" }
geometry = { git = "git://benbridle.com/geometry", tag = "v1.0.0" }
diff --git a/src/bin/br.rs b/src/bin/br.rs
index bf3befe..e96df54 100644
--- a/src/bin/br.rs
+++ b/src/bin/br.rs
@@ -43,13 +43,18 @@ fn main_run(args: Run) {
unsafe { VERBOSE = true; }
}
- let bytecode = load_bytecode(args.program.as_ref().map(|p| p.as_path()));
+ let program_path = args.program.as_ref().map(|p| p.as_path());
+ let Bytecode { bytes: bytecode, path } = load_bytecode(program_path);
+ let symbols_path = path.as_ref().map(|p| {
+ let mut path = p.to_path_buf();
+ path.set_extension("br.sym");
+ path
+ });
+
let metadata = parse_metadata(&bytecode);
if metadata.is_none() {
verbose!("Could not read program metadata");
}
- // TODO: Load and parse symbols file into debug state, use nearest symbol
- // path when debugging.
let mut config = EmulatorConfig {
dimensions: ScreenDimensions::ZERO,
@@ -60,6 +65,7 @@ fn main_run(args: Run) {
initial_transmission: None,
decode_stdin: args.decode_stdin,
encode_stdout: args.encode_stdout,
+ symbols_path,
};
let phosphor = Phosphor::new();
@@ -106,14 +112,18 @@ fn main_run(args: Run) {
std::process::exit(0);
}
-fn load_bytecode(path: Option<&Path>) -> Vec<u8> {
+fn load_bytecode(path: Option<&Path>) -> Bytecode {
// TODO: Etch file location into bytecode.
if let Some(path) = path {
if let Ok(bytecode) = load_bytecode_from_file(path) {
- verbose!("Loaded program from {path:?} ({} bytes)", bytecode.len());
+ let length = bytecode.bytes.len();
+ let path = bytecode.path();
+ verbose!("Loaded program from {path:?} ({length} bytes)");
return bytecode;
- } else if let Some((bytecode, path)) = load_bytecode_from_bedrock_path(path) {
- verbose!("Loaded program from {path:?} ({} bytes)", bytecode.len());
+ } else if let Some(bytecode) = load_bytecode_from_bedrock_path(path) {
+ let length = bytecode.bytes.len();
+ let path = bytecode.path();
+ verbose!("Loaded program from {path:?} ({length} bytes)");
return bytecode;
} else {
eprintln!("Could not read program from {path:?}, exiting");
@@ -122,7 +132,8 @@ fn load_bytecode(path: Option<&Path>) -> Vec<u8> {
} else {
verbose!("Reading program from standard input...");
if let Ok(bytecode) = load_bytecode_from_stdin() {
- verbose!("Loaded program from standard input ({} bytes)", bytecode.len());
+ let length = bytecode.bytes.len();
+ verbose!("Loaded program from standard input ({length} bytes)");
return bytecode;
} else {
eprintln!("Could not read program from standard input, exiting");
@@ -131,13 +142,8 @@ fn load_bytecode(path: Option<&Path>) -> Vec<u8> {
}
}
-/// Attempt to load bytecode from a file path.
-fn load_bytecode_from_file(path: &Path) -> Result<Vec<u8>, std::io::Error> {
- load_bytecode_from_readable_source(std::fs::File::open(path)?)
-}
-
/// Attempt to load bytecode from a directory in the BEDROCK_PATH environment variable.
-fn load_bytecode_from_bedrock_path(path: &Path) -> Option<(Vec<u8>, PathBuf)> {
+fn load_bytecode_from_bedrock_path(path: &Path) -> Option<Bytecode> {
if path.is_relative() && path.components().count() == 1 {
for base_path in std::env::var("BEDROCK_PATH").ok()?.split(':') {
let mut base_path = PathBuf::from(base_path);
@@ -145,29 +151,48 @@ fn load_bytecode_from_bedrock_path(path: &Path) -> Option<(Vec<u8>, PathBuf)> {
base_path.push(path);
verbose!("Attempting to load program from {base_path:?}");
if let Ok(bytecode) = load_bytecode_from_file(&base_path) {
- return Some((bytecode, base_path));
+ return Some(bytecode);
}
if path.extension().is_some() { continue; }
base_path.set_extension("br");
verbose!("Attempting to load program from {base_path:?}");
if let Ok(bytecode) = load_bytecode_from_file(&base_path) {
- return Some((bytecode, base_path));
+ return Some(bytecode);
}
}
}
return None;
}
+/// Attempt to load bytecode from a file path.
+fn load_bytecode_from_file(path: &Path) -> Result<Bytecode, std::io::Error> {
+ load_bytecode_from_readable_source(std::fs::File::open(path)?, Some(path))
+}
+
/// Attempt to load bytecode from standard input.
-fn load_bytecode_from_stdin() -> Result<Vec<u8>, std::io::Error> {
- load_bytecode_from_readable_source(std::io::stdin())
+fn load_bytecode_from_stdin() -> Result<Bytecode, std::io::Error> {
+ load_bytecode_from_readable_source(std::io::stdin(), None)
}
/// Attempt to load bytecode from a source that implements std::io::Read.
-fn load_bytecode_from_readable_source(source: impl Read) -> Result<Vec<u8>, std::io::Error> {
- let mut bytecode = Vec::<u8>::new();
- source.take(65536).read_to_end(&mut bytecode)?;
- return Ok(bytecode);
+fn load_bytecode_from_readable_source(source: impl Read, path: Option<&Path>) -> Result<Bytecode, std::io::Error> {
+ let mut bytes = Vec::<u8>::new();
+ source.take(65536).read_to_end(&mut bytes)?;
+ return Ok(Bytecode { bytes, path: path.map(|p| p.to_path_buf()) });
+}
+
+struct Bytecode {
+ bytes: Vec<u8>,
+ path: Option<PathBuf>,
+}
+
+impl Bytecode {
+ fn path(&self) -> String {
+ match &self.path {
+ Some(path) => path.as_os_str().to_string_lossy().to_string(),
+ None => String::from("<unknown>"),
+ }
+ }
}
@@ -248,7 +273,19 @@ fn main_asm(args: Asm) {
// -----------------------------------------------------------------------
// GENERATE symbols file and bytecode
let bytecode = generate_bytecode(&mut semantic_tokens);
- // let symbols = generate_symbols_file(&semantic_tokens);
+
+ if args.symbols && !args.check {
+ if let Some(path) = &args.output {
+ let mut symbols_path = path.to_path_buf();
+ symbols_path.set_extension("br.sym");
+ let symbols = generate_symbols_file(&semantic_tokens);
+ if let Err(err) = std::fs::write(&symbols_path, symbols) {
+ verbose!("Could not write to symbols path {symbols_path:?}: ({err:?}).");
+ } else {
+ verbose!("Saved debug symbols to {symbols_path:?}");
+ }
+ }
+ }
let length = bytecode.len();
let percentage = (length as f32 / 65536.0 * 100.0).round() as u16;
@@ -361,6 +398,8 @@ xflags::xflags! {
optional --check
/// Only return resolved source code.
optional --resolve
+ /// Generate debug symbols with file extension '.br.sym'
+ optional --symbols
}
}
}
diff --git a/src/debug.rs b/src/debug.rs
index d19dbec..1593d9d 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -1,23 +1,28 @@
use bedrock_core::*;
+use std::path::Path;
use std::time::Instant;
-macro_rules! yellow { () => { eprint!("\x1b[33m") };}
-macro_rules! normal { () => { eprint!("\x1b[0m" ) };}
+
+const NORMAL: &str = "\x1b[0m";
+const YELLOW: &str = "\x1b[33m";
+const BLUE: &str = "\x1b[34m";
pub struct DebugState {
pub enabled: bool,
last_cycle: usize,
last_mark: Instant,
+ symbols: DebugSymbols,
}
impl DebugState {
- pub fn new(enabled: bool) -> Self {
+ pub fn new<P: AsRef<Path>>(enabled: bool, symbols_path: Option<P>) -> Self {
Self {
enabled,
last_cycle: 0,
last_mark: Instant::now(),
+ symbols: DebugSymbols::from_path_opt(symbols_path),
}
}
@@ -37,7 +42,12 @@ impl DebugState {
debug_stack(&core.wst, 0x10);
eprint!("RST: ");
debug_stack(&core.rst, 0x10);
-
+ // Print information about the current symbol.
+ if let Some(symbol) = self.symbols.for_address(core.mem.pc) {
+ eprint!("SYM: {BLUE}@{}{NORMAL}", symbol.name);
+ if let Some(location) = &symbol.location { eprint!(" {location}"); }
+ eprintln!();
+ }
self.last_cycle = core.cycle;
self.last_mark = Instant::now();
}
@@ -46,9 +56,64 @@ impl DebugState {
fn debug_stack(stack: &Stack, len: usize) {
for i in 0..len {
- if i == stack.sp as usize { yellow!(); }
+ if i == stack.sp as usize { eprint!("{YELLOW}"); }
eprint!("{:02x} ", stack.mem[i]);
}
- normal!();
- eprintln!();
+ eprintln!("{NORMAL}");
+}
+
+
+struct DebugSymbols {
+ symbols: Vec<DebugSymbol>
+}
+
+impl DebugSymbols {
+ /// Load debug symbols from a symbols file.
+ pub fn from_path_opt<P: AsRef<Path>>(path: Option<P>) -> Self {
+ let mut symbols = Vec::new();
+ if let Some(path) = path {
+ if let Ok(string) = std::fs::read_to_string(path) {
+ for line in string.lines() {
+ if let Some(symbol) = DebugSymbol::from_line(line) {
+ symbols.push(symbol);
+ }
+ }
+ }
+ }
+ symbols.sort_by_key(|s| s.address);
+ Self { symbols }
+ }
+
+ pub fn for_address(&self, address: u16) -> Option<&DebugSymbol> {
+ if self.symbols.is_empty() { return None; }
+ let symbol = match self.symbols.binary_search_by_key(&address, |s| s.address) {
+ Ok(index) => self.symbols.get(index)?,
+ Err(index) => self.symbols.get(index.checked_sub(1)?)?,
+ };
+ Some(&symbol)
+ }
+}
+
+struct DebugSymbol {
+ address: u16,
+ name: String,
+ location: Option<String>,
+}
+
+impl DebugSymbol {
+ pub fn from_line(line: &str) -> Option<Self> {
+ if let Some((address, line)) = line.split_once(' ') {
+ let address = u16::from_str_radix(address, 16).ok()?;
+ if let Some((name, location)) = line.split_once(' ') {
+ let name = name.to_string();
+ let location = Some(location.to_string());
+ Some( DebugSymbol { address, name, location } )
+ } else {
+ let name = line.to_string();
+ Some( DebugSymbol { address, name, location: None } )
+ }
+ } else {
+ None
+ }
+ }
}
diff --git a/src/emulators.rs b/src/emulators.rs
index fcebe1d..56f7181 100644
--- a/src/emulators.rs
+++ b/src/emulators.rs
@@ -8,6 +8,8 @@ use crate::*;
use phosphor::Colour;
+use std::path::PathBuf;
+
pub enum EmulatorSignal {
Promote,
@@ -25,4 +27,6 @@ pub struct EmulatorConfig {
pub initial_transmission: Option<Vec<u8>>,
pub decode_stdin: bool,
pub encode_stdout: bool,
+
+ pub symbols_path: Option<PathBuf>,
}
diff --git a/src/emulators/graphical_emulator.rs b/src/emulators/graphical_emulator.rs
index a183262..bc6aaeb 100644
--- a/src/emulators/graphical_emulator.rs
+++ b/src/emulators/graphical_emulator.rs
@@ -115,7 +115,7 @@ impl GraphicalEmulator {
let devices = GraphicalDeviceBus::new(config);
Self {
br: BedrockEmulator::new(devices),
- debug: DebugState::new(debug),
+ debug: DebugState::new(debug, config.symbols_path.as_ref()),
dimensions: config.dimensions,
fullscreen: config.fullscreen,
scale: config.scale,
diff --git a/src/emulators/headless_emulator.rs b/src/emulators/headless_emulator.rs
index 418ea9c..3f54f6a 100644
--- a/src/emulators/headless_emulator.rs
+++ b/src/emulators/headless_emulator.rs
@@ -89,7 +89,7 @@ impl HeadlessEmulator {
pub fn new(config: &EmulatorConfig, debug: bool) -> Self {
Self {
br: BedrockEmulator::new(HeadlessDeviceBus::new(config)),
- debug: DebugState::new(debug),
+ debug: DebugState::new(debug, config.symbols_path.as_ref()),
}
}