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 }