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}