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 2 Esc)
 15     (write 2 "[?1049h")
 16     #
 17     (clear-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 2 Esc)
 30     (write 2 "[?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 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 $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-screen:
 76     # . prologue
 77     55/push-ebp
 78     89/<- %ebp 4/r32/esp
 79     #
 80     (write 2 Esc)
 81     (write 2 "[H")
 82     (write 2 Esc)
 83     (write 2 "[2J")
 84 $clear-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-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-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-screen:  # s: (addr array byte)
123     # . prologue
124     55/push-ebp
125     89/<- %ebp 4/r32/esp
126     #
127     (write 2 *(ebp+8))
128 $print-string-to-screen:end:
129     # . epilogue
130     89/<- %esp 5/r32/ebp
131     5d/pop-to-ebp
132     c3/return
133 
134 print-byte-to-screen:  # c: byte
135     # . prologue
136     55/push-ebp
137     89/<- %ebp 4/r32/esp
138     # . save registers
139     51/push-ecx
140     # var s/ecx: (addr array byte)
141     ff 6/subop/push *(ebp+8)
142     68/push 4/imm32/size
143     89/<- %ecx 4/r32/esp
144     (write 2 %ecx)
145 $print-byte-to-screen:end:
146     # . reclaim locals
147     81 0/subop/add %esp 8/imm32
148     # . restore registers
149     59/pop-to-ecx
150     # . epilogue
151     89/<- %esp 5/r32/ebp
152     5d/pop-to-ebp
153     c3/return
154 
155 print-int32-hex-to-screen:  # n: int
156     # . prologue
157     55/push-ebp
158     89/<- %ebp 4/r32/esp
159     #
160     (write-int32-hex-buffered Stdout *(ebp+8))
161     (flush Stdout)
162 $print-int32-hex-to-screen:end:
163     # . epilogue
164     89/<- %esp 5/r32/ebp
165     5d/pop-to-ebp
166     c3/return
167 
168 reset-formatting-on-screen:
169     # . prologue
170     55/push-ebp
171     89/<- %ebp 4/r32/esp
172     #
173     (write 2 Esc)
174     (write 2 "(B")
175     (write 2 Esc)
176     (write 2 "[m")
177 $reset-formatting-on-screen:end:
178     # . epilogue
179     89/<- %esp 5/r32/ebp
180     5d/pop-to-ebp
181     c3/return
182 
183 start-color-on-screen:  # fg: int, bg: int
184     # . prologue
185     55/push-ebp
186     89/<- %ebp 4/r32/esp
187     # . save registers
188     51/push-ecx
189     # var buf/ecx: (stream byte 32)
190     81 5/subop/subtract %esp 0x20/imm32
191     68/push 0x20/imm32/size
192     68/push 0/imm32/read
193     68/push 0/imm32/write
194     89/<- %ecx 4/r32/esp
195     # construct directive in buf
196     # . set fg
197     (write %ecx Esc)
198     (write %ecx "[38;5;")
199     (write-int32-decimal %ecx *(ebp+8))
200     (write %ecx "m")
201     # . set bg
202     (write %ecx Esc)
203     (write %ecx "[48;5;")
204     (write-int32-decimal %ecx *(ebp+0xc))
205     (write %ecx "m")
206     # flush
207     (write-stream 2 %ecx)
208 $start-color-on-screen:end:
209     # . reclaim locals
210     81 0/subop/add %esp 0x2c/imm32
211     # . restore registers
212     59/pop-to-ecx
213     # . epilogue
214     89/<- %esp 5/r32/ebp
215     5d/pop-to-ebp
216     c3/return
217 
218 start-bold-on-screen:
219     # . prologue
220     55/push-ebp
221     89/<- %ebp 4/r32/esp
222     #
223     (write 2 Esc)
224     (write 2 "[1m")
225 $start-bold-on-screen:end:
226     # . epilogue
227     89/<- %esp 5/r32/ebp
228     5d/pop-to-ebp
229     c3/return
230 
231 start-underline-on-screen:
232     # . prologue
233     55/push-ebp
234     89/<- %ebp 4/r32/esp
235     #
236     (write 2 Esc)
237     (write 2 "[4m")
238 $start-underline-on-screen:end:
239     # . epilogue
240     89/<- %esp 5/r32/ebp
241     5d/pop-to-ebp
242     c3/return
243 
244 start-reverse-video-on-screen:
245     # . prologue
246     55/push-ebp
247     89/<- %ebp 4/r32/esp
248     #
249     (write 2 Esc)
250     (write 2 "[7m")
251 $start-reverse-video-on-screen:end:
252     # . epilogue
253     89/<- %esp 5/r32/ebp
254     5d/pop-to-ebp
255     c3/return
256 
257 # might require enabling blinking in your terminal program
258 start-blinking-on-screen:
259     # . prologue
260     55/push-ebp
261     89/<- %ebp 4/r32/esp
262     #
263     (write 2 Esc)
264     (write 2 "[5m")
265 $start-blinking-on-screen:end:
266     # . epilogue
267     89/<- %esp 5/r32/ebp
268     5d/pop-to-ebp
269     c3/return
270 
271 hide-cursor-on-screen:
272     # . prologue
273     55/push-ebp
274     89/<- %ebp 4/r32/esp
275     #
276     (write 2 Esc)
277     (write 2 "[?25l")
278 $hide-cursor-on-screen:end:
279     # . epilogue
280     89/<- %esp 5/r32/ebp
281     5d/pop-to-ebp
282     c3/return
283 
284 show-cursor-on-screen:
285     # . prologue
286     55/push-ebp
287     89/<- %ebp 4/r32/esp
288     #
289     (write 2 Esc)
290     (write 2 "[?12l")
291     (write 2 Esc)
292     (write 2 "[?25h")
293 $show-cursor-on-screen:end:
294     # . epilogue
295     89/<- %esp 5/r32/ebp
296     5d/pop-to-ebp
297     c3/return
298 
299 # This is a low-level detail; I don't think everything should be a file.
300 #
301 # Open "/dev/tty" if necessary and cache its file descriptor in Terminal-file-descriptor
302 # where later primitives can use it.
303 _maybe-open-terminal:
304     81 7/subop/compare *Terminal-file-descriptor -1/imm32
305     75/jump-if-!= $_maybe-open-terminal:epilogue/disp8
306     # . save registers
307     50/push-eax
308     51/push-ecx
309     53/push-ebx
310     # open("/dev/tty", O_RDWR)
311     bb/copy-to-ebx Terminal-filename/imm32
312     b9/copy-to-ecx 2/imm32/O_RDWR
313     e8/call syscall_open/disp32
314     89/<- *Terminal-file-descriptor 0/r32/eax
315 $_maybe-open-terminal:end:
316     # . restore registers
317     5b/pop-to-ebx
318     59/pop-to-ecx
319     58/pop-to-eax
320 $_maybe-open-terminal:epilogue:
321     c3/return
322 
323 == data
324 
325 Terminal-file-descriptor:  # (addr int)
326   -1/imm32
327 
328 Esc:  # (addr array byte)
329   # size
330   1/imm32
331   # data
332   0x1b
333 
334 Terminal-filename:  # (addr kernel-string)
335   # "/dev/null"
336   2f/slash 64/d 65/e 76/v 2f/slash 74/t 74/t 79/y 0/nul