Merge pull request 'traducción_día_7,_apéndice_a_y_demases' (#6) from jota/compudanzas:traducción_día_7_y_demases into main

Reviewed-on: #6
This commit is contained in:
sejo 2022-01-12 23:19:29 +00:00
commit 3f2de48d7c
10 changed files with 1378 additions and 117 deletions

View File

@ -1,11 +1,17 @@
# hexadecimal
numeral system in base 16: it uses 16 digits, from 0 to 9 and from 'a' to 'f'.
eng: numeral system in base 16: it uses 16 digits, from 0 to 9 and from 'a' to 'f'.
there's a direct mapping between each possible combination of 4 bits (nibble), and an hexadecimal (hex) digit:
there's a direct mapping between each possible combination of 4 bits (nibble), and an hexadecimal (hex) digit:
//
esp: sistema numérico en base 16: utiliza 16 dígitos, del 0 al 9 y de la 'a' a la 'f'.
hay un mapeo directo entre cada posible combinación de 4 bits (nibble), y un dígito hexadecimal (hex):
+ <table>
+ <tr><th>binary</th><th>hex</th><th>dec</th></tr>
+ <tr><th>binar(y/io)</th><th>hex</th><th>dec</th></tr>
+ <tr><td>0000</td><td>0</td><td>0</td></tr>
+ <tr><td>0001</td><td>1</td><td>1</td></tr>
+ <tr><td>0010</td><td>2</td><td>2</td></tr>
@ -43,9 +49,16 @@ there's a direct mapping between each possible combination of 4 bits (nibble), a
# 24 bits
24 bits correspond to:
eng: 24 bits correspond to:
* 3 bytes
* 4 {base64} digits
* 6 hexadecimal digits
* 8 {octal} digits
esp: 24 bits corresponden a:
* 3 bytes
* 4 dígitos {base64}
* 6 dígitos hexadecimales
* 8 dígitos {octales}

View File

@ -62,7 +62,19 @@ además de utilizar estrategias y fragmentos de código anteriores, cubrimos est
# día 7
¡próximamente!
aquí hablamos de los dispositivos del ordenador varvara que aún no hemos cubierto: audio, archivo y fechahora o "datetime".
este debería ser un final ligero y tranquilo de nuestro recorrido, ya que tiene que ver menos con la lógica de programación y más con las convenciones de entrada y salida en estos dispositivos.
{tutorial de uxn día 7}
## apéndices
# apéndice a: repetir un tile dentro de un rectángulo
aquí generalizaremos un procedimiento similar en una subrutina dibuja-tiles que dibuje un rectángulo relleno con un tile dado.
{tutorial de uxn apéndice a}
# índice tentativo

View File

@ -1,3 +1,596 @@
# apéndice A
# tutorial uxn apéndice a: repetir un tile dentro de un rectángulo
¡proximamente! traducción en proceso. :)
en la primera parte del {tutorial de uxn día 6} hablamos de cómo cubrir el fondo de la pantalla con un tile dado.
aquí generalizaremos un procedimiento similar en una subrutina dibuja-tiles que dibuje un rectángulo relleno con un tile dado. recibirá las coordenadas x,y de la esquina superior izquierda del rectángulo, su anchura y altura en píxeles, y la dirección del tile:
```
@dibuja-tiles ( x^ y^ ancho^ alto^ direc* -- )
```
un recordatorio de que estamos utilizando la convención de añadir un signo de intercalación (^) después del nombre de un valor para indicar que es un corto, y un asterisco (*) para indicar que es un corto que funciona como un puntero (es decir, una dirección en la memoria del programa)
vamos a detallar cómo llegar a dos versiones de esta subrutina, una que se basa en una fuerte manipulación de la pila, y otra que utiliza variables. esto con el fin de comparar ambos enfoques y darnos una visión más amplia de las posibilidades dentro de uxntal.
## configuración
comencemos con el siguiente programa como plantilla. incluye los datos para un sprite de 1bpp compuesto por líneas diagonales.
```
( hola-fondo.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ]
( macros )
%RTN { JMP2r }
( programa principal )
|0100
@configuracion
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
BRK
@tile-fondo 1122 4488 1122 4488
```
## repetir un tile en una fila
¿qué procedimiento podríamos seguir para repetir el dibujo de un tile empezando por x, y terminando en un límite correspondiente a x+ancho?
una forma sería algo así como:
* dibujar tile en x
* añadir 8 (el tamaño del tile) a x
* ¿es x menor que el límite? si lo es, repite el procedimiento, si no, termina
### versión concreta
antes de abstraerlo, recomiendo que lo escribamos con números concretos.
digamos que nuestra x inicial es 0008, nuestro ancho es 0100, y el tile que estamos dibujando es tile-fondo.
el límite, x+ancho, sería 0108.
el primer paso, dibujar el tile en x sería:
```
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
#0008 .Pantalla/x DEO2 ( establecer la x inicial )
#03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 )
```
añadiendo 8 a x, ya sabemos:
```
.Pantalla/x DEI2 #0008 ADD2 ( añadir 8 a x )
.Pantalla/x DEO2 ( guardar la nueva x )
```
comprobar si x es menor que el límite, saltando si lo es, sería algo así:
```
.Pantalla/x DEI2
#0108 ( el límite )
LTH2 ,&bucle JCN ( saltar si x es menor que el límite )
```
integrando todo ello, podríamos obtener:
```
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
#0008 .Pantalla/x DEO2 ( establecer la x inicial )
&bucle-x
#03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 )
.Pantalla/x DEI2 #0008 ADD2 DUP2 ( añadir 8 a x )
.Pantalla/x DEO2 ( almacenar la nueva x )
#0108 LTH2 ,&bucle-x JCN ( salta si x es menor que el límite )
```
nótese el uso de DUP2 para evitar releer el valor de x.
### abstrayendo
ahora, digamos que queremos que el código anterior funcione con cualquier x inicial y ancho inicial dados, presentes en la pila antes de empezar.
podemos incluso pensar en ello como una subrutina por sí misma con la siguiente firma:
```
@dibuja-tiles-en-fila ( x^ ancho^ -- )
```
asumamos por el momento que la dirección del sprite ya fue establecida, para enfocarnos en `x` y en el ancho.
al iniciar la subrutina, el ancho está en la parte superior de la pila, seguido de la x inicial.
podemos utilizar estos dos valores para calcular el límite, que podemos almacenar en la pila de retorno.
una forma de conseguirlo, anotando el estado de la pila de trabajo después de cada instrucción, podría ser:
```
( estado inicial: ptra: x^ ancho^ )
OVR2 ( ptra: x^ ancho^ x^ )
ADD2 ( ptra: x^ límite^ )
STH2 ( ptra: x^ / pret: límite^ )
```
otra:
```
( estado inicial: ptra: x^ ancho^ )
ADD2k ( ptra: x^ ancho^ límite^ )
STH2 ( ptra: x^ ancho^ / pret: límite^ )
POP2 ( ptra: x^ / pret: límite^ )
```
recuerda que estamos mostrando la parte superior de las pilas a su derecha.
después de estos pasos, la x inicial está en la parte superior de la pila, por lo que podemos enviarla directamente a la pantalla.
el último cambio que necesitaríamos es reemplazar nuestro límite codificado por una instrucción STH2kr (copiar el límite de la pila de retorno a la pila de trabajo), y terminar nuestra rutina con una POP2r (eliminar el límite de la pila de retorno).
nuestra subrutina se vería entonces de la siguiente manera:
```
@dibuja-tiles-en-fila ( x^ ancho^ -- )
( calcular y guardar el límite )
OVR2 ( ptra: x^ ancho^ x^ )
ADD2 ( ptra: x^ límite^ )
STH2 ( ptra: x^ / pret: límite^ )
.Pantalla/x DEO2 ( fijar x inicial )
&bucle-x
#03 .Pantalla/sprite DEO ( dibujar sprite con color 3 y 0 )
.Pantalla/x DEI2 #0008 ADD2 DUP2 ( añadir 8 a x )
.Pantalla/x DEO2 ( guardar la nueva x )
STH2kr ( copiar límite de pret en ptra )
LTH2 ,&bucle-x JCN ( saltar si x es menor que el límite )
POP2r ( hacer POP en límite de pret )
RTN
```
### programa completo
lo siguiente muestra nuestro programa en contexto, llenando completamente la primera fila de nuestra pantalla con nuestro tile:
=> ./img/screenshot_uxn-background-row.png captura de pantalla mostrando la primera fila de la pantalla varvara rellenada con líneas diagonales
```
( hola-fondo.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ]
( macros )
%RTN { JMP2r }
( programa principal )
|0100
@configuracion
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
#0000 ( x inicial )
.Pantalla/ancho DEI2 ( obtener el ancho de la pantalla )
;dibuja-tiles-en-fila JSR2
BRK
@dibuja-tiles-en-fila ( x^ ancho^ -- )
OVR2 ( ptra: x^ límite^ x^ )
ADD2 ( ptra: x^ límite^ )
STH2 ( ptra: x^ / pret: límite^ )
.Pantalla/x DEO2 ( fijar x inicial )
&bucle-x
#03 .Pantalla/sprite DEO ( dibujar sprite con color 3 y 0 )
.Pantalla/x DEI2 #0008 ADD2 DUP2 ( añadir 8 a x )
.Pantalla/x DEO2 ( guarda la nueva x )
STH2kr ( copiar límite de pret en ptra )
LTH2 ,&bucle-x JCN ( saltar si x es menor que el límite )
POP2r ( sacar el límite de pret )
RTN
@tile-fondo 1122 4488 1122 4488
```
## repetir una fila
similar a lo que acabamos de hacer: ¿cuál es el procedimiento que podríamos seguir para repetir verticalmente una fila empezando por `y`, y terminando en un límite correspondiente a y+altura?
siguiendo la misma estrategia, podríamos hacer:
* dibujar la fila en y
* añadir 8 (el tamaño del tile) a y
* ¿es `y` menor que el límite? si lo es, repetir el procedimiento, en caso contrario terminar
### versión concreta
utilicemos los mismos números que antes, suponiendo que nuestra `y` inicial es 0008, nuestra altura es 0100, y por tanto nuestro límite siendo 0108.
en el caso de x, comencemos en 0000 y tengamos un ancho correspondiente al ancho de la pantalla.
como la dirección no cambiaría en el proceso, podemos establecerla al principio y olvidarnos de ella.
el siguiente código se basa en el bucle anterior de x, pero ahora dibuja una fila en una coordenada `y` dada, le suma 8 y luego comprueba si es menor que el límite:
```
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
#0008 .Pantalla/y ( establecer y inicial )
&bucle-y
( preparar y dibujar fila )
#0000 ( x inicial )
.Pantalla/ancho DEI2 ( obtener el ancho de la pantalla )
;dibuja-tiles-en-fila JSR2
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a y )
.Pantalla/y DEO2 ( guardar la nueva y )
#0108 ( poner límite en la parte superior de la pila )
LTH2 ,&bucle-y JCN ( saltar si x es menor que el límite )
```
### versión abstracta
ahora, antes de saltar directamente a la emulación de la solución para dibujar la fila, vamos a notar que en este caso no es tan fácil.
¿por qué? porque la idea de nuestra subrutina dibuja-tiles es que debe ser capaz de recibir la x inicial y el ancho del rectángulo, y ahora mismo estos valores están codificados dentro del bucle.
esta debería ser la firma de nuestra subrutina:
```
@dibuja-tiles ( x^ y^ ancho^ alto^ direc* -- )
```
podemos abordar este problema bien con alguna "manipulación de la pila ", o bien con "variables ".
también hay que tener en cuenta que lo haremos porque estamos tratando de llegar a una subrutina generalizada.
si sólo quisiéramos cubrir toda la pantalla con un sprite, ya tenemos todo el código necesario: sólo tendríamos que adaptar el límite vertical del bucle para que se corresponda con la altura de la pantalla, ¡y ya está!
entonces podríamos pasar a la sección relativa de las paletas. sin embargo, lo que sigue puede ser interesante como una forma de ver un posible enfoque para escribir un código uxntal más complejo :)
### usando la manipulación de la pila
en principio podríamos simplemente manipular los elementos dados en la pila, almacenándolos cuando sea apropiado, para adaptar nuestra subrutina a su firma.
en primer lugar, la dirección del tile es el valor de la parte superior de la pila. podemos consumirlo y olvidarnos de él:
```
( ptra inicial: x^ y^ ancho^ alto^ direc* )
.Pantalla/direc DEO2 ( ptra: x^ y^ ancho^ alto^ )
```
pensando en el bucle vertical, necesitamos calcular su límite sumando la altura a `y`, y necesitamos establecer la `y` inicial.
podríamos hacer lo siguiente:
```
ROT2 ( ptra: x^ ancho^ altura^ y^ )
DUP2 ( ptra: x^ ancho^ altura^ y^ y^ )
( establecer y inicial: )
.Pantalla/y DEO2 ( ptra: x^ ancho^ alto^ y^ )
( calcular y almacenar el límite vertical )
ADD2 ( ptra: x^ ancho^ límite-y^ )
STH2 ( ptra: x^ ancho^ / pret: límite-y^ )
```
ahora, podríamos también almacenar el ancho y `x`, ya que las necesitamos después en su orden original (primero x, luego anchura )
```
STH2 ( ptra: x^ / pret: límite-y^ ancho^ )
STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
```
en teoría, la primera parte de nuestra subrutina podría verse como:
```
@dibuja-tiles ( x^ y^ ancho^ alto^ direc* -- )
( establecer la dirección del tile )
.Pantalla/direc DEO2 ( ptra: x^ y^ ancho^ alto^ )
ROT2 ( ptra: x^ ^ altura^ y^ )
DUP2 ( ptra: x^ ^ altura^ y^ )
( fijar y inicial )
.Pantalla/y DEO2 ( fijar y inicial, ptra: x^ ^ altura^ y^ )
( calcular y guardar límite-y )
ADD2 ( ptra: x^ ^ límite-y^ )
STH2 ( ptra: x^ ^ / pret: límite-y^ )
( almacenar anchura y `x` )
STH2 STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2r ( ptra: x^ / pret: límite-y^ ancho^ )
( recuperar la anchura )
STH2r ( ptra: x^ ancho^ / pret: límite-y^ )
;dibuja-tiles-en-fila JSR2
```
el problema es que dentro del bucle, ambas instrucciones STH2r recuperan y consumen los valores de `x` y ancho de la pila de retorno. por tanto, en la siguiente iteración no podríamos volver a utilizarlos, ya que se perderían.
podemos pensar que podríamos reemplazar estas instrucciones con STH2kr:
```
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2kr ( ptra: x^ / pret: límite-y^ ancho^ x^ )
```
¡pero entonces no podemos recuperar el ancho porque la x sigue en la parte superior de la pila de retorno!
oh, muchas dificultades, pero por el bien del ejemplo de la pila, vamos a seguir resolviendo esto (?)
¿cómo podemos poner el ancho en la parte superior de la pila de retorno? tal vez con un intercambio aplicado a la pila de retorno:
```
SWP2r ( ptra: x^ / pret: límite-y^ x^ ancho^ )
```
entonces podemos recuperar el ancho y utilizarlo:
```
STH2kr ( ptra: x^ ancho^ / pret: límite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: límite-y^ x^ ancho^ )
```
¿qué sigue? añadir 8 a `y`, y comprobar si es menor que el límite. la primera parte va sin problemas:
```
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a y; ptra: y^ y^ / pret: límite-y^ x^ ancho^ )
.Pantalla/y DEO2 ( almacenar nueva `y`; ptra: y^ / pret: límite-y^ x^ ancho^ )
```
para conseguir el límite en la pila de trabajo para la comparación, tenemos que girar la pila de retorno:
```
ROT2r ( ptra: y^ / pret: x^ ancho^ límite-y^ )
STH2kr ( ptra: y^ límite-y^ / pret: x^ ancho^ límite-y^ )
```
pero ah, antes de hacer la comparación y el salto, debemos reordenar la pila de retorno para que se corresponda con la ordenación que teníamos al principio del bucle:
```
SWP2r ( ptra: y^ límite-y^ / pret: x^ límite-y^ ancho^ )
ROT2r ( ptra: y^ límite-y^ / pret: límite-y^ ancho^ x^ )
```
ahora podemos hacer la comparación y saltar:
```
LTH2 ,&bucle-y JCN ( salta si x es menor que el límite )
```
después debemos limpiar la pila de retorno:
```
POP2r POP2r POP2r
```
después de todo esto, nuestra subrutina tendría el siguiente aspecto:
```
@dibuja-tiles ( x^ y^ ancho^ alto^ direc* -- )
( establecer la dirección del tile )
.Pantalla/direc DEO2 ( ptra: x^ y^ ancho^ alto^ )
ROT2 ( ptra: x^ ^ altura^ y^ )
DUP2 ( ptra: x^ ^ altura^ y^ )
( fijar y inicial )
.Pantalla/y DEO2 ( fijar y inicial, ptra: x^ ^ altura^ y^ )
( calcular y almacenar límite-y )
ADD2 ( ptra: x^ ^ límite-y^ )
STH2 ( ptra: x^ ^ / pret: límite-y^ )
( almacenar anchura y `x` )
STH2 STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2kr ( ptra: x^ / pret: límite-y^ ancho^ x^ )
( recuperar la anchura )
SWP2r ( ptra: x^ / pret: límite-y^ x^ ancho^ )
STH2kr ( ptra: x^ ancho^ / pret: límite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: límite-y^ x^ ancho^ )
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añadir 8 a y )
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar límite-y )
ROT2r ( ptra: y^ / pret: x^ ^ límite-y^ )
STH2kr ( ptra: y^ límite-y^ / pret: x^ ancho^ límite-y^ )
( reordenar la pila de retorno )
SWP2r ( ptra: y^ límite-y^ / pret: x^ límite-y^ ancho^ )
ROT2r ( ptra: y^ límite-y^ / pret: límite-y^ ancho^ x^ )
LTH2 ,&bucle-y JCN ( salta si x es menor que el límite )
POP2r POP2r POP2r ( limpiar la pila de retorno )
RTN
```
podemos entonces llamarlo de la siguiente manera para obtener un cuadrado de 256x256 lleno de tiles:
```
#0008 #0008 ( x y `y` )
#0100 #0100 ( ancho y alto )
;tile-fondo
;dibuja-tiles JSR2
```
=> ./img/screenshot_uxn-background-square.png captura de pantalla que muestra un gran cuadrado en la pantalla varvara compuesto por líneas diagonales
### usando variables
comparemos el enfoque anterior con el uso de variables relativas.
iremos "con todo" de una manera relativamente derrochadora, sin optimizar los procedimientos que podrían beneficiarse de la manipulación de la pila.
declararemos las siguientes etiquetas para nuestras variables, después del RTN que termina la subrutina:
```
( variables )
&alto $2 &ancho $2 &y $2 &x $2 &límite-y $2
```
ahora, iniciamos la subrutina de la misma manera que antes, estableciendo la dirección para nuestro sprite:
```
( ptra inicial: x^ y^ ancho^ alto^ direc* )
.Pantalla/direc DEO2 ( ptra: x^ y^ ancho^ alto^ )
```
entonces, simplemente almacenamos los siguientes valores en direcciones relativas:
```
,&alto STR2
,&ancho STR2
&y STR2
&x STR2
```
nótese que vamos en orden inverso.
después de estas operaciones las pilas están vacías.
entonces podemos fijar la `y` inicial y calcular el límite vertical, utilizando los valores almacenados en las variables:
```
( establecer y inicial )
,&y LDR2 DUP2 ( ptra: y^ y^ )
.Pantalla/y DEO2 ( ptra: y^ )
( calcular límite-y )
,&alto LDR2 ( ptra: y^ alto^ )
ADD2 ( ptra: límite-y^ )
,&límite-y STR2 ( ptra: )
```
nuestro bucle ahora tendría el siguiente aspecto:
```
&bucle-y
( recuperar x y ancho )
,&x LDR2
&ancho LDR2
( dibujar fila )
;dibuja-tiles-en-fila JSR2
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a y )
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar el límite vertical )
,&límite-y LDR2
LTH2 ,&bucle-y JCN ( saltar si x es menor que el límite )
```
¡y eso es todo!
compara esto con la versión "concreta" que desarrollamos anteriormente, ¡es muy similar en su estructura!
la subrutina completa tendría el siguiente aspecto:
```
@dibuja-tiles ( x^ y^ ancho^ alto^ direc* -- )
( establecer la dirección del tile )
.Pantalla/direc DEO2 ( ptra: x^ y^ ancho^ alto^ )
( almacenar valores )
,&altura STR2
,&ancho STR2
&y STR2
,&x STR2
( establecer y inicial )
,&y LDR2 DUP2 ( ptra: y^ y^ )
.Pantalla/y DEO2 ( ptra: y^ )
( calcular el límite vertical )
,&alto LDR2 ( ptra: y^ alto^ )
ADD2 ( ptra: límite-y^ )
,&límite-y STR2 ( ptra: )
&bucle-y
( recuperar x y anchura )
,&x LDR2
,&ancho LDR2
( dibujar fila )
;dibuja-tiles-en-fila JSR2
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a y )
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar el límite vertical )
,&límite-y LDR2
LTH2 ,&bucle-y JCN ( saltar si x es menor que el límite )
RTN
( variables )
&alto $2 &ancho $2 &y $2 &x $2 &límite-y $2
```
como he dicho antes, podemos encontrar aquí algunas oportunidades de optimización.
tal vez el límite vertical puede ser escondido en la pila de retorno como en el bucle dibuja-tiles-en-fila, o tal vez la variable para la altura y para la `y` inicial no son necesarios.
dejo que lo resuelvas :)
nota que esta subrutina tal como está, requiere 24 bytes de memoria de programa más que la versión de la pila.
en nuestro caso eso no es un gran problema, pero es una buena manera de evaluar nuestras prioridades: código súper legible pero probablemente ineficiente (como esta última subrutina), código súper optimizado pero probablemente ilegible (código de sólo escritura, dicen), o algo en el medio.
## dibuja-fondo
ahora que tenemos estas bonitas subrutinas, podemos simplemente envolverlas en otra que cubrirá todo el fondo con nuestro tile elegido.
por ejemplo:
```
@dibuja-fondo ( -- )
#0000 #0010 ( x e `y` iniciales )
.Pantalla/ancho DEI2
.Pantalla/alto DEI2 #0010 SUB2
;tile-fondo
;dibuja-tiles JSR2
RTN
```
que podemos llamar simplemente desde nuestra subrutina de iniciación:
```
dibuja-fondo JSR2
```
=> ./img/screenshot_uxn-background-full.png captura de pantalla mostrando la pantalla varvara cubierta de líneas diagonales.
¡bonito! te recomiendo que te tomes un pequeño descanso; ¡esto ha sido pesado!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)

View File

@ -1,6 +1,6 @@
# tutorial uxn: día 1, aspectos básicos
# tutorial uxn: día 1, aspectos básicos
hola! en esta primera sección del {tutorial de uxn} vamos a hablar de los aspectos básicos de la computadora uxn llamada varvara, su paradigma de programación en un lenguaje llamado uxntal, su arquitectura, y por que puede que quieras aprender a programarla.
¡hola! en esta primera sección del {tutorial de uxn} vamos a hablar de los aspectos básicos de la computadora uxn llamada varvara, su paradigma de programación en un lenguaje llamado uxntal, su arquitectura, y por que puede que quieras aprender a programarla.
también saltaremos directo a nuestros primeros programas simples para demostrar conceptos fundamentales que desarrollaremos en los próximos días.

View File

@ -4,7 +4,7 @@ tutorial de uxn: día 2, la pantalla
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 instrucciones de salto corto ("short"; 2 bytes) junto a los de un sólo byte en uxntal.
también discutiremos el trabajo con el modo corto o "short" (2 bytes) junto a los de un sólo byte en uxntal.
si todavía no lo has hecho te recomiendo que leas las sección anterior:
@ -26,7 +26,7 @@ ese número corresponde a los valores que se pueden representar usando dos bytes
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 shorts en lugar de bytes será el camino a seguir.
para estos y otros casos, usar cortos en lugar de bytes será el camino a seguir.
¿cómo los tratamos?
@ -40,21 +40,26 @@ el byte que esté más adentro de la pila será el byte "alto" del corto, y el b
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!
¡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 )
```
@ -94,6 +99,7 @@ ahora, comparemos con lo que ocurre con el 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
@ -102,7 +108,7 @@ en este caso estamos empujando los mismos 4 bytes hacia abajo en la pila, pero A
* empuja el byte alto del resultado (00) hacia abajo en la pila
* empuja el byte bajo del resultado (0c) hacia abajo en la pila
the stack ends up looking as follows:
la pila acaba teniendo el siguiente aspecto:
```
00 0c <- arriba
@ -138,9 +144,9 @@ recuerda que las 256 direcciones de entrada/salida ya están cubiertas usando un
considerando esto, los siguientes son los comportamientos que podemos esperar:
la instrucción DEO2 necesita un valor (1 short) para salir, y una dirección i/o (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.
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 i/o (1 byte) en la pila, y empujará hacia abajo en la pila el valor (1 short) que corresponde a esa entrada.
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.
@ -157,7 +163,6 @@ en los ejemplos uxntal podemos ver sus etiquetas definidas de la siguiente maner
```
ignoraremos los primeros elementos por el momento, y nos centraremos en los componentes de color.
dispositivo de sistema y colores
## colores del sistema
@ -175,7 +180,7 @@ podemos escribirlo de la siguiente manera:
( hola-pantalla.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
( programa principal )
|0100
@ -196,7 +201,7 @@ podemos leer cada uno de los colores verticalmente, de izquierda a derecha:
si ejecutamos el programa ahora veremos una pantalla de color púrpura oscuro, en lugar de negro como lo que teníamos antes.
prueba a cambiar los valores del color 0, es decir, la primera columna, y mira lo que pasa :)
prueba cambiar los valores del color 0, es decir, la primera columna, y mira lo que pasa :)
# el dispositivo de pantalla
@ -212,7 +217,7 @@ en los programas uxntal para el ordenador varvara puedes encontrar las etiquetas
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ]
```
las entradas que podemos leer de este dispositivo son
las entradas que podemos leer de este dispositivo son:
* vector (2 bytes)
* anchura de la pantalla (2 bytes)
@ -267,25 +272,38 @@ una pregunta para ti: si quisiéramos establecer las coordenadas como ( x: 4, y:
el envío de un único byte a .Pantalla/pixel 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
* 4: dibujar un solo píxel en el primer plano
palabras en
el nibble inferior del byte, es decir, el dígito hexadecimal de la derecha, determinará su color.
las 8 posibles combinaciones del byte 'pixel' que tenemos para dibujar un pixel son:
* 00: dibujar el píxel con el color 0 en la capa del fondo
* 01: dibujar un píxel con el color 1 en la capa del fondo
* 02: dibujar el píxel con el color 2 en la capa del fondo
* 03: dibujar el píxel con el color 3 en la capa del fondo
* 40: dibujar un píxel con el color 0 en la capa del primer plano
* 41: dibujar el píxel con el color 1 en la capa del primer plano
* 42: dibujar el píxel con el color 2 en la capa del primer plano
* 43: dibujar el píxel con el color 3 en la capa del primer plano
+ <table>
+ <tr><th>byte de píxel</th><th>capa</th><th>color</th></tr>
+ <tr><td>00</td><td>plano de fondo</td><td>0</td></tr>
+ <tr><td>01</td><td>plano de fondo</td><td>1</td></tr>
+ <tr><td>02</td><td>plano de fondo</td><td>2</td></tr>
+ <tr><td>03</td><td>plano de fondo</td><td>3</td></tr>
+ <tr><td>40</td><td>primer plano</td><td>0</td></tr>
+ <tr><td>41</td><td>primer plano</td><td>1</td></tr>
+ <tr><td>42</td><td>primer plano</td><td>2</td></tr>
+ <tr><td>43</td><td>primer plano</td><td>3</td></tr>
+ </table>
& * 00: dibujar el píxel con el color 0 en la capa del fondo
& * 01: dibujar un píxel con el color 1 en la capa del fondo
& * 02: dibujar el píxel con el color 2 en la capa del fondo
& * 03: dibujar el píxel con el color 3 en la capa del fondo
& * 40: dibujar un píxel con el color 0 en la capa del primer plano
& * 41: dibujar el píxel con el color 1 en la capa del primer plano
& * 42: dibujar el píxel con el color 2 en la capa del primer plano
& * 43: dibujar el píxel con el color 3 en la capa del primer plano
## hola píxel
el siguiente código dibujará un píxel con el color 1 en la capa del primer plano, en las coordenadas (8,8):
¡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
@ -321,9 +339,9 @@ recuerda que puedes usar F1 para cambiar de nivel de zoom, y F3 para hacer captu
## hola píxeles
los valores que establecemos en las coordenadas x e y permanecen ahí hasta que los sobrescribimos.
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" sólo una vez:
Por ejemplo, podemos dibujar múltiples píxeles en una línea horizontal, estableciendo la coordenada `y` sólo una vez:
```
( establecer coordenadas y )
@ -361,7 +379,7 @@ podemos definir una macro para que este proceso sea más fácil de escribir:
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.
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:
@ -513,7 +531,6 @@ basándonos en eso, podríamos codificar nuestro cuadrado de la siguiente manera
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
```
@ -544,29 +561,44 @@ de forma similar a lo que ya vimos con el píxel, el envío de un byte a .Pantal
### sprite de nibble alto para 1bpp
el nibble alto del byte 'sprite' determinará la capa en la que dibujaremos, igual que cuando dibujábamos usando el byte 'pixel'.
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 rotar el sprite en el eje horizontal (x) y/o en el vertical (y).
los ocho valores posibles de este nibble alto, utilizados para dibujar un sprite de 1bpp, son:
* 0: dibujar un sprite de 1bpp en el fondo, con la orientación original
* 1: dibujar un sprite de 1bpp en el fondo, rotado horizontalmente
* 2: dibujar un sprite de 1bpp en el fondo, rotado verticalmente
* 3: dibujar un sprite de 1bpp en el fondo, rotado horizontalmente y verticalmente
* 4: dibujar un sprite de 1bpp en primer plano, con la orientación original
* 5: dibujar un sprite de 1bpp en primer plano, rotado horizontalmente
* 6: dibujar un sprite de 1bpp en primer plano, rotado verticalmente
* 7: dibujar un sprite de 1bpp en primer plano, rotado horizontalmente y verticalmente
+ <table>
+ <tr><th>nibble alto</th><th>capa</th><th>rotar-y</th><th>rotar-x</th></tr>
+ <tr><td>0</td><td>plano de fondo</td><td>no</td><td>no</td></tr>
+ <tr><td>1</td><td>plano de fondo</td><td>no</td><td>sí</td></tr>
+ <tr><td>2</td><td>plano de fondo</td><td>sí</td><td>no</td></tr>
+ <tr><td>3</td><td>plano de fondo</td><td>sí</td><td>sí</td></tr>
+ <tr><td>4</td><td>primer plano</td><td>no</td><td>no</td></tr>
+ <tr><td>5</td><td>primero plano</td><td>no</td><td>sí</td></tr>
+ <tr><td>6</td><td>primero plano</td><td>sí</td><td>no</td></tr>
+ <tr><td>7</td><td>primero plano</td><td>sí</td><td>sí</td></tr>
+ </table>
& * 0: dibujar un sprite de 1bpp en el fondo, con la orientación original
& * 1: dibujar un sprite de 1bpp en el fondo, rotado horizontalmente
& * 2: dibujar un sprite de 1bpp en el fondo, rotado verticalmente
& * 3: dibujar un sprite de 1bpp en el fondo, rotado horizontalmente y verticalmente
& * 4: dibujar un sprite de 1bpp en primer plano, con la orientación original
& * 5: dibujar un sprite de 1bpp en primer plano, rotado horizontalmente
& * 6: dibujar un sprite de 1bpp en primer plano, rotado verticalmente
& * 7: dibujar un sprite de 1bpp en primer plano, 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:
* bit 4: rotación-x
* bit 5: rotación-y
* bit 6: capa (0 es plano de fondo, 1 es primer plano)
* bit 7: modo (0 es 1bpp, 1 es 2bpp)
+ <table>
+ <tr><th>bit 7</th><th>bit 6</th><th>bit 5</th><th>bit 4</th></tr>
+ <tr><td>modo (0 es 1bpp, 1 es 2bpp)</td><td>capa (0 es plano de fondo, 1 es primer plano)</td><td>rotar verticalmente (0 es no, 1 es sí)</td><td>rotar horizontalmente (0 es no, 1 es sí)</td></tr>
+ </table>
& * bit 4: rotación-x
& * bit 5: rotación-y
& * bit 6: capa (0 es plano de fondo, 1 es primer plano)
& * bit 7: modo (0 es 1bpp, 1 es 2bpp)
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 rotado ni verticalmente (0) ni horizontalmente (0).
@ -574,24 +606,43 @@ un nibble alto de 1, es decir, 0001 en binario, tiene la última bandera encendi
### sprite de nibble bajo para 1bpp
el nibble bajo del byte 'sprite' determinará los colores que se utilizan para dibujar los píxeles "on" y "off" de los tiles.
el nibble bajo del byte 'sprite' determinará los colores que se utilizan para dibujar los píxeles "encendido" y "apagado" de los tiles.
* 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: "on" con el color 3, "off" 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: "on" con el color 1, "off" con el color 2
* a: "on" con color 2, "off" sin color
* b: "encendido" con el color 3, "apagado" con el color 2
* c: "on" con color 0, "off" con color 3
* d: "encendido" con el color 1, "apagado" con el color 3
* e: "on" con el color 2, "off" con el color 3
* f: "encendido" con el color 3, "apagado" sin color
+ <table>
+ <tr><th>nibble bajo</th><th>color "encendido"</th><th>color "apagado"</th></tr>
+ <tr><td>0</td><td>borrar</td><td>borrar</td></tr>
+ <tr><td>1</td><td>1</td><td>0</td></tr>
+ <tr><td>2</td><td>2</td><td>0</td></tr>
+ <tr><td>3</td><td>3</td><td>0</td></tr>
+ <tr><td>4</td><td>0</td><td>1</td></tr>
+ <tr><td>5</td><td>1</td><td>nada</td></tr>
+ <tr><td>6</td><td>2</td><td>1</td></tr>
+ <tr><td>7</td><td>3</td><td>1</td></tr>
+ <tr><td>8</td><td>0</td><td>2</td></tr>
+ <tr><td>9</td><td>1</td><td>2</td></tr>
+ <tr><td>a</td><td>2</td><td>nada</td></tr>
+ <tr><td>b</td><td>3</td><td>2</td></tr>
+ <tr><td>c</td><td>0</td><td>3</td></tr>
+ <tr><td>d</td><td>1</td><td>3</td></tr>
+ <tr><td>e</td><td>2</td><td>3</td></tr>
+ <tr><td>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
notemos que un 0 en el nibble inferior borrará el tile.
@ -599,7 +650,7 @@ además, 5, 'a' y 'f' en el nibble bajo dibujarán los píxeles que están "ence
## hola sprite
hagámoslo! El siguiente programa dibujará nuestro sprite una vez:
¡hagámoslo! el siguiente programa dibujará nuestro sprite una vez:
```
( hola-sprite.tal )
@ -660,7 +711,7 @@ el siguiente código dibujará nuestro sprite cuadrado con las 16 combinaciones
( establecer coordenadas iniciales x,y )
INIT-X INIT-Y
( establecer direccion del sprite )
( establecer dirección del sprite )
;cuadrado .Pantalla/direc DEO2
#00 .Pantalla/sprite DEO 8ADD-X
@ -800,14 +851,25 @@ el nibble alto para los sprites de 2bpp nos permitirá elegir la capa que querem
los ocho valores posibles para este nibble son:
* 8: dibujar un sprite de 2bpp en el fondo, con la orientación original
* 9: dibujar un sprite de 2bpp en el fondo, rotado horizontalmente
* a: dibujar un sprite 2bpp en el fondo, rotado verticalmente
* b: dibujar un sprite de 2bpp en el fondo, rotado horizontal y verticalmente
* c: dibujar un sprite de 2bpp en primer plano, con la orientación original
* d: dibujar un sprite de 2bpp en primer plano, rotado horizontalmente
* e: dibujar un sprite de 2bpp en primer plano, rotado verticalmente
* f: dibujar un sprite de 2bpp en primer plano, rotado horizontal y verticalmente
+ <table>
+ <tr><th>nibble alto</th><th>capa</th><th>rotar-y</th><th>rotar-x</th></tr>
+ <tr><td>8</td><td>plano de fondo</td><td>no</td><td>no</td></tr>
+ <tr><td>9</td><td>plano de fondo</td><td>no</td><td>sí</td></tr>
+ <tr><td>a</td><td>plano de fondo</td><td>sí</td><td>no</td></tr>
+ <tr><td>b</td><td>plano de fondo</td><td>sí</td><td>sí</td></tr>
+ <tr><td>c</td><td>primer plano</td><td>no</td><td>no</td></tr>
+ <tr><td>d</td><td>primer plano</td><td>no</td><td>sí</td></tr>
+ <tr><td>e</td><td>primer plano</td><td>sí</td><td>no</td></tr>
+ <tr><td>f</td><td>primer plano</td><td>sí</td><td>sí</td></tr>
+ </table>
& * 8: dibujar un sprite de 2bpp en el fondo, con la orientación original
& * 9: dibujar un sprite de 2bpp en el fondo, rotado horizontalmente
& * a: dibujar un sprite 2bpp en el fondo, rotado verticalmente
& * b: dibujar un sprite de 2bpp en el fondo, rotado horizontal y verticalmente
& * c: dibujar un sprite de 2bpp en primer plano, con la orientación original
& * d: dibujar un sprite de 2bpp en primer plano, rotado horizontalmente
& * e: dibujar un sprite de 2bpp en primer plano, rotado verticalmente
& * f: dibujar un sprite de 2bpp en primer plano, rotado 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.
@ -815,24 +877,43 @@ notemos que estos ocho valores tienen todos un bit más a la izquierda en 1: est
el nibble bajo nos permitirá elegir entre muchas combinaciones de colores asignados a cada uno de los diferentes estados de los píxeles:
* 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
+ <table>
+ <tr><th>nibble bajo</th><th>color del estado 0</th><th>color del estado 1</th><th>color del estado 2</th><th>color del estado 3</th></tr>
+ <tr><td>0</td><td>0</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td>1</td><td>0</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td>2</td><td>0</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td>3</td><td>0</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td>4</td><td>1</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td>5</td><td>nada</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td>6</td><td>1</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td>7</td><td>1</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td>8</td><td>2</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td>9</td><td>2</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td>a</td><td>nada</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td>b</td><td>2</td><td>3</td><td>1</td><td>2</td></tr>
+ <tr><td>c</td><td>3</td><td>0</td><td>1</td><td>2</td></tr>
+ <tr><td>d</td><td>3</td><td>1</td><td>2</td><td>3</td></tr>
+ <tr><td>e</td><td>3</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td>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!
## ¡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.
@ -860,7 +941,7 @@ el siguiente código mostrará nuestro sprite en las 16 diferentes combinaciones
( establecer coordenadas iniciales x,y )
INIT-X INIT-Y
( set sprite direcess )
( establecer dirección del sprite )
;nuevo-cuadrado .Pantalla/direc DEO2
#80 .Pantalla/sprite DEO cADD-X
@ -891,7 +972,7 @@ BRK
@nuevo-cuadrado 017f 7b73 6343 7fff 007c 7c7c 7c7c 0000
```
intenta rotar los tiles!
¡intenta rotar los tiles!
## screen.tal y las combinaciones del byte del sprite
@ -953,7 +1034,7 @@ si te has descargado el repositorio con el código fuente, verás que dentro del
#define HEIGHT 40 * 8
```
esos dos números, 64 y 40, son el tamaño de pantalla por defecto en tiles, como mencionamos anteriormente.
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.
@ -1025,18 +1106,18 @@ para seguir ilustrando el uso de las macros, podríamos definir unas macros MITA
usando DIV:
```
%MITAD { #02 DIV } ( numero -- numero/2 )
%MITAD2 { #0002 DIV2 } ( numero -- numero/2 )
%MITAD { #02 DIV } ( número -- número/2 )
%MITAD2 { #0002 DIV2 } ( número -- número/2 )
```
usando SFT:
```
%MITAD { #01 SFT } ( numero -- numero/2 )
%MITAD2 { #01 SFT2 } ( numero -- numero/2 )
%MITAD { #01 SFT } ( número -- número/2 )
%MITAD2 { #01 SFT2 } ( número -- número/2 )
```
and use any of them to calculate the center:
y utilizar cualquiera de ellos para calcular el centro:
```
.Pantalla/ancho DEI2 MITAD2 .Pantalla/x DEO2
@ -1070,9 +1151,7 @@ también cubrimos el modo corto, que le indica a la cpu que debe operar con pala
# día 3
¡en el tutorial de uxn del día 3 empezamos a trabajar con la interactividad usando el teclado, y cubrimos en profundidad varias instrucciones uxntales!
=> ./tutorial_de_uxn_día_3.gmi {tutorial de uxn 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!

View File

@ -1700,7 +1700,7 @@ aquí hay algunas posibilidades extra para que practiques y trates de implementa
# día 7
en el {tutorial de uxn día 7} hablamos de los dispositivos del ordenador varvara que aún no hemos cubierto: audio, archivo y fechatiempo o "datetime".
en el {tutorial de uxn día 7} hablamos de los dispositivos del ordenador varvara que aún no hemos cubierto: audio, archivo y fechahora o "datetime".
este debería ser un final ligero y tranquilo de nuestro recorrido, ya que tiene que ver menos con la lógica de programación y más con las convenciones de entrada y salida en estos dispositivos.

View File

@ -1,3 +1,571 @@
# día 7
# tutorial uxn: día 7, más dispositivos
¡proximamente! traducción en proceso. :)
esta es la séptima y última sección del {tutorial de uxn}! aquí hablamos de los dispositivos del ordenador varvara que aún no hemos cubierto: audio, archivo y fechahora o "datetime".
este debería ser un final ligero y tranquilo de nuestro recorrido, ya que tiene que ver menos con la lógica de programación y más con las convenciones de entrada y salida en estos dispositivos.
¡comencemos!
# el dispositivo de archivo
el dispositivo de archivo en el ordenador varvara nos permite leer y escribir en archivos externos.
sus puertos se definen normalmente de la siguiente manera:
```
|a0 @Archivo [ &vector $2 &exito $2 &estad $2 &borrar $1 &adjuntar $1 &nombre $2 &largo $2 &leer $2 &escribir $2 ]
```
* el corto vector no se utiliza actualmente
* el corto éxito almacena la longitud de los datos que se han leído o escrito con éxito, o cero si ha habido un error
* el corto nombre es para la dirección de memoria donde se almacena el nombre del archivo (terminado en cero, es decir, con un 00)
* el corto largo es la cantidad de bytes a leer o escribir: ¡no olvidemos que la memoria del programa es ffff más 1 byte de largo, y que el programa mismo se almacena allí!
* el short leer es para la dirección de memoria inicial donde los datos de lectura deben ser almacenados
* el corto escribir es para la dirección de memoria inicial donde se almacenan los datos a escribir
* el corto estad es similar al de leer, pero lee la entrada del directorio para el nombre del archivo
* el byte borrar borra el archivo cuando se escribe cualquier valor en él.
* establecer el byte adjuntar a 01 hace que `escribir` añada datos al final del archivo. cuando el byte adjuntar tiene el valor por defecto, 00, `escribir` sobrescribe el contenido desde el principio
una operación de lectura se inicia cuando se escribe en el corto `leer`, y una operación de escritura se inicia cuando se escribe en el corto `escribir`.
¡estos pueden parecer muchos campos para manejar, pero veremos que no son demasiado problema!
## leer un archivo
para leer un archivo, necesitamos saber lo siguiente:
* la ruta del archivo, escrita como una cadena de texto etiquetada en la memoria del programa y terminada por un 00 - esta ruta sería relativa a la ubicación donde se ejecuta uxnemu.
* la cantidad de bytes que queremos leer del archivo: no pasa nada si este número no es igual al tamaño del archivo; puede ser menor o incluso mayor.
* la etiqueta para una sección reservada de la memoria del programa donde se almacenarán los datos leídos
¡y eso es todo!
podemos usar una estructura como la siguiente, donde el nombre del archivo y la memoria reservada están bajo una etiqueta, y la subrutina de carga del archivo bajo otra:
```
@carga-archivo ( -- )
;archivo/nombre .Archivo/nombre DEO2 ( dirección de la ruta del archivo )
#00ff .Archivo/largo DEO2 ( intentará leer 255 bytes )
( establecer la dirección de los datos a leer y hacer la lectura )
;archivo/datos .Archivo/lectura DEO2
( comprobar el byte éxito y saltar según corresponda )
Archivo/exito DEI2 #0000 EQU2 ,&failed JCN
&exito
LIT 'Y .Consola/escribir DEO
RTN
&fallo
LIT 'N .Consola/escritura DEO
RTN
@archivo
&nombre "prueba.txt 00
&datos $ff ( reservando 255 bytes para los datos )
```
nótese que para el nombre del archivo estamos usando la runa de cadena cruda o `raw` (") que nos permite escribir varios caracteres en la memoria del programa hasta encontrar un espacio en blanco.
en este ejemplo estamos escribiendo un carácter en la consola en función de que el corto `exito` sea cero o no, pero podríamos decidir realizar cualquier acción que consideremos oportuna.
además, en este ejemplo no nos preocupa realmente cuántos bytes se han leído realmente: ¡tenga en cuenta que esta información se almacena en `Archivo/exito` hasta que se produzca otra lectura o escritura!
es importante recordar que, como siempre en este contexto, estamos tratando con bytes crudos.
¡no sólo podemos elegir tratar estos bytes como caracteres de texto, sino que también podemos elegir usarlos como sprites, coordenadas, dimensiones, colores, etc!
## escribir un archivo
para escribir un archivo, necesitamos:
* la ruta del archivo, escrita como una cadena en la memoria del programa como el caso anterior
* la cantidad de bytes que queremos escribir en el archivo
* la etiqueta de la sección de la memoria del programa que escribiremos en el archivo
¡tenga en cuenta que el archivo se sobrescribirá completamente a menos que `adjuntar` se establezca a 01!
el siguiente programa escribirá "hola" y una nueva línea (0a) en un archivo llamado "prueba.txt":
```
@guardar-archivo ( -- )
;archivo/nombre .Archivo/nombre DEO2 ( establecer el nombre de archivo )
#0006 .Archivo/largo DEO2 ( intentará escribir 6 bytes )
( establecer la dirección de inicio de los datos, y hacer la escritura )
;archivo/datos .Archivo/escribir DEO2
( leer y evaluar el byte éxito )
.Archivo/exito DEI2 #0006 NEQ2 ,&fallo JCN
&exito
LIT 'Y .Consola/escribir DEO
RTN
&fallo
LIT 'N .Consola/escribir DEO
RTN
@archivo
&nombre "prueba.txt 00
&datos "hola 0a
```
¡observe lo similar que es a la subrutina cargar-archivo!
las únicas diferencias, además del uso de `Archivo/escribir` en lugar de `Archivo/leer`, son la longitud del archivo y la comparación para el corto éxito: en este caso sabemos con seguridad cuántos bytes deberían haberse escrito.
## un breve estudio de caso: el archivo de temas
los programas para el ordenador varvara escritos por 100r suelen tener la capacidad de leer un archivo "tema" que contiene seis bytes correspondientes a los tres cortos para los colores del sistema.
estos seis bytes están en orden: los dos primeros son para el canal rojo, los dos siguientes para el canal verde y los dos últimos para el canal azul.
este archivo tiene el nombre de ".theme" y se escribe en un directorio local de nasu cada vez que se guarda una hoja de sprites.
=> https://wiki.xxiivv.com/site/theme.html temas uxn
### leyendo el archivo de temas
podríamos adaptar nuestra subrutina anterior para cargar el archivo de temas y aplicar sus datos como colores del sistema:
```
@cargar-tema ( -- )
;tema/nombre .Archivo/nombre DEO2 ( establecer la dirección de la ruta del archivo )
#0006 .Archivo/largo DEO2 ( intentará leer 6 bytes )
( establecer la dirección de los datos a leer y hacer la lectura )
;tema/datos .Archivo/cargar DEO2
( comprobar el byte éxito y saltar según corresponda )
.Archivo/exito DEI2 #0006 NEQ2 ,&fallo JCN
&exito
( establecer los colores del sistema a partir de los datos leídos )
;tema/r LDA2 .Sistema/r DEO2
;tema/g LDA2 .Sistema/g DEO2
;tema/b LDA2 .Sistema/b DEO2
RTN
&fallo
RTN
@tema
&nombre ".theme 00
&datos ( reservando 6 bytes para los datos: )
&r $2 &g $2 &b $2
```
observe cómo las etiquetas &datos y &r apuntan a la misma ubicación: ¡no es un problema! :)
### escribiendo el archivo de temas
y para hacer la operación contraria, podemos leer los colores del sistema en nuestro espacio reservado en memoria, y luego escribirlos en el archivo:
```
@guardar-tema ( -- )
( leer los colores del sistema en la memoria del programa )
.Sistema/r DEO2 ;tema/r STA2
.Sistema/g DEO2 ;tema/g STA2
.Sistema/b DEO2 ;tema/b STA2
;tema/nombre .Archivo/nombre DEO2 ( establecer la dirección de la ruta del archivo )
#0006 .Archivo/leer DEO2 ( intentará escribir 6 bytes )
( establecer la dirección de los datos y hacer la escritura )
;tema/datos .Archivo/guardar DEO2
( comprobar el byte de éxito y saltar según corresponda )
.Archivo/exito DEI2 #0006 NEQ2 ,&fallo JCN
&exito
( ¿informar el éxito? )
RTN
&fallo
RTN
```
¡te invito a comparar estas subrutinas con las presentes en los programas 100r como nasu!
=> https://git.sr.ht/~rabbits/nasu/tree/main/item/src/main.tal código fuente de nasu
# el dispositivo fechahora o "datetime"
el dispositivo fechahora puede ser útil para el cronometraje de baja precisión y/o para las visualizaciones del tiempo.
tiene varios campos que podemos leer, todos ellos basados en la hora del sistema actual y la zona horaria:
```
|b0 @FechaHora [ &ano $2 &mes $1 &dia $1 &hora $1 &minuto $1 &segundo $1 &ddls $1 &dda $2 &eshdv $1 ]
```
* el byte año corresponde al número de año de la llamada era común
* el byte mes cuenta los meses desde enero (es decir, enero es 0, febrero 1, etc.)
* el byte día cuenta los días del mes a partir del 1
* Los bytes hora, minuto y segundo corresponden a lo que se espera: sus valores van de 0 a 23, o de 0 a 59 respectivamente.
* ddls (día de la semana) es un byte que cuenta los días desde el domingo (es decir, el domingo es 0, el lunes es 1, el martes es 2, etc.).
* dda (día del año) es un byte que cuenta los días desde el 1 de enero (es decir, el 1 de enero es 0, el 2 de enero es 1, etc.)
* eshdv ( es horario de verano) es una bandera, 01 si es horario de verano y 00 si no lo es.
basándonos en esto, debería ser sencillo utilizarlos. por ejemplo, para leer la hora del día en la pila, haríamos:
```
.FechaHora/hora DEI
```
## algunas posibilidades basadas en el tiempo
¡te invito a desarrollar una visualización creativa del tiempo!
tal vez puedas usar estos valores como coordenadas para algunos sprites, o tal vez puedas usarlos como tamaños o límites para figuras creadas con bucles.
o ¿qué tal dibujar sprites condicionalmente, y/o cambiar los colores del sistema dependiendo de la hora? :)
¡también puedes utilizar los valores de la fecha y la hora como semillas para generar alguna pseudo-aleatoriedad!
por último, recuerda que para cronometrar eventos con más precisión que segundos puedes contar las veces que se ha disparado el vector pantalla.
# el dispositivo de audio
por fin, ¡el dispositivo de audio! o debería decir, ¡los dispositivos de audio!
varvara tiene cuatro dispositivos estéreo idénticos (o "canales"), que se mezclan antes de pasar a los altavoces/auriculares:
```
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|40 @Audio1 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|50 @Audio2 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|60 @Audio3 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
```
de forma similar a como en el dispositivo de pantalla podemos dibujar apuntando a direcciones con datos de sprite, en los dispositivos de audio podremos reproducir sonidos apuntando a direcciones con datos de audio (muestras o "samples").
extendiendo la analogía: de forma similar a como podemos dibujar sprites en diferentes posiciones en la pantalla, podemos reproducir nuestras muestras a diferentes velocidades, volumen y envolventes.
supondremos que no estás familiarizado con estos conceptos, así que los discutiremos brevemente.
## muestras
como hemos mencionado anteriormente, podemos pensar en los datos de las muestras como el equivalente a los datos de los sprites.
tienen que estar en la memoria del programa, tienen una longitud que debemos conocer, y podemos referirnos a ellos mediante etiquetas.
el ejemplo piano.tal en el repositorio uxn, tiene varios de ellos, todos de 256 bytes de largo:
```
@piano-pcm
8182 8588 8d91 959b a1a6 aaad b2b5 b8bd
c1c7 cbd0 d5d9 dde1 e5e5 e4e4 e1dc d7d1
cbc5 bfb8 b2ac a6a2 9c97 928d 8884 807c
7977 7574 7372 7272 7273 7372 706d 6964
605b 5650 4d49 4643 4342 4244 4548 4a4d
5052 5556 5758 5554 5150 4c4a 4744 423f
3d3c 3a38 3835 3431 3030 2f31 3336 393e
4449 4e54 5a60 666b 7175 7b82 8990 989e
a6ab b1b6 babd bebf bfbe bbb9 b6b3 b0ae
aaa8 a6a3 a19e 9c9a 9997 9696 9798 9b9e
a1a4 a6a9 a9ac adad adae aeaf b0b0 b1b1
b3b3 b4b4 b4b3 b3b1 b0ad abab a9a9 a8a8
a7a5 a19d 9891 8b84 7e77 726e 6b6b 6b6c
6f71 7477 7776 7370 6c65 5e56 4e48 423f
3d3c 3b3a 3a39 3838 3839 393a 3c3e 4146
4a50 575b 6064 686a 6e70 7274 7677 7a7d
@violin-pcm
8186 8d94 9ba0 a3a7 acb1 b5bc c2c7 cacc
cecf d0d1 d3d5 d8db dee1 e3e5 e6e5 e5e3
dfdc d7d0 c8c2 bbb2 a99f 968c 847c 746e
675f 5851 4b43 3e3a 3533 312e 2c2b 2826
2422 2122 2327 2d34 3c44 4c57 5f68 7075
7b80 8487 8789 8a8c 8d90 9397 999c 9ea0
a2a2 a2a0 9c97 9491 8f8e 908f 918f 8e88
827a 726a 6058 5047 423f 3f40 4245 4748
4949 4746 4545 4a4f 5863 717f 8b9a a6b1
b8be c1c1 bfbd bab5 b1af acac aeb1 b7bc
c2c9 cfd3 d5d4 d3d3 d1ce cbc6 c0ba b3ab
a39a 8f85 7b72 6c67 6462 605f 5e5d 5b58
5550 4d49 4848 4949 4a4d 5052 5558 5b5e
6164 686c 7074 7677 7979 7a7b 7b7a 7977
7473 6f6e 6b69 696b 6f72 7576 7574 716b
655d 554e 4742 3f3f 4045 4b52 5a62 6b74
@sin-pcm
8083 8689 8c8f 9295 989b 9ea1 a4a7 aaad
b0b3 b6b9 bbbe c1c3 c6c9 cbce d0d2 d5d7
d9db dee0 e2e4 e6e7 e9eb ecee f0f1 f2f4
f5f6 f7f8 f9fa fbfb fcfd fdfe fefe fefe
fffe fefe fefe fdfd fcfb fbfa f9f8 f7f6
f5f4 f2f1 f0ee eceb e9e7 e6e4 e2e0 dedb
d9d7 d5d2 d0ce cbc9 c6c3 c1be bbb9 b6b3
b0ad aaa7 a4a1 9e9b 9895 928f 8c89 8683
807d 7a77 7471 6e6b 6865 625f 5c59 5653
504d 4a47 4542 3f3d 3a37 3532 302e 2b29
2725 2220 1e1c 1a19 1715 1412 100f 0e0c
0b0a 0908 0706 0505 0403 0302 0202 0202
0102 0202 0202 0303 0405 0506 0708 090a
0b0c 0e0f 1012 1415 1719 1a1c 1e20 2225
2729 2b2e 3032 3537 3a3d 3f42 4547 4a4d
5053 5659 5c5f 6265 686b 6e71 7477 7a7d
@tri-pcm
8082 8486 888a 8c8e 9092 9496 989a 9c9e
a0a2 a4a6 a8aa acae b0b2 b4b6 b8ba bcbe
c0c2 c4c6 c8ca ccce d0d2 d4d6 d8da dcde
e0e2 e4e6 e8ea ecee f0f2 f4f6 f8fa fcfe
fffd fbf9 f7f5 f3f1 efed ebe9 e7e5 e3e1
dfdd dbd9 d7d5 d3d1 cfcd cbc9 c7c5 c3c1
bfbd bbb9 b7b5 b3b1 afad aba9 a7a5 a3a1
9f9d 9b99 9795 9391 8f8d 8b89 8785 8381
7f7d 7b79 7775 7371 6f6d 6b69 6765 6361
5f5d 5b59 5755 5351 4f4d 4b49 4745 4341
3f3d 3b39 3735 3331 2f2d 2b29 2725 2321
1f1d 1b19 1715 1311 0f0d 0b09 0705 0301
0103 0507 090b 0d0f 1113 1517 191b 1d1f
2123 2527 292b 2d2f 3133 3537 393b 3d3f
4143 4547 494b 4d4f 5153 5557 595b 5d5f
6163 6567 696b 6d6f 7173 7577 797b 7d7f
@sierra-pcm
8282 8183 8384 8685 8888 8889 8a8b 8c8c
8e8e 8f90 9092 9193 9494 9596 9699 9899
9b9a 9c9c 9c9d 9ea0 a1a0 a2a2 a3a5 a4a6
a7a7 a9a8 a9aa aaac adad aeae b0b0 b1b3
b2b4 b5b5 b6b7 b9b8 b9bb babc bdbc bdbe
bfc1 bfc1 c3c1 c4c5 c5c6 c6c7 c9c7 cbca
cbcc cdcd cfcf d2d0 d2d2 d2d5 d4d5 d6d7
d8d8 d9dc d9df dadf dce1 dde5 dce6 dceb
cb1f 1b1e 1c21 1c21 1f23 2025 2127 2329
2529 2829 2a2b 2b2e 2d2f 302f 3231 3234
3334 3536 3836 3939 3a3b 3b3d 3e3d 3f40
4042 4242 4444 4646 4748 474a 4a4b 4d4c
4e4e 4f50 5052 5252 5554 5557 5759 5959
5b5b 5c5d 5d5f 5e60 6160 6264 6365 6566
6867 6969 6a6c 6c6d 6d6e 706f 7071 7174
7475 7576 7777 797a 7a7c 7b7c 7e7d 7f7f
```
=> https://git.sr.ht/~rabbits/uxn/tree/main/item/projects/examples/demos/piano.tal piano.tal código fuente
¿y qué significan estos números?
en el contexto de varvara, podemos entenderlos como múltiples bytes sin signo (u8) que corresponden a las amplitudes de la onda sonora que componen la muestra.
un "cabezal de reproducción" visita cada uno de estos números durante un tiempo determinado, y los utiliza para establecer la amplitud de la onda sonora.
las siguientes imágenes muestran la forma de la onda (o "waveform") de cada una de estas muestras.
cuando hacemos un bucle con estas formas de onda, ¡obtenemos un tono basado en su forma!
piano-pcm:
=> ./img/screenshot_uxn-waveform_piano.png forma de onda de la muestra de piano
violín-pcm:
=> ./img/screenshot_uxn-waveform_violin.png forma de onda de la muestra de violín
sin-pcm:
=> ./img/screenshot_uxn-waveform_sin.png forma de onda de la muestra de sin
tri-pcm:
=> ./img/screenshot_uxn-waveform_tri.png forma de onda de la muestra de tri
sierra-pcm:
=> ./img/screenshot_uxn-waveform_saw.png forma de onda de la muestra de sierra
de forma similar a como hemos tratado los sprites, y de forma parecida al dispositivo de archivo comentado anteriormente, para fijar una muestra en el dispositivo de audio sólo tenemos que escribir su dirección y su longitud:
```
;sierra-pcm .Audio0/direc DEO2 ( establecer la dirección de la muestra )
#0100 .Audio0/largo DEO2 ( establecer la longitud de la muestra )
```
la frecuencia a la que se reproduce esta muestra (es decir, a la que la amplitud de la onda toma el valor del siguiente byte) viene determinada por el byte pitch.
## pitch
el byte pitch hace que la muestra comience a reproducirse cada vez que le escribimos, de forma similar a como el byte de sprite realiza el dibujo del sprite cuando le escribimos.
los primeros 7 bits (de derecha a izquierda) del byte corresponden a una nota midi, y por tanto a la frecuencia a la que se reproducirá la muestra.
el octavo bit es una bandera: cuando es 0 la muestra se reproducirá en bucle, y cuando es 1 la muestra se reproducirá sólo una vez.
normalmente querremos hacer un bucle de la muestra para generar un tono basado en ella. sólo cuando la muestra sea lo suficientemente larga tendrá sentido no hacer un bucle y reproducirla una vez.
con respecto a los bits para la nota midi, es una buena idea tener una tabla midi alrededor para ver los valores hexadecimales correspondientes a las diferentes notas.
=> https://wiki.xxiivv.com/site/midi.html tabla midi
el Do medio (C4, o 3c en midi) se asume como el tono por defecto de las muestras.
### un programa de "muestra"
en teoría, parecería que el siguiente programa debería reproducir nuestra muestra a esa frecuencia, ¿o no?
```
( hola-sonido.tal )
( dispositivos )
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
( programa principal )
|0100
;sierra-pcm .Audio0/direc DEO2 ( establecer la dirección de la muestra )
#0100 .Audio0/largo DEO2 ( establecer la longitud de la muestra )
#3c .Audio0/pitch DEO ( establecer el pitch como Do medio )
BRK
```
¡no realmente!
para poder escuchar el sonido, necesitamos dos cosas más: ajustar el volumen del dispositivo y ajustar la envolvente ADSL.
## volumen
el byte de volumen se divide en dos nibbles: el nibble alto corresponde al volumen del canal izquierdo, y el nibble bajo corresponde al volumen del canal derecho.
por lo tanto, cada canal tiene 16 niveles posibles: 0 es el mínimo, y f el máximo.
lo siguiente establecería el volumen máximo en el dispositivo:
```
#ff .Audio0/volumen DEO ( establecer el volumen máximo en izquierda y derecha )
```
¡aunque las muestras son mono, podemos panoramizarlas con el byte de volumen para obtener un sonido estéreo!
## envolvente ADSR
el último componente que necesitamos para reproducir audio es la envolvente ADSR.
ADSR son las siglas de ataque, decaimiento, sostenimiento y relajación. es el nombre de una "envolvente" común que modula la amplitud de un sonido de principio a fin.
en el ordenador varvara, los componentes ADSR funcionan de la siguiente manera:
* la sección de ataque es el tiempo que tarda en pasar la amplitud del sonido que se está reproduciendo de 0 a 100%.
* luego, la sección de decaimiento es el tiempo que se necesita para llevar la amplitud del 100% al 50%.
* luego, durante la sección de sostenimiento la amplitud se mantiene al 50%
* y finalmente, en la sección de relajación la amplitud pasa del 50% al 0%.
cada una de estas transiciones se realiza de forma lineal.
en el corto ADSR del dispositivo de audio, hay un nibble para cada uno de los componentes: por lo tanto, cada uno puede tener una duración de 0 a f.
las unidades para estas duraciones son 15vos de segundo.
como ejemplo, si la duración del componente de ataque es 'f', entonces durará un segundo (15/15 de segundo, en decimal).
lo siguiente establecerá la duración máxima de cada uno de los componentes, haciendo que el sonido dure 4 segundos en total:
```
#ffff .Audio0/adsr
```
ok, ¡ahora estamos listos para reproducir el sonido!
## reproduciendo la muestra
¡el siguiente programa tiene ahora los cinco componentes que necesitamos para reproducir un sonido: una dirección de muestra, su longitud, las duraciones de adsr, el volumen, y su pitch!
```
( hola-sonido.tal )
( dispositivos )
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
( programa principal )
|0100
;sierra-pcm .Audio0/direc DEO2 ( establecer la dirección de la muestra )
#0100 .Audio0/largo DEO2 ( establecer la longitud de la muestra )
#ffff .Audio0/adsr DEO2 ( establecer la envolvente )
#ff .Audio0/volumen DEO ( establecer el volumen máximo )
#3c .Audio0/pitch DEO ( establecer el pitch como Do central )
BRK
```
nota (!) que sólo se reproducirá el sonido una vez, y lo hace cuando se inicia el programa.
### algunos experimentos sugeridos
te invito a que experimentes modificando los valores del ADSR: ¿cómo cambia el sonido cuando sólo hay uno de ellos? ¿o cuando todos son números pequeños o con diferentes combinaciones de duraciones?
también, prueba a cambiar el byte pitch: ¿corresponden con tus oídos los valores midi que esperas?
¿y cómo cambia el sonido cuando usas una muestra diferente? ¿puedes encontrar o crear otras diferentes?
## tocar más de una vez
una vez que hemos configurado nuestro dispositivo de audio con una muestra, longitud, envolvente ADSR y volumen, podríamos reproducirlo una y otra vez (re)escribiendo un tono en un momento diferente; los demás parámetros pueden dejarse intactos.
por ejemplo, una macro como la siguiente podría permitirnos reproducir una nota de nuevo según el pitch dado en la parte superior de la pila:
```
%REPR-NOTA { .Audio0/pitch DEO } ( pitch -- )
```
cuando ocurriera un evento específico, podrías llamarlo:
```
#3c REPR-NOTA ( reproducir Do central )
```
ten en cuenta que cada vez que escribes un pitch, la reproducción de la muestra y la forma de la envolvente vuelve a empezar, independientemente de dónde se encuentre.
### algunas ideas
¿qué tal si implementas la reproducción de diferentes tonos presionando diferentes teclas en el teclado? podrías usar nuestros ejemplos anteriores, pero escribiendo un tono en el dispositivo en lugar de, por ejemplo, incrementar una coordenada :)
¿o qué tal si complementas nuestro programa pong de {tutorial de uxn día 6} con efectos de sonido, haciendo que el dispositivo toque una nota cada vez que la pelota rebote?
¿o qué tal si utilizas el vector de la pantalla para cronometrar la reproducción repetitiva de una nota? ¿o qué tal si haces que toque una melodía siguiendo una secuencia de notas? ¿podría venir esta secuencia de un archivo de texto? :)
## información de la reproducción
el dispositivo de audio nos proporciona dos formas de comprobar el estado de la reproducción durante el tiempo de ejecución:
* el corto de posición
* el byte de salida
cuando leemos el corto de posición, obtenemos la posición actual de la "cabeza lectora" en la muestra, empezando por 0 (es decir, la cabeza lectora está al principio de la muestra) y terminando en la longitud de la muestra menos uno.
el byte de salida nos permite leer la amplitud de la envolvente. devuelve 0 cuando la muestra no se está reproduciendo, por lo que se puede utilizar como una forma de saber que la reproducción ha terminado.
## polifonía
la idea de tener cuatro dispositivos de audio es que podemos tenerlos todos sonando a la vez, y cada uno puede tener una muestra, envolvente ADSR, volumen y tono diferentes.
esto nos da muchas más posibilidades:
¿quizás en un juego podría haber una melodía sonando de fondo junto con sonidos incidentales relacionados con la jugabilidad?
¿tal vez se pueda construir un secuenciador en el que se puedan controlar los cuatro dispositivos como pistas diferentes?
¿o tal vez crear una plataforma de livecoding para tener un diálogo con cada uno de los cuatro instrumentos?
en cualquier caso, ¡no dudes en compartir lo que crees! :)
# el final
aunque no lo creas, ¡este es el final!
¡has llegado al final de la serie del tutorial! ¡felicidades!
¡espero que lo hayas disfrutado y que lo veas como el comienzo de tu viaje uxn!
¡nos encantaría ver lo que creas! no dudes en compartirlo en mastodon, en el foro o en el canal irc (¡o incluso por correo electrónico!)
=> https://llllllll.co/t/uxn-virtual-computer/ uxn en el foro lines (inglés)
=> ./contacto.gmi {contacto}
canal irc: #uxn en irc.esper.net
pero antes de hacer todo esto, ¡no te olvides de tomar un descanso! :)
¡nos vemos pronto!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)

View File

@ -1,4 +1,4 @@
# uxn tutorial appendix b: repeating a tile inside a rectangle
# uxn tutorial appendix a: repeating a tile inside a rectangle
in the first part of {uxn tutorial day 6} we discuss how to cover the background of the screen with a given tile.
@ -34,7 +34,6 @@ let's start with the following program as a template. it includes the data for a
#01c0 .System/g DEO2
#2ce5 .System/b DEO2
BRK
@tile-background 1122 4488 1122 4488
@ -167,8 +166,8 @@ the following shows our program in context, completely filling the first row of
( hello-background.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
( macros )
%RTN { JMP2r }
@ -209,7 +208,7 @@ RTN
similar to what we just did: what's a procedure we could follow to repeat vertically a row starting from y, and ending at a limit corresponding to y+height?
following the same strategy, we could do
following the same strategy, we could do:
* draw row in y
* add 8 (the size of the tile) to y

View File

@ -45,6 +45,7 @@ let's see some examples!
### LIT2
first of all, let's recap. the following code will push number 02 down onto the stack, then it will push number 30 (hexadecimal) down onto the stack, and finally add them together, leaving number 32 in the stack:
```
#02 #30 ADD
```
@ -98,6 +99,7 @@ now, let's compare with what happens with ADD2:
```
in this case we are pushing the same 4 bytes down onto the stack, but ADD2 is doing the following actions:
* take the top element of the stack (08), and store it as the low byte of the first short
* take the new top element of the stack (00), and store it as the high byte of the first short, that is now 0008
* take the new top element of the stack (04), and store it as the low byte of the second short
@ -178,7 +180,7 @@ we could write that as follows:
( hello-screen.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
( main program )
|0100
@ -308,7 +310,7 @@ let's try it all together! the following code will draw a pixel with color 1 in
#41 .Screen/pixel DEO
```
the complete program would look as follows:
the complete program would look as follows:
```
( hello-pixel.tal )
@ -575,7 +577,6 @@ the eight possible values of this high nibble, used for drawing a 1bpp sprite, a
+ <tr><td>6</td><td>foreground</td><td>yes</td><td>no</td></tr>
+ <tr><td>7</td><td>foreground</td><td>yes</td><td>yes</td></tr>
+ </table>
& * 0: draw a 1bpp sprite in the background, original orientation
& * 1: draw a 1bpp sprite in the background, flipped horizontally
& * 2: draw a 1bpp sprite in the background, flipped vertically
@ -647,7 +648,6 @@ note that 0 in the low nibble will clear the tile.
additionally, 5, 'a' and 'f' in the low nibble will draw the pixels that are "on" but will leave the ones that are "off" as is: this will allow you to draw over something that has been drawn before, without erasing it completely.
## hello sprite
let's do this! the following program will draw our sprite once:
@ -862,7 +862,6 @@ the eight possible values for this nibble are:
+ <tr><td>e</td><td>foreground</td><td>yes</td><td>no</td></tr>
+ <tr><td>f</td><td>foreground</td><td>yes</td><td>yes</td></tr>
+ </table>
& * 8: draw a 2bpp sprite in the background, original orientation
& * 9: draw a 2bpp sprite in the background, flipped horizontally
& * a: draw a 2bpp sprite in the background, flipped vertically
@ -897,7 +896,6 @@ the low nibble will allow us to choose between many combinations of colors assig
+ <tr><td>e</td><td>3</td><td>2</td><td>3</td><td>1</td></tr>
+ <tr><td>f</td><td>none</td><td>3</td><td>1</td><td>2</td></tr>
+ </table>
& * 0: colors 0, 0, 1, 2
& * 1: colors 0, 1, 2, 3
& * 2: colors 0, 2, 3, 1
@ -1157,7 +1155,6 @@ in {uxn tutorial day 3} we start working with interactivity using the keyboard,
however, i invite you to take a break, and maybe keep exploring drawing in the uxn screen via code, before continuing!
# support
if you enjoyed this tutorial and found it helpful, consider sharing it and giving it your {support} :)

View File

@ -22,7 +22,7 @@ its ports are normally defined as follows:
* the length short is the amount of bytes to read or write: don't forget that the program memory is ffff plus 1 bytes long, and that the program itself is stored there!
* the read short is for the starting memory address where the read data should be stored
* the write short is for the starting memory address where the data to be written is stored
* the stat short is similar to load, but reads the directory entry for the filename
* the stat short is similar to read, but reads the directory entry for the filename
* the delete byte deletes the file when any value is written to it
* setting the append byte to 01 makes write append data to the end of the file. when the append byte has the the default value, 00, write overwrites the contents from the start