about summary refs log tree commit diff stats
path: root/500fake-screen.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-04-13 22:27:59 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-04-13 22:27:59 -0700
commitbbabe8bd1a727c8ea971d759241ad0b7b98bc8d8 (patch)
treed658082e04df7b7a6b3120c17bcbc1b700d9b448 /500fake-screen.mu
parent5a3f9a31850d6da72dff7211c9760cd4a8e1090b (diff)
downloadmu-bbabe8bd1a727c8ea971d759241ad0b7b98bc8d8.tar.gz
shell: pixel graphics
Diffstat (limited to '500fake-screen.mu')
-rw-r--r--500fake-screen.mu431
1 files changed, 431 insertions, 0 deletions
diff --git a/500fake-screen.mu b/500fake-screen.mu
new file mode 100644
index 00000000..1731a63c
--- /dev/null
+++ b/500fake-screen.mu
@@ -0,0 +1,431 @@
+# Testable primitives for writing to screen.
+#
+# Mu mostly uses the screen for text, but it builds it out of pixel graphics
+# and a bitmap font. There is no support for a blinking cursor, scrolling and
+# so on.
+#
+# Fake screens are primarily for testing text-mode prints. However, they do
+# support some rudimentary pixel operations as well. Caveats:
+#
+# - Drawing pixels atop text or vice versa is not supported. Results in a fake
+#   screen will not mimic real screens in these situations.
+# - Fake screens currently also assume a fixed-width 8x16 font.
+
+type screen {
+  # text mode
+  width: int
+  height: int
+  data: (handle array screen-cell)
+  cursor-x: int  # [0..width)
+  cursor-y: int  # [0..height)
+  # pixel graphics
+  pixels: (handle stream pixel)  # sparse representation
+}
+
+type screen-cell {
+  data: grapheme
+  color: int
+  background-color: int
+}
+
+type pixel {
+  x: int  # [0..width*font-width)
+  y: int  # [0..height*font-height)
+  color: int  # [0..256)
+}
+
+fn initialize-screen _screen: (addr screen), width: int, height: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var tmp/eax: int <- copy 0
+  var dest/edi: (addr int) <- copy 0
+  # screen->width = width
+  dest <- get screen, width
+  tmp <- copy width
+  copy-to *dest, tmp
+  # screen->height = height
+  dest <- get screen, height
+  tmp <- copy height
+  copy-to *dest, tmp
+  # screen->data = new screen-cell[width*height]
+  {
+    var data-addr/edi: (addr handle array screen-cell) <- get screen, data
+    tmp <- multiply width
+    populate data-addr, tmp
+  }
+  # pixels
+  var pixels-ah/ecx: (addr handle stream pixel) <- get screen, pixels
+  populate-stream pixels-ah, tmp
+  # screen->cursor-x = 0
+  dest <- get screen, cursor-x
+  copy-to *dest, 0
+  # screen->cursor-y = 0
+  dest <- get screen, cursor-y
+  copy-to *dest, 0
+}
+
+# in graphemes
+fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var width/eax: int <- copy 0
+  var height/ecx: int <- copy 0
+  compare screen, 0
+  {
+    break-if-!=
+    return 0x80/128, 0x30/48
+  }
+  # fake screen
+  var tmp/edx: (addr int) <- get screen, width
+  width <- copy *tmp
+  tmp <- get screen, height
+  height <- copy *tmp
+  return width, height
+}
+
+fn pixel screen: (addr screen), x: int, y: int, color: int {
+  {
+    compare screen, 0
+    break-if-!=
+    pixel-on-real-screen x, y, color
+    return
+  }
+  # fake screen
+  # prepare a pixel
+  var pixel-storage: pixel
+  var src/ecx: int <- copy x
+  var dest/edx: (addr int) <- get pixel-storage, x
+  copy-to *dest, src
+  src <- copy y
+  dest <- get pixel-storage, y
+  copy-to *dest, src
+  src <- copy color
+  dest <- get pixel-storage, color
+  copy-to *dest, src
+  # save it
+  var src/ecx: (addr pixel) <- address pixel-storage
+  var screen/eax: (addr screen) <- copy screen
+  var dest-stream-ah/eax: (addr handle stream pixel) <- get screen, pixels
+  var dest-stream/eax: (addr stream pixel) <- lookup *dest-stream-ah
+  write-to-stream dest-stream, src
+}
+
+# testable screen primitive
+fn draw-grapheme _screen: (addr screen), g: grapheme, x: int, y: int, color: int, background-color: 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
+  }
+  # fake screen
+  var idx/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, idx
+  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
+}
+
+# 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 {
+  var g/eax: grapheme <- copy c
+  draw-grapheme screen, g, x, y, color, background-color
+}
+
+# not really needed for a real screen, though it shouldn't do any harm
+fn screen-cell-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
+  var screen/esi: (addr screen) <- copy _screen
+  # only one bounds check isn't automatically handled
+  {
+    var xmax/eax: (addr int) <- get screen, width
+    var xcurr/ecx: int <- copy x
+    compare xcurr, *xmax
+    break-if-<
+    abort "tried to print out of screen bounds"
+  }
+  var width-addr/eax: (addr int) <- get screen, width
+  var result/ecx: int <- copy y
+  result <- multiply *width-addr
+  result <- add x
+  return result
+}
+
+fn cursor-position _screen: (addr screen) -> _/eax: int, _/ecx: int {
+  var screen/esi: (addr screen) <- copy _screen
+  {
+    compare screen, 0
+    break-if-!=
+    var x/eax: int <- copy 0
+    var y/ecx: int <- copy 0
+    x, y <- cursor-position-on-real-screen
+    return x, y
+  }
+  # fake screen
+  var cursor-x-addr/eax: (addr int) <- get screen, cursor-x
+  var cursor-y-addr/ecx: (addr int) <- get screen, cursor-y
+  return *cursor-x-addr, *cursor-y-addr
+}
+
+fn set-cursor-position _screen: (addr screen), x: int, y: int {
+  var screen/esi: (addr screen) <- copy _screen
+  {
+    compare screen, 0
+    break-if-!=
+    set-cursor-position-on-real-screen x, y
+    return
+  }
+  # fake screen
+  # ignore x < 0
+  {
+    compare x, 0
+    break-if->=
+    return
+  }
+  # ignore x >= width
+  {
+    var width-addr/eax: (addr int) <- get screen, width
+    var width/eax: int <- copy *width-addr
+    compare x, width
+    break-if-<=
+    return
+  }
+  # ignore y < 0
+  {
+    compare y, 0
+    break-if->=
+    return
+  }
+  # ignore y >= height
+  {
+    var height-addr/eax: (addr int) <- get screen, height
+    var height/eax: int <- copy *height-addr
+    compare y, height
+    break-if-<
+    return
+  }
+  # screen->cursor-x = x
+  var dest/edi: (addr int) <- get screen, cursor-x
+  var src/eax: int <- copy x
+  copy-to *dest, src
+  # screen->cursor-y = y
+  dest <- get screen, cursor-y
+  src <- copy y
+  copy-to *dest, src
+}
+
+fn draw-cursor screen: (addr screen), g: grapheme {
+  {
+    compare screen, 0
+    break-if-!=
+    draw-cursor-on-real-screen g
+    return
+  }
+  # fake screen
+  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
+}
+
+fn clear-screen _screen: (addr screen) {
+  var screen/esi: (addr screen) <- copy _screen
+  {
+    compare screen, 0
+    break-if-!=
+    clear-real-screen
+    return
+  }
+  # fake screen
+  set-cursor-position screen, 0, 0
+  var y/eax: int <- copy 0
+  var height/ecx: (addr int) <- get screen, height
+  {
+    compare y, *height
+    break-if->=
+    var x/edx: int <- copy 0
+    var width/ebx: (addr int) <- get screen, width
+    {
+      compare x, *width
+      break-if->=
+      draw-code-point screen, 0x20/space, x, y, 0/fg=black, 0/bg=black
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+  set-cursor-position screen, 0, 0
+  var dest-stream-ah/eax: (addr handle stream pixel) <- get screen, pixels
+  var dest-stream/eax: (addr stream pixel) <- lookup *dest-stream-ah
+  clear-stream dest-stream
+}
+
+fn fake-screen-empty? _screen: (addr screen) -> _/eax: boolean {
+  var screen/esi: (addr screen) <- copy _screen
+  var y/eax: int <- copy 0
+  var height/ecx: (addr int) <- get screen, height
+  {
+    compare y, *height
+    break-if->=
+    var x/edx: int <- copy 0
+    var width/ebx: (addr int) <- get screen, width
+    {
+      compare x, *width
+      break-if->=
+      var g/eax: grapheme <- screen-grapheme-at screen, x, y
+      {
+        compare g, 0
+        break-if-=
+        compare g, 0x20/space
+        break-if-=
+        return 0/false
+      }
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+  var pixels-ah/eax: (addr handle stream pixel) <- get screen, pixels
+  var pixels/eax: (addr stream pixel) <- lookup *pixels-ah
+  var result/eax: boolean <- stream-empty? pixels
+  return result
+}
+
+fn clear-rect _screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
+  var screen/esi: (addr screen) <- copy _screen
+  {
+    compare screen, 0
+    break-if-!=
+    clear-rect-on-real-screen xmin, ymin, xmax, ymax, background-color
+    return
+  }
+  # fake screen
+  set-cursor-position screen, 0, 0
+  var y/eax: int <- copy ymin
+  var ymax/ecx: int <- copy ymax
+  {
+    compare y, ymax
+    break-if->=
+    var x/edx: int <- copy xmin
+    var xmax/ebx: int <- copy xmax
+    {
+      compare x, xmax
+      break-if->=
+      draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+  set-cursor-position screen, 0, 0
+}
+
+# there's no grapheme that guarantees to cover every pixel, so we'll bump down
+# to pixels for a real screen
+fn clear-real-screen {
+  var y/eax: int <- copy 0
+  {
+    compare y, 0x300/screen-height=768
+    break-if->=
+    var x/edx: int <- copy 0
+    {
+      compare x, 0x400/screen-width=1024
+      break-if->=
+      pixel-on-real-screen x, y, 0/color=black
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+}
+
+fn clear-rect-on-real-screen xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
+  var y/eax: int <- copy ymin
+  y <- shift-left 4/log-font-height
+  var ymax/ecx: int <- copy ymax
+  ymax <- shift-left 4/log-font-height
+  {
+    compare y, ymax
+    break-if->=
+    var x/edx: int <- copy xmin
+    x <- shift-left 3/log-font-width
+    var xmax/ebx: int <- copy xmax
+    xmax <- shift-left 3/log-font-width
+    {
+      compare x, xmax
+      break-if->=
+      pixel-on-real-screen x, y, background-color
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+}
+
+fn screen-grapheme-at _screen: (addr screen), x: int, y: int -> _/eax: grapheme {
+  var screen/esi: (addr screen) <- copy _screen
+  var idx/ecx: int <- screen-cell-index screen, x, y
+  var result/eax: grapheme <- screen-grapheme-at-idx screen, idx
+  return result
+}
+
+fn screen-grapheme-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: grapheme {
+  var screen/esi: (addr screen) <- copy _screen
+  var data-ah/eax: (addr handle array screen-cell) <- get screen, data
+  var data/eax: (addr array screen-cell) <- lookup *data-ah
+  var idx/ecx: int <- copy idx-on-stack
+  var offset/ecx: (offset screen-cell) <- compute-offset data, idx
+  var cell/eax: (addr screen-cell) <- index data, offset
+  var src/eax: (addr grapheme) <- get cell, data
+  return *src
+}
+
+fn screen-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var idx/ecx: int <- screen-cell-index screen, x, y
+  var result/eax: int <- screen-color-at-idx screen, idx
+  return result
+}
+
+fn screen-color-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var data-ah/eax: (addr handle array screen-cell) <- get screen, data
+  var data/eax: (addr array screen-cell) <- lookup *data-ah
+  var idx/ecx: int <- copy idx-on-stack
+  var offset/ecx: (offset screen-cell) <- compute-offset data, idx
+  var cell/eax: (addr screen-cell) <- index data, offset
+  var src/eax: (addr int) <- get cell, color
+  var result/eax: int <- copy *src
+  return result
+}
+
+fn screen-background-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var idx/ecx: int <- screen-cell-index screen, x, y
+  var result/eax: int <- screen-background-color-at-idx screen, idx
+  return result
+}
+
+fn screen-background-color-at-idx _screen: (addr screen), idx-on-stack: int -> _/eax: int {
+  var screen/esi: (addr screen) <- copy _screen
+  var data-ah/eax: (addr handle array screen-cell) <- get screen, data
+  var data/eax: (addr array screen-cell) <- lookup *data-ah
+  var idx/ecx: int <- copy idx-on-stack
+  var offset/ecx: (offset screen-cell) <- compute-offset data, idx
+  var cell/eax: (addr screen-cell) <- index data, offset
+  var src/eax: (addr int) <- get cell, background-color
+  var result/eax: int <- copy *src
+  return result
+}