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) 1 *(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 >= 4352) return false
134     3d/compare-eax-and 0x1100/imm32
135     0f 8d/jump-if->= $wide-code-point?:return-false/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 $wide-code-point?:return-false:
151     b8/copy-to-eax 0/imm32/false
152     # . epilogue
153     89/<- %esp 5/r32/ebp
154     5d/pop-to-ebp
155     c3/return
156 
157 combining-code-point?:  # c: code-point -> _/eax: boolean
158     # . prologue
159     55/push-ebp
160     89/<- %ebp 4/r32/esp
161     # eax = c
162     8b/-> *(ebp+8) 0/r32/eax
163     # if (c >= 4352) return false
164     3d/compare-eax-and 0x1100/imm32
165     0f 8d/jump-if->= $combining-code-point?:return-false/disp32
166     # var letter-bitmap/eax = font[c]
167     69/multiply %eax 0x22/imm32/glyph-size 0/r32/eax
168     05/add-to-eax 0x0010000c/imm32/Font  # see boot.subx
169     # dispatch based on letter-bitmap->is-combine?
170     8a/byte-> *(eax+1) 0/r32/AL
171     25/and-eax-with  0xff/imm32
172 $combining-code-point?:end:
173     # . epilogue
174     89/<- %esp 5/r32/ebp
175     5d/pop-to-ebp
176     c3/return
177 
178 $combining-code-point?:return-false:
179     b8/copy-to-eax 0/imm32/false
180     # . epilogue
181     89/<- %esp 5/r32/ebp
182     5d/pop-to-ebp
183     c3/return
184 
185 # buffer: naked address to raw screen RAM without a length
186 # letter-bitmap: naked address to 8-pixel wide font glyph
187 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
188     # . prologue
189     55/push-ebp
190     89/<- %ebp 4/r32/esp
191     # . save registers
192     52/push-edx
193     53/push-ebx
194     56/push-esi
195     57/push-edi
196     # esi = letter-bitmap
197     8b/-> *(ebp+0xc) 6/r32/esi
198     # var ycurr/edx: int = y*16
199     8b/-> *(ebp+0x14) 2/r32/edx
200     c1 4/subop/shift-left %edx 4/imm8
201     # var ymax/edi: int = ycurr + 16
202     8b/-> *(ebp+0x14) 7/r32/edi
203     c1 4/subop/shift-left %edi 4/imm8
204     81 0/subop/add %edi 0x10/imm32
205     {
206       # if (ycurr >= ymax) break
207       39/compare %edx 7/r32/edi
208       0f 8d/jump-if->= break/disp32
209       # var row-bitmap/ebx: byte = *letter-bitmap
210       bb/copy-to-ebx 0/imm32
211       8a/byte-> *esi 3/r32/BL
212       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx *(ebp+0x10) %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
213       # ++y
214       42/increment-edx
215       # next bitmap row
216       46/increment-esi
217       #
218       e9/jump loop/disp32
219     }
220 $draw-narrow-code-point-on-screen-buffer:end:
221     # . restore registers
222     5f/pop-to-edi
223     5e/pop-to-esi
224     5b/pop-to-ebx
225     5a/pop-to-edx
226     # . epilogue
227     89/<- %esp 5/r32/ebp
228     5d/pop-to-ebp
229     c3/return
230 
231 # buffer: naked address to raw screen RAM without a length
232 # letter-bitmap: naked address to 16-pixel wide font glyph
233 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
234     # . prologue
235     55/push-ebp
236     89/<- %ebp 4/r32/esp
237     # . save registers
238     50/push-eax
239     51/push-ecx
240     52/push-edx
241     53/push-ebx
242     56/push-esi
243     57/push-edi
244     # esi = letter-bitmap
245     8b/-> *(ebp+0xc) 6/r32/esi
246     #
247     bb/copy-to-ebx 0/imm32
248     # var ycurr/edx: int = y*16
249     8b/-> *(ebp+0x14) 2/r32/edx
250     c1 4/subop/shift-left %edx 4/imm8
251     # var ymax/edi: int = ycurr + 16
252     8b/-> *(ebp+0x14) 7/r32/edi
253     c1 4/subop/shift-left %edi 4/imm8
254     81 0/subop/add %edi 0x10/imm32
255     {
256       # if (ycurr >= ymax) break
257       39/compare %edx 7/r32/edi
258       0f 8d/jump-if->= break/disp32
259       # var row-bitmap/ebx: byte = *letter-bitmap
260       8a/byte-> *esi 3/r32/BL
261       # ecx = x
262       8b/-> *(ebp+0x10) 1/r32/ecx
263       # first half-row
264       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
265       # second half-row
266       8a/byte-> *(esi+1) 3/r32/BL
267       41/increment-ecx
268       (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24) *(ebp+0x28))
269       # ++y
270       42/increment-edx
271       # next bitmap row
272       81 0/subop/add %esi 2/imm32
273       #
274       e9/jump loop/disp32
275     }
276 $draw-wide-code-point-on-screen-buffer:end:
277     # . restore registers
278     5f/pop-to-edi
279     5e/pop-to-esi
280     5b/pop-to-ebx
281     5a/pop-to-edx
282     59/pop-to-ecx
283     58/pop-to-eax
284     # . epilogue
285     89/<- %esp 5/r32/ebp
286     5d/pop-to-ebp
287     c3/return
288 
289 # draw 8 pixels from a single glyph byte in a font bitmap
290 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
291     # . prologue
292     55/push-ebp
293     89/<- %ebp 4/r32/esp
294     # . save registers
295     50/push-eax
296     51/push-ecx
297     56/push-esi
298     # esi = glyph-byte
299     8b/-> *(ebp+0xc) 6/r32/esi
300     # var xcurr/eax: int = x*8 + 7
301     8b/-> *(ebp+0x10) 0/r32/eax
302     c1 4/subop/shift-left %eax 3/imm8
303     05/add-to-eax 7/imm32
304     # var xmin/ecx: int = x*8
305     8b/-> *(ebp+0x10) 1/r32/ecx
306     c1 4/subop/shift-left %ecx 3/imm8
307     {
308       # if (xcurr < xmin) break
309       39/compare %eax 1/r32/ecx
310       7c/jump-if-< break/disp8
311       # shift LSB from row-bitmap into carry flag (CF)
312       c1 5/subop/shift-right-logical %esi 1/imm8
313       # if LSB, draw a pixel in the given color
314       {
315         73/jump-if-not-CF break/disp8
316         (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x18) *(ebp+0x24) *(ebp+0x28))
317         eb/jump $draw-code-point-on-screen-buffer:continue/disp8
318       }
319       # otherwise use the background color (except when overlay?)
320       {
321         81 7/subop/compare *(ebp+0x20) 0/imm32/false
322         75/jump-if-!= break/disp8
323         (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x1c) *(ebp+0x24) *(ebp+0x28))
324       }
325 $draw-code-point-on-screen-buffer:continue:
326       # --x
327       48/decrement-eax
328       #
329       eb/jump loop/disp8
330     }
331 $draw-run-of-pixels-from-glyph:end:
332     # . restore registers
333     5e/pop-to-esi
334     59/pop-to-ecx
335     58/pop-to-eax
336     # . epilogue
337     89/<- %esp 5/r32/ebp
338     5d/pop-to-ebp
339     c3/return
340 
341 cursor-position-on-real-screen:  # -> _/eax: int, _/ecx: int
342     # . prologue
343     55/push-ebp
344     89/<- %ebp 4/r32/esp
345     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
346     8b/-> *Real-screen-cursor-x 0/r32/eax
347     8b/-> *Real-screen-cursor-y 1/r32/ecx
348 $cursor-position-on-real-screen:end:
349     # . epilogue
350     89/<- %esp 5/r32/ebp
351     5d/pop-to-ebp
352     c3/return
353 
354 set-cursor-position-on-real-screen:  # x: int, y: int
355     # . prologue
356     55/push-ebp
357     89/<- %ebp 4/r32/esp
358     # . save registers
359     50/push-eax
360     #
361     8b/-> *(ebp+8) 0/r32/eax
362     89/<- *Real-screen-cursor-x 0/r32/eax
363     8b/-> *(ebp+0xc) 0/r32/eax
364     89/<- *Real-screen-cursor-y 0/r32/eax
365 $set-cursor-position-on-real-screen:end:
366     # . restore registers
367     58/pop-to-eax
368     # . epilogue
369     89/<- %esp 5/r32/ebp
370     5d/pop-to-ebp
371     c3/return
372 
373 # Not a real `show-cursor` primitive:
374 #   - does not clear previous location cursor was shown at.
375 #   - does not preserve what was at the cursor. Caller is responsible for
376 #     tracking what was on the screen at this position before and passing it
377 #     in again.
378 #   - does not stop showing the cursor at this location when the cursor moves
379 draw-cursor-on-real-screen:  # c: code-point
380     # . prologue
381     55/push-ebp
382     89/<- %ebp 4/r32/esp
383     # . save registers
384     50/push-eax
385     51/push-ecx
386     #
387     (cursor-position-on-real-screen)  # => eax, ecx
388     (draw-code-point-on-real-screen *(ebp+8) %eax %ecx 0 7)  # => eax
389 $draw-cursor-on-real-screen:end:
390     # . restore registers
391     59/pop-to-ecx
392     58/pop-to-eax
393     # . epilogue
394     89/<- %esp 5/r32/ebp
395     5d/pop-to-ebp
396     c3/return
397 
398 == data
399 
400 # The cursor is where certain Mu functions (usually of the form
401 # 'draw*cursor*') print to by default.
402 #
403 # We don't bother displaying the cursor when drawing. It only becomes visible
404 # on draw-cursor, which is quite rickety (see above)
405 #
406 # It's up to applications to manage cursor display:
407 #   - clean up where it used to be
408 #   - display the cursor before waiting for a key
409 #   - ensure its location appropriately suggests the effect keystrokes will have
410 #   - ensure its contents (and colors) appropriately reflect the state of the
411 #     screen
412 #
413 # There's no blinking, etc. We aren't using any hardware-supported text mode
414 # here.
415 Real-screen-cursor-x:
416   0/imm32
417 Real-screen-cursor-y:
418   0/imm32