#![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 `, where is the path to an assembled Bedrock program. To load a Bedrock program from piped input, run ` | br`, where 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 `, where is the path of the source file and is the path to write to. To assemble and run a Bedrock program without saving to a file, run `br asm | br`, where 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= (-s) Set the initial window size in the format x --zoom= (-z) Set the pixel size for the screen (change with F5/F6) --show-cursor (-c) Show the operating system cursor over the window --palette= Set a debug colour palette in the format ,... (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 "); }