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