405 lines
14 KiB
Plaintext
405 lines
14 KiB
Plaintext
# Use the built-in font to draw glyphs to screen.
|
|
# https://en.wikipedia.org/wiki/Glyph#Typography
|
|
# Extremely hacky support for combining characters.
|
|
# https://en.wikipedia.org/wiki/Code_point
|
|
# https://en.wikipedia.org/wiki/Combining_character
|
|
# All we support is drawing combining characters atop the same screen cell as
|
|
# a single base code point. See the overlay? arguments below.
|
|
#
|
|
# 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) 0 0x80 0x30) # 0/no-overlay => eax
|
|
$draw-code-point-on-real-screen:end:
|
|
# . epilogue
|
|
89/<- %esp 5/r32/ebp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
overlay-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) 1 0x80 0x30) # 1/overlay => eax
|
|
$overlay-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, overlay?: boolean, 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+24) 3/imm8/log2-font-width
|
|
c1 4/subop/shift-left *(ebp+28) 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) *(ebp+0x28))
|
|
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) *(ebp+0x28))
|
|
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
|
|
|
|
combining-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->= $combining-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->is-combine?
|
|
8a/byte-> *(eax+1) 0/r32/AL
|
|
25/and-eax-with 0xff/imm32
|
|
$combining-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, overlay?: boolean, 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) *(ebp+0x28))
|
|
# ++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, overlay?: boolean, 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) *(ebp+0x28))
|
|
# 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) *(ebp+0x28))
|
|
# ++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, overlay?: boolean, 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+0x24) *(ebp+0x28))
|
|
eb/jump $draw-code-point-on-screen-buffer:continue/disp8
|
|
}
|
|
# otherwise use the background color (except when overlay?)
|
|
{
|
|
81 7/subop/compare *(ebp+0x20) 0/imm32/false
|
|
75/jump-if-!= break/disp8
|
|
(pixel-on-screen-buffer *(ebp+8) %eax *(ebp+0x14) *(ebp+0x1c) *(ebp+0x24) *(ebp+0x28))
|
|
}
|
|
$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
|