summaryrefslogtreecommitdiff
path: root/src/bin/br/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/br/main.rs')
-rw-r--r--src/bin/br/main.rs217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/bin/br/main.rs b/src/bin/br/main.rs
new file mode 100644
index 0000000..81c5ec9
--- /dev/null
+++ b/src/bin/br/main.rs
@@ -0,0 +1,217 @@
+#![feature(path_add_extension)]
+
+mod config;
+mod load;
+pub use config::*;
+pub use load::*;
+
+use bedrock_asm::*;
+use bedrock_core::*;
+use bedrock_pc::*;
+use log::*;
+use switchboard::*;
+use phosphor::*;
+
+use std::cmp::{min, max};
+use std::num::NonZeroU32;
+
+
+fn main() {
+ let mut args = Switchboard::from_env();
+ if let Some("asm") = args.peek() {
+ args.pop();
+ assemble(args, "br asm");
+ }
+
+ // -----------------------------------------------------------------------
+
+ args.named("help").short('h');
+ args.named("version");
+ args.named("verbose").short('v');
+
+ if args.get("help").as_bool() {
+ print_help();
+ std::process::exit(0);
+ }
+ if args.get("version").as_bool() {
+ let name = env!("CARGO_PKG_NAME");
+ let version = env!("CARGO_PKG_VERSION");
+ eprintln!("{name} v{version}");
+ eprintln!("Written by Ben Bridle.");
+ std::process::exit(0);
+ }
+ if args.get("verbose").as_bool() {
+ log::set_log_level(log::LogLevel::Info);
+ }
+
+ args.positional("source");
+ args.named("debug").short('d');
+ args.named("mode").default("dynamic");
+
+ args.named("palette");
+ args.named("fullscreen").short('f');
+ args.named("show-cursor").short('c');
+ args.named("zoom").short('z').quick("3").default("1");
+ args.named("size").short('s');
+ args.named("decode-stdin").short('i');
+ args.named("encode-stdout").short('o');
+ args.raise_errors();
+
+ let source = args.get("source").as_path_opt();
+ let debug = args.get("debug").as_bool();
+ let mode = Mode::from_str(args.get("mode").as_str());
+ let palette = args.get("palette").as_str_opt().map(parse_palette);
+ let fullscreen = args.get("fullscreen").as_bool();
+ let show_cursor = args.get("show-cursor").as_bool();
+ let zoom_raw = min(10, max(1, args.get("zoom").as_u32()));
+ let zoom = unsafe { NonZeroU32::new_unchecked(zoom_raw) };
+ let dimensions = match args.get("size").as_str_opt() {
+ Some(string) => parse_dimensions(string),
+ None => DEFAULT_SCREEN_SIZE / (zoom_raw as u16),
+ };
+ let decode_stdin = args.get("decode-stdin").as_bool();
+ let encode_stdout = args.get("encode-stdout").as_bool();
+
+ // -----------------------------------------------------------------------
+
+ let LoadedProgram { bytecode, path } = load_program(source.as_ref());
+ let mut title = String::from("Bedrock program");
+ let mut icon = None;
+
+ if let Some(metadata) = Metadata::from(&bytecode) {
+ let name = metadata.name().unwrap_or("unnamed".to_string());
+ let authors = metadata.authors().unwrap_or_else(Vec::new);
+ let mut metadata_string = format!("Program is '{name}'");
+ if authors.len() > 0 {
+ metadata_string.push_str(&format!(", by {}", authors[0])); }
+ if authors.len() > 1 {
+ metadata_string.push_str(" and others"); }
+ info!("{metadata_string}");
+
+ match name.split_once('/') {
+ Some((name, _version)) if !name.is_empty() => title = name.to_string(),
+ _ => title = name.to_string(),
+ }
+ let bg = parse_metadata_colour(metadata.bg_colour()).unwrap_or(Colour::rgb(0x1E1C26));
+ let fg = parse_metadata_colour(metadata.fg_colour()).unwrap_or(Colour::rgb(0xED614F));
+ match parse_large_icon(metadata.large_icon(), bg, fg) {
+ Some(large_icon) => icon = Some(large_icon),
+ None => match parse_small_icon(metadata.small_icon(), bg, fg) {
+ Some(small_icon) => icon = Some(small_icon),
+ None => (),
+ }
+ }
+ } else {
+ info!("Program does not contain metadata");
+ }
+
+ let symbols_path = path.as_ref().map(|p| {
+ let mut path = p.to_path_buf();
+ path.add_extension("sym"); path
+ });
+
+ let config = EmulatorConfig {
+ dimensions, fullscreen, zoom, palette, show_cursor,
+ decode_stdin, encode_stdout,
+ symbols_path, title, icon,
+ };
+
+ if let Ok(phosphor) = Phosphor::new() {
+ match mode {
+ Mode::Dynamic => {
+ info!("Starting graphical emulator");
+ let mut emulator = GraphicalEmulator::new(config, debug);
+ emulator.load_program(&bytecode);
+ emulator.run(phosphor, false);
+ }
+ Mode::Graphical => {
+ info!("Starting graphical emulator");
+ let mut emulator = GraphicalEmulator::new(config, debug);
+ emulator.load_program(&bytecode);
+ emulator.run(phosphor, false);
+ }
+ Mode::Headless => {
+ info!("Starting headless emulator");
+ let mut emulator = HeadlessEmulator::new(&config, debug);
+ emulator.load_program(&bytecode);
+ emulator.run();
+ }
+ }
+ } else {
+ match mode {
+ Mode::Dynamic => {
+ info!("Could not start graphical event loop");
+ info!("Starting headless emulator");
+ let mut emulator = HeadlessEmulator::new(&config, debug);
+ emulator.load_program(&bytecode);
+ emulator.run();
+ }
+ Mode::Graphical => {
+ fatal!("Could not start graphical event loop");
+ }
+ Mode::Headless => {
+ info!("Starting headless emulator");
+ let mut emulator = HeadlessEmulator::new(&config, debug);
+ emulator.load_program(&bytecode);
+ emulator.run();
+ }
+ }
+ }
+}
+
+
+fn print_help() {
+ eprintln!("\
+Usage: br [source]
+ br asm [source] [destination]
+
+Emulator and assembler for the Bedrock computer system.
+
+To access the assembler, run `br asm`. To learn how to use the
+assembler, run `br asm --help`.
+
+Usage:
+ To load a Bedrock program from a file, run `br <path>`, where
+ <path> is the path to an assembled Bedrock program.
+
+ To load a Bedrock program from piped input, run `<command> | br`,
+ where <command> is a command that generates Bedrock bytecode.
+
+ To assemble a Bedrock program from a source file and write to an
+ output file, run `br asm <source> <output>`, where <source> is the
+ path of the source file and <output> is the path to write to.
+
+ To assemble and run a Bedrock program without saving to a file,
+ run `br asm <source> | br`, where <source> is the path of the
+ source file.
+
+Environment variables:
+ BEDROCK_PATH
+ A list of colon-separated paths that will be searched to find
+ a Bedrock program when the program doesn't exist in the current
+ directory. This allows the user to run frequently-used programs
+ from any directory.
+ BEDROCK_LIBS
+ A list of colon-separated paths that will be searched to find
+ Bedrock source code files to use as libraries when assembling a
+ Bedrock program. If a library file resolves an unresolved symbol
+ in the program being assembled, the library file will be merged
+ into the program.
+
+Arguments:
+ [program] Path to a Bedrock program to run
+
+Switches:
+ --fullscreen (-f) Start the program in fullscreen mode (toggle with F11)
+ --size=<dim> (-s) Set the initial window size in the format <width>x<height>
+ --zoom=<scale> (-z) Set the pixel size for the screen (change with F5/F6)
+ --show-cursor (-c) Show the operating system cursor over the window
+ --palette=<pal> Set a debug colour palette in the format <rgb>,... (toggle with F2)
+ --debug, (-d) Show debug information while the program is running
+ --decode-stdin (-i) Decode transmissions on standard input from text lines.
+ --encode-stdout (-o) Encode transmissions on standard output as text lines.
+ --help (-h) Print this help information
+ --verbose, (-v) Print additional information
+ --version Print the program version and exit
+");
+}