https://github.com/akkartik/mu/blob/main/103grapheme.subx

      1 # Use the built-in font to draw a grapheme to real screen.
      2 #
      3 # We need to do this in machine code because Mu doesn't have global variables
      4 # yet (for the start of the font).
      5 #
      6 # There are uncomfortable assumptions baked in here about english/latin
      7 # script. We convert the grid of pixels into a fixed-width grid of graphemes,
      8 # which may not work well with other language families.
      9
     10 == code
     11
     12 # The Mu computer's screen is 1024px wide and 768px tall.
     13 # The Mu computer's font is 8px wide and 16px tall.
     14 # Therefore 'x' here is in [0, 128), and 'y' is in [0, 48)
     15 # Doesn't update the cursor; where the cursor should go after printing the
     16 # current grapheme is a higher-level concern.
     17 draw-grapheme-on-real-screen:  # g: grapheme, x: int, y: int, color: int, background-color: int
     18     # . prologue
     19     55/push-ebp
     20     89/<- %ebp 4/r32/esp
     21     #
     22     (draw-grapheme-on-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 0x80 0x30)
     23 $draw-grapheme-on-real-screen:end:
     24     # . epilogue
     25     89/<- %esp 5/r32/ebp
     26     5d/pop-to-ebp
     27     c3/return
     28
     29 draw-grapheme-on-screen-array:  # screen-data: (addr array byte), g: grapheme, x: int, y: int, color: int, background-color: int, screen-width: int, screen-height: int
     30     # . prologue
     31     55/push-ebp
     32     89/<- %ebp 4/r32/esp
     33     # . save registers
     34     50/push-eax
     35     51/push-ecx
     36     52/push-edx
     37     # if screen-width*screen-height > len(screen-data) abort
     38     {
     39       # ecx = len(screen-data)
     40       8b/-> *(ebp+8) 1/r32/ecx
     41       8b/-> *ecx 1/r32/ecx
     42       # eax = screen-width*screen-height
     43       ba/copy-to-edx 0/imm32
     44       8b/-> *(ebp+0x20) 0/r32/eax
     45       f7 4/subop/multiply-into-eax *(ebp+0x24)
     46       81 7/subop/compare %edx 0/imm32
     47       0f 85/jump-if-!= $draw-grapheme-on-screen-array:overflow/disp32
     48       # if (eax > ecx) abort
     49       39/compare %eax 1/r32/ecx
     50       0f 8f/jump-if-> $draw-grapheme-on-screen-array:abort/disp32
     51     }
     52     # eax = screen-data+4   (skip length)
     53     8b/-> *(ebp+8) 0/r32/eax
     54     05/add-to-eax 4/imm32
     55     #
     56     (draw-grapheme-on-screen-buffer %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24))
     57 $draw-grapheme-on-screen-array:end:
     58     # . restore registers
     59     5a/pop-to-edx
     60     59/pop-to-ecx
     61     58/pop-to-eax
     62     # . epilogue
     63     89/<- %esp 5/r32/ebp
     64     5d/pop-to-ebp
     65     c3/return
     66
     67 $draw-grapheme-on-screen-array:overflow:
     68     (abort "draw-grapheme-on-screen-array: screen dimensions too large")
     69
     70 $draw-grapheme-on-screen-array:abort:
     71     (abort "draw-grapheme-on-screen-array: coordinates are off the screen. Are the screen dimensions correct?")
     72
     73 # 'buffer' here is not a valid Mu type: a naked address without a length.
     74 draw-grapheme-on-screen-buffer:  # buffer: (addr byte), g: grapheme, x: int, y: int, color: int, background-color: int, screen-width: int, screen-height: int
     75     # . prologue
     76     55/push-ebp
     77     89/<- %ebp 4/r32/esp
     78     # . save registers
     79     50/push-eax
     80     51/push-ecx
     81     52/push-edx
     82     53/push-ebx
     83     56/push-esi
     84     # switch screen-width and screen-height from grapheme to pixel units
     85     c1 4/subop/shift-left *(ebp+20) 3/imm8/log2-font-width
     86     c1 4/subop/shift-left *(ebp+24) 4/imm8/log2-font-height
     87     # esi = g
     88     8b/-> *(ebp+0xc) 6/r32/esi
     89     # if (g >= 128) return  # characters beyond ASCII currently not supported
     90     81 7/subop/compare %esi 0x80/imm32
     91     0f 8d/jump-if->= $draw-grapheme-on-screen-buffer:end/disp32
     92     # var letter-bitmap/esi = font[g]
     93     c1 4/subop/shift-left %esi 4/imm8
     94     81 0/subop/add %esi Font/imm32
     95     # var ycurr/edx: int = y*16
     96     8b/-> *(ebp+0x14) 2/r32/edx
     97     c1 4/subop/shift-left %edx 4/imm8
     98     # var ymax/ebx: int = ycurr + 16
     99     8b/-> *(ebp+0x14) 3/r32/ebx
    100     c1 4/subop/shift-left %ebx 4/imm8
    101     81 0/subop/add %ebx 0x10/imm32
    102     {
    103       # if (ycurr >= ymax) break
    104       39/compare %edx 3/r32/ebx
    105       0f 8d/jump-if->= break/disp32
    106       # var xcurr/eax: int = x*8 + 7
    107       8b/-> *(ebp+0x10) 0/r32/eax  # font-width - 1
    108       c1 4/subop/shift-left %eax 3/imm8
    109       05/add-to-eax 7/imm32
    110       # var xmin/ecx: int = x*8
    111       8b/-> *(ebp+0x10) 1/r32/ecx
    112       c1 4/subop/shift-left %ecx 3/imm8
    113       # var row-bitmap/ebx: int = *letter-bitmap
    114       53/push-ebx
    115       8b/-> *esi 3/r32/ebx
    116       {
    117         # if (xcurr < xmin) break
    118         39/compare %eax 1/r32/ecx
    119         7c/jump-if-< break/disp8
    120         # shift LSB from row-bitmap into carry flag (CF)
    121         c1 5/subop/shift-right-logical %ebx 1/imm8
    122         # if LSB, draw a pixel in the given color
    123         {
    124           73/jump-if-not-CF break/disp8
    125           (pixel-on-screen-buffer *(ebp+8) %eax %edx *(ebp+0x18) *(ebp+0x20) *(ebp+0x24))
    126           eb/jump $draw-grapheme-on-screen-buffer:continue/disp8
    127         }
    128         # otherwise use the background color
    129         (pixel-on-screen-buffer *(ebp+8) %eax %edx *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24))
    130 $draw-grapheme-on-screen-buffer:continue:
    131         # --x
    132         48/decrement-eax
    133         #
    134         eb/jump loop/disp8
    135       }
    136       # reclaim row-bitmap
    137       5b/pop-to-ebx
    138       # ++y
    139       42/increment-edx
    140       # next bitmap row
    141       46/increment-esi
    142       #
    143       e9/jump loop/disp32
    144     }
    145 $draw-grapheme-on-screen-buffer:end:
    146     # . restore registers
    147     5e/pop-to-esi
    148     5b/pop-to-ebx
    149     5a/pop-to-edx
    150     59/pop-to-ecx
    151     58/pop-to-eax
    152     # . epilogue
    153     89/<- %esp 5/r32/ebp
    154     5d/pop-to-ebp
    155     c3/return
    156
    157 cursor-position-on-real-screen:  # -> _/eax: int, _/ecx: int
    158     # . prologue
    159     55/push-ebp
    160     89/<- %ebp 4/r32/esp
    161     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
    162     8b/-> *Real-screen-cursor-x 0/r32/eax
    163     8b/-> *Real-screen-cursor-y 1/r32/ecx
    164 $cursor-position-on-real-screen:end:
    165     # . epilogue
    166     89/<- %esp 5/r32/ebp
    167     5d/pop-to-ebp
    168     c3/return
    169
    170 set-cursor-position-on-real-screen:  # x: int, y: int
    171     # . prologue
    172     55/push-ebp
    173     89/<- %ebp 4/r32/esp
    174     # . save registers
    175     50/push-eax
    176     #
    177     8b/-> *(ebp+8) 0/r32/eax
    178     89/<- *Real-screen-cursor-x 0/r32/eax
    179     8b/-> *(ebp+0xc) 0/r32/eax
    180     89/<- *Real-screen-cursor-y 0/r32/eax
    181 $set-cursor-position-on-real-screen:end:
    182     # . restore registers
    183     58/pop-to-eax
    184     # . epilogue
    185     89/<- %esp 5/r32/ebp
    186     5d/pop-to-ebp
    187     c3/return
    188
    189 # Not a real `show-cursor` primitive:
    190 #   - does not clear previous location cursor was shown at.
    191 #   - does not preserve what was at the cursor. Caller is responsible for
    192 #     tracking what was on the screen at this position before and passing it
    193 #     in again.
    194 #   - does not stop showing the cursor at this location when the cursor moves
    195 draw-cursor-on-real-screen:  # g: grapheme
    196     # . prologue
    197     55/push-ebp
    198     89/<- %ebp 4/r32/esp
    199     # . save registers
    200     50/push-eax
    201     51/push-ecx
    202     #
    203     (cursor-position-on-real-screen)  # => eax, ecx
    204     (draw-grapheme-on-real-screen *(ebp+8) %eax %ecx 0 7)
    205 $draw-cursor-on-real-screen:end:
    206     # . restore registers
    207     59/pop-to-ecx
    208     58/pop-to-eax
    209     # . epilogue
    210     89/<- %esp 5/r32/ebp
    211     5d/pop-to-ebp
    212     c3/return
    213
    214 == data
    215
    216 # The cursor is where certain Mu functions (usually of the form
    217 # 'draw*cursor*') print to by default.
    218 #
    219 # We don't bother displaying the cursor when drawing. It only becomes visible
    220 # on draw-cursor, which is quite rickety (see above)
    221 #
    222 # It's up to applications to manage cursor display:
    223 #   - clean up where it used to be
    224 #   - display the cursor before waiting for a key
    225 #   - ensure its location appropriately suggests the effect keystrokes will have
    226 #   - ensure its contents (and colors) appropriately reflect the state of the
    227 #     screen
    228 #
    229 # There's no blinking, etc. We aren't using any hardware-supported text mode
    230 # here.
    231 Real-screen-cursor-x:
    232   0/imm32
    233 Real-screen-cursor-y:
    234   0/imm32