https://github.com/akkartik/mu/blob/main/501draw-text.mu
  1 # some primitives for moving the cursor without making assumptions about
  2 # raster order
  3 fn move-cursor-left screen: (addr screen) {
  4   var cursor-x/eax: int <- copy 0
  5   var cursor-y/ecx: int <- copy 0
  6   cursor-x, cursor-y <- cursor-position screen
  7   compare cursor-x, 0
  8   {
  9     break-if->
 10     return
 11   }
 12   cursor-x <- decrement
 13   set-cursor-position screen, cursor-x, cursor-y
 14 }
 15 
 16 fn move-cursor-right screen: (addr screen) {
 17   var _width/eax: int <- copy 0
 18   var dummy/ecx: int <- copy 0
 19   _width, dummy <- screen-size screen
 20   var limit/edx: int <- copy _width
 21   limit <- decrement
 22   var cursor-x/eax: int <- copy 0
 23   var cursor-y/ecx: int <- copy 0
 24   cursor-x, cursor-y <- cursor-position screen
 25   compare cursor-x, limit
 26   {
 27     break-if-<
 28     return
 29   }
 30   cursor-x <- increment
 31   set-cursor-position screen, cursor-x, cursor-y
 32 }
 33 
 34 fn move-cursor-up screen: (addr screen) {
 35   var cursor-x/eax: int <- copy 0
 36   var cursor-y/ecx: int <- copy 0
 37   cursor-x, cursor-y <- cursor-position screen
 38   compare cursor-y, 0
 39   {
 40     break-if->
 41     return
 42   }
 43   cursor-y <- decrement
 44   set-cursor-position screen, cursor-x, cursor-y
 45 }
 46 
 47 fn move-cursor-down screen: (addr screen) {
 48   var dummy/eax: int <- copy 0
 49   var _height/ecx: int <- copy 0
 50   dummy, _height <- screen-size screen
 51   var limit/edx: int <- copy _height
 52   limit <- decrement
 53   var cursor-x/eax: int <- copy 0
 54   var cursor-y/ecx: int <- copy 0
 55   cursor-x, cursor-y <- cursor-position screen
 56   compare cursor-y, limit
 57   {
 58     break-if-<
 59     return
 60   }
 61   cursor-y <- increment
 62   set-cursor-position screen, cursor-x, cursor-y
 63 }
 64 
 65 fn move-cursor-to-left-margin-of-next-line screen: (addr screen) {
 66   var dummy/eax: int <- copy 0
 67   var _height/ecx: int <- copy 0
 68   dummy, _height <- screen-size screen
 69   var limit/edx: int <- copy _height
 70   limit <- decrement
 71   var cursor-x/eax: int <- copy 0
 72   var cursor-y/ecx: int <- copy 0
 73   cursor-x, cursor-y <- cursor-position screen
 74   compare cursor-y, limit
 75   {
 76     break-if-<
 77     return
 78   }
 79   cursor-y <- increment
 80   cursor-x <- copy 0
 81   set-cursor-position screen, cursor-x, cursor-y
 82 }
 83 
 84 fn draw-grapheme-at-cursor screen: (addr screen), g: grapheme, color: int, background-color: int {
 85   var cursor-x/eax: int <- copy 0
 86   var cursor-y/ecx: int <- copy 0
 87   cursor-x, cursor-y <- cursor-position screen
 88   draw-grapheme screen, g, cursor-x, cursor-y, color, background-color
 89 }
 90 
 91 # we can't really render non-ASCII yet, but when we do we'll be ready
 92 fn draw-code-point-at-cursor screen: (addr screen), c: code-point, color: int, background-color: int {
 93   var g/eax: grapheme <- copy c
 94   draw-grapheme-at-cursor screen, g, color, background-color
 95 }
 96 
 97 # draw a single line of text from x, y to xmax
 98 # return the next 'x' coordinate
 99 # if there isn't enough space, truncate
100 fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int {
101   var stream-storage: (stream byte 0x100)
102   var stream/esi: (addr stream byte) <- address stream-storage
103   write stream, text
104   var xcurr/eax: int <- draw-stream-rightward screen, stream, x, xmax, y, color, background-color
105   return xcurr
106 }
107 
108 # draw a single-line stream from x, y to xmax
109 # return the next 'x' coordinate
110 # if there isn't enough space, truncate
111 fn draw-stream-rightward screen: (addr screen), stream: (addr stream byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int {
112   var xcurr/ecx: int <- copy x
113   {
114     var g/eax: grapheme <- read-grapheme stream
115     compare g, 0xffffffff/end-of-file
116     break-if-=
117     draw-grapheme screen, g, xcurr, y, color, background-color
118     xcurr <- increment
119     loop
120   }
121   set-cursor-position screen, xcurr, y
122   return xcurr
123 }
124 
125 fn draw-text-rightward-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int {
126   var width/eax: int <- copy 0
127   var height/ecx: int <- copy 0
128   width, height <- screen-size screen
129   var result/eax: int <- draw-text-rightward screen, text, x, width, y, color, background-color
130   return result
131 }
132 
133 fn draw-text-rightward-from-cursor screen: (addr screen), text: (addr array byte), xmax: int, color: int, background-color: int {
134   var cursor-x/eax: int <- copy 0
135   var cursor-y/ecx: int <- copy 0
136   cursor-x, cursor-y <- cursor-position screen
137   cursor-x <- draw-text-rightward screen, text, cursor-x, xmax, cursor-y, color, background-color
138   set-cursor-position screen, cursor-x, cursor-y
139 }
140 
141 fn render-grapheme screen: (addr screen), g: grapheme, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
142   compare g, 0xa/newline
143   var x/eax: int <- copy x
144   {
145     break-if-!=
146     # minimum effort to clear cursor
147     draw-code-point screen, 0x20/space, x, y, color, background-color
148     x <- copy xmin
149     increment y
150     return x, y
151   }
152   draw-grapheme screen, g, x, y, color, background-color
153   x <- increment
154   compare x, xmax
155   {
156     break-if-<
157     x <- copy xmin
158     increment y
159   }
160   return x, y
161 }
162 
163 # draw text in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
164 # return the next (x, y) coordinate in raster order where drawing stopped
165 # that way the caller can draw more if given the same min and max bounding-box.
166 # if there isn't enough space, truncate
167 fn draw-text-wrapping-right-then-down screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
168   var stream-storage: (stream byte 0x100)
169   var stream/esi: (addr stream byte) <- address stream-storage
170   write stream, text
171   var x/eax: int <- copy _x
172   var y/ecx: int <- copy _y
173   x, y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color
174   return x, y
175 }
176 
177 # draw a stream in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
178 # return the next (x, y) coordinate in raster order where drawing stopped
179 # that way the caller can draw more if given the same min and max bounding-box.
180 # if there isn't enough space, truncate
181 fn draw-stream-wrapping-right-then-down screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
182   var xcurr/eax: int <- copy x
183   var ycurr/ecx: int <- copy y
184   var g/ebx: grapheme <- copy 0
185   {
186     {
187       var _g/eax: grapheme <- read-grapheme stream
188       g <- copy _g
189     }
190     compare g, 0xffffffff/end-of-file
191     break-if-=
192     xcurr, ycurr <- render-grapheme screen, g, xmin, ymin, xmax, ymax, xcurr, ycurr, color, background-color
193     loop
194   }
195   set-cursor-position screen, xcurr, ycurr
196   return xcurr, ycurr
197 }
198 
199 fn move-cursor-rightward-and-downward screen: (addr screen), xmin: int, xmax: int {
200   var cursor-x/eax: int <- copy 0
201   var cursor-y/ecx: int <- copy 0
202   cursor-x, cursor-y <- cursor-position screen
203   cursor-x <- increment
204   compare cursor-x, xmax
205   {
206     break-if-<
207     cursor-x <- copy xmin
208     cursor-y <- increment
209   }
210   set-cursor-position screen, cursor-x, cursor-y
211 }
212 
213 fn draw-text-wrapping-right-then-down-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
214   var x2/eax: int <- copy 0
215   var y2/ecx: int <- copy 0
216   x2, y2 <- screen-size screen  # width, height
217   x2, y2 <- draw-text-wrapping-right-then-down screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
218   return x2, y2  # cursor-x, cursor-y
219 }
220 
221 fn draw-text-wrapping-right-then-down-from-cursor screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int {
222   var cursor-x/eax: int <- copy 0
223   var cursor-y/ecx: int <- copy 0
224   cursor-x, cursor-y <- cursor-position screen
225   var end-x/edx: int <- copy cursor-x
226   end-x <- increment
227   compare end-x, xmax
228   {
229     break-if-<
230     cursor-x <- copy xmin
231     cursor-y <- increment
232   }
233   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
234 }
235 
236 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
237   var width/eax: int <- copy 0
238   var height/ecx: int <- copy 0
239   width, height <- screen-size screen
240   draw-text-wrapping-right-then-down-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
241 }
242 
243 fn draw-int32-hex-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
244   var stream-storage: (stream byte 0x100)
245   var stream/esi: (addr stream byte) <- address stream-storage
246   write-int32-hex stream, n
247   var xcurr/edx: int <- copy x
248   var ycurr/ecx: int <- copy y
249   {
250     var g/eax: grapheme <- read-grapheme stream
251     compare g, 0xffffffff/end-of-file
252     break-if-=
253     draw-grapheme screen, g, xcurr, ycurr, color, background-color
254     xcurr <- increment
255     compare xcurr, xmax
256     {
257       break-if-<
258       xcurr <- copy xmin
259       ycurr <- increment
260     }
261     loop
262   }
263   set-cursor-position screen, xcurr, ycurr
264   return xcurr, ycurr
265 }
266 
267 fn draw-int32-hex-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
268   var x2/eax: int <- copy 0
269   var y2/ecx: int <- copy 0
270   x2, y2 <- screen-size screen  # width, height
271   x2, y2 <- draw-int32-hex-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
272   return x2, y2  # cursor-x, cursor-y
273 }
274 
275 fn draw-int32-hex-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int {
276   var cursor-x/eax: int <- copy 0
277   var cursor-y/ecx: int <- copy 0
278   cursor-x, cursor-y <- cursor-position screen
279   var end-x/edx: int <- copy cursor-x
280   end-x <- increment
281   compare end-x, xmax
282   {
283     break-if-<
284     cursor-x <- copy xmin
285     cursor-y <- increment
286   }
287   cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
288 }
289 
290 fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
291   var width/eax: int <- copy 0
292   var height/ecx: int <- copy 0
293   width, height <- screen-size screen
294   draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
295 }
296 
297 fn draw-int32-decimal-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
298   var stream-storage: (stream byte 0x100)
299   var stream/esi: (addr stream byte) <- address stream-storage
300   write-int32-decimal stream, n
301   var xcurr/edx: int <- copy x
302   var ycurr/ecx: int <- copy y
303   {
304     var g/eax: grapheme <- read-grapheme stream
305     compare g, 0xffffffff/end-of-file
306     break-if-=
307     draw-grapheme screen, g, xcurr, ycurr, color, background-color
308     xcurr <- increment
309     compare xcurr, xmax
310     {
311       break-if-<
312       xcurr <- copy xmin
313       ycurr <- increment
314     }
315     loop
316   }
317   set-cursor-position screen, xcurr, ycurr
318   return xcurr, ycurr
319 }
320 
321 fn draw-int32-decimal-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
322   var x2/eax: int <- copy 0
323   var y2/ecx: int <- copy 0
324   x2, y2 <- screen-size screen  # width, height
325   x2, y2 <- draw-int32-decimal-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
326   return x2, y2  # cursor-x, cursor-y
327 }
328 
329 fn draw-int32-decimal-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int {
330   var cursor-x/eax: int <- copy 0
331   var cursor-y/ecx: int <- copy 0
332   cursor-x, cursor-y <- cursor-position screen
333   var end-x/edx: int <- copy cursor-x
334   end-x <- increment
335   compare end-x, xmax
336   {
337     break-if-<
338     cursor-x <- copy xmin
339     cursor-y <- increment
340   }
341   cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
342 }
343 
344 fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
345   var width/eax: int <- copy 0
346   var height/ecx: int <- copy 0
347   width, height <- screen-size screen
348   draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
349 }
350 
351 ## Text direction: down then right
352 
353 # draw a single line of text vertically from x, y to ymax
354 # return the next 'y' coordinate
355 # if there isn't enough space, truncate
356 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
357   var stream-storage: (stream byte 0x100)
358   var stream/esi: (addr stream byte) <- address stream-storage
359   write stream, text
360   var ycurr/eax: int <- draw-stream-downward screen, stream, x, y, ymax, color, background-color
361   return ycurr
362 }
363 
364 # draw a single-line stream vertically from x, y to ymax
365 # return the next 'y' coordinate
366 # if there isn't enough space, truncate
367 fn draw-stream-downward screen: (addr screen), stream: (addr stream byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
368   var ycurr/ecx: int <- copy y
369   {
370     var g/eax: grapheme <- read-grapheme stream
371     compare g, 0xffffffff/end-of-file
372     break-if-=
373     draw-grapheme screen, g, x, ycurr, color, background-color
374     ycurr <- increment
375     loop
376   }
377   set-cursor-position screen, x, ycurr
378   return ycurr
379 }
380 
381 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int, background-color: int {
382   var cursor-x/eax: int <- copy 0
383   var cursor-y/ecx: int <- copy 0
384   cursor-x, cursor-y <- cursor-position screen
385   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color, background-color
386 }
387 
388 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
389 # return the next (x, y) coordinate in raster order where drawing stopped
390 # that way the caller can draw more if given the same min and max bounding-box.
391 # if there isn't enough space, truncate
392 fn draw-text-wrapping-down-then-right screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
393   var stream-storage: (stream byte 0x100)
394   var stream/esi: (addr stream byte) <- address stream-storage
395   write stream, text
396   var x/eax: int <- copy _x
397   var y/ecx: int <- copy _y
398   x, y <- draw-stream-wrapping-down-then-right screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color
399   return x, y
400 }
401 
402 # draw a stream down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
403 # return the next (x, y) coordinate in raster order where drawing stopped
404 # that way the caller can draw more if given the same min and max bounding-box.
405 # if there isn't enough space, truncate
406 fn draw-stream-wrapping-down-then-right screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
407   var xcurr/edx: int <- copy x
408   var ycurr/ecx: int <- copy y
409   {
410     var g/eax: grapheme <- read-grapheme stream
411     compare g, 0xffffffff/end-of-file
412     break-if-=
413     draw-grapheme screen, g, xcurr, ycurr, color, background-color
414     ycurr <- increment
415     compare ycurr, ymax
416     {
417       break-if-<
418       xcurr <- increment
419       ycurr <- copy ymin
420     }
421     loop
422   }
423   set-cursor-position screen, xcurr, ycurr
424   return xcurr, ycurr
425 }
426 
427 fn draw-text-wrapping-down-then-right-over-full-screen screen: (addr screen), text: (addr array byte), x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
428   var x2/eax: int <- copy 0
429   var y2/ecx: int <- copy 0
430   x2, y2 <- screen-size screen  # width, height
431   x2, y2 <- draw-text-wrapping-down-then-right screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
432   return x2, y2  # cursor-x, cursor-y
433 }
434 
435 fn draw-text-wrapping-down-then-right-from-cursor screen: (addr screen), text: (addr array byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: int {
436   var cursor-x/eax: int <- copy 0
437   var cursor-y/ecx: int <- copy 0
438   cursor-x, cursor-y <- cursor-position screen
439   var end-y/edx: int <- copy cursor-y
440   end-y <- increment
441   compare end-y, ymax
442   {
443     break-if-<
444     cursor-x <- increment
445     cursor-y <- copy ymin
446   }
447   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
448 }
449 
450 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
451   var width/eax: int <- copy 0
452   var height/ecx: int <- copy 0
453   width, height <- screen-size screen
454   draw-text-wrapping-down-then-right-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
455 }
456 
457 # hacky error-handling
458 # just go into an infinite loop
459 fn abort e: (addr array byte) {
460   var dummy1/eax: int <- copy 0
461   var dummy2/ecx: int <- copy 0
462   dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen 0/screen, e, 0/x, 0x2f/y, 0xf/fg=white, 0xc/bg=red
463   {
464     loop
465   }
466 }