1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! # MLeM
//! The Machine Learning Machine is a 64-bit virtual Harvard-arch
//! machine for evolutionary algorithms to program against.
//!
//! The machine has eight GPRs (`R0` through `R7`), a hardware stack with SP and BP, 
//! and hardware I/O with Input and Output. 
//! 
//! These I/O instructions write out whole `u64`s in big endian using `byteorder`.
//! 
//! # Example
//! ```
//! use mlem::{execute, Instruction, Address, Register, Outcome};
//! let input = vec![2, 2, 2, 2];
//! let expected = vec![4, 0];
//! let program = vec![
//!     // Get all input values
//!     Instruction::Input(Address::RegAbs(Register::R0)),
//!     Instruction::Input(Address::RegAbs(Register::R1)),
//!     Instruction::Input(Address::RegAbs(Register::R2)),
//!     Instruction::Input(Address::RegAbs(Register::R3)),
//!     // Perform arithmetic
//!     Instruction::Add(Address::RegAbs(Register::R0), Address::RegAbs(Register::R1)),
//!     Instruction::Sub(Address::RegAbs(Register::R2), Address::RegAbs(Register::R3)),
//!     // Output computed values
//!     Instruction::Output(Address::RegAbs(Register::R0)),
//!     Instruction::Output(Address::RegAbs(Register::R2)),
//!     // Halt
//!     Instruction::Halt
//! ];
//!
//! // The last value here is the maximum number of instructions to let the program run for.
//! let (outcome, cycles, output) = execute(program, input, Some(10));
//! assert!(outcome == Outcome::Halt, "Program did not successfully halt! {:?}", outcome);
//! assert!(output == expected, "Program did not produce {:?} as expected, but rather {:?}, in {} cycles.", expected, output, cycles);
//! ```

extern crate byteorder;
#[macro_use]
extern crate serde_derive;
extern crate serde_cbor;

mod types;
pub use types::*;

mod machine;
#[cfg(test)]
mod test_machine;
pub use machine::*;

mod instructions;
#[cfg(test)]
mod test_instructions;
pub use instructions::*;

/// Given a Program (that is, a Vec of Instructions), this function will manage creating a Machine and hooking up its 
/// Input and Output for you. It returns a tuple of the final outcome of the program, the number of instructions executed, and
/// a Vector of the output.
pub fn execute(program: Program, input: Vec<u64>, limit: Option<u64>) -> (Outcome, u64, Vec<u64>) {
    use std::io::{Cursor, Seek};
    use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
    // Create and fill a buffer of u8s with the values of the given u64s, in big endian
    let mut internal_input = Cursor::new(Vec::with_capacity(input.len() * 8));
    for v in input {
        internal_input.write_u64::<BigEndian>(v).unwrap();
    }
    internal_input.seek(std::io::SeekFrom::Start(0)).unwrap();

    // Create output buffer
    let mut internal_output = Cursor::new(Vec::new());
    
    // Actually run the machine.
    let o;
    let cycles;
    {
        let mut m = Machine::new(128, &mut internal_input, &mut internal_output);

        m.load_program(program);
        let actual_limit = limit.unwrap_or(u64::max_value());
        let (a, b) = m.run_for(actual_limit);
        o = a;
        cycles = b;
    }
    // Compose output into u64 values
    let mut output = Vec::new();
    internal_output.seek(std::io::SeekFrom::Start(0)).unwrap();
    while let Ok(v) = internal_output.read_u64::<BigEndian>() {
        output.push(v);
    }

    (o, cycles, output)
}