https://github.com/akkartik/mu/blob/main/500text-screen.mu

      1 # Testable primitives for writing text to screen.
      2 # (Mu doesn't yet have testable primitives for graphics.)
      3 #
      4 # Unlike the top-level, this text mode has no scrolling.
      5
      6 # coordinates here don't match top-level
      7 # Here we're consistent with graphics mode. Top-level is consistent with
      8 # terminal emulators.
      9 type screen {
     10   width: int
     11   height: int
     12   data: (handle array screen-cell)
     13   cursor-x: int
     14   cursor-y: int
     15 }
     16
     17 type screen-cell {
     18   data: grapheme
     19   color: int
     20   background-color: int
     21 }
     22
     23 fn initialize-screen screen: (addr screen), width: int, height: int {
     24   var screen-addr/esi: (addr screen) <- copy screen
     25   var tmp/eax: int <- copy 0
     26   var dest/edi: (addr int) <- copy 0
     27   # screen->width = width
     28   dest <- get screen-addr, width
     29   tmp <- copy width
     30   copy-to *dest, tmp
     31   # screen->height = height
     32   dest <- get screen-addr, height
     33   tmp <- copy height
     34   copy-to *dest, tmp
     35   # screen->data = new screen-cell[width*height]
     36   {
     37     var data-addr/edi: (addr handle array screen-cell) <- get screen-addr, data
     38     tmp <- multiply width
     39     populate data-addr, tmp
     40   }
     41   # screen->cursor-x = 0
     42   dest <- get screen-addr, cursor-x
     43   copy-to *dest, 0
     44   # screen->cursor-y = 0
     45   dest <- get screen-addr, cursor-y
     46   copy-to *dest, 0
     47 }
     48
     49 # in graphemes
     50 fn screen-size screen: (addr screen) -> _/eax: int, _/ecx: int {
     51   var width/eax: int <- copy 0
     52   var height/ecx: int <- copy 0
     53   compare screen, 0
     54   {
     55     break-if-!=
     56     return 0x80/128, 0x30/48
     57   }
     58   # fake screen
     59   var screen-addr/esi: (addr screen) <- copy screen
     60   var tmp/edx: (addr int) <- get screen-addr, width
     61   width <- copy *tmp
     62   tmp <- get screen-addr, height
     63   height <- copy *tmp
     64   return width, height
     65 }
     66
     67 # testable screen primitive
     68 fn draw-grapheme screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int {
     69   {
     70     compare screen, 0
     71     break-if-!=
     72     draw-grapheme-on-real-screen g, x, y, color, background-color
     73     return
     74   }
     75   # fake screen
     76   var screen-addr/esi: (addr screen) <- copy screen
     77   var idx/ecx: int <- screen-cell-index screen-addr, x, y
     78   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
     79   var data/eax: (addr array screen-cell) <- lookup *data-ah
     80   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
     81   var dest-cell/ecx: (addr screen-cell) <- index data, offset
     82   var dest-grapheme/eax: (addr grapheme) <- get dest-cell, data
     83   var g2/edx: grapheme <- copy g
     84   copy-to *dest-grapheme, g2
     85   var dest-color/eax: (addr int) <- get dest-cell, color
     86   var src-color/edx: int <- copy color
     87   copy-to *dest-color, src-color
     88   dest-color <- get dest-cell, background-color
     89   src-color <- copy background-color
     90   copy-to *dest-color, src-color
     91 }
     92
     93 # we can't really render non-ASCII yet, but when we do we'll be ready
     94 fn draw-code-point screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
     95   var g/eax: grapheme <- copy c
     96   draw-grapheme screen, g, x, y, color, background-color
     97 }
     98
     99 # not really needed for a real screen, though it shouldn't do any harm
    100 fn screen-cell-index screen-on-stack: (addr screen), x: int, y: int -> _/ecx: int {
    101   var screen/esi: (addr screen) <- copy screen-on-stack
    102   # only one bounds check isn't automatically handled
    103   {
    104     var xmax/eax: (addr int) <- get screen, width
    105     var xcurr/ecx: int <- copy x
    106     compare xcurr, *xmax
    107     break-if-<
    108     abort "tried to print out of screen bounds"
    109   }
    110   var width-addr/eax: (addr int) <- get screen, width
    111   var result/ecx: int <- copy y
    112   result <- multiply *width-addr
    113   result <- add x
    114   return result
    115 }
    116
    117 fn cursor-position screen: (addr screen) -> _/eax: int, _/ecx: int {
    118   {
    119     compare screen, 0
    120     break-if-!=
    121     var x/eax: int <- copy 0
    122     var y/ecx: int <- copy 0
    123     x, y <- cursor-position-on-real-screen
    124     return x, y
    125   }
    126   # fake screen
    127   var screen-addr/esi: (addr screen) <- copy screen
    128   var cursor-x-addr/eax: (addr int) <- get screen-addr, cursor-x
    129   var cursor-y-addr/ecx: (addr int) <- get screen-addr, cursor-y
    130   return *cursor-x-addr, *cursor-y-addr
    131 }
    132
    133 fn set-cursor-position screen: (addr screen), x: int, y: int {
    134   {
    135     compare screen, 0
    136     break-if-!=
    137     set-cursor-position-on-real-screen x, y
    138     return
    139   }
    140   # fake screen
    141   var screen-addr/esi: (addr screen) <- copy screen
    142   # ignore x < 0
    143   {
    144     compare x, 0
    145     break-if->=
    146     return
    147   }
    148   # ignore x >= width
    149   {
    150     var width-addr/eax: (addr int) <- get screen-addr, width
    151     var width/eax: int <- copy *width-addr
    152     compare x, width
    153     break-if-<=
    154     return
    155   }
    156   # ignore y < 0
    157   {
    158     compare y, 0
    159     break-if->=
    160     return
    161   }
    162   # ignore y >= height
    163   {
    164     var height-addr/eax: (addr int) <- get screen-addr, height
    165     var height/eax: int <- copy *height-addr
    166     compare y, height
    167     break-if-<
    168     return
    169   }
    170   # screen->cursor-x = x
    171   var dest/edi: (addr int) <- get screen-addr, cursor-x
    172   var src/eax: int <- copy x
    173   copy-to *dest, src
    174   # screen->cursor-y = y
    175   dest <- get screen-addr, cursor-y
    176   src <- copy y
    177   copy-to *dest, src
    178 }
    179
    180 fn draw-cursor screen: (addr screen), g: grapheme {
    181   {
    182     compare screen, 0
    183     break-if-!=
    184     draw-cursor-on-real-screen g
    185     return
    186   }
    187   # fake screen
    188   var cursor-x/eax: int <- copy 0
    189   var cursor-y/ecx: int <- copy 0
    190   cursor-x, cursor-y <- cursor-position screen
    191   draw-grapheme screen, g, cursor-x, cursor-y, 0/fg, 7/bg
    192 }
    193
    194 fn clear-screen screen: (addr screen) {
    195   {
    196     compare screen, 0
    197     break-if-!=
    198     clear-real-screen
    199     return
    200   }
    201   # fake screen
    202   set-cursor-position screen, 0, 0
    203   var screen-addr/esi: (addr screen) <- copy screen
    204   var y/eax: int <- copy 0
    205   var height/ecx: (addr int) <- get screen-addr, height
    206   {
    207     compare y, *height
    208     break-if->=
    209     var x/edx: int <- copy 0
    210     var width/ebx: (addr int) <- get screen-addr, width
    211     {
    212       compare x, *width
    213       break-if->=
    214       draw-code-point screen, 0x20/space, x, y, 0/fg=black, 0/bg=black
    215       x <- increment
    216       loop
    217     }
    218     y <- increment
    219     loop
    220   }
    221   set-cursor-position screen, 0, 0
    222 }
    223
    224 # there's no grapheme that guarantees to cover every pixel, so we'll bump down
    225 # to pixels for a real screen
    226 fn clear-real-screen {
    227   var y/eax: int <- copy 0
    228   {
    229     compare y, 0x300/screen-height=768
    230     break-if->=
    231     var x/edx: int <- copy 0
    232     {
    233       compare x, 0x400/screen-width=1024
    234       break-if->=
    235       pixel-on-real-screen x, y, 0/color=black
    236       x <- increment
    237       loop
    238     }
    239     y <- increment
    240     loop
    241   }
    242 }
    243
    244 fn screen-grapheme-at screen-on-stack: (addr screen), x: int, y: int -> _/eax: grapheme {
    245   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    246   var idx/ecx: int <- screen-cell-index screen-addr, x, y
    247   var result/eax: grapheme <- screen-grapheme-at-idx screen-addr, idx
    248   return result
    249 }
    250
    251 fn screen-grapheme-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: grapheme {
    252   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    253   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
    254   var data/eax: (addr array screen-cell) <- lookup *data-ah
    255   var idx/ecx: int <- copy idx-on-stack
    256   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
    257   var cell/eax: (addr screen-cell) <- index data, offset
    258   var src/eax: (addr grapheme) <- get cell, data
    259   return *src
    260 }
    261
    262 fn screen-color-at screen-on-stack: (addr screen), x: int, y: int -> _/eax: int {
    263   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    264   var idx/ecx: int <- screen-cell-index screen-addr, x, y
    265   var result/eax: int <- screen-color-at-idx screen-addr, idx
    266   return result
    267 }
    268
    269 fn screen-color-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: int {
    270   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    271   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
    272   var data/eax: (addr array screen-cell) <- lookup *data-ah
    273   var idx/ecx: int <- copy idx-on-stack
    274   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
    275   var cell/eax: (addr screen-cell) <- index data, offset
    276   var src/eax: (addr int) <- get cell, color
    277   var result/eax: int <- copy *src
    278   return result
    279 }
    280
    281 fn screen-background-color-at screen-on-stack: (addr screen), x: int, y: int -> _/eax: int {
    282   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    283   var idx/ecx: int <- screen-cell-index screen-addr, x, y
    284   var result/eax: int <- screen-background-color-at-idx screen-addr, idx
    285   return result
    286 }
    287
    288 fn screen-background-color-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> _/eax: int {
    289   var screen-addr/esi: (addr screen) <- copy screen-on-stack
    290   var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
    291   var data/eax: (addr array screen-cell) <- lookup *data-ah
    292   var idx/ecx: int <- copy idx-on-stack
    293   var offset/ecx: (offset screen-cell) <- compute-offset data, idx
    294   var cell/eax: (addr screen-cell) <- index data, offset
    295   var src/eax: (addr int) <- get cell, background-color
    296   var result/eax: int <- copy *src
    297   return result
    298 }