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