compudanzas/src/tutorial_de_uxn_día_2.gmo

1186 lines
46 KiB
Plaintext

# tutorial de uxn: día 2, la pantalla
lang=es en->{uxn tutorial day 2}
¡esta es la segunda sección del {tutorial de uxn}!
en esta sección vamos a empezar a explorar los aspectos visuales de la computadora varvara ¡hablamos sobre los aspectos fundamentales del dispositivo de pantalla para que podamos empezar a dibujar en ella!
también discutiremos el trabajo con el modo corto o "short" (2 bytes) junto a los de un solo byte en uxntal.
si todavía no lo has hecho te recomiendo que leas las sección anterior en el {tutorial de uxn día 1}
# ¿dónde están tus cortos?
antes de pasar a dibujar en la pantalla, tenemos que hablar de bytes y cortos :)
## bytes y cortos
aunque uxn es un ordenador que trabaja de forma nativa con palabras de 8 bits (bytes), nos encontramos en varias ocasiones que la cantidad de datos que es posible almacenar en un byte no es suficiente.
cuando utilizamos 8 bits, podemos representar 256 valores diferentes (2 a la potencia de 8). en cualquier momento dado, un byte almacenará solo uno de esos posibles valores.
en la sección anterior, hablamos de un caso en el que esta cantidad no es suficiente en uxn: el número de bytes que alberga la memoria principal, 65536.
ese número corresponde a los valores que se pueden representar usando dos bytes, o 16 bits, o un "corto": 2 a la potencia de 16. esa cantidad también se conoce como 64KB, donde 1KB corresponde a 1024 o 2 a la potencia de 10.
además de expresar direcciones en la memoria principal, hoy veremos otro caso en el que 256 valores no siempre son suficientes: las coordenadas x e y de los píxeles de nuestra pantalla.
para estos y otros casos, usar cortos en lugar de bytes será el camino a seguir.
¿cómo los tratamos?
## el modo corto
contando de derecha a izquierda, el 6º bit de un byte que codifica una instrucción para el ordenador uxn es una bandera binaria que indica si el modo corto está activado o no.
siempre que el modo corto esté activado, es decir, cuando ese bit sea 1 en lugar de 0, la cpu uxn realizará la instrucción dada por los 5 primeros bits (el opcode) pero utilizando pares de bytes en lugar de bytes individuales.
el byte que esté más adentro de la pila será el byte "alto" del corto y el byte que esté más cerca de la parte superior de la pila será el byte "bajo" del corto.
en uxntal, indicamos que queremos poner esta bandera añadiendo el dígito '2' al final de una instrucción mnemónica.
¡veamos algunos ejemplos!
## ejemplo del modo corto
### LIT2
en primer lugar, recapitulemos. el siguiente código empujará el número 02 hacia abajo en la pila, luego empujará el número 30 (hexadecimal) hacia abajo en la pila y finalmente los sumará, dejando el número 32 en la pila:
```
#02 #30 ADD
```
este sería el estado final de la pila:
```
32 <- arriba
```
en el día anterior mencionamos que la runa hexadecimal literal (#) es una abreviatura de la instrucción LIT. por lo tanto, podríamos haber escrito nuestro código de la siguiente manera:
```
LIT 02 LIT 30 ADD ( código ensamblado: 80 02 80 30 18 )
```
ahora, si añadimos el sufijo '2' a la instrucción LIT, podríamos escribir en su lugar:
```
LIT2 02 30 ADD ( código ensamblado: a0 02 30 18 )
```
en lugar de empujar un byte, LIT2 está empujando el corto (dos bytes) que sigue en la memoria, hacia abajo en la pila.
podemos utilizar la runa hexadecimal literal (#) con un corto (cuatro nibbles) en lugar de un byte (dos nibbles) y funcionará como una abreviatura de LIT2:
```
#0230 ADD
```
### ADD2
ahora veamos que pasa con la instrucción ADD cuando usamos el modo corto.
¿cuál sería el estado de la pila después de ejecutar este código?
```
#0004 #0008 ADD
```
¡así es! la pila tendrá los siguientes valores, porque estamos empujando 4 bytes hacia abajo en la pila, sumando (ADD) los dos más cercanos a la parte superior y empujando el resultado hacia abajo en la pila.
```
00 04 08 <- arriba
```
ahora, comparemos con lo que ocurre con el ADD2:
```
#0004 #0008 ADD2
```
en este caso estamos empujando los mismos 4 bytes hacia abajo en la pila, pero ADD2 está haciendo las siguientes acciones:
* toma el elemento superior de la pila (08) y lo almacena como el byte bajo del primer corto
* toma el nuevo elemento superior de la pila (00) y lo almacena como el byte alto del primer corto, que ahora es 0008
* toma el nuevo elemento superior de la pila (04) y lo almacena como el byte bajo del segundo corto
* toma el nuevo elemento superior de la pila (00) y lo almacena como el byte alto del segundo corto, que ahora es 0004
* suma los dos cortos (0004 + 0008), obteniendo un resultado de 000c
* empuja el byte alto del resultado (00) hacia abajo en la pila
* empuja el byte bajo del resultado (0c) hacia abajo en la pila
la pila acaba teniendo el siguiente aspecto:
```
00 0c <- arriba
```
puede que no necesitemos pensar demasiado en las manipulaciones por byte de las operaciones aritméticas, porque normalmente podemos pensar que están haciendo la misma operación que antes, pero utilizando pares de bytes en lugar de bytes individuales. su orden no cambia realmente.
en cualquier caso, es útil tener en cuenta cómo funcionan para algunos comportamientos que podríamos necesitar más adelante :)
### DEO2, DEI, DEI2
hablemos ahora de la instrucción DEO ("device out" o salida de dispositivo) de la que hablamos el día anterior, ya que su modo corto implica algo especial.
la instrucción DEO necesita un valor (1 byte) para salir y una dirección entrada/salida (1 byte) en la pila, para poder sacar ese valor en esa dirección.
```
DEO ( valor dirección -- )
```
esta instrucción tiene una contrapartida: DEI ("device in" o entrada de dispositivo).
la instrucción DEI toma una dirección de entrada/salida (1 byte) de la pila y va a empujar hacia abajo en la pila el valor (1 byte) que corresponde a la lectura de esa entrada.
```
DEI ( dirección -- valor )
```
¿qué crees que harán DEO2 y DEI2?
en el caso del modo corto de DEO y DEI, el aspecto corto se aplica al valor de salida o entrada y no a la dirección.
recuerda que las 256 direcciones de entrada/salida ya están cubiertas usando un solo byte, por lo que usar un corto para ellas sería redundante: el byte alto sería siempre 00.
considerando esto, los siguientes son los comportamientos que podemos esperar:
la instrucción DEO2 necesita un valor (1 corto) para salir y una dirección entrada/salida (1 byte) en la pila, para poder sacar ese valor a esa dirección. por lo tanto necesita un total de 3 bytes en la pila para operar.
por otro lado, la instrucción DEI2 necesita una dirección entrada/salida (1 byte) en la pila y empujará hacia abajo en la pila el valor (1 corto) que corresponde a esa entrada.
en la siguiente sección veremos algunos ejemplos en los que podremos utilizar estas instrucciones.
el puerto de 'escritura' del dispositivo de la consola que utilizamos la última vez tiene un tamaño de 1 byte, por lo que no podemos utilizar estas nuevas instrucciones de forma significativa con él.
# dispositivo de sistema y colores
el dispositivo del sistema es el dispositivo varvara con una dirección de 00. sus puertos de salida (que comienzan en la dirección 08) corresponden a tres cortos diferentes: uno llamado rojo (r), el otro verde (g) y el último azul (b).
en los ejemplos uxntal podemos ver sus etiquetas definidas de la siguiente manera:
```
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
```
ignoraremos los primeros elementos por el momento y nos centraremos en los componentes de color.
## colores del sistema
el dispositivo de pantalla varvara solo puede mostrar un máximo de cuatro colores a la vez.
estos cuatro colores se denominan color 0, color 1, color 2 y color 3.
cada color tiene una profundidad total de 12 bits: 4 bits para el componente rojo, 4 bits para el componente verde y 4 bits para el componente azul.
podemos definir los valores de estos colores fijando los valores r, g, b del dispositivo del sistema.
podemos escribirlo de la siguiente manera:
```
( hola-pantalla.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
```
¿cómo podríamos leer lo que significan esos cortos literales?
podemos leer cada uno de los colores verticalmente, de izquierda a derecha:
* el color 0 sería rojo: 2, verde: 0, azul: 2 (#220022 en notación de color hexadecimal, púrpura oscuro)
* el color 1 sería rojo: c, verde: 1, azul: c (#cc11cc en notación de color hexadecimal, magenta)
* el color 2 sería rojo: e, verde: c, azul: e (#eeccee en notación de color hexadecimal, rosa claro)
* el color 3 sería rojo: 9, verde: 0, azul: 5 (#990055 en notación de color hexadecimal, rojo oscuro)
si ejecutamos el programa ahora veremos una pantalla de color púrpura oscuro, en lugar de negro como lo que teníamos antes.
prueba cambiar los valores del color 0, es decir, la primera columna, y mira lo que pasa :)
# el dispositivo de pantalla
como recapitulación: mencionamos que el dispositivo de pantalla solo puede mostrar cuatro colores diferentes en un momento dado y que estos colores están numerados del 0 al 3. fijamos estos colores usando los puertos correspondientes en el dispositivo del sistema.
¡ahora hablemos del dispositivo de pantalla y empecemos a usarlo!
## puertos de dispositivo
en los programas uxntal para el ordenador varvara puedes encontrar las etiquetas correspondientes a los puertos de este dispositivo de la siguiente manera:
```
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &píxel $1 &sprite $1 ]
```
estos son los puertos en formato de lista:
* vector (2 bytes)
* anchura de la pantalla (2 bytes)
* altura de la pantalla (2 bytes)
* auto (1 byte)
* coordenada x (2 bytes)
* coordenada y (2 bytes)
* dirección de memoria (2 bytes)
* píxel (1 byte)
* sprite (1 byte)
hablaremos sobre el corto vector en el {tutorial de uxn día 4} y sobre el byte auto en el {tutorial de uxn día 6}.
## primer plano y plano de fondo
el dispositivo de pantalla tiene dos capas superpuestas del mismo tamaño, el primer plano y el plano de fondo.
lo que se dibuje sobre la capa del primer plano cubrirá todo lo que se dibuje en la misma posición en la capa del plano de fondo.
al principio, la capa del primer plano es completamente transparente: un proceso de mezcla alfa asegura que podamos ver la capa de fondo.
# dibujando un píxel
la primera y más sencilla forma de dibujar en la pantalla es dibujando un solo píxel.
para hacer esto necesitamos establecer un par de coordenadas x,y donde queremos que se dibuje el píxel y necesitamos establecer el byte 'píxel' a un valor específico para realizar realmente el dibujo.
## estableciendo las coordenadas
las coordenadas x,y siguen las convenciones comunes a otros programas de gráficos por ordenador:
* x comienza en 0 a la izquierda y aumenta hacia la derecha de la pantalla
* y comienza en 0 en la parte superior y aumenta hacia la parte inferior de la pantalla
si quisiéramos dibujar un píxel en coordenadas (8, 8), estableceríamos sus coordenadas de esta manera:
```
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
```
alternativamente, podríamos empujar primero los valores de las coordenadas hacia abajo en la pila y la salida de ellos después:
```
#0008 #0008 .Pantalla/x DEO2 .Pantalla/y DEO2
```
una pregunta para ti: si quisiéramos establecer las coordenadas como (x: 4, y: 8), ¿cuál de los cortos en el código anterior deberías cambiar por 0004?
## estableciendo el color
el envío de un único byte a .Pantalla/píxel realizará el dibujo en la pantalla.
el nibble alto de ese byte, es decir, el dígito hexadecimal de la izquierda, determinará la capa en la que dibujaremos:
* 0: dibujar un solo píxel en el fondo (fn)
* 4: dibujar un solo píxel en el primer plano (pp)
el nibble inferior del byte, es decir, el dígito hexadecimal de la derecha, determinará su color.
las 8 posibles combinaciones del byte 'píxel' que tenemos para dibujar un píxel son:
+ <table>
+ <tr><th>píxel</th><th>color</th><th>capa</th></tr>
+ <tr><td class="num">00</td><td class="num">0</td><td>fn</td></tr>
+ <tr><td class="num">01</td><td class="num">1</td><td></td></tr>
+ <tr><td class="num">02</td><td class="num">2</td><td></td></tr>
+ <tr><td class="num">03</td><td class="num">3</td><td></td></tr>
+ <tr><td class="num">40</td><td class="num">0</td><td>pp</td></tr>
+ <tr><td class="num">41</td><td class="num">1</td><td></td></tr>
+ <tr><td class="num">42</td><td class="num">2</td><td></td></tr>
+ <tr><td class="num">43</td><td class="num">3</td><td></td></tr>
+ </table>
& capa del fondo:
& * 00: dibujar el píxel con el color 0
& * 01: dibujar un píxel con el color 1
& * 02: dibujar el píxel con el color 2
& * 03: dibujar el píxel con el color 3
&
& capa del primer plano:
& * 40: dibujar un píxel con el color 0
& * 41: dibujar el píxel con el color 1
& * 42: dibujar el píxel con el color 2
& * 43: dibujar el píxel con el color 3
## hola píxel
¡probemos todo juntos! el siguiente código dibujará un píxel con el color 1 en la capa del primer plano, en las coordenadas (8,8):
```
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
#41 .Pantalla/píxel DEO
```
el programa completo se vería de la siguiente manera:
```
( hola-píxel.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 ]
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( dibujar un píxel en la pantalla )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
#41 .Pantalla/píxel DEO ( capa de primer plano, color 1 )
```
¡wuju!
recuerda que puedes usar F1 para cambiar de nivel de zoom y F3 para hacer capturas de pantalla de tus bocetos :)
## hola píxeles
los valores que establecemos en las coordenadas x e `y` permanecen ahí hasta que los sobrescribimos.
por ejemplo, podemos dibujar múltiples píxeles en una línea horizontal, estableciendo la coordenada `y` solo una vez:
```
( establecer coordenadas y )
#0008 .Pantalla/y DEO2
( dibujar 6 píxeles en una línea horizontal )
#0008 .Pantalla/x DEO2
#41 .Pantalla/píxel DEO
#0009 .Pantalla/x DEO2
#41 .Pantalla/píxel DEO
#000a .Pantalla/x DEO2
#41 .Pantalla/píxel DEO
#000b .Pantalla/x DEO2
#41 .Pantalla/píxel DEO
#000c .Pantalla/x DEO2
#41 .Pantalla/píxel DEO
#000d .Pantalla/x DEO2
#11 .Pantalla/píxel DEO
```
nótese que tenemos que establecer el color para cada píxel que dibujamos; esa operación señala el dibujo y tiene que repetirse.
podemos definir una macro para que este proceso sea más fácil de escribir:
```
%DIBUJAR-PÍXEL { #41 .Pantalla/píxel DEO } ( -- )
```
## leyendo y manipulando coordenadas
todavía no cubriremos las estructuras repetitivas, pero esta es una buena oportunidad para empezar a alinear nuestro código hacia eso.
aunque las coordenadas x e `y` del dispositivo de pantalla están pensadas como salidas, también podemos leerlas como entradas.
por ejemplo, para leer la coordenada x, empujando su valor hacia abajo en la pila, podemos escribir:
```
.Pantalla/x DEI2
```
teniendo en cuenta esto, ¿se puede saber qué haría este código?
```
.Pantalla/x DEI2
#0001 ADD2
.Pantalla/x DEO2
```
¡lo has adivinado bien, espero!
* la primera línea empuja la coordenada x como un corto, hacia abajo en la pila.
* la segunda línea empuja el número 0001, lo añade al corto anterior y empuja el resultado a la pila.
* la tercera línea toma el resultado de la pila y lo escribe como la nueva coordenada x.
ese conjunto de instrucciones incrementa la coordenada x de la pantalla por uno :)
parecen útiles, así que también podríamos guardarlos como una macro:
```
%INC-X { .Pantalla/x DEI2 #0001 ADD2 .Pantalla/x DEO2 } ( -- )
```
aquí hay otra pregunta para ti: ¿cómo escribirías una macro ADD-X que te permita incrementar la coordenada x en una cantidad arbitraria que pongas en la pila?
```
%ADD-X { } ( incremento -- )
```
## instrucción INC
añadir 1 al valor de la parte superior de la pila es tan común que hay una instrucción para conseguirlo utilizando menos espacio, INC:
```
INC ( a -- a+1 )
```
INC toma el valor de la parte superior de la pila, lo incrementa por uno y lo empuja de vuelta.
en el caso del modo corto, INC2 hace lo mismo pero incrementando un corto en lugar de un byte.
nuestra macro para incrementar la coordenada x podría entonces escribirse como sigue:
```
%INC-X { .Pantalla/x DEI2 INC2 .Pantalla/x DEO2 } ( -- )
```
## hola píxeles usando macros
usando estas macros que definimos arriba, nuestro código puede terminar viéndose de la siguiente forma:
```
( hola-píxeles.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 ]
( macros )
%DIBUJAR-PÍXEL { #41 .Pantalla/píxel DEO } ( -- )
%INC-X { .Pantalla/x DEI2 INC2 .Pantalla/x DEO2 } ( -- )
( programa princpal )
|0100
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer las coordenadas iniciales x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( dibujar 6 píxeles en una línea horizontal )
DIBUJAR-PÍXEL INC-X
DIBUJAR-PÍXEL INC-X
DIBUJAR-PÍXEL INC-X
DIBUJAR-PÍXEL INC-X
DIBUJAR-PÍXEL INC-X
DIBUJAR-PÍXEL
```
agradable, ¿no? ¡las operaciones ahora se ven más claras! y si quisiéramos tener esta línea disponible para usarla en otras posiciones, podríamos definir una macro para ella:
```
%DIBUJAR-LÍNEA { } ( -- )
```
¡intenta escribiendo la macro y utilizándola en diferentes posiciones de la pantalla!
# dibujando sprites
¡ahora, veremos cómo aprovechar el soporte incorporado para "sprites" en el dispositivo de pantalla varvara para dibujar muchos píxeles a la vez!
el dispositivo de pantalla varvara nos permite utilizar y dibujar tiles de 8x8 píxeles, también llamados sprites.
hay dos modos posibles: 1bpp (1 bit por píxel) y 2bpp (2 bits por píxel).
los mosaicos o "tiles" de 1bpp usan dos colores y se codifican usando 8 bytes; usar un bit por píxel significa que solo podemos codificar si ese píxel está usando un color o el otro.
los tiles de 2bpp utilizan cuatro colores y se codifican utilizando 16 bytes; el uso de dos bits por píxel significa que podemos codificar cuál de los cuatro colores disponibles tiene el píxel.
almacenaremos y accederemos a estos tiles desde la memoria principal.
## dibujando sprites de 1bpp
un tile de 1bpp consiste en un conjunto de 8 bytes que codifican el estado de sus 8x8 píxeles.
cada byte corresponde a una fila del tile y cada bit de una fila corresponde al estado de un píxel de izquierda a derecha: puede estar "encendido" (1) o "apagado" (0).
## codificando un sprite de 1bpp
por ejemplo, podríamos diseñar un azulejo que corresponda al contorno de un cuadrado de 8x8, activando o desactivando sus píxeles en consecuencia.
``` el contorno de un cuadrado marcado con 1s y su interior marcado con 0s
11111111
10000001
10000001
10000001
10000001
10000001
10000001
11111111
```
como cada una de las filas es un byte, podemos codificarlas como números hexadecimales en lugar de binarios.
vale la pena notar (o recordar) que los grupos de cuatro bits corresponden a un nibble y cada combinación posible en un nibble puede ser codificada como un dígito {hexadecimal}.
basándonos en eso, podríamos codificar nuestro cuadrado de la siguiente manera:
``` el contorno de un cuadrado marcado con 1s y sus interiores marcados con 0s, y su equivalente en hexadecimal
11111111: ff
10000001: 81
10000001: 81
10000001: 81
10000001: 81
10000001: 81
10000001: 81
11111111: ff
```
## almacenando el sprite
en uxntal, necesitamos etiquetar y escribir en la memoria principal los datos correspondientes al sprite. escribimos los bytes que van de arriba a abajo del sprite:
```
@cuadrado ff81 8181 8181 81ff
```
tengamos en cuenta que aquí no estamos utilizando la runa hexadecimal literal (#): queremos utilizar los bytes en bruto en la memoria y no necesitamos empujarlos hacia abajo en la pila.
para asegurarse de que estos bytes no son leídos como instrucciones por la cpu uxn, es una buena práctica precederlos con la instrucción BRK: esto interrumpirá la ejecución del programa antes de llegar aquí, dejando a uxn en un estado en el que está esperando entradas.
un poco más adelante revisaremos dónde colocar este código.
## configurando la dirección
para dibujar el sprite, necesitamos enviar su dirección en memoria al dispositivo de pantalla y necesitamos asignar un byte de sprite apropiado.
para lograr esto, escribimos lo siguiente:
```
;cuadrado .Pantalla/direc DEO2
```
¡una nueva runa está aquí! la runa de dirección absoluta literal (;) nos permite empujar hacia abajo en la pila la dirección absoluta de la etiqueta dada en la memoria principal.
una dirección absoluta tendría 2 bytes de longitud y se introduce en la pila con LIT2, incluida por el ensamblador cuando se utiliza esta runa.
como la dirección es de 2 bytes, la imprimimos con DEO2.
## configurando el color
de forma similar a lo que ya vimos con el píxel, el envío de un byte a .Pantalla/sprite realizará el dibujo en la pantalla.
### nibble alto de sprite para 1bpp
el nibble alto del byte 'sprite' determinará la capa en la que dibujaremos, igual que cuando dibujábamos usando el byte 'píxel'.
sin embargo, en este caso tendremos otras posibilidades: podemos invertir el sprite en el eje horizontal (x) o en el vertical (y).
los ocho valores posibles de este nibble alto de 'sprite', utilizados para dibujar un sprite de 1bpp, son:
+ <table>
+ <tr><th>sprite alto</th><th>capa</th><th>inv-y</th><th>inv-x</th></tr>
+ <tr><td class="num">0</td><td>fn</td><td>no</td><td>no</td></tr>
+ <tr><td class="num">1</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">2</td><td></td><td>sí</td><td>no</td></tr>
+ <tr><td class="num">3</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">4</td><td>pp</td><td>no</td><td>no</td></tr>
+ <tr><td class="num">5</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">6</td><td></td><td>sí</td><td>no</td></tr>
+ <tr><td class="num">7</td><td></td><td></td><td>sí</td></tr>
+ </table>
& fondo:
& * 0: dibujar un sprite de 1bpp, con la orientación original
& * 1: dibujar un sprite de 1bpp, rotado horizontalmente
& * 2: dibujar un sprite de 1bpp, rotado verticalmente
& * 3: dibujar un sprite de 1bpp, rotado horizontalmente y verticalmente
&
& primer plano:
& * 4: dibujar un sprite de 1bpp, con la orientación original
& * 5: dibujar un sprite de 1bpp, rotado horizontalmente
& * 6: dibujar un sprite de 1bpp, rotado verticalmente
& * 7: dibujar un sprite de 1bpp, rotado horizontalmente y verticalmente
si se observa con atención, se puede ver algún patrón: cada bit del nibble alto del byte del sprite corresponde a un aspecto diferente de este comportamiento.
lo siguiente muestra el significado de cada uno de estos bits en el nibble alto, suponiendo que estamos contando los bits del byte de derecha a izquierda y de 0 a 7:
+ <table>
+ <tr><th>bit</th><th>bandera</th><th>0</th><th>1</th></tr>
+ <tr><td class="num">7</td><td>modo</td><td>1bpp</td><td>2bpp</td></tr>
+ <tr><td class="num">6</td><td>capa</td><td>fn</td><td>pp</td></tr>
+ <tr><td class="num">5</td><td>inv-y</td><td>no</td><td>sí</td></tr>
+ <tr><td class="num">4</td><td>inv-x</td><td>no</td><td>sí</td></tr>
+ </table>
& * bit 7: modo (0 es 1bpp, 1 es 2bpp)
& * bit 6: capa (0 es plano de fondo, 1 es primer plano)
& * bit 5: inversión-y
& * bit 4: inversión-x
como por ejemplo, cuando el nibble alto del 'sprite' es 0, que en binario es 0000, significa que todas las banderas están apagadas: por eso dibuja un sprite de 1bpp (0) en el fondo (0), que no esta invertido ni verticalmente (0) ni horizontalmente (0).
un nibble alto de 1, es decir, 0001 en binario, tiene la última bandera encendida, por eso se invierte horizontalmente, y así sucesivamente.
### nibble bajo de sprite para 1bpp
el nibble bajo del byte 'sprite' determinará los colores que se utilizan para dibujar los píxeles "encendido" (1) y "apagado" (0) de los tiles.
+ <table>
+ <tr><th></th><th colspan="2">colores para:</th></tr>
+ <tr><th>sprite bajo</th><th>1</th><th>0</th></tr>
+ <tr><td class="num">0</td><td>borrar</td><td>borrar</td></tr>
+ <tr><td class="num">1</td><td>1</td><td>0</td></tr>
+ <tr><td class="num">2</td><td>2</td><td>0</td></tr>
+ <tr><td class="num">3</td><td>3</td><td>0</td></tr>
+ <tr><td class="num">4</td><td>0</td><td>1</td></tr>
+ <tr><td class="num">5</td><td>1</td><td>nada</td></tr>
+ <tr><td class="num">6</td><td>2</td><td>1</td></tr>
+ <tr><td class="num">7</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">8</td><td>0</td><td>2</td></tr>
+ <tr><td class="num">9</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">a</td><td>2</td><td>nada</td></tr>
+ <tr><td class="num">b</td><td>3</td><td>2</td></tr>
+ <tr><td class="num">c</td><td>0</td><td>3</td></tr>
+ <tr><td class="num">d</td><td>1</td><td>3</td></tr>
+ <tr><td class="num">e</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">f</td><td>3</td><td>nada</td></tr>
+ </table>
& * 0: borrar tile
& * 1: "encendido" con el color 1, "apagado" con el color 0
& * 2: "encendido" con el color 2, "apagado" con el color 0
& * 3: "encendido" con el color 3, "apagado" con el color 0
& * 4: "encendido" con el color 0, "apagado" con el color 1
& * 5: "encendido" con el color 1, "apagado" sin color
& * 6: "encendido" con el color 2, "apagado" con el color 1
& * 7: "encendido" con el color 3, "apagado" con el color 1
& * 8: "encendido" con el color 0, "apagado" con el color 2
& * 9: "encendido" con el color 1, "apagado" con el color 2
& * a: "encendido" con color 2, "apagado" sin color
& * b: "encendido" con el color 3, "apagado" con el color 2
& * c: "encendido" con color 0, "apagado" con color 3
& * d: "encendido" con el color 1, "apagado" con el color 3
& * e: "encendido" con el color 2, "apagado" con el color 3
& * f: "encendido" con el color 3, "apagado" sin color
por ejemplo, esto significa que si establecemos el nibble bajo de 'sprite' con el valor de 6, varvara dibujará el sprite utilizando el color 2 para los pixeles "encendidos" y el color 1 para los pixeles "apagados".
notemos que un 0 en el este nibble borrará el tile.
además, 5, 'a' y 'f' en el nibble bajo dibujarán los píxeles que están "encendidos" pero dejarán los que están "apagados" como están: esto le permitirá dibujar sobre algo que ha sido dibujado antes, sin borrarlo completamente.
no te preocupes si esto no está teniendo mucho sentido, ¡veamos un ejemplo!
## hola sprite
el siguiente programa dibujará nuestro sprite una vez:
```
( hola-sprite.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 ]
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer coordenadas x,y )
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establecer la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
( dibujar el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla/sprite DEO
BRK
@cuadrado ff81 8181 8181 81ff
```
## hola sprites
=> ./img/screenshot_uxn-tiles.png captura de pantalla del resultado del programa, mostrando 16 cuadrados coloreados con diferentes combinaciones de contorno y relleno.
el siguiente código dibujará nuestro sprite cuadrado con las 16 combinaciones de color:
```
( hola-sprites.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 ]
( macros )
%INIT-X { #0008 .Pantalla/x DEO2 } ( -- )
%INIT-Y { #0008 .Pantalla/y DEO2 } ( -- )
%8ADD-X { .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 } ( -- )
%8ADD-Y { .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 } ( -- )
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer coordenadas iniciales x,y )
INIT-X INIT-Y
( establecer dirección del sprite )
;cuadrado .Pantalla/direc DEO2
#00 .Pantalla/sprite DEO 8ADD-X
#01 .Pantalla/sprite DEO 8ADD-X
#02 .Pantalla/sprite DEO 8ADD-X
#03 .Pantalla/sprite DEO 8ADD-Y
INIT-X
#04 .Pantalla/sprite DEO 8ADD-X
#05 .Pantalla/sprite DEO 8ADD-X
#06 .Pantalla/sprite DEO 8ADD-X
#07 .Pantalla/sprite DEO 8ADD-Y
INIT-X
#08 .Pantalla/sprite DEO 8ADD-X
#09 .Pantalla/sprite DEO 8ADD-X
#0a .Pantalla/sprite DEO 8ADD-X
#0b .Pantalla/sprite DEO 8ADD-Y
INIT-X
#0c .Pantalla/sprite DEO 8ADD-X
#0d .Pantalla/sprite DEO 8ADD-X
#0e .Pantalla/sprite DEO 8ADD-X
#0f .Pantalla/sprite DEO
BRK
@cuadrado ff81 8181 8181 81ff
```
observemos que en este caso, tenemos un par de macros 8ADD-X y 8ADD-Y para incrementar cada coordenada por 0008: ese es el tamaño del tile.
## experimentos de inversión
como el sprite cuadrado es simétrico, no podemos ver el efecto de invertirlo.
aquí están los sprites de la roca y del personaje de {darena}:
```
@piedra 3c4e 9ffd f962 3c00
@personaje 3c7e 5a7f 1b3c 5a18
```
te invito a que intentes usar estos sprites para explorar cómo dibujarlos invertidos en diferentes direcciones.
## dibujando sprites de 2bpp
en los sprites de 2bpp cada píxel puede tener uno de los cuatro colores posibles.
podemos pensar que, para asignar estos colores, codificaremos uno de los cuatro estados en cada uno de los píxeles del sprite.
cada uno de estos estados puede codificarse con una combinación de dos bits. a estos estados se les puede asignar diferentes combinaciones de los cuatro colores del sistema utilizando los valores apropiados en el byte 'sprite' de la pantalla.
un solo tile de 2bpp de 8x8 píxeles necesita 16 bytes para ser codificada. estos bytes se ordenan según un formato llamado chr.
## codificando un sprite de 2bpp
para demostrar esta codificación, vamos a remezclar nuestro cuadrado de 8x8, asignando uno de los cuatro estados posibles (0, 1, 2, 3) a cada uno de los píxeles:
``` un cuadrado de 8x8 construido con los dígitos 0 y 1 en el borde y 2 y 3 en el interior
00000001
03333311
03333211
03332211
03322211
03222211
01111111
11111111
```
podemos pensar en cada uno de estos dígitos como un par de bits: 0 es 00, 1 es 01, 2 es 10 y 3 es 11.
de esta manera, podríamos pensar en nuestro sprite de la siguiente manera:
``` un cuadrado de 8x8 construido con un par de bits entre paréntesis, correspondientes a la representación binaria de cada uno de los estados
(00) (00) (00) (00) (00) (00) (00) (01)
(00) (11) (11) (11) (11) (11) (01) (01)
(00) (11) (11) (11) (11) (10) (01) (01)
(00) (11) (11) (11) (10) (10) (01) (01)
(00) (11) (11) (10) (10) (10) (01) (01)
(00) (11) (10) (10) (10) (10) (01) (01)
(00) (01) (01) (01) (01) (01) (01) (01)
(01) (01) (01) (01) (01) (01) (01) (01)
```
la codificación chr requiere una interesante manipulación de esos bits: podemos pensar que cada par de bits tiene un bit alto en la izquierda y un bit bajo en la derecha.
separamos nuestro tile en dos cuadrados diferentes, uno para los bits altos y otro para los bits bajos:
``` dos cuadrados de 8x8 correspondientes a la división del cuadrado anterior en sus bits altos y bajos
00000000 00000001
01111100 01111111
01111100 01111011
01111100 01110011
01111100 01100011
01111100 01000011
00000000 01111111
00000000 11111111
```
ahora podemos pensar en cada uno de estos cuadrados como sprites de 1bpp y codificarlos en hexadecimal como lo hicimos antes:
``` los dos cuadrados 8x8 anteriores con su correspondiente codificación hexadecimal
00000000: 00 00000001: 01
01111100: 7c 01111111: 7f
01111100: 7c 01111011: 7b
01111100: 7c 01110011: 73
01111100: 7c 01100011: 63
01111100: 7c 01000011: 43
00000000: 00 01111111: 7f
00000000: 00 11111111: ff
```
## almacenando el sprite
para escribir este sprite en la memoria, primero almacenamos el cuadrado correspondiente a los bits bajos y luego el cuadrado correspondiente a los bits altos. cada uno de ellos, de arriba a abajo:
```
@nuevo-cuadrado 017f 7b73 6343 7fff 007c 7c7c 7c7c 0000
```
podemos establecer esta dirección en el dispositivo de pantalla igual que antes:
```
;nuevo-cuadrado .Pantalla/direc DEO2
```
el dispositivo de pantalla tratará esta dirección como un sprite 2bpp cuando usemos el byte de color apropiado.
## configurando el color
¡veamos cómo utilizar el sprite byte para dibujar tiles de 2bpp!
### nibble alto de sprite para 2bpp
el nibble alto para los sprites de 2bpp nos permitirá elegir la capa que queremos que se dibuje y la dirección de rotación.
los ocho valores posibles para este nibble son:
+ <table>
+ <tr><th>sprite alto</th><th>capa</th><th>inv-y</th><th>inv-x</th></tr>
+ <tr><td class="num">8</td><td>fn</td><td>no</td><td>no</td></tr>
+ <tr><td class="num">9</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">a</td><td></td><td>sí</td><td>no</td></tr>
+ <tr><td class="num">b</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">c</td><td>pp</td><td>no</td><td>no</td></tr>
+ <tr><td class="num">d</td><td></td><td></td><td>sí</td></tr>
+ <tr><td class="num">e</td><td></td><td>sí</td><td>no</td></tr>
+ <tr><td class="num">f</td><td></td><td></td><td>sí</td></tr>
+ </table>
& fondo:
& * 8: dibujar un sprite de 2bpp, con la orientación original
& * 9: dibujar un sprite de 2bpp, invertido horizontalmente
& * a: dibujar un sprite 2bpp, invertido verticalmente
& * b: dibujar un sprite de 2bpp, invertido horizontal y verticalmente
&
& primer plano:
& * c: dibujar un sprite de 2bpp, con la orientación original
& * d: dibujar un sprite de 2bpp, invertido horizontalmente
& * e: dibujar un sprite de 2bpp, invertido verticalmente
& * f: dibujar un sprite de 2bpp, invertido horizontal y verticalmente
notemos que estos ocho valores tienen todos un bit más a la izquierda en 1: este bit señala que vamos a dibujar un sprite de 2bpp. los otros tres bits del nibble se comportan como se ha descrito anteriormente en el caso de 1bpp.
### sprite de nibble bajo para 2bpp
el nibble bajo nos permitirá elegir entre muchas combinaciones de colores asignados a cada uno de los diferentes estados de los píxeles:
+ <table>
+ <tr><th></th><th colspan="4">colores para:</th></tr>
+ <tr><th>sprite bajo</th><th>0</th><th> 1</th><th>2</th><th>3</th></tr>
+ <tr><td class="num">0</td><td>0</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">1</td><td>0</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">2</td><td>0</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">3</td><td>0</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">4</td><td>1</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">5</td><td>nada</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">6</td><td>1</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">7</td><td>1</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">8</td><td>2</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">9</td><td>2</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">a</td><td>nada</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">b</td><td>2</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">c</td><td>3</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">d</td><td>3</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">e</td><td>3</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">f</td><td>nada</td><td>3</td><td>1</td><td>2</td></tr>
+ </table>
& * 0: colores 0, 0, 1, 2
& * 1: colores 0, 1, 2, 3
& * 2: colores 0, 2, 3, 1
& * 3: colores 0, 3, 1, 2
& * 4: colores 1, 0, 1, 2
& * 5: colores ninguno, 1, 2, 3
& * 6: colores 1, 2, 3, 1
& * 7: colores 1, 3, 1, 2
& * 8: colores 2, 0, 1, 2
& * 9: colores 2, 1, 2, 3
& * a: colores ninguno, 2, 3, 1
& * b: colores 2, 3, 1, 2
& * c: colores 3, 0, 1, 2
& * d: colores 3, 1, 2, 3
& * e: colores 3, 2, 3, 1
& * f: colores ninguno, 3, 1, 2
## ¡hola nuevos sprites!
=> ./img/screenshot_uxn-tiles-2bpp.png captura de pantalla de la salida del programa, mostrando 16 cuadrados coloreados con diferentes combinaciones de contorno y relleno.
el siguiente código mostrará nuestro sprite en las 16 diferentes combinaciones de color. hay un poco de margen entre las baldosas para poder apreciarlas mejor:
```
( hola-sprite-2bpp.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 ]
( macros )
%INIT-X { #0008 .Pantalla/x DEO2 } ( -- )
%INIT-Y { #0008 .Pantalla/y DEO2 } ( -- )
%cADD-X { .Pantalla/x DEI2 #000c ADD2 .Pantalla/x DEO2 } ( -- )
%cADD-Y { .Pantalla/y DEI2 #000c ADD2 .Pantalla/y DEO2 } ( -- )
( programa principal )
|0100
( establecer los colores del sistema )
#2eef .Sistema/r DEO2
#1eb8 .Sistema/g DEO2
#1e2e .Sistema/b DEO2
( establecer coordenadas iniciales x,y )
INIT-X INIT-Y
( establecer dirección del sprite )
;nuevo-cuadrado .Pantalla/direc DEO2
#80 .Pantalla/sprite DEO cADD-X
#81 .Pantalla/sprite DEO cADD-X
#82 .Pantalla/sprite DEO cADD-X
#83 .Pantalla/sprite DEO cADD-Y
INIT-X
#84 .Pantalla/sprite DEO cADD-X
#85 .Pantalla/sprite DEO cADD-X
#86 .Pantalla/sprite DEO cADD-X
#87 .Pantalla/sprite DEO cADD-Y
INIT-X
#88 .Pantalla/sprite DEO cADD-X
#89 .Pantalla/sprite DEO cADD-X
#8a .Pantalla/sprite DEO cADD-X
#8b .Pantalla/sprite DEO cADD-Y
INIT-X
#8c .Pantalla/sprite DEO cADD-X
#8d .Pantalla/sprite DEO cADD-X
#8e .Pantalla/sprite DEO cADD-X
#8f .Pantalla/sprite DEO
BRK
@nuevo-cuadrado 017f 7b73 6343 7fff 007c 7c7c 7c7c 0000
```
¡intenta rotar los tiles!
## screen.tal y las combinaciones del byte del sprite
el ejemplo screen.tal en el repo de uxn consiste en una tabla que muestra todas las posibles (¡256!) combinaciones de nibbles altos y bajos en el byte del sprite.
=> ./img/screenshot_uxn-screen.png captura de pantalla del ejemplo screen.tal, que muestra un sprite coloreado y volteado de diferentes maneras.
=> https://git.sr.ht/~rabbits/uxn/tree/main/item/projects/examples/devices/screen.tal código de screen.tal
comparémoslo con todo lo que hemos dicho sobre el byte "sprite".
## diseñando sprites
nasu es una herramienta de 100R, escrita en uxntal, que facilita el diseño y la exportación de sprites 2bpp.
=> https://100r.co/site/nasu.html 100R - nasu
además de usarlo para dibujar con los colores 1, 2, 3 (y borrar para obtener el color 0), puedes usarlo para encontrar los colores de tu sistema, para ver cómo se verán tus sprites con los diferentes modos de color (también conocidos como modos de mezcla) y para ensamblar objetos hechos de múltiples sprites.
con nasu puedes exportar e importar archivos chr que después puedes incluir en tu código.
para esto conviene usar una herramienta como hexdump para obtener su representación hexadecimal:
```
$ hexdump -C sprites.chr
```
¡te recomiendo que pruebes nasu!
# tamaño de la pantalla y capacidad de respuesta
lo último que cubriremos hoy tiene que ver con las suposiciones que hace varvara sobre el tamaño de su pantalla y algunas estrategias de código que podemos usar para lidiar con ellas.
en resumen, ¡no hay un tamaño de pantalla estándar!
por defecto, la pantalla del emulador varvara tiene un tamaño de 512x320 píxeles (o 64x40 tiles).
sin embargo, y a modo de ejemplo, el ordenador virtual también funciona en la nintendo ds, con una resolución de 256x192 píxeles (32x24 tiles), y en el teletipo, con una resolución de 128x64 píxeles (16x8 tiles)
como programadorxs, se espera que decidamos qué hacer con ellos: nuestros programas pueden adaptarse a los distintos tamaños de pantalla, pueden tener distintos modos según el tamaño de la pantalla, etc.
## cambiando el tamaño de la pantalla
adicionalmente, podemos cambiar el tamaño de la pantalla varvara escribiendo en los puertos .Pantalla/ancho y .Pantalla/alto.
por ejemplo, el siguiente código cambiaría la pantalla a una resolución de 640x480:
```
#0280 .Pantalla/ancho DEO2 ( anchura de 640 )
#01e0 .Pantalla/alto DEO2 ( altura de 480 )
```
tengamos en cuenta que esto solo funcionaría para las instancias del emulador varvara en las que el tamaño de la pantalla puede cambiarse realmente, por ejemplo, porque la pantalla virtual es una ventana.
¡sería importante tener en cuenta los aspectos de la capacidad de respuesta que se discuten a continuación, para los casos en los que no podemos cambiar el tamaño de la pantalla!
### tamaño de pantalla por defecto
originalmente, la forma de cambiar el tamaño de la pantalla en uxnemu implicaba editar su código fuente.
si te has descargado el repositorio con el código fuente, verás que dentro del directorio src/ hay un uxnemu.c, con un par de líneas parecidas a las siguientes:
```
#define WIDTH 64 * 8
#define HEIGHT 40 * 8
```
esos dos números, 64 y 40 (ancho y alto), son el tamaño de pantalla por defecto en tiles, como mencionamos anteriormente.
puedes cambiarlos, guardar el archivo y volver a ejecutar el script build.sh para que uxnemu funcione con esta nueva resolución.
## leyendo y adaptando el tamaño de la pantalla (lo básico)
como recordarás de los puertos de dispositivos de pantalla mencionados anteriormente, la pantalla nos permite leer su anchura y altura como cortos.
si quisiéramos, por ejemplo, dibujar un píxel en el centro de la pantalla independientemente del tamaño de la misma, podemos traducir a uxntal una expresión como la siguiente:
```
x = anchopantalla/2
y = altopantalla/2
```
### división uxntal
para esto, vamos a introducir las instrucciones MUL y DIV: funcionan como ADD y SUB, pero para la multiplicación y la división:
* MUL: toma los dos elementos superiores de la pila, los multiplica y empuja el resultado ( a b -- a*b )
* DIV: toma los dos elementos superiores de la pila, los divide y empuja el resultado ( a b -- a/b )
usando DIV, nuestra expresión traducida para el caso de la coordenada x, podría verse como:
```
.Pantalla/ancho DEI2 ( obtener el ancho de la pantalla en la pila )
#0002 DIV2 ( dividir sobre 2 )
.Pantalla/x DEO2 ( tomar el resultado de la pila y enviarlo a .Pantalla/x )
```
### desplazamiento de bits
si lo que queremos es dividir por encima o multiplicar por potencias de dos (como en este caso), también podemos utilizar la instrucción SFT.
esta instrucción toma un número y un "valor de desplazamiento" que indica la cantidad de posiciones de bit a desplazar a la derecha o a la izquierda.
el nibble inferior del valor de desplazamiento indica a uxn cuántas posiciones hay que desplazar a la derecha y el nibble superior expresa cuántos bits hay que desplazar a la izquierda.
para dividir un número por encima de 2, tendríamos que desplazar sus bits un espacio a la derecha.
por ejemplo, dividir 10 (en decimal) entre 2 podría expresarse de la siguiente manera:
```
#0a #01 SFT ( resultado: 05 )
```
0a es 0000 1010 en binario y 05 es 0000 0101 en binario: los bits de 0a se desplazaron una posición a la derecha y se introdujo un cero como bit más a la izquierda.
para multiplicar por 2, desplazamos un espacio a la izquierda:
```
#0a #10 SFT ( resultado: 14 en hexadecimal )
```
14 en hexadecimal (20 en decimal), es 0001 0100 en binario: los bits de 0a fueron desplazados una posición a la izquierda y un cero fue introducido como el bit más a la derecha.
en modo corto, el número a desplazar es un corto, pero el valor de desplazamiento sigue siendo un byte.
por ejemplo, lo siguiente dividirá el ancho de la pantalla en dos, utilizando el desplazamiento a nivel de bits:
```
.Pantalla/ancho DEI2
#01 SFT2
```
### macros MITAD
para seguir ilustrando el uso de las macros, podríamos definir unas macros MITAD y MITAD2, utilizando DIV o SFT.
usando DIV:
```
%MITAD { #02 DIV } ( número -- número/2 )
%MITAD2 { #0002 DIV2 } ( número -- número/2 )
```
usando SFT:
```
%MITAD { #01 SFT } ( número -- número/2 )
%MITAD2 { #01 SFT2 } ( número -- número/2 )
```
y utilizar cualquiera de ellos para calcular el centro:
```
.Pantalla/ancho DEI2 MITAD2 .Pantalla/x DEO2
.Pantalla/alto DEI2 MITAD2 .Pantalla/y DEO2
```
notemos que la macro MITAD2 que utiliza SFT2 necesitará un byte menos que la que utiliza DIV2. esto puede o no ser importante dependiendo de tus prioridades :)
## dibujando sprites en posiciones específicas
como ejercicio para ti, te invito a que escribas el código que lograría algo o todo lo siguiente:
* dibuja un tile de 8x8 completamente centrado en la pantalla
* dibujar un tile de 8x8 en cada una de las esquinas de la pantalla
* dibujar un tile de 8x8 tocando cada uno de los bordes de la pantalla, centrados en cada uno de ellos
una vez que lo logres, te invito a que hagas lo mismo, pero utilizando una imagen compuesta por múltiples tiles (por ejemplo, tiles de 2x2, tiles de 1x2, etc).
# instrucciones del día 2
además de cubrir los fundamentos del dispositivo de pantalla hoy, discutimos estas nuevas instrucciones:
* DEI: lee un valor en la pila, desde la dirección del dispositivo dada en la pila ( dirección -- valor )
* INC: incrementa el valor en la parte superior de la pila ( a -- a+1 )
* BRK: rompe el flujo del programa, para cerrar subrutinas
* MUL: toma los dos primeros elementos de la pila, los multiplica y empuja el resultado ( a b -- a*b )
* DIV: toma los dos primeros elementos de la pila, los divide y empuja el resultado ( a b -- a/b )
* SFT: toma un número a desplazar y un valor de desplazamiento y empuja el número desplazado las posiciones que indica el valor. el nibble inferior del valor de desplazamiento indica el desplazamiento a la derecha y el nibble superior, el desplazamiento a la izquierda ( número valordesplazamiento -- númerodesplazado )
también cubrimos el modo corto, que le indica a la cpu que debe operar con palabras de 2 bytes de longitud.
# día 3
¡en el {tutorial de uxn día 3} empezamos a trabajar con la interactividad usando el teclado y cubrimos en profundidad varias instrucciones uxntales!
sin embargo, ¡te invito a que te tomes un descanso y a que sigas explorando el dibujo en la pantalla de uxn a través del código antes de continuar!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)