https://github.com/akkartik/mu/blob/main/103glyph.subx
  1 # Use the built-in font to draw glyphs to screen.
  2 #   https://en.wikipedia.org/wiki/Glyph#Typography
  3 # Extremely hacky support for combining characters.
  4 #   https://en.wikipedia.org/wiki/Code_point
  5 #   https://en.wikipedia.org/wiki/Combining_character
  6 # All we support is drawing combining characters atop the same screen cell as
  7 # a single base code point. See the overlay? arguments below.
  8 #
  9 # We need to do this in machine code because Mu doesn't have global variables
 10 # yet (for the start of the font).
 11 
 12 == code
 13 
 14 # The Mu computer's screen is 1024px wide and 768px tall.
 15 # The Mu computer's font is 8px wide and 16px tall.
 16 # Therefore 'x' here is in [0, 128), and 'y' is in [0, 48)
 17 # Doesn't update the cursor; where the cursor should go after printing the
 18 # current code-point is a higher-level concern.
 19 draw-code-point-on-real-screen:  # c: code-point, x: int, y: int, color: int, background-color: int -> _/eax
 20     # . prologue
 21     55/push-ebp
 22     89/<- %ebp 4/r32/esp
 23     #
 24     (draw-code-point-on-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 0 0x80 0x30)  # 0/no-overlay => eax
 25 $draw-code-point-on-real-screen:end:
 26     # . epilogue
 27     89/<- %esp 5/r32/ebp
 28     5d/pop-to-ebp
 29     c3/return
 30 
 31 overlay-code-point-on-real-screen:  # c: code-point, x: int, y: int, color: int, background-color: int -> _/eax
 32     # . prologue
 33     55/push-ebp
 34     89/<- %ebp 4/r32/esp
 35     #
 36     (draw-code-point-on-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 1 0x80 0x30)  # 1/overlay => eax
 37 $overlay-code-point-on-real-screen:end:
 38     # . epilogue
 39     89/<- %esp 5/r32/ebp
 40     5d/pop-to-ebp
 41     c3/return
 42 
 43 draw-code-point-on-screen-array:  # screen-data: (addr array byte), c: code-point, x: int, y: int, color: int, background-color: int, screen-width: int, screen-height: int -> _/eax: int
 44     # . prologue
 45     55/push-ebp
 46     89/<- %ebp 4/r32/esp
 47     # . save registers
 48     51/push-ecx
 49     52/push-edx
 50     # if screen-width*screen-height > len(screen-data) abort
 51     {
 52       # ecx = len(screen-data)
 53       8b/-> *(ebp+8) 1/r32/ecx
 54       8b/-> *ecx 1/r32/ecx
 55       # eax = screen-width*screen-height
 56       ba/copy-to-edx 0/imm32
 57       8b/-> *(ebp+0x20) 0/r32/eax
 58       f7 4/subop/multiply-into-eax *(ebp+0x24)
 59       81 7/subop/compare %edx 0/imm32
 60       0f 85/jump-if-!= $draw-code-point-on-screen-array:overflow/disp32
 61       # if (eax > ecx) abort
 62       39/compare %eax 1/r32/ecx
 63       0f 8f/jump-if-> $draw-code-point-on-screen-array:abort/disp32
 64     }
 65     # eax = screen-data+4   (skip length)
 66     8b/-> *(ebp+8) 0/r32/eax
 67     05/add-to-eax 4/imm32
 68     #
 69     (draw-code-point-on-screen-buffer %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24))  # => eax
 70 $draw-code-point-on-screen-array:end:
 71     # . restore registers
 72     5a/pop-to-edx
 73     59/pop-to-ecx
 74     # . epilogue
 75     89/<- %esp 5/r32/ebp
 76     5d/pop-to-ebp
 77     c3/return
 78 
 79 $draw-code-point-on-screen-array:overflow:
 80     (abort "draw-code-point-on-screen-array: screen dimensions too large")
 81 
 82 $draw-code-point-on-screen-array:abort:
 83     (abort "draw-code-point-on-screen-array: coordinates are off the screen. Are the screen dimensions correct?")
 84 
 85 # 'buffer' here is not a valid Mu type: a naked address without a length.
 86 # returns number of 8x16 units printed to screen (1 or 2).
 87 draw-code-point-on-screen-buffer:  # buffer: (addr byte), c: code-point, x: int, y: int, color: int, background-color: int, overlay?: boolean, screen-width: int, screen-height: int -> _/eax: int
 88     # . prologue
 89     55/push-ebp
 90     89/<- %ebp 4/r32/esp
 91     # . save registers
 92     56/push-esi
 93     # switch screen-width and screen-height from code-point to pixel units
 94     c1 4/subop/shift-left *(ebp+24) 3/imm8/log2-font-width
 95     c1 4/subop/shift-left *(ebp+28) 4/imm8/log2-font-height
 96     # esi = c
 97     8b/-> *(ebp+0xc) 6/r32/esi
 98     # if (c >= 4352) return  # unicode planes supported: latin, greek, cyrillic, armenian, hebrew, arabic, syriac, thaana, n'ko, indian (iscii), sinhala, thai, lao, tibetan, myanmar, georgian
 99                              # next few to support: CJK, ethiopic, cherokee, ...
100     81 7/subop/compare %esi 0x1100/imm32/4352
101     0f 8d/jump-if->= $draw-code-point-on-screen-buffer:end/disp32
102     # var letter-bitmap/esi = font[c]
103     69/multiply %esi 0x22/imm32/glyph-size 6/r32/esi
104     81 0/subop/add %esi 0x0010000c/imm32/Font  # see boot.subx
105     # dispatch based on letter-bitmap->size
106     b8/copy-to-eax 0/imm32
107     8a/byte-> *esi 0/r32/AL
108     46/increment-esi  # skip size
109     46/increment-esi  # skip size
110     3d/compare-eax-and 8/imm32
111     {
112       75/jump-if-!= break/disp8
113       (draw-narrow-code-point-on-screen-buffer *(ebp+8) %esi *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
114       b8/copy-to-eax 1/imm32
115       eb/jump $draw-code-point-on-screen-buffer:end/disp8
116     }
117     (draw-wide-code-point-on-screen-buffer *(ebp+8) %esi *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
118     b8/copy-to-eax 2/imm32
119 $draw-code-point-on-screen-buffer:end:
120     # . restore registers
121     5e/pop-to-esi
122     # . epilogue
123     89/<- %esp 5/r32/ebp
124     5d/pop-to-ebp
125     c3/return
126 
127 wide-code-point?:  # c: code-point -> _/eax: boolean
128     # . prologue
129     55/push-ebp
130     89/<- %ebp 4/r32/esp
131     # eax = c
132     8b/-> *(ebp+8) 0/r32/eax
133     # if (c >= 128) return  # characters beyond ASCII currently not supported
134     3d/compare-eax-and 0x80/imm32
135     0f 8d/jump-if->= $wide-code-point?:end/disp32
136     # var letter-bitmap/eax = font[c]
137     69/multiply %eax 0x22/imm32/glyph-size 0/r32/eax
138     05/add-to-eax 0x0010000c/imm32/Font  # see boot.subx
139     # dispatch based on letter-bitmap->size
140     8a/byte-> *eax 0/r32/AL
141     25/and-eax-with  0xff/imm32
142     3d/compare-eax-and 8/imm32
143     0f 95/set-if-!= %eax
144 $wide-code-point?:end:
145     # . epilogue
146     89/<- %esp 5/r32/ebp
147     5d/pop-to-ebp
148     c3/return
149 
150 combining-code-point?:  # c: code-point -> _/eax: boolean
151     # . prologue
152     55/push-ebp
153     89/<- %ebp 4/r32/esp
154     # eax = c
155     8b/-> *(ebp+8) 0/r32/eax
156     # if (c >= 128) return  # characters beyond ASCII currently not supported
157     3d/compare-eax-and 0x80/imm32
158     0f 8d/jump-if->= $combining-code-point?:end/disp32
159     # var letter-bitmap/eax = font[c]
160     69/multiply %eax 0x22/imm32/glyph-size 0/r32/eax
161     05/add-to-eax 0x0010000c/imm32/Font  # see boot.subx
162     # dispatch based on letter-bitmap->is-combine?
163     8a/byte-> *(eax+1) 0/r32/AL
164     25/and-eax-with  0xff/imm32
165 $combining-code-point?:end:
166     # . epilogue
167     89/<- %esp 5/r32/ebp
168     5d/pop-to-ebp
169     c3/return
170 
171 # buffer: naked address to raw screen RAM without a length
172 # letter-bitmap: naked address to 8-pixel wide font glyph
173 draw-narrow-code-point-on-screen-buffer:  # buffer: (addr byte), letter-bitmap: (addr byte), x: int, y: int, color: int, background-color: int, overlay?: boolean, screen-width: int, screen-height: int
174     # . prologue
175     55/push-ebp
176     89/<- %ebp 4/r32/esp
177     # . save registers
178     52/push-edx
179     53/push-ebx
180     56/push-esi
181     57/push-edi
182     # esi = letter-bitmap
183     8b/-> *(ebp+0xc) 6/r32/esi
184     # var ycurr/edx: int = y*16
185     8b/-> *(ebp+0x14) 2/r32/edx
186     c1 4/subop/shift-left %edx 4/imm8
187     # var ymax/edi: int = ycurr + 16
188     8b/-> *(ebp+0x14) 7/r32/edi
189     c1 4/subop/shift-left %edi 4/imm8
190     81 0/subop/add %edi 0x10/imm32
191     {
192       # if (ycurr >= ymax) break
193       39/compare %edx 7/r32/edi
194       0f 8d/jump-if->= break/disp32
195       # var row-bitmap/ebx: byte = *letter-bitmap
196       bb/copy-to-ebx 0/imm32
197       8a/byte-> *esi 3/r32/BL
198       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx *(ebp+0x10) %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
199       # ++y
200       42/increment-edx
201       # next bitmap row
202       46/increment-esi
203       #
204       e9/jump loop/disp32
205     }
206 $draw-narrow-code-point-on-screen-buffer:end:
207     # . restore registers
208     5f/pop-to-edi
209     5e/pop-to-esi
210     5b/pop-to-ebx
211     5a/pop-to-edx
212     # . epilogue
213     89/<- %esp 5/r32/ebp
214     5d/pop-to-ebp
215     c3/return
216 
217 # buffer: naked address to raw screen RAM without a length
218 # letter-bitmap: naked address to 16-pixel wide font glyph
219 draw-wide-code-point-on-screen-buffer:  # buffer: (addr byte), letter-bitmap: (addr byte), x: int, y: int, color: int, background-color: int, overlay?: boolean, screen-width: int, screen-height: int
220     # . prologue
221     55/push-ebp
222     89/<- %ebp 4/r32/esp
223     # . save registers
224     50/push-eax
225     51/push-ecx
226     52/push-edx
227     53/push-ebx
228     56/push-esi
229     57/push-edi
230     # esi = letter-bitmap
231     8b/-> *(ebp+0xc) 6/r32/esi
232     #
233     bb/copy-to-ebx 0/imm32
234     # var ycurr/edx: int = y*16
235     8b/-> *(ebp+0x14) 2/r32/edx
236     c1 4/subop/shift-left %edx 4/imm8
237     # var ymax/edi: int = ycurr + 16
238     8b/-> *(ebp+0x14) 7/r32/edi
239     c1 4/subop/shift-left %edi 4/imm8
240     81 0/subop/add %edi 0x10/imm32
241     {
242       # if (ycurr >= ymax) break
243       39/compare %edx 7/r32/edi
244       0f 8d/jump-if->= break/disp32
245       # var row-bitmap/ebx: byte = *letter-bitmap
246       8a/byte-> *esi 3/r32/BL
247       # ecx = x
248       8b/-> *(ebp+0x10) 1/r32/ecx
249       # first half-row
250       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
251       # second half-row
252       8a/byte-> *(esi+1) 3/r32/BL
253       41/increment-ecx
254       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
255       # ++y
256       42/increment-edx
257       # next bitmap row
258       81 0/subop/add %esi 2/imm32
259       #
260       e9/jump loop/disp32
261     }
262 $draw-wide-code-point-on-screen-buffer:end:
263     # . restore registers
264     5f/pop-to-edi
265     5e/pop-to-esi
266     5b/pop-to-ebx
267     5a/pop-to-edx
268     59/pop-to-ecx
269     58/pop-to-eax
270     # . epilogue
271     89/<- %esp 5/r32/ebp
272     5d/pop-to-ebp
273     c3/return
274 
275 # draw 8 pixels from a single glyph byte in a font bitmap
276 draw-run-of-pixels-from-glyph:  # buffer: (addr byte), glyph-byte: byte, x: int, y: int, color: int, background-color: int, overlay?: boolean, screen-width: int, screen-height: int
277     # . prologue
278     55/push-ebp
279     89/<- %ebp 4/r32/esp
280     # . save registers
281     50/push-eax
282     51/push-ecx
283     56/push-esi
284     # esi = glyph-byte
285     8b/-> *(ebp+0xc) 6/r32/esi
286     # var xcurr/eax: int = x*8 + 7
287     8b/-> *(ebp+0x10) 0/r32/eax
288     c1 4/subop/shift-left %eax 3/imm8
289     05/add-to-eax 7/imm32
290     # var xmin/ecx: int = x*8
291     8b/-> *(ebp+0x10) 1/r32/ecx
292     c1 4/subop/shift-left %ecx 3/imm8
293     {
294       # if (xcurr < xmin) break
295       39/compare %eax 1/r32/ecx
296       7c/jump-if-< break/disp8
297       # shift LSB from row-bitmap into carry flag (CF)
298       c1 5/subop/shift-right-logical %esi 1/imm8
299       # if LSB, draw a pixel in the given color
300       {
301         73/jump-if-not-CF break/disp8
302         (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x18) *(ebp+0x24) *(ebp+0x28))
303         eb/jump $draw-code-point-on-screen-buffer:continue/disp8
304       }
305       # otherwise use the background color (except when overlay?)
306       {
307         81 7/subop/compare *(ebp+0x20) 0/imm32/false
308         75/jump-if-!= break/disp8
309         (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x1c) *(ebp+0x24) *(ebp+0x28))
310       }
311 $draw-code-point-on-screen-buffer:continue:
312       # --x
313       48/decrement-eax
314       #
315       eb/jump loop/disp8
316     }
317 $draw-run-of-pixels-from-glyph:end:
318     # . restore registers
319     5e/pop-to-esi
320     59/pop-to-ecx
321     58/pop-to-eax
322     # . epilogue
323     89/<- %esp 5/r32/ebp
324     5d/pop-to-ebp
325     c3/return
326 
327 cursor-position-on-real-screen:  # -> _/eax: int, _/ecx: int
328     # . prologue
329     55/push-ebp
330     89/<- %ebp 4/r32/esp
331     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
332     8b/-> *Real-screen-cursor-x 0/r32/eax
333     8b/-> *Real-screen-cursor-y 1/r32/ecx
334 $cursor-position-on-real-screen:end:
335     # . epilogue
336     89/<- %esp 5/r32/ebp
337     5d/pop-to-ebp
338     c3/return
339 
340 set-cursor-position-on-real-screen:  # x: int, y: int
341     # . prologue
342     55/push-ebp
343     89/<- %ebp 4/r32/esp
344     # . save registers
345     50/push-eax
346     #
347     8b/-> *(ebp+8) 0/r32/eax
348     89/<- *Real-screen-cursor-x 0/r32/eax
349     8b/-> *(ebp+0xc) 0/r32/eax
350     89/<- *Real-screen-cursor-y 0/r32/eax
351 $set-cursor-position-on-real-screen:end:
352     # . restore registers
353     58/pop-to-eax
354     # . epilogue
355     89/<- %esp 5/r32/ebp
356     5d/pop-to-ebp
357     c3/return
358 
359 # Not a real `show-cursor` primitive:
360 #   - does not clear previous location cursor was shown at.
361 #   - does not preserve what was at the cursor. Caller is responsible for
362 #     tracking what was on the screen at this position before and passing it
363 #     in again.
364 #   - does not stop showing the cursor at this location when the cursor moves
365 draw-cursor-on-real-screen:  # c: code-point
366     # . prologue
367     55/push-ebp
368     89/<- %ebp 4/r32/esp
369     # . save registers
370     50/push-eax
371     51/push-ecx
372     #
373     (cursor-position-on-real-screen)  # => eax, ecx
374     (draw-code-point-on-real-screen *(ebp+8) %eax %ecx 0 7)  # => eax
375 $draw-cursor-on-real-screen:end:
376     # . restore registers
377     59/pop-to-ecx
378     58/pop-to-eax
379     # . epilogue
380     89/<- %esp 5/r32/ebp
381     5d/pop-to-ebp
382     c3/return
383 
384 == data
385 
386 # The cursor is where certain Mu functions (usually of the form
387 # 'draw*cursor*') print to by default.
388 #
389 # We don't bother displaying the cursor when drawing. It only becomes visible
390 # on draw-cursor, which is quite rickety (see above)
391 #
392 # It's up to applications to manage cursor display:
393 #   - clean up where it used to be
394 #   - display the cursor before waiting for a key
395 #   - ensure its location appropriately suggests the effect keystrokes will have
396 #   - ensure its contents (and colors) appropriately reflect the state of the
397 #     screen
398 #
399 # There's no blinking, etc. We aren't using any hardware-supported text mode
400 # here.
401 Real-screen-cursor-x:
402   0/imm32
403 Real-screen-cursor-y:
404   0/imm32