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-stream-to-real-screen:  # s: (addr stream byte)
135     # . prologue
136     55/push-ebp
137     89/<- %ebp 4/r32/esp
138     #
139     (write-stream-data Stdout *(ebp+8))
140     (flush Stdout)
141 $print-stream-to-real-screen:end:
142     # . epilogue
143     89/<- %esp 5/r32/ebp
144     5d/pop-to-ebp
145     c3/return
146 
147 # print a grapheme in utf-8 (only up to 4 bytes so far)
148 print-grapheme-to-real-screen:  # c: grapheme
149     # . prologue
150     55/push-ebp
151     89/<- %ebp 4/r32/esp
152     # . save registers
153     50/push-eax
154     # var curr/eax: byte = 0
155     b8/copy-to-eax 0/imm32
156     # curr = *(ebp+8)
157     8a/byte-> *(ebp+8) 0/r32/al
158     # if (curr == 0) return
159     3d/compare-eax-and 0/imm32
160     74/jump-if-= $print-grapheme-to-real-screen:end/disp8
161     #
162     (print-byte-to-real-screen %eax)
163     # curr = *(ebp+9)
164     8a/byte-> *(ebp+9) 0/r32/al
165     # if (curr == 0) return
166     3d/compare-eax-and 0/imm32
167     74/jump-if-= $print-grapheme-to-real-screen:end/disp8
168     #
169     (print-byte-to-real-screen %eax)
170     # curr = *(ebp+10)
171     8a/byte-> *(ebp+0xa) 0/r32/al
172     # if (curr == 0) return
173     3d/compare-eax-and 0/imm32
174     74/jump-if-= $print-grapheme-to-real-screen:end/disp8
175     #
176     (print-byte-to-real-screen %eax)
177     # curr = *(ebp+11)
178     8a/byte-> *(ebp+0xb) 0/r32/al
179     # if (curr == 0) return
180     3d/compare-eax-and 0/imm32
181     74/jump-if-= $print-grapheme-to-real-screen:end/disp8
182     #
183     (print-byte-to-real-screen %eax)
184 $print-grapheme-to-real-screen:end:
185     # . restore registers
186     58/pop-to-eax
187     # . epilogue
188     89/<- %esp 5/r32/ebp
189     5d/pop-to-ebp
190     c3/return
191 
192 print-byte-to-real-screen:  # c: byte
193     # . prologue
194     55/push-ebp
195     89/<- %ebp 4/r32/esp
196     # . save registers
197     51/push-ecx
198     # var s/ecx: (addr array byte)
199     ff 6/subop/push *(ebp+8)
200     68/push 1/imm32/size
201     89/<- %ecx 4/r32/esp
202     (write 1 %ecx)
203 $print-byte-to-real-screen:end:
204     # . reclaim locals
205     81 0/subop/add %esp 8/imm32
206     # . restore registers
207     59/pop-to-ecx
208     # . epilogue
209     89/<- %esp 5/r32/ebp
210     5d/pop-to-ebp
211     c3/return
212 
213 print-int32-hex-to-real-screen:  # n: int
214     # . prologue
215     55/push-ebp
216     89/<- %ebp 4/r32/esp
217     #
218     (write-int32-hex-buffered Stdout *(ebp+8))
219     (flush Stdout)
220 $print-int32-hex-to-real-screen:end:
221     # . epilogue
222     89/<- %esp 5/r32/ebp
223     5d/pop-to-ebp
224     c3/return
225 
226 reset-formatting-on-real-screen:
227     # . prologue
228     55/push-ebp
229     89/<- %ebp 4/r32/esp
230     #
231     (write 1 Esc)
232     (write 1 "(B")
233     (write 1 Esc)
234     (write 1 "[m")
235 $reset-formatting-on-real-screen:end:
236     # . epilogue
237     89/<- %esp 5/r32/ebp
238     5d/pop-to-ebp
239     c3/return
240 
241 start-color-on-real-screen:  # fg: int, bg: int
242     # . prologue
243     55/push-ebp
244     89/<- %ebp 4/r32/esp
245     # . save registers
246     51/push-ecx
247     # var buf/ecx: (stream byte 32)
248     81 5/subop/subtract %esp 0x20/imm32
249     68/push 0x20/imm32/size
250     68/push 0/imm32/read
251     68/push 0/imm32/write
252     89/<- %ecx 4/r32/esp
253     # construct directive in buf
254     # . set fg
255     (write %ecx Esc)
256     (write %ecx "[38;5;")
257     (write-int32-decimal %ecx *(ebp+8))
258     (write %ecx "m")
259     # . set bg
260     (write %ecx Esc)
261     (write %ecx "[48;5;")
262     (write-int32-decimal %ecx *(ebp+0xc))
263     (write %ecx "m")
264     # flush
265     (write-stream 2 %ecx)
266 $start-color-on-real-screen:end:
267     # . reclaim locals
268     81 0/subop/add %esp 0x2c/imm32
269     # . restore registers
270     59/pop-to-ecx
271     # . epilogue
272     89/<- %esp 5/r32/ebp
273     5d/pop-to-ebp
274     c3/return
275 
276 start-bold-on-real-screen:
277     # . prologue
278     55/push-ebp
279     89/<- %ebp 4/r32/esp
280     #
281     (write 1 Esc)
282     (write 1 "[1m")
283 $start-bold-on-real-screen:end:
284     # . epilogue
285     89/<- %esp 5/r32/ebp
286     5d/pop-to-ebp
287     c3/return
288 
289 start-underline-on-real-screen:
290     # . prologue
291     55/push-ebp
292     89/<- %ebp 4/r32/esp
293     #
294     (write 1 Esc)
295     (write 1 "[4m")
296 $start-underline-on-real-screen:end:
297     # . epilogue
298     89/<- %esp 5/r32/ebp
299     5d/pop-to-ebp
300     c3/return
301 
302 start-reverse-video-on-real-screen:
303     # . prologue
304     55/push-ebp
305     89/<- %ebp 4/r32/esp
306     #
307     (write 1 Esc)
308     (write 1 "[7m")
309 $start-reverse-video-on-real-screen:end:
310     # . epilogue
311     89/<- %esp 5/r32/ebp
312     5d/pop-to-ebp
313     c3/return
314 
315 # might require enabling blinking in your terminal program
316 start-blinking-on-real-screen:
317     # . prologue
318     55/push-ebp
319     89/<- %ebp 4/r32/esp
320     #
321     (write 1 Esc)
322     (write 1 "[5m")
323 $start-blinking-on-real-screen:end:
324     # . epilogue
325     89/<- %esp 5/r32/ebp
326     5d/pop-to-ebp
327     c3/return
328 
329 hide-cursor-on-real-screen:
330     # . prologue
331     55/push-ebp
332     89/<- %ebp 4/r32/esp
333     #
334     (write 1 Esc)
335     (write 1 "[?25l")
336 $hide-cursor-on-real-screen:end:
337     # . epilogue
338     89/<- %esp 5/r32/ebp
339     5d/pop-to-ebp
340     c3/return
341 
342 show-cursor-on-real-screen:
343     # . prologue
344     55/push-ebp
345     89/<- %ebp 4/r32/esp
346     #
347     (write 1 Esc)
348     (write 1 "[?12l")
349     (write 1 Esc)
350     (write 1 "[?25h")
351 $show-cursor-on-real-screen:end:
352     # . epilogue
353     89/<- %esp 5/r32/ebp
354     5d/pop-to-ebp
355     c3/return
356 
357 # This is a low-level detail; I don't think everything should be a file.
358 #
359 # Open "/dev/tty" if necessary and cache its file descriptor in Terminal-file-descriptor
360 # where later primitives can use it.
361 _maybe-open-terminal:
362     81 7/subop/compare *Terminal-file-descriptor -1/imm32
363     75/jump-if-!= $_maybe-open-terminal:epilogue/disp8
364     # . save registers
365     50/push-eax
366     51/push-ecx
367     53/push-ebx
368     # open("/dev/tty", O_RDWR)
369     bb/copy-to-ebx Terminal-filename/imm32
370     b9/copy-to-ecx 2/imm32/O_RDWR
371     e8/call syscall_open/disp32
372     89/<- *Terminal-file-descriptor 0/r32/eax
373 $_maybe-open-terminal:end:
374     # . restore registers
375     5b/pop-to-ebx
376     59/pop-to-ecx
377     58/pop-to-eax
378 $_maybe-open-terminal:epilogue:
379     c3/return
380 
381 == data
382 
383 Terminal-file-descriptor:  # (addr int)
384   -1/imm32
385 
386 Esc:  # (addr array byte)
387   # size
388   1/imm32
389   # data
390   0x1b
391 
392 Terminal-filename:  # (addr kernel-string)
393   # "/dev/null"
394   2f/slash 64/d 65/e 76/v 2f/slash 74/t 74/t 79/y 0/nul