about summary refs log tree commit diff stats
path: root/linux/tile/surface.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-03-03 22:09:50 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-03-03 22:21:03 -0800
commit71e4f3812982dba2efb471283d310224e8db363e (patch)
treeea111a1acb8b8845dbda39c0e1b4bac1d198143b /linux/tile/surface.mu
parentc6b928be29ac8cdb4e4d6e1eaa20420ff03e5a4c (diff)
downloadmu-71e4f3812982dba2efb471283d310224e8db363e.tar.gz
7842 - new directory organization
Baremetal is now the default build target and therefore has its sources
at the top-level. Baremetal programs build using the phase-2 Mu toolchain
that requires a Linux kernel. This phase-2 codebase which used to be at
the top-level is now under the linux/ directory. Finally, the phase-2 toolchain,
while self-hosting, has a way to bootstrap from a C implementation, which
is now stored in linux/bootstrap. The bootstrap C implementation uses some
literate programming tools that are now in linux/bootstrap/tools.

So the whole thing has gotten inverted. Each directory should build one
artifact and include the main sources (along with standard library). Tools
used for building it are relegated to sub-directories, even though those
tools are often useful in their own right, and have had lots of interesting
programs written using them.

A couple of things have gotten dropped in this process:
  - I had old ways to run on just a Linux kernel, or with a Soso kernel.
    No more.
  - I had some old tooling for running a single test at the cursor. I haven't
    used that lately. Maybe I'll bring it back one day.

The reorg isn't done yet. Still to do:
  - redo documentation everywhere. All the README files, all other markdown,
    particularly vocabulary.md.
  - clean up how-to-run comments at the start of programs everywhere
  - rethink what to do with the html/ directory. Do we even want to keep
    supporting it?

In spite of these shortcomings, all the scripts at the top-level, linux/
and linux/bootstrap are working. The names of the scripts also feel reasonable.
This is a good milestone to take stock at.
Diffstat (limited to 'linux/tile/surface.mu')
-rw-r--r--linux/tile/surface.mu412
1 files changed, 412 insertions, 0 deletions
diff --git a/linux/tile/surface.mu b/linux/tile/surface.mu
new file mode 100644
index 00000000..2e353022
--- /dev/null
+++ b/linux/tile/surface.mu
@@ -0,0 +1,412 @@
+# A surface is a large 2-D grid that you can only see a subset of through the
+# screen.
+# Imagine a pin going through both surface and screen. As we update the
+# surface contents, the pinned point stays fixed, providing a sense of
+# stability.
+
+type surface {
+  screen: (handle screen)
+  data: (handle array screen-cell)
+  nrows: int
+  ncols: int
+  screen-nrows: int
+  screen-ncols: int
+  pin-row: int  # 1-indexed
+  pin-col: int  # 1-indexed
+  pin-screen-row: int  # 1-indexed
+  pin-screen-col: int  # 1-indexed
+}
+
+# intended mostly for tests; could be slow
+fn initialize-surface-with _self: (addr surface), in: (addr array byte) {
+  var self/esi: (addr surface) <- copy _self
+  # fill in nrows, ncols
+  var nrows/ecx: int <- num-lines in
+  var dest/eax: (addr int) <- get self, nrows
+  copy-to *dest, nrows
+  var ncols/edx: int <- first-line-length in  # assume all lines are the same length
+  dest <- get self, ncols
+  copy-to *dest, ncols
+  # fill in data
+  var len/ecx: int <- copy nrows
+  len <- multiply ncols
+  var out/edi: (addr surface) <- copy _self
+  var data/eax: (addr handle array screen-cell) <- get out, data
+  populate data, len
+  var data-addr/eax: (addr array screen-cell) <- lookup *data
+  fill-in data-addr, in
+  # fill in screen-nrows, screen-ncols
+  {
+    var screen-ah/eax: (addr handle screen) <- get self, screen
+    var _screen-addr/eax: (addr screen) <- lookup *screen-ah
+    var screen-addr/edi: (addr screen) <- copy _screen-addr
+    var nrows/eax: int <- copy 0
+    var ncols/ecx: int <- copy 0
+    nrows, ncols <- screen-size screen-addr
+    var dest/edi: (addr int) <- get self, screen-nrows
+    copy-to *dest, nrows
+    dest <- get self, screen-ncols
+    copy-to *dest, ncols
+  }
+}
+
+fn pin-surface-at _self: (addr surface), r: int, c: int {
+  var self/esi: (addr surface) <- copy _self
+  var dest/ecx: (addr int) <- get self, pin-row
+  var tmp/eax: int <- copy r
+  copy-to *dest, tmp
+  dest <- get self, pin-col
+  tmp <- copy c
+  copy-to *dest, tmp
+}
+
+fn pin-surface-to _self: (addr surface), sr: int, sc: int {
+  var self/esi: (addr surface) <- copy _self
+  var dest/ecx: (addr int) <- get self, pin-screen-row
+  var tmp/eax: int <- copy sr
+  copy-to *dest, tmp
+  dest <- get self, pin-screen-col
+  tmp <- copy sc
+  copy-to *dest, tmp
+}
+
+fn render-surface _self: (addr surface) {
+#?   print-string-to-real-screen "render-surface\n"
+  var self/esi: (addr surface) <- copy _self
+  # clear screen
+  var screen-ah/eax: (addr handle screen) <- get self, screen
+  var screen/eax: (addr screen) <- lookup *screen-ah
+  clear-screen screen
+  #
+  var nrows/edx: (addr int) <- get self, screen-nrows
+  var ncols/ebx: (addr int) <- get self, screen-ncols
+  var screen-row/ecx: int <- copy 1
+  {
+    compare screen-row, *nrows
+    break-if->
+    var screen-col/eax: int <- copy 1
+    {
+      compare screen-col, *ncols
+      break-if->
+#?       print-string-to-real-screen "X"
+      print-surface-cell-at self, screen-row, screen-col
+      screen-col <- increment
+      loop
+    }
+#?     print-string-to-real-screen "\n"
+    screen-row <- increment
+    loop
+  }
+}
+
+fn print-surface-cell-at _self: (addr surface), screen-row: int, screen-col: int {
+  var self/esi: (addr surface) <- copy _self
+  var row/ecx: int <- screen-row-to-surface self, screen-row
+  var col/edx: int <- screen-col-to-surface self, screen-col
+  var data-ah/edi: (addr handle array screen-cell) <- get self, data
+  var _data-addr/eax: (addr array screen-cell) <- lookup *data-ah
+  var data-addr/edi: (addr array screen-cell) <- copy _data-addr
+  var idx/eax: int <- surface-screen-cell-index self, row, col
+  # if out of bounds, print ' '
+  compare idx, 0
+  {
+    break-if->=
+    var space/ecx: grapheme <- copy 0x20
+    var screen-ah/edi: (addr handle screen) <- get self, screen
+    var screen/eax: (addr screen) <- lookup *screen-ah
+    print-grapheme screen, space
+    return
+  }
+  # otherwise print the appropriate screen-cell
+  var offset/ecx: (offset screen-cell) <- compute-offset data-addr, idx
+  var src/ecx: (addr screen-cell) <- index data-addr, offset
+  var screen-ah/edi: (addr handle screen) <- get self, screen
+  var screen/eax: (addr screen) <- lookup *screen-ah
+  print-screen-cell screen, src
+}
+
+# print a cell with all its formatting at the cursor location
+fn print-screen-cell screen: (addr screen), _cell: (addr screen-cell) {
+  var cell/esi: (addr screen-cell) <- copy _cell
+  reset-formatting screen
+  var fg/eax: (addr int) <- get cell, color
+  var bg/ecx: (addr int) <- get cell, background-color
+  start-color screen, *fg, *bg
+  var tmp/eax: (addr boolean) <- get cell, bold?
+  {
+    compare *tmp, 0
+    break-if-=
+    start-bold screen
+  }
+  {
+    tmp <- get cell, underline?
+    compare *tmp, 0
+    break-if-=
+    start-underline screen
+  }
+  {
+    tmp <- get cell, reverse?
+    compare *tmp, 0
+    break-if-=
+    start-reverse-video screen
+  }
+  {
+    tmp <- get cell, blink?
+    compare *tmp, 0
+    break-if-=
+    start-blinking screen
+  }
+  var g/eax: (addr grapheme) <- get cell, data
+  print-grapheme screen, *g
+#?   var g2/eax: grapheme <- copy *g
+#?   var g3/eax: int <- copy g2
+#?   print-int32-hex-to-real-screen g3
+#?   print-string-to-real-screen "\n"
+}
+
+fn surface-screen-cell-index _self: (addr surface), row: int, col: int -> _/eax: int {
+  var self/esi: (addr surface) <- copy _self
+#?   print-int32-hex-to-real-screen row
+#?   print-string-to-real-screen ", "
+#?   print-int32-hex-to-real-screen col
+#?   print-string-to-real-screen "\n"
+  var result/eax: int <- copy -1
+  {
+    compare row, 1
+    break-if-<
+    compare col, 1
+    break-if-<
+    var nrows-addr/ecx: (addr int) <- get self, nrows
+    var nrows/ecx: int <- copy *nrows-addr
+    compare row, nrows
+    break-if->
+    var ncols-addr/ecx: (addr int) <- get self, ncols
+    var ncols/ecx: int <- copy *ncols-addr
+    compare col, ncols
+    break-if->
+  #?   print-string-to-real-screen "!\n"
+    result <- copy row
+    result <- subtract 1
+    result <- multiply ncols
+    result <- add col
+    result <- subtract 1
+  }
+  return result
+}
+
+fn screen-row-to-surface _self: (addr surface), screen-row: int -> _/ecx: int {
+  var self/esi: (addr surface) <- copy _self
+  var result/ecx: int <- copy screen-row
+  var tmp/eax: (addr int) <- get self, pin-row
+  result <- add *tmp
+  tmp <- get self, pin-screen-row
+  result <- subtract *tmp
+  return result
+}
+
+fn max _a: int, b: int -> _/eax: int {
+  var a/eax: int <- copy _a
+  compare a, b
+  {
+    break-if->
+    return b
+  }
+  return a
+}
+
+fn min _a: int, b: int -> _/eax: int {
+  var a/eax: int <- copy _a
+  compare a, b
+  {
+    break-if->
+    return a
+  }
+  return b
+}
+
+fn screen-col-to-surface _self: (addr surface), screen-col: int -> _/edx: int {
+  var self/esi: (addr surface) <- copy _self
+  var result/edx: int <- copy screen-col
+  var tmp/eax: (addr int) <- get self, pin-col
+  result <- add *tmp
+  tmp <- get self, pin-screen-col
+  result <- subtract *tmp
+  return result
+}
+
+fn surface-row-to-screen _self: (addr surface), row: int -> _/ecx: int {
+  var self/esi: (addr surface) <- copy _self
+  var result/ecx: int <- copy row
+  var tmp/eax: (addr int) <- get self, pin-screen-row
+  result <- add *tmp
+  tmp <- get self, pin-row
+  result <- subtract *tmp
+  return result
+}
+
+fn surface-col-to-screen _self: (addr surface), col: int -> _/edx: int {
+  var self/esi: (addr surface) <- copy _self
+  var result/edx: int <- copy col
+  var tmp/eax: (addr int) <- get self, pin-screen-col
+  result <- add *tmp
+  tmp <- get self, pin-col
+  result <- subtract *tmp
+  return result
+}
+
+# assumes last line doesn't end in '\n'
+fn num-lines in: (addr array byte) -> _/ecx: int {
+  var s: (stream byte 0x100)
+  var s-addr/esi: (addr stream byte) <- address s
+  write s-addr, in
+  var result/ecx: int <- copy 1
+  {
+    var done?/eax: boolean <- stream-empty? s-addr
+    compare done?, 0/false
+    break-if-!=
+    var g/eax: grapheme <- read-grapheme s-addr
+    compare g, 0xa/newline
+    loop-if-!=
+    result <- increment
+    loop
+  }
+  return result
+}
+
+fn first-line-length in: (addr array byte) -> _/edx: int {
+  var s: (stream byte 0x100)
+  var s-addr/esi: (addr stream byte) <- address s
+  write s-addr, in
+  var result/edx: int <- copy 0
+  {
+    var done?/eax: boolean <- stream-empty? s-addr
+    compare done?, 0/false
+    break-if-!=
+    var g/eax: grapheme <- read-grapheme s-addr
+    compare g, 0xa/newline
+    break-if-=
+    result <- increment
+    loop
+  }
+  return result
+}
+
+fn fill-in _out: (addr array screen-cell), in: (addr array byte) {
+  var s: (stream byte 0x100)
+  var out/edi: (addr array screen-cell) <- copy _out
+  var s-addr/esi: (addr stream byte) <- address s
+  write s-addr, in
+  var idx/ecx: int <- copy 0
+  {
+    var done?/eax: boolean <- stream-empty? s-addr
+    compare done?, 0/false
+    break-if-!=
+    var g/eax: grapheme <- read-grapheme s-addr
+    compare g, 0xa/newline
+    loop-if-=
+    var offset/edx: (offset screen-cell) <- compute-offset out, idx
+    var dest/edx: (addr screen-cell) <- index out, offset
+    var dest2/edx: (addr grapheme) <- get dest, data
+    copy-to *dest2, g
+    idx <- increment
+    loop
+  }
+}
+
+# pin (1, 1) to (1, 1) on screen
+fn test-surface-pin-at-origin {
+  var s: surface
+  var s-addr/esi: (addr surface) <- address s
+  # surface contents are a fixed grid with 8 rows and 6 columns
+  # (strip vowels second time around to break vertical alignment of letters)
+  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
+  pin-surface-at s-addr, 1, 1  # surface row and column
+  pin-surface-to s-addr, 1, 1  # screen row and column
+  render-surface s-addr
+  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  check-screen-row screen-addr, 1, "abcd", "F - test-surface-pin-at-origin"
+  check-screen-row screen-addr, 2, "ghij", "F - test-surface-pin-at-origin"
+  check-screen-row screen-addr, 3, "mnop", "F - test-surface-pin-at-origin"
+}
+
+# pin (1, 1) to (2, 1) on screen; screen goes past edge of the universe
+fn test-surface-pin-2 {
+  var s: surface
+  var s-addr/esi: (addr surface) <- address s
+  # surface contents are a fixed grid with 8 rows and 6 columns
+  # (strip vowels second time around to break vertical alignment of letters)
+  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
+  pin-surface-at s-addr, 1, 1  # surface row and column
+  pin-surface-to s-addr, 2, 1  # screen row and column
+  render-surface s-addr
+  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  # surface edge reached (should seldom happen in the app)
+  check-screen-row screen-addr, 1, "    ", "F - test-surface-pin-2"
+  check-screen-row screen-addr, 2, "abcd", "F - test-surface-pin-2"
+  check-screen-row screen-addr, 3, "ghij", "F - test-surface-pin-2"
+}
+
+# pin (2, 1) to (1, 1) on screen
+fn test-surface-pin-3 {
+  var s: surface
+  var s-addr/esi: (addr surface) <- address s
+  # surface contents are a fixed grid with 8 rows and 6 columns
+  # (strip vowels second time around to break vertical alignment of letters)
+  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
+  pin-surface-at s-addr, 2, 1  # surface row and column
+  pin-surface-to s-addr, 1, 1  # screen row and column
+  render-surface s-addr
+  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  check-screen-row screen-addr, 1, "ghij", "F - test-surface-pin-3"
+  check-screen-row screen-addr, 2, "mnop", "F - test-surface-pin-3"
+  check-screen-row screen-addr, 3, "stuv", "F - test-surface-pin-3"
+}
+
+# pin (1, 1) to (1, 2) on screen; screen goes past edge of the universe
+fn test-surface-pin-4 {
+  var s: surface
+  var s-addr/esi: (addr surface) <- address s
+  # surface contents are a fixed grid with 8 rows and 6 columns
+  # (strip vowels second time around to break vertical alignment of letters)
+  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
+  pin-surface-at s-addr, 1, 1  # surface row and column
+  pin-surface-to s-addr, 1, 2  # screen row and column
+  render-surface s-addr
+  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  # surface edge reached (should seldom happen in the app)
+  check-screen-row screen-addr, 1, " abc", "F - test-surface-pin-4"
+  check-screen-row screen-addr, 2, " ghi", "F - test-surface-pin-4"
+  check-screen-row screen-addr, 3, " mno", "F - test-surface-pin-4"
+}
+
+# pin (1, 2) to (1, 1) on screen
+fn test-surface-pin-5 {
+  var s: surface
+  var s-addr/esi: (addr surface) <- address s
+  # surface contents are a fixed grid with 8 rows and 6 columns
+  # (strip vowels second time around to break vertical alignment of letters)
+  initialize-surface-with-fake-screen s-addr, 3, 4, "abcdef\nghijkl\nmnopqr\nstuvwx\nyzabcd\nfghjkl\nmnpqrs\ntvwxyz"
+  pin-surface-at s-addr, 1, 2  # surface row and column
+  pin-surface-to s-addr, 1, 1  # screen row and column
+  render-surface s-addr
+  var screen-ah/eax: (addr handle screen) <- get s-addr, screen
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  check-screen-row screen-addr, 1, "bcde", "F - test-surface-pin-5"
+  check-screen-row screen-addr, 2, "hijk", "F - test-surface-pin-5"
+  check-screen-row screen-addr, 3, "nopq", "F - test-surface-pin-5"
+}
+
+fn initialize-surface-with-fake-screen _self: (addr surface), nrows: int, ncols: int, in: (addr array byte) {
+  var self/esi: (addr surface) <- copy _self
+  # fill in screen
+  var screen-ah/eax: (addr handle screen) <- get self, screen
+  allocate screen-ah
+  var screen-addr/eax: (addr screen) <- lookup *screen-ah
+  initialize-screen screen-addr, nrows, ncols
+  # fill in everything else
+  initialize-surface-with self, in
+}