retroshield_z80_workbench/stdlib/
terminal.rs

1//! VT100/VT220/ANSI Terminal escape sequences
2//!
3//! Provides routines for cursor control, screen clearing, etc.
4
5use crate::CodeGen;
6
7/// ESC character
8const ESC: u8 = 0x1B;
9
10impl CodeGen {
11    // ========== Inline Escape Sequence Helpers ==========
12
13    /// Emit ESC [ prefix (common to most sequences)
14    fn emit_csi(&mut self) {
15        self.ld_a(ESC);
16        self.call("putchar");
17        self.ld_a(b'[');
18        self.call("putchar");
19    }
20
21    // ========== Screen Control Routines ==========
22
23    /// Emit clear_screen routine (ESC[2J ESC[H)
24    ///
25    /// Labels created: `clear_screen`
26    /// Requires: `putchar`
27    pub fn emit_clear_screen(&mut self) {
28        self.label("clear_screen");
29        self.emit_csi();
30        self.ld_a(b'2');
31        self.call("putchar");
32        self.ld_a(b'J');
33        self.call("putchar");
34        // Fall through to cursor_home
35    }
36
37    /// Emit cursor_home routine (ESC[H)
38    ///
39    /// Labels created: `cursor_home`
40    /// Requires: `putchar`
41    pub fn emit_cursor_home(&mut self) {
42        self.label("cursor_home");
43        self.emit_csi();
44        self.ld_a(b'H');
45        self.call("putchar");
46        self.ret();
47    }
48
49    /// Emit combined clear_screen + cursor_home
50    ///
51    /// Labels created: `clear_screen`, `cursor_home`
52    /// Requires: `putchar`
53    pub fn emit_clear_screen_and_home(&mut self) {
54        self.emit_clear_screen();
55        self.emit_cursor_home();
56    }
57
58    /// Emit cursor_pos routine (ESC[row;colH)
59    /// Input: B = row (1-based), C = col (1-based)
60    ///
61    /// Labels created: `cursor_pos`
62    /// Requires: `putchar`, `print_byte_dec`
63    pub fn emit_cursor_pos(&mut self) {
64        self.label("cursor_pos");
65        self.emit_csi();
66        self.ld_a_b();              // Row
67        self.call("print_byte_dec");
68        self.ld_a(b';');
69        self.call("putchar");
70        self.ld_a_c();              // Col
71        self.call("print_byte_dec");
72        self.ld_a(b'H');
73        self.call("putchar");
74        self.ret();
75    }
76
77    /// Emit clear_to_eol routine (ESC[K)
78    ///
79    /// Labels created: `clear_to_eol`
80    /// Requires: `putchar`
81    pub fn emit_clear_to_eol(&mut self) {
82        self.label("clear_to_eol");
83        self.emit_csi();
84        self.ld_a(b'K');
85        self.call("putchar");
86        self.ret();
87    }
88
89    /// Emit clear_to_eos routine (ESC[J) - clear from cursor to end of screen
90    ///
91    /// Labels created: `clear_to_eos`
92    /// Requires: `putchar`
93    pub fn emit_clear_to_eos(&mut self) {
94        self.label("clear_to_eos");
95        self.emit_csi();
96        self.ld_a(b'J');
97        self.call("putchar");
98        self.ret();
99    }
100
101    // ========== Cursor Visibility ==========
102
103    /// Emit cursor_hide routine (ESC[?25l)
104    ///
105    /// Labels created: `cursor_hide`
106    /// Requires: `putchar`
107    pub fn emit_cursor_hide(&mut self) {
108        self.label("cursor_hide");
109        self.emit_csi();
110        self.ld_a(b'?');
111        self.call("putchar");
112        self.ld_a(b'2');
113        self.call("putchar");
114        self.ld_a(b'5');
115        self.call("putchar");
116        self.ld_a(b'l');
117        self.call("putchar");
118        self.ret();
119    }
120
121    /// Emit cursor_show routine (ESC[?25h)
122    ///
123    /// Labels created: `cursor_show`
124    /// Requires: `putchar`
125    pub fn emit_cursor_show(&mut self) {
126        self.label("cursor_show");
127        self.emit_csi();
128        self.ld_a(b'?');
129        self.call("putchar");
130        self.ld_a(b'2');
131        self.call("putchar");
132        self.ld_a(b'5');
133        self.call("putchar");
134        self.ld_a(b'h');
135        self.call("putchar");
136        self.ret();
137    }
138
139    // ========== Cursor Movement ==========
140
141    /// Emit cursor_up routine (ESC[A) - move cursor up 1 line
142    ///
143    /// Labels created: `cursor_up`
144    /// Requires: `putchar`
145    pub fn emit_cursor_up(&mut self) {
146        self.label("cursor_up");
147        self.emit_csi();
148        self.ld_a(b'A');
149        self.call("putchar");
150        self.ret();
151    }
152
153    /// Emit cursor_down routine (ESC[B) - move cursor down 1 line
154    ///
155    /// Labels created: `cursor_down`
156    /// Requires: `putchar`
157    pub fn emit_cursor_down(&mut self) {
158        self.label("cursor_down");
159        self.emit_csi();
160        self.ld_a(b'B');
161        self.call("putchar");
162        self.ret();
163    }
164
165    /// Emit cursor_right routine (ESC[C) - move cursor right 1 column
166    ///
167    /// Labels created: `cursor_right`
168    /// Requires: `putchar`
169    pub fn emit_cursor_right(&mut self) {
170        self.label("cursor_right");
171        self.emit_csi();
172        self.ld_a(b'C');
173        self.call("putchar");
174        self.ret();
175    }
176
177    /// Emit cursor_left routine (ESC[D) - move cursor left 1 column
178    ///
179    /// Labels created: `cursor_left`
180    /// Requires: `putchar`
181    pub fn emit_cursor_left(&mut self) {
182        self.label("cursor_left");
183        self.emit_csi();
184        self.ld_a(b'D');
185        self.call("putchar");
186        self.ret();
187    }
188
189    // ========== Text Attributes ==========
190
191    /// Emit reset_attrs routine (ESC[0m) - reset all text attributes
192    ///
193    /// Labels created: `reset_attrs`
194    /// Requires: `putchar`
195    pub fn emit_reset_attrs(&mut self) {
196        self.label("reset_attrs");
197        self.emit_csi();
198        self.ld_a(b'0');
199        self.call("putchar");
200        self.ld_a(b'm');
201        self.call("putchar");
202        self.ret();
203    }
204
205    /// Emit reverse_video routine (ESC[7m)
206    ///
207    /// Labels created: `reverse_video`
208    /// Requires: `putchar`
209    pub fn emit_reverse_video(&mut self) {
210        self.label("reverse_video");
211        self.emit_csi();
212        self.ld_a(b'7');
213        self.call("putchar");
214        self.ld_a(b'm');
215        self.call("putchar");
216        self.ret();
217    }
218
219    // ========== Bundle Emitters ==========
220
221    /// Emit all terminal routines
222    ///
223    /// Includes: clear_screen, cursor_home, cursor_pos, clear_to_eol,
224    /// cursor_hide, cursor_show
225    /// Requires: `putchar`, `print_byte_dec`
226    pub fn emit_terminal_routines(&mut self) {
227        self.emit_clear_screen_and_home();
228        self.emit_cursor_pos();
229        self.emit_clear_to_eol();
230        self.emit_cursor_hide();
231        self.emit_cursor_show();
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_clear_screen_emits() {
241        let mut cg = CodeGen::new();
242        cg.emit_clear_screen_and_home();
243        assert!(cg.has_label("clear_screen"));
244        assert!(cg.has_label("cursor_home"));
245    }
246}