cpmrun/
cpmrun.rs

1use intel8080::cpu::CPU;
2use std::{env, error::Error, process};
3
4fn main() {
5    if let Err(e) = load_execute() {
6        println!("{}", e);
7        process::exit(1);
8    }
9}
10
11fn load_execute() -> Result<(), Box<dyn Error>> {
12    let a: Vec<String> = env::args().collect();
13    let mut c = CPU::new(0xFFFF);
14    // Loads assembled program into memory
15    c.bus.load_bin(&a[1], 0x100)?;
16
17    // RET at 0x05 for mocking of CP/M BDOS system calls
18    c.bus.write_word(0x0005, 0xc9);
19
20    // Setting PC to 0x0100 (CP/M Binaries are loaded with a 256 byte offset)
21    c.pc = 0x0100;
22
23    /* Setting up stack : by disassembling CP/M software, it seems
24    that the $0006 address is read to set the stack by some programs */
25    c.bus.write_word(0x0006, 0xFF00);
26
27    /* Setting up stack in case of the program does not read the $0006 address
28    and does not set any stack. */
29    c.sp = 0xFF00;
30
31    loop {
32        c.execute();
33        if c.pc == 0x0005 {
34            bdos_call(&c)
35        }
36        if c.pc == 0x0000 {
37            break;
38        } //  if CP/M warm boot -> we exit
39    }
40    Ok(())
41}
42
43fn bdos_call(c: &CPU) {
44    if c.reg.c == 0x09 {
45        let mut a = c.reg.get_de();
46        loop {
47            let c = c.bus.read_byte(a);
48            if c as char == '$' {
49                break;
50            } else {
51                a += 1;
52            }
53            print!("{}", c as char);
54        }
55    }
56    if c.reg.c == 0x02 {
57        print!("{}", c.reg.e as char);
58    }
59}