9 releases
0.2.0 | Mar 2, 2024 |
---|---|
0.1.7 | Sep 18, 2023 |
0.1.4 | Aug 19, 2023 |
#58 in Emulators
Used in ssemu
130KB
1.5K
SLoC
Manchester Small-Scale Experimental Machine "Baby" Emulator Library
This library provides a collections of types and methods for emulating & assembling code for the Manchester Baby, the first program stored computer.
Explaination
The Manchester "Baby" was the first computer to store both its program code and data in a common randomly-accessible memory, it is for this reason the Baby is considered the first machine to run "true" software, providing a familiar (albeit primitive) programming environment to anyone familiar with assembly, this library can be included in a variety of software and platforms allowing emulation functionality of this historic machine.
This library provides an interface for emulating the Baby as a bytecode
interpreter (baby_emulator::core
), and also a library for assembling
asm using both modern and original asm notations into a format that
can be ran by the emulator (baby_emulator::assembler
).
Please log any questions or issues to the GitHub repo.
Installation
Command line:
cargo add baby-emulator
Cargo.toml:
baby-emulator = "0.2.0"
Example
This shows a few short examples of what this library is capable of, designed to be a starting point allowing further experimentation by the "user". See the docs for further examples and info.
Bytecode Interpreter Emulation
The core of this library is baby_emulator::core::BabyModel
,
this struct has fields representing all of the Baby's internal
registers and 32 word memory, you can initialise this struct with
an array of [i32; 32]
, this array can contain the program code
instructions starting at position 0.
This example runs an example program that adds 5 to 5 and stores
the result in the accumulator. Running here is done with the run_loop
method, this method will simply execute sucessive instructions until
either an error is thrown (like a stop instruction), or the number
os iterations exceeds the specified limmit.
use baby_emulator::core::BabyModel;
use baby_emulator::core::errors::BabyErrors;
use baby_emulator::core::errors::BabyError;
let model = BabyModel::new_example_program();
match model.run_loop(100) {
(model, BabyErrors::Stop(_)) => println!("{}", model.core_dump()),
(_, err) => println!("{}", err.get_descriptor())
}
You can also single step through an emulation, executing a single
instruction at a time using the execute
method and seeing the
direct result.
use baby_emulator::core::BabyModel;
use baby_emulator::core::errors::BabyError;
let model = BabyModel::new_example_program();
match model.execute() {
Ok(m) => println!("{}", m.core_dump()),
Err(e) => println!("Error {}", e.get_descriptor())
}
Assembly
Here is an example of assembling a Baby asm string using
modern notation, then running the resultant program;
see the baby_emulator::assembler
docs for more information:
use baby_emulator::assembler::assemble;
use baby_emulator::core::{BabyModel, instructions::BabyInstruction};
const ASM: &str =
"
ldn $start_value ; Loads 10 into the accumulator
:loop_start_value ; The memory address the loop should return to
sub $subtract_val ; Subtract 1 from the accumulator
cmp ; Skip the next jump instruction if the accumulator is negative
jmp $loop_start ; Jump to the start of the loop
stp ; Program stops when the accumulator is negative
:loop_start ; Pointer to the memory address the loop should return to
abs $loop_start_value
:subtract_val ; Value to be subtracted
abs 0d1
:start_value ; Value to start in the accumulator
abs 0d-10
";
fn main() {
let instructions = match assemble(&String::from(ASM), false) {
Ok(v) => v,
Err(e) => { println!("{}", e.describe(true)); return; }
};
let main_store = BabyInstruction::to_numbers(instructions);
let mut model = BabyModel::new_with_program(main_store);
loop {
model = match model.execute() {
Ok(m) => m,
Err(_) => break
};
}
println!("{}", model.core_dump());
}