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