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 }