commit 042c35e0db71d58e1d88aabb1224420474700693
parent b546fde88a1543b2a298157a61a427066cf55c23
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Tue, 20 Dec 2022 12:41:00 -0500
emul/uxn: controller!
You can try it with "f<< tests/manual/uxn/ctrl.fs" I've changed the Home button
to Right Shift, but otherwise everything works.
I've also added uxn docs at doc/uxn
Diffstat:
11 files changed, 405 insertions(+), 131 deletions(-)
diff --git a/fs/doc/ps2.txt b/fs/doc/ps2.txt
@@ -20,6 +20,11 @@ This subsystem only supports PS/2 Set 1 for now and provides these words:
ps2@? ( -- keycode? f )
Calls (ps2@?) and yields the result as-is, but also update the ps2mods flags.
+ps2kcset1>ascii ( kc -- is-released c )
+ Given a keycode from Set 1 "kc", yield the corresponding ASCII character.
+ "is-released" is a flag being 1 if "kc" indicates that the key has been
+ released and 0 if it indicates that the key has been pressed.
+
ps2keyset1 ( -- c )
Blocks until a key is released (which implies it has been pressed) from the
keyboard and yield its associated ASCII value according to PS/2 Set 1,
diff --git a/fs/doc/uxn.txt b/fs/doc/uxn.txt
@@ -0,0 +1,75 @@
+# uxn emulator
+
+There's a uxn emulator along with varvara device implementation living in
+emul/uxn. Loading emul/uxn/vm.fs exposes this API:
+
+Device
+ An exported structure to allow direct access to the device memory area. See
+ emul/uxn/vm.c for field list.
+
+uxn_init ( -- )
+ Initialize stacks and zero-out all device data and DEI/DEO handler pointers to
+ "null" handlers (will simply read/write from/to "Device dat").
+
+uxn_ram ( -- a )
+ Address, on the host, of the 64K memory area for the uxn VM. You will
+ typically want to put your bytecode at uxn_ram + $100.
+
+uxn_dev ( port -- dev )
+ Yields the address of the Device structure corresponding to "port", a number
+ between 0 and 15.
+
+uxn_exec ( frompc -- )
+ Repeatedly execute instructions from memory, starting at PC "frompc". Stop
+ when a BRK is encountered. You'll typically want to call this with,
+ frompc=$100, but varvara device handlers also call this with frompc=vector.
+
+## Varvara
+
+uxn is a lot more fun when combined with varvara, its companion set of virtual
+devices. Dusk splits varvara in two: the "console" part and the "gui" part.
+
+The console part is loaded through emul/uxn/varvara.fs and exposes this:
+
+varvara_init ( -- )
+ Initialize the uxn VM devices with DEI/DEO handlers for System, Console and
+ File devices.
+
+uxn_cli ( -- )
+ Initialize uxn, varvara and then call uxn_exec with frompc=$100.
+
+## Varvara GUI
+
+The GUI part of varvara is loaded through emul/uxn/gui.fs:
+
+gui_init ( -- )
+ Puts the screen into graphical mode and initializes the Screen and Controller
+ devices. If your console isn't graphical (that is, 100% of console
+ implementations in Dusk at the moment), this locks you out of the computer, so
+ don't call this interactively without a way to get out of it again.
+
+gui_deinit ( -- )
+ Put the screen back to text mode.
+
+uxn_gui ( -- )
+ Initializes uxn, varvara and put the screen in graphical mode. Then, call
+ uxn_exec with frompc=$100, and then enter the "graphical loop". That is:
+
+ 1. Call Screen vector if nonzero
+ 2. Check for controller input
+ 3. Update Controller device accordingly and call vector if needed.
+ 4. Check if Escape key has been pressed then released.
+ 5. If yes, deinit screen and exit loop. If no, goto 1.
+
+## uxntal
+
+You can assemble ".tal" file with the uxntal assembler at asm/uxntal.fs. When
+loaded, exposes this API:
+
+uxntal ( srchdl -- res )
+ Read IO handler "srchdl" until EOF and spits the resulting bytecode to StdOut.
+ Yields a numberical result code which is 0 on success and nonzero on error.
+
+tal>vm ( path -- res )
+ Conveniently wraps "uxntal" for a common case: open a file path "path" (a
+ string) and assemble it at uxn_ram + $100.
diff --git a/fs/emul/uxn/gui.fs b/fs/emul/uxn/gui.fs
@@ -0,0 +1,137 @@
+\ Varvara GUI devices implementation
+require /sys/draw.fs
+?f<< /lib/bit.fs
+?f<< /emul/uxn/varvara.fs
+
+\ FG Mask: to properly handle FG/BG logic, we need to know which pixels are
+\ "filled" on the FG layer, that is, have a non-zero color. When it is, a draw
+\ operation on the BG layer will have no effect.
+\ Let's say, for now, that maximum supported resolution is 1024x768
+1024 768 * 8 / const FGMASKSZ
+create _fgmask FGMASKSZ allot
+
+: _fg' ( x y -- bit a )
+ scrwidth * + 8 /mod ( bit a ) _fgmask + ;
+: _fg! ( x y id -- )
+ >r _fg' dup c@ ( bit a n )
+ rot r> if bit1! else bit0! then ( a n )
+ swap c! ;
+: _fg? ( x y -- f ) _fg' c@ swap bit? ;
+
+\ example: $5678 for "r" means ID0=5 ID1=6 ID2=7 ID3=8
+: _extract ( systemrgb id -- rgb8 )
+ 3 -^ << << rshift $f and ( rgb4 ) 4 lshift ;
+\ get color from uxn's System.(r|g|b) for specified ID (0-3)
+: screencolor ( id -- color ) >r \ V1=id
+ 0 uxn_dev Device dat 8 + ( 'r )
+ dup short@ r@ _extract ( 'r r8 )
+ swap 1+ 1+ dup short@ r@ _extract ( r8 'g g8 )
+ swap 1+ 1+ short@ r> _extract ( r8 g8 b8 ) rgbcolor ;
+: _drawpixel ( x y pixel -- )
+ 3 and screencolor ( x y color )
+ rot> pixel' pixel! ;
+: _drawlayer ( x y pixel fg? -- )
+ if >r 2dup r@ _fg! r> _drawpixel
+ else >r 2dup _fg? if 2drop rdrop else r> _drawpixel then then ;
+:c void _drawlayer(ushort x, ushort y, uchar pixel, int fg);
+
+: screendei ( dev port -- c ) case
+ $2 of = drop scrwidth 8 rshift endof
+ $3 of = drop scrwidth $ff and endof
+ $4 of = drop scrheight 8 rshift endof
+ $5 of = drop scrheight $ff and endof
+ Device dat r@ + c@ endcase ;
+
+: _pixel ( dev -- ) >r \ V1=dev
+ $8 r@ devshort@ ( x ) $a r@ devshort@ ( x y )
+ $6 r@ Device dat + c@ ( x y auto )
+ dup 2 and if ( y+ ) over 1+ $a r@ devshort! then ( x y auto )
+ 1 and if ( x+ ) over 1+ $8 r@ devshort! then ( x y )
+ $e r> Device dat + c@ ( x y pixel )
+ dup $40 and _drawlayer ;
+
+\ Let's go with C for _blit(), the Forth version is ugly
+\ 2bit channel * 4bit blend
+:c static uchar _blending[$40] = {
+ 0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0,
+ 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
+ 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1,
+ 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2};
+
+:c static uchar _opaque[$10] = {
+ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0};
+
+:c void _blit(ushort x, ushort y, uchar *sprite, int spriteopts) {
+ int i, j, row, blend, rx, ry;
+ int color = spriteopts & $f;
+ int opaque = _opaque[color];
+ int twobpp = spriteopts & $80;
+ int fg = spriteopts & $40;
+
+ for(i=0; i<8; ++i) {
+ row = sprite[i] | (twobpp ? sprite[i + 8] : 0) << 8;
+ // TODO: "ry = spriteopts & $20 ? y + 7 - i : y + i" doesn't compile
+ if (spriteopts & $20) ry = y + 7 - i; else ry = y + i;
+ for(j=0; j<8; ++j) {
+ if (spriteopts & $10) rx = x + 7 - j; else rx = x + j;
+ blend = ((row & $80) >> 3) | ((row & $8000) >> 10);
+ if (opaque || blend) {
+ blend = _blending[blend | color];
+ _drawlayer(rx, ry, blend, fg);
+ }
+ row <<= 1;
+ }
+ }
+}
+
+: _sprite ( dev -- )
+ $f over Device dat + c@ >r \ V1=spriteopts
+ $8 over devshort@ >r $a over devshort@ >r \ V2=x V3=y
+ $c over devshort@ uxn_ram + >r ( dev ) \ V4=sprite'
+ $6 over Device dat + c@ swap ( auto dev )
+ over $1 and if $8 over V2 8 + rot> devshort! then ( auto dev )
+ over $2 and if $a over V3 8 + rot> devshort! then ( auto dev )
+ drop dup 4 rshift ( length ) 1+ >r begin ( auto )
+ V2 V3 V4 V1 _blit ( auto )
+ dup $1 and if 8 to+ V3 then
+ dup $2 and if 8 to+ V2 then
+ dup $4 and if 8 to+ V4 V1 $80 and if 8 to+ V4 then then
+ next ( auto ) drop rfree ;
+
+: screendeo ( dev port -- )
+ 0 to@! rgbchanged if \ we need to fill the screen with the new color
+ 0 screencolor scrwidth scrheight 0 0 drawrect then
+ case ( dev ) \ V1=case
+ $e of = _pixel endof
+ $f of = _sprite endof
+ drop endcase ;
+
+\ Check for key presses and return whether the GUI program should exit (when ESC
+\ is pressed).
+: controllerloop ( -- f )
+ ps2@? if
+ $8 uxn_dev >r \ V1=dev
+ ps2mods $f000 and 8 rshift ( kc arrows )
+ ps2mods PS2_MOD_CTRL and bool or ( kc buttons )
+ ps2mods PS2_MOD_ALT and bool << or ( kc buttons )
+ ps2mods PS2_MOD_LSHIFT and bool << << or ( kc buttons )
+ ps2mods PS2_MOD_RSHIFT and bool << << << or ( kc buttons )
+ r@ Device dat 2 + c! ( kc )
+ ps2kcset1>ascii swap not if drop 0 then ( c-or-0 )
+ dup r@ Device dat 3 + c! ( c-or-0 )
+ $1b =
+ r> Device dat short@ ?dup if uxn_exec then ( f )
+ else 0 then ;
+
+\ TODO depend on more generic versions of vesamode! vgatext! and ps2@?
+: gui_init
+ _fgmask FGMASKSZ 0 fill
+ $2 ['] screendei ['] screendeo uxn_set_dev
+ $112 vesamode! ;
+
+: gui_deinit vgatext! ;
+
+: uxn_gui ( -- )
+ uxn_init varvara_init gui_init $100 uxn_exec begin ( )
+ $2 uxn_dev Device dat short@ ?dup if uxn_exec then controllerloop until
+ gui_deinit ;
diff --git a/fs/emul/uxn/screen.fs b/fs/emul/uxn/screen.fs
@@ -1,120 +0,0 @@
-\ Varvara screen implementation
-require /sys/draw.fs
-?f<< /lib/bit.fs
-?f<< /emul/uxn/varvara.fs
-
-\ FG Mask: to properly handle FG/BG logic, we need to know which pixels are
-\ "filled" on the FG layer, that is, have a non-zero color. When it is, a draw
-\ operation on the BG layer will have no effect.
-\ Let's say, for now, that maximum supported resolution is 1024x768
-1024 768 * 8 / const FGMASKSZ
-create _fgmask FGMASKSZ allot
-
-: _fg' ( x y -- bit a )
- scrwidth * + 8 /mod ( bit a ) _fgmask + ;
-: _fg! ( x y id -- )
- >r _fg' dup c@ ( bit a n )
- rot r> if bit1! else bit0! then ( a n )
- swap c! ;
-: _fg? ( x y -- f ) _fg' c@ swap bit? ;
-
-\ example: $5678 for "r" means ID0=5 ID1=6 ID2=7 ID3=8
-: _extract ( systemrgb id -- rgb8 )
- 3 -^ << << rshift $f and ( rgb4 ) 4 lshift ;
-\ get color from uxn's System.(r|g|b) for specified ID (0-3)
-: screencolor ( id -- color ) >r \ V1=id
- 0 uxn_dev Device dat 8 + ( 'r )
- dup short@ r@ _extract ( 'r r8 )
- swap 1+ 1+ dup short@ r@ _extract ( r8 'g g8 )
- swap 1+ 1+ short@ r> _extract ( r8 g8 b8 ) rgbcolor ;
-: _drawpixel ( x y pixel -- )
- 3 and screencolor ( x y color )
- rot> pixel' pixel! ;
-: _drawlayer ( x y pixel fg? -- )
- if >r 2dup r@ _fg! r> _drawpixel
- else >r 2dup _fg? if 2drop rdrop else r> _drawpixel then then ;
-:c void _drawlayer(ushort x, ushort y, uchar pixel, int fg);
-
-: screendei ( dev port -- c ) case
- $2 of = drop scrwidth 8 rshift endof
- $3 of = drop scrwidth $ff and endof
- $4 of = drop scrheight 8 rshift endof
- $5 of = drop scrheight $ff and endof
- Device dat r@ + c@ endcase ;
-
-: _pixel ( dev -- ) >r \ V1=dev
- $8 r@ devshort@ ( x ) $a r@ devshort@ ( x y )
- $6 r@ Device dat + c@ ( x y auto )
- dup 2 and if ( y+ ) over 1+ $a r@ devshort! then ( x y auto )
- 1 and if ( x+ ) over 1+ $8 r@ devshort! then ( x y )
- $e r> Device dat + c@ ( x y pixel )
- dup $40 and _drawlayer ;
-
-\ Let's go with C for _blit(), the Forth version is ugly
-\ 2bit channel * 4bit blend
-:c static uchar _blending[$40] = {
- 0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0,
- 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
- 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1,
- 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2};
-
-:c static uchar _opaque[$10] = {
- 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0};
-
-:c void _blit(ushort x, ushort y, uchar *sprite, int spriteopts) {
- int i, j, row, blend, rx, ry;
- int color = spriteopts & $f;
- int opaque = _opaque[color];
- int twobpp = spriteopts & $80;
- int fg = spriteopts & $40;
-
- for(i=0; i<8; ++i) {
- row = sprite[i] | (twobpp ? sprite[i + 8] : 0) << 8;
- // TODO: "ry = spriteopts & $20 ? y + 7 - i : y + i" doesn't compile
- if (spriteopts & $20) ry = y + 7 - i; else ry = y + i;
- for(j=0; j<8; ++j) {
- if (spriteopts & $10) rx = x + 7 - j; else rx = x + j;
- blend = ((row & $80) >> 3) | ((row & $8000) >> 10);
- if (opaque || blend) {
- blend = _blending[blend | color];
- _drawlayer(rx, ry, blend, fg);
- }
- row <<= 1;
- }
- }
-}
-
-: _sprite ( dev -- )
- $f over Device dat + c@ >r \ V1=spriteopts
- $8 over devshort@ >r $a over devshort@ >r \ V2=x V3=y
- $c over devshort@ uxn_ram + >r ( dev ) \ V4=sprite'
- $6 over Device dat + c@ swap ( auto dev )
- over $1 and if $8 over V2 8 + rot> devshort! then ( auto dev )
- over $2 and if $a over V3 8 + rot> devshort! then ( auto dev )
- drop dup 4 rshift ( length ) 1+ >r begin ( auto )
- V2 V3 V4 V1 _blit ( auto )
- dup $1 and if 8 to+ V3 then
- dup $2 and if 8 to+ V2 then
- dup $4 and if 8 to+ V4 V1 $80 and if 8 to+ V4 then then
- next ( auto ) drop rfree ;
-
-: screendeo ( dev port -- )
- 0 to@! rgbchanged if \ we need to fill the screen with the new color
- 0 screencolor scrwidth scrheight 0 0 drawrect then
- case ( dev ) \ V1=case
- $e of = _pixel endof
- $f of = _sprite endof
- drop endcase ;
-
-\ TODO depend on more generic versions of vesamode! vgatext! and ps2@?
-: screen_init
- _fgmask FGMASKSZ 0 fill
- $2 ['] screendei ['] screendeo uxn_set_dev
- $112 vesamode! ;
-
-: screen_deinit vgatext! ;
-
-: uxn_gui ( -- )
- 8042ps2$ uxn_init varvara_init screen_init $100 uxn_exec begin ( )
- $2 uxn_dev Device dat short@ ?dup if uxn_exec then ps2@? until
- drop screen_deinit ;
diff --git a/fs/sys/ps2.fs b/fs/sys/ps2.fs
@@ -37,7 +37,7 @@ alias abort (ps2@?) ( -- keycode? f )
\ 0 1 2 3 4 5 6 7 8 9 a b c d e f
create _maplower $80 nc,
-0 0 '1' '2' '3' '4' '5' '6' '7' '8' '9' '0' '-' '=' BS $09
+0 $1b '1' '2' '3' '4' '5' '6' '7' '8' '9' '0' '-' '=' BS $09
'q' 'w' 'e' 'r' 't' 'y' 'u' 'i' 'o' 'p' '[' ']' CR $82 'a' 's'
'd' 'f' 'g' 'h' 'j' 'k' 'l' ';' ''' '`' $80 '\' 'z' 'x' 'c' 'v'
'b' 'n' 'm' ',' '.' '/' $81 '*' $84 SPC $88 $90 $91 $92 $93 $94
@@ -49,7 +49,7 @@ $95 $96 $97 $98 $99 $89 $8a '7' '8' '9' '-' '4' '5' '6' '+' '1'
\ Same values, but shifted
\ 0 1 2 3 4 5 6 7 8 9 a b c d e f
create _mapupper $80 nc,
-0 0 '!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '_' '+' BS $09
+0 $1b '!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '_' '+' BS $09
'Q' 'W' 'E' 'R' 'T' 'Y' 'U' 'I' 'O' 'P' '{' '}' CR $82 'A' 'S'
'D' 'F' 'G' 'H' 'J' 'K' 'L' ':' '"' '~' $80 '|' 'Z' 'X' 'C' 'V'
'B' 'N' 'M' '<' '>' '?' $81 '*' $84 SPC $88 $90 $91 $92 $93 $94
@@ -65,19 +65,19 @@ create _mapextended $80 nc,
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 $8d 0 0 $8e 0 $8f 0 0
-$8c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 $8c 0 0 $8e 0 $8f 0 0
+$8d 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-: _map@ ( kc -- is-released c-or-0 )
+: ps2kcset1>ascii ( kc -- is-released c-or-0 )
dup $80 and swap dup $7f and swap 8 rshift if
_mapextended else _shift? if _mapupper else _maplower then then
+ c@ ;
: ps2@? ( -- keycode? f )
(ps2@?) dup if
- over _map@ dup $f0 and $80 = if ( kc f is-released mod-id )
+ over ps2kcset1>ascii dup $f0 and $80 = if ( kc f is-released mod-id )
$0f and swap if ( kc f mod-id ) \ key released, unset mod
$fe swap lshift ps2mods and to ps2mods
else ( kc f mod-id ) \ key pressed, set mod
@@ -89,7 +89,7 @@ $8c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ Poll ps2@, apply shift logic, return the corresponding ASCII code.
: ps2keyset1 ( -- c )
begin
- begin ps2@? until ( kc ) _map@ ( is-released c-or-0 )
+ begin ps2@? until ( kc ) ps2kcset1>ascii ( is-released c-or-0 )
swap if \ released, do nothing
drop 0
else \ pressed
diff --git a/fs/tests/manual/uxn/ctrl.fs b/fs/tests/manual/uxn/ctrl.fs
@@ -0,0 +1,5 @@
+?f<< /asm/uxntal.fs
+?f<< /emul/uxn/gui.fs
+
+S" /tests/manual/uxn/ctrl.tal" tal>vm drop
+uxn_gui
diff --git a/fs/tests/manual/uxn/ctrl.tal b/fs/tests/manual/uxn/ctrl.tal
@@ -0,0 +1,172 @@
+( Source: https://git.sr.ht/~rabbits/uxn
+ Filename: projects/examples/devices/controller.tal
+ License: /license/dll-uxn.txt )
+( Controller:
+ Buttons should highlight on press and display the button and key bytes. )
+
+|00 @System &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1
+|20 @Screen &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1
+|80 @Controller &vector $2 &button $1 &key $1
+
+|0000
+
+@center
+ &x $2 &y $2
+@frame
+ &w $2 &h $2 &x0 $2 &y0 $2 &x1 $2 &y1 $2
+
+|0100 ( -> )
+
+ ( theme )
+ #0fff .System/r DEO2
+ #0f0f .System/g DEO2
+ #0f0f .System/b DEO2
+ ( find center )
+ .Screen/width DEI2 #01 SFT2 .center/x STZ2
+ .Screen/height DEI2 #01 SFT2 .center/y STZ2
+ ( place controller )
+ #0068 .frame/w STZ2
+ #0030 .frame/h STZ2
+ .center/x LDZ2 .frame/w LDZ2 #01 SFT2 SUB2 .frame/x0 STZ2
+ .center/y LDZ2 .frame/h LDZ2 #01 SFT2 SUB2 .frame/y0 STZ2
+ .frame/x0 LDZ2 .frame/w LDZ2 ADD2 .frame/x1 STZ2
+ .frame/y0 LDZ2 .frame/h LDZ2 ADD2 .frame/y1 STZ2
+ ( vectors )
+ ;on-button .Controller/vector DEO2
+ ( frame )
+ .frame/x0 LDZ2 .frame/y0 LDZ2
+ .frame/x1 LDZ2 .frame/y1 LDZ2
+ #03 ;line-rect JSR2
+
+ ,draw-controller JSR
+
+BRK
+
+@on-button ( -> )
+
+ ,draw-controller JSR
+
+ ( print stack on start button )
+ .Controller/button DEI #08 EQU [ JMP BRK ] #010e DEO
+
+BRK
+
+@draw-controller ( -- )
+
+ .Controller/button DEI STH
+
+ ( d-pad )
+ .frame/x0 LDZ2 #0010 ADD2 .Screen/x DEO2
+ .frame/y0 LDZ2 #0010 ADD2 .Screen/y DEO2
+ ;controller-icn/dpad-up .Screen/addr DEO2
+ #03 STHkr #04 SFT #01 AND SUB .Screen/sprite DEO
+ .Screen/y DEI2 #0010 ADD2 .Screen/y DEO2
+ ;controller-icn/dpad-down .Screen/addr DEO2
+ #03 STHkr #05 SFT #01 AND SUB .Screen/sprite DEO
+ .Screen/y DEI2 #0008 SUB2 .Screen/y DEO2
+ .Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
+ ;controller-icn/dpad-left .Screen/addr DEO2
+ #03 STHkr #06 SFT #01 AND SUB .Screen/sprite DEO
+ .Screen/x DEI2 #0010 ADD2 .Screen/x DEO2
+ ;controller-icn/dpad-right .Screen/addr DEO2
+ #03 STHkr #07 SFT #01 AND SUB .Screen/sprite DEO
+ .Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
+ ;controller-icn/dpad .Screen/addr DEO2
+ #03 .Screen/sprite DEO
+
+ ( options )
+ .center/y LDZ2 #0009 ADD2 .Screen/y DEO2
+ .center/x LDZ2 #0009 SUB2 .Screen/x DEO2
+ ;controller-icn/option .Screen/addr DEO2
+ #03 STHkr #03 SFT #01 AND SUB .Screen/sprite DEO
+ .center/x LDZ2 #0004 ADD2 .Screen/x DEO2
+ ;controller-icn/option .Screen/addr DEO2
+ #03 STHkr #02 SFT #01 AND SUB .Screen/sprite DEO
+
+ ( buttons )
+ .center/y LDZ2 .Screen/y DEO2
+ .center/x LDZ2 #0018 ADD2 .Screen/x DEO2
+ ;controller-icn/button .Screen/addr DEO2
+ #03 STHkr #01 SFT #01 AND SUB .Screen/sprite DEO
+ .Screen/y DEI2 #000a ADD2 .Screen/y DEO2
+ ;font-hex/b .Screen/addr DEO2
+ #03 .Screen/sprite DEO
+
+ .center/y LDZ2 .Screen/y DEO2
+ .center/x LDZ2 #0024 ADD2 .Screen/x DEO2
+ ;controller-icn/button .Screen/addr DEO2
+ #03 STHr #01 AND SUB .Screen/sprite DEO
+ .Screen/y DEI2 #000a ADD2 .Screen/y DEO2
+ ;font-hex/a .Screen/addr DEO2
+ #03 .Screen/sprite DEO
+
+ .center/x LDZ2 #0010 SUB2 .Screen/x DEO2
+ .center/y LDZ2 #0010 SUB2 .Screen/y DEO2
+ #01 .Screen/auto DEO
+ .Controller/button DEI2 ,draw-short JSR
+ #00 .Screen/auto DEO
+
+JMP2r
+
+( generics )
+
+@draw-short ( short* -- )
+
+ SWP ,draw-byte JSR
+
+@draw-byte ( byte -- )
+
+ DUP #04 SFT ,draw-hex JSR
+
+@draw-hex ( char -- )
+
+ #00 SWP #0f AND #30 SFT2 ;font-hex ADD2 .Screen/addr DEO2
+ #03 .Screen/sprite DEO
+
+JMP2r
+
+@line-rect ( x1* y1* x2* y2* color -- )
+
+ STH
+ DUP2 ,&ver-y2 STR2 ,&hor-y2 STR2
+ DUP2 ,&ver-x2 STR2 ,&hor-x2 STR2
+ DUP2 ,&ver-y1 STR2 ,&hor-y1 STR2
+ DUP2 ,&ver-x1 STR2 ,&hor-x1 STR2
+ ( horizontal )
+ [ LIT2 &hor-x2 $2 ] INC2 [ LIT2 &hor-x1 $2 ]
+ &hor
+ DUP2 .Screen/x DEO2
+ [ LIT2 &hor-y1 $2 ] .Screen/y DEO2 STHkr .Screen/pixel DEOk
+ [ LIT2 &hor-y2 $2 ] .Screen/y DEO2 DEO
+ INC2 GTH2k ,&hor JCN
+ POP2 POP2
+ ( vertical )
+ [ LIT2 &ver-y2 $2 ] [ LIT2 &ver-y1 $2 ]
+ &ver
+ DUP2 .Screen/y DEO2
+ [ LIT2 &ver-x1 $2 ] .Screen/x DEO2 STHkr .Screen/pixel DEOk
+ [ LIT2 &ver-x2 $2 ] .Screen/x DEO2 DEO
+ INC2 GTH2k ,&ver JCN
+ POP2 POP2
+ POPr
+
+JMP2r
+
+@controller-icn
+ &dpad ffff ffff ffff ffff
+ &dpad-up 7eff e7c3 ffff ffff
+ &dpad-down ffff ffff c3e7 ff7e
+ &dpad-left 7fff efcf cfef ff7f
+ &dpad-right feff f7f3 f3f7 fffe
+ &option 0000 7eff ff7e 0000
+ &button 3c7e ffff ffff 7e3c
+
+@font-hex
+ 007c 8282 8282 827c 0030 1010 1010 1010
+ 007c 8202 7c80 80fe 007c 8202 1c02 827c
+ 000c 1424 4484 fe04 00fe 8080 7c02 827c
+ 007c 8280 fc82 827c 00fe 0202 0408 1010
+ 007c 8282 7c82 827c 007c 8282 7e02 827c
+ &a 007c 8202 7e82 827e &b 00fc 8282 fc82 82fc
+ 007c 8280 8080 827c 00fc 8282 8282 82fc
+ 00fe 8080 fe80 80fe 00fe 8080 f080 8080
diff --git a/fs/tests/manual/uxn/mandel.fs b/fs/tests/manual/uxn/mandel.fs
@@ -1,6 +1,6 @@
\ Test varvara's screen by drawing a mandelbrot on screen
?f<< /asm/uxntal.fs
-?f<< /emul/uxn/screen.fs
+?f<< /emul/uxn/gui.fs
S" /tests/manual/uxn/mandel.tal" tal>vm drop
uxn_gui
diff --git a/fs/tests/manual/uxn/pixdance.fs b/fs/tests/manual/uxn/pixdance.fs
@@ -1,6 +1,6 @@
\ Test varvara's screen vector by progressively drawing pixels
?f<< /asm/uxntal.fs
-?f<< /emul/uxn/screen.fs
+?f<< /emul/uxn/gui.fs
S" /tests/manual/uxn/pixdance.tal" tal>vm drop
uxn_gui
diff --git a/fs/tests/manual/uxn/rect.fs b/fs/tests/manual/uxn/rect.fs
@@ -1,6 +1,6 @@
\ Test varvara's screen by drawing a rect on screen
?f<< /asm/uxntal.fs
-?f<< /emul/uxn/screen.fs
+?f<< /emul/uxn/gui.fs
S" /tests/manual/uxn/rect.tal" tal>vm drop
uxn_gui
diff --git a/fs/tests/manual/uxn/sprite.fs b/fs/tests/manual/uxn/sprite.fs
@@ -1,5 +1,5 @@
?f<< /asm/uxntal.fs
-?f<< /emul/uxn/screen.fs
+?f<< /emul/uxn/gui.fs
S" /tests/manual/uxn/sprite.tal" tal>vm drop
uxn_gui