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