Merge pull request 'Revisión del día 1 del tutorial de Uxn' (#6) from Roboe/compudanzas:revisión/día-1 into main

Reviewed-on: https://codeberg.org/sejo/compudanzas/pulls/6
This commit is contained in:
sejo 2022-01-31 20:23:07 +01:00
commit 28c9247c71
9 changed files with 294 additions and 294 deletions

View File

@ -2,11 +2,11 @@
una guía introductoria y pausada para programar la computadora varvara basada en el núcleo {uxn}.
> El ecosistema de Uxn es un espacio de juego y exploración de computación personal, creado para desarrollar y utilizar pequeñas herramientas y juegos, y programable en su propio lenguaje ensamblador.
> El ecosistema de Uxn es un espacio de juego y exploración de computación personal, creado para desarrollar y utilizar pequeñas herramientas y juegos y programable en su propio lenguaje ensamblador.
=> https://100r.co/site/uxn.html 100R - uxn
también puedes conseguir una versión "offline" de este material, y apoyar nuestros proyectos, con el libro {introducción a programación uxn}.
también puedes conseguir una versión "offline" de este material y apoyar nuestros proyectos, con el libro {introducción a programación uxn}.
esta es una traducción del {uxn tutorial} a cargo de ~jota.
@ -15,7 +15,7 @@ esta es una traducción del {uxn tutorial} a cargo de ~jota.
# día 1
en esta primera sección del tutorial vamos a hablar de los aspectos básicos de la computadora uxn llamada varvara, su paradigma de programación, su arquitectura, y por qué podrías queres aprender a programar en ella.
en esta primera sección del tutorial vamos a hablar de los aspectos básicos de la computadora uxn llamada varvara, su paradigma de programación, su arquitectura y por qué podrías querer aprender a programar en ella.
también vamos a saltar directo a nuestros primeros programas simples para demostrar conceptos fundamentales que desarrollaremos en los días siguientes.
@ -25,13 +25,13 @@ también vamos a saltar directo a nuestros primeros programas simples para demos
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 cortos (2 bytes) además de los números de un sólo byte en uxntal.
también discutiremos el trabajo con cortos (2 bytes) además de los números de un solo byte en uxntal.
{tutorial de uxn día 2}
# día 3
aquí introducimos el uso del dispositivo controlador en la computadora varvara: esto nos permite agregar interactividad a nuestros programas, y empezar a implementar control de flujo en uxntal.
aquí introducimos el uso del dispositivo controlador en la computadora varvara: esto nos permite agregar interactividad a nuestros programas y empezar a implementar control de flujo en uxntal.
también hablamos de instrucciones lógicas y de manipulación de la pila en uxntal.
@ -47,7 +47,7 @@ también hablamos del uso de la memoria del programa como un espacio para datos
# día 5
aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones, y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo de retención.
aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo de retención.
también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos.
@ -59,7 +59,7 @@ aquí hablamos de cómo podemos integrar todo lo que hemos cubierto para crear s
basamos nuestra discusión en una recreación del clásico juego pong.
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles, y para comprobar las colisiones.
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles y para comprobar las colisiones.
{tutorial de uxn día 6}

View File

@ -2,13 +2,13 @@
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:
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)
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.
@ -41,7 +41,7 @@ BRK
## 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?
¿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:
@ -53,7 +53,7 @@ una forma sería algo así como:
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.
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.
@ -134,7 +134,7 @@ 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).
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:
@ -206,7 +206,7 @@ RTN
## 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?
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:
@ -216,7 +216,7 @@ siguiendo la misma estrategia, podríamos hacer:
### 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.
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.
@ -245,7 +245,7 @@ el siguiente código se basa en el bucle anterior de x, pero ahora dibuja una fi
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.
¿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:
@ -257,7 +257,7 @@ podemos abordar este problema bien con alguna "manipulación de la pila ", o bie
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á!
si solo quisiéramos cubrir toda la pantalla con un sprite, ya tenemos todo el código necesario: solo 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 :)
@ -272,7 +272,7 @@ en primer lugar, la dirección del tile es el valor de la parte superior de la p
.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.
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:
@ -353,7 +353,7 @@ STH2kr ( ptra: x^ ancho^ / pret: limite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: limite-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:
¿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: limite-y^ x^ ancho^ )
@ -563,7 +563,7 @@ 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.
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 solo escritura, dicen), o algo en el medio.
## dibuja-fondo

View File

@ -1,6 +1,6 @@
# 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 qué 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.
@ -8,7 +8,7 @@ también saltaremos directo a nuestros primeros programas simples para demostrar
o primero que nada... ¿qué es uxn?
> El ecosistema de Uxn es un espacio de juego y exploración de computación personal, creado para desarrollar y utilizar pequeñas herramientas y juegos, y programable en su propio lenguaje ensamblador.
> El ecosistema de Uxn es un espacio de juego y exploración de computación personal, creado para desarrollar y utilizar pequeñas herramientas y juegos y programable en su propio lenguaje ensamblador.
=> https://100r.co/site/uxn.html 100R - uxn
@ -20,12 +20,12 @@ personalmente, veo en ella las siguientes virtudes:
* hecha para el largo plazo, conforme a la escala humana
* hecha para aplicaciones audiovisuales interactivas
* arquitectura y set de instrucciones simple (¡sólo 32 instrucciones!)
* primero-offline: funciona localmente y sólo necesitas unos pocos archivos de documentación para avanzar
* arquitectura y conjunto de instrucciones simple (¡solo 32 instrucciones!)
* primero-offline: funciona localmente y solo necesitas unos pocos archivos de documentación para avanzar
* ámbito de práctica y experimentación de computación dentro de límites
* ya portada a plataformas de computación actuales y de varios años de antigüedad
¡todos estos conceotos suenan genial para mí, y espero que para tí también!
¡todos estos conceotos suenan genial para mí y espero que para ti también!
sin embargo, noto algunos aspectos que pueden hacerla parecer no tan asequible:
@ -36,9 +36,9 @@ la idea de este tutorial es explorar estos dos aspectos y revelar cómo trabajan
# notación postfija (y la pila)
el núcleo uxn está inspirado por las máquinas forth en que utiliza la recombinación de componentes simples para lograr soluciones apropiadas, y en que es una máquina basada en pila.
el núcleo uxn está inspirado por las máquinas forth en que utiliza la recombinación de componentes simples para lograr soluciones apropiadas y en que es una máquina basada en pila.
esto implica que está principalmente basada en interacciones con una pila "push down", dónde las operaciones son indicadas mediante la llamada notación postfija.
esto implica que está principalmente basada en interacciones con una pila "push down", donde las operaciones son indicadas mediante la llamada notación postfija.
> Notación Polaca Inversa (NPR), también conocida como notación postfija polaca o simplemente notación postfija, es una notación matemática en la que los operadores siguen a sus operandos [...]
@ -54,9 +54,9 @@ en notación postfija, la suma de nos números sería escrita de la siguiente fo
dónde, leyendo de izquierda a derecha:
* el número 1 es empujado en la pila
* el número 48 es empujado en la pila
* + toma dos elementos de la parte superior de la pila, los suma, y empuja el resultado en la pila
* el número 1 es empujado a la pila
* el número 48 es empujado a la pila
* + toma dos elementos de la parte superior de la pila, los suma y empuja el resultado a la pila
el libro Starting Forth tiene algunas buenas ilustraciones sobre este proceso de suma:
@ -84,10 +84,10 @@ también podemos escribirla de muchas otras maneras, por ejemplo:
48 3 5 + 2 / +
```
¡asegúrate de que éstas expresiones funcionan y son equivalentes! sólo debes seguir estas reglas, leyendo de izquierda a derecha:
¡asegúrate de que estas expresiones funcionan y son equivalentes! solo debes seguir estas reglas, leyendo de izquierda a derecha:
* si es un número, empujarlo a la pila
* si es un operador, tomar dos elementos de la parte superior de la pila, aplicar la operación, y empujar el resultado en la pila.
* si es un operador, tomar dos elementos de la parte superior de la pila, aplicar la operación y empujar el resultado a la pila.
nota: en el caso de la división, los operandos siguen el mismo orden de izquierda a derecha. 3/2 sería escrito como:
@ -95,7 +95,7 @@ nota: en el caso de la división, los operandos siguen el mismo orden de izquier
3 2 /
```
irás descubriendo cómo el uso de la pila puede ser muy poderoso ya que ahorra operandos y/o resultados intermedios sin necesidad de que asignemos explicitamente un espacio en memoria para ellos (por ejemplo, mediante el uso de "variables" en otros lenguajes de programación)
irás descubriendo cómo el uso de la pila puede ser muy poderoso ya que ahorra operandos o resultados intermedios sin necesidad de que asignemos explícitamente un espacio en memoria para ellos (por ejemplo, mediante el uso de "variables" en otros lenguajes de programación)
¡vamos a volver a la notación postfija y la pila muy pronto!
@ -111,15 +111,15 @@ uxn puede manejar también palabras binarias de 16 bits (2 bytes), también cono
los números en uxn son expresados utilizando el sistema hexadecimal (base 16), en el que cada dígito (nibble) va de 0 a 9 y luego de 'a' a 'f' (en minúscula).
un byte necesita dos dígitos hexadecimales (nibbles) para ser expresado, y un corto necesita cuatro.
un byte necesita dos dígitos hexadecimales (nibbles) para ser expresado y un corto necesita cuatro.
## el cpu uxn
se ha dicho que el cpu uxn es una betabel, capaz de ejecutar 32 instrucciones diferentes con tres banderas de modo diferentes.
se ha dicho que el cpu uxn es una betabel o remolacha, capaz de ejecutar 32 instrucciones diferentes con tres banderas de modo diferentes.
cada instrucción junto con sus banderas de modo puede ser codificada en una sola palabra de 8 bits.
todas estas instrucciones operan sobre elementos en la pila, ya sea tomando de ella sus operandos y/o empujando en ella sus resultados.
todas estas instrucciones operan sobre elementos en la pila, ya sea tomando de ella sus operandos o empujando en ella sus resultados.
vamos a ir cubriendo lentamente estas instrucciones durante este tutorial.
@ -134,7 +134,7 @@ la memoria en la computadora uxn consiste en cuatro espacios separados:
cada byte en la memoria principal posee una dirección de 16 bits (2 bytes) de tamaño, mientras que cada byte en la memoria de entrada/salida posee una dirección de 8 bits (1 byte) de tamaño. ambos pueden ser accedidos aleatoriamente.
los primeros 256 bytes de la memoria principal constituyen una sección llamada página cero. esta sección puede ser referenciada por 8 bits (1 byte), y su propósito es almacenar datos durante el tiempo de ejecución de la máquina.
los primeros 256 bytes de la memoria principal constituyen una sección llamada página cero. esta sección puede ser referenciada por 8 bits (1 byte) y su propósito es almacenar datos durante el tiempo de ejecución de la máquina.
hay tres instrucciones diferentes para interactuar con cada uno de estos espacios de memoria.
@ -150,11 +150,11 @@ el contador de programa es una palabra de 16 bits que indica la dirección del p
una vez que el cpu lee un byte, lo decodifica como instrucción y lo ejecuta.
la instrucción va a implicar normalmente un cambio en la(s) pila(s), y algunas veces también un cambio en el flujo normal del contador de programa: en lugar de apuntar al siguiente byte en memoria, puede ser apuntado a otro lado, "saltando" de un lugar en memoria a otro.
la instrucción va a implicar normalmente un cambio en la o las pilas y algunas veces también un cambio en el flujo normal del contador de programa: en lugar de apuntar al siguiente byte en memoria, puede ser apuntado a otro lado, "saltando" de un lugar en memoria a otro.
# instalación y toolchain
##instalación local
## instalación local
¿liste? para ejecutar varvara localmente y fuera de la red, tendríamos que obtener el ensamblador uxn (uxnasm) y el emulador (uxnemu) de su repositorio git:
@ -176,7 +176,7 @@ en una terminal en debian/ubuntu, correr:
$ sudo apt install libsdl2-dev
```
on en guix:
o en guix:
``` guix install sdl2
$ guix install sdl2
@ -192,7 +192,7 @@ $ cd uxn
$ ./build.sh
```
si todo fué bién, verás varios mensajes en la terminal y una pequeña ventana con el título uxn, y una aplicación demo: uxnemu está ahora corriendo una "rom" correspondiendo a esa aplicación.
si todo fue bien, verás varios mensajes en la terminal y una pequeña ventana con el título uxn y una aplicación demo: uxnemu está ahora corriendo una "rom" correspondiendo a esa aplicación.
## controles uxnemu
@ -213,7 +213,7 @@ en principio puedes hacer doble clic en uxnemu y que se ejecute. sin embargo, ut
puedes ajustar tu $PATH para tenerlos disponibles en todos lados.
la idea es que para correr un programa escrito en uxntal (el lenguaje ensamblador de uxn), primero tienes que ensamblarlo en una "rom", y luego puedes correr esta rom con el emulador.
la idea es que para correr un programa escrito en uxntal (el lenguaje ensamblador de uxn), primero tienes que ensamblarlo en una "rom" y luego puedes correr esta rom con el emulador.
por ejemplo, para correr {darena} que se encuentra en projects/examples/demos/ :
@ -239,17 +239,17 @@ estuvimos hablando antes sobre el cpu uxn y las 32 instrucciones que sabe cómo
ese lenguaje ensamblador uxntal implica que hay una relación uno a uno mapeando de una instrucción escrita en el lenguaje a una palabra de 8 bit correspondiente que el cpu puede interpretar.
por ejemplo, la instrucción ADD (suma) en uxntal es codificada como un byte con el valor 18 en hexadecimal, y corresponde al siguiente conjunto de de acciones: toma los dos elementos superiores de la pila, los suma, y empuja el resultado a la pila.
por ejemplo, la instrucción ADD (suma) en uxntal es codificada como un byte con el valor 18 en hexadecimal y corresponde al siguiente conjunto de de acciones: toma los dos elementos superiores de la pila, los suma y empuja el resultado a la pila.
en sistemas de tipo forth podemos ver el siguiente tipo de notación para expresar los operandos que una instrucción toma de la pila, y el(los) resultado(s) que empuja devuelta a la pila:
en sistemas de tipo forth podemos ver el siguiente tipo de notación para expresar los operandos que una instrucción toma de la pila y el o los resultados que empuja de vuelta a la pila:
```
ADD ( a b -- a+b )
```
esto significa que ADD toma el primer elemento desde arriba 'b', luego toma el siguiente primer elemento 'a', y empuja devuelta el resultado de sumar a+b.
esto significa que ADD toma el primer elemento desde arriba 'b', luego toma el siguiente primer elemento 'a' y empuja de vuelta el resultado de sumar a+b.
ahora que estamos en eso, hay una instrucción complementaria, SUB (resta) (opcode 19), que toma los dos elementos superiores de la pila, los resta, y empuja a la pila el resultado:
ahora que estamos en eso, hay una instrucción complementaria, SUB (resta) (opcode 19), que toma los dos elementos superiores de la pila, los resta y empuja a la pila el resultado:
```
SUB ( a b -- a-b )
@ -259,7 +259,7 @@ nota que el orden de los operandos es similar al de la división que discutimos
## un primer programa
escribamos el siguiente programa en nuestro editor de texto favorito, y guardémoslo como hola.tal:
escribamos el siguiente programa en nuestro editor de texto favorito y guardémoslo como hola.tal:
```
( hola.tal )
@ -295,13 +295,13 @@ acabamos de correr el siguiente programa en uxntal:
la primera línea es un comentario: los comentarios son encerrados entre paréntesis y debe haber un espacio entre cada paréntesis y el contenido. de manera similar a otros lenguajes de programación, los comentarios son ignorados por el ensamblador.
en la segunda linea ocurren varias cosas:
en la segunda línea ocurren varias cosas:
* |0100 : puede que recuerdes este número de antes - este es el valor inicial del contador de programa; la dirección del primer byte que el cpu lee. usamos esta notación para indicar que lo que esté escrito después será escrito en memoria después de esta dirección.
* LIT : esta aparece dos veces, y es una instrucción uxn con las siguientes acciones: empuja el siguiente valor en memoria a la pila, y hace que el contador de programa saltee ese byte.
* 68 : un número hexadecimal, que corresponde al código ascii del caracter 'h'
* 18 : un número hexadecimal, que corresponde a una dirección de entrada/salida: dispositivo 1 (consola), dirección 8.
* DEO : otra instrucción uxn, que podemos definir como lo siguiente: escribir el byte dado en la dirección de dispositivo dada, ambos tomados de la pila ( byte dirección -- )
* |0100: puede que recuerdes este número de antes - este es el valor inicial del contador de programa; la dirección del primer byte que el cpu lee. usamos esta notación para indicar que lo que esté escrito después será escrito en memoria después de esta dirección
* LIT: esta aparece dos veces y es una instrucción uxn con las siguientes acciones: empuja el siguiente valor en memoria a la pila y hace que el contador de programa salte ese byte
* 68: un número hexadecimal, que corresponde al código ascii del carácter 'h'
* 18: un número hexadecimal, que corresponde a una dirección de entrada/salida: dispositivo 1 (consola), dirección 8
* DEO: otra instrucción uxn, que podemos definir como lo siguiente: escribir el byte dado en la dirección de dispositivo dada, ambos tomados de la pila ( byte dirección -- )
leyendo el programa de izquierda a derecha, podemos ver el siguiente comportamiento:
@ -313,13 +313,13 @@ leyendo el programa de izquierda a derecha, podemos ver el siguiente comportamie
¿y qué es el dispositivo de entrada/salida con la dirección 18?
mirando en la tabla de dispositivos de la referencia varvara, podemos ver que el dispositivo con la dirección 1 en el nibble superior es la consola (entrada y salida estandard), y que la columna con la dirección 8 corresponde a "escritura".
mirando en la tabla de dispositivos de la referencia varvara, podemos ver que el dispositivo con la dirección 1 en el nibble superior es la consola (entrada y salida estándar) y que la columna con la dirección 8 corresponde a "escritura".
=> https://wiki.xxiivv.com/site/varvara.html varvara
asique, el dispositivo 18 corresponde a "escribir en consola", o salida estándar.
¡nuestro programa está enviando el valor hexadecimal 68 (caracter 'h') a la salida estandard!
¡nuestro programa está enviando el valor hexadecimal 68 (carácter 'h') a la salida estándar!
puedes ver los valores hexadecimales de los caracteres ascii en la siguiente tabla:
@ -329,7 +329,7 @@ puedes ver los valores hexadecimales de los caracteres ascii en la siguiente tab
observe que los números literales que escribimos, 0100, 18 y 68, se escriben en hexadecimal utilizando 4 dígitos correspondientes a dos bytes, o 2 dígitos correspondientes a un byte.
en uxntal sólo podemos escribir números de 2 ó 4 dígitos hexadecimales. si, por ejemplo, sólo nos interesara escribir un único dígito hexadecimal, tendríamos que incluir un 0 a su izquierda.
en uxntal solo podemos escribir números de 2 ó 4 dígitos hexadecimales. si, por ejemplo, solo nos interesara escribir un único dígito hexadecimal, tendríamos que incluir un 0 a su izquierda.
## rom ensamblada
@ -347,11 +347,11 @@ $ hexdump -C bin/hola.rom
00000005
```
80 es el "opcode" correspondiente a LIT, y 17 es el opcode correspondiente a DEO. ¡y ahí están nuestros 68 y 18!
80 es el "opcode" correspondiente a LIT y 17 es el opcode correspondiente a DEO. ¡y ahí están nuestros 68 y 18!
¡osea, efectivamente, nuestro programa ensamblado presenta una correspondencia uno a uno con las instrucciones que acabamos de escribir!
de hecho, podríamos haber escrito nuestro programa con estos números hexadecimales (el código máquina), y hubiera funcionado igual:
de hecho, podríamos haber escrito nuestro programa con estos números hexadecimales (el código máquina) y hubiera funcionado igual:
```
( hola.tal )
@ -381,17 +381,17 @@ si lo ensamblamos y corremos, tendremos un 'hola' en nuestra terminal, usando 25
¿ok, y... te gusta? ¿parece sencillo? ¿quizás innecesariamente complejo?
veremos algunas características de uxntal que hacen escribir y leer su código una experiencia más "cómoda".
veremos algunas características de uxntal que hacen que escribir y leer su código sea más cómodo.
# runas, etiquetas, macros
las runas son caracteres especiales que indican a uxnasm algún pre procesamiento a hacer al ensamblar nuestro programa.
las runas son caracteres especiales que indican a uxnasm algún preprocesamiento a hacer al ensamblar nuestro programa.
## runa de pad absoluto
ya vimos la primera de ellas: | define un pad absoluto: la dirección donde el siguiente elemento escrito será ubicado en memoria.
si la dirección es de 1 byte de longitud, es asumido que es una dirección de el espacio de entrada/salida o de la página cero.
si la dirección es de 1 byte de longitud, es asumido que es una dirección del espacio de entrada/salida o de la página cero.
si la dirección es de 2 bytes de longitud, es asumido que es una dirección de la memoria principal.
@ -399,7 +399,7 @@ si la dirección es de 2 bytes de longitud, es asumido que es una dirección de
hablemos de otra runa: #
éste caracter define un "hex literal": es básicamente un atajo para la instrucción LIT.
este carácter define un "hex literal": es básicamente un atajo para la instrucción LIT.
usando esta runa, podemos reescribir nuestro primer programa como:
@ -415,17 +415,17 @@ el siguiente tendría el mismo comportamiento que el programa de arriba, pero us
|0100 #6818 DEO
```
nota que sólo puedes usar esta runa para escribir los contenidos de uno o dos bytes (dos o cuatro nibbles).
nota que solo puedes usar esta runa para escribir los contenidos de uno o dos bytes (dos o cuatro nibbles).
importante: recuerda que esta runa (y las otras con la palabra "literal" en su nombre) es un atajo para la instrucción LIT. esto implica que uxn empujará estos valores hacia abajo en la pila. esto puede prestarse a confusión en algunos casos :)
si sólo queremos tener un número específico en la memoria principal, sin empujarlo a la pila, simplemente escribiríamos el número tal cual, "crudo". esta es la forma en que lo hicimos en nuestros primeros programas de arriba.
si solo queremos tener un número específico en la memoria principal, sin empujarlo a la pila, simplemente escribiríamos el número tal cual, "crudo". esta es la forma en que lo hicimos en nuestros primeros programas de arriba.
## runa de caracter "crudo" o "raw"
## runa de carácter "crudo" o "raw"
ésta es la runa de caracter raw: '
esta es la runa de carácter raw: '
nos permite que uxnasm decodifique el valor numérico de un caracter ascii.
nos permite que uxnasm decodifique el valor numérico de un carácter ascii.
nuestro "programa hola" luciría de la siguiente manera, usando las nuevas runas que acabamos de aprender:
@ -444,9 +444,9 @@ por eso debemos incluir una instrucción LIT.
## runas para etiquetas
incluso ahora que sabemos que #18 corresponde a empujar la dirección de dispositivo escribir en consola en la pila, por legibilidad y para asegurar nuestro código a futuro es una buena práctica asignar una serie de etiquetas que corresponderán a ese dispositivo y sus sub direcciones.
incluso ahora que sabemos que #18 corresponde a empujar a la pila la dirección de dispositivo del dispositivo para escribir en consola, por legibilidad y para asegurar nuestro código a futuro, es una buena práctica asignar una serie de etiquetas que corresponderán a ese dispositivo y sus subdirecciones.
la runa @ nos permite definir etiquetas, y la runa & nos permite definir sub etiquetas.
la runa @ nos permite definir etiquetas y la runa & nos permite definir subetiquetas.
por ejemplo, para el dispositivo de consola, la manera en que verías esto escrito en programas para la computadora varvara es la siguiente:
@ -454,27 +454,27 @@ por ejemplo, para el dispositivo de consola, la manera en que verías esto escri
|10 @Consola [ &vector $2 &lee $1 &pad $5 &escribe $1 &error $1 ]
```
podemos ver un pad absoluto a la dirección 10, que asigna lo siguiente a esa dirección. dado que la dirección consiste en un sólo byte, uxnasm asume que es para el espacio de memoria de entrada/salida o la página cero.
podemos ver un pad absoluto a la dirección 10, que asigna lo siguiente a esa dirección. dado que la dirección consiste en un solo byte, uxnasm asume que es para el espacio de memoria de entrada/salida o la página cero.
luego vemos la etiqueta @Consola: ésta etiqueta va a corresponder a la dirección 10.
luego vemos la etiqueta @Consola: esta etiqueta va a corresponder a la dirección 10.
los corchetes son ignorados, pero incluidos por legibilidad.
luego tenemos varias sub etiquetas, indicadas por la runa &, y pads relativos, indicados por la runa $. ¿cómo los leemos/interpretamos?
luego tenemos varias subetiquetas, indicadas por la runa &, y pads relativos, indicados por la runa $. ¿cómo los leemos/interpretamos?
* la sub etiqueta &vector tiene la misma dirección que su etiqueta madre @Consola: 10
* la subetiqueta &vector tiene la misma dirección que su etiqueta madre @Consola: 10
* $2 salta dos bytes (podemos leer esto como &vector siendo una dirección a una palabra de dos bytes de longitud)
* la sub etiqueta &lee tiene la dirección 12
* la subetiqueta &lee tiene la dirección 12
* $1 salta un byte (&lee sería una dirección para una palabra de 1 byte de longitud)
* la sub etiqueta &pad tiene la dirección 13
* $5 salta el resto de los bytes del primer grupo de 8 bytes del dispositivo: éstos bytes corresponden a las "entradas"
* la sub etiqueta &escribe tiene la dirección 18 (¡la que ya conocíamos!)
* la subetiqueta &pad tiene la dirección 13
* $5 salta el resto de los bytes del primer grupo de 8 bytes del dispositivo: estos bytes corresponden a las "entradas"
* la subetiqueta &escribe tiene la dirección 18 (¡la que ya conocíamos!)
* $1 salta un byte (&escribe sería una dirección para una palabra de 1 byte de longitud)
* la subetiqueta &error tiene la dirección 19
nada de esto sería traducido a código máquina, pero nos asiste al escribir código uxntal.
la runa para referirse a una dirección literal en la página cero o el espacio de direcciones de entrada/salida, es . (punto), y una / (barra) nos permite referirnos a una de sus sub etiquetas.
la runa para referirse a una dirección literal en la página cero o el espacio de direcciones de entrada/salida es . (punto) y una / (barra) nos permite referirnos a una de sus subetiquetas.
recuerda: al ser una runa de "dirección literal" va a agregar una instrucción LIT antes de la correspondiente dirección :)
@ -494,11 +494,11 @@ podemos reescribir nuestro "programa hola mundo" como sigue:
#0a .Consola/escribe DEO ( nuevalínea )
```
ahora esto empieza a parecerse más a los ejemplos que puedes encontrar en línea y/o en el repositorio uxn :)
ahora esto empieza a parecerse más a los ejemplos que puedes encontrar en línea o en el repositorio uxn :)
## macros
siguiendo con la herencia de FORTH, en uxntal podemos definir nuestras propias "palabras" que nos permiten agrupar y reutilizar instrucciones.
siguiendo con la herencia de forth, en uxntal podemos definir nuestras propias "palabras" que nos permiten agrupar y reutilizar instrucciones.
durante el ensamblado, estos macros son (recursivamente) reemplazados por los contenidos de sus definiciones.
@ -508,23 +508,23 @@ por ejemplo, podemos ver que el siguiente fragmento de código es repetido varia
.Consola/escribe DEO ( equivalente a #18 DEO, o a LIT 18 DEO )
```
podemos definir un macro llamado EMIT que tomará de la pila un byte correspondiente a un caracter, y lo imprimirá en la salida estandard. para esto, necesitamos la runa %, y llaves para la definición.
podemos definir un macro llamado EMIT que tomará de la pila un byte correspondiente a un carácter y lo imprimirá en la salida estándar. para esto, necesitamos la runa % y llaves para la definición.
¡no olvides los espacios!
```
( escribe un caracter a la salida estandard )
%EMIT { .Consola/escribe DEO } ( caracter -- )
( escribe un carácter a la salida estándar )
%EMIT { .Consola/escribe DEO } ( carácter -- )
```
para llamar a un macro, sólo escribimos su nombre:
para llamar a un macro, solo escribimos su nombre:
```
( imprime caracter h )
( imprime carácter h )
LIT 'h EMIT
```
podemos llamar macros dentro de macros, por ejemplo:
podemos llamar a macros dentro de macros, por ejemplo:
```
( imprime una nueva línea )
@ -541,8 +541,8 @@ usando todos estos macros y runas, nuestro programa puede terminar luciendo como
|10 @Consola [ &vector $2 &lee $1 &pad $5 &escribe $1 &error $1 ]
( macros )
( imprime un caracter en la salida estandard )
%EMIT { .Consola/escribe DEO } ( caracter -- )
( imprime un carácter en la salida estándar )
%EMIT { .Consola/escribe DEO } ( carácter -- )
( imprime una nueva línea )
%NL { #0a EMIT } ( -- )
@ -556,13 +556,13 @@ usando todos estos macros y runas, nuestro programa puede terminar luciendo como
termina siendo ensamblado en los mismos 25 bytes que los ejemplos de arriba, pero con suerte más legible y mantenible.
podemos "mejorar" este programa haciendo que un loop imprima los caracteres, pero estudiaremos eso más tarde :
podemos "mejorar" este programa haciendo que un bucle imprima los caracteres, pero estudiaremos eso más tarde.
# ejercicios
## reubicando EMIT
en nuestro programa previo, el macro EMIT es llamado justo después de empujar un caracter a la pila.
en nuestro programa previo, el macro EMIT es llamado justo después de empujar un carácter a la pila.
¿cómo reescribirías el programa para que primero empuje todos los caracteres y luego los emita (o haga "EMIT") todos con una secuencia como esta?
@ -574,7 +574,7 @@ EMIT EMIT EMIT EMIT EMIT
si miras en la tabla ascii, verás que el código hexadecimal 30 corresponde al dígito 0, 31 al dígito 1, siguiendo hasta el 39 que corresponde al dígito 9.
define un macro IMPRIMIR-DIGITO que toma un número (del 0 al 9) de la pila, e imprime el correspondiente dígito en la salida estandard.
define un macro IMPRIMIR-DIGITO que toma un número (del 0 al 9) de la pila, e imprime el correspondiente dígito en la salida estándar.
```
%IMPRIMIR-DIGITO { } ( número -- )
@ -588,20 +588,20 @@ recordemos que el número tendría que ser escrito como un byte completo para qu
# instrucciones del día 1
éstas son las instrucciones que cubrimos hoy:
estas son las instrucciones que cubrimos hoy:
* ADD: toma los dos elementos superiores de la pila, los suma, y empuja el resultado ( a b -- a+b )
* SUB: toma los dos elementos superiores de la pila, los resta, y empuja el resultado ( a b -- a-b )
* ADD: toma los dos elementos superiores de la pila, los suma y empuja el resultado ( a b -- a+b )
* SUB: toma los dos elementos superiores de la pila, los resta y empuja el resultado ( a b -- a-b )
* LIT: empuja el siguiente byte en memoria a la pila
* DEO: escribe el byte dado en la dirección de dispositivo dada, tomando ambos de la pila ( byte dirección -- )
# día 2
¡bién hecho!
¡bien hecho!
en el {tutorial de uxn día 2} empezamos 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 el!
en el {tutorial de uxn día 2} empezamos 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 él!
¡sin embargo, te invito a tomar un pequeño descanzo antes de continuar! :)
¡sin embargo, te invito a tomar un pequeño descanso antes de continuar! :)
# apoyo

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 el modo corto o "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 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}
@ -16,7 +16,7 @@ antes de pasar a dibujar en la pantalla, tenemos que hablar de 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á sólo uno de esos posibles valores.
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.
@ -34,7 +34,7 @@ contando de derecha a izquierda, el 6to bit de un byte que codifica una instrucc
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.
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.
@ -44,7 +44,7 @@ en uxntal, indicamos que queremos poner esta bandera añadiendo el dígito '2' a
### 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:
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
@ -70,7 +70,7 @@ 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:
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
@ -86,7 +86,7 @@ ahora veamos que pasa con la instrucción ADD cuando usamos el modo corto.
#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.
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
@ -101,9 +101,9 @@ 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
* toma el nuevo elemento superior de la pila (00), y lo almacena como el byte alto del segundo corto, que ahora es 0004
* 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
@ -122,7 +122,7 @@ en cualquier caso, es útil tener en cuenta cómo funcionan para algunos comport
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.
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 -- )
@ -130,7 +130,7 @@ 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.
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 )
@ -138,15 +138,15 @@ 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.
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.
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.
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.
@ -154,7 +154,7 @@ el puerto de 'escritura' del dispositivo de la consola que utilizamos la última
# 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).
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:
@ -162,11 +162,11 @@ en los ejemplos uxntal podemos ver sus etiquetas definidas de la siguiente maner
|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.
ignoraremos los primeros elementos por el momento y nos centraremos en los componentes de color.
## colores del sistema
el dispositivo de pantalla varvara sólo puede mostrar un máximo de cuatro colores a la vez.
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.
@ -205,7 +205,7 @@ prueba cambiar los valores del color 0, es decir, la primera columna, y mira lo
# el dispositivo de pantalla
como recapitulación: mencionamos que el dispositivo de pantalla sólo 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.
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!
@ -243,14 +243,14 @@ al principio, la capa del primer plano es completamente transparente: un proceso
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 pixel, y necesitamos establecer el byte 'pixel' a un valor específico para realizar realmente el dibujo.
para hacer esto necesitamos establecer un par de coordenadas x,y donde queremos que se dibuje el pixel y necesitamos establecer el byte 'pixel' 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
* 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:
@ -259,7 +259,7 @@ si quisiéramos dibujar un píxel en coordenadas ( 8, 8 ), estableceríamos sus
#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:
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
@ -335,13 +335,13 @@ el programa completo se vería de la siguiente manera:
¡wuju!
recuerda que puedes usar F1 para cambiar de nivel de zoom, y F3 para hacer capturas de pantalla de tus bocetos :)
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` sólo una vez:
Por ejemplo, podemos dibujar múltiples píxeles en una línea horizontal, estableciendo la coordenada `y` solo una vez:
```
( establecer coordenadas y )
@ -398,7 +398,7 @@ teniendo en cuenta esto, ¿se puede saber qué haría este código?
¡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 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 :)
@ -481,9 +481,9 @@ agradable, ¿no? ¡las operaciones ahora se ven más claras! y si quisiéramos t
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).
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 sólo podemos codificar si ese píxel está usando un color, o el otro.
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.
@ -493,13 +493,13 @@ almacenaremos y accederemos a estos tiles desde la memoria principal.
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).
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
``` el contorno de un cuadrado marcado con 1s y su interior marcado con 0s
11111111
10000001
10000001
@ -512,11 +512,11 @@ por ejemplo, podríamos diseñar un azulejo que corresponda al contorno de un cu
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}.
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
``` 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
@ -535,13 +535,13 @@ en uxntal, necesitamos etiquetar y escribir en la memoria principal los datos co
@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.
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.
## 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 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:
@ -551,7 +551,7 @@ para lograr esto, escribimos lo siguiente:
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.
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.
@ -563,7 +563,7 @@ de forma similar a lo que ya vimos con el píxel, el envío de un byte a .Pantal
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).
sin embargo, en este caso tendremos otras posibilidades: podemos rotar el sprite en el eje horizontal (x) o en el vertical (y).
los ocho valores posibles de este nibble alto, utilizados para dibujar un sprite de 1bpp, son:
@ -589,7 +589,7 @@ los ocho valores posibles de este nibble alto, utilizados para dibujar un sprite
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:
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 7</th><th>bit 6</th><th>bit 5</th><th>bit 4</th></tr>
@ -771,7 +771,7 @@ un solo tile de 2bpp de 8x8 píxeles necesita 16 bytes para ser codificada. esto
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
``` 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
@ -812,7 +812,7 @@ separamos nuestro tile en dos cuadrados diferentes, uno para los bits altos y ot
00000000 11111111
```
ahora podemos pensar en cada uno de estos cuadrados como sprites de 1bpp, y codificarlos en hexadecimal como lo hicimos antes:
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
@ -827,7 +827,7 @@ ahora podemos pensar en cada uno de estos cuadrados como sprites de 1bpp, y codi
## 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:
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
@ -847,7 +847,7 @@ el dispositivo de pantalla tratará esta dirección como un sprite 2bpp cuando u
### sprite de nibble alto 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.
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:
@ -990,7 +990,7 @@ nasu es una herramienta de 100R, escrita en uxntal, que facilita el diseño y la
=> 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.
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.
puedes exportar e importar archivos chr, que puedes incluir en tu código usando una herramienta como hexdump.
@ -998,13 +998,13 @@ puedes exportar e importar archivos chr, que puedes incluir en tu código usando
# 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.
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)
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.
@ -1019,7 +1019,7 @@ por ejemplo, el siguiente código cambiaría la pantalla a una resolución de 64
#01e0 .Pantalla/alto DEO2 ( altura de 480 )
```
tengamos en cuenta que esto sólo 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.
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!
@ -1054,7 +1054,7 @@ y = altopantalla/2
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 )
* 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:
@ -1068,9 +1068,9 @@ usando DIV, nuestra expresión traducida para el caso de la coordenada x, podrí
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, y/o a la izquierda.
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.
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.
@ -1080,7 +1080,7 @@ por ejemplo, dividir 10 (en decimal) entre 2 podría expresarse de la siguiente
#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.
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:
@ -1088,7 +1088,7 @@ 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.
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.
@ -1144,16 +1144,16 @@ además de cubrir los fundamentos del dispositivo de pantalla hoy, discutimos es
* 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 valor de desplazamiento y un número a desplazar con ese valor, y lo desplaza. el nibble inferior del valor de desplazamiento indica el desplazamiento a la derecha, y el nibble superior el desplazamiento a la izquierda ( número desplazo -- númerodesplazado )
* DIV: toma los dos primeros elementos de la pila, los divide y empuja el resultado ( a b -- a/b )
* SFT: toma un valor de desplazamiento y un número a desplazar con ese valor y lo desplaza. el nibble inferior del valor de desplazamiento indica el desplazamiento a la derecha y el nibble superior el desplazamiento a la izquierda ( número desplazo -- 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!
¡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!
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

View File

@ -2,13 +2,13 @@
¡esta es la tercera sección del {tutorial de uxn}!
aquí introducimos el uso del dispositivo controlador en la computadora uxn varvara: esto nos permitirá añadir interactividad a nuestros programas, y empezar a discutir el flujo de control en uxntal.
aquí introducimos el uso del dispositivo controlador en la computadora uxn varvara: esto nos permitirá añadir interactividad a nuestros programas y empezar a discutir el flujo de control en uxntal.
también hablamos de las instrucciones lógicas y de manipulación de la pila en uxntal.
# el dispositivo controlador
el dispositivo controlador del ordenador varvara nos permite leer las entradas del teclado y/o de los botones del controlador.
el dispositivo controlador del ordenador varvara nos permite leer las entradas del teclado o de los botones del controlador.
la definición de sus puertos tendría el siguiente aspecto en un programa típico:
@ -22,7 +22,7 @@ el byte de botón codifica en cada uno de sus ocho bits el estado de ocho "boton
=> https://wiki.nesdev.com/w/index.php/Standard_controller controlador NES estándar
numerando los bits de derecha a izquierda, y de 0 a 7, las teclas correspondientes (y los botones de NES) son
numerando los bits de derecha a izquierda y de 0 a 7, las teclas correspondientes (y los botones de NES) son
+ <table>
+ <tr><th>bit 7</th><th>bit 6</th><th>bit 5</th><th>bit 4</th><th>bit 3</th><th>bit 2</th><th>bit 1</th><th>bit 0</th></tr>
@ -65,9 +65,9 @@ la siguiente línea de código asignaría ese vector, utilizando la dirección a
# flujo de control: subrutinas vectoriales
hasta ahora nuestros programas uxntal han seguido un flujo lineal: comienzan en la dirección 0100, y terminan en la primera instrucción BRK que se encuentra.
hasta ahora nuestros programas uxntal han seguido un flujo lineal: comienzan en la dirección 0100 y terminan en la primera instrucción BRK que se encuentra.
podemos pensar en estos programas como rutinas de configuración: configuran los colores del sistema, pueden dibujar o imprimir algunas cosas, y luego dejan a uxn esperando. ¿qué estaría esperando uxn?
podemos pensar en estos programas como rutinas de configuración: configuran los colores del sistema, pueden dibujar o imprimir algunas cosas y luego dejan a uxn esperando. ¿qué estaría esperando uxn?
sí, una opción sería: ¡esperar la entrada del teclado!
@ -79,7 +79,7 @@ cada una de estas subrutinas terminará con la instrucción BRK, para que puedan
para ilustrar ese comportamiento, leamos el siguiente programa.
este utiliza el procedimiento de dibujo de sprites que probamos el día anterior, pero hace que ocurra sólo cuando se pulsa una tecla. al principio, la pantalla está vacía, y cuando pulsamos una tecla se dibuja un cuadrado:
este utiliza el procedimiento de dibujo de sprites que probamos el día anterior, pero hace que ocurra solo cuando se pulsa una tecla. al principio, la pantalla está vacía, y cuando pulsamos una tecla, se dibuja un cuadrado:
```
( hola-teclado.tal )
@ -137,9 +137,9 @@ uxntal tiene cuatro instrucciones para comparar los dos primeros elementos de la
* GTH: empuja 01 hacia abajo en la pila si el primer elemento es mayor que el segundo, o empuja 00 en caso contrario ( a b -- a>b )
* LTH: empuja 01 hacia abajo en la pila si el primer elemento es menor que el segundo, o empuja 00 en caso contrario ( a b -- a>b )
podemos pensar en los resultados empujados por estas instrucciones como banderas booleanas: son 01 si la comparación fue verdadera, y son 00 si fue falsa.
podemos pensar en los resultados empujados por estas instrucciones como banderas booleanas: son 01 si la comparación fue verdadera y son 00 si fue falsa.
el siguiente código leerá el valor de la tecla del controlador, y empujará a la pila una bandera correspondiente a que sea igual al carácter 'a':
el siguiente código leerá el valor de la tecla del controlador y empujará a la pila una bandera correspondiente a que sea igual al carácter 'a':
```
.Controlador/tecla DEI ( lee la tecla y la empuja hacia abajo en la pila )
@ -155,22 +155,22 @@ uxntal tiene tres instrucciones lógicas a nivel de bit.
pueden funcionar como operadores lógicos que utilizan como operandos las banderas dadas por las instrucciones de comparación que hemos comentado anteriormente:
* AND: realiza un AND a nivel de bits con los dos elementos superiores de la pila, y empuja el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila, y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila, y empuja hacia abajo el resultado ( a b -- a^b )
* AND: realiza un AND a nivel de bits con los dos elementos superiores de la pila y empuja el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a^b )
AND2, ORA2, EOR2 funcionarán de la misma manera, pero con cortos en lugar de bytes.
### AND
lo siguiente empujará hacia abajo en la pila una bandera que indica si el byte tecla está entre 30 y 39 inclusive, usando 01 para representar 'verdadero', y 00 para representar 'falso':
lo siguiente empujará hacia abajo en la pila una bandera que indica si el byte tecla está entre 30 y 39 inclusive, usando 01 para representar 'verdadero' y 00 para representar 'falso':
```
.Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas de la pila, y empuja el resultado a la pila )
AND ( aplica un AND a las banderas de la pila y empuja el resultado a la pila )
```
que la instrucción sea a nivel de bit (o "bitwise") significa que aplica la operación AND a cada uno de los bits de los operandos.
@ -193,7 +193,7 @@ AND 0000 0000 ( falso )
0000 0000 ( falso )
```
como estos indicadores sólo utilizan el bit menos significativo (el bit más a la derecha) para codificar su valor, un AND a nivel de bits es equivalente a un AND lógico convencional.
como estos indicadores solo utilizan el bit menos significativo (el bit más a la derecha) para codificar su valor, un AND a nivel de bits es equivalente a un AND lógico convencional.
### OR
@ -204,7 +204,7 @@ el siguiente código empujará una bandera hacia abajo en la pila si el byte tec
LIT '1 EQU ( ¿es '1'? empuja la bandera a la pila )
.Controlador/tecla DEI ( lee la tecla y empujar a la pila )
LIT 'a EQU ( ¿es 'a'? empuja la bandera a la pila )
ORA ( aplica un OR a las banderas en la pila, y empuja el resultado en la pila )
ORA ( aplica un OR a las banderas en la pila y empuja el resultado en la pila )
```
cuando alguna o ambas banderas son verdaderas, la bandera será verdadera:
@ -216,15 +216,15 @@ OR 0000 0000 ( falso )
0000 0001 ( verdadero )
```
sólo cuando ambas banderas sean falsas, la bandera resultante será falsa.
solo cuando ambas banderas sean falsas, la bandera resultante será falsa.
### EOR
un "exclusive-OR", o también "o-exclusivo", es una operación lógica que tiene un resultado de verdadero sólo cuando una u otra entrada es verdadera. si ambas entradas son verdaderas, o si ambas entradas son falsas, el resultado es falso.
un "exclusive-OR", o también "o-exclusivo", es una operación lógica que tiene un resultado de verdadero solo cuando una u otra entrada es verdadera. si ambas entradas son verdaderas, o si ambas entradas son falsas, el resultado es falso.
basándose en este comportamiento, esta instrucción puede utilizarse para invertir el valor de una bandera utilizando un valor especial en el que el/los bit(s) que queremos invertir se pongan a 1. este tipo de valores se llaman máscaras.
basándose en este comportamiento, esta instrucción puede utilizarse para invertir el valor de una bandera utilizando un valor especial en el que el o los bits que queremos invertir se pongan a 1. este tipo de valores se llaman máscaras.
por ejemplo, el siguiente código empujará una bandera correspondiente a que la te sea mayor o igual a 20, calculando primero si es menor que 20, y luego invirtiendo el resultado:
por ejemplo, el siguiente código empujará una bandera correspondiente a que la te sea mayor o igual a 20, calculando primero si es menor que 20 y luego invirtiendo el resultado:
```
.Controlador/tecla DEI ( lee la tecla y la empuja a la pila )
@ -252,7 +252,7 @@ EOR 0000 0001 ( máscara )
0000 0001 ( verdadero )
```
observe que la máscara es la misma, y el resultado es el valor opuesto de la bandera.
observe que la máscara es la misma y el resultado es el valor opuesto de la bandera.
# flujo de control: saltos condicionales
@ -277,7 +277,7 @@ en modo corto, las direcciones que toman estas instrucciones son absolutas (es d
hay varias runas que se refieren a direcciones y etiquetas. uxnasm las lee y las convierte a los valores binarios correspondientes.
en los días anteriores ya hablamos de algunas de ellas; esta es una recapitulación de las mismas, y una introducción de las nuevas:
en los días anteriores ya hablamos de algunas de ellas; esta es una recapitulación de las mismas y una introducción de las nuevas:
* dirección literal en la página cero: .etiqueta (un byte)
* dirección literal en memoria principal: ;label (un corto)
@ -299,7 +299,7 @@ y finalmente, para referirse a las etiquetas dentro de nuestro código uxntal, t
¡unamos todo esto!
la siguiente subrutina en-controlador ilustra el uso de los saltos, dibujando nuestro sprite sólo cuando la tecla que se pulsó fue '1':
la siguiente subrutina en-controlador ilustra el uso de los saltos, dibujando nuestro sprite solo cuando la tecla que se pulsó fue '1':
```
@en-controlador
@ -382,7 +382,7 @@ observe cómo las condiciones se escriben una tras otra: siempre que una bandera
también note que este código no está optimizado para el tamaño o la velocidad, sino para la legibilidad.
estaría en tí, por ejemplo, realizar una aritmética con el valor de la tecla que se pulsó para calcular el color a asignar al sprite - ¡podrías inspirarte en tu macro IMPRIMIR-DIGITO del día 1!
estaría en ti, por ejemplo, realizar una aritmética con el valor de la tecla que se pulsó para calcular el color a asignar al sprite - ¡podrías inspirarte en tu macro IMPRIMIR-DIGITO del día 1!
# manipulación de la pila
@ -418,10 +418,10 @@ discutimos anteriormente este segmento de código, que empuja una bandera que re
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
Controlador/tecla DEI ( lee la tecla y la introduce en la pila )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas en la pila, y empuja el resultado en la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
en lugar de leer la tecla dos veces, podríamos hacerlo una vez, y luego usar la instrucción DUP para copiar el valor:
en lugar de leer la tecla dos veces, podríamos hacerlo una vez y luego usar la instrucción DUP para copiar el valor:
```
.Controlador/tecla DEI DUP ( leer y duplicar la tecla )
@ -463,10 +463,10 @@ finalmente podemos proceder a la comparación y al AND:
```
#3a LTH ( ¿es menor que 3a? empuja la bandera en la pila )
AND ( aplica un AND a las banderas en la pila, y empuja el resultado en la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
terminando con una pila que sólo tiene el resultado:
terminando con una pila que solo tiene el resultado:
```
resultado <- arriba
@ -479,12 +479,12 @@ el código completo se leería como:
#2f GTH ( ¿es mayor que 2f? empuja la bandera a la pila )
SWP ( poner la tecla en la parte superior )
#3a LTH ( ¿es menor que 3a? empuja la bandera a la pila )
AND ( aplica un AND a las banderas en la pila, y empuja el resultado en la pila )
AND ( aplica un AND a las banderas en la pila y empuja el resultado en la pila )
```
el primer código se ensambla en 13 bytes, y éste se ensambla en 12 bytes. quizá no haya demasiada diferencia en ese aspecto.
el primer código se ensambla en 13 bytes y este se ensambla en 12 bytes. quizá no haya demasiada diferencia en ese aspecto.
sin embargo, una ventaja más significativa es que esta nueva rutina ahora necesita su entrada empujada hacia abajo en la pila sólo al principio.
sin embargo, una ventaja más significativa es que esta nueva rutina ahora necesita su entrada empujada hacia abajo en la pila solo al principio.
en el caso que acabamos de discutir la entrada es la tecla que se presiona, pero podríamos fácilmente tener como entrada cualquier otro valor de la pila.
@ -580,13 +580,13 @@ en nuestra máscara AND, estableceremos como 1 los bits en las posiciones en las
por ejemplo, digamos que queremos ver si el bit número 4, que corresponde al botón Arriba, está encendido o apagado, independientemente del estado de los otros botones.
nuestra máscara AND tendrá un 1 en el bit número 4 (de derecha a izquierda, y empezando por el 0), y 0 en el resto:
nuestra máscara AND tendrá un 1 en el bit número 4 (de derecha a izquierda y empezando por el 0) y 0 en el resto:
```
0001 000: 10
```
¿qué pasaría si se pulsa el botón A (tecla Ctrl), con su estado codificado en el bit 0, y nada más?
¿qué pasaría si se pulsa el botón A (tecla Ctrl), con su estado codificado en el bit 0 y nada más?
```
0000 0001 ( botón )
@ -627,7 +627,7 @@ aplicar esta máscara sería tan sencillo como escribir:
el siguiente programa uxntal permite dibujar utilizando las teclas de las flechas y la tecla Ctrl (botón A).
las flechas mueven la posición de un sprite, y al pulsar Ctrl mientras se mueve lo dibujará con los colores inversos en relleno y trazo.
las flechas mueven la posición de un sprite, y al pulsar Ctrl mientras se mueve, lo dibujará con los colores inversos en relleno y trazo.
observa el uso de las máscaras AND, los saltos condicionales y algunas operaciones de la pila.
@ -704,7 +704,7 @@ BRK
algunas posibilidades para que practiques:
* modificar el código para que también responda a que pulses más de una flecha al mismo tiempo.
* convertir los incrementos y decrementos de las coordenadas en macros que tomen la dirección del puerto como entrada, y realizar una operación equivalente. estas dos líneas deberían funcionar usando la misma macro:
* convertir los incrementos y decrementos de las coordenadas en macros que tomen la dirección del puerto como entrada y realizar una operación equivalente. estas dos líneas deberían funcionar usando la misma macro:
```
.Pantalla/x INCREMENTO
@ -720,7 +720,7 @@ recuerde que .Pantalla/x es una dirección literal en la página cero, es decir,
* dibujar un controlador virtual que muestre cuáles de sus botones, mapeados a teclas del teclado, están siendo presionados
* crear una especie de máquina de escribir que dibuje diferentes símbolos y mueva el cursor de dibujo dependiendo de la tecla que se haya pulsado
* dibujar un personaje que cambie su estado según la tecla que se haya pulsado. ¿tal vez utilizar múltiples tiles para dibujarlo?
* crea un simple tablero de tres-en-raya para dos jugadores: una tecla dibuja una X, otra dibuja una O, y las flechas permiten elegir la casilla a dibujar.
* crea un simple tablero de tres-en-raya para dos jugadores: una tecla dibuja una X, otra dibuja una O y las flechas permiten elegir la casilla a dibujar.
¡ten en cuenta que para un suave movimiento interactivo puede ser mejor utilizar el vector de pantalla que se llama 60 veces por segundo!
@ -739,14 +739,14 @@ recuerde que .Pantalla/x es una dirección literal en la página cero, es decir,
## lógica a nivel de bits
* AND: realiza un AND a nivel de bits con los dos primeros elementos de la pila, y empuja hacia abajo el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila, y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila, y empuja hacia abajo el resultado ( a b -- a^b )
* AND: realiza un AND a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a&b )
* ORA: realiza un OR a nivel de bits con los dos primeros elementos de la pila y empuja el resultado ( a b -- a|b )
* EOR: realiza un OR exclusivo a nivel de bits con los dos primeros elementos de la pila y empuja hacia abajo el resultado ( a b -- a^b )
## saltos
* JMP: salto incondicional a la dirección de la pila ( direc -- )
* JCN: salto condicional: toma una dirección y un valor de la pila, y si el valor no es 00, salta a la dirección; en caso contrario continúa con la siguiente instrucción ( valor direc -- )
* JCN: salto condicional: toma una dirección y un valor de la pila y si el valor no es 00, salta a la dirección; en caso contrario continúa con la siguiente instrucción ( valor direc -- )
## pila

View File

@ -4,7 +4,7 @@
aquí hablamos del bucle de animación del ordenador varvara, a través de su vector de dispositivo de pantalla.
también hablamos del uso de la memoria del programa como un espacio para datos usando "variables". esto nos permite guardar y recuperar datos durante el tiempo de ejecución de nuestros programas, y puede ahorrarnos manejos complejos en la pila :)
también hablamos del uso de la memoria del programa como un espacio para datos usando "variables". esto nos permite guardar y recuperar datos durante el tiempo de ejecución de nuestros programas y puede ahorrarnos manejos complejos en la pila :)
# el vector pantalla
@ -24,11 +24,11 @@ la siguiente línea de uxntal asignaría la dirección absoluta de la etiqueta e
;en-cuadro .Pantalla/vector DEO2
```
uxn saltará a la ubicación de la etiqueta a una frecuencia de 60 veces por segundo: podemos utilizar la subrutina bajo en-cuadro para cambiar el contenido de la pantalla, generando animación, y/o también podemos utilizarla para otros propósitos relacionados con la temporización.
uxn saltará a la ubicación de la etiqueta a una frecuencia de 60 veces por segundo: podemos utilizar la subrutina bajo en-cuadro para cambiar el contenido de la pantalla, generando animación, o también podemos utilizarla para otros propósitos relacionados con la temporización.
## una línea que crece
el siguiente programa demuestra un uso básico pero potente del vector pantalla: en cada fotograma, dibuja un píxel en las coordenadas x,y de la pantalla dadas, y añade 1 al valor de la coordenada x:
el siguiente programa demuestra un uso básico pero potente del vector pantalla: en cada fotograma, dibuja un píxel en las coordenadas x,y de la pantalla dadas y añade 1 al valor de la coordenada x:
```
( hola-linea.tal )
@ -136,11 +136,11 @@ aquí están las dos instrucciones que nos ayudarían:
como ya hemos comentado, una dirección absoluta siempre tendrá una longitud de dos bytes.
en el modo corto, LDA2 cargará un corto desde la dirección dada, y STA2 almacenará un corto en la dirección dada.
en el modo corto, LDA2 cargará un corto desde la dirección dada y STA2 almacenará un corto en la dirección dada.
### ejemplos
como ejemplo, el siguiente código leería los dos bytes de pixel/x, los incrementaría en uno, y los almacenaría de nuevo en pixel/x:
como ejemplo, el siguiente código leería los dos bytes de pixel/x, los incrementaría en uno y los almacenaría de nuevo en pixel/x:
```
;pixel/x LDA2 ( cargar pixel/x en la pila )
@ -168,7 +168,7 @@ BRK
@pixel [ &x $2 &y $2 ]
```
nótese que podríamos haber conseguido el mismo resultado almacenando el resultado, y luego recargándolo y enviándolo como salida.
nótese que podríamos haber conseguido el mismo resultado almacenando el resultado y luego recargándolo y enviándolo como salida.
aquí podemos ver cómo un DUP2 puede facilitar esa operación, siempre y cuando mantengamos un modelo mental (¡o tangible!) de lo que ocurre en la pila.
@ -198,7 +198,7 @@ esto significa que una salvedad de usar variables allí, es que para iniciarlas
### etiquetas en la página cero
las etiquetas para la página cero funcionarían igual que antes; sólo tenemos que especificar que están en la página cero con una almohadilla absoluta:
las etiquetas para la página cero funcionarían igual que antes; solo tenemos que especificar que están en la página cero con una almohadilla absoluta:
```
|0000 ( página cero )
@ -216,7 +216,7 @@ las instrucciones para cargar (leer) y almacenar (escribir) desde y hacia la pá
en estas instrucciones, la dirección siempre será de un byte.
en el modo corto, LDZ2 cargará un corto desde la dirección dada, y STZ2 almacenará un corto en la dirección dada.
en el modo corto, LDZ2 cargará un corto desde la dirección dada y STZ2 almacenará un corto en la dirección dada.
### ejemplos
@ -269,7 +269,7 @@ además, observe que en el caso de .pixel la dirección se refiere a la página
### un poco de práctica de manipulación en la pila
nota que las siguientes instrucciones también incrementarían .pixel/x, pero estableciendo su dirección sólo una vez:
nota que las siguientes instrucciones también incrementarían .pixel/x, pero estableciendo su dirección solo una vez:
```
.pixel/x DUP LDZ2 INC2 ROT STZ2
@ -290,9 +290,9 @@ una posible desventaja es que podría ser menos legible. pero una posible ventaj
otra posibilidad que tenemos en uxn y que podría ser más apropiada para las "variables locales", consiste en utilizar direcciones relativas.
de forma similar a las variables de la página cero, para direccionar estas variables sólo necesitamos un byte.
de forma similar a las variables de la página cero, para direccionar estas variables solo necesitamos un byte.
sin embargo, como estas direcciones se dan como desfases ("offsets") relativos y pueden considerarse positivas y negativas, sólo se pueden alcanzar si están dentro de los 256 bytes que rodean a la instrucción que las carga o almacena.
sin embargo, como estas direcciones se dan como desfases ("offsets") relativos y pueden considerarse positivas y negativas, solo se pueden alcanzar si están dentro de los 256 bytes que rodean a la instrucción que las carga o almacena.
### instrucciones: LDR, STR
@ -303,7 +303,7 @@ las instrucciones para trabajar de esta manera son:
similar a LDZ y STZ, en estas instrucciones la dirección siempre será de un byte.
en el modo corto, LDR2 cargará un corto desde la dirección dada, y STR2 almacenará un corto en la dirección dada.
en el modo corto, LDR2 cargará un corto desde la dirección dada y STR2 almacenará un corto en la dirección dada.
### ejemplos
@ -361,7 +361,7 @@ el uso de "variables" nos ayudará ahora a discutir tres formas diferentes de an
los revisaremos por separado para mantener los ejemplos relativamente simples y legibles.
tenga en cuenta que estos ejemplos también sirven para discutir más posibilidades de programación uxntal, y pueden llegar a ser un poco abrumadores.
tenga en cuenta que estos ejemplos también sirven para discutir más posibilidades de programación uxntal y pueden llegar a ser un poco abrumadores.
te recomiendo que revises y experimentes con uno a la vez, pacientemente :)
@ -369,7 +369,7 @@ te recomiendo que revises y experimentes con uno a la vez, pacientemente :)
ya discutimos como hacer que uxn cambie la posición de un pixel en la pantalla, dejando un rastro.
cambiar ese programa para dibujar un sprite de 8x8 en su lugar sería relativamente sencillo, y puede que ya lo hayas probado: tendríamos que usar Pantalla/sprite en lugar de Pantalla/pixel, con un byte apropiado para definir su color y orientación, y tendríamos que establecer la dirección de los datos de nuestro sprite en Pantalla/direc.
cambiar ese programa para dibujar un sprite de 8x8 en su lugar sería relativamente sencillo y puede que ya lo hayas probado: tendríamos que usar Pantalla/sprite en lugar de Pantalla/pixel, con un byte apropiado para definir su color y orientación, y tendríamos que establecer la dirección de los datos de nuestro sprite en Pantalla/direc.
eso daría como resultado un sprite que se mueve y que además deja un rastro: ¡te invito a que lo pruebes primero!
@ -383,7 +383,7 @@ una posible forma de conseguirlo sería siguiendo este orden de operaciones dent
* cambiar de posición
* dibujar nuevo sprite
esto nos permite borrar el sprite de su posición en el fotograma anterior, actualizar sus coordenadas a una nueva posición, y luego dibujarlo ahí.
esto nos permite borrar el sprite de su posición en el fotograma anterior, actualizar sus coordenadas a una nueva posición y luego dibujarlo ahí.
### código de ejemplo
@ -447,7 +447,7 @@ BRK
nítido, ¿no? :)
como esto es sólo un ejemplo para ilustrar un punto, hay algunas cosas que podrían ser optimizadas para hacer nuestro programa más pequeño, y hay algunas cosas que podrían ser útiles pero fueron omitidas. por ejemplo, no hay un valor inicial para la coordenada x, o la coordenada `y` no se utiliza.
como esto es solo un ejemplo para ilustrar un punto, hay algunas cosas que podrían ser optimizadas para hacer nuestro programa más pequeño y hay algunas cosas que podrían ser útiles pero fueron omitidas. por ejemplo, no hay un valor inicial para la coordenada x, o la coordenada `y` no se utiliza.
### posibilidades adicionales
@ -478,7 +478,7 @@ cuando usamos el vector controlador, estamos actuando en base a un cambio en el/
pero, ¿cómo podemos tratar de hacer una acción continua cuando una tecla se mantiene presionada?
en algunos sistemas operativos, si mantenemos una tecla pulsada, ésta dispara el vector controlador varias veces, ¡pero no necesariamente al mismo ritmo que el vector pantalla!
en algunos sistemas operativos, si mantenemos una tecla pulsada, esta dispara el vector controlador varias veces, ¡pero no necesariamente al mismo ritmo que el vector pantalla!
¡esta repetición puede no permitir un movimiento suave como el que podemos conseguir si comprobamos el estado del controlador dentro de la subrutina en-cuadro!
@ -488,7 +488,7 @@ el siguiente programa nos permite controlar la posición horizontal de nuestro c
=> ./img/screencap_uxn-moving-square.gif animación que muestra un cuadrado moviéndose horizontalmente en la pantalla, aparentemente controlado por un humano.
¡nótese las similitudes entre el programa anterior, y lo que cubrimos en el {tutorial de uxn día 3}!
¡nótese las similitudes entre el programa anterior y lo que cubrimos en el {tutorial de uxn día 3}!
```
( hola-sprite-en-movimiento.tal )
@ -597,7 +597,7 @@ EQU2 ( ¿es x igual a la anchura de la pantalla - 8? )
### módulo
otra posibilidad podría ser aplicar una operación de módulo a nuestras coordenadas cambiadas para que siempre se mantengan dentro de los límites, volviendo a la izquierda cuando se cruce con la derecha, y viceversa.
otra posibilidad podría ser aplicar una operación de módulo a nuestras coordenadas cambiadas para que siempre se mantengan dentro de los límites, volviendo a la izquierda cuando se cruce con la derecha y viceversa.
un posible conjunto de macros de módulo podría ser:
@ -655,7 +655,7 @@ para tener una animación compuesta por esos fotogramas necesitamos cambiar la d
una forma de conseguirlo es teniendo una "variable global" en la página cero que cuente los fotogramas del programa. además, tendríamos que tener ese conteo acotado en un rango correspondiente a la cantidad de fotogramas de nuestra animación.
¡ya sabemos cómo hacer la primera parte, y más o menos sabemos cómo hacer la segunda!
¡ya sabemos cómo hacer la primera parte y más o menos sabemos cómo hacer la segunda!
### cargar, incrementar y almacenar la cuenta de fotogramas
@ -684,15 +684,15 @@ para que ese recuento de fotogramas se limite a un rango correspondiente a nuest
cuando tenemos un número de fotogramas que corresponde a una potencia de dos, como se recomienda más arriba, podemos utilizar una "máscara AND" para realizar esta operación de módulo más rápidamente que si utilizáramos las macros MOD sugeridas anteriormente.
por ejemplo, si tenemos 8 fotogramas numerados del 0 al 7, podemos observar que esos números sólo requieren tres bits para ser representados.
por ejemplo, si tenemos 8 fotogramas numerados del 0 al 7, podemos observar que esos números solo requieren tres bits para ser representados.
para construir nuestra máscara AND, ponemos como 1 esos tres bits, y 0 los demás:
para construir nuestra máscara AND, ponemos como 1 esos tres bits y 0 los demás:
```
0000 0111: 07
```
esta máscara AND "dejará pasar" los tres bits menos significativos de otro byte, y desactivará los demás.
esta máscara AND "dejará pasar" los tres bits menos significativos de otro byte y desactivará los demás.
en uxntal este proceso se vería de la siguiente manera:
@ -731,7 +731,7 @@ o, otra forma de verlo:
generalizando, ¡el fotogramaN esta (N veces 8) bytes después de la etiqueta de animación!
esto significa que si obtenemos la dirección absoluta de la etiqueta de animación, y le añadimos (N veces 8) bytes, obtendremos la dirección absoluta del fotogramaN :)
esto significa que si obtenemos la dirección absoluta de la etiqueta de animación y le añadimos (N veces 8) bytes, obtendremos la dirección absoluta del fotogramaN :)
la cantidad de bytes que separa cada subetiqueta se llama "offset" o desfase.
@ -747,7 +747,7 @@ después de aplicar el módulo 8 a nuestro cuentafotogramas podemos multiplicarl
### de byte a corto
nota que hasta ahora hemos estado trabajando con bytes, y todo ha ido bien.
nota que hasta ahora hemos estado trabajando con bytes y todo ha ido bien.
sin embargo, ¡las direcciones absolutas son cortos!
@ -927,9 +927,9 @@ ah, ¡mucho mejor!
ten en cuenta que si quieres dividir la frecuencia a números que no son potencias de 2, podrías empezar a ver algunas fallas ("glitches") aproximadamente cada 4 segundos: esto se debe a que el cuentafotograma se sobrepasa y no da una buena secuencia de resultados para esos divisores.
esto también puede ocurrir si tienes una animación que consta de un número de fotogramas que no es una potencia de 2, y utilizas una operación MOD normal para calcular el desfase al fotograma correspondiente.
esto también puede ocurrir si tienes una animación que consta de un número de fotogramas que no es una potencia de 2 y utilizas una operación MOD normal para calcular el desfase al fotograma correspondiente.
la solución más sencilla para estos problemas sería utilizar un número de fotogramas de pequeño tamaño que sólo causara esos fallos de sobreflujo aproximadamente cada 18 minutos.
la solución más sencilla para estos problemas sería utilizar un número de fotogramas de pequeño tamaño que solo causara esos fallos de sobreflujo aproximadamente cada 18 minutos.
tendrías que adaptar el programa para que funcione con ese tamaño de cuentafotograma - ¡siento y pienso que es un buen ejercicio!
@ -950,7 +950,7 @@ en modo corto, estas instrucciones cargan o almacenan cortos desde o hacia la me
# día 5
en el {tutorial de uxn día 5} introducimos el dispositivo de ratón varvara para explorar más interacciones posibles, y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo mantener.
en el {tutorial de uxn día 5} introducimos el dispositivo de ratón varvara para explorar más interacciones posibles y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo mantener.
¡también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos!

View File

@ -1,6 +1,6 @@
# tutorial de uxn: día 5, el ratón y chucherías de uxntal
esta es la quinta sección del {tutorial de uxn}! aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones, y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo mantener.
esta es la quinta sección del {tutorial de uxn}! aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo mantener.
¡también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos!
@ -27,9 +27,9 @@ normalmente, en un ratón de tres botones, el primer botón es el izquierdo, el
utilizando un ratón de tres botones como este, tendríamos ocho valores posibles para el byte de estado, por ejemplo :
* 00 cuando no se pulsa ninguno de los botones
* 01 cuando sólo se pulsa el primer botón
* 02 cuando sólo se presiona el segundo botón
* 04 cuando sólo se pulsa el tercer botón
* 01 cuando solo se pulsa el primer botón
* 02 cuando solo se presiona el segundo botón
* 04 cuando solo se pulsa el tercer botón
nota que al igual que el dispositivo controlador, este sistema nos permite comprobar si hay varios botones pulsados a la vez:
@ -44,7 +44,7 @@ recuerda que podemos utilizar las máscaras AND, tal y como se introdujo en el {
el dispositivo del ratón tiene un par de cortos para indicar si el ratón se está desplazando.
despy indicará un desplazamiento vertical con un valor "positivo" cuando se mueve hacia arriba (o en realidad, "lejos del usuario"), y un valor "negativo" cuando se mueve hacia abajo (o "hacia el usuario")
despy indicará un desplazamiento vertical con un valor "positivo" cuando se mueve hacia arriba (o en realidad, "lejos del usuario") y un valor "negativo" cuando se mueve hacia abajo (o "hacia el usuario")
* 0001 cuando se desplaza hacia arriba
* ffff cuando se desplaza hacia abajo
@ -201,9 +201,9 @@ BRK
@puntero_icn [ 80c0 e0f0 f8e0 1000 ]
```
nótese que dibuja el puntero en primer plano, y que utiliza 'a' en el nibble bajo del byte del sprite: esto implica que utilizará el color 2 para dibujar la forma del puntero, y dibujará con transparencia el resto del tile. ( ver dibujar sprites de 1bpp en el {tutorial de uxn día 2} )
nótese que dibuja el puntero en primer plano y que utiliza 'a' en el nibble bajo del byte del sprite: esto implica que utilizará el color 2 para dibujar la forma del puntero y dibujará con transparencia el resto del tile. ( ver dibujar sprites de 1bpp en el {tutorial de uxn día 2} )
este modo de mezcla te permitiría dibujar cosas en el plano de fondo y que el puntero las cubra sólo con su forma, y no con todo el cuadrado del tile.
este modo de mezcla te permitiría dibujar cosas en el plano de fondo y que el puntero las cubra solo con su forma, y no con todo el cuadrado del tile.
¡te invito a que lo pruebes!
@ -213,7 +213,7 @@ este modo de mezcla te permitiría dibujar cosas en el plano de fondo y que el p
ahora, podemos ver que el programa funciona, pero está inundando la subrutina en-raton con mucho código.
eso es aun mas cierto si consideramos que solo estamos dibujando el puntero, y no tomando ninguna otra acción, como responder a los botones.
eso es aun mas cierto si consideramos que solo estamos dibujando el puntero y no tomando ninguna otra acción, como responder a los botones.
crear una macro para todo este código podría ser posible, pero también poco práctico debido a la cantidad de código.
@ -365,7 +365,7 @@ como cada uno de los modos es un bit independiente, es posible combinarlos, por
## un breve ejemplo de pila de retorno
por ejemplo, el siguiente código empujaría dos números hacia abajo en la pila de retorno, los sumaría, y empujaría el resultado de nuevo en la pila de retorno:
por ejemplo, el siguiente código empujaría dos números hacia abajo en la pila de retorno, los sumaría y empujaría el resultado de nuevo en la pila de retorno:
```
LITr 01 LITr 02 ADDr
@ -480,7 +480,7 @@ además, una posible forma de que una subrutina "devuelva" sus resultados sería
estos elementos pueden entonces ser consumidos desde la pila de trabajo después de regresar de la subrutina.
puede haber otros casos en los que el uso de "variables" tendría un sentido más lógico y/o legible para pasar argumentos y resultados.
puede haber otros casos en los que el uso de "variables" tendría un sentido más lógico o legible para pasar argumentos y resultados.
# almacena, no saltes
@ -596,7 +596,7 @@ el modo mantener puede combinarse con los otros modos, para un total de ocho com
## modo mantener en aritmética
sabemos lo que hace el siguiente código uxntal; empuja 01 y 02 hacia abajo en la pila, suma ambos elementos, y empuja el resultado (03) hacia abajo en la pila:
sabemos lo que hace el siguiente código uxntal; empuja 01 y 02 hacia abajo en la pila, suma ambos elementos y empuja el resultado (03) hacia abajo en la pila:
```
#01 #02 ( pt: 01 02 )
@ -623,7 +623,7 @@ en realidad, si recuerdas, en el {tutorial de uxn día 4} compartí contigo un p
%MOD2 { OVR2 OVR2 DIV2 MUL2 SUB2 } ( a b -- a%b )
```
dije entonces que había un conjunto más optimizado, y que lo discutiríamos más adelante.
dije entonces que había un conjunto más optimizado y que lo discutiríamos más adelante.
¡ahora es ese momento!
@ -651,7 +651,7 @@ SUB ( pt: 01 )
¿ves la posibilidad de introducir el modo mantener?
si te fijas bien, verás que el DUP2 está ahí para no perder los valores originales en la división, y así poder realizar la multiplicación y la resta después.
si te fijas bien, verás que el DUP2 está ahí para no perder los valores originales en la división y así poder realizar la multiplicación y la resta después.
pero entonces, ¿cómo podemos realizar la división sin perder sus operandos y sin usar DUP2?
@ -668,7 +668,7 @@ SUB ( pt: 01 )
¡de esta manera, nuestra macro puede tener un byte menos!
podemos generalizar este comportamiento para el modo corto, y obtener el conjunto óptimo de macros que mencioné anteriormente:
podemos generalizar este comportamiento para el modo corto y obtener el conjunto óptimo de macros que mencioné anteriormente:
```
%MOD { DIVk MUL SUB }
@ -682,18 +682,18 @@ el modo mantener puede ser útil cuando hacemos comparaciones y no queremos perd
por ejemplo, en nuestra subrutina dibuja-línea-horizontal, teníamos el siguiente conjunto de líneas de código:
```
( duplicar longitud y el conteo, comparar, y saltar )
( duplicar longitud y el conteo, comparar y saltar )
DUP2 ( pt: longitud conteo longitud conteo / pr: )
NEQ ( pt: longitud conteo bandera / pr: )
,&bucle JCN ( pt: longitud conteo / pr: )
```
verás que aquí, como en el caso DIVk anterior, el DUP2 está ahí sólo para asegurarse de que la longitud y el recuento no se pierden al realizar NEQ.
verás que aquí, como en el caso DIVk anterior, el DUP2 está ahí solo para asegurarse de que la longitud y el recuento no se pierden al realizar NEQ.
por lo tanto, podríamos reemplazar DUP2 NEQ con NEQk:
```
( duplicar la longitud y el conteo, comparar, y saltar )
( duplicar la longitud y el conteo, comparar y saltar )
NEQk ( pt: longitud conteo bandera / pr: )
,&bucle JCN ( pt: longitud conteo / pr: )
```
@ -734,15 +734,15 @@ con lo que ya hemos cubierto, y en caso de que quieras algunas ideas, aquí hay
## sprites de varios tiles
dibujar un sprite formado por varios tiles es un proceso que puede beneficiarse del uso de subrutinas: ten una subrutina que reciba un par de coordenadas x,y en la pila de trabajo cuando sea llamada, y úsala para dibujar los tiles en las posiciones correspondientes relativas a esas coordenadas.
dibujar un sprite formado por varios tiles es un proceso que puede beneficiarse del uso de subrutinas: ten una subrutina que reciba un par de coordenadas x,y en la pila de trabajo cuando sea llamada y úsala para dibujar los tiles en las posiciones correspondientes relativas a esas coordenadas.
## herramienta de dibujo
¡muchas posibilidades aquí!
tal vez comienza con dibujar sólo cuando se presiona un botón. cambia el color y/o el "pincel" dependiendo del botón que se presiona.
tal vez comienza con dibujar solo cuando se presiona un botón. cambia el color o el "pincel" dependiendo del botón que se presiona.
puedes tener diferentes "modos" seleccionables: tal vez cambien el pincel que estás usando, la forma en que el pincel se comporta (por ejemplo, ¿en espejo, caleidoscopio?), y/o las formas que se dibujan.
puedes tener diferentes "modos" seleccionables: tal vez cambien el pincel que estás usando, la forma en que el pincel se comporta (por ejemplo, ¿en espejo, caleidoscopio?) o las formas que se dibujan.
considera cómo seleccionarías esos modos: ¿botones en pantalla? ¿teclas del teclado? ¿acordes con los botones del ratón?
@ -775,7 +775,7 @@ en el {tutorial de uxn día 6} hablamos de cómo podemos integrar todo lo que he
¡basamos nuestra discusión en una recreación del clásico juego pong!
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar los sprites de varios tiles, y para comprobar las colisiones.
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar los sprites de varios tiles y para comprobar las colisiones.
primero, te invito a tomar un descanso!

View File

@ -4,7 +4,7 @@ esta es la sexta sección del {tutorial de uxn}! aquí hablamos de cómo podemos
basamos nuestra discusión en una recreación del clásico juego pong.
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles, y para comprobar las colisiones.
además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles y para comprobar las colisiones.
# lógica general
@ -52,7 +52,7 @@ BRK
## 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?
¿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:
@ -62,7 +62,7 @@ una forma sería algo así como:
### una primera versión
digamos que nuestra x inicial es 0000, nuestro ancho es el ancho de la pantalla, y el tile que estamos dibujando es tile-fondo.
digamos que nuestra x inicial es 0000, nuestro ancho es el ancho de la pantalla y el tile que estamos dibujando es tile-fondo.
el límite del bucle, x+ancho, sería también el ancho de la pantalla.
@ -132,9 +132,9 @@ entremedio, podemos enviar nuestro byte de sprite para dibujar el tile.
#0008 ADD2 ( incrementar x )
```
en este punto, la pila tiene la nueva x en la parte superior de la pila, y el ancho de la pantalla debajo.
en este punto, la pila tiene la nueva x en la parte superior de la pila y el ancho de la pantalla debajo.
podemos compararlos en modo mantener para conservar esos valores en la pila, y hacer nuestro salto como antes:
podemos compararlos en modo mantener para conservar esos valores en la pila y hacer nuestro salto como antes:
```
GTH2k ( ¿es ese ancho mayor que x?, o también, ¿es x menor que el ancho? )
@ -160,7 +160,7 @@ usando esta estrategia, obtendríamos el siguiente bucle:
POP2 POP2 ( eliminar x y el límite )
```
no sólo es un código más corto, sino que también es más rápido porque realiza menos operaciones dentro del bucle.
no solo es un código más corto, sino que también es más rápido porque realiza menos operaciones dentro del bucle.
¡es bueno tenerlo en cuenta!
@ -207,7 +207,7 @@ BRK
## repitiendo 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?
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:
@ -223,7 +223,7 @@ para ilustrar un pequeño cambio, supongamos que queremos tener un margen en la
%MARGEN-PARED { #0010 } ( margen en la parte superior e inferior )
```
nuestra `y` inicial sería MARGEN-PARED, y nuestro límite sería la altura de la pantalla menos MARGEN-PARED.
nuestra `y` inicial sería MARGEN-PARED y nuestro límite sería la altura de la pantalla menos MARGEN-PARED.
podemos usar la misma estructura que antes, pero usando `y`:
@ -320,17 +320,17 @@ que podemos llamar simplemente desde nuestra subrutina de iniciación:
¡lindo!
en el {tutorial de uxn apéndice a} puedes encontrar una discusión detallada de cómo generalizar un procedimiento como éste en una subrutina dibuja-tiles que dibuje un rectángulo arbitrario rellenado con un tile dado.
en el {tutorial de uxn apéndice a} puedes encontrar una discusión detallada de cómo generalizar un procedimiento como este en una subrutina dibuja-tiles que dibuje un rectángulo arbitrario rellenado con un tile dado.
se habla de varias posibilidades para usar uxntal de esa manera abstracta: yo diría que es muy interesante, pero está definitivamente fuera del alcance para hacer el juego :)
# las palas
podemos pensar en las dos palas del juego como dos rectángulos, cada uno con sus propias coordenadas x e `y`, y ambos con la misma anchura y altura.
podemos pensar en las dos palas del juego como dos rectángulos, cada uno con sus propias coordenadas x e `y` y ambos con la misma anchura y altura.
la coordenada x de cada pala puede ser constante, y la coordenada `y` debe ser de seguro una variable.
la coordenada x de cada pala puede ser constante y la coordenada `y` debe ser de seguro una variable.
en esta parte veremos cómo dibujar las palas en base a estos parámetros, y también recapitularemos cómo cambiar sus coordenadas `y` con el controlador.
en esta parte veremos cómo dibujar las palas en base a estos parámetros y también recapitularemos cómo cambiar sus coordenadas `y` con el controlador.
## dibujar las palas multi-tile
@ -384,11 +384,11 @@ pero añadamos también un byte de color para el byte del sprite:
@dibuja-pala ( x^ y^ color -- )
```
recordemos 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)
recordemos 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)
por un lado esta segunda versión nos permitiría cambiar de color cuando, por ejemplo, le demos a la pelota, pero lo más importante es que esto nos permitirá borrar la pala antes de moverla, como hemos hecho en días anteriores.
en principio la subrutina debería ser directa: tenemos que establecer las coordenadas x e `y` de cada una de las fichas, relativas a las coordenadas x e `y` dadas, y dibujarlas con el color dado.
en principio la subrutina debería ser directa: tenemos que establecer las coordenadas x e `y` de cada una de las fichas, relativas a las coordenadas x e `y` dadas y dibujarlas con el color dado.
hay muchas maneras de hacerlo, dependiendo del gusto.
@ -404,17 +404,17 @@ podríamos por ejemplo dibujar los tiles en el siguiente orden, con las siguient
o podríamos hacerlo de forma más tradicional:
* dibujar el tile 0, luego añadir 8 a x
* dibujar el tile 1, luego restar 8 a x, y añadir 8 a `y`
* dibujar el tile 1, luego restar 8 a x y añadir 8 a `y`
* dibujar el tile 2, luego añadir 8 a x
* dibujar el tile 3, luego restar 8 a x, y añadir 8 a `y`
* dibujar el tile 3, luego restar 8 a x y añadir 8 a `y`
* dibujar el tile 4, luego añadir 8 a x
* dibujar el tile 5
en lugar de restar podríamos recuperar x de la pila de retorno, o de una variable relativa.
una posible ventaja de ir en orden es que podemos incrementar la dirección del sprite por 10 (16 en decimal) para llegar a la dirección del siguiente tile. para esto, y/o para los cambios de coordenadas, podemos aprovechar el byte auto de la pantalla.
una posible ventaja de ir en orden es que podemos incrementar la dirección del sprite por 10 (16 en decimal) para llegar a la dirección del siguiente tile. para esto, o para los cambios de coordenadas, podemos aprovechar el byte auto de la pantalla.
sin embargo, en este caso voy a ir por la primera opción, y voy a establecer manualmente la dirección para cada tile.
sin embargo, en este caso voy a ir por la primera opción y voy a establecer manualmente la dirección para cada tile.
adicionalmente, guardaré el color en la pila de retorno:
@ -480,7 +480,7 @@ ahora podemos llamarlo, por ejemplo, de la siguiente manera y obtener nuestra pa
=> ./img/screenshot_uxn-pong-paddle.png captura de pantalla de la pala dibujada sobre el fondo
es posible considerar formas más eficientes de dibujarla. por ejemplo, podríamos tener un dibuja-sprite generalizado que reciba la dirección inicial de un conjunto de tiles, y la anchura y altura en términos de número de tiles:
es posible considerar formas más eficientes de dibujarla. por ejemplo, podríamos tener un dibuja-sprite generalizado que reciba la dirección inicial de un conjunto de tiles y la anchura y altura en términos de número de tiles:
```
@dibuja-sprite ( x^ y^ ancho alto direc* color )
@ -541,7 +541,7 @@ MARGEN SUB2 ANCHO-PALA SUB2
.derecha/x STZ2
```
para centrar las coordenadas `y` podemos restar la altura de la pala a la altura de la pantalla, y luego dividir entre dos:
para centrar las coordenadas `y` podemos restar la altura de la pala a la altura de la pantalla y luego dividir entre dos:
```
.Pantalla/alto DEI2 ALTO-PALA SUB2 MITAD2
@ -562,7 +562,7 @@ para dibujar cada pala, podemos hacer el siguiente procedimiento dentro de nuest
### el programa hasta ahora
omitiendo la definición de las subrutinas dibuja-fondo y dibuja-pala, y como forma de tener un punto de comprobación, ahora mismo nuestro programa tendría el siguiente aspecto:
omitiendo la definición de las subrutinas dibuja-fondo y dibuja-pala y como forma de tener un punto de comprobación, ahora mismo nuestro programa tendría el siguiente aspecto:
=> ./img/screenshot_uxn-pong-paddles.png captura de pantalla de las dos palas, centradas verticalmente y con el mismo margen respecto a los lados
@ -662,7 +662,7 @@ donde COLOR-BORRAR en este caso sería:
para actualizar la posición de nuestras palas, podemos recurrir al ejemplo hola-sprite-en-movimiento.tal del {tutorial de uxn día 4}.
podemos usar las flechas arriba y abajo para cambiar la posición de la pala izquierda, y los botones ctrl y alt (A y B) para cambiar la posición de la pala derecha.
podemos usar las flechas arriba y abajo para cambiar la posición de la pala izquierda y los botones ctrl y alt (A y B) para cambiar la posición de la pala derecha.
podemos tener una macro para definir la velocidad de la pala, es decir, cuánto sumaremos o restaremos al mover cada cuadro:
@ -832,14 +832,14 @@ hagamos que la subrutina reciba el color como argumento, para poder borrar la pe
RTN
```
para dibujarla, sólo tendríamos que hacer:
para dibujarla, solo tendríamos que hacer:
```
( dibujar pelota )
COLOR-PELOTA ;dibuja-pelota JSR2
```
=> ./img/screenshot_uxn-pong-paddles-and-ball.png captura de la pantalla mostrando las palas en su posición horizontal pero a diferentes alturas, y la pelota completamente centrada en la pantalla.
=> ./img/screenshot_uxn-pong-paddles-and-ball.png captura de la pantalla mostrando las palas en su posición horizontal pero a diferentes alturas y la pelota completamente centrada en la pantalla.
## movimiento de la pelota
@ -849,7 +849,7 @@ para el movimiento de la pelota, seguiremos la misma estructura que antes:
* actualizar su posición
* dibujar la pelota en la nueva posición
se vería algo como lo siguiente, y podría situarse a lo largo de los procedimientos equivalentes para las palas dentro de la subrutina en-cuadro:`
se vería algo como lo siguiente y podría situarse a lo largo de los procedimientos equivalentes para las palas dentro de la subrutina en-cuadro:`
```
( dentro de en-cuadro )
@ -913,7 +913,7 @@ entonces, ¿existe un valor positivo de vel-x que haga que x se reduzca al sumar
sin embargo, aquí estamos limitados por 8 o 16 bits. ¿y qué implica eso?
por ejemplo, si tenemos el número ffff (16 bits, todos son unos), y sumamos 0001, ¿qué obtenemos?
por ejemplo, si tenemos el número ffff (16 bits, todos son unos) y sumamos 0001, ¿qué obtenemos?
```
1111 1111 1111 1111
@ -926,9 +926,9 @@ de acuerdo, es un número mayor, pero el 1 de la izquierda queda fuera de los 16
en uxn, el resultado de sumar ffff y 0001 es 0000: decimos que estamos desbordando los 16 bits.
veámoslo al revés: si tenemos 0001, y sumamos ffff, obtenemos 0000, ¡es decir, 1 menos que 1!
veámoslo al revés: si tenemos 0001 y sumamos ffff, obtenemos 0000, ¡es decir, 1 menos que 1!
si tenemos 0002, y añadimos ffff:
si tenemos 0002 y añadimos ffff:
```
0000 0000 0000 0010
@ -1022,9 +1022,9 @@ hemos definido la forma general de actualizar la posición de la pelota dada su
¡ahora veamos cómo implementar el clásico "rebote"!
primero, empecemos con las paredes en la parte superior e inferior de la pantalla; recordando que hay un margen (MARGEN-PARED) entre el borde real de la pantalla, y las paredes.
primero, empecemos con las paredes en la parte superior e inferior de la pantalla; recordando que hay un margen (MARGEN-PARED) entre el borde real de la pantalla y las paredes.
para realizar estas detecciones de colisión, tendríamos que comprobar sólo la coordenada `y` de la pelota.
para realizar estas detecciones de colisión, tendríamos que comprobar solo la coordenada `y` de la pelota.
como siempre, hay muchas maneras de lograr esto. una podría ser:
@ -1136,7 +1136,7 @@ puedes probarlo usando diferentes velocidades iniciales dentro de la configuraci
## colisiones con las palas
¡trabajemos con lo que acabamos de hacer, y adaptémoslo para rebotar con las palas!
¡trabajemos con lo que acabamos de hacer y adaptémoslo para rebotar con las palas!
### pala izquierda
@ -1219,7 +1219,7 @@ todo el código x-en-izquierda terminaría pareciendo:
&verif-pala-der
```
"fin" sería una etiqueta al final de la subrutina, y "reset" es una subrutina de la cuálf hablaremos más adelante.
"fin" sería una etiqueta al final de la subrutina y "reset" es una subrutina de la cuálf hablaremos más adelante.
esta aproximación de comparar con 0000 es la más fácil, pero ten en cuenta que podría no funcionar si cambias la velocidad de la pelota: podría ocurrir que cruzara la pared pero con una coordenada x que nunca fuera igual a 0.
@ -1231,7 +1231,7 @@ este puede ser otro buen ejercicio para ti: ¿cómo comprobarías si la pelota h
### pala derecha
para la pala derecha haremos lo mismo que arriba, pero cambiando las comparaciones relativas a la coordenada x de la pelota: usaremos el ancho de la pantalla como referencia para la pared derecha, y a partir de ahí le restaremos el margen y el ancho.
para la pala derecha haremos lo mismo que arriba, pero cambiando las comparaciones relativas a la coordenada x de la pelota: usaremos el ancho de la pantalla como referencia para la pared derecha y a partir de ahí le restaremos el margen y el ancho.
```
&verif-pala-der
@ -1292,7 +1292,7 @@ sería interesante tener algún mecanismo para también cambiar la velocidad: ta
aquí está todo el código que hemos escrito hoy:
=> ./img/screencap_uxn-pong.gif animación que muestra el pong en acción: las palas se mueven, la pelota rebota en las paredes superior e inferior y en las palas, y la pelota se reinicia desde el centro cuando la pelota golpea cualquier lado.
=> ./img/screencap_uxn-pong.gif animación que muestra el pong en acción: las palas se mueven, la pelota rebota en las paredes superior e inferior y en las palas y la pelota se reinicia desde el centro cuando la pelota golpea cualquier lado.
## configuración
@ -1690,11 +1690,11 @@ RTN
aquí hay algunas posibilidades extra para que practiques y trates de implementar:
* contar y dibujar algún tipo de puntuación
* cambiar el color de la pelota y/o de la pala cuando chocan
* cambiar el color de la pelota o de la pala cuando chocan
* cambiar la dirección o el tipo de rebote dependiendo de la parte de la pala que golpea la bola
* iniciar el juego cuando se pulsa un botón
* posición "aleatoria" inicial de la pelota
* velocidad variable de la pelota y/o de las palas
* velocidad variable de la pelota o de las palas
* ¡etc!
¡comparte lo que termines creando en base a todo esto! :)

View File

@ -19,14 +19,14 @@ sus puertos se definen normalmente de la siguiente manera:
* 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 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 corto 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`.
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 detalles para manejar, pero veremos que no son demasiado problema!
@ -40,7 +40,7 @@ para leer un archivo, necesitamos saber lo siguiente:
¡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 carga-archivo bajo otra:
podemos usar una estructura como la siguiente, donde el nombre del archivo y la memoria reservada están bajo una etiqueta y la subrutina carga-archivo bajo otra:
```
@carga-archivo ( -- )
@ -74,7 +74,7 @@ además, en este ejemplo no nos preocupa realmente cuántos bytes se han leído
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!
¡no solo 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
@ -93,7 +93,7 @@ el siguiente programa escribirá "hola" y una nueva línea (0a) en un archivo ll
;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 )
( establecer la dirección de inicio de los datos y hacer la escritura )
;archivo/datos .Archivo/escribir DEO2
( leer y evaluar el byte éxito )
@ -161,7 +161,7 @@ observe cómo las etiquetas &datos y &r apuntan a la misma ubicación: ¡no es u
### 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:
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 ( -- )
@ -193,7 +193,7 @@ RTN
# el dispositivo fechahora
el dispositivo fechahora (o "datetime") puede ser útil para el cronometraje de baja precisión y/o para las visualizaciones del tiempo.
el dispositivo fechahora (o "datetime") puede ser útil para el cronometraje de baja precisión 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:
@ -221,7 +221,7 @@ basándonos en esto, debería ser sencillo utilizarlos. por ejemplo, para leer l
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? :)
o ¿qué tal dibujar sprites condicionalmente 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 algo de pseudo-aleatoriedad!
@ -250,7 +250,7 @@ supondremos que no estás familiarizado con estos conceptos, así que los discut
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.
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:
@ -352,7 +352,7 @@ el ejemplo piano.tal en el repositorio uxn, tiene varios de ellos, todos de 256
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.
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.
@ -373,7 +373,7 @@ tri-pcm:
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:
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 solo tenemos que escribir su dirección y su longitud:
```
;sierra-pcm .Audio0/direc DEO2 ( establecer la dirección de la muestra )
@ -386,11 +386,11 @@ la frecuencia a la que se reproduce esta muestra (es decir, a la que la amplitud
el byte tono 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.
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.
el octavo bit es una bandera: cuando es 0 la muestra se reproducirá en bucle y cuando es 1 la muestra se reproducirá solo 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.
normalmente querremos hacer un bucle de la muestra para generar un tono basado en ella. solo 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.
@ -423,9 +423,9 @@ BRK
## 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.
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.
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:
@ -466,7 +466,7 @@ 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 tono!
¡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 tono!
```
( hola-sonido.tal )
@ -485,11 +485,11 @@ ok, ¡ahora estamos listos para reproducir el sonido!
BRK
```
nota (!) que sólo se reproducirá el sonido una vez, y lo hace cuando se inicia el programa.
nota (!) que solo 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?
te invito a que experimentes modificando los valores del ADSR: ¿cómo cambia el sonido cuando solo 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 tono: ¿corresponden con tus oídos los valores midi que esperas?
@ -534,7 +534,7 @@ el byte de salida nos permite leer la amplitud de la envolvente. devuelve 0 cuan
## 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.
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:
@ -567,4 +567,4 @@ pero antes de hacer todo esto, ¡no te olvides de tomar un descanso! :)
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)