# Use the built-in font to draw glyphs to screen. # https://en.wikipedia.org/wiki/Glyph#Typography # The Mu computer can currently only render glyphs corresponding to single # code points. No combining characters. # https://en.wikipedia.org/wiki/Code_point # https://en.wikipedia.org/wiki/Combining_character # # We need to do this in machine code because Mu doesn't have global variables # yet (for the start of the font). == code # The Mu computer's screen is 1024px wide and 768px tall. # The Mu computer's font is 8px wide and 16px tall. # Therefore 'x' here is in [0, 128), and 'y' is in [0, 48) # Doesn't update the cursor; where the cursor should go after printing the # current code-point is a higher-level concern. draw-code-point-on-real-screen: # c: code-point, x: int, y: int, color: int, background-color: int -> _/eax # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # (draw-code-point-on-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 0x80 0x30) # => eax $draw-code-point-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return 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 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx 52/push-edx # if screen-width*screen-height > len(screen-data) abort { # ecx = len(screen-data) 8b/-> *(ebp+8) 1/r32/ecx 8b/-> *ecx 1/r32/ecx # eax = screen-width*screen-height ba/copy-to-edx 0/imm32 8b/-> *(ebp+0x20) 0/r32/eax f7 4/subop/multiply-into-eax *(ebp+0x24) 81 7/subop/compare %edx 0/imm32 0f 85/jump-if-!= $draw-code-point-on-screen-array:overflow/disp32 # if (eax > ecx) abort 39/compare %eax 1/r32/ecx 0f 8f/jump-if-> $draw-code-point-on-screen-array:abort/disp32 } # eax = screen-data+4 (skip length) 8b/-> *(ebp+8) 0/r32/eax 05/add-to-eax 4/imm32 # (draw-code-point-on-screen-buffer %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) # => eax $draw-code-point-on-screen-array:end: # . restore registers 5a/pop-to-edx 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return $draw-code-point-on-screen-array:overflow: (abort "draw-code-point-on-screen-array: screen dimensions too large") $draw-code-point-on-screen-array:abort: (abort "draw-code-point-on-screen-array: coordinates are off the screen. Are the screen dimensions correct?") # 'buffer' here is not a valid Mu type: a naked address without a length. # returns number of 8x16 units printed to screen (1 or 2). 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 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 56/push-esi # switch screen-width and screen-height from code-point to pixel units c1 4/subop/shift-left *(ebp+20) 3/imm8/log2-font-width c1 4/subop/shift-left *(ebp+24) 4/imm8/log2-font-height # esi = c 8b/-> *(ebp+0xc) 6/r32/esi # 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 # next few to support: CJK, ethiopic, cherokee, ... 81 7/subop/compare %esi 0x1100/imm32/4352 0f 8d/jump-if->= $draw-code-point-on-screen-buffer:end/disp32 # var letter-bitmap/esi = font[c] 69/multiply %esi 0x22/imm32/glyph-size 6/r32/esi 81 0/subop/add %esi 0x0010000c/imm32/Font # see boot.subx # dispatch based on letter-bitmap->size b8/copy-to-eax 0/imm32 8a/byte-> *esi 0/r32/AL 46/increment-esi # skip size 46/increment-esi # skip size 3d/compare-eax-and 8/imm32 { 75/jump-if-!= break/disp8 (draw-narrow-code-point-on-screen-buffer *(ebp+8) %esi *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) b8/copy-to-eax 1/imm32 eb/jump $draw-code-point-on-screen-buffer:end/disp8 } (draw-wide-code-point-on-screen-buffer *(ebp+8) %esi *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) b8/copy-to-eax 2/imm32 $draw-code-point-on-screen-buffer:end: # . restore registers 5e/pop-to-esi # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return wide-code-point?: # c: code-point -> _/eax: boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # eax = c 8b/-> *(ebp+8) 0/r32/eax # if (c >= 128) return # characters beyond ASCII currently not supported 3d/compare-eax-and 0x80/imm32 0f 8d/jump-if->= $wide-code-point?:end/disp32 # var letter-bitmap/eax = font[c] 69/multiply %eax 0x22/imm32/glyph-size 0/r32/eax 05/add-to-eax 0x0010000c/imm32/Font # see boot.subx # dispatch based on letter-bitmap->size 8a/byte-> *eax 0/r32/AL 25/and-eax-with 0xff/imm32 3d/compare-eax-and 8/imm32 0f 95/set-if-!= %eax $wide-code-point?:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # buffer: naked address to raw screen RAM without a length # letter-bitmap: naked address to 8-pixel wide font glyph 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 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # esi = letter-bitmap 8b/-> *(ebp+0xc) 6/r32/esi # var ycurr/edx: int = y*16 8b/-> *(ebp+0x14) 2/r32/edx c1 4/subop/shift-left %edx 4/imm8 # var ymax/edi: int = ycurr + 16 8b/-> *(ebp+0x14) 7/r32/edi c1 4/subop/shift-left %edi 4/imm8 81 0/subop/add %edi 0x10/imm32 { # if (ycurr >= ymax) break 39/compare %edx 7/r32/edi 0f 8d/jump-if->= break/disp32 # var row-bitmap/ebx: byte = *letter-bitmap bb/copy-to-ebx 0/imm32 8a/byte-> *esi 3/r32/BL (draw-run-of-pixels-from-glyph *(ebp+8) %ebx *(ebp+0x10) %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) # ++y 42/increment-edx # next bitmap row 46/increment-esi # e9/jump loop/disp32 } $draw-narrow-code-point-on-screen-buffer:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # buffer: naked address to raw screen RAM without a length # letter-bitmap: naked address to 16-pixel wide font glyph 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 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # esi = letter-bitmap 8b/-> *(ebp+0xc) 6/r32/esi # bb/copy-to-ebx 0/imm32 # var ycurr/edx: int = y*16 8b/-> *(ebp+0x14) 2/r32/edx c1 4/subop/shift-left %edx 4/imm8 # var ymax/edi: int = ycurr + 16 8b/-> *(ebp+0x14) 7/r32/edi c1 4/subop/shift-left %edi 4/imm8 81 0/subop/add %edi 0x10/imm32 { # if (ycurr >= ymax) break 39/compare %edx 7/r32/edi 0f 8d/jump-if->= break/disp32 # var row-bitmap/ebx: byte = *letter-bitmap 8a/byte-> *esi 3/r32/BL # ecx = x 8b/-> *(ebp+0x10) 1/r32/ecx # first half-row (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) # second half-row 8a/byte-> *(esi+1) 3/r32/BL 41/increment-ecx (draw-run-of-pixels-from-glyph *(ebp+8) %ebx %ecx %edx *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) # ++y 42/increment-edx # next bitmap row 81 0/subop/add %esi 2/imm32 # e9/jump loop/disp32 } $draw-wide-code-point-on-screen-buffer:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # draw 8 pixels from a single glyph byte in a font bitmap 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 # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi # esi = glyph-byte 8b/-> *(ebp+0xc) 6/r32/esi # var xcurr/eax: int = x*8 + 7 8b/-> *(ebp+0x10) 0/r32/eax c1 4/subop/shift-left %eax 3/imm8 05/add-to-eax 7/imm32 # var xmin/ecx: int = x*8 8b/-> *(ebp+0x10) 1/r32/ecx c1 4/subop/shift-left %ecx 3/imm8 { # if (xcurr < xmin) break 39/compare %eax 1/r32/ecx 7c/jump-if-< break/disp8 # shift LSB from row-bitmap into carry flag (CF) c1 5/subop/shift-right-logical %esi 1/imm8 # if LSB, draw a pixel in the given color { 73/jump-if-not-CF break/disp8 (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x18) *(ebp+0x20) *(ebp+0x24)) eb/jump $draw-code-point-on-screen-buffer:continue/disp8 } # otherwise use the background color (pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) $draw-code-point-on-screen-buffer:continue: # --x 48/decrement-eax # eb/jump loop/disp8 } $draw-run-of-pixels-from-glyph:end: # . restore registers 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return cursor-position-on-real-screen: # -> _/eax: int, _/ecx: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # TODO: support fake screen; we currently assume 'screen' is always 0 (real) 8b/-> *Real-screen-cursor-x 0/r32/eax 8b/-> *Real-screen-cursor-y 1/r32/ecx $cursor-position-on-real-screen:end: # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return set-cursor-position-on-real-screen: # x: int, y: int # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax # 8b/-> *(ebp+8) 0/r32/eax 89/<- *Real-screen-cursor-x 0/r32/eax 8b/-> *(ebp+0xc) 0/r32/eax 89/<- *Real-screen-cursor-y 0/r32/eax $set-cursor-position-on-real-screen:end: # . restore registers 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # Not a real `show-cursor` primitive: # - does not clear previous location cursor was shown at. # - does not preserve what was at the cursor. Caller is responsible for # tracking what was on the screen at this position before and passing it # in again. # - does not stop showing the cursor at this location when the cursor moves draw-cursor-on-real-screen: # c: code-point # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx # (cursor-position-on-real-screen) # => eax, ecx (draw-code-point-on-real-screen *(ebp+8) %eax %ecx 0 7) # => eax $draw-cursor-on-real-screen:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return == data # The cursor is where certain Mu functions (usually of the form # 'draw*cursor*') print to by default. # # We don't bother displaying the cursor when drawing. It only becomes visible # on draw-cursor, which is quite rickety (see above) # # It's up to applications to manage cursor display: # - clean up where it used to be # - display the cursor before waiting for a key # - ensure its location appropriately suggests the effect keystrokes will have # - ensure its contents (and colors) appropriately reflect the state of the # screen # # There's no blinking, etc. We aren't using any hardware-supported text mode # here. Real-screen-cursor-x: 0/imm32 Real-screen-cursor-y: 0/imm32