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 video memory).
  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     # . save registers
 22     50/push-eax
 23     51/push-ecx
 24     52/push-edx
 25     53/push-ebx
 26     56/push-esi
 27     # var letter-bitmap/esi = font[g]
 28     8b/-> *(ebp+8) 6/r32/esi
 29     c1 4/subop/shift-left %esi 4/imm8
 30     81 0/subop/add %esi Font/imm32
 31     # if (letter-bitmap >= 0x9400) return  # characters beyond ASCII currently not supported
 32     81 7/subop/compare %esi 0x9400/imm32
 33     7d/jump-if->= $draw-grapheme-on-real-screen:end/disp8
 34     # var ycurr/edx: int = y*16
 35     8b/-> *(ebp+0x10) 2/r32/edx
 36     c1 4/subop/shift-left %edx 4/imm8
 37     # var ymax/ebx: int = ycurr + 16
 38     8b/-> *(ebp+0x10) 3/r32/ebx
 39     c1 4/subop/shift-left %ebx 4/imm8
 40     81 0/subop/add %ebx 0x10/imm32
 41     {
 42       # if (ycurr >= ymax) break
 43       39/compare %edx 3/r32/ebx
 44       7d/jump-if->= break/disp8
 45       # var xcurr/eax: int = x*8 + 7
 46       8b/-> *(ebp+0xc) 0/r32/eax  # font-width - 1
 47       c1 4/subop/shift-left %eax 3/imm8
 48       81 0/subop/add %eax 7/imm32
 49       # var xmin/ecx: int = x*8
 50       8b/-> *(ebp+0xc) 1/r32/ecx
 51       c1 4/subop/shift-left %ecx 3/imm8
 52       # var row-bitmap/ebx: int = *letter-bitmap
 53       53/push-ebx
 54       8b/-> *esi 3/r32/ebx
 55       {
 56         # if (xcurr < xmin) break
 57         39/compare %eax 1/r32/ecx
 58         7c/jump-if-< break/disp8
 59         # shift LSB from row-bitmap into carry flag (CF)
 60         c1 5/subop/shift-right-logical %ebx 1/imm8
 61         # if LSB, draw a pixel in the given color
 62         {
 63           73/jump-if-not-CF break/disp8
 64           (pixel-on-real-screen %eax %edx *(ebp+0x14))
 65           eb/jump $draw-grapheme-on-real-screen:continue/disp8
 66         }
 67         # otherwise use the background color
 68         (pixel-on-real-screen %eax %edx *(ebp+0x18))
 69 $draw-grapheme-on-real-screen:continue:
 70         # --x
 71         48/decrement-eax
 72         #
 73         eb/jump loop/disp8
 74       }
 75       # reclaim row-bitmap
 76       5b/pop-to-ebx
 77       # ++y
 78       42/increment-edx
 79       # next bitmap row
 80       46/increment-esi
 81       #
 82       eb/jump loop/disp8
 83     }
 84 $draw-grapheme-on-real-screen:end:
 85     # . restore registers
 86     5e/pop-to-esi
 87     5b/pop-to-ebx
 88     5a/pop-to-edx
 89     59/pop-to-ecx
 90     58/pop-to-eax
 91     # . epilogue
 92     89/<- %esp 5/r32/ebp
 93     5d/pop-to-ebp
 94     c3/return
 95 
 96 cursor-position-on-real-screen:  # -> _/eax: int, _/ecx: int
 97     # . prologue
 98     55/push-ebp
 99     89/<- %ebp 4/r32/esp
100     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
101     8b/-> *Real-screen-cursor-x 0/r32/eax
102     8b/-> *Real-screen-cursor-y 1/r32/ecx
103 $cursor-position-on-real-screen:end:
104     # . epilogue
105     89/<- %esp 5/r32/ebp
106     5d/pop-to-ebp
107     c3/return
108 
109 set-cursor-position-on-real-screen:  # x: int, y: int
110     # . prologue
111     55/push-ebp
112     89/<- %ebp 4/r32/esp
113     # . save registers
114     50/push-eax
115     #
116     8b/-> *(ebp+8) 0/r32/eax
117     89/<- *Real-screen-cursor-x 0/r32/eax
118     8b/-> *(ebp+0xc) 0/r32/eax
119     89/<- *Real-screen-cursor-y 0/r32/eax
120 $set-cursor-position-on-real-screen:end:
121     # . restore registers
122     58/pop-to-eax
123     # . epilogue
124     89/<- %esp 5/r32/ebp
125     5d/pop-to-ebp
126     c3/return
127 
128 # Not a real `show-cursor` primitive:
129 #   - does not clear previous location cursor was shown at.
130 #   - does not preserve what was at the cursor. Caller is responsible for
131 #     tracking what was on the screen at this position before and passing it
132 #     in again.
133 #   - does not stop showing the cursor at this location when the cursor moves
134 draw-cursor-on-real-screen:  # g: grapheme
135     # . prologue
136     55/push-ebp
137     89/<- %ebp 4/r32/esp
138     # . save registers
139     50/push-eax
140     51/push-ecx
141     #
142     (cursor-position-on-real-screen)  # => eax, ecx
143     (draw-grapheme-on-real-screen *(ebp+8) %eax %ecx 0 7)
144 $draw-cursor-on-real-screen:end:
145     # . restore registers
146     59/pop-to-ecx
147     58/pop-to-eax
148     # . epilogue
149     89/<- %esp 5/r32/ebp
150     5d/pop-to-ebp
151     c3/return
152 
153 == data
154 
155 # The cursor is where certain Mu functions (usually of the form
156 # 'draw*cursor*') print to by default.
157 #
158 # We don't bother displaying the cursor when drawing. It only becomes visible
159 # on draw-cursor, which is quite rickety (see above)
160 #
161 # It's up to applications to manage cursor display:
162 #   - clean up where it used to be
163 #   - display the cursor before waiting for a key
164 #   - ensure its location appropriately suggests the effect keystrokes will have
165 #   - ensure its contents (and colors) appropriately reflect the state of the
166 #     screen
167 #
168 # There's no blinking, etc. We aren't using any hardware-supported text mode
169 # here.
170 Real-screen-cursor-x:
171   0/imm32
172 Real-screen-cursor-y:
173   0/imm32