From 8e182e394e492b9ff157dc88837dee894893c1ee Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sun, 29 Aug 2021 00:01:08 -0700 Subject: [PATCH] width-aware drawing primitives No support yet for drawing wide graphemes. --- 103grapheme.subx | 103 ++++++++++++++++++++++++++++++++++------ 400.mu | 5 +- 500fake-screen.mu | 116 ++++++++++++++++++++++++++++++++++++++-------- 501draw-text.mu | 39 ++++++++++------ 504test-screen.mu | 2 +- apps/ex4.mu | 2 +- 6 files changed, 215 insertions(+), 52 deletions(-) diff --git a/103grapheme.subx b/103grapheme.subx index fa3b17d4..67e34ca1 100644 --- a/103grapheme.subx +++ b/103grapheme.subx @@ -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 diff --git a/400.mu b/400.mu index 7eba6f46..fe2263a4 100644 --- a/400.mu +++ b/400.mu @@ -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 diff --git a/500fake-screen.mu b/500fake-screen.mu index 43f6152c..1e5ea687 100644 --- a/500fake-screen.mu +++ b/500fake-screen.mu @@ -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 diff --git a/501draw-text.mu b/501draw-text.mu index 9f526497..4a415ddd 100644 --- a/501draw-text.mu +++ b/501draw-text.mu @@ -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 diff --git a/504test-screen.mu b/504test-screen.mu index c73d0aa6..aef85ac6 100644 --- a/504test-screen.mu +++ b/504test-screen.mu @@ -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" diff --git a/apps/ex4.mu b/apps/ex4.mu index 9c3e28ee..f6c0bf8a 100644 --- a/apps/ex4.mu +++ b/apps/ex4.mu @@ -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 }