diff options
Diffstat (limited to 'src/bin/br/main.rs')
-rw-r--r-- | src/bin/br/main.rs | 217 |
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 +"); +} |