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