compudanzas/src/tutorial_de_uxn_día_3.gmo

791 lines
30 KiB
Plaintext

# tutorial de uxn: día 3, saltos condicionales y el teclado o controlador
lang=es en->{uxn tutorial day 3}
¡esta es la tercera sección del {tutorial de uxn}!
aquí introducimos el uso del dispositivo controlador en la computadora uxn varvara: esto nos permitirá añadir interactividad a nuestros programas y empezar a discutir el flujo de control en uxntal.
también hablamos de las instrucciones lógicas y de manipulación de la pila en uxntal.
# el dispositivo controlador
el dispositivo controlador del ordenador varvara nos permite leer las entradas del teclado o de los botones del controlador.
la definición de sus puertos tendría el siguiente aspecto en un programa típico:
```
|80 @Controlador [ &vector $2 &botón $1 &tecla $1 ]
```
## el byte de botón
el byte de botón codifica en cada uno de sus ocho bits el estado de ocho "botones" diferentes, basados en la disposición del controlador de NES, la videoconsola clásica de 8 bits de Nintendo.
=> https://wiki.nesdev.com/w/index.php/Standard_controller controlador NES estándar
numerando los bits de derecha a izquierda y de 0 a 7, las teclas correspondientes (y los botones de NES) son
+ <table>
+ <tr><th>bit</th><th>tecla</th><th>botón</th></tr>
+ <tr><td class="num">7</td><td colspan="2">Derecha</td></tr>
+ <tr><td class="num">6</td><td colspan="2">Izquierda</td></tr>
+ <tr><td class="num">5</td><td colspan="2">Abajo</td></tr>
+ <tr><td class="num">4</td><td colspan="2">Arriba</td></tr>
+ <tr><td class="num">3</td><td>Home</td><td>Iniciar</td></tr>
+ <tr><td class="num">2</td><td>Shift</td><td>Selecccionar</td></tr>
+ <tr><td class="num">1</td><td>Alt</td><td>B</td></tr>
+ <tr><td class="num">0</td><td>Ctrl</td><td>A</td></tr>
+ </table>
& * 7: Derecha
& * 6: Izquierda
& * 5: Abajo
& * 4: Arriba
& * 3: Home (Iniciar)
& * 2: Shift (Selecccionar)
& * 1: Alt (botón B)
& * 0: Ctrl (botón A)
codificar los estados de los botones de esta manera nos permite presionar y leer muchas de estas teclas al mismo tiempo.
## el byte de tecla
el byte de tecla almacena el código ascii de la tecla del teclado que se está pulsando en ese momento.
la diferencia entre el byte 'de tecla' y el byte 'de botón' puede ser confusa, especialmente cuando se ejecuta varvara desde uxnemu, donde varias teclas actúan además de botones.
una posible manera de recordar podría ser pensar en el byte 'de botón' como refiriéndose a un controlador de gamepad.
## el vector del controlador
en el contexto de la programación de uxn, un vector se refiere a una dirección en la memoria principal donde se asigna a uxn para que salte cuando ocurra un evento específico.
en el caso del vector del controlador, este evento específico consiste en cada vez que se pulsa o suelta una tecla.
en otras palabras: uxn saltará a la dirección asignada como vector del controlador, cada vez que se pulse o suelte una tecla.
la siguiente línea de código asignaría ese vector, utilizando la dirección absoluta de la etiqueta en-controlador:
```
;en-controlador .Controlador/vector DEO2
```
¡veamos a continuación como funcionaría!
# flujo de control: subrutinas vectoriales
hasta ahora nuestros programas uxntal han seguido un flujo lineal: comienzan en la dirección 0100 y terminan en la primera instrucción BRK que se encuentra.
podemos pensar en estos programas como rutinas de configuración: configuran los colores del sistema, pueden dibujar o imprimir algunas cosas y luego dejan a uxn esperando. ¿qué estaría esperando uxn?
sí, una opción sería: ¡esperar la entrada del teclado!
vamos a empezar a organizar nuestros programas uxntal en términos de subrutinas que corresponden a diferentes vectores.
cada una de estas subrutinas terminará con la instrucción BRK, para que puedan hacer que uxn vuelva al estado de espera.
## subrutina del vector del controlador
para ilustrar ese comportamiento, leamos el siguiente programa.
este utiliza el procedimiento de dibujo de sprites que probamos el día anterior, pero hace que ocurra solo cuando se pulsa una tecla. al principio, la pantalla está vacía, y cuando pulsamos una tecla, se dibuja un cuadrado:
```
( hola-teclado.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &píxel $1 &sprite $1 ]
|80 @Controlador [ &vector $2 &botón $1 &tecla $1 ]
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( asignar vector del controlador )
;en-controlador .Controlador/vector DEO2
BRK
( ejecutar este código cada vez que se pulse o suelte una tecla )
@en-controlador ( -> )
( establecer coordenadas x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establecer la direccion del sprite )
;cuadrado .Pantalla/direc DEO2
( dibujar el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla/sprite DEO
BRK
( sprite )
@cuadrado ff81 8181 8181 81ff
```
bonito, ¿no?
ahora, ¿cómo podemos realizar diferentes acciones dependiendo de la tecla que se haya pulsado?
en primer lugar, tenemos que hacer que nuestro programa sepa qué tecla se ha pulsado para que pueda actuar en consecuencia.
para ello, veamos algunas instrucciones uxntal nuevas.
# instrucciones de comparación y lógica
## instrucciones de comparación
uxntal tiene cuatro instrucciones para comparar los dos primeros elementos de la pila:
* EQU: empuja 01 hacia abajo en la pila si los dos elementos superiores de la pila son iguales, o empuja 00 en caso contrario ( a b -- a==b )
* NEQ: empuja 01 hacia abajo en la pila si los dos elementos superiores de la pila no son iguales, o empuja 00 en caso contrario ( a b -- a!=b )
* GTH: empuja 01 hacia abajo en la pila si el primer elemento es mayor que el segundo, o empuja 00 en caso contrario ( a b -- a>b )
* LTH: empuja 01 hacia abajo en la pila si el primer elemento es menor que el segundo, o empuja 00 en caso contrario ( a b -- a<b )
podemos pensar en los resultados empujados por estas instrucciones como banderas booleanas: son 01 si la comparación fue verdadera y son 00 si fue falsa.
el siguiente código leerá el valor de la tecla del controlador y empujará a la pila una bandera correspondiente a que sea igual al carácter 'a':
```
.Controlador/tecla DEI ( lee la tecla y la empuja hacia abajo en la pila )
LIT "a ( empuja el código ascii del carácter 'a' )
EQU ( compara ambos bytes y empuja 01 si son iguales, 00 si no )
```
EQU2, NEQ2, GTH2 y LTH2 funcionarán de la misma manera, pero comparando cortos en lugar de bytes.
## instrucciones lógicas
uxntal tiene tres instrucciones lógicas a nivel de bit.
pueden funcionar como operadores lógicos que utilizan como operandos las banderas dadas por las instrucciones de comparación que hemos comentado anteriormente:
* AND: realiza un AND a nivel de bits con los dos elementos superiores de la pila y empuja el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a^b )
AND2, ORA2, EOR2 funcionarán de la misma manera, pero con cortos en lugar de bytes.
### AND
lo siguiente empujará hacia abajo en la pila una bandera que indica si el byte tecla está entre 30 y 39 inclusive, usando 01 para representar 'verdadero' y 00 para representar 'falso':
```
.Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas de la pila y empuja el resultado a la pila )
```
que la instrucción sea a nivel de bit (o "bitwise") significa que aplica la operación AND a cada uno de los bits de los operandos.
si ambas banderas fueran "verdaderas":
```
0000 0001 ( verdadero )
AND 0000 0001 ( verdadero )
----------
0000 0001 ( verdadero )
```
si alguno (o ambos) de los indicadores fuera "falso":
```
0000 0001 ( verdadero )
AND 0000 0000 ( falso )
----------
0000 0000 ( falso )
```
como estos indicadores solo utilizan el bit menos significativo (el bit más a la derecha) para codificar su valor, un AND a nivel de bits es equivalente a un AND lógico convencional.
### OR
el siguiente código empujará una bandera hacia abajo en la pila si el byte tecla es '1' o 'a':
```
.Controlador/tecla DEI ( lee la tecla y la empuja a la pila )
LIT "1 EQU ( ¿es '1'? empuja la bandera a la pila )
.Controlador/tecla DEI ( lee la tecla y empujar a la pila )
LIT "a EQU ( ¿es 'a'? empuja la bandera a la pila )
ORA ( aplica un OR a las banderas en la pila y empuja el resultado en la pila )
```
cuando alguna o ambas banderas son verdaderas, la bandera será verdadera:
```
0000 0001 ( verdadero )
OR 0000 0000 ( falso )
----------
0000 0001 ( verdadero )
```
solo cuando ambas banderas sean falsas, la bandera resultante será falsa.
### EOR
un "exclusive-OR", o también "o-exclusivo", es una operación lógica que tiene un resultado de verdadero solo cuando una u otra entrada es verdadera. si ambas entradas son verdaderas, o si ambas entradas son falsas, el resultado es falso.
basándose en este comportamiento, esta instrucción puede utilizarse para invertir el valor de una bandera utilizando un valor especial en el que el o los bits que queremos invertir se pongan a 1. este tipo de valores se llaman máscaras.
por ejemplo, el siguiente código empujará una bandera correspondiente a que la tecla sea mayor o igual a 20, calculando primero si es menor que 20 y luego invirtiendo el resultado:
```
.Controlador/tecla DEI ( lee la tecla y la empuja a la pila )
#20 LTH ( ¿es menor que 20? empuja la bandera a la pila )
#01 EOR ( invierte el bit más a la derecha de la bandera y empuja el resultado a la pila )
```
cuando la bandera original es verdadera, lo que significa que el valor de la tecla es menor que 20, el EOR la invertirá y la hará falsa: el valor NO es mayor o igual que 20:
```
0000 0001 ( verdadero )
EOR 0000 0001 ( máscara )
----------
0000 0000 ( falso )
```
como podemos ver, debido a que los dos bits de entrada son 1, el bit de salida es 0.
cuando la bandera original es falsa, lo que significa que el valor NO es menor que 20, el EOR lo invertirá y lo hará verdadero: el valor es mayor o igual que 20:
```
0000 0000 ( falso )
EOR 0000 0001 ( máscara )
----------
0000 0001 ( verdadero )
```
observa que la máscara es la misma y el resultado es el valor opuesto de la bandera.
# flujo de control: saltos condicionales
ok, ahora nuestros programas pueden identificar y almacenar en banderas si un valor (como la tecla de teclado leída) es un valor específico, o dentro de algún rango.
¿cómo podemos usar estas banderas para tener comportamientos condicionales en nuestros programas, donde se toman diferentes acciones dependiendo de los resultados?
¡introduzcamos otro conjunto de nuevas instrucciones para que uxn rompa su flujo lineal!
## instrucciones para saltos
* JMP: "jump" o salto incondicional a la dirección de la pila ( direc -- )
* JCN: "conditional jump" o salto condicional: toma una dirección y un valor de la pila, y si el valor no es 00, salta a la dirección; en caso contrario continúa con la siguiente instrucción ( valor direc -- )
en modo byte, las direcciones que utilizan estas instrucciones son de un byte.
estas direcciones de un byte son relativas y pueden considerarse positivas y negativas: indican cuántos bytes hay que saltar en la memoria principal desde la posición actual del contador de programa, ya sea hacia delante (positivo) o hacia atrás (negativo). el rango de estas direcciones relativas es de -128 a 127 inclusive.
en modo corto, las direcciones que toman estas instrucciones son absolutas (es decir, de dos bytes de longitud), pero el valor que toma JCN para decidir sigue siendo un byte.
## runas para direcciones
hay varias runas que se refieren a direcciones y etiquetas. uxnasm las lee y las convierte a los valores binarios correspondientes.
en los días anteriores ya hablamos de algunas de ellas; esta es una recapitulación de las mismas y una introducción de las nuevas:
### direcciones literales
* dirección literal en la página cero: .etiqueta (un byte)
* dirección literal en memoria principal: ;etiqueta (un corto)
* dirección literal relativa en la memoria principal: ,etiqueta (un byte)
### direcciones crudas
existe un conjunto más avanzado de runas que nos permiten usar las direcciones crudas ("raw"), es decir sin LIT o LIT2 implícitos:
* dirección cruda en la página cero: -etiqueta (un byte)
* dirección cruda en la memoria principal: =etiqueta (un corto)
* dirección relativa cruda en la memoria principal: _etiqueta (un byte)
### etiquetas
para definir las etiquetas, utilizamos:
* definición de etiquetas: @etiqueta
* definición de la subetiqueta: &subetiqueta, donde esta subetiqueta será "hije" de la etiqueta previamente definida
y finalmente, para referirse a las etiquetas dentro de nuestro código uxntal, tenemos los siguientes casos:
* para una etiqueta principal: utilizar el nombre de la etiqueta
* para una subetiqueta: utilizar etiqueta/subetiqueta
* para una subetiqueta local: utilizar &subetiqueta
## salto condicional
¡unamos todo esto!
la siguiente subrutina en-controlador ilustra el uso de los saltos, dibujando nuestro sprite solo cuando la tecla que se pulsó fue '1':
```
@en-controlador
.Controlador/tecla DEI ( lee la tecla )
LIT "1 EQU ( ¿es '1'? )
( salta a dibuja-sprite si es el caso )
,&dibuja-sprite JCN
,&fin JMP ( si no, salta al final )
&dibuja-sprite
( fijar coordenadas x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establece la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
( dibuja el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla/sprite DEO
&fin
BRK
```
nota el uso de subetiquetas "dentro" (después) de en-controlador.
también nota como la expresión ,&subetiqueta corresponde a la dirección relativa (,) que se necesita para saltar a esa ubicación en el código nombrado con una subetiqueta local (&).
estas direcciones relativas, de un byte, son utilizadas por JCN o JMP.
## saltos condicionales
el siguiente código ilustra el uso de muchas condiciones: el color del sprite cambia según se pulsen las teclas 1, 2 o 3.
```
@en-controlador
( establecer coordenadas x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establecer direccion del sprite )
;cuadrado .Pantalla/direc DEO2
.Controlador/tecla DEI LIT "1 EQU ( ¿es la tecla '1'? )
,&color-1 JCN ( salta al color-1 si es el caso )
.Controlador/tecla DEI LIT "2 EQU ( ¿es la tecla '2'? )
,&color-2 JCN ( salta al color-2 si es el caso )
.Controlador/tecla DEI LIT "3 EQU ( ¿es la tecla '3'? )
,&color-3 JCN ( salta al color-3 si es el caso )
( en cualquier otro caso, terminar )
BRK
&color-1
( dibujar el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla/sprite DEO
BRK
&color-2
( dibujar sprite en el fondo )
( usando el color 2 para el contorno )
#02 .Pantalla/sprite DEO
BRK
&color-3
( dibujar sprite en el fondo )
( usando el color 3 para el contorno )
#03 .Pantalla/sprite DEO
BRK
BRK
```
observa cómo las condiciones se escriben una tras otra: siempre que una bandera es falsa, JCN permite a uxn continuar con la siguiente instrucción en memoria.
también nota que este código no está optimizado para el tamaño o la velocidad, sino para la legibilidad.
estaría en ti, por ejemplo, realizar una aritmética con el valor de la tecla que se pulsó para calcular el color a asignar al sprite - ¡podrías inspirarte en tu macro IMPRIMIR-DÍGITO del día 1!
# manipulación de la pila
hasta ahora hemos estado usando la pila como un lugar para almacenar operandos de instrucciones y sus resultados, ¡pero aún no hemos usado todo el potencial de este entorno basado en pila!
## instrucciones de pila
uxntal tiene seis instrucciones que actúan sobre los elementos de la pila más cercanos a la parte superior:
* POP: eliminar el elemento superior de la pila ( a -- )
* DUP: duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: "swap" o intercambiar; cambiar el orden de los dos primeros elementos de la pila ( a b -- b a )
* NIP: elimina el segundo elemento superior de la pila ( a b -- b )
* OVR: "over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
en modo corto, POP2, DUP2, SWP2, NIP2, OVR2 y ROT2 realizan las mismas acciones pero utilizando cortos en lugar de bytes.
## ejemplos
vamos a utilizar estas instrucciones de muchas maneras diferentes durante los próximos días.
los siguientes son algunos ejemplos basados en fragmentos de código que ya hemos discutido.
ten en cuenta que el uso de estas instrucciones puede contribuir a que el código sea difícil de seguir o leer, por lo que siempre será una buena idea utilizarlas dentro de macros o tener comentarios en el código explicando lo que está sucediendo :)
### dígito ascii: duplicar e intercambiar
discutimos anteriormente este segmento de código, que empuja una bandera que responde si la tecla que se pulsa tiene un código ascii entre 30 y 39, ambos inclusive (es decir, calcula si un byte tiene un código ascii correspondiente a un dígito decimal)
```
.Controlador/tecla DEI ( lee la tecla y la empuja a la pila)
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
.Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
en lugar de leer la tecla dos veces, podríamos hacerlo una vez y luego usar la instrucción DUP para copiar el valor:
```
.Controlador/tecla DEI DUP ( leer y duplicar la tecla )
```
la pila después de estas instrucciones tendría dos copias del valor de la tecla:
```
tecla tecla <- arriba
```
entonces en nuestro código podemos seguir añadiendo la primera comparación:
```
#2f GTH ( ¿es mayor que 2f? empuja la bandera en la pila )
```
después de esto, la pila se vería como:
```
tecla bandera1 <- arriba
```
para realizar la segunda comparación, necesitamos tener la tecla en la parte superior, no la bandera.
¿cómo lo conseguimos? así es, utilizando un SWP:
```
SWP ( poner la tecla en la parte superior )
```
ahora la pila se ve así:
```
bandera1 tecla <- arriba
```
finalmente podemos proceder a la comparación y al AND:
```
#3a LTH ( ¿es menor que 3a? empuja la bandera en la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
terminando con una pila que solo tiene el resultado:
```
resultado <- arriba
```
el código completo se leería como:
```
.Controlador/tecla DEI DUP ( lee y duplica la tecla )
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
SWP ( poner la tecla en la parte superior )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
el primer código se ensambla en 13 bytes y este se ensambla en 12 bytes. quizá no haya demasiada diferencia en ese aspecto.
sin embargo, la ventaja más significativa es que esta nueva rutina ahora necesita su entrada empujada hacia abajo en la pila solo al principio.
en el caso que acabamos de discutir la entrada es la tecla que se presiona, pero podríamos fácilmente tener como entrada cualquier otro valor de la pila.
esto implica que podríamos escribir la rutina como una macro:
```
%?DÍGITO-ASCII { DUP #2f GTH SWP #3a LTH AND } ( byte -- bandera )
```
y utilizarla con el byte que queramos:
```
#30 ?DÍGITO-ASCII ( empuja 01 hacia abajo en la pila )
#20 ?DÍGITO-ASCII ( empuja 00 hacia abajo en la pila )
.Controlador/tecla DEI ?DÍGITO-ASCII ( empuja la bandera correspondiente a la pila )
```
### duplicados para los condicionales
otro caso anterior en el que repetimos muchas lecturas de la tecla del teclado fue cuando usamos las condicionales múltiples.
podríamos reescribirlo usando varios DUPs y POPs:
```
@en-controlador
( establecer coordenadas x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establecer dirección del sprite )
;cuadrado .Pantalla/direc DEO2
.Controlador/tecla DEI ( leer tecla )
DUP LIT "1 EQU ( ¿es la tecla '1'? )
,&color-1 JCN ( salta al color-1 si es el caso )
DUP LIT "2 EQU ( ¿es la tecla '2'? )
,&color-2 JCN ( salta al color-2 si es el caso )
DUP LIT "3 EQU ( ¿es la tecla '3'? )
,&color-3 JCN ( salta al color-3 si es el caso )
( en cualquier otro caso, termina )
POP
BRK
&color-1
( dibujar el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla/sprite DEO
POP
BRK
&color-2
( dibujar sprite en el fondo )
( usando el color 2 para el contorno )
#02 .Pantalla/sprite DEO
POP
BRK
&color-3
( dibujar sprite en el fondo )
( usando el color 3 para el contorno )
#03 .Pantalla/sprite DEO
POP
BRK
BRK
```
¿puedes decir por qué necesitamos todos esos POPs?
pista: compara el estado final de la pila con y sin las instrucciones POP.
¡en los próximos días veremos más usos y ejemplos de manipulación de la pila!
# botón del controlador
la última cosa que discutiremos hoy es el uso del byte de botón del controlador en la computadora varvara.
como ya hemos mencionado, la principal diferencia aquí es que este byte mantiene el estado de 8 botones en cada uno de sus bits.
dependiendo de nuestra aplicación, podríamos necesitar permitir que algunos de estos botones sean presionados al mismo tiempo.
en ese caso, ¿cómo podríamos aislar cada uno de los bits para comprobar su estado individualmente?
conoce las máscaras AND a nivel de bits.
## máscara AND
una máscara AND es un valor especial que utilizaremos para mantener o perder bits específicos de otro valor dado, como el byte de botón del controlador.
en nuestra máscara AND, estableceremos como 1 los bits en las posiciones en las que queramos mantener el valor de los bits de entrada. las posiciones en las que los bits de la máscara sean 0 se convertirán a 0 en la salida.
por ejemplo, digamos que queremos ver si el bit número 4, que corresponde al botón Arriba, está encendido o apagado, independientemente del estado de los otros botones.
nuestra máscara AND tendrá un 1 en el bit número 4 (de derecha a izquierda y empezando por el 0) y 0 en el resto:
```
0001 0000: 10
```
¿qué pasaría si se pulsa el botón A (tecla Ctrl), con su estado codificado en el bit 0 y nada más?
```
0000 0001 ( botón )
AND 0001 0000 ( máscara )
----------
0000 0000 ( resultado )
```
¿qué ocurre si se pulsa el botón Arriba?
```
0001 0000 ( botón )
AND 0001 0000 ( máscara )
----------
0001 0000 ( resultado )
```
¿y si se pulsan tanto Arriba como Ctrl?
```
0001 0001 ( botón )
AND 0001 0000 ( máscara )
----------
0001 0000 ( resultado )
```
así vemos cómo la máscara nos permite aislar efectivamente el bit que nos importa, independientemente del estado de los otros bits.
aplicar esta máscara sería tan sencillo como escribir:
```
#10 AND ( aplicar máscara 0001 0000 )
```
## ejemplo: dibujar con flechas y Ctrl
=> ./img/screenshot_uxn-draw-with-keyboard.png captura de pantalla de un posible resultado de la ejecución del siguiente programa; muestra un rastro dibujado con cuadrados rellenos o delineados.
el siguiente programa uxntal permite dibujar utilizando las teclas de las flechas y la tecla Ctrl (botón A).
las flechas mueven la posición de un sprite, y al pulsar Ctrl mientras se mueve, lo dibujará con los colores inversos en relleno y trazo.
observa el uso de las máscaras AND, los saltos condicionales y algunas operaciones de la pila.
```
( dibujar-con-teclado.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &píxel $1 &sprite ]
|80 @Controlador [ &vector $2 &botón $1 &tecla $1 ]
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( asignar vector del controlador )
;en-controlador .Controlador/vector DEO2
( establecer coordenadas iniciales x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establece la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
BRK
@en-controlador ( -> )
.Controlador/botón DEI DUP ( leer y duplicar el byte del botón )
#01 AND ( aislar el bit 0, correspondiente a Ctrl )
,&relleno JCN ( si el bit no es 0, saltar al relleno; si es 0, continuar )
&contorno
#01 .Pantalla/sprite DEO ( dibujar contorno )
,&comprobar-flechas JMP ( continuar con comprobar-flechas )
&relleno
#04 .Pantalla/sprite DEO ( dibujar relleno )
&comprobar-flechas
( usar el byte del botón de la pila )
DUP #10 AND ( aislar el bit 4, correspondiente a Arriba )
,&arriba JCN ( saltar si no es 0 )
DUP #20 AND ( aísla el bit 5, correspondiente a Abajo )
&abajo JCN ( salta si no es 0 )
DUP #40 AND ( aislar el bit 6, correspondiente a Izquierda )
,&izquierda JCN ( salta si no es 0 )
DUP #80 AND ( aísla el bit 7, correspondiente a Derecha )
&derecha JCN ( salta si no es 0 )
POP BRK
&arriba
.Pantalla/y DEI2 #0008 SUB2 .Pantalla/y DEO2 ( disminuye y )
POP
BRK
&abajo
.Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 ( incrementa y )
POP
BRK
&izquierda
.Pantalla/x DEI2 #0008 SUB2 .Pantalla/x DEO2 ( disminuye x )
POP
BRK
&derecha
.Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 ( incrementa x )
POP
BRK
BRK
( sprite )
@cuadrado ff81 8181 8181 81ff
```
algunas posibilidades para que practiques:
* modificar el código para que también responda a que pulses más de una flecha al mismo tiempo.
* convertir los incrementos y decrementos de las coordenadas en macros que tomen la dirección del puerto como entrada y realizar una operación equivalente. estas dos líneas deberían funcionar usando la misma macro:
```
.Pantalla/x INCREMENTO
.Pantalla/y INCREMENTO
```
recuerde que .Pantalla/x es una dirección literal en la página cero, es decir, empuja un byte correspondiente a la dirección de la subetiqueta Pantalla/x :)
# practica posibilidades
¡aquí tienes otras ideas para que practiques con lo que hemos cubierto hoy!
* dibujar un controlador virtual que muestre cuáles de sus botones, mapeados a teclas del teclado, están siendo presionados
* crear una especie de máquina de escribir que dibuje diferentes símbolos y mueva el cursor de dibujo dependiendo de la tecla que se haya pulsado
* dibujar un personaje que cambie su estado según la tecla que se haya pulsado. ¿tal vez utilizar múltiples tiles para dibujarlo?
* crea un simple tablero de tres-en-raya para dos jugadores: una tecla dibuja una X, otra dibuja una O y las flechas permiten elegir la casilla a dibujar.
¡ten en cuenta que para un suave movimiento interactivo puede ser mejor utilizar el vector de pantalla que se llama 60 veces por segundo!
¡lo cubriremos en profundidad en el próximo día del tutorial!
# instrucciones del día 3
¡estas son todas las instrucciones uxntal de las que hemos hablado hoy!
## instrucciones de comparación
* EQU: empuja 01 hacia abajo en la pila si los dos primeros elementos de la pila son iguales, o empuja 00 en caso contrario ( a b -- a==b )
* NEQ: empuja 01 hacia abajo en la pila si los dos primeros elementos de la pila no son iguales, o empuja 00 en caso contrario ( a b -- a!=b )
* GTH: empuja 01 hacia abajo en la pila si el primer elemento es mayor que el segundo, o empuja 00 en caso contrario ( a b -- a>b )
* LTH: empuja 01 hacia abajo en la pila si el primer elemento es menor que el segundo, o empuja 00 en caso contrario ( a b -- a<b )
## lógica a nivel de bits
* AND: realiza un AND a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a^b )
## saltos
* JMP: salto incondicional a la dirección de la pila ( direc -- )
* JCN: salto condicional: toma una dirección y un valor de la pila y si el valor no es 00, salta a la dirección; en caso contrario continúa con la siguiente instrucción ( valor direc -- )
## pila
* POP: eliminar el elemento superior de la pila ( a -- )
* DUP: duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: "swap" o intercambiar; cambiar el orden de los dos primeros elementos de la pila ( a b -- b a )
* NIP: elimina el segundo elemento superior de la pila ( a b -- b )
* OVR: "over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
# día 4
en el {tutorial de uxn día 4} cubrimos el uso del vector de pantalla para crear animaciones, ya sean interactivas o no.
¡también exploramos las posibilidades de usar "variables" en uxntal que pueden ayudarnos a crear programas más elaborados!
¡antes de entrar en materia, te invito a seguir explorando y también a tomar un descanso!
¡mantente en sintonía!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)