about summary refs log tree commit diff stats
path: root/archive/2.vm/081print.mu
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-01-01 17:04:37 -0800
committerKartik Agaram <vc@akkartik.com>2020-01-01 17:04:37 -0800
commit2a4088119cf41175457414dfa59bd4064b8f0562 (patch)
tree64fe184e399f9870ebd481a90eec34d51e5dff68 /archive/2.vm/081print.mu
parent23fd294d85959c6b476bcdc35ed6ad508cc99b8f (diff)
downloadmu-2a4088119cf41175457414dfa59bd4064b8f0562.tar.gz
5852
Diffstat (limited to 'archive/2.vm/081print.mu')
-rw-r--r--archive/2.vm/081print.mu914
1 files changed, 0 insertions, 914 deletions
diff --git a/archive/2.vm/081print.mu b/archive/2.vm/081print.mu
deleted file mode 100644
index 98ea0f21..00000000
--- a/archive/2.vm/081print.mu
+++ /dev/null
@@ -1,914 +0,0 @@
-# Wrappers around print primitives that take a 'screen' object and are thus
-# easier to test.
-#
-# Screen objects are intended to exactly mimic the behavior of traditional
-# terminals. Moving a cursor too far right wraps it to the next line,
-# scrolling if necessary. The details are subtle:
-#
-# a) Rows can take unbounded values. When printing, large values for the row
-# saturate to the bottom row (because scrolling).
-#
-# b) If you print to a square (row, right) on the right margin, the cursor
-# position depends on whether 'row' is in range. If it is, the new cursor
-# position is (row+1, 0). If it isn't, the new cursor position is (row, 0).
-# Because scrolling.
-
-container screen [
-  num-rows:num
-  num-columns:num
-  cursor-row:num
-  cursor-column:num
-  data:&:@:screen-cell  # capacity num-rows*num-columns
-  pending-scroll?:bool
-  top-idx:num  # index inside data that corresponds to top-left of screen
-               # modified on scroll, wrapping around to the top of data
-]
-
-container screen-cell [
-  contents:char
-  color:num
-]
-
-def new-fake-screen w:num, h:num -> result:&:screen [
-  local-scope
-  load-inputs
-  result <- new screen:type
-  non-zero-width?:bool <- greater-than w, 0
-  assert non-zero-width?, [screen can't have zero width]
-  non-zero-height?:bool <- greater-than h, 0
-  assert non-zero-height?, [screen can't have zero height]
-  bufsize:num <- multiply w, h
-  data:&:@:screen-cell <- new screen-cell:type, bufsize
-  *result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, false/pending-scroll?, 0/top-idx
-  result <- clear-screen result
-]
-
-def clear-screen screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [clear-screen]
-  {
-    break-if screen
-    # real screen
-    clear-display
-    return
-  }
-  # fake screen
-  buf:&:@:screen-cell <- get *screen, data:offset
-  max:num <- length *buf
-  i:num <- copy 0
-  {
-    done?:bool <- greater-or-equal i, max
-    break-if done?
-    curr:screen-cell <- merge 0/empty, 7/white
-    *buf <- put-index *buf, i, curr
-    i <- add i, 1
-    loop
-  }
-  # reset cursor
-  *screen <- put *screen, cursor-row:offset, 0
-  *screen <- put *screen, cursor-column:offset, 0
-  *screen <- put *screen, top-idx:offset, 0
-]
-
-def fake-screen-is-empty? screen:&:screen -> result:bool [
-  local-scope
-  load-inputs
-#?   stash [fake-screen-is-empty?]
-  return-unless screen, true  # do nothing for real screens
-  buf:&:@:screen-cell <- get *screen, data:offset
-  i:num <- copy 0
-  len:num <- length *buf
-  {
-    done?:bool <- greater-or-equal i, len
-    break-if done?
-    curr:screen-cell <- index *buf, i
-    curr-contents:char <- get curr, contents:offset
-    i <- add i, 1
-    loop-unless curr-contents
-    # not 0
-    return false
-  }
-  return true
-]
-
-def print screen:&:screen, c:char -> screen:&:screen [
-  local-scope
-  load-inputs
-  color:num, color-found?:bool <- next-input
-  {
-    # default color to white
-    break-if color-found?
-    color <- copy 7/white
-  }
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  c2:num <- character-to-code c
-  trace 90, [print-character], c2
-  {
-    # real screen
-    break-if screen
-    print-character-to-display c, color, bg-color
-    return
-  }
-  # fake screen
-  # (handle special cases exactly like in the real screen)
-  width:num <- get *screen, num-columns:offset
-  height:num <- get *screen, num-rows:offset
-  capacity:num <- multiply width, height
-  row:num <- get *screen, cursor-row:offset
-  column:num <- get *screen, cursor-column:offset
-  buf:&:@:screen-cell <- get *screen, data:offset
-  # some potentially slow sanity checks for preconditions {
-  # eliminate fractions from column and row
-  row <- round row
-  column <- round column
-  # if cursor is past left margin (error), reset to left margin
-  {
-    too-far-left?:bool <- lesser-than column, 0
-    break-unless too-far-left?
-    column <- copy 0
-    *screen <- put *screen, cursor-column:offset, column
-  }
-  # if cursor is at or past right margin, wrap
-  {
-    at-right?:bool <- greater-or-equal column, width
-    break-unless at-right?
-    column <- copy 0
-    *screen <- put *screen, cursor-column:offset, column
-    row <- add row, 1
-    *screen <- put *screen, cursor-row:offset, row
-  }
-  # }
-  # if there's a pending scroll, perform it
-  {
-    pending-scroll?:bool <- get *screen, pending-scroll?:offset
-    break-unless pending-scroll?
-#?     stash [scroll]
-    scroll-fake-screen screen
-    *screen <- put *screen, pending-scroll?:offset, false
-  }
-#?     $print [print-character (], row, [, ], column, [): ], c, 10/newline
-  # special-case: newline
-  {
-    newline?:bool <- equal c, 10/newline
-    break-unless newline?
-    cursor-down-on-fake-screen screen  # doesn't modify column
-    return
-  }
-  # special-case: linefeed
-  {
-    linefeed?:bool <- equal c, 13/linefeed
-    break-unless linefeed?
-    *screen <- put *screen, cursor-column:offset, 0
-    return
-  }
-  # special-case: backspace
-  # moves cursor left but does not erase
-  {
-    backspace?:bool <- equal c, 8/backspace
-    break-unless backspace?
-    {
-      break-unless column
-      column <- subtract column, 1
-      *screen <- put *screen, cursor-column:offset, column
-    }
-    return
-  }
-  # save character in fake screen
-  top-idx:num <- get *screen, top-idx:offset
-  index:num <- data-index row, column, width, height, top-idx
-  cursor:screen-cell <- merge c, color
-  *buf <- put-index *buf, index, cursor
-  # move cursor to next character, wrapping as necessary
-  # however, don't scroll just yet
-  column <- add column, 1
-  {
-    past-right?:bool <- greater-or-equal column, width
-    break-unless past-right?
-    column <- copy 0
-    row <- add row, 1
-    past-bottom?:bool <- greater-or-equal row, height
-    break-unless past-bottom?
-    # queue up a scroll
-#?     stash [pending scroll]
-    *screen <- put *screen, pending-scroll?:offset, true
-    row <- subtract row, 1  # update cursor as if scroll already happened
-  }
-  *screen <- put *screen, cursor-row:offset, row
-  *screen <- put *screen, cursor-column:offset, column
-]
-
-def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-down]
-  row:num <- get *screen, cursor-row:offset
-  height:num <- get *screen, num-rows:offset
-  bottom:num <- subtract height, 1
-  at-bottom?:bool <- greater-or-equal row, bottom
-  {
-    break-if at-bottom?
-    row <- add row, 1
-    *screen <- put *screen, cursor-row:offset, row
-  }
-  {
-    break-unless at-bottom?
-    scroll-fake-screen screen  # does not modify row
-  }
-]
-
-def scroll-fake-screen screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [scroll-fake-screen]
-  width:num <- get *screen, num-columns:offset
-  height:num <- get *screen, num-rows:offset
-  buf:&:@:screen-cell <- get *screen, data:offset
-  # clear top line and 'rotate' it to the bottom
-  top-idx:num <- get *screen, top-idx:offset  # 0 <= top-idx < len(buf)
-  next-top-idx:num <- add top-idx, width  # 0 <= next-top-idx <= len(buf)
-  empty-cell:screen-cell <- merge 0/empty, 7/white
-  {
-    done?:bool <- greater-or-equal top-idx, next-top-idx
-    break-if done?
-    put-index *buf, top-idx, empty-cell
-    top-idx <- add top-idx, 1
-    # no modulo; top-idx is always a multiple of width,
-    # so it can never wrap around inside this loop
-    loop
-  }
-  # top-idx now same as next-top-idx; wrap around if necessary
-  capacity:num <- multiply width, height
-  _, top-idx <- divide-with-remainder, top-idx, capacity
-  *screen <- put *screen, top-idx:offset, top-idx
-]
-
-# translate from screen (row, column) coordinates to an index into data
-# while accounting for scrolling (sliding top-idx)
-def data-index row:num, column:num, width:num, height:num, top-idx:num -> result:num [
-  local-scope
-  load-inputs
-  {
-    overflow?:bool <- greater-or-equal row, height
-    break-unless overflow?
-    row <- subtract height, 1
-  }
-  result <- multiply width, row
-  result <- add result, column, top-idx
-  capacity:num <- multiply width, height
-  _, result <- divide-with-remainder result, capacity
-]
-
-scenario print-character-at-top-left [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  run [
-    a:char <- copy 97/a
-    fake-screen <- print fake-screen, a:char
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    1:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    1 <- 6  # width*height
-    2 <- 97  # 'a'
-    3 <- 7  # white
-    # rest of screen is empty
-    4 <- 0
-  ]
-]
-
-scenario print-character-at-fractional-coordinate [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  a:char <- copy 97/a
-  run [
-    move-cursor fake-screen, 0.5, 0
-    fake-screen <- print fake-screen, a:char
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    1:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    1 <- 6  # width*height
-    2 <- 97  # 'a'
-    3 <- 7  # white
-    # rest of screen is empty
-    4 <- 0
-  ]
-]
-
-scenario print-character-in-color [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  run [
-    a:char <- copy 97/a
-    fake-screen <- print fake-screen, a:char, 1/red
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    1:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    1 <- 6  # width*height
-    2 <- 97  # 'a'
-    3 <- 1  # red
-    # rest of screen is empty
-    4 <- 0
-  ]
-]
-
-scenario print-backspace-character [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  run [
-    backspace:char <- copy 8/backspace
-    fake-screen <- print fake-screen, backspace
-    10:num/raw <- get *fake-screen, cursor-column:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    11:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    10 <- 0  # cursor column
-    11 <- 6  # width*height
-    12 <- 97  # still 'a'
-    13 <- 7  # white
-    # rest of screen is empty
-    14 <- 0
-  ]
-]
-
-scenario print-extra-backspace-character [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  run [
-    backspace:char <- copy 8/backspace
-    fake-screen <- print fake-screen, backspace
-    fake-screen <- print fake-screen, backspace  # cursor already at left margin
-    1:num/raw <- get *fake-screen, cursor-column:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    3:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    1 <- 0  # cursor column
-    3 <- 6  # width*height
-    4 <- 97  # still 'a'
-    5 <- 7  # white
-    # rest of screen is empty
-    6 <- 0
-  ]
-]
-
-scenario print-character-at-right-margin [
-  # fill top row of screen with text
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 2/width, 2/height
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  b:char <- copy 98/b
-  fake-screen <- print fake-screen, b
-  run [
-    # cursor now at next row
-    c:char <- copy 99/c
-    fake-screen <- print fake-screen, c
-    10:num/raw <- get *fake-screen, cursor-row:offset
-    11:num/raw <- get *fake-screen, cursor-column:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    12:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    10 <- 1  # cursor row
-    11 <- 1  # cursor column
-    12 <- 4  # width*height
-    13 <- 97  # 'a'
-    14 <- 7  # white
-    15 <- 98  # 'b'
-    16 <- 7  # white
-    17 <- 99  # 'c'
-    18 <- 7  # white
-    19 <- 0  # ' '
-    20 <- 7  # white
-  ]
-]
-
-scenario print-newline-character [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  run [
-    newline:char <- copy 10/newline
-    fake-screen <- print fake-screen, newline
-    10:num/raw <- get *fake-screen, cursor-row:offset
-    11:num/raw <- get *fake-screen, cursor-column:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    12:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    10 <- 1  # cursor row
-    11 <- 1  # cursor column
-    12 <- 6  # width*height
-    13 <- 97  # 'a'
-    14 <- 7  # white
-    # rest of screen is empty
-    15 <- 0
-  ]
-]
-
-scenario print-newline-at-bottom-line [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  newline:char <- copy 10/newline
-  fake-screen <- print fake-screen, newline
-  fake-screen <- print fake-screen, newline
-  run [
-    # cursor now at bottom of screen
-    fake-screen <- print fake-screen, newline
-    10:num/raw <- get *fake-screen, cursor-row:offset
-    11:num/raw <- get *fake-screen, cursor-column:offset
-  ]
-  # doesn't move further down
-  memory-should-contain [
-    10 <- 1  # cursor row
-    11 <- 0  # cursor column
-  ]
-]
-
-scenario print-character-at-bottom-right [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 2/width, 2/height
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  b:char <- copy 98/b
-  fake-screen <- print fake-screen, b
-  c:char <- copy 99/c
-  fake-screen <- print fake-screen, c
-  run [
-    # cursor now at bottom right
-    d:char <- copy 100/d
-    fake-screen <- print fake-screen, d
-    10:num/raw <- get *fake-screen, cursor-row:offset
-    11:num/raw <- get *fake-screen, cursor-column:offset
-    12:num/raw <- get *fake-screen, top-idx:offset
-    13:bool/raw <- get *fake-screen, pending-scroll?:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    20:@:screen-cell/raw <- copy *cell
-  ]
-  # cursor column wraps but the screen doesn't scroll yet
-  memory-should-contain [
-    10 <- 1  # cursor row
-    11 <- 0  # cursor column -- outside screen
-    12 <- 0  # top-idx -- not yet scrolled
-    13 <- 1  # pending-scroll?
-    20 <- 4  # screen size (width*height)
-    21 <- 97  # 'a'
-    22 <- 7  # white
-    23 <- 98  # 'b'
-    24 <- 7  # white
-    25 <- 99 # 'c'
-    26 <- 7  # white
-    27 <- 100  # 'd'
-    28 <- 7  # white
-  ]
-  run [
-    e:char <- copy 101/e
-    print fake-screen, e
-    10:num/raw <- get *fake-screen, cursor-row:offset
-    11:num/raw <- get *fake-screen, cursor-column:offset
-    12:num/raw <- get *fake-screen, top-idx:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    20:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    # text scrolls by 1, we lose the top line
-    10 <- 1  # cursor row
-    11 <- 1  # cursor column -- wrapped
-    12 <- 2  # top-idx -- scrolled
-    20 <- 4  # screen size (width*height)
-    # screen now checked in rotated order
-    25 <- 99 # 'c'
-    26 <- 7  # white
-    27 <- 100  # 'd'
-    28 <- 7  # white
-    # screen wraps; bottom line is cleared of old contents
-    21 <- 101  # 'e'
-    22 <- 7  # white
-    23 <- 0  # unused
-    24 <- 7  # white
-  ]
-]
-
-# even though our screen supports scrolling, some apps may want to avoid
-# scrolling
-# these helpers help check for scrolling at development time
-def save-top-idx screen:&:screen -> result:num [
-  local-scope
-  load-inputs
-  return-unless screen, 0  # check is only for fake screens
-  result <- get *screen, top-idx:offset
-]
-def assert-no-scroll screen:&:screen, old-top-idx:num [
-  local-scope
-  load-inputs
-  return-unless screen
-  new-top-idx:num <- get *screen, top-idx:offset
-  no-scroll?:bool <- equal old-top-idx, new-top-idx
-  assert no-scroll?, [render should never use screen's scrolling capabilities]
-]
-
-def clear-line screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [clear-line]
-  space:char <- copy 0/nul
-  {
-    break-if screen
-    # real screen
-    clear-line-on-display
-    return
-  }
-  # fake screen
-  width:num <- get *screen, num-columns:offset
-  column:num <- get *screen, cursor-column:offset
-  original-column:num <- copy column
-  # space over the entire line
-  {
-    right:num <- subtract width, 1
-    done?:bool <- greater-or-equal column, right
-    break-if done?
-    print screen, space
-    column <- add column, 1
-    loop
-  }
-  # now back to where the cursor was
-  *screen <- put *screen, cursor-column:offset, original-column
-]
-
-# only for non-scrolling apps
-def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [
-  local-scope
-  load-inputs
-  row:num, column:num <- cursor-position screen
-#?   stash [clear-line-until] row column
-  height:num <- screen-height screen
-  past-bottom?:bool <- greater-or-equal row, height
-  return-if past-bottom?
-  space:char <- copy 32/space
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  {
-    done?:bool <- greater-than column, right
-    break-if done?
-    screen <- print screen, space, 7/white, bg-color  # foreground color is mostly unused except if the cursor shows up at this cell
-    column <- add column, 1
-    loop
-  }
-]
-
-def cursor-position screen:&:screen -> row:num, column:num [
-  local-scope
-  load-inputs
-  {
-    break-if screen
-    # real screen
-    row, column <- cursor-position-on-display
-    return
-  }
-  # fake screen
-  row:num <- get *screen, cursor-row:offset
-  column:num <- get *screen, cursor-column:offset
-]
-
-def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [move-cursor] new-row new-column
-  {
-    break-if screen
-    # real screen
-    move-cursor-on-display new-row, new-column
-    return
-  }
-  # fake screen
-  *screen <- put *screen, cursor-row:offset, new-row
-  *screen <- put *screen, cursor-column:offset, new-column
-  # if cursor column is within bounds, reset 'pending-scroll?'
-  {
-    width:num <- get *screen, num-columns:offset
-    scroll?:bool <- greater-or-equal new-column, width
-    break-if scroll?
-#?     stash [resetting pending-scroll?]
-    *screen <- put *screen, pending-scroll?:offset, false
-  }
-]
-
-scenario clear-line-erases-printed-characters [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  # print a character
-  a:char <- copy 97/a
-  fake-screen <- print fake-screen, a
-  # move cursor to start of line
-  fake-screen <- move-cursor fake-screen, 0/row, 0/column
-  run [
-    fake-screen <- clear-line fake-screen
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    10:@:screen-cell/raw <- copy *cell
-  ]
-  # screen should be blank
-  memory-should-contain [
-    10 <- 6  # width*height
-    11 <- 0
-    12 <- 7
-    13 <- 0
-    14 <- 7
-    15 <- 0
-    16 <- 7
-    17 <- 0
-    18 <- 7
-    19 <- 0
-    20 <- 7
-    21 <- 0
-    22 <- 7
-  ]
-]
-
-def cursor-down screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-down]
-  {
-    break-if screen
-    # real screen
-    move-cursor-down-on-display
-    return
-  }
-  # fake screen
-  cursor-down-on-fake-screen screen
-]
-
-scenario cursor-down-scrolls [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  # print something to screen and scroll
-  run [
-    print fake-screen, [abc]
-    cursor-to-next-line fake-screen
-    cursor-to-next-line fake-screen
-    data:&:@:screen-cell <- get *fake-screen, data:offset
-    10:@:screen-cell/raw <- copy *data
-  ]
-  # screen is now blank
-  memory-should-contain [
-    10 <- 6  # width*height
-    11 <- 0
-    12 <- 7  # white
-    13 <- 0
-    14 <- 7  # white
-    15 <- 0
-    16 <- 7  # white
-    17 <- 0
-    18 <- 7  # white
-    19 <- 0
-    20 <- 7  # white
-    21 <- 0
-    22 <- 7  # white
-  ]
-]
-
-def cursor-up screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-up]
-  {
-    break-if screen
-    # real screen
-    move-cursor-up-on-display
-    return
-  }
-  # fake screen
-  row:num <- get *screen, cursor-row:offset
-  at-top?:bool <- lesser-or-equal row, 0
-  return-if at-top?
-  row <- subtract row, 1
-  *screen <- put *screen, cursor-row:offset, row
-]
-
-def cursor-right screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-right]
-  {
-    break-if screen
-    # real screen
-    move-cursor-right-on-display
-    return
-  }
-  # fake screen
-  width:num <- get *screen, num-columns:offset
-  column:num <- get *screen, cursor-column:offset
-  max:num <- subtract width, 1
-  at-bottom?:bool <- greater-or-equal column, max
-  return-if at-bottom?
-  column <- add column, 1
-  *screen <- put *screen, cursor-column:offset, column
-]
-
-def cursor-left screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-left]
-  {
-    break-if screen
-    # real screen
-    move-cursor-left-on-display
-    return
-  }
-  # fake screen
-  column:num <- get *screen, cursor-column:offset
-  at-top?:bool <- lesser-or-equal column, 0
-  return-if at-top?
-  column <- subtract column, 1
-  *screen <- put *screen, cursor-column:offset, column
-]
-
-def cursor-to-start-of-line screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-to-start-of-line]
-  row:num <- cursor-position screen
-  screen <- move-cursor screen, row, 0/column
-]
-
-def cursor-to-next-line screen:&:screen -> screen:&:screen [
-  local-scope
-  load-inputs
-#?   stash [cursor-to-next-line]
-  screen <- cursor-down screen
-  screen <- cursor-to-start-of-line screen
-]
-
-def move-cursor-to-column screen:&:screen, column:num -> screen:&:screen [
-  local-scope
-  load-inputs
-  row:num, _ <- cursor-position screen
-#?   stash [move-cursor-to-column] row
-  move-cursor screen, row, column
-]
-
-def screen-width screen:&:screen -> width:num [
-  local-scope
-  load-inputs
-#?   stash [screen-width]
-  {
-    break-unless screen
-    # fake screen
-    width <- get *screen, num-columns:offset
-    return
-  }
-  # real screen
-  width <- display-width
-]
-
-def screen-height screen:&:screen -> height:num [
-  local-scope
-  load-inputs
-#?   stash [screen-height]
-  {
-    break-unless screen
-    # fake screen
-    height <- get *screen, num-rows:offset
-    return
-  }
-  # real screen
-  height <- display-height
-]
-
-def print screen:&:screen, s:text -> screen:&:screen [
-  local-scope
-  load-inputs
-  color:num, color-found?:bool <- next-input
-  {
-    # default color to white
-    break-if color-found?
-    color <- copy 7/white
-  }
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  len:num <- length *s
-  i:num <- copy 0
-  {
-    done?:bool <- greater-or-equal i, len
-    break-if done?
-    c:char <- index *s, i
-    print screen, c, color, bg-color
-    i <- add i, 1
-    loop
-  }
-]
-
-scenario print-text-wraps-past-right-margin [
-  local-scope
-  fake-screen:&:screen <- new-fake-screen 3/width, 2/height
-  run [
-    fake-screen <- print fake-screen, [abcd]
-    5:num/raw <- get *fake-screen, cursor-row:offset
-    6:num/raw <- get *fake-screen, cursor-column:offset
-    7:num/raw <- get *fake-screen, top-idx:offset
-    cell:&:@:screen-cell <- get *fake-screen, data:offset
-    10:@:screen-cell/raw <- copy *cell
-  ]
-  memory-should-contain [
-    5 <- 1  # cursor-row
-    6 <- 1  # cursor-column
-    7 <- 0  # top-idx
-    10 <- 6  # width*height
-    11 <- 97  # 'a'
-    12 <- 7  # white
-    13 <- 98  # 'b'
-    14 <- 7  # white
-    15 <- 99  # 'c'
-    16 <- 7  # white
-    17 <- 100  # 'd'
-    18 <- 7  # white
-    # rest of screen is empty
-    19 <- 0
-  ]
-]
-
-def print screen:&:screen, n:num -> screen:&:screen [
-  local-scope
-  load-inputs
-  color:num, color-found?:bool <- next-input
-  {
-    # default color to white
-    break-if color-found?
-    color <- copy 7/white
-  }
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  # todo: other bases besides decimal
-  s:text <- to-text n
-  screen <- print screen, s, color, bg-color
-]
-
-def print screen:&:screen, n:bool -> screen:&:screen [
-  local-scope
-  load-inputs
-  color:num, color-found?:bool <- next-input
-  {
-    # default color to white
-    break-if color-found?
-    color <- copy 7/white
-  }
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  {
-    break-if n
-    screen <- print screen, [false], color, bg-color
-  }
-  {
-    break-unless n
-    screen <- print screen, [true], color, bg-color
-  }
-]
-
-def print screen:&:screen, n:&:_elem -> screen:&:screen [
-  local-scope
-  load-inputs
-  color:num, color-found?:bool <- next-input
-  {
-    # default color to white
-    break-if color-found?
-    color <- copy 7/white
-  }
-  bg-color:num, bg-color-found?:bool <- next-input
-  {
-    # default bg-color to black
-    break-if bg-color-found?
-    bg-color <- copy 0/black
-  }
-  n2:num <- deaddress n
-  screen <- print screen, n2, color, bg-color
-]