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