cpuville/
cpuville.rs

1
2/*
3http://cpuville.com/Code/CPM-on-a-new-computer.html
4http://cpuville.com/Code/Tiny-BASIC.html
5*/
6use std::io::{Write, stdin, stdout};
7use std::sync::mpsc;
8use std::sync::mpsc::Receiver;
9use std::sync::mpsc::TryRecvError;
10use std::thread;
11use std::time::Duration;
12
13use iz80::Cpu;
14use iz80::Machine;
15use iz80::TimedRunner;
16
17static TINY_BASIC: &[u8] = include_bytes!("rom/tinybasic2dms.bin");
18const MHZ: f64 = 4.0;
19
20fn main() {
21    let mut machine = VilleMachine::new();
22    let mut cpu = Cpu::new();
23    let mut timed_runner = TimedRunner::default();
24    timed_runner.set_mhz(&cpu, MHZ, 1000);
25
26    // Init console
27    let mut stdout = stdout();
28    let stdin_channel = spawn_stdin_channel();
29    let mut in_char_waiting = false;
30
31    // Load program
32    let code = TINY_BASIC;
33    for (i, e) in code.iter().enumerate() {
34        machine.poke(i as u16, *e);
35    }
36
37    // Init
38    cpu.registers().set_pc(0x0000);
39    machine.in_values[3] = 1; // TX Ready
40
41    loop {
42        timed_runner.execute(&mut cpu, &mut machine);
43
44        if let Some(port) = machine.out_port {
45            match port {
46                2 => {
47                    print!("{}", machine.out_value as char);
48                    stdout.flush().unwrap();
49                },
50                3 => {},
51                _ => panic!("BDOS command not implemented")
52            }
53            machine.out_port = None;
54        }
55
56        if let Some(port) = machine.in_port {
57            match port {
58                2 => {
59                    in_char_waiting = false;
60                },
61                3 => {},
62                _ => panic!("BDOS command not implemented")
63            }
64            machine.in_port = None;
65
66            // Avoid 100% CPU usage waiting for input.
67            if MHZ == 0.0 {
68                thread::sleep(Duration::from_millis(1));  
69            }
70        }
71
72        if !in_char_waiting {
73            // Let's get another char if available
74            match stdin_channel.try_recv() {
75                Ok(key) => {
76                    machine.in_values[2] = key;
77                    in_char_waiting = true;
78                    machine.in_values[3] = 3; // RX Ready
79                },
80                Err(TryRecvError::Empty) => {
81                    machine.in_values[3] = 1; // RX Not ready
82                },
83                Err(TryRecvError::Disconnected) => {},
84            }
85        }
86    }
87}
88
89fn spawn_stdin_channel() -> Receiver<u8> {
90    let (tx, rx) = mpsc::channel::<u8>();
91    thread::spawn(move || loop {
92        let mut buffer = String::new();
93        stdin().read_line(&mut buffer).unwrap();
94        for mut c in buffer.bytes() {
95            if c == 10 {c = 13};
96            tx.send(c).unwrap();
97        }
98    });
99    rx
100}
101
102struct VilleMachine {
103    mem: [u8; 65536],
104    in_values: [u8; 256],
105    in_port: Option<u8>,
106    out_port: Option<u8>,
107    out_value: u8
108}
109
110impl VilleMachine {
111    pub fn new() -> VilleMachine {
112        VilleMachine {
113            mem: [0; 65536],
114            in_values: [0; 256],
115            out_port: None,
116            out_value: 0,
117            in_port: None
118        }
119    }
120}
121
122impl Machine for VilleMachine {
123    fn peek(&self, address: u16) -> u8 {
124        self.mem[address as usize]
125    }
126
127    fn poke(&mut self, address: u16, value: u8) {
128        self.mem[address as usize] = value;
129    }
130
131    fn port_in(&mut self, address: u16) -> u8 {
132        let value = self.in_values[address as u8 as usize];
133        if value != 1 {
134            //print!("Port {:04x} in {:02x}\n", address, value);
135        }
136        self.in_port = Some(address as u8);
137        value
138    }
139
140    fn port_out(&mut self, address: u16, value: u8) {
141        //print!("Port {:04x} out {:02x} {}\n", address, value, value as char);
142        self.out_port = Some(address as u8);
143        self.out_value = value;
144    }
145}
146
147