From c2c6f4c7ab40356f1138a3f4d8f06464373ad50b Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Tue, 15 Jun 2021 10:33:18 -0700 Subject: [PATCH] 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. --- 103grapheme.subx | 44 ++++++++++++++++++++++++++++++++++++++++++++ 400.mu | 1 + 500fake-screen.mu | 46 +++++++++++++++++++++++++++++++++++++++++++++- shell/evaluate.mu | 2 +- shell/sandbox.mu | 42 +++++++++--------------------------------- 5 files changed, 100 insertions(+), 35 deletions(-) diff --git a/103grapheme.subx b/103grapheme.subx index a63e3098..c5615430 100644 --- a/103grapheme.subx +++ b/103grapheme.subx @@ -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 diff --git a/400.mu b/400.mu index c8f001d7..a0f2d85b 100644 --- a/400.mu +++ b/400.mu @@ -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 diff --git a/500fake-screen.mu b/500fake-screen.mu index 641a8d4b..59b312e2 100644 --- a/500fake-screen.mu +++ b/500fake-screen.mu @@ -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 + } +} diff --git a/shell/evaluate.mu b/shell/evaluate.mu index 70cd544d..e976e279 100644 --- a/shell/evaluate.mu +++ b/shell/evaluate.mu @@ -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-= diff --git a/shell/sandbox.mu b/shell/sandbox.mu index 1b6cd61e..a6b3453c 100644 --- a/shell/sandbox.mu +++ b/shell/sandbox.mu @@ -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 {