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 draw-stream-wrapping-right-then-down-from-cursor screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, color: int, background-color: 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   var end-x/edx: int <- copy cursor-x
204   end-x <- increment
205   compare end-x, xmax
206   {
207     break-if-<
208     cursor-x <- copy xmin
209     cursor-y <- increment
210   }
211   cursor-x, cursor-y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
212 }
213 
214 fn draw-stream-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), stream: (addr stream byte), color: int, background-color: int {
215   var width/eax: int <- copy 0
216   var height/ecx: int <- copy 0
217   width, height <- screen-size screen
218   draw-stream-wrapping-right-then-down-from-cursor screen, stream, 0/xmin, 0/ymin, width, height, color, background-color
219 }
220 
221 fn move-cursor-rightward-and-downward screen: (addr screen), xmin: int, xmax: 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   cursor-x <- increment
226   compare cursor-x, xmax
227   {
228     break-if-<
229     cursor-x <- copy xmin
230     cursor-y <- increment
231   }
232   set-cursor-position screen, cursor-x, cursor-y
233 }
234 
235 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 {
236   var x2/eax: int <- copy 0
237   var y2/ecx: int <- copy 0
238   x2, y2 <- screen-size screen  # width, height
239   x2, y2 <- draw-text-wrapping-right-then-down screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
240   return x2, y2  # cursor-x, cursor-y
241 }
242 
243 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 {
244   var cursor-x/eax: int <- copy 0
245   var cursor-y/ecx: int <- copy 0
246   cursor-x, cursor-y <- cursor-position screen
247   var end-x/edx: int <- copy cursor-x
248   end-x <- increment
249   compare end-x, xmax
250   {
251     break-if-<
252     cursor-x <- copy xmin
253     cursor-y <- increment
254   }
255   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
256 }
257 
258 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
259   var width/eax: int <- copy 0
260   var height/ecx: int <- copy 0
261   width, height <- screen-size screen
262   draw-text-wrapping-right-then-down-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
263 }
264 
265 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 {
266   var stream-storage: (stream byte 0x100)
267   var stream/esi: (addr stream byte) <- address stream-storage
268   write-int32-hex stream, n
269   var xcurr/edx: int <- copy x
270   var ycurr/ecx: int <- copy y
271   {
272     var g/eax: grapheme <- read-grapheme stream
273     compare g, 0xffffffff/end-of-file
274     break-if-=
275     draw-grapheme screen, g, xcurr, ycurr, color, background-color
276     xcurr <- increment
277     compare xcurr, xmax
278     {
279       break-if-<
280       xcurr <- copy xmin
281       ycurr <- increment
282     }
283     loop
284   }
285   set-cursor-position screen, xcurr, ycurr
286   return xcurr, ycurr
287 }
288 
289 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 {
290   var x2/eax: int <- copy 0
291   var y2/ecx: int <- copy 0
292   x2, y2 <- screen-size screen  # width, height
293   x2, y2 <- draw-int32-hex-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
294   return x2, y2  # cursor-x, cursor-y
295 }
296 
297 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 {
298   var cursor-x/eax: int <- copy 0
299   var cursor-y/ecx: int <- copy 0
300   cursor-x, cursor-y <- cursor-position screen
301   var end-x/edx: int <- copy cursor-x
302   end-x <- increment
303   compare end-x, xmax
304   {
305     break-if-<
306     cursor-x <- copy xmin
307     cursor-y <- increment
308   }
309   cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
310 }
311 
312 fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
313   var width/eax: int <- copy 0
314   var height/ecx: int <- copy 0
315   width, height <- screen-size screen
316   draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
317 }
318 
319 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 {
320   var stream-storage: (stream byte 0x100)
321   var stream/esi: (addr stream byte) <- address stream-storage
322   write-int32-decimal stream, n
323   var xcurr/edx: int <- copy x
324   var ycurr/ecx: int <- copy y
325   {
326     var g/eax: grapheme <- read-grapheme stream
327     compare g, 0xffffffff/end-of-file
328     break-if-=
329     draw-grapheme screen, g, xcurr, ycurr, color, background-color
330     xcurr <- increment
331     compare xcurr, xmax
332     {
333       break-if-<
334       xcurr <- copy xmin
335       ycurr <- increment
336     }
337     loop
338   }
339   set-cursor-position screen, xcurr, ycurr
340   return xcurr, ycurr
341 }
342 
343 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 {
344   var x2/eax: int <- copy 0
345   var y2/ecx: int <- copy 0
346   x2, y2 <- screen-size screen  # width, height
347   x2, y2 <- draw-int32-decimal-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
348   return x2, y2  # cursor-x, cursor-y
349 }
350 
351 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 {
352   var cursor-x/eax: int <- copy 0
353   var cursor-y/ecx: int <- copy 0
354   cursor-x, cursor-y <- cursor-position screen
355   var end-x/edx: int <- copy cursor-x
356   end-x <- increment
357   compare end-x, xmax
358   {
359     break-if-<
360     cursor-x <- copy xmin
361     cursor-y <- increment
362   }
363   cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
364 }
365 
366 fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
367   var width/eax: int <- copy 0
368   var height/ecx: int <- copy 0
369   width, height <- screen-size screen
370   draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
371 }
372 
373 ## Text direction: down then right
374 
375 # draw a single line of text vertically from x, y to ymax
376 # return the next 'y' coordinate
377 # if there isn't enough space, truncate
378 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
379   var stream-storage: (stream byte 0x100)
380   var stream/esi: (addr stream byte) <- address stream-storage
381   write stream, text
382   var ycurr/eax: int <- draw-stream-downward screen, stream, x, y, ymax, color, background-color
383   return ycurr
384 }
385 
386 # draw a single-line stream vertically from x, y to ymax
387 # return the next 'y' coordinate
388 # if there isn't enough space, truncate
389 fn draw-stream-downward screen: (addr screen), stream: (addr stream byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
390   var ycurr/ecx: int <- copy y
391   {
392     var g/eax: grapheme <- read-grapheme stream
393     compare g, 0xffffffff/end-of-file
394     break-if-=
395     draw-grapheme screen, g, x, ycurr, color, background-color
396     ycurr <- increment
397     loop
398   }
399   set-cursor-position screen, x, ycurr
400   return ycurr
401 }
402 
403 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int, background-color: int {
404   var cursor-x/eax: int <- copy 0
405   var cursor-y/ecx: int <- copy 0
406   cursor-x, cursor-y <- cursor-position screen
407   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color, background-color
408 }
409 
410 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
411 # return the next (x, y) coordinate in raster order where drawing stopped
412 # that way the caller can draw more if given the same min and max bounding-box.
413 # if there isn't enough space, truncate
414 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 {
415   var stream-storage: (stream byte 0x100)
416   var stream/esi: (addr stream byte) <- address stream-storage
417   write stream, text
418   var x/eax: int <- copy _x
419   var y/ecx: int <- copy _y
420   x, y <- draw-stream-wrapping-down-then-right screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color
421   return x, y
422 }
423 
424 # draw a stream down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
425 # return the next (x, y) coordinate in raster order where drawing stopped
426 # that way the caller can draw more if given the same min and max bounding-box.
427 # if there isn't enough space, truncate
428 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 {
429   var xcurr/edx: int <- copy x
430   var ycurr/ecx: int <- copy y
431   {
432     var g/eax: grapheme <- read-grapheme stream
433     compare g, 0xffffffff/end-of-file
434     break-if-=
435     draw-grapheme screen, g, xcurr, ycurr, color, background-color
436     ycurr <- increment
437     compare ycurr, ymax
438     {
439       break-if-<
440       xcurr <- increment
441       ycurr <- copy ymin
442     }
443     loop
444   }
445   set-cursor-position screen, xcurr, ycurr
446   return xcurr, ycurr
447 }
448 
449 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 {
450   var x2/eax: int <- copy 0
451   var y2/ecx: int <- copy 0
452   x2, y2 <- screen-size screen  # width, height
453   x2, y2 <- draw-text-wrapping-down-then-right screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
454   return x2, y2  # cursor-x, cursor-y
455 }
456 
457 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 {
458   var cursor-x/eax: int <- copy 0
459   var cursor-y/ecx: int <- copy 0
460   cursor-x, cursor-y <- cursor-position screen
461   var end-y/edx: int <- copy cursor-y
462   end-y <- increment
463   compare end-y, ymax
464   {
465     break-if-<
466     cursor-x <- increment
467     cursor-y <- copy ymin
468   }
469   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
470 }
471 
472 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
473   var width/eax: int <- copy 0
474   var height/ecx: int <- copy 0
475   width, height <- screen-size screen
476   draw-text-wrapping-down-then-right-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
477 }
478 
479 # hacky error-handling
480 # just go into an infinite loop
481 fn abort e: (addr array byte) {
482   var dummy1/eax: int <- copy 0
483   var dummy2/ecx: int <- copy 0
484   dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen 0/screen, e, 0/x, 0x2f/y, 0xf/fg=white, 0xc/bg=red
485   {
486     loop
487   }
488 }