shell: pixel graphics

This commit is contained in:
Kartik K. Agaram 2021-04-13 22:27:59 -07:00
parent 5a3f9a3185
commit bbabe8bd1a
3 changed files with 194 additions and 26 deletions

View File

@ -1,17 +1,25 @@
# Testable primitives for writing text to screen.
# (Mu doesn't yet have testable primitives for graphics.)
# Testable primitives for writing to screen.
#
# Unlike the top-level, this text mode has no scrolling.
# 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.
# coordinates here don't match top-level
# Here we're consistent with graphics mode. Top-level is consistent with
# terminal emulators.
type screen {
# text mode
width: int
height: int
data: (handle array screen-cell)
cursor-x: int
cursor-y: int
cursor-x: int # [0..width)
cursor-y: int # [0..height)
# pixel graphics
pixels: (handle stream pixel) # sparse representation
}
type screen-cell {
@ -20,6 +28,12 @@ type screen-cell {
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
@ -38,6 +52,9 @@ fn initialize-screen _screen: (addr screen), width: int, height: int {
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
@ -64,6 +81,33 @@ fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
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
@ -219,6 +263,9 @@ fn clear-screen _screen: (addr screen) {
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 {
@ -247,7 +294,10 @@ fn fake-screen-empty? _screen: (addr screen) -> _/eax: boolean {
y <- increment
loop
}
return 1/true
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 {

View File

@ -26,6 +26,7 @@ fn initialize-globals _self: (addr global-table) {
append-primitive self, "cons"
# for screens
append-primitive self, "print"
append-primitive self, "pixel"
# for keyboards
append-primitive self, "key"
# for streams
@ -302,6 +303,13 @@ fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr hand
apply-print args-ah, out, trace
return
}
{
var is-pixel?/eax: boolean <- string-equal? f-name, "pixel"
compare is-pixel?, 0/false
break-if-=
apply-pixel args-ah, out, trace
return
}
{
var wait-for-key?/eax: boolean <- string-equal? f-name, "key"
compare wait-for-key?, 0/false
@ -686,6 +694,84 @@ fn apply-print _args-ah: (addr handle cell), out: (addr handle cell), trace: (ad
copy-object second-ah, out
}
fn apply-pixel _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
trace-text trace, "eval", "apply pixel"
var args-ah/eax: (addr handle cell) <- copy _args-ah
var _args/eax: (addr cell) <- lookup *args-ah
var args/esi: (addr cell) <- copy _args
# TODO: check that args is a pair
var empty-args?/eax: boolean <- nil? args
compare empty-args?, 0/false
{
break-if-=
error trace, "pixel needs 4 args but got 0"
return
}
# screen = args->left
var first-ah/eax: (addr handle cell) <- get args, left
var first/eax: (addr cell) <- lookup *first-ah
var first-type/ecx: (addr int) <- get first, type
compare *first-type, 5/screen
{
break-if-=
error trace, "first arg for 'pixel' is not a screen"
return
}
var screen-ah/eax: (addr handle screen) <- get first, screen-data
var _screen/eax: (addr screen) <- lookup *screen-ah
var screen/edi: (addr screen) <- copy _screen
# x = args->right->left->value
var rest-ah/eax: (addr handle cell) <- get args, right
var _rest/eax: (addr cell) <- lookup *rest-ah
var rest/esi: (addr cell) <- copy _rest
# TODO: check that rest is a pair
var second-ah/eax: (addr handle cell) <- get rest, left
var second/eax: (addr cell) <- lookup *second-ah
var second-type/ecx: (addr int) <- get second, type
compare *second-type, 1/number
{
break-if-=
error trace, "second arg for 'pixel' is not an int (x coordinate)"
return
}
var second-value/eax: (addr float) <- get second, number-data
var x/edx: int <- convert *second-value
# y = rest->right->left->value
var rest-ah/eax: (addr handle cell) <- get rest, right
var _rest/eax: (addr cell) <- lookup *rest-ah
rest <- copy _rest
# TODO: check that rest is a pair
var third-ah/eax: (addr handle cell) <- get rest, left
var third/eax: (addr cell) <- lookup *third-ah
var third-type/ecx: (addr int) <- get third, type
compare *third-type, 1/number
{
break-if-=
error trace, "third arg for 'pixel' is not an int (y coordinate)"
return
}
var third-value/eax: (addr float) <- get third, number-data
var y/ebx: int <- convert *third-value
# color = rest->right->left->value
var rest-ah/eax: (addr handle cell) <- get rest, right
var _rest/eax: (addr cell) <- lookup *rest-ah
rest <- copy _rest
# TODO: check that rest is a pair
var fourth-ah/eax: (addr handle cell) <- get rest, left
var fourth/eax: (addr cell) <- lookup *fourth-ah
var fourth-type/ecx: (addr int) <- get fourth, type
compare *fourth-type, 1/number
{
break-if-=
error trace, "fourth arg for 'pixel' is not an int (color; 0..0xff)"
return
}
var fourth-value/eax: (addr float) <- get fourth, number-data
var color/eax: int <- convert *fourth-value
pixel screen, x, y, color
# return nothing
}
fn apply-wait-for-key _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
trace-text trace, "eval", "apply key"
var args-ah/eax: (addr handle cell) <- copy _args-ah

View File

@ -250,29 +250,61 @@ fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int
}
screen-y <- increment
}
# screen
var height/edx: (addr int) <- get target-screen, height
var y/ecx: int <- copy 0
# text data
{
compare y, *height
break-if->=
set-cursor-position screen, xmin, screen-y
draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
move-cursor-right screen
var width/edx: (addr int) <- get target-screen, width
var x/ebx: int <- copy 0
var height/edx: (addr int) <- get target-screen, height
var y/ecx: int <- copy 0
{
compare x, *width
compare y, *height
break-if->=
print-screen-cell-of-fake-screen screen, target-screen, x, y
set-cursor-position screen, xmin, screen-y
draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
move-cursor-right screen
x <- increment
var width/edx: (addr int) <- get target-screen, width
var x/ebx: int <- copy 0
{
compare x, *width
break-if->=
print-screen-cell-of-fake-screen screen, target-screen, x, y
move-cursor-right screen
x <- increment
loop
}
draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
y <- increment
screen-y <- increment
loop
}
}
# pixel data
{
var left/ebx: int <- copy xmin
left <- add 1/margin-left
left <- shift-left 3/log-font-width
var top/edx: int <- copy ymin
top <- add 1/margin-top
top <- shift-left 4/log-font-height
var pixels-ah/esi: (addr handle stream pixel) <- get target-screen, pixels
var _pixels/eax: (addr stream pixel) <- lookup *pixels-ah
var pixels/esi: (addr stream pixel) <- copy _pixels
rewind-stream pixels
{
var done?/eax: boolean <- stream-empty? pixels
compare done?, 0/false
break-if-!=
var curr-pixel: pixel
var curr-pixel-addr/eax: (addr pixel) <- address curr-pixel
read-from-stream pixels, curr-pixel-addr
var curr-x/eax: (addr int) <- get curr-pixel, x
var x/eax: int <- copy *curr-x
x <- add left
var curr-y/ecx: (addr int) <- get curr-pixel, y
var y/ecx: int <- copy *curr-y
y <- add top
var curr-color/edx: (addr int) <- get curr-pixel, color
pixel screen, x, y, *curr-color
loop
}
draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
y <- increment
screen-y <- increment
loop
}
# bottom border
{