icono de compudanzas

uxn tutorial: day 5, the mouse and uxntal goodies

this is the fifth section of the uxn tutorial! here we introduce the varvara mouse device to explore more possible interactions, and we cover the remaining elements of uxntal and uxn: the return stack, the return mode and the keep mode.

we also discuss possible structures to create loops and more complex programs using these resources!

the mouse device

the mouse device in the varvara computer is similar to the controller device in several ways: it has a vector that is called with any mouse event (change of buttons state, movement, wheel movement) and a couple of bytes to check its state.

additionally, it has a couple of shorts corresponding to the x,y coordinates of the mouse pointer.

we see this device defined in uxntal in the following way:

|90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ]

state byte

the state byte has these possible values:

wheel byte

the wheel byte has these possible values:

mouse vector

the mouse vector will be fired in any of the following events:

hello mouse

let's start drawing!

the following is a simple example that illustrates the use of

combined with a conditional and sprite drawing.

it draws our square in the position of the mouse, changing its color when any mouse button is pressed.

screenshot showing a drawing made with the program: wiggly lines composed of overlapped squares of two different colors
( hello-mouse.tal )

( devices )
|00 @System  [ &vector $2 &pad $6  &r $2 &g $2 &b $2 ]
|20 @Screen  [ &vector $2 &width $2 &height $2 &pad  $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ]

( init )
|0100
     ( set system colors )
     #2ce9 .System/r DEO2
     #01c0 .System/g DEO2
     #2ce5 .System/b DEO2

     ( set mouse vector )
     ;on-mouse .Mouse/vector DEO2

     ( set sprite address )
     ;square .Screen/addr DEO2

BRK

@on-mouse ( -> )
    .Mouse/x DEI2 .Screen/x DEO2
    .Mouse/y DEI2 .Screen/y DEO2

    ( jump if a button is pressed )
    .Mouse/state DEI ,&pressed JCN 

    ( draw sprite using color 2 and 0 in background )
        #02 .Screen/sprite DEO 
    BRK

    &pressed
    ( draw sprite using color 1 and 0 in background )
        #01 .Screen/sprite DEO 
BRK

@square [ ff81 8181 8181 81ff ]

hello pointer

maybe you have noticed that, before today, the mouse pointer has disappeared when entering the uxnemu window.

how could we program and replicate its behavior in uxntal?

we could use a similar strategy to what we did for animating a sprite:

this procedure can happen whenever the mouse vector is fired.

we can use a set of variables in the zero page in order to store the pointer position, so that we have a way of clearing the sprite in the previous coordinates of the mouse.

additionally, here we have a 1bpp sprite of a mouse pointer, taken from the uxn examples:

@pointer_icn [ 80c0 e0f0 f8e0 1000 ]

the program

this is a program that accomplishes drawing the pointer on the screen!

( hello-pointer.tal )

( devices )
|00 @System  [ &vector $2 &pad $6  &r $2 &g $2 &b $2 ]
|20 @Screen  [ &vector $2 &width $2 &height $2 &pad  $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ]

( zero page )
|0000
@pointer [ &x $2 &y $2 ]

( init )
|0100
    ( set system colors )
    #2ce9 .System/r DEO2
    #01c0 .System/g DEO2
    #2ce5 .System/b DEO2

    ( set mouse vector )
    ;on-mouse .Mouse/vector DEO2

    ( set sprite address )
    ;pointer_icn .Screen/addr DEO2

BRK

@on-mouse ( -> )
    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( clear sprite from foreground )
    #40 .Screen/sprite DEO 

    ( update pointer position )
    .Mouse/x DEI2 .pointer/x STZ2
    .Mouse/y DEI2 .pointer/y STZ2

    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( draw sprite with color 2 in foreground )
    #4a .Screen/sprite DEO 
BRK

@pointer_icn [ 80c0 e0f0 f8e0 1000 ]

note that it draws the pointer in the foreground, and that it uses 'a' in the low nibble of the sprite byte: this implies that it will use color 2 to draw the pointer shape, and will draw with transparency the rest of the tile. (see drawing 1bpp sprites in uxn tutorial day 2 )

this blending mode would allow you to draw things in the background and have the pointer cover them with its shape only, and not with the whole square tile.

i invite you to try this!

draw in the background and see how the pointer looks, and then replace its sprite byte with e.g. 42 to see the difference!

some issues

now, we can see that the program does work, but it is kind of flooding the on-mouse subroutine with a lot of code.

that's even more true if we consider that we are only drawing the pointer, and not taking any other action like responding to the buttons.

creating a macro for all this code could be possible, but also kind of impractical due to the amount of code.

maybe we could have a JMP to another section of the program, that at its ends has another JMP to return to the corresponding position in the on-mouse subroutine?

actually, that's almost what we will do, but with an additional element of uxn: its return stack!

the return stack

so far, "the stack" that we have been using is what is usually called "the working stack".

uxn, like other forth-like systems, has another stack of the same size, "the return stack".

why is it called like that?

the idea is that it would be normally used to push down into it addresses of program memory.

these addresses would correspond to locations to which we'd like to eventually "return".

preparing our program

first of all, let's move our pointer drawing subroutine to another label in our program:

@draw-pointer ( -- )
    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( clear sprite from foreground )
    #40 .Screen/sprite DEO 

    ( update pointer position )
    .Mouse/x DEI2 .pointer/x STZ2
    .Mouse/y DEI2 .pointer/y STZ2

    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( draw sprite with color 2 in foreground )
    #4a .Screen/sprite DEO 
BRK

this would leave our on-mouse subroutine empty:

@on-mouse ( -> )

BRK

note that we could join the update pointer position with sending it to the screen, using a pair of DUP2:

( update pointer position and send to screen )
.Mouse/x DEI2 DUP2 .pointer/x STZ2 .Screen/x DEO2
.Mouse/y DEI2 DUP2 .pointer/y STZ2 .Screen/y DEO2

using normal jumps

with what we know already, and depending on the position of draw-pointer with respect to on-mouse, we could do a relative jump:

@on-mouse ( -> )
    ,draw-pointer JMP

    &return
    ( something else here )
BRK

or an absolute jump:

@on-mouse ( -> )
    ;draw-pointer JMP2
 
    &return
    ( something else here )
BRK

the thing is, if we want something else to happen after drawing the pointer inside on-mouse, we can't go back easily.

at the end of our draw-pointer subroutine, we'd need to "jump back" like this:

    ;on-mouse/return JMP2 

let's meet an alternative, the "jump and stash" instruction!

jump and stash

the jump and stash instruction, JSR, does the same as JMP (unconditionally jump to the address present in the working stack), with the additional action of pushing down into the return stack the address of the next instruction in memory.

in the normal mode, JSR takes a relative address (one byte) from the working stack, and in short mode, JSR2 takes an absolute address.

relative jump

for example, our jumps could be re-written as follows. in the case of a relative jump:

@on-mouse ( -> )
    ,draw-pointer JSR

    ( something else here )
BRK

absolute jump

and in the case of an absolute jump:

@on-mouse ( -> )
    ;draw-pointer JSR2

    ( something else here )
BRK

returning

JSR is pushing the "return address" down into the return stack.

now the question is: how do we take that address from that stack, in order to jump there?

we'd need to use the "return mode"!

the return mode

similar to the short mode, the return mode in uxn consists in activating a binary flag in the byte that encodes an instruction.

the return mode is encoded in the 7th bit of an instruction byte, counting from right to left.

whenever this flag is set, uxn will perform the given instruction but using as sources the contents of the return stack instead of the contents of the working stack.

in uxntal, we indicate that we want to set this flag adding the letter 'r' to the end of an instruction mnemonic.

as each of the modes is an independent bit, it is possible to combine them, e.g. activating the return mode and the short mode by using the suffix '2r'.

jumping to return

as we have discussed already, JMP will allow us to unconditionally jump to the address given in the top of the (working) stack.

JSR or JSR2 push down into the return stack the absolute address of the next instruction, a short, so that we can eventually return there.

how can we unconditionally jump to that absolute address in the return stack?

exactly!

activating the return mode in the JMP instruction!

additionally, as the addresses pushed by JSR are shorts, we need to activate the short mode as well:

JMP2r ( jump to the absolute address at the top of the return stack )

normally, in uxntal programs you will see this instruction written as a macro, RTN (return)

%RTN { JMP2r }

we can finish a subroutine using this macro in order to "return" to the position in the program after the corresponding JSR.

complete example using subroutines

this is the hello-pointer.tal program, but using draw-pointer as a subroutine that is "called" with JSR2 and that ends with RTN:

( hello-pointer.tal )

( devices )
|00 @System  [ &vector $2 &pad $6  &r $2 &g $2 &b $2 ]
|20 @Screen  [ &vector $2 &width $2 &height $2 &pad  $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ]

( macros )
%RTN { JMP2r }

( zero page )
|0000
@pointer [ &x $2 &y $2 ]

( init )
|0100
    ( set system colors )
    #2ce9 .System/r DEO2
    #01c0 .System/g DEO2
    #2ce5 .System/b DEO2

    ( set mouse vector )
    ;on-mouse .Mouse/vector DEO2

    ( set sprite address )
    ;pointer_icn .Screen/addr DEO2

BRK

@on-mouse ( -> )
   ;draw-pointer JSR2 ( or ,draw-pointer JSR )

   ( something else )
BRK

@draw-pointer ( -- )
    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( clear sprite from foreground )
    #40 .Screen/sprite DEO 

    ( update pointer position )
    .Mouse/x DEI2 .pointer/x STZ2
    .Mouse/y DEI2 .pointer/y STZ2

    ( send pointer position to screen )
    .pointer/x LDZ2 .Screen/x DEO2
    .pointer/y LDZ2 .Screen/y DEO2

    ( draw sprite with color 2 in foreground )
    #4a .Screen/sprite DEO 
RTN

@pointer_icn [ 80c0 e0f0 f8e0 1000 ]

note that the draw-pointer label is accompanied by the stack state notation ( -- ) to indicate that, in this case, it doesn't consume or produce contents from or to the working stack.

notes on subroutines as "functions"

keep in mind that a possible way of sending "arguments" to a subroutine would be to push them down into the working stack before calling it.

additionaly, a possible way of receiving the results of a subroutine would be to have it pushing them down into the working stack so that they are there to be consumed after returning.

there might be other instances where using "variables" would make more logical and/or readable sense to pass arguments and results.

stash, don't jump

having introduced the return stack and the return mode, another world of possibilities opens for us: we can also use the return stack as an additional, temporary stack, to store some values while we operate on others.

in order to achieve this, uxn has an instruction called STH, stash.

this is the last instruction we had to cover in this tutorial series! :)

STH

STH takes a value from the working stack and pushes it down into the return stack.

in return mode, STHr does the opposite: it takes a value from the return stack, and pushes it down into the working stack.

and, as you might have guessed, in short mode this instruction operates moving shorts instead of bytes.

example: horizontal line

the following is an example subroutine that shows some possibilities of the return stack and mode.

it is a subroutine that draws an horizontal line of a given length (from 1 to 255 pixels, i.e. using one byte), starting from a given x coordinate (short) and using a given y coordinate (short).

these parameters are given as arguments in the working stack.

the subroutine uses the return stack to "stash" one of these arguments while working with the others.

additionally, it has a working loop! written in one of several ways of implementing it :)

the state of both the working (ws) and the return (rs) stacks is shown in the comments after almost every step. the top of the stacks is located at their right.

a caret (^) after a value name indicates that it corresponds to a short.

@draw-horizontal-line ( x^ y^ length -- )
    ( beginning )
    ( ws: x^ y^ length / rs : )

    ( store length in return stack )
    STH  ( ws: x^ y^ / rs: length )

    ( set initial coordinates )
    .Screen/y DEO2 ( ws: x^ / rs: length )
    .Screen/x DEO2 ( ws: / rs: length )

    ( retrieve length from return stack )
    STHr ( ws: length / rs:  )

    ( initialize count )
    #00 ( ws: length 00 / rs: )
    
    &loop
        ( stash length and count )
        STH2 ( ws: / rs: length count )

        ( draw pixel with color 2 )
        #02 .Screen/pixel DEO
        
        ( increment x )
        .Screen/x DEI2 INC2 .Screen/x DEO2

        ( retrieve length and count )
        STH2r ( ws: length count / rs: )

        ( increment count to get new count )
        INC ( ws: length count / rs: )

        ( duplicate length and count, compare, and jump )
        DUP2 ( ws: length count length count / rs: )
        NEQ ( ws: length count flag / rs: )
        ,&loop JCN ( ws: length count / rs: )
     POP2 ( ws: / rs: )
RTN

calling

in order to call the subroutine, you could do something like the following:

    #0008 ( push initial x )
    .Screen/height DEI2 HALF2 ( push y )
    #ff ( push length of line )
    ;draw-horizontal-line JSR2 ( call subroutine )

notes

note that in this specific subroutine, the use of STH2 and STH2r after the &loop sublabel is actually not needed: the operations in between these instructions do touch the working stack but afterwards leave it as it was.

however, it shows how we can use these instructions to have a clean working stack without other values interfering.

possible exercises

the keep mode

the last element of uxntal that we can cover is its third mode for instructions: the keep mode.

the keep mode is encoded in the 8th bit of an instruction byte, counting from right to left.

in uxntal, we indicate that we want to set this flag adding the letter 'k' to the end of an instruction mnemonic.

whenever this flag is set, uxn will perform the given instruction but "keeping" the original values in the corresponding stack.

in other words, in keep mode items will not be consumed from the stack, but the corresponding results will be pushed down into the stack.

keep mode can be combined with the other modes, for a total of eight possible combinations of modes.

keep mode in arithmetic

we know what the following uxntal code does; it pushes 01 and 02 down into the stack, adds both elements, and pushes the result (03) down into the stack:

#01 #02 ( ws: 01 02 )
ADD     ( ws: 03 )

compare with what happens when using ADDk instead:

#01 #02 ( ws: 01 02 )
ADDk    ( ws: 01 02 03 )

the addition is performed and the result is pushed, but the operands are left in the stack.

it might be hard to think in general of a use for this, but "keep" it in mind!

modulo

actually, if you remember, on uxn tutorial day 4 i shared with you a couple of macros to perform a modulo operation:

%MOD { DUP2 DIV MUL SUB } ( a b -- a%b )
%MOD2 { OVR2 OVR2 DIV2 MUL2 SUB2 } ( a b -- a%b )

i said then that there was a more optimized set, and that we'd discuss it later.

now is that later moment!

first of all, let's analyze what's happening with MOD. it is calculating what would be written in infix notation as follows, assumming that the slash (/) indicates an integer division

a - ( a/b )*b

try for example replacing 'a' with 7 and 'b' with 3; the modulo or remainder should be 1

in our original macro, what happens goes as follows:

#07 #03 ( ws: 07 03 )
DUP2    ( ws: 07 03 07 03 )
DIV     ( ws: 07 03 02 )
MUL     ( ws: 07 06 )
SUB     ( ws: 01 )

do you see a possibility for introducing the keep mode?

if you look carefully, you'll see that DUP2 is there in order to avoid losing the original values, so that we can perform the multiplication and subtraction later.

therefore, DUP2 DIV is equivalent to... DIVk! a division that doesn't lose its operands!

#07 #03 ( ws: 07 03 )
DIVk    ( ws: 07 03 02 )
MUL     ( ws: 07 06 )
SUB     ( ws: 01 )

so our macro can have one byte less!

we can generalize this behavior for the short mode, and obtain the optimal set of macros that i mentioned earlier:

%MOD { DIVk MUL SUB }
%MOD2 { DIV2k MUL2 SUB2 }

keep mode and comparisons

keep mode can be useful when we do comparisons and we don't want to lose the original values.

for example, in our draw-horizontal-line subroutine, we had the following set of lines of code:

( duplicate length and count, compare, and jump )
DUP2 ( ws: length count length count / rs: )
NEQ ( ws: length count flag / rs: )
,&loop JCN ( ws: length count / rs: )

you'll see that here, like in the DIVk case above, the DUP2 is there only to make sure that the length and count are not lost when performing NEQ.

we could therefore replace DUP2 NEQ with NEQk:

( duplicate length and count, compare, and jump )
NEQk ( ws: length count flag / rs: )
,&loop JCN ( ws: length count / rs: )

keep mode and the return stack

sometimes we'll want to stash a copy of a value that we'll use at the moment.

without keep mode, we'd write:

DUP STH ( duplicate and stash )

with keep mode, we can write:

STHk ( stash and keep )

similarly, there will be occasions where we want to retrieve a copy of a value from the return stack.

for that, we can write:

STHkr ( retrieve a copy from the return stack )

more and more keep mode

new and interesting uses for the keep mode are still being found :)

don't hesitate to share them with us! and keep an eye over here for more possibilities!

more exercises

with what we have covered already, and in case you want some ideas, here are some things that should be easier to try building now:

multi-tile sprites

drawing a sprite consisting of several tiles is a process that can benefit from using subroutines: have a subroutine that receives a pair of x,y coordinates in the working stack when called, and use it to draw the tiles in the corresponding positions relative to those coordinates.

drawing tool

a lot of possibilities here!

maybe start with drawing only when a button is pressed. change color and/or "brush" depending on the button that is pressed.

you can have different selectable "modes": maybe they change the brush you are using, the way the brush behaves (e.g. in mirror? kaleidoscope?), and/or the shapes that are drawn.

consider how you would select those modes: on screen buttons? keys from the keyboard?

keep in mind that you can change a device's vector during runtime: you could have a different on-mouse subroutine depending on the mode you have selected :)

how could you use the mouse wheel as an aid for drawing?

and more...

basically, the gates for interactive visual applications in the varvara computer are completely open to you now :)

will you create games? small applications, useful or not? an instrument for live visuals? programs targeting specific handheld devices?

some things might appear difficult to build, but fortunately, (i feel-think) there's nothing else in the workings of the machine that we haven't covered already.

you can go slowly, step by step, practicing your stack wrangling and exercising the postfix brain, and you'll get anywhere you want :)

we'd love to see what you create!

instructions of day 5

these are the uxntal instructions that we discussed today! with these, we have covered them all!

day 6

in uxn tutorial day 6 we talk about how we can integrate everything that we have covered in order to create even more complex subroutines and programs for the varvara computer.

we base our discussion in a recreation of the classic pong game!

besides using previous strategies and snippets of code, we cover strategies for drawing and controlling multi-tile sprites, and for checking collisions.

first, i invite you to take a break!

then, keep exploring, and share your findings!

support

if you found this tutorial to be helpful, consider sharing it and giving it your support :)

incoming links

log

uxn tutorial day 4

uxn tutorial