logicsim 0.1.7

composable, modular, digital logic simulation
Documentation
#[macro_use]
#[allow(dead_code)]
mod assembler;
mod clock_timer;
mod computer;
mod instruction_set;
#[allow(dead_code)]
mod programs;
mod stdin_peekable;
use clock_timer::ClockTimer;
use computer::{mk_computer, ComputerIO};
use programs::{list_programs, program, OutputType};
use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
use stdin_peekable::StdinPeekable;
#[macro_use]
extern crate strum_macros;
mod control_logic;

fn main() {
    // Handle ctrl-c
    static STOP: AtomicBool = AtomicBool::new(false);
    ctrlc::set_handler(|| STOP.store(true, Ordering::Relaxed)).unwrap();

    let program_name = std::env::args()
        .skip(1)
        .next()
        .expect("Please provide a program name as the first argument.");

    let selected_program = if let Some(p) = program(&program_name) {
        p
    } else {
        panic!(
            "\nSelected program not available: {}, available programs:\n{}\n",
            program_name,
            list_programs().join("\n")
        )
    };

    let ComputerIO {
        ack,
        clock,
        mut ig,
        write_input,
        input,
        input_busy,
        output,
        output_updated,
        ..
    } = mk_computer(
        &selected_program.rom(),
        selected_program.ram_address_space_bits(),
    );

    let ig = &mut ig;
    ig.dump_dot("computer_optimized.dot");

    let mut should_reset_ack = false;
    let mut stdin = StdinPeekable::new();
    let output_type = selected_program.output_type();

    let mut timer = ClockTimer::new(selected_program.clock_print_interval());
    for i in 0..std::u32::MAX {
        if STOP.load(Ordering::Relaxed) {
            break;
        }

        ig.flip_lever_stable(clock);

        // If there's data in stdin and the computer is not busy handling input,
        // input some data.
        if let (Some(c), false) = (stdin.peek(), input_busy.b0(ig)) {
            input.set_to(ig, c);
            ig.pulse_lever_stable(write_input);
            stdin.next();
        }

        // Since the acknowledgement is synchronous we need to leave the lever on
        // during a clock cycle and then turn it off.
        if should_reset_ack {
            ig.reset_lever(ack);
            should_reset_ack = false
        }

        // If the computer has updated its output, print it, and send an acknowledgement
        // to the computer that we have consumed its output.
        if output_updated.b0(ig) && i % 2 == 1 {
            match output_type {
                OutputType::Number => {
                    print!("{}", output.u8(ig));
                }
                OutputType::Text => {
                    print!("{}", output.char(ig));
                }
            }
            std::io::stdout().flush().unwrap();
            ig.set_lever(ack);
            should_reset_ack = true;
        }
        if i % 2 == 1 {
            // Every 2 flips it's a clock cycle.
            timer.clock();
        }
    }
}