compudanzas/src/tutorial_de_uxn_día_5.gmo

787 lines
27 KiB
Plaintext

# tutorial de uxn: día 5, el ratón y chucherías de uxntal
lang=es en->{uxn tutorial day 5}
esta es la quinta sección del {tutorial de uxn}! aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo mantener.
¡también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos!
# el dispositivo del ratón
el dispositivo del ratón en el ordenador varvara es similar al dispositivo controlador en varios aspectos: tiene un vector que es llamado con cualquier evento del ratón (cambio de estado de los botones, movimiento, movimiento de desplazamiento) y un par de bytes para comprobar su estado.
adicionalmente, tiene un par de cortos correspondientes a las coordenadas x,y del puntero del ratón.
vemos este dispositivo definido en uxntal de la siguiente manera:
```
|90 @Ratón [ &vector $2 &x $2 &y $2 &estado $1 &pad $3 &despx $2 &despy $2 ]
```
## byte de estado
el byte de estado codifica el estado de activación/desactivación de hasta 8 botones del ratón; uno por bit.
contando de derecha a izquierda, el primer bit corresponde al primer botón del ratón, el segundo bit al segundo botón del ratón, y así sucesivamente.
normalmente, en un ratón de tres botones, el primer botón es el izquierdo, el segundo el del medio y el tercero el derecho.
utilizando un ratón de tres botones como este, tendríamos ocho valores posibles para el byte de estado, por ejemplo :
* 00 cuando no se presiona ninguno de los botones
* 01 cuando solo se presiona el primer botón
* 02 cuando solo se presiona el segundo botón
* 04 cuando solo se presiona el tercer botón
nota que al igual que el dispositivo controlador, este sistema nos permite comprobar si hay varios botones presionados a la vez:
* 03 cuando el primer y el segundo botón están presionados
* 05 cuando se presionan el primer y el tercer botón
* 06 cuando se presionan el segundo y el tercer botón
* 07 cuando se presionan los tres botones
recuerda que podemos utilizar las máscaras AND, tal y como se introdujo en el {tutorial de uxn día 3}, para aislar y evaluar por separado cualquiera de estos bits.
## cortos de desplazamiento
el dispositivo del ratón tiene un par de cortos para indicar si el ratón se está desplazando.
despy indicará un desplazamiento vertical con un valor "positivo" cuando se mueve hacia arriba (o en realidad, "lejos del usuario") y un valor "negativo" cuando se mueve hacia abajo (o "hacia el usuario")
* 0001 cuando se desplaza hacia arriba
* ffff cuando se desplaza hacia abajo
* 0000 cuando no se desplaza
del mismo modo, despx indicará un desplazamiento horizontal
* 0001 cuando se desplaza hacia la derecha
* ffff cuando se desplaza hacia la izquierda
* 0000 cuando no se desplaza
dependiendo del dispositivo, los valores pueden ser mayores que 0001 o menores que ffff dependiendo de la velocidad de desplazamiento.
## vector ratón
el vector del ratón se disparará en cualquiera de los siguientes eventos:
* se ha presionado un botón
* se suelta un botón
* el ratón se ha movido
* la rueda del ratón se ha movido
## hola ratón
¡empecemos a dibujar!
el siguiente es un ejemplo simple que ilustra el uso de los siguientes elementos:
* vector del ratón
* coordenadas x e `y` del ratón
* estado del ratón (presionado o no presionado)
combinado con un condicional y el dibujo de un sprite.
dibuja nuestro cuadrado en la posición del ratón, cambiando su color cuando se presiona cualquier botón del ratón.
=> ./img/screenshot_uxn-draw-with-mouse.png captura de pantalla que muestra un dibujo realizado con el programa: líneas onduladas compuestas por cuadrados superpuestos de dos colores diferentes
```
( hola-ratón.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 ]
|90 @Ratón [ &vector $2 &x $2 &y $2 &estado $1 &pad $3 &despx $2 &despy $2 ]
( init )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer vector del ratón )
;en-ratón .Ratón/vector DEO2
( establecer dirección del sprite )
;cuadrado .Pantalla/direc DEO2
BRK
@en-ratón ( -> )
.Ratón/x DEI2 .Pantalla/x DEO2
.Ratón/y DEI2 .Pantalla/y DEO2
( salta si se presiona algún botón )
.Ratón/estado DEI ,&presionado JCN
( dibujar sprite usando el color 2 y 0 en el fondo )
#02 .Pantalla/sprite DEO
BRK
&presionado
( dibujar sprite usando el color 1 y 0 en el fondo )
#01 .Pantalla/sprite DEO
BRK
@cuadrado [ ff81 8181 8181 81ff ]
```
## hola puntero
tal vez hayas notado que, antes de hoy, el puntero del ratón ha desaparecido al entrar en la ventana de uxnemu.
¿cómo podríamos programar y replicar su comportamiento en uxntal?
podríamos utilizar una estrategia similar a la que hicimos para animar un sprite:
* borrar el sprite de la posición anterior
* actualizar la posición
* dibujar el sprite en la nueva posición
este procedimiento puede ocurrir cada vez que se dispara el vector del ratón.
podemos utilizar un conjunto de variables en la página cero para almacenar la posición del puntero, de forma que tengamos una forma de limpiar el sprite en las coordenadas anteriores del ratón.
adicionalmente, aquí tenemos los datos de un sprite de 1bpp de un puntero de ratón, tomados de los ejemplos de uxn:
```
@puntero_icn [ 80c0 e0f0 f8e0 1000 ]
```
### el programa
este es un programa que logra dibujar el puntero en la pantalla
```
( hola-puntero.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 ]
|90 @Ratón [ &vector $2 &x $2 &y $2 &estado $1 &pad $3 &despx $2 &despy $2 ]
( página cero )
|0000
@puntero [ &x $2 &y $2 ]
( init )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer el vector del ratón )
;en-ratón .Ratón/vector DEO2
( establecer dirección del sprite )
;puntero_icn .Pantalla/direc DEO2
BRK
@en-ratón ( -> )
( envía la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( borrar el sprite del primer plano )
#40 .Pantalla/sprite DEO
( actualizar la posición del puntero )
.Ratón/x DEI2 .puntero/x STZ2
.Ratón/y DEI2 .puntero/y STZ2
( enviar la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( dibujar sprite con color 2 en primer plano )
#4a .Pantalla/sprite DEO
BRK
@puntero_icn [ 80c0 e0f0 f8e0 1000 ]
```
nótese que dibuja el puntero en primer plano y que utiliza 'a' en el nibble bajo del byte del sprite: esto implica que utilizará el color 2 para dibujar la forma del puntero y dibujará con transparencia el resto del tile. (ver dibujar sprites de 1bpp en el {tutorial de uxn día 2})
este modo de mezcla te permitiría dibujar cosas en el plano de fondo y que el puntero las cubra solo con su forma, y no con todo el cuadrado del tile.
¡te invito a que lo pruebes!
¡dibuja algunas cosas en el fondo y ve cómo se ve el puntero, tal y como está ahora. luego reemplaza el byte del sprite del puntero con, por ejemplo, 42 para ver la diferencia!
### algunos problemas
ahora, podemos ver que el programa funciona, pero está inundando la subrutina en-ratón con mucho código.
eso es aun mas cierto si consideramos que solo estamos dibujando el puntero y no tomando ninguna otra acción, como responder a los botones.
crear una macro para todo este código podría ser posible, pero también poco práctico debido a la cantidad de código.
¿tal vez podríamos tener un JMP a otra sección del programa, que al final tiene otro JMP para c vnm a la posición correspondiente en la subrutina en-ratón?
de hecho, eso es casi lo que haremos, pero con un elemento adicional de uxn: ¡su pila de retorno!
# la pila de retorno
hasta ahora, "la pila" que hemos estado utilizando es lo que se suele llamar "la pila de trabajo".
uxn, al igual que otros sistemas tipo forth, tiene otra pila del mismo tamaño, "la pila de retorno".
¿por qué se llama así?
la idea es que se utiliza normalmente para introducir en ella direcciones de memoria de programa.
estas direcciones corresponderían a ubicaciones a las que quisiéramos eventualmente "regresar".
## preparando nuestro programa
en primer lugar, vamos a mover nuestra subrutina de dibujo de punteros a otra etiqueta de nuestro programa:
```
@dibuja-puntero ( -- )
( enviar la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( borrar el sprite del primer plano )
#40 .Pantalla/sprite DEO
( actualizar la posición del puntero )
.Ratón/x DEI2 .puntero/x STZ2
.Ratón/y DEI2 .puntero/y STZ2
( enviar la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( dibujar sprite con color 2 en primer plano )
#4a .Pantalla/sprite DEO
BRK
```
notemos que podríamos unir las acciones de actualizar la posición del puntero y enviarla a la pantalla, usando un par de DUP2:
```
( actualizar la posición del puntero y enviarla a la pantalla )
.Ratón/x DEI2 DUP2 .puntero/x STZ2 .Pantalla/x DEO2
.Ratón/y DEI2 DUP2 .puntero/y STZ2 .Pantalla/y DEO2
```
esto dejaría nuestra subrutina en-ratón vacía:
```
@en-ratón( -> )
BRK
```
### usando saltos normales
con lo que ya sabemos, y dependiendo de la posición de dibuja-puntero con respecto a en-ratón, podríamos hacer un salto relativo:
```
@en-ratón ( -> )
,dibuja-puntero JMP
&retorno
( algo más aquí )
BRK
```
o un salto absoluto:
```
@en-ratón ( -> )
;dibuja-puntero JMP2
&retorno
( algo más aquí )
BRK
```
el detalle esta en que si queremos que ocurra algo más después de dibujar el puntero dentro de en-ratón, no podemos atrás fácilmente.
al final de nuestra subrutina dibuja-puntero, necesitaríamos "saltar hacia atrás" así:
```
;en-ratón/retornar JMP2
```
funcionaría, pero no es la mejor forma.
¡conozcamos una alternativa, la instrucción "saltar y almacenar"!
# salta y almacena o "jump and stash"
la instrucción de salto y stash, JSR, hace lo mismo que JMP (saltar incondicionalmente a la dirección presente en la pila de trabajo), con la acción adicional de empujar hacia abajo en la pila de retorno la dirección absoluta de lo que sería la siguiente instrucción en memoria después de JSR.
en el modo normal, JSR toma una dirección relativa (un byte) de la pila de trabajo, y en el modo corto, JSR2 toma una dirección absoluta.
en ambos casos, JSR empujará una dirección absoluta (2 bytes) hacia abajo en la pila de retorno.
## salto relativo
por ejemplo, nuestros saltos podrían reescribirse de la siguiente manera. en el caso de un salto relativo:
```
@en-ratón ( -> )
,dibuja-puntero JSR
( algo más aquí )
BRK
```
## salto absoluto
y en el caso de un salto absoluto:
```
@en-ratón ( -> )
;dibuja-puntero JSR2
( algo más aquí )
BRK
```
## retornando
JSR está empujando la "dirección de retorno" hacia la pila de retorno.
ahora la pregunta es: ¿cómo tomamos la dirección de esa pila, para saltar allí?
¡tendríamos que usar el "modo de retorno"!
# el modo de retorno
similar al modo corto, el modo de retorno en uxn consiste en activar una bandera binaria en el byte que codifica una instrucción.
el modo de retorno se codifica en el 7mo bit de un byte de instrucción, contando de derecha a izquierda.
siempre que se active esta bandera, uxn realizará la instrucción dada pero utilizando como fuentes el contenido de la pila de retorno en lugar del contenido de la pila de trabajo.
en uxntal, indicamos que queremos activar esta bandera añadiendo la letra 'r' al final de un mnemónico de instrucción.
como cada uno de los modos es un bit independiente, es posible combinarlos, por ejemplo, activando el modo de retorno y el modo corto utilizando el sufijo '2r'.
## un breve ejemplo de pila de retorno
por ejemplo, el siguiente código empujaría dos números hacia abajo en la pila de retorno, los sumaría y empujaría el resultado de nuevo en la pila de retorno:
```
LITr 01 LITr 02 ADDr
```
o, combinando los modos corto y de retorno en la instrucción LIT:
```
LIT2r 0102 ADDr
```
ahora volvamos a nuestros saltos :)
## saltando a retorno
como ya hemos discutido, JMP nos permitirá saltar incondicionalmente a la dirección dada en la parte superior de la pila (de trabajo).
JSR o JSR2 empujan hacia abajo en la pila de retorno la dirección absoluta de la siguiente instrucción, un corto, para que eventualmente podamos retornar allí.
¿cómo podemos saltar incondicionalmente a esa dirección absoluta que está presente en la pila de retorno?
¡exactamente!
¡activando el modo de retorno en la instrucción JMP!
adicionalmente, como las direcciones empujadas por JSR son cortos, necesitamos activar el modo corto también:
```
JMP2r ( saltar a la dirección absoluta en la parte superior de la pila de retorno )
```
en muchos programas uxntal verás esta instrucción escrita como una macro, RTN (retornar o "return"):
```
%RTN { JMP2r }
```
podemos terminar una subrutina usando esta macro para "retornar" a la posición en el programa después del JSR correspondiente.
## ejemplo completo usando subrutinas
este es el programa hola-puntero.tal, pero utilizando dibuja-puntero como subrutina que se " invoca" con JSR2 y que termina con RTN:
```
( hola-puntero.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 ]
|90 @Ratón [ &vector $2 &x $2 &y $2 &estado $1 &pad $3 &despx $2 &despy $2 ]
( macros )
%RTN { JMP2r }
( página cero )
|0000
@puntero [ &x $2 &y $2 ]
( init )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer el vector del ratón )
;en-ratón .Ratón/vector DEO2
( establecer dirección del sprite )
;puntero_icn .Pantalla/direc DEO2
BRK
@en-ratón ( -> )
;dibuja-puntero JSR2 ( o ,dibuja-puntero JSR )
( algo más )
BRK
@dibuja-puntero ( -- )
( envía la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( borrar el sprite del primer plano )
#40 .Pantalla/sprite DEO
( actualizar la posición del puntero )
.Ratón/x DEI2 .puntero/x STZ2
.Ratón/y DEI2 .puntero/y STZ2
( enviar la posición del puntero a la pantalla )
.puntero/x LDZ2 .Pantalla/x DEO2
.puntero/y LDZ2 .Pantalla/y DEO2
( dibujar sprite con color 2 en primer plano )
#4a .Pantalla/sprite DEO
RTN
@puntero_icn [ 80c0 e0f0 f8e0 1000 ]
```
observe que la etiqueta dibuja-puntero va acompañada de la notación de estado de la pila ( -- ) para indicar que, en este caso, no consume ni produce contenidos desde o hacia la pila de trabajo.
también note como esta subrutina termina con un RTN (JMP2r) que indica que el flujo del programa volverá a la posición posterior a la llamada de la subrutina.
## notas sobre subrutinas como "funciones"
tengamos en cuenta que una posible forma de enviar "argumentos" a una subrutina sería empujarlos hacia abajo en la pila de trabajo antes de llamarla.
además, una posible forma de que una subrutina "devuelva" sus resultados sería que los empujara hacia abajo en la pila de trabajo.
estos elementos pueden entonces ser consumidos desde la pila de trabajo después de regresar de la subrutina.
puede haber otros casos en los que el uso de "variables" tendría un sentido más lógico o legible para pasar argumentos y resultados.
# almacena, no saltes
habiendo introducido la pila de retorno y el modo de retorno, se nos abre otro mundo de posibilidades: también podemos utilizar la pila de retorno como una pila adicional y temporal, para almacenar algunos valores mientras operamos con otros.
para lograr esto, uxn tiene una instrucción llamada STH, "stash" o almacenar.
¡esta es la última instrucción de uxn que teníamos en mente cubrir en este tutorial! :)
## STH
STH toma un valor de la pila de trabajo y lo empuja hacia abajo en la pila de retorno.
en modo retorno, STHr hace lo contrario: toma un valor de la pila de retorno y lo empuja hacia abajo en la pila de trabajo.
y, como habrás previsto, en el modo corto esta instrucción opera moviendo cortos en lugar de bytes.
## ejemplo: línea horizontal
el siguiente es un ejemplo de subrutina que muestra algunas posibilidades de la pila y el modo de retorno.
se trata de una subrutina que dibuja una línea horizontal de una longitud dada (de 1 a 255 píxeles, es decir, utilizando un byte), partiendo de una coordenada x dada (corto) y utilizando una coordenada `y` dada (corto).
estos parámetros se dan como argumentos en la pila de trabajo.
la subrutina utiliza la pila de retorno para "almacenar" uno de estos argumentos mientras trabaja con los otros.
además, ¡tiene un bucle de trabajo! escrito en una de las varias formas de implementarlo :)
el estado de las pilas de trabajo (pt) y de retorno (pr) se muestra en los comentarios después de casi cada paso. la parte superior de las pilas se encuentra a su derecha.
un signo de intercalación (^) después de un nombre de valor indica que corresponde a un corto.
```
@dibuja-linea-horizontal ( x^ y^ longitud -- )
( inicio )
( pt: x^ y^ longitud / pr: )
( almacenar la longitud en la pila de retorno )
STH ( pt: x^ y^ / pr: longitud )
( fijar las coordenadas iniciales )
.Pantalla/y DEO2 ( pt: x^ / pr: longitud )
.Pantalla/x DEO2 ( pt: / pr: longitud )
( recuperar la longitud de la pila de retorno )
STHr ( pt: longitud / pr: )
( iniciar el conteo )
#00 ( pt: longitud 00 / pr: )
&bucle
( almacenar la longitud y el conteo )
STH2 ( pt: / pr: longitud conteo )
( dibujar píxel con color 2 )
#02 .Pantalla/píxel DEO
( incrementar x )
.Pantalla/x DEI2 INC2 .Pantalla/x DEO2
( recupera la longitud y el conteo )
STH2r ( pt: longitud conteo / pr: )
( incrementa el conteo para obtener el nuevo conteo )
INC ( pt: longitud conteo / pr: )
( duplicar longitud y conteo, comparar y saltar )
DUP2 ( pt: longitud conteo / pr: )
NEQ ( pt: longitud conteo bandera / pr: )
,&bucle JCN ( pt: longitud conteo / pr: )
POP2 ( pt: / pr: )
RTN
```
### llamando
para llamar a la subrutina, podrías hacer algo como lo siguiente:
```
#0008 ( empujar x inicial )
.Pantalla/altura DEI2 MITAD2 ( empujar y )
#ff ( empujar longitud de la línea )
;dibuja-linea-horizontal JSR2 ( llamar subrutina )
```
### notas
nota que en esta subrutina específica, el uso de STH2 y STH2r después de la subetiqueta &bucle no es realmente necesario: las operaciones entre estas instrucciones sí tocan la pila de trabajo pero después la dejan como estaba.
sin embargo, muestra cómo podemos usar estas instrucciones para tener una pila de trabajo limpia sin que otros valores interfieran.
### posibles ejercicios
* hacer que la subrutina dibuje una línea hecha de sprites en lugar de píxeles individuales
* modificar la subrutina para que pueda recibir como argumento (en la pila de trabajo) el color de los sprites o píxeles
* modificar la subrutina para que pueda recibir como argumento (en la pila de trabajo) la dirección del sprite a dibujar
* reescribir la subrutina para que utilice una longitud de pequeño tamaño para la línea, en lugar de un byte.
# el modo mantener
el último elemento básico de uxntal que nos queda por cubrir es su tercer modo para las instrucciones: el modo mantener o "keep".
el modo mantener se codifica en el 8vo bit de un byte de instrucción, contando de derecha a izquierda.
en uxntal, indicamos que queremos activar esta bandera añadiendo la letra 'k' al final de un mnemónico de instrucción.
siempre que se active esta bandera, uxn realizará la instrucción dada pero "manteniendo" los valores originales en la pila correspondiente.
en otras palabras, en el modo mantener los elementos no serán consumidos de la pila, pero los resultados correspondientes serán empujados hacia abajo en la pila.
el modo mantener puede combinarse con los otros modos, para un total de ocho combinaciones posibles de modos.
## modo mantener en aritmética
sabemos lo que hace el siguiente código uxntal; empuja 01 y 02 hacia abajo en la pila, suma ambos elementos y empuja el resultado (03) hacia abajo en la pila:
```
#01 #02 ( pt: 01 02 )
ADD ( pt: 03 )
```
compáralo con lo que ocurre al usar ADDk en su lugar:
```
#01 #02 ( pt: 01 02 )
ADDk ( pt: 01 02 03 )
```
la suma se realiza y el resultado es empujado, pero los operandos se dejan en la pila.
puede ser difícil pensar en general en un uso para esto, ¡pero "mantenlo" en mente!
### módulo
en realidad, si recuerdas, en el {tutorial de uxn día 4} compartí contigo un par de macros para realizar una operación de módulo:
```
%MOD { DUP2 DIV MUL SUB } ( a b -- a%b )
%MOD2 { OVR2 OVR2 DIV2 MUL2 SUB2 } ( a b -- a%b )
```
dije entonces que había un conjunto más optimizado y que lo discutiríamos más adelante.
¡ahora es ese momento!
en primer lugar, analicemos lo que ocurre con MOD. está calculando lo que se escribiría en notación infija de la siguiente manera, suponiendo que la barra (/) indica una división entera:
```
a - ( a/b )*b
```
pruebe, por ejemplo, sustituir "a" por 7 y "b" por 3; el módulo o remanente debería ser 1
* a/b nos da el resultado de la división entera: 7/3 es 2
* (a/b)*b intenta obtener 'a' de nuevo, pero posiblemente "falla" debido al resto que se perdió en la división: 2*3 es 6
* a - (a/b)*b calcula cuál es la diferencia entre 'a' y el resultado obtenido: 7-6 es 1
en nuestra macro original, lo que ocurre es lo siguiente:
```
#07 #03 ( pt: 07 03 )
DUP2 ( pt: 07 03 07 03 )
DIV ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
```
¿ves la posibilidad de introducir el modo mantener?
si te fijas bien, verás que el DUP2 está ahí para no perder los valores originales en la división y así poder realizar la multiplicación y la resta después.
pero entonces, ¿cómo podemos realizar la división sin perder sus operandos y sin usar DUP2?
¡así es!
DUP2 DIV es equivalente a... ¡DIVk! ¡una división que no pierde sus operandos!
```
#07 #03 ( pt: 07 03 )
DIVk ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
```
¡de esta manera, nuestra macro puede tener un byte menos!
podemos generalizar este comportamiento para el modo corto y obtener el conjunto óptimo de macros que mencioné anteriormente:
```
%MOD { DIVk MUL SUB }
%MOD2 { DIV2k MUL2 SUB2 }
```
## modo mantener y comparaciones
el modo mantener puede ser útil cuando hacemos comparaciones y no queremos perder los valores originales.
por ejemplo, en nuestra subrutina dibuja-línea-horizontal, teníamos el siguiente conjunto de líneas de código:
```
( duplicar longitud y el conteo, comparar y saltar )
DUP2 ( pt: longitud conteo longitud conteo / pr: )
NEQ ( pt: longitud conteo bandera / pr: )
,&bucle JCN ( pt: longitud conteo / pr: )
```
verás que aquí, como en el caso DIVk anterior, el DUP2 está ahí solo para asegurarse de que la longitud y el recuento no se pierden al realizar NEQ.
por lo tanto, podríamos reemplazar DUP2 NEQ con NEQk:
```
( duplicar la longitud y el conteo, comparar y saltar )
NEQk ( pt: longitud conteo bandera / pr: )
,&bucle JCN ( pt: longitud conteo / pr: )
```
## el modo mantener y la pila de retorno
a veces queremos almacenar una copia de un valor que usaremos en el momento.
sin el modo mantener, escribiríamos:
```
DUP STH ( duplicar y almacenar )
```
con el modo mantener, podemos escribir:
```
STHk ( almacenar y mantener )
```
del mismo modo, habrá ocasiones en las que queramos recuperar una copia de un valor de la pila de retorno.
para ello, podemos escribir:
```
STHkr ( recuperar una copia de la pila de retorno )
```
## más y más modo mantener
se siguen encontrando nuevos e interesantes usos para el modo mantener :)
¡no dudes en compartirlos con nosotres!
# más ejercicios
con lo que ya hemos cubierto, y en caso de que quieras algunas ideas, aquí hay algunas cosas que ahora deberían ser más fáciles de intentar construir:
## sprites de varios tiles
dibujar un sprite formado por varios tiles es un proceso que puede beneficiarse del uso de subrutinas: ten una subrutina que reciba un par de coordenadas x,y en la pila de trabajo cuando sea llamada y úsala para dibujar los tiles en las posiciones correspondientes relativas a esas coordenadas.
## herramienta de dibujo
¡muchas posibilidades aquí!
tal vez comienza con dibujar solo cuando se presiona un botón. cambia el color o el "pincel" dependiendo del botón que se presiona.
puedes tener diferentes "modos" seleccionables: tal vez cambien el pincel que estás usando, la forma en que el pincel se comporta (por ejemplo, ¿en espejo, caleidoscopio?) o las formas que se dibujan.
considera cómo seleccionarías esos modos: ¿botones en pantalla? ¿teclas del teclado? ¿acordes con los botones del ratón?
ten en cuenta que puedes cambiar el vector de un dispositivo durante el tiempo de ejecución: podrías tener una subrutina en-ratón diferente dependiendo del modo que hayas seleccionado :)
¿cómo podrías utilizar la rueda del ratón como ayuda para dibujar?
## y más...
básicamente, las puertas para las aplicaciones visuales interactivas en el ordenador varvara están completamente abiertas ahora para ti :)
¿crearás juegos? ¿pequeñas aplicaciones, útiles o no? ¿un instrumento para visuales en vivo? ¿programas dirigidos a dispositivos de mano específicos?
algunas cosas pueden parecer difíciles de construir, pero afortunadamente, por ahora no hay nada más en el funcionamiento de la máquina que no hayamos cubierto ya.
puedes ir poco a poco, paso a paso, practicando tu manejo de la pila y ejercitando el cerebro postfijo, y llegarás a donde quieras :)
¡nos encantaría ver lo que creas!
# instrucciones del día 5
¡estas son las instrucciones uxntal que hemos discutido hoy! ¡con estas, las hemos cubierto todas!
* JSR: salta incondicionalmente a la dirección de la pila de trabajo, empujando hacia abajo en la pila de retorno la dirección de la siguiente instrucción en memoria
* STH: toma un valor de la pila de trabajo y lo empuja hacia abajo en la pila de retorno. en el modo de retorno, hace lo contrario.
# día 6
en el {tutorial de uxn día 6} hablamos de cómo podemos integrar todo lo que hemos cubierto para crear subrutinas y programas aún más complejos para el ordenador varvara.
¡basamos nuestra discusión en una recreación del clásico juego pong!
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar los sprites de varios tiles y para comprobar las colisiones.
primero, te invito a tomar un descanso!
después, ¡sigue explorando y comparte tus descubrimientos!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)