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-code-point-at-cursor-over-full-screen screen: (addr screen), c: code-point, 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   var _offset/eax: int <- draw-code-point screen, c, cursor-x, cursor-y, color, background-color
 89   var offset/edx: int <- copy _offset
 90   var width/eax: int <- copy 0
 91   var dummy/ecx: int <- copy 0
 92   width, dummy <- screen-size screen
 93   move-cursor-rightward-and-downward screen, 0 width
 94   offset <- decrement
 95   compare offset, 0
 96   {
 97     break-if-=
 98     # should never move downward here
 99     move-cursor-rightward-and-downward screen, 0 width
100   }
101 }
102 
103 # draw a single line of text from x, y to xmax
104 # return the next 'x' coordinate
105 # if there isn't enough space, truncate
106 fn draw-text-rightward screen: (addr screen), text: (addr array byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int {
107   var stream-storage: (stream byte 0x400/print-buffer-size)
108   var stream/esi: (addr stream byte) <- address stream-storage
109   write stream, text
110   var xcurr/eax: int <- draw-stream-rightward screen, stream, x, xmax, y, color, background-color
111   return xcurr
112 }
113 
114 # draw a single-line stream from x, y to xmax
115 # return the next 'x' coordinate
116 # if there isn't enough space, truncate
117 fn draw-stream-rightward screen: (addr screen), stream: (addr stream byte), x: int, xmax: int, y: int, color: int, background-color: int -> _/eax: int {
118   var xcurr/ecx: int <- copy x
119   {
120     var g/eax: grapheme <- read-grapheme stream
121     compare g, 0xffffffff/end-of-file
122     break-if-=
123     var c/eax: code-point <- to-code-point g
124     var offset/eax: int <- draw-code-point screen, c, xcurr, y, color, background-color
125     xcurr <- add offset
126     loop
127   }
128   set-cursor-position screen, xcurr, y
129   return xcurr
130 }
131 
132 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 {
133   var width/eax: int <- copy 0
134   var height/ecx: int <- copy 0
135   width, height <- screen-size screen
136   var result/eax: int <- draw-text-rightward screen, text, x, width, y, color, background-color
137   return result
138 }
139 
140 fn draw-text-rightward-from-cursor screen: (addr screen), text: (addr array byte), xmax: int, color: int, background-color: int {
141   var cursor-x/eax: int <- copy 0
142   var cursor-y/ecx: int <- copy 0
143   cursor-x, cursor-y <- cursor-position screen
144   cursor-x <- draw-text-rightward screen, text, cursor-x, xmax, cursor-y, color, background-color
145   set-cursor-position screen, cursor-x, cursor-y
146 }
147 
148 fn draw-text-rightward-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
149   var width/eax: int <- copy 0
150   var height/ecx: int <- copy 0
151   width, height <- screen-size screen
152   draw-text-rightward-from-cursor screen, text, width, color, background-color
153 }
154 
155 fn render-code-point screen: (addr screen), c: code-point, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
156   compare c, 0xa/newline
157   var x/ecx: int <- copy x
158   {
159     break-if-!=
160     # minimum effort to clear cursor
161     var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, color, background-color
162     x <- copy xmin
163     increment y
164     return x, y
165   }
166   var offset/eax: int <- draw-code-point screen, c, x, y, color, background-color
167   x <- add offset
168   compare x, xmax
169   {
170     break-if-<
171     x <- copy xmin
172     increment y
173   }
174   return x, y
175 }
176 
177 # draw text 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-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 {
182   var stream-storage: (stream byte 0x200/print-buffer-size)
183   var stream/edi: (addr stream byte) <- address stream-storage
184   var text/esi: (addr array byte) <- copy _text
185   var len/eax: int <- length text
186   compare len, 0x200/print-buffer-size
187   {
188     break-if-<
189     write stream, "ERROR: stream too small in draw-text-wrapping-right-then-down"
190   }
191   compare len, 0x200/print-buffer-size
192   {
193     break-if->=
194     write stream, text
195   }
196   var x/eax: int <- copy _x
197   var y/ecx: int <- copy _y
198   x, y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color
199   return x, y
200 }
201 
202 # draw a stream in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
203 # return the next (x, y) coordinate in raster order where drawing stopped
204 # that way the caller can draw more if given the same min and max bounding-box.
205 # if there isn't enough space, truncate
206 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 {
207   var xcurr/eax: int <- copy x
208   var ycurr/ecx: int <- copy y
209   var c/ebx: code-point <- copy 0
210   {
211     {
212       var g/eax: grapheme <- read-grapheme stream
213       var _c/eax: code-point <- to-code-point g
214       c <- copy _c
215     }
216     compare c, 0xffffffff/end-of-file
217     break-if-=
218     xcurr, ycurr <- render-code-point screen, c, xmin, ymin, xmax, ymax, xcurr, ycurr, color, background-color
219     loop
220   }
221   set-cursor-position screen, xcurr, ycurr
222   return xcurr, ycurr
223 }
224 
225 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 {
226   var cursor-x/eax: int <- copy 0
227   var cursor-y/ecx: int <- copy 0
228   cursor-x, cursor-y <- cursor-position screen
229   var end-x/edx: int <- copy cursor-x
230   end-x <- increment
231   compare end-x, xmax
232   {
233     break-if-<
234     cursor-x <- copy xmin
235     cursor-y <- increment
236   }
237   cursor-x, cursor-y <- draw-stream-wrapping-right-then-down screen, stream, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
238 }
239 
240 fn draw-stream-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), stream: (addr stream byte), color: int, background-color: int {
241   var width/eax: int <- copy 0
242   var height/ecx: int <- copy 0
243   width, height <- screen-size screen
244   draw-stream-wrapping-right-then-down-from-cursor screen, stream, 0/xmin, 0/ymin, width, height, color, background-color
245 }
246 
247 fn move-cursor-rightward-and-downward screen: (addr screen), xmin: int, xmax: int {
248   var cursor-x/eax: int <- copy 0
249   var cursor-y/ecx: int <- copy 0
250   cursor-x, cursor-y <- cursor-position screen
251   cursor-x <- increment
252   compare cursor-x, xmax
253   {
254     break-if-<
255     cursor-x <- copy xmin
256     cursor-y <- increment
257   }
258   set-cursor-position screen, cursor-x, cursor-y
259 }
260 
261 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 {
262   var x2/eax: int <- copy 0
263   var y2/ecx: int <- copy 0
264   x2, y2 <- screen-size screen  # width, height
265   x2, y2 <- draw-text-wrapping-right-then-down screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
266   return x2, y2  # cursor-x, cursor-y
267 }
268 
269 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 {
270   var cursor-x/eax: int <- copy 0
271   var cursor-y/ecx: int <- copy 0
272   cursor-x, cursor-y <- cursor-position screen
273   var end-x/edx: int <- copy cursor-x
274   end-x <- increment
275   compare end-x, xmax
276   {
277     break-if-<
278     cursor-x <- copy xmin
279     cursor-y <- increment
280   }
281   cursor-x, cursor-y <- draw-text-wrapping-right-then-down screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
282 }
283 
284 fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
285   var width/eax: int <- copy 0
286   var height/ecx: int <- copy 0
287   width, height <- screen-size screen
288   draw-text-wrapping-right-then-down-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
289 }
290 
291 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 {
292   var stream-storage: (stream byte 0x100)
293   var stream/esi: (addr stream byte) <- address stream-storage
294   write-int32-hex stream, n
295   var xcurr/edx: int <- copy x
296   var ycurr/ecx: int <- copy y
297   {
298     var g/eax: grapheme <- read-grapheme stream
299     compare g, 0xffffffff/end-of-file
300     break-if-=
301     var c/eax: code-point <- to-code-point g
302     var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color
303     xcurr <- add offset
304     compare xcurr, xmax
305     {
306       break-if-<
307       xcurr <- copy xmin
308       ycurr <- increment
309     }
310     loop
311   }
312   set-cursor-position screen, xcurr, ycurr
313   return xcurr, ycurr
314 }
315 
316 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 {
317   var x2/eax: int <- copy 0
318   var y2/ecx: int <- copy 0
319   x2, y2 <- screen-size screen  # width, height
320   x2, y2 <- draw-int32-hex-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
321   return x2, y2  # cursor-x, cursor-y
322 }
323 
324 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 {
325   var cursor-x/eax: int <- copy 0
326   var cursor-y/ecx: int <- copy 0
327   cursor-x, cursor-y <- cursor-position screen
328   var end-x/edx: int <- copy cursor-x
329   end-x <- increment
330   compare end-x, xmax
331   {
332     break-if-<
333     cursor-x <- copy xmin
334     cursor-y <- increment
335   }
336   cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
337 }
338 
339 fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
340   var width/eax: int <- copy 0
341   var height/ecx: int <- copy 0
342   width, height <- screen-size screen
343   draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
344 }
345 
346 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 {
347   var stream-storage: (stream byte 0x100)
348   var stream/esi: (addr stream byte) <- address stream-storage
349   write-int32-decimal stream, n
350   var xcurr/edx: int <- copy x
351   var ycurr/ecx: int <- copy y
352   {
353     var g/eax: grapheme <- read-grapheme stream
354     compare g, 0xffffffff/end-of-file
355     break-if-=
356     var c/eax: code-point <- to-code-point g
357     var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color
358     xcurr <- add offset
359     compare xcurr, xmax
360     {
361       break-if-<
362       xcurr <- copy xmin
363       ycurr <- increment
364     }
365     loop
366   }
367   set-cursor-position screen, xcurr, ycurr
368   return xcurr, ycurr
369 }
370 
371 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 {
372   var x2/eax: int <- copy 0
373   var y2/ecx: int <- copy 0
374   x2, y2 <- screen-size screen  # width, height
375   x2, y2 <- draw-int32-decimal-wrapping-right-then-down screen, n, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
376   return x2, y2  # cursor-x, cursor-y
377 }
378 
379 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 {
380   var cursor-x/eax: int <- copy 0
381   var cursor-y/ecx: int <- copy 0
382   cursor-x, cursor-y <- cursor-position screen
383   var end-x/edx: int <- copy cursor-x
384   end-x <- increment
385   compare end-x, xmax
386   {
387     break-if-<
388     cursor-x <- copy xmin
389     cursor-y <- increment
390   }
391   cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
392 }
393 
394 fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int, background-color: int {
395   var width/eax: int <- copy 0
396   var height/ecx: int <- copy 0
397   width, height <- screen-size screen
398   draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0/xmin, 0/ymin, width, height, color, background-color
399 }
400 
401 ## Text direction: down then right
402 
403 # draw a single line of text vertically from x, y to ymax
404 # return the next 'y' coordinate
405 # if there isn't enough space, truncate
406 fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
407   var stream-storage: (stream byte 0x100)
408   var stream/esi: (addr stream byte) <- address stream-storage
409   write stream, text
410   var ycurr/eax: int <- draw-stream-downward screen, stream, x, y, ymax, color, background-color
411   return ycurr
412 }
413 
414 # draw a single-line stream vertically from x, y to ymax
415 # return the next 'y' coordinate
416 # if there isn't enough space, truncate
417 # TODO: should we track horizontal width?
418 fn draw-stream-downward screen: (addr screen), stream: (addr stream byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
419   var ycurr/ecx: int <- copy y
420   {
421     var g/eax: grapheme <- read-grapheme stream
422     compare g, 0xffffffff/end-of-file
423     break-if-=
424     var c/eax: code-point <- to-code-point g
425     var dummy/eax: int <- draw-code-point screen, c, x, ycurr, color, background-color
426     ycurr <- increment
427     loop
428   }
429   set-cursor-position screen, x, ycurr
430   return ycurr
431 }
432 
433 fn draw-text-downward-from-cursor screen: (addr screen), text: (addr array byte), ymax: int, color: int, background-color: int {
434   var cursor-x/eax: int <- copy 0
435   var cursor-y/ecx: int <- copy 0
436   cursor-x, cursor-y <- cursor-position screen
437   var result/eax: int <- draw-text-downward screen, text, cursor-x, cursor-y, ymax, color, background-color
438 }
439 
440 # draw text down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
441 # return the next (x, y) coordinate in raster order where drawing stopped
442 # that way the caller can draw more if given the same min and max bounding-box.
443 # if there isn't enough space, truncate
444 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 {
445   var stream-storage: (stream byte 0x100)
446   var stream/esi: (addr stream byte) <- address stream-storage
447   write stream, text
448   var x/eax: int <- copy _x
449   var y/ecx: int <- copy _y
450   x, y <- draw-stream-wrapping-down-then-right screen, stream, xmin, ymin, xmax, ymax, x, y, color, background-color
451   return x, y
452 }
453 
454 # draw a stream down and right in the rectangle from (xmin, ymin) to (xmax, ymax), starting from (x, y), wrapping as necessary
455 # return the next (x, y) coordinate in raster order where drawing stopped
456 # that way the caller can draw more if given the same min and max bounding-box.
457 # if there isn't enough space, truncate
458 # TODO: should we track horizontal width? just always offset by 2 for now
459 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 {
460   var xcurr/edx: int <- copy x
461   var ycurr/ecx: int <- copy y
462   {
463     var g/eax: grapheme <- read-grapheme stream
464     compare g, 0xffffffff/end-of-file
465     break-if-=
466     var c/eax: code-point <- to-code-point g
467     var offset/eax: int <- draw-code-point screen, c, xcurr, ycurr, color, background-color
468     ycurr <- increment
469     compare ycurr, ymax
470     {
471       break-if-<
472       xcurr <- add 2
473       ycurr <- copy ymin
474     }
475     loop
476   }
477   set-cursor-position screen, xcurr, ycurr
478   return xcurr, ycurr
479 }
480 
481 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 {
482   var x2/eax: int <- copy 0
483   var y2/ecx: int <- copy 0
484   x2, y2 <- screen-size screen  # width, height
485   x2, y2 <- draw-text-wrapping-down-then-right screen, text, 0/xmin, 0/ymin, x2, y2, x, y, color, background-color
486   return x2, y2  # cursor-x, cursor-y
487 }
488 
489 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 {
490   var cursor-x/eax: int <- copy 0
491   var cursor-y/ecx: int <- copy 0
492   cursor-x, cursor-y <- cursor-position screen
493   var end-y/edx: int <- copy cursor-y
494   end-y <- increment
495   compare end-y, ymax
496   {
497     break-if-<
498     cursor-x <- increment
499     cursor-y <- copy ymin
500   }
501   cursor-x, cursor-y <- draw-text-wrapping-down-then-right screen, text, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color, background-color
502 }
503 
504 fn draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen: (addr screen), text: (addr array byte), color: int, background-color: int {
505   var width/eax: int <- copy 0
506   var height/ecx: int <- copy 0
507   width, height <- screen-size screen
508   draw-text-wrapping-down-then-right-from-cursor screen, text, 0/xmin, 0/ymin, width, height, color, background-color
509 }