mu/405screen.mu

473 lines
12 KiB
Forth
Raw Normal View History

# Wrappers for real screen primitives that can be passed a fake screen.
# There are no tests here, but commented scenarios are painstakingly validated
# against a real terminal emulator. I believe functionality here is broadly
# portable across terminal emulators.
#
# Remember: fake screen co-ordinates are 1-based, just like in real terminal
# emulators.
type screen {
num-rows: int
num-cols: int
data: (handle array screen-cell)
top-index: int
cursor-row: int
cursor-col: int
cursor-hide?: boolean
curr-attributes: screen-cell
}
type screen-cell {
data: grapheme
color: int
background-color: int
bold?: boolean
underline?: boolean
reverse?: boolean
blink?: boolean
}
fn initialize-screen screen: (addr screen), nrows: int, ncols: int {
var screen-addr/esi: (addr screen) <- copy screen
var tmp/eax: int <- copy 0
var dest/edi: (addr int) <- copy 0
# screen->num-rows = nrows
dest <- get screen-addr, num-rows
tmp <- copy nrows
copy-to *dest, tmp
# screen->num-cols = ncols
dest <- get screen-addr, num-cols
tmp <- copy ncols
copy-to *dest, tmp
# screen->data = new screen-cell[nrows*ncols]
{
var data-addr/edi: (addr handle array screen-cell) <- get screen-addr, data
tmp <- multiply nrows
populate data-addr, tmp
}
# screen->cursor-row = 1
dest <- get screen-addr, cursor-row
copy-to *dest, 1
# screen->cursor-col = 1
dest <- get screen-addr, cursor-col
copy-to *dest, 1
# screen->curr-attributes->background-color = 7 (simulate light background)
var tmp2/eax: (addr screen-cell) <- get screen-addr, curr-attributes
dest <- get tmp2, background-color
copy-to *dest, 7
}
fn screen-size screen: (addr screen) -> nrows/eax: int, ncols/ecx: int {
$screen-size:body: {
compare screen, 0
{
break-if-!=
nrows, ncols <- real-screen-size
break $screen-size:body
}
{
break-if-=
# fake screen
var screen-addr/esi: (addr screen) <- copy screen
var tmp/edx: (addr int) <- get screen-addr, num-rows
nrows <- copy *tmp
tmp <- get screen-addr, num-cols
ncols <- copy *tmp
}
}
}
fn clear-screen screen: (addr screen) {
$clear-screen:body: {
compare screen, 0
{
break-if-!=
clear-real-screen
break $clear-screen:body
}
{
break-if-=
# fake screen
var space/edi: grapheme <- copy 0x20
move-cursor screen, 1, 1
var screen-addr/esi: (addr screen) <- copy screen
var i/eax: int <- copy 1
var nrows/ecx: (addr int) <- get screen-addr, num-rows
{
compare i, *nrows
break-if->
var j/edx: int <- copy 1
var ncols/ebx: (addr int) <- get screen-addr, num-cols
{
compare j, *ncols
break-if->
print-grapheme screen, space
j <- increment
loop
}
i <- increment
loop
}
move-cursor screen, 1, 1
}
}
}
fn move-cursor screen: (addr screen), row: int, column: int {
$move-cursor:body: {
compare screen, 0
{
break-if-!=
move-cursor-on-real-screen row, column
break $move-cursor:body
}
{
break-if-=
# fake screen
2020-08-03 04:19:16 +00:00
var screen-addr/esi: (addr screen) <- copy screen
# row < 0 is ignored
{
compare row, 0
break-if-< $move-cursor:body
}
# row = 0 is treated same as 1
{
compare row, 0
break-if-!=
copy-to row, 1
}
# row > num-rows saturates to num-rows
{
var nrows-addr/eax: (addr int) <- get screen-addr, num-rows
var nrows/eax: int <- copy *nrows-addr
compare row, nrows
break-if-<=
copy-to row, nrows
}
# column < 0 is ignored
{
compare column, 0
break-if-< $move-cursor:body
}
# column = 0 is treated same as 1
{
compare column, 0
break-if-!=
copy-to column, 1
}
# column > num-cols saturates to num-cols+1 (so wrapping to next row)
{
var ncols-addr/eax: (addr int) <- get screen-addr, num-cols
var ncols/eax: int <- copy *ncols-addr
compare column, ncols
break-if-<=
copy-to column, ncols
increment column
}
2020-08-03 04:19:16 +00:00
# screen->cursor-row = row
var dest/edi: (addr int) <- get screen-addr, cursor-row
var src/eax: int <- copy row
copy-to *dest, src
# screen->cursor-col = column
dest <- get screen-addr, cursor-col
src <- copy column
copy-to *dest, src
}
}
}
fn print-string screen: (addr screen), s: (addr array byte) {
$print-string:body: {
compare screen, 0
{
break-if-!=
print-string-to-real-screen s
break $print-string:body
}
{
break-if-=
# fake screen
}
}
}
fn print-grapheme screen: (addr screen), c: grapheme {
$print-grapheme:body: {
compare screen, 0
{
break-if-!=
print-grapheme-to-real-screen c
break $print-grapheme:body
}
{
break-if-=
# fake screen
var screen-addr/esi: (addr screen) <- copy screen
var idx/ecx: int <- current-screen-cell-index screen-addr
var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, data
var data/eax: (addr array screen-cell) <- lookup *data-ah
var offset/ecx: (offset screen-cell) <- compute-offset data, idx
var cell/eax: (addr screen-cell) <- index data, offset
var dest/eax: (addr grapheme) <- get cell, data
var c2/ecx: grapheme <- copy c
copy-to *dest, c2
}
}
}
fn current-screen-cell-index screen-on-stack: (addr screen) -> result/ecx: int {
2020-08-29 04:57:06 +00:00
var screen/esi: (addr screen) <- copy screen-on-stack
var cursor-row-addr/ecx: (addr int) <- get screen, cursor-row
var cursor-col-addr/eax: (addr int) <- get screen, cursor-col
result <- screen-cell-index screen, *cursor-row-addr, *cursor-col-addr
}
fn screen-cell-index screen-on-stack: (addr screen), row: int, col: int -> result/ecx: int {
var screen/esi: (addr screen) <- copy screen-on-stack
var num-cols-addr/eax: (addr int) <- get screen, num-cols
var num-cols/eax: int <- copy *num-cols-addr
2020-08-29 04:57:06 +00:00
result <- copy row
result <- subtract 1
result <- multiply num-cols
2020-08-29 04:57:06 +00:00
result <- add col
result <- subtract 1
}
fn screen-grapheme-at screen-on-stack: (addr screen), row: int, col: int -> result/eax: grapheme {
var screen-addr/esi: (addr screen) <- copy screen-on-stack
var idx/ecx: int <- screen-cell-index screen-addr, row, col
result <- screen-grapheme-at-idx screen-addr, idx
}
fn screen-grapheme-at-idx screen-on-stack: (addr screen), idx-on-stack: int -> result/eax: grapheme {
var screen-addr/esi: (addr screen) <- copy screen-on-stack
var data-ah/eax: (addr handle array screen-cell) <- get screen-addr, 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
result <- copy *src
}
fn print-code-point screen: (addr screen), c: code-point {
var g/eax: grapheme <- to-grapheme c
print-grapheme screen, g
}
fn print-int32-hex screen: (addr screen), n: int {
$print-int32-hex:body: {
compare screen, 0
{
break-if-!=
print-int32-hex-to-real-screen n
break $print-int32-hex:body
}
{
break-if-=
# fake screen
}
}
}
fn reset-formatting screen: (addr screen) {
$reset-formatting:body: {
compare screen, 0
{
break-if-!=
reset-formatting-on-real-screen
break $reset-formatting:body
}
{
break-if-=
# fake screen
}
}
}
fn start-color screen: (addr screen), fg: int, bg: int {
$start-color:body: {
compare screen, 0
{
break-if-!=
start-color-on-real-screen fg, bg
break $start-color:body
}
{
break-if-=
# fake screen
}
}
}
fn start-bold screen: (addr screen) {
$start-bold:body: {
compare screen, 0
{
break-if-!=
start-bold-on-real-screen
break $start-bold:body
}
{
break-if-=
# fake screen
}
}
}
fn start-underline screen: (addr screen) {
$start-underline:body: {
compare screen, 0
{
break-if-!=
start-underline-on-real-screen
break $start-underline:body
}
{
break-if-=
# fake screen
}
}
}
fn start-reverse-video screen: (addr screen) {
$start-reverse-video:body: {
compare screen, 0
{
break-if-!=
start-reverse-video-on-real-screen
break $start-reverse-video:body
}
{
break-if-=
# fake screen
}
}
}
fn start-blinking screen: (addr screen) {
$start-blinking:body: {
compare screen, 0
{
break-if-!=
start-blinking-on-real-screen
break $start-blinking:body
}
{
break-if-=
# fake screen
}
}
}
fn hide-cursor screen: (addr screen) {
$hide-cursor:body: {
compare screen, 0
{
break-if-!=
hide-cursor-on-real-screen
break $hide-cursor:body
}
{
break-if-=
# fake screen
}
}
}
fn show-cursor screen: (addr screen) {
$show-cursor:body: {
compare screen, 0
{
break-if-!=
show-cursor-on-real-screen
break $show-cursor:body
}
{
break-if-=
# fake screen
}
}
}
# validate data on screen regardless of attributes (color, bold, etc.)
# Mu doesn't have multi-line strings, so we provide functions for rows or portions of rows.
fn check-screen-row screen-on-stack: (addr screen), row-idx: int, expected: (addr array byte), msg: (addr array byte) {
2020-08-29 04:57:06 +00:00
var screen/esi: (addr screen) <- copy screen-on-stack
var idx/ecx: int <- screen-cell-index screen, row-idx, 1
# compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme
var e: (stream byte 0x100)
var e-addr/edx: (addr stream byte) <- address e
write e-addr, expected
{
var done?/eax: boolean <- stream-empty? e-addr
compare done?, 0
break-if-!=
var g/eax: grapheme <- screen-grapheme-at-idx screen, idx
var g2/ebx: int <- copy g
var expected-grapheme/eax: grapheme <- read-grapheme e-addr
var expected-grapheme2/eax: int <- copy expected-grapheme
check-ints-equal g2, expected-grapheme2, msg
idx <- increment
loop
}
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte) {
}
# various variants by screen-cell attribute; spaces in the 'expected' data should not match the attribute
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-color screen-on-stack: (addr screen), fg: color, row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-color-from screen-on-stack: (addr screen), fg: color, row-idx: int, col-idx: int, expected: (addr array byte) {
}
# background color is visible even for spaces, so 'expected' behaves as an array of booleans.
# non-space = given background must match; space = background must not match
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-background-color screen-on-stack: (addr screen), fg: color, row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-background-color-from screen-on-stack: (addr screen), fg: color, row-idx: int, col-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-bold screen-on-stack: (addr screen), row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-bold-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-underline screen-on-stack: (addr screen), row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-underline-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-reverse screen-on-stack: (addr screen), row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-reverse-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-blinking screen-on-stack: (addr screen), row-idx: int, expected: (addr array byte) {
}
2020-08-29 04:57:06 +00:00
fn check-screen-row-in-blinking-from screen-on-stack: (addr screen), row-idx: int, col-idx: int, expected: (addr array byte) {
}
fn test-print-single-grapheme {
var screen-on-stack: screen
var screen/esi: (addr screen) <- address screen-on-stack
initialize-screen screen, 5, 4
var c/eax: grapheme <- copy 0x61 # 'a'
print-grapheme screen, c
check-screen-row screen, 1, "a", "F - test-print-single-grapheme" # top-left corner of the screen
}
#? fn main -> exit-status/ebx: int {
#? test-print-single-grapheme
#? exit-status <- copy 0
#? }