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