retroshield_z80_workbench/stdlib/
io.rs

1//! MC6850 ACIA Serial I/O routines
2//!
3//! Standard RetroShield configuration:
4//! - Status port: 0x80
5//! - Data port: 0x81
6//! - Bit 0 of status: RX ready
7//! - Bit 1 of status: TX ready
8
9use crate::CodeGen;
10
11/// MC6850 port configuration
12pub struct MC6850Config {
13    pub status_port: u8,
14    pub data_port: u8,
15    pub rx_ready_bit: u8,
16    pub tx_ready_bit: u8,
17}
18
19impl Default for MC6850Config {
20    fn default() -> Self {
21        Self {
22            status_port: 0x80,
23            data_port: 0x81,
24            rx_ready_bit: 0x01,
25            tx_ready_bit: 0x02,
26        }
27    }
28}
29
30impl CodeGen {
31    /// Emit getchar routine (blocking read, char returned in A)
32    ///
33    /// Labels created: `getchar`
34    pub fn emit_getchar(&mut self) {
35        self.emit_getchar_config(&MC6850Config::default());
36    }
37
38    /// Emit getchar with custom port configuration
39    pub fn emit_getchar_config(&mut self, config: &MC6850Config) {
40        self.label("getchar");
41        self.in_a(config.status_port);
42        self.and_a(config.rx_ready_bit);
43        self.emit(&[0x28, 0xFA]); // JR Z, -6 (back to getchar)
44        self.in_a(config.data_port);
45        self.ret();
46    }
47
48    /// Emit putchar routine (blocking write, char in A)
49    ///
50    /// Labels created: `putchar`, `putchar_wait`
51    pub fn emit_putchar(&mut self) {
52        self.emit_putchar_config(&MC6850Config::default());
53    }
54
55    /// Emit putchar with custom port configuration
56    pub fn emit_putchar_config(&mut self, config: &MC6850Config) {
57        self.label("putchar");
58        self.push_af();
59        self.label("putchar_wait");
60        self.in_a(config.status_port);
61        self.and_a(config.tx_ready_bit);
62        self.emit(&[0x28, 0xFA]); // JR Z, -6 (back to putchar_wait)
63        self.pop_af();
64        self.out_a(config.data_port);
65        self.ret();
66    }
67
68    /// Emit newline routine (prints CR LF)
69    ///
70    /// Labels created: `newline`
71    /// Requires: `putchar`
72    pub fn emit_newline(&mut self) {
73        self.label("newline");
74        self.ld_a(0x0D); // CR
75        self.call("putchar");
76        self.ld_a(0x0A); // LF
77        self.call("putchar");
78        self.ret();
79    }
80
81    /// Emit print_string routine (prints null-terminated string at HL)
82    ///
83    /// Labels created: `print_string`, `print_string_loop`
84    /// Requires: `putchar`
85    pub fn emit_print_string(&mut self) {
86        self.label("print_string");
87        self.label("print_string_loop");
88        self.ld_a_hl_ind();      // LD A, (HL)
89        self.or_a_a();           // OR A (test for null)
90        self.ret_z();            // RET Z (if null, done)
91        self.call("putchar");
92        self.inc_hl();
93        self.jp("print_string_loop");
94    }
95
96    /// Emit all standard I/O routines
97    ///
98    /// Includes: getchar, putchar, newline, print_string
99    pub fn emit_io_routines(&mut self) {
100        self.emit_getchar();
101        self.emit_putchar();
102        self.emit_newline();
103        self.emit_print_string();
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_getchar_emits() {
113        let mut cg = CodeGen::new();
114        cg.emit_getchar();
115        assert!(cg.has_label("getchar"));
116        assert!(cg.size() > 0);
117    }
118
119    #[test]
120    fn test_putchar_emits() {
121        let mut cg = CodeGen::new();
122        cg.emit_putchar();
123        assert!(cg.has_label("putchar"));
124        assert!(cg.has_label("putchar_wait"));
125    }
126}