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