From 01a38bd682ee259e8b58b74e96f3ccfde212f17a Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:10:40 +0000 Subject: [PATCH 1/8] Make assembler callable programmatically --- Cargo.toml | 2 +- src/{rmc_assemble.rs => assembler.rs} | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) rename src/{rmc_assemble.rs => assembler.rs} (98%) diff --git a/Cargo.toml b/Cargo.toml index 76f57f7..6ceed6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ path = "src/bin_creator.rs" [[bin]] name = "rmc_assemble" -path = "src/rmc_assemble.rs" +path = "src/assembler.rs" diff --git a/src/rmc_assemble.rs b/src/assembler.rs similarity index 98% rename from src/rmc_assemble.rs rename to src/assembler.rs index 314c50d..57c6764 100644 --- a/src/rmc_assemble.rs +++ b/src/assembler.rs @@ -254,8 +254,7 @@ pub struct Args { output: PathBuf, } -fn main() -> Result<(), AssemblerError> { - let args = Args::parse(); +fn assemble_from_file(args: Args) -> Result<(), AssemblerError> { let program = std::fs::read_to_string(args.program).map_err(|e| AssemblerError::ReadError(e))?; let assembler_result = assemble(&program); @@ -269,6 +268,11 @@ fn main() -> Result<(), AssemblerError> { } } +fn main() -> Result<(), AssemblerError> { + let args = Args::parse(); + assemble_from_file(args) +} + #[cfg(test)] mod tests { use super::*; From 447cbd515690c3e661e8d099d16446a742cfdbc2 Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:33:00 +0000 Subject: [PATCH 2/8] + --- src/lib.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0d40d8f..7b79094 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use clap::Parser; +use clap::{Parser, Subcommand}; use std::{error::Error, fs, io::Write, path::PathBuf}; use value::Value; @@ -516,6 +516,10 @@ pub struct Config { impl Config { pub fn from_args(args: Args) -> Config { + let command = args.command.or_else( + // convert the xecuteLegacy into an execute, printing a warning + ); // and thendo below + if args.ram_legacy.is_some() && args.ram.is_some() { print_error("Warning: Ignoring positional argument and using --ram argument instead."); print_error("Specifying a RAM file without --ram is no longer recommended."); @@ -549,12 +553,33 @@ impl Default for Config { #[derive(Parser)] #[command(version)] pub struct Args { - // Positional arg for memory file (kept for backwards compatibility) + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + /// executes the provided Rusty-Man machine code + Execute { + // Positional arg for memory file (kept for backwards compatibility) + #[arg(hide = true)] + ram_legacy: Option, + /// Path to a memory dump (.bin) file to load into RAM + #[arg(long)] + ram: Option, + /// Only print the output of the LMC, excluding the RAM and register values. + #[arg(long)] + output_only: bool, + }, + ExecuteLegacy, +} + +/// Deprecated: Simpler `execute` interface, kept for backwards-compatibility +#[derive(Parser)] +pub struct ExecuteLegacy { + // Positional arg for memory file #[arg(hide = true)] ram_legacy: Option, - /// Path to a memory dump (.bin) file to load into RAM - #[arg(long)] - ram: Option, /// Only print the output of the LMC, excluding the RAM and register values. #[arg(long)] output_only: bool, From 4ee3d54f1f2031ac4068f7c73664504a4e1dec1e Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:33:31 +0000 Subject: [PATCH 3/8] trying to get ExecuteLegacy work as a default subcommand --- src/lib.rs | 90 +++++++++++++++++++++------------------ src/main.rs | 9 +++- tests/integration_test.rs | 18 ++++---- 3 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7b79094..88e2251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,11 +236,11 @@ pub struct Computer { ram: RAM, registers: Registers, pub output: Output, - config: Config, + config: ComputerConfig, } impl Computer { - pub fn new(config: Config) -> Computer { + pub fn new(config: ComputerConfig) -> Computer { Computer { ram: [Value::zero(); 100], registers: Registers { @@ -500,7 +500,7 @@ fn read_input_until_valid(prompt: &str) -> Result { } } -pub struct Config { +pub struct ComputerConfig { pub load_ram_file_path: Option, /// If the register values, output buffer, RAM values, and branch messages should be printed after every clock cycle pub print_computer_state: bool, @@ -514,18 +514,14 @@ pub struct Config { pub input: Option>, } -impl Config { - pub fn from_args(args: Args) -> Config { - let command = args.command.or_else( - // convert the xecuteLegacy into an execute, printing a warning - ); // and thendo below - +impl ComputerConfig { + pub fn from_args(args: Execute) -> ComputerConfig { if args.ram_legacy.is_some() && args.ram.is_some() { print_error("Warning: Ignoring positional argument and using --ram argument instead."); print_error("Specifying a RAM file without --ram is no longer recommended."); } - Config { + ComputerConfig { load_ram_file_path: args.ram.or_else(|| { eprintln!( "Note: It is recommended to use the --ram argument to specify a RAM file." @@ -539,9 +535,9 @@ impl Config { } } -impl Default for Config { +impl Default for ComputerConfig { fn default() -> Self { - Config { + ComputerConfig { load_ram_file_path: None, print_computer_state: true, print_raw_output: false, @@ -554,23 +550,20 @@ impl Default for Config { #[command(version)] pub struct Args { #[command(subcommand)] - command: Option, + pub command: Option, +} + +impl Args { + pub fn command(&self) -> Commands { + // Fall back to using the legacy execute command if a subcommand is not provided + self.command.clone().unwrap_or(Commands::ExecuteLegacy) + } } -#[derive(Subcommand)] -enum Commands { +#[derive(Subcommand, Clone)] +pub enum Commands { /// executes the provided Rusty-Man machine code - Execute { - // Positional arg for memory file (kept for backwards compatibility) - #[arg(hide = true)] - ram_legacy: Option, - /// Path to a memory dump (.bin) file to load into RAM - #[arg(long)] - ram: Option, - /// Only print the output of the LMC, excluding the RAM and register values. - #[arg(long)] - output_only: bool, - }, + Execute, ExecuteLegacy, } @@ -585,7 +578,20 @@ pub struct ExecuteLegacy { output_only: bool, } -pub fn run(config: Config) -> Result<(), Box> { +#[derive(Parser)] +pub struct Execute { + // Positional arg for memory file (kept for backwards compatibility) + #[arg(hide = true)] + ram_legacy: Option, + /// Path to a memory dump (.bin) file to load into RAM + #[arg(long)] + ram: Option, + /// Only print the output of the LMC, excluding the RAM and register values. + #[arg(long)] + output_only: bool, +} + +pub fn run(config: ComputerConfig) -> Result<(), Box> { let mut computer = Computer::new(config); computer.initialize_ram_from_file()?; computer.run(); @@ -598,7 +604,7 @@ mod tests { #[test] fn hlt_instruction_works() { - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.ram[0] = 000.into(); let has_halted = !computer.clock_cycle(); // It should halt after the first clock cycle @@ -608,7 +614,7 @@ mod tests { #[test] fn add_instruction_works() { // Test 40 + 2 = 42 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 40.into(); computer.ram[99] = 2.into(); // Operand computer.ram[0] = Value::new(199).unwrap(); // Add address 99 to ACC @@ -619,7 +625,7 @@ mod tests { #[test] fn sub_instruction_works() { // Test 42 - 2 = 40 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 42.into(); computer.ram[99] = 2.into(); // Operand computer.ram[0] = Value::new(299).unwrap(); // Subtract address 99 from ACC @@ -630,7 +636,7 @@ mod tests { #[test] fn store_instruction_works() { // Test storing 42 in address 99 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 42.into(); computer.ram[0] = Value::new(399).unwrap(); // Store ACC to address 99 computer.clock_cycle(); @@ -640,7 +646,7 @@ mod tests { #[test] fn load_instruction_works() { // Test loading 42 from address 99 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.ram[99] = 42.into(); computer.ram[0] = Value::new(599).unwrap(); // Load ACC from address 99 computer.clock_cycle(); @@ -650,7 +656,7 @@ mod tests { #[test] fn branch_instruction_works() { // Test branching/jumping to address 42 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.ram[0] = Value::new(642).unwrap(); // Branch to address 42 computer.clock_cycle(); assert_eq!(computer.registers.program_counter, 42); @@ -659,7 +665,7 @@ mod tests { #[test] fn branch_zero_instruction_when_zero() { // Test BRZ when the accumulator is zero (so it should branch) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 0.into(); computer.ram[0] = Value::new(742).unwrap(); // Branch to address 42 if ACC is zero computer.clock_cycle(); @@ -669,7 +675,7 @@ mod tests { #[test] fn branch_zero_instruction_when_non_zero() { // Test BRZ when the accumulator is non-zero (so it should not branch) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = (-5).into(); computer.ram[0] = Value::new(742).unwrap(); // Branch to address 42 if ACC is zero computer.clock_cycle(); @@ -679,7 +685,7 @@ mod tests { #[test] fn branch_positive_instruction_when_positive() { // Test BRP when the accumulator is positive (so it should branch) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 5.into(); computer.ram[0] = Value::new(842).unwrap(); // Branch to address 42 if ACC is positive computer.clock_cycle(); @@ -690,7 +696,7 @@ mod tests { fn branch_positive_instruction_when_zero() { // Test BRP when the accumulator is zero (so it should branch) // (boundary data) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 0.into(); computer.ram[0] = Value::new(842).unwrap(); // Branch to address 42 if ACC is positive computer.clock_cycle(); @@ -700,7 +706,7 @@ mod tests { #[test] fn branch_positive_instruction_when_negative() { // Test BRP when the accumulator is negative (so it should not branch) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = (-5).into(); computer.ram[0] = Value::new(842).unwrap(); // Branch to address 42 if ACC is positive computer.clock_cycle(); @@ -710,9 +716,9 @@ mod tests { #[test] fn input_instruction_works() { // Test inputting 21 - let mut computer = Computer::new(Config { + let mut computer = Computer::new(ComputerConfig { input: Some(vec![21.into()]), - ..Config::default() + ..ComputerConfig::default() }); computer.ram[0] = Value::new(901).unwrap(); computer.clock_cycle(); @@ -722,7 +728,7 @@ mod tests { #[test] fn output_instruction_works() { // Test outputting 21 - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 21.into(); computer.ram[0] = Value::new(902).unwrap(); computer.clock_cycle(); @@ -732,7 +738,7 @@ mod tests { #[test] fn output_character_instruction_works() { // Test outputting ASCII value 104 (h) - let mut computer = Computer::new(Config::default()); + let mut computer = Computer::new(ComputerConfig::default()); computer.registers.accumulator = 104.into(); computer.ram[0] = Value::new(922).unwrap(); computer.clock_cycle(); diff --git a/src/main.rs b/src/main.rs index a3ff4bd..6fe6027 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,13 @@ use clap::Parser; -use rusty_man_computer::{Args, Config, print_error}; +use rusty_man_computer::{Args, ExecuteLegacy, ComputerConfig, print_error}; fn main() -> () { - let config = Config::from_args(Args::parse()); + let args = Args::parse(); + let command = args.command.or_else(|| { + ExecuteLegacy { + + } + }) println!("Little Man Computer implemented in Rust!"); if let Err(e) = rusty_man_computer::run(config) { diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 0b6d338..6c0428f 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,12 +1,12 @@ use std::path::PathBuf; -use rusty_man_computer::{Computer, Config}; +use rusty_man_computer::{Computer, ComputerConfig}; #[test] fn test_ascii_program() { - let mut computer = Computer::new(Config { + let mut computer = Computer::new(ComputerConfig { load_ram_file_path: Some(PathBuf::from("demos/ascii.bin")), - ..Config::default() + ..ComputerConfig::default() }); computer .initialize_ram_from_file() @@ -24,10 +24,10 @@ fn test_add_program() { let number_1 = 3; let number_2 = -5; let expected_output = number_1 + number_2; - let mut computer = Computer::new(Config { + let mut computer = Computer::new(ComputerConfig { load_ram_file_path: Some(PathBuf::from("demos/add.bin")), input: Some(vec![number_1.into(), number_2.into()]), - ..Config::default() + ..ComputerConfig::default() }); computer .initialize_ram_from_file() @@ -43,10 +43,10 @@ fn test_add_subtract_program() { let expected_sum = number_1 + number_2; let number_3 = 100; let expected_difference = number_3 - number_1; - let mut computer = Computer::new(Config { + let mut computer = Computer::new(ComputerConfig { load_ram_file_path: Some(PathBuf::from("demos/add-subtract.bin")), input: Some(vec![number_1.into(), number_2.into(), number_3.into()]), - ..Config::default() + ..ComputerConfig::default() }); computer .initialize_ram_from_file() @@ -61,10 +61,10 @@ fn test_factorial_program() { let input = 6; // 6! = 720 let expected_output = 720; - let mut computer = Computer::new(Config { + let mut computer = Computer::new(ComputerConfig { load_ram_file_path: Some(PathBuf::from("demos/factorial.bin")), input: Some(vec![input.into()]), - ..Config::default() + ..ComputerConfig::default() }); computer .initialize_ram_from_file() From d5aa281925c855230da3e5e2af8aa7fa95b87a4e Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:55:55 +0000 Subject: [PATCH 4/8] Give up on ExecuteLegacy --- src/lib.rs | 29 +++++------------------------ src/main.rs | 17 ++++++++--------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 88e2251..c490850 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -515,7 +515,7 @@ pub struct ComputerConfig { } impl ComputerConfig { - pub fn from_args(args: Execute) -> ComputerConfig { + pub fn from_args(args: ExecuteArgs) -> ComputerConfig { if args.ram_legacy.is_some() && args.ram.is_some() { print_error("Warning: Ignoring positional argument and using --ram argument instead."); print_error("Specifying a RAM file without --ram is no longer recommended."); @@ -550,36 +550,17 @@ impl Default for ComputerConfig { #[command(version)] pub struct Args { #[command(subcommand)] - pub command: Option, -} - -impl Args { - pub fn command(&self) -> Commands { - // Fall back to using the legacy execute command if a subcommand is not provided - self.command.clone().unwrap_or(Commands::ExecuteLegacy) - } + pub command: Commands, } #[derive(Subcommand, Clone)] pub enum Commands { /// executes the provided Rusty-Man machine code - Execute, - ExecuteLegacy, -} - -/// Deprecated: Simpler `execute` interface, kept for backwards-compatibility -#[derive(Parser)] -pub struct ExecuteLegacy { - // Positional arg for memory file - #[arg(hide = true)] - ram_legacy: Option, - /// Only print the output of the LMC, excluding the RAM and register values. - #[arg(long)] - output_only: bool, + Execute(ExecuteArgs), } -#[derive(Parser)] -pub struct Execute { +#[derive(Parser, Clone)] +pub struct ExecuteArgs { // Positional arg for memory file (kept for backwards compatibility) #[arg(hide = true)] ram_legacy: Option, diff --git a/src/main.rs b/src/main.rs index 6fe6027..906b080 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,15 @@ use clap::Parser; -use rusty_man_computer::{Args, ExecuteLegacy, ComputerConfig, print_error}; +use rusty_man_computer::{Args, Commands, ComputerConfig, print_error}; fn main() -> () { let args = Args::parse(); - let command = args.command.or_else(|| { - ExecuteLegacy { - - } - }) - println!("Little Man Computer implemented in Rust!"); - if let Err(e) = rusty_man_computer::run(config) { - print_error(&format!("Application error: {}", e)); + match args.command { + Commands::Execute(execute) => { + let config = ComputerConfig::from_args(execute); + if let Err(e) = rusty_man_computer::run(config) { + print_error(&format!("Application error: {}", e)); + } + } } } From c2548b7210cb871601e59e3283667812a631bd91 Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:59:25 +0000 Subject: [PATCH 5/8] Rename Commands enum --- src/lib.rs | 4 ++-- src/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c490850..06dc5c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -550,11 +550,11 @@ impl Default for ComputerConfig { #[command(version)] pub struct Args { #[command(subcommand)] - pub command: Commands, + pub command: Command, } #[derive(Subcommand, Clone)] -pub enum Commands { +pub enum Command { /// executes the provided Rusty-Man machine code Execute(ExecuteArgs), } diff --git a/src/main.rs b/src/main.rs index 906b080..8912ea4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ use clap::Parser; -use rusty_man_computer::{Args, Commands, ComputerConfig, print_error}; +use rusty_man_computer::{Args, Command, ComputerConfig, print_error}; fn main() -> () { let args = Args::parse(); match args.command { - Commands::Execute(execute) => { + Command::Execute(execute) => { let config = ComputerConfig::from_args(execute); if let Err(e) = rusty_man_computer::run(config) { print_error(&format!("Application error: {}", e)); From 5ca85e5483b1cd687cae056a20a689326f8576bd Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:45:35 +0000 Subject: [PATCH 6/8] "rustyman run" is nearly ready --- Cargo.lock | 141 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/assembler.rs | 12 +++- src/lib.rs | 4 ++ src/main.rs | 19 ++++++- 5 files changed, 172 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a0b2db..2bb7450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "anstream" version = "0.6.18" @@ -52,6 +67,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + [[package]] name = "clap" version = "4.5.31" @@ -92,30 +128,101 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "owo-colors" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" + [[package]] name = "proc-macro2" version = "1.0.94" @@ -134,11 +241,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + [[package]] name = "rusty_man_computer" version = "0.5.0" dependencies = [ "clap", + "color-eyre", + "thiserror", ] [[package]] @@ -158,6 +273,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.17" @@ -170,6 +305,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 6ceed6b..42f570e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2024" [dependencies] clap = { version = "4.5.30", features = ["derive"] } +color-eyre = { version = "0.6", default-features = false } +thiserror = "2.0.17" [[bin]] name = "bin_creator" diff --git a/src/assembler.rs b/src/assembler.rs index 57c6764..401c54b 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -1,5 +1,6 @@ use clap::Parser; use std::{collections::HashMap, fmt, fs, io, path::PathBuf}; +use thiserror::Error; use rusty_man_computer::value::Value; @@ -55,7 +56,7 @@ impl fmt::Display for ParseErrorType { } #[derive(Debug)] -struct ParseError { +pub struct ParseError { error: ParseErrorType, line: usize, } @@ -206,10 +207,15 @@ fn generate_machine_code(lines: Vec) -> Result, &'static str> { Ok(output) } -enum AssemblerError { +#[derive(Error)] +pub enum AssemblerError { + #[error("{0}")] ParseError(ParseError), + #[error("Machine code error: {0}")] MachineCodeError(&'static str), + #[error("Failed to read input file: {0}")] ReadError(io::Error), + #[error("Failed to write to output file: {0}")] WriteError(io::Error), } @@ -224,7 +230,7 @@ impl fmt::Debug for AssemblerError { } } -fn assemble(program: &str) -> Result, AssemblerError> { +pub fn assemble(program: &str) -> Result, AssemblerError> { let parsed = parse_assembly(program); let mut valid_lines: Vec = Vec::new(); // Only go forward with non-empty lines, and raise an error if we encounter an invalid line diff --git a/src/lib.rs b/src/lib.rs index 06dc5c4..11b54aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -557,6 +557,10 @@ pub struct Args { pub enum Command { /// executes the provided Rusty-Man machine code Execute(ExecuteArgs), + Run { + #[arg()] + file: PathBuf, + }, } #[derive(Parser, Clone)] diff --git a/src/main.rs b/src/main.rs index 8912ea4..d06d9bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use clap::Parser; -use rusty_man_computer::{Args, Command, ComputerConfig, print_error}; +use rusty_man_computer::{Args, Command, Computer, ComputerConfig, print_error}; +mod assembler; -fn main() -> () { +fn main() -> Result<(), color_eyre::Report> { let args = Args::parse(); match args.command { @@ -9,7 +10,19 @@ fn main() -> () { let config = ComputerConfig::from_args(execute); if let Err(e) = rusty_man_computer::run(config) { print_error(&format!("Application error: {}", e)); - } + }; + Ok(()) + } + Command::Run { file } => { + let program = std::fs::read_to_string(file)?; + let machine_code = assembler::assemble(&program)?; + let mut computer = Computer::new(ComputerConfig { + // FIXME + ram: Some(machine_code), + ..ComputerConfig::default() + }); + computer.run(); + Ok(()) } } } From 7a4a79d51825ba3a85e61d33a4fc40130a018652 Mon Sep 17 00:00:00 2001 From: MMK21 <50421330+MMK21Hub@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:22:05 +0000 Subject: [PATCH 7/8] Work on putting machine code straight to RAM --- src/lib.rs | 7 ++++++- src/main.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 11b54aa..118ead0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,7 +242,7 @@ pub struct Computer { impl Computer { pub fn new(config: ComputerConfig) -> Computer { Computer { - ram: [Value::zero(); 100], + ram: config.ram, registers: Registers { program_counter: 0, instruction_register: 0, @@ -501,7 +501,10 @@ fn read_input_until_valid(prompt: &str) -> Result { } pub struct ComputerConfig { + /// TODO remove pub load_ram_file_path: Option, + /// The initial contents of RAM (i.e. initial values for the letterboxes) + pub ram: [Value; 100], /// If the register values, output buffer, RAM values, and branch messages should be printed after every clock cycle pub print_computer_state: bool, /// If output should be directly and immediately printed when a OUT/OTC instruction is executed @@ -528,6 +531,7 @@ impl ComputerConfig { ); args.ram_legacy }), + ram: [Value::zero(); 100], print_computer_state: !args.output_only, print_raw_output: args.output_only, input: None, @@ -539,6 +543,7 @@ impl Default for ComputerConfig { fn default() -> Self { ComputerConfig { load_ram_file_path: None, + ram: [Value::zero(); 100], print_computer_state: true, print_raw_output: false, input: None, diff --git a/src/main.rs b/src/main.rs index d06d9bc..e81e8bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use clap::Parser; -use rusty_man_computer::{Args, Command, Computer, ComputerConfig, print_error}; +use color_eyre::eyre::eyre; +use rusty_man_computer::{Args, Command, Computer, ComputerConfig, print_error, value::Value}; mod assembler; fn main() -> Result<(), color_eyre::Report> { @@ -16,9 +17,12 @@ fn main() -> Result<(), color_eyre::Report> { Command::Run { file } => { let program = std::fs::read_to_string(file)?; let machine_code = assembler::assemble(&program)?; + let machine_code_array: &[Value; 100] = &machine_code.clone().try_into().map_err(|_| { + eyre!("Assembled machine code does ({} letterboxes) not fit into memory (100 letterboxes max).", &machine_code.len()) + })?; let mut computer = Computer::new(ComputerConfig { // FIXME - ram: Some(machine_code), + ram: machine_code_array.clone(), ..ComputerConfig::default() }); computer.run(); From d0b331af6772e78bcab2a73e67b853cd216c3b51 Mon Sep 17 00:00:00 2001 From: RandomSearch <101704343+RandomSearch18@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:34:19 +0000 Subject: [PATCH 8/8] Finish `rustyman run` --- src/lib.rs | 4 ++++ src/main.rs | 16 +++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 118ead0..064be40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -292,6 +292,10 @@ impl Computer { touched_addresses } + // pub fn load_values_to_ram(&mut self, values: Vec) { + + // } + pub fn clock_cycle(&mut self) -> bool { // Stage 1: Fetch let ram_index = self.registers.program_counter; diff --git a/src/main.rs b/src/main.rs index e81e8bc..7f56d79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,12 +17,18 @@ fn main() -> Result<(), color_eyre::Report> { Command::Run { file } => { let program = std::fs::read_to_string(file)?; let machine_code = assembler::assemble(&program)?; - let machine_code_array: &[Value; 100] = &machine_code.clone().try_into().map_err(|_| { - eyre!("Assembled machine code does ({} letterboxes) not fit into memory (100 letterboxes max).", &machine_code.len()) - })?; + + // Initialize memory with the machine code + let mut machine_code_array: [Value; 100] = [Value::zero(); 100]; + if &machine_code.len() > &machine_code_array.len() { + return Err(eyre!("Program too large to fit in memory")); + } + for i in 0..machine_code.len() { + machine_code_array[i] = machine_code[i]; + } + let mut computer = Computer::new(ComputerConfig { - // FIXME - ram: machine_code_array.clone(), + ram: machine_code_array, ..ComputerConfig::default() }); computer.run();