flickerlessly render fake screens in environment

Font rendering now happens off the real screen, which provides the effect
of double-buffering.

Apps can now also use convert-graphemes-to-pixels for more traditional
double-buffering.
This commit is contained in:
Kartik K. Agaram 2021-06-15 10:33:18 -07:00
parent b9fea69687
commit c2c6f4c7ab
5 changed files with 100 additions and 35 deletions

View File

@ -26,6 +26,50 @@ $draw-grapheme-on-real-screen:end:
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

1
400.mu
View File

@ -1,6 +1,7 @@
# 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 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

@ -268,7 +268,7 @@ fn clear-screen _screen: (addr screen) {
{
compare x, *width
break-if->=
draw-code-point screen, 0x20/space, x, y, 0/fg=black, 0/bg=black
draw-code-point screen, 0/nul, x, y, 0/fg=black, 0/bg=black
x <- increment
loop
}
@ -580,3 +580,47 @@ fn copy-pixels _screen: (addr screen), target-screen: (addr screen) {
loop
}
}
# It turns out double-buffering graphemes is useless because rendering fonts
# takes too long. (At least under Qemu.)
# So we'll instead convert graphemes to pixels when double-buffering.
# 'screen' must be a fake screen.
fn convert-graphemes-to-pixels _screen: (addr screen) {
var screen/esi: (addr screen) <- copy _screen
var width-a/ebx: (addr int) <- get screen, width
var height-a/edx: (addr int) <- get screen, height
var data-ah/eax: (addr handle array byte) <- get screen, pixels
var _data/eax: (addr array byte) <- lookup *data-ah
var data: (addr array byte)
copy-to data, _data
var y/ecx: int <- copy 0
{
compare y, *height-a
break-if->=
var x/edi: int <- copy 0
{
compare x, *width-a
break-if->=
{
var tmp/eax: grapheme <- screen-grapheme-at screen, x, y
# skip null graphemes that only get created when clearing screen
# there may be other pixels drawn there, and we don't want to clobber them
# this is a situation where fake screens aren't faithful to real screens; we don't support overlap between graphemes and raw pixels
compare tmp, 0
break-if-=
abort "bb"
var g: grapheme
copy-to g, tmp
var tmp/eax: int <- screen-color-at screen, x, y
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
}
x <- increment
loop
}
y <- increment
loop
}
}

View File

@ -34,7 +34,7 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
var screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
compare screen-obj, 0
break-if-=
var y/ecx: int <- render-screen 0/screen, screen-obj, 0x58/xmin, 2/ymin
render-screen 0/screen, screen-obj, 0x58/xmin, 2/ymin
var key/eax: byte <- read-key 0/keyboard
compare key, 0
break-if-=

View File

@ -124,7 +124,7 @@ fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin:
var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0xc5/bg=blue-bg
}
y <- add 2 # padding
y <- maybe-render-screen screen, self, xmin, y
maybe-render-screen screen, self, xmin, y
}
fn render-sandbox-menu screen: (addr screen), _self: (addr sandbox) {
@ -195,20 +195,20 @@ fn maybe-render-empty-screen screen: (addr screen), _self: (addr sandbox), xmin:
return y
}
fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int {
var self/esi: (addr sandbox) <- copy _self
var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
compare screen-obj-cell, 0
{
break-if-!=
return ymin
return
}
var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
compare *screen-obj-cell-type, 5/screen
{
break-if-=
return ymin # silently give up on rendering the screen
return # silently give up on rendering the screen
}
var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
@ -217,15 +217,14 @@ fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int,
var screen-empty?/eax: boolean <- fake-screen-empty? screen-obj
compare screen-empty?, 0/false
break-if-=
return ymin
return
}
var x/eax: int <- draw-text-rightward screen, "screen: ", xmin, 0x99/xmax, ymin, 0x17/fg, 0xc5/bg=blue-bg
x <- copy xmin
x <- add 2
var y/ecx: int <- copy ymin
y <- increment
y <- render-screen screen, screen-obj, x, y
return y
render-screen screen, screen-obj, x, y
}
fn render-empty-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
@ -255,32 +254,10 @@ fn render-empty-screen screen: (addr screen), _target-screen: (addr screen), xmi
return screen-y
}
fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int {
var target-screen/esi: (addr screen) <- copy _target-screen
var screen-y/edi: int <- copy ymin
# text data
{
var height/edx: (addr int) <- get target-screen, height
var y/ecx: int <- copy 0
{
compare y, *height
break-if->=
set-cursor-position screen, xmin, screen-y
var width/edx: (addr int) <- get target-screen, width
var x/ebx: int <- copy 0
{
compare x, *width
break-if->=
print-screen-cell-of-fake-screen screen, target-screen, x, y
move-cursor-right screen
x <- increment
loop
}
y <- increment
screen-y <- increment
loop
}
}
convert-graphemes-to-pixels target-screen # might overwrite existing pixel data with graphemes
# overlapping the two is not supported
# pixel data
{
# screen top left pixels x y width height
@ -331,7 +308,6 @@ fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int
loop
}
}
return screen-y
}
fn has-keyboard? _self: (addr sandbox) -> _/eax: boolean {