9 releases

0.2.0 Mar 2, 2024
0.1.7 Sep 18, 2023
0.1.4 Aug 19, 2023

#29 in Emulators

Download history 10/week @ 2024-02-19 139/week @ 2024-02-26 28/week @ 2024-03-04 29/week @ 2024-03-11 2/week @ 2024-03-25 84/week @ 2024-04-01

118 downloads per month
Used in ssemu

MIT license

130KB
1.5K SLoC

Manchester Small-Scale Experimental Machine "Baby" Emulator Library

crates.io Released API docs MIT licensed

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());
}

No runtime deps