width-aware drawing primitives

No support yet for drawing wide graphemes.
This commit is contained in:
Kartik K. Agaram 2021-08-29 00:01:08 -07:00
parent bc859a7ca4
commit 8e182e394e
6 changed files with 215 additions and 52 deletions

View File

@ -10,24 +10,23 @@
# 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
draw-grapheme-on-real-screen: # g: grapheme, x: int, y: int, color: int, background-color: int -> _/eax
# . 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-screen-buffer *Video-memory-addr *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) 0x80 0x30) # => eax
$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
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 -> _/eax: 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
@ -49,12 +48,11 @@ draw-grapheme-on-screen-array: # screen-data: (addr array byte), g: grapheme, x
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-buffer %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20) *(ebp+0x24)) # => eax
$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
@ -67,15 +65,12 @@ $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
# returns number of 8x16 units printed to screen (1 or 2).
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 -> _/eax: 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
@ -88,7 +83,64 @@ draw-grapheme-on-screen-buffer: # buffer: (addr byte), g: grapheme, x: int, y:
# var letter-bitmap/esi = font[g]
69/multiply %esi 0x21/imm32/glyph-size 6/r32/esi
81 0/subop/add %esi Font/imm32
# dispatch based on letter-bitmap->size
b8/copy-to-eax 0/imm32
8a/byte-> *esi 0/r32/AL
46/increment-esi # skip size
3d/compare-eax-and 8/imm32
{
75/jump-if-!= break/disp8
(draw-narrow-grapheme-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-grapheme-on-screen-buffer:end/disp8
}
(draw-wide-grapheme-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-grapheme-on-screen-buffer:end:
# . restore registers
5e/pop-to-esi
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
wide-grapheme?: # g: grapheme -> _/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# eax = g
8b/-> *(ebp+8) 0/r32/eax
# if (g >= 128) return # characters beyond ASCII currently not supported
3d/compare-eax-and 0x80/imm32
0f 8d/jump-if->= $wide-grapheme?:end/disp32
# var letter-bitmap/eax = font[g]
69/multiply %eax 0x21/imm32/glyph-size 0/r32/eax
05/add-to-eax Font/imm32
# 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-grapheme?: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-grapheme-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
# 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
@ -139,7 +191,32 @@ $draw-grapheme-on-screen-buffer:continue:
#
e9/jump loop/disp32
}
$draw-grapheme-on-screen-buffer:end:
$draw-narrow-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
# buffer: naked address to raw screen RAM without a length
# letter-bitmap: naked address to 16-pixel wide font glyph
draw-wide-grapheme-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
# HERE
$draw-wide-grapheme-on-screen-buffer:end:
# . restore registers
5e/pop-to-esi
5b/pop-to-ebx
@ -198,7 +275,7 @@ draw-cursor-on-real-screen: # g: grapheme
51/push-ecx
#
(cursor-position-on-real-screen) # => eax, ecx
(draw-grapheme-on-real-screen *(ebp+8) %eax %ecx 0 7)
(draw-grapheme-on-real-screen *(ebp+8) %eax %ecx 0 7) # => eax
$draw-cursor-on-real-screen:end:
# . restore registers
59/pop-to-ecx

5
400.mu
View File

@ -1,7 +1,8 @@
# screen
sig pixel-on-real-screen x: int, y: int, color: int
sig draw-grapheme-on-real-screen g: grapheme, x: int, y: int, color: int, background-color: int
sig 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
sig draw-grapheme-on-real-screen g: grapheme, x: int, y: int, color: int, background-color: int -> _/eax: int
sig 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 -> _/eax: int
sig wide-grapheme? g: grapheme -> _/eax: boolean
sig cursor-position-on-real-screen -> _/eax: int, _/ecx: int
sig set-cursor-position-on-real-screen x: int, y: int
sig draw-cursor-on-real-screen g: grapheme

View File

@ -48,8 +48,6 @@ fn initialize-screen _screen: (addr screen), width: int, height: int, pixel-grap
capacity <- multiply height
#
populate data-ah, capacity
# save sentinel index
capacity <- decrement
}
# if necessary, populate screen->pixels
{
@ -62,8 +60,6 @@ fn initialize-screen _screen: (addr screen), width: int, height: int, pixel-grap
capacity <- shift-left 4/log2-font-height
#
populate pixels-ah, capacity
# save sentinel index
capacity <- decrement
}
# screen->cursor-x = 0
dest <- get screen, cursor-x
@ -92,15 +88,29 @@ fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
}
# testable screen primitive
fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int {
# return number of 8x16 units drawn
fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int -> _/eax: int {
var screen/esi: (addr screen) <- copy _screen
{
compare screen, 0
break-if-!=
draw-grapheme-on-real-screen g, x, y, color, background-color
return
var result/eax: int <- draw-grapheme-on-real-screen g, x, y, color, background-color
return result
}
# fake screen
var wide?/eax: boolean <- wide-grapheme? g
compare wide?, 0/false
{
break-if-=
draw-wide-grapheme-on-fake-screen screen, g, x, y, color, background-color
return 2
}
draw-narrow-grapheme-on-fake-screen screen, g, x, y, color, background-color
return 1
}
fn draw-narrow-grapheme-on-fake-screen _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int {
var screen/esi: (addr screen) <- copy _screen
# ignore if out of bounds
{
compare x, 0
@ -108,10 +118,13 @@ fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int
return
}
{
var xmax/eax: (addr int) <- get screen, width
var xcurr/ecx: int <- copy x
compare xcurr, *xmax
var xmax-addr/eax: (addr int) <- get screen, width
var xmax/eax: int <- copy *xmax-addr
compare x, xmax
break-if-<
{
loop
}
return
}
{
@ -120,9 +133,9 @@ fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int
return
}
{
var ymax/eax: (addr int) <- get screen, height
var ycurr/ecx: int <- copy y
compare ycurr, *ymax
var ymax-addr/eax: (addr int) <- get screen, height
var ymax/eax: int <- copy *ymax-addr
compare y, ymax
break-if-<
return
}
@ -141,12 +154,75 @@ fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int
dest-color <- get dest-cell, background-color
src-color <- copy background-color
copy-to *dest-color, src-color
var dest/eax: (addr boolean) <- get dest-cell, unused?
copy-to *dest, 0/false
}
fn draw-wide-grapheme-on-fake-screen _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: int {
var screen/esi: (addr screen) <- copy _screen
# ignore if out of bounds
{
compare x, 0
break-if->=
return
}
{
var xmax-addr/eax: (addr int) <- get screen, width
var xmax/eax: int <- copy *xmax-addr
xmax <- decrement # wide graphemes need an extra unit
compare x, xmax
break-if-<
return
}
{
compare y, 0
break-if->=
return
}
{
var ymax-addr/eax: (addr int) <- get screen, height
var ymax/eax: int <- copy *ymax-addr
compare y, ymax
break-if-<
return
}
#
var index/ecx: int <- screen-cell-index screen, x, y
{
var data-ah/eax: (addr handle array screen-cell) <- get screen, data
var data/eax: (addr array screen-cell) <- lookup *data-ah
var offset/ecx: (offset screen-cell) <- compute-offset data, index
var dest-cell/ecx: (addr screen-cell) <- index data, offset
var dest-grapheme/eax: (addr grapheme) <- get dest-cell, data
var g2/edx: grapheme <- copy g
copy-to *dest-grapheme, g2
var dest-color/eax: (addr int) <- get dest-cell, color
var src-color/edx: int <- copy color
copy-to *dest-color, src-color
dest-color <- get dest-cell, background-color
src-color <- copy background-color
copy-to *dest-color, src-color
var dest/eax: (addr boolean) <- get dest-cell, unused?
copy-to *dest, 0/false
}
# set next screen-cell to unused
index <- increment
{
var data-ah/eax: (addr handle array screen-cell) <- get screen, data
var data/eax: (addr array screen-cell) <- lookup *data-ah
var offset/ecx: (offset screen-cell) <- compute-offset data, index
var dest-cell/ecx: (addr screen-cell) <- index data, offset
var dest/eax: (addr boolean) <- get dest-cell, unused?
copy-to *dest, 1/true
}
}
# we can't really render non-ASCII yet, but when we do we'll be ready
fn draw-code-point screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
# return number of 8x16 units drawn
fn draw-code-point screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int -> _/eax: int {
var g/eax: grapheme <- copy c
draw-grapheme screen, g, x, y, color, background-color
var result/eax: int <- draw-grapheme screen, g, x, y, color, background-color
return result
}
# fake screens only
@ -233,7 +309,7 @@ fn draw-cursor screen: (addr screen), g: grapheme {
var cursor-x/eax: int <- copy 0
var cursor-y/ecx: int <- copy 0
cursor-x, cursor-y <- cursor-position screen
draw-grapheme screen, g, cursor-x, cursor-y, 0/fg, 7/bg
var dummy/eax: int <- draw-grapheme screen, g, cursor-x, cursor-y, 0/fg, 7/bg
}
fn clear-screen _screen: (addr screen) {
@ -256,7 +332,7 @@ fn clear-screen _screen: (addr screen) {
{
compare x, *width
break-if->=
draw-code-point screen, 0/nul, x, y, 0/fg=black, 0/bg=black
var dummy/eax: int <- draw-code-point screen, 0/nul, x, y, 0/fg=black, 0/bg=black
x <- increment
loop
}
@ -358,7 +434,7 @@ fn clear-rect _screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int
{
compare x, xmax
break-if->=
draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
x <- increment
loop
}
@ -618,9 +694,9 @@ fn convert-graphemes-to-pixels _screen: (addr screen) {
var fg: int
copy-to fg, tmp
var bg/eax: int <- screen-background-color-at screen, x, y
draw-grapheme-on-screen-array data, g, x, y, fg, bg, *width-a, *height-a
var offset/eax: int <- draw-grapheme-on-screen-array data, g, x, y, fg, bg, *width-a, *height-a
x <- add offset
}
x <- increment
loop
}
y <- increment

View File

@ -85,14 +85,21 @@ fn draw-grapheme-at-cursor-over-full-screen screen: (addr screen), g: grapheme,
var cursor-x/eax: int <- copy 0
var cursor-y/ecx: int <- copy 0
cursor-x, cursor-y <- cursor-position screen
draw-grapheme screen, g, cursor-x, cursor-y, color, background-color
var _offset/eax: int <- draw-grapheme screen, g, cursor-x, cursor-y, color, background-color
var offset/edx: int <- copy _offset
var width/eax: int <- copy 0
var dummy/ecx: int <- copy 0
width, dummy <- screen-size screen
move-cursor-rightward-and-downward screen, 0 width
offset <- decrement
compare offset, 0
{
break-if-=
# should never move downward here
move-cursor-rightward-and-downward screen, 0 width
}
}
# we can't really render non-ASCII yet, but when we do we'll be ready
fn draw-code-point-at-cursor-over-full-screen screen: (addr screen), c: code-point, color: int, background-color: int {
var g/eax: grapheme <- copy c
draw-grapheme-at-cursor-over-full-screen screen, g, color, background-color
@ -118,8 +125,8 @@ fn draw-stream-rightward screen: (addr screen), stream: (addr stream byte), x: i
var g/eax: grapheme <- read-grapheme stream
compare g, 0xffffffff/end-of-file
break-if-=
draw-grapheme screen, g, xcurr, y, color, background-color
xcurr <- increment
var offset/eax: int <- draw-grapheme screen, g, xcurr, y, color, background-color
xcurr <- add offset
loop
}
set-cursor-position screen, xcurr, y
@ -151,17 +158,17 @@ fn draw-text-rightward-from-cursor-over-full-screen screen: (addr screen), text:
fn render-grapheme screen: (addr screen), g: grapheme, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
compare g, 0xa/newline
var x/eax: int <- copy x
var x/ecx: int <- copy x
{
break-if-!=
# minimum effort to clear cursor
draw-code-point screen, 0x20/space, x, y, color, background-color
var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, color, background-color
x <- copy xmin
increment y
return x, y
}
draw-grapheme screen, g, x, y, color, background-color
x <- increment
var offset/eax: int <- draw-grapheme screen, g, x, y, color, background-color
x <- add offset
compare x, xmax
{
break-if-<
@ -294,8 +301,8 @@ fn draw-int32-hex-wrapping-right-then-down screen: (addr screen), n: int, xmin:
var g/eax: grapheme <- read-grapheme stream
compare g, 0xffffffff/end-of-file
break-if-=
draw-grapheme screen, g, xcurr, ycurr, color, background-color
xcurr <- increment
var offset/eax: int <- draw-grapheme screen, g, xcurr, ycurr, color, background-color
xcurr <- add offset
compare xcurr, xmax
{
break-if-<
@ -348,8 +355,8 @@ fn draw-int32-decimal-wrapping-right-then-down screen: (addr screen), n: int, xm
var g/eax: grapheme <- read-grapheme stream
compare g, 0xffffffff/end-of-file
break-if-=
draw-grapheme screen, g, xcurr, ycurr, color, background-color
xcurr <- increment
var offset/eax: int <- draw-grapheme screen, g, xcurr, ycurr, color, background-color
xcurr <- add offset
compare xcurr, xmax
{
break-if-<
@ -408,13 +415,14 @@ fn draw-text-downward screen: (addr screen), text: (addr array byte), x: int, y:
# draw a single-line stream vertically from x, y to ymax
# return the next 'y' coordinate
# if there isn't enough space, truncate
# TODO: should we track horizontal width?
fn draw-stream-downward screen: (addr screen), stream: (addr stream byte), x: int, y: int, ymax: int, color: int, background-color: int -> _/eax: int {
var ycurr/ecx: int <- copy y
{
var g/eax: grapheme <- read-grapheme stream
compare g, 0xffffffff/end-of-file
break-if-=
draw-grapheme screen, g, x, ycurr, color, background-color
var dummy/eax: int <- draw-grapheme screen, g, x, ycurr, color, background-color
ycurr <- increment
loop
}
@ -447,6 +455,7 @@ fn draw-text-wrapping-down-then-right screen: (addr screen), text: (addr array b
# return the next (x, y) coordinate in raster order where drawing stopped
# that way the caller can draw more if given the same min and max bounding-box.
# if there isn't enough space, truncate
# TODO: should we track horizontal width? just always offset by 2 for now
fn draw-stream-wrapping-down-then-right screen: (addr screen), stream: (addr stream byte), xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
var xcurr/edx: int <- copy x
var ycurr/ecx: int <- copy y
@ -454,12 +463,12 @@ fn draw-stream-wrapping-down-then-right screen: (addr screen), stream: (addr str
var g/eax: grapheme <- read-grapheme stream
compare g, 0xffffffff/end-of-file
break-if-=
draw-grapheme screen, g, xcurr, ycurr, color, background-color
var offset/eax: int <- draw-grapheme screen, g, xcurr, ycurr, color, background-color
ycurr <- increment
compare ycurr, ymax
{
break-if-<
xcurr <- increment
xcurr <- add 2
ycurr <- copy ymin
}
loop

View File

@ -337,7 +337,7 @@ fn test-draw-single-grapheme {
var _screen: screen
var screen/esi: (addr screen) <- address _screen
initialize-screen screen, 5, 4, 0/no-pixel-graphics
draw-code-point screen, 0x61/a, 0/x, 0/y, 1/fg, 2/bg
var dummy/eax: int <- draw-code-point screen, 0x61/a, 0/x, 0/y, 1/fg, 2/bg
check-screen-row screen, 0/y, "a", "F - test-draw-single-grapheme" # top-left corner of the screen
check-screen-row-in-color screen, 1/fg, 0/y, "a", "F - test-draw-single-grapheme-fg"
check-screen-row-in-background-color screen, 2/bg, 0/y, "a", "F - test-draw-single-grapheme-bg"

View File

@ -10,5 +10,5 @@
# Expected output: letter 'A' in green near the top-left corner of screen
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
draw-code-point screen, 0x41/A, 2/row, 1/col, 0xa/fg, 0/bg
var dummy/eax: int <- draw-code-point screen, 0x41/A, 2/row, 1/col, 0xa/fg, 0/bg
}