https://github.com/akkartik/mu/blob/master/103screen.subx
  1 # Primitives for screen control.
  2 # Require Linux and a modern terminal.
  3 
  4 == code
  5 
  6 enable-screen-grid-mode:
  7     # . prologue
  8     55/push-ebp
  9     89/<- %ebp 4/r32/esp
 10     (flush Stdout)
 11     (flush Stderr)
 12     # switch to second screen buffer
 13     (write 2 Esc)
 14     (write 2 "[?1049h")
 15     #
 16     (clear-screen 0)
 17 $enable-screen-grid-mode:end:
 18     # . epilogue
 19     89/<- %esp 5/r32/ebp
 20     5d/pop-to-ebp
 21     c3/return
 22 
 23 enable-screen-type-mode:
 24     # . prologue
 25     55/push-ebp
 26     89/<- %ebp 4/r32/esp
 27     (flush Stdout)
 28     (flush Stderr)
 29     # switch to first screen buffer
 30     (write 2 Esc)
 31     (write 2 "[?1049l")
 32 $enable-screen-type-mode:end:
 33     # . epilogue
 34     89/<- %esp 5/r32/ebp
 35     5d/pop-to-ebp
 36     c3/return
 37 
 38 screen-size:  # -> nrows/eax: int, ncols/ecx: int
 39     # . prologue
 40     55/push-ebp
 41     89/<- %ebp 4/r32/esp
 42     #
 43     (_maybe-open-terminal)
 44     # var window-size-info/esi: (addr winsize)
 45     # winsize is a type from the Linux kernel. We don't care how large it is.
 46     81 5/subop/subtract %esp 0x40/imm32
 47     89/<- %esi 4/r32/esp
 48     # ioctl(*Terminal-file-descriptor, TIOCGWINSZ, window-size-info)
 49     89/<- %edx 6/r32/esi
 50     b9/copy-to-ecx 0x5413/imm32/TIOCGWINSZ
 51     8b/-> *Terminal-file-descriptor 3/r32/ebx
 52     e8/call syscall_ioctl/disp32
 53     # some bitworking to extract 2 16-bit shorts
 54     8b/-> *esi 0/r32/eax
 55     81 4/subop/and %eax 0xffff/imm32
 56     8b/-> *esi 1/r32/ecx
 57     c1/shift 5/subop/logical-right %ecx 0x10/imm8
 58 $screen-size:end:
 59     # . reclaim locals
 60     81 0/subop/add %esp 0x40/imm32
 61     # . epilogue
 62     89/<- %esp 5/r32/ebp
 63     5d/pop-to-ebp
 64     c3/return
 65 
 66 clear-screen:
 67     # . prologue
 68     55/push-ebp
 69     89/<- %ebp 4/r32/esp
 70     #
 71     (write 2 Esc)
 72     (write 2 "[H")
 73     (write 2 Esc)
 74     (write 2 "[2J")
 75 $clear-screen:end:
 76     # . epilogue
 77     89/<- %esp 5/r32/ebp
 78     5d/pop-to-ebp
 79     c3/return
 80 
 81 move-cursor:  # row: int, column: int
 82     # . prologue
 83     55/push-ebp
 84     89/<- %ebp 4/r32/esp
 85     # . save registers
 86     50/push-eax
 87     51/push-ecx
 88     # var buf/ecx: (stream byte 32)
 89     81 5/subop/subtract %esp 0x20/imm32
 90     68/push 0x20/imm32/size
 91     68/push 0/imm32/read
 92     68/push 0/imm32/write
 93     89/<- %ecx 4/r32/esp
 94     # construct directive in buf
 95     (write %ecx Esc)
 96     (write %ecx "[")
 97     (print-int32-decimal %ecx *(ebp+8))
 98     (write %ecx ";")
 99     (print-int32-decimal %ecx *(ebp+0xc))
100     (write %ecx "H")
101     # flush
102     (write-stream 2 %ecx)
103 $move-cursor:end:
104     # . reclaim locals
105     81 0/subop/add %esp 0x2c/imm32
106     # . restore registers
107     59/pop-to-ecx
108     58/pop-to-eax
109     # . epilogue
110     89/<- %esp 5/r32/ebp
111     5d/pop-to-ebp
112     c3/return
113 
114 print-string:  # s: (addr array byte)
115     # . prologue
116     55/push-ebp
117     89/<- %ebp 4/r32/esp
118     #
119     (write 2 *(ebp+8))
120 $print-string:end:
121     # . epilogue
122     89/<- %esp 5/r32/ebp
123     5d/pop-to-ebp
124     c3/return
125 
126 print-int32-to-screen:  # n: int
127     # . prologue
128     55/push-ebp
129     89/<- %ebp 4/r32/esp
130     #
131     (print-int32-buffered Stdout *(ebp+8))
132     (flush Stdout)
133 $print-int32-to-screen:end:
134     # . epilogue
135     89/<- %esp 5/r32/ebp
136     5d/pop-to-ebp
137     c3/return
138 
139 reset-formatting:
140     # . prologue
141     55/push-ebp
142     89/<- %ebp 4/r32/esp
143     #
144     (write 2 Esc)
145     (write 2 "(B")
146     (write 2 Esc)
147     (write 2 "[m")
148 $reset-formatting:end:
149     # . epilogue
150     89/<- %esp 5/r32/ebp
151     5d/pop-to-ebp
152     c3/return
153 
154 start-color:  # fg: int, bg: int
155     # . prologue
156     55/push-ebp
157     89/<- %ebp 4/r32/esp
158     # . save registers
159     50/push-eax
160     51/push-ecx
161     # var buf/ecx: (stream byte 32)
162     81 5/subop/subtract %esp 0x20/imm32
163     68/push 0x20/imm32/size
164     68/push 0/imm32/read
165     68/push 0/imm32/write
166     89/<- %ecx 4/r32/esp
167     # construct directive in buf
168     # . set fg
169     (write %ecx Esc)
170     (write %ecx "[38;5;")
171     (print-int32-decimal %ecx *(ebp+8))
172     (write %ecx "m")
173     # . set bg
174     (write %ecx Esc)
175     (write %ecx "[48;5;")
176     (print-int32-decimal %ecx *(ebp+0xc))
177     (write %ecx "m")
178     # flush
179     (write-stream 2 %ecx)
180 $start-color:end:
181     # . reclaim locals
182     81 0/subop/add %esp 0x2c/imm32
183     # . restore registers
184     59/pop-to-ecx
185     58/pop-to-eax
186     # . epilogue
187     89/<- %esp 5/r32/ebp
188     5d/pop-to-ebp
189     c3/return
190 
191 start-bold:
192     # . prologue
193     55/push-ebp
194     89/<- %ebp 4/r32/esp
195     #
196     (write 2 Esc)
197     (write 2 "[1m")
198 $start-bold:end:
199     # . epilogue
200     89/<- %esp 5/r32/ebp
201     5d/pop-to-ebp
202     c3/return
203 
204 start-underline:
205     # . prologue
206     55/push-ebp
207     89/<- %ebp 4/r32/esp
208     #
209     (write 2 Esc)
210     (write 2 "[4m")
211 $start-underline:end:
212     # . epilogue
213     89/<- %esp 5/r32/ebp
214     5d/pop-to-ebp
215     c3/return
216 
217 start-reverse-video:
218     # . prologue
219     55/push-ebp
220     89/<- %ebp 4/r32/esp
221     #
222     (write 2 Esc)
223     (write 2 "[7m")
224 $start-reverse-video:end:
225     # . epilogue
226     89/<- %esp 5/r32/ebp
227     5d/pop-to-ebp
228     c3/return
229 
230 # might require enabling blinking in your terminal program
231 start-blinking:
232     # . prologue
233     55/push-ebp
234     89/<- %ebp 4/r32/esp
235     #
236     (write 2 Esc)
237     (write 2 "[5m")
238 $start-blinking:end:
239     # . epilogue
240     89/<- %esp 5/r32/ebp
241     5d/pop-to-ebp
242     c3/return
243 
244 hide-cursor:
245     # . prologue
246     55/push-ebp
247     89/<- %ebp 4/r32/esp
248     #
249     (write 2 Esc)
250     (write 2 "[?25l")
251 $hide-cursor:end:
252     # . epilogue
253     89/<- %esp 5/r32/ebp
254     5d/pop-to-ebp
255     c3/return
256 
257 show-cursor:
258     # . prologue
259     55/push-ebp
260     89/<- %ebp 4/r32/esp
261     #
262     (write 2 Esc)
263     (write 2 "[?12l")
264     (write 2 Esc)
265     (write 2 "[?25h")
266 $show-cursor:end:
267     # . epilogue
268     89/<- %esp 5/r32/ebp
269     5d/pop-to-ebp
270     c3/return
271 
272 # This is a low-level detail; I don't think everything should be a file.
273 #
274 # Open "/dev/tty" if necessary and cache its file descriptor in Terminal-file-descriptor
275 # where later primitives can use it.
276 _maybe-open-terminal:
277     81 7/subop/compare *Terminal-file-descriptor -1/imm32
278     75/jump-if-!= $_maybe-open-terminal:epilogue/disp8
279     # . save registers
280     50/push-eax
281     51/push-ecx
282     53/push-ebx
283     # open("/dev/tty", O_RDWR)
284     bb/copy-to-ebx Terminal-filename/imm32
285     b9/copy-to-ecx 2/imm32/O_RDWR
286     e8/call syscall_open/disp32
287     89/<- *Terminal-file-descriptor 0/r32/eax
288 $_maybe-open-terminal:end:
289     # . restore registers
290     5b/pop-to-ebx
291     59/pop-to-ecx
292     58/pop-to-eax
293 $_maybe-open-terminal:epilogue:
294     c3/return
295 
296 == data
297 
298 Terminal-file-descriptor:  # (addr int)
299   -1/imm32
300 
301 Esc:  # (addr array byte)
302   # size
303   1/imm32
304   # data
305   0x1b
306 
307 Terminal-filename:  # (addr kernel-string)
308   # "/dev/null"
309   2f/slash 64/d 65/e 76/v 2f/slash 74/t 74/t 79/y 0/nul