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 }