# 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 {
$print-surface-cell-at:body: {
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
break $print-surface-cell-at:body
}
# 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 -> result/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"
result <- 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
}
fn screen-row-to-surface _self: (addr surface), screen-row: int -> result/ecx: int {
var self/esi: (addr surface) <- copy _self
result <- copy screen-row
var tmp/eax: (addr int) <- get self, pin-row
result <- add *tmp
tmp <- get self, pin-screen-row
result <- subtract *tmp
}
fn max a: int, b: int -> result/eax: int {
$max:body: {
var a2/eax: int <- copy a
compare a2, b
{
break-if->
result <- copy b
break $max:body
}
{
break-if-<=
result <- copy a2
}
}
}
fn min a: int, b: int -> result/eax: int {
$min:body: {
var a2/eax: int <- copy a
compare a2, b
{
break-if->
result <- copy a2
break $min:body
}
{
break-if-<=
result <- copy b
}
}
}
fn screen-col-to-surface _self: (addr surface), screen-col: int -> result/edx: int {
var self/esi: (addr surface) <- copy _self
result <- copy screen-col
var tmp/eax: (addr int) <- get self, pin-col
result <- add *tmp
tmp <- get self, pin-screen-col
result <- subtract *tmp
}
fn surface-row-to-screen _self: (addr surface), row: int -> result/ecx: int {
var self/esi: (addr surface) <- copy _self
result <- copy row
var tmp/eax: (addr int) <- get self, pin-screen-row
result <- add *tmp
tmp <- get self, pin-row
result <- subtract *tmp
}
fn surface-col-to-screen _self: (addr surface), col: int -> result/edx: int {
var self/esi: (addr surface) <- copy _self
result <- copy col
var tmp/eax: (addr int) <- get self, pin-screen-col
result <- add *tmp
tmp <- get self, pin-col
result <- subtract *tmp
}
# assumes last line doesn't end in '\n'
fn num-lines in: (addr array byte) -> result/ecx: int {
var s: (stream byte 0x100)
var s-addr/esi: (addr stream byte) <- address s
write s-addr, in
result <- 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
}
}
fn first-line-length in: (addr array byte) -> result/edx: int {
var s: (stream byte 0x100)
var s-addr/esi: (addr stream byte) <- address s
write s-addr, in
result <- 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
}
}
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
}