# Use the built-in font to draw a grapheme to real screen.
#
# We need to do this in machine code because Mu doesn't have global variables
# yet (for the start of the font).
#
# There are uncomfortable assumptions baked in here about english/latin
# script. We convert the grid of pixels into a fixed-width grid of graphemes,
# which may not work well with other language families.
== 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 grapheme is a higher-level concern.
draw-grapheme-on-real-screen: # g: grapheme, x: int, y: int, color: int, background-color: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(draw-grapheme-on-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 0x80 0x30)
$draw-grapheme-on-real-screen:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
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
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
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-grapheme-on-screen-array:overflow/disp32
# if (eax > ecx) abort
39/compare %eax 1/r32/ecx
0f 8f/jump-if-> $draw-grapheme-on-screen-array:abort/disp32
}
# eax = screen-data+4 (skip length)
8b/-> *(ebp+8) 0/r32/eax
05/add-to-eax 4/imm32
#
(draw-grapheme-on-screen-buffer %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24))
$draw-grapheme-on-screen-array:end:
# . restore registers
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-grapheme-on-screen-array:overflow:
(abort "draw-grapheme-on-screen-array: screen dimensions too large")
$draw-grapheme-on-screen-array:abort:
(abort "draw-grapheme-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.
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
# . 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
# switch screen-width and screen-height from grapheme 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 = g
8b/-> *(ebp+0xc) 6/r32/esi
# if (g >= 128) return # characters beyond ASCII currently not supported
81 7/subop/compare %esi 0x80/imm32
0f 8d/jump-if->= $draw-grapheme-on-screen-buffer:end/disp32
# var letter-bitmap/esi = font[g]
c1 4/subop/shift-left %esi 4/imm8
81 0/subop/add %esi Font/imm32
# var ycurr/edx: int = y*16
8b/-> *(ebp+0x14) 2/r32/edx
c1 4/subop/shift-left %edx 4/imm8
# var ymax/ebx: int = ycurr + 16
8b/-> *(ebp+0x14) 3/r32/ebx
c1 4/subop/shift-left %ebx 4/imm8
81 0/subop/add %ebx 0x10/imm32
{
# if (ycurr >= ymax) break
39/compare %edx 3/r32/ebx
0f 8d/jump-if->= break/disp32
# var xcurr/eax: int = x*8 + 7
8b/-> *(ebp+0x10) 0/r32/eax # font-width - 1
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
# var row-bitmap/ebx: int = *letter-bitmap
53/push-ebx
8b/-> *esi 3/r32/ebx
{
# 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 %ebx 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 %edx *(ebp+0x18) *(ebp+0x20) *(ebp+0x24))
eb/jump $draw-grapheme-on-screen-buffer:continue/disp8
}
# otherwise use the background color
(pixel-on-screen-buffer *(ebp+8) %eax %edx *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24))
$draw-grapheme-on-screen-buffer:continue:
# --x
48/decrement-eax
#
eb/jump loop/disp8
}
# reclaim row-bitmap
5b/pop-to-ebx
# ++y
42/increment-edx
# next bitmap row
46/increment-esi
#
e9/jump loop/disp32
}
$draw-grapheme-on-screen-buffer:end:
# . restore registers
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
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: # g: grapheme
# . 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-grapheme-on-real-screen *(ebp+8) %eax %ecx 0 7)
$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