# uxn tutorial: day 3, conditional jumps and the keyboard/controller lang=en es->{tutorial de uxn día 3} this is the third section of the {uxn tutorial}! here we introduce the use of the controller device in the varvara uxn computer: this will allow us to add interactivity to our programs, and to start discussing control flow in uxntal. we also talk about logic and stack manipulation instructions in uxntal. # the controller device the controller device in the varvara computer allows us to read inputs from the keyboard and/or from controller buttons. the definition of its ports would look as follows in a typical program: ``` |80 @Controller [ &vector $2 &button $1 &key $1 ] ``` ## the button byte the button byte encodes in each of its eight bits the state of eight different "buttons", based on the NES controller layout. => https://wiki.nesdev.com/w/index.php/Standard_controller standard NES controller numbering the bits from right to left, and from 0 to 7, the corresponding keys (and NES buttons) are: + + + + + + + + + + +
bitkeybutton
7Right
6Left
5Down
4Up
3HomeStart
2ShiftSelect
1AltB
0CtrlA
& * 7: Right & * 6: Left & * 5: Down & * 4: Up & * 3: Home (Start button) & * 2: Shift (Select button) & * 1: Alt (button B) & * 0: Ctrl (button A) enconding the states of the buttons in this way allows us to press and read many of these keys at the same time. ## the key byte the key byte stores the ascii code of the keyboard key that is being pressed at the moment. the difference between the 'key' byte and the 'button' byte can be confusing, especially when running varvara from uxnemu where the buttons are in the same place as the keys. a possible way to remember might be to think of the 'button' byte as referring to a gamepad controller. ## the controller vector in the context of uxn programming, a vector refers to an address in main memory where uxn is assigned to jump to when a specific event happens. in the case of the controller vector, this specific event consists in whenever a key is pressed or released. in other words: uxn will jump to the address assigned as the controller vector, whenever a key is pressed or released. the following line of code would assign that vector, using the absolute address of the label on-controller: ``` ;on-controller .Controller/vector DEO2 ``` let's see next how that would work! # control flow: vector subroutines so far our uxntal programs have followed a linear flow: they start at address 0100, and they end at the first BRK instruction that is found. we can think of these programs as setup routines: they setup the system colors, they might draw or print some things, and then they leave uxn waiting. what would uxn be waiting for? yes, an option would be: waiting for keyboard input! we will start organizing our uxntal programs in terms of subroutines that correspond to different vectors. each of these subroutines will end with the BRK instruction, so that they can make uxn return to the waiting state. ## controller vector subroutine to illustrate that behavior, let's read the following program. it uses the sprite drawing procedure we tried in the previous day, but has it happening only when a key is pressed. in the beginning, the screen is empty, and when we press a key a square is drawn: ``` ( hello-keyboard.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 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] ( main program ) |0100 ( set system colors ) #2ce9 .System/r DEO2 #01c0 .System/g DEO2 #2ce5 .System/b DEO2 ( assign controller vector ) ;on-controller .Controller/vector DEO2 BRK ( run this code whenever a key is pressed or released ) @on-controller ( -> ) ( set x,y coordinates ) #0008 .Screen/x DEO2 #0008 .Screen/y DEO2 ( set sprite address ) ;square .Screen/addr DEO2 ( draw sprite in the background ) ( using color 1 for the outline ) #01 .Screen/sprite DEO BRK ( sprite ) @square ff81 8181 8181 81ff ``` nice, isn't it? now, how can we take different actions depending on the key that was pressed? first of all, we have to let our program know which key was pressed so that it can act accordingly. in order to achieve that, let's take a look at some new uxntal instructions! # comparison and logic instructions ## comparison instructions uxntal has four instructions for comparing the top two elements from the stack: * EQU: push 01 down into the stack if the top two elements of the stack are equal, or push 00 otherwise ( a b -- a==b ) * NEQ: push 01 down into the stack if the top two elements of the stack are not equal, or push 00 otherwise ( a b -- a!=b ) * GTH: push 01 down into the stack if the first element is greater than the second, or push 00 otherwise ( a b -- a>b ) * LTH: push 01 down into the stack if the first element is less than the second, or push 00 otherwise ( a b -- a ./img/screenshot_uxn-draw-with-keyboard.png screenshot of a possible result of running the following program; it shows a trail drawn with filled or outlined squares. the following uxntal program allows you to draw using the arrows keys and the Ctrl key (button A). the arrows move the position of a sprite, and pressing Ctrl while moving it will draw it with the inverse colors in fill and stroke. note the use of AND masks, conditional jumps, and some stack operations! ``` ( draw-with-keyboard.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 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] ( main program ) |0100 ( set system colors ) #2ce9 .System/r DEO2 #01c0 .System/g DEO2 #2ce5 .System/b DEO2 ( assign controller vector ) ;on-controller .Controller/vector DEO2 ( set initial x,y coordinates ) #0008 .Screen/x DEO2 #0008 .Screen/y DEO2 ( set sprite address ) ;square .Screen/addr DEO2 BRK @on-controller ( -> ) .Controller/button DEI DUP ( read and duplicate button byte ) #01 AND ( isolate bit 0, corresponding to Ctrl ) ,&fill JCN ( if the bit is not 0, jump to fill, otherwise continue ) &outline #01 .Screen/sprite DEO ( draw outline ) ,&check-arrows JMP ( continue to check-arrows ) &fill #04 .Screen/sprite DEO ( draw filled ) &check-arrows ( use button byte from the stack ) DUP #10 AND ( isolate bit 4, corresponding to Up ) ,&up JCN ( jump if not 0 ) DUP #20 AND ( isolate bit 5, corresponding to Down ) ,&down JCN ( jump if not 0 ) DUP #40 AND ( isolate bit 6, corresponding to Left ) ,&left JCN ( jump if not 0 ) DUP #80 AND ( isolate bit 7, corresponding to Right ) ,&right JCN ( jump if not 0 ) POP BRK &up .Screen/y DEI2 #0008 SUB2 .Screen/y DEO2 ( decrement y ) POP BRK &down .Screen/y DEI2 #0008 ADD2 .Screen/y DEO2 ( increment y ) POP BRK &left .Screen/x DEI2 #0008 SUB2 .Screen/x DEO2 ( decrement x ) POP BRK &right .Screen/x DEI2 #0008 ADD2 .Screen/x DEO2 ( increment x ) POP BRK BRK ( sprite ) @square ff81 8181 8181 81ff ``` as a possible exercise for you, modify the code so that it will also respond to you pressing more than one arrow at the same time. # practice possibilities here are some other ideas for you to practice with what we covered today! * draw a virtual controller that shows which of its buttons, mapped to keyboard keys, are being pressed * create some kind of typewriter that draws different symbols and moves the drawing cursor depending on the key that was pressed. * draw a character that changes its state according to the key you pressed. maybe use multiple tiles to draw it? * create a simple tic-tac-toe board for two players: one key draws a X, another draws a O, and the arrows allow you to choose the cell to draw. note that for smooth interactive movement it might be better to use the screen vector that is called 60 times per second! we'll cover that in depth in the next day of the tutorial! # instructions of day 3 these are all the uxntal instructions that we discussed today! ## comparison instructions * EQU: push 01 down into the stack if the top two elements of the stack are equal, or push 00 otherwise ( a b -- a==b ) * NEQ: push 01 down into the stack if the top two elements of the stack are not equal, or push 00 otherwise ( a b -- a!=b ) * GTH: push 01 down into the stack if the first element is greater than the second, or push 00 otherwise ( a b -- a>b ) * LTH: push 01 down into the stack if the first element is less than the second, or push 00 otherwise ( a b -- a