Merge branch 'main' of tildegit.org:sejo/compudanzas

This commit is contained in:
sejo 2022-01-14 15:43:37 -06:00
commit 471083a26d
13 changed files with 437 additions and 444 deletions

View File

@ -121,7 +121,7 @@ ADD2 ( ptra: x^ límite^ )
STH2 ( ptra: x^ / pret: límite^ )
```
otra:
otra más:
```
( estado inicial: ptra: x^ ancho^ )
@ -284,15 +284,15 @@ DUP2 ( ptra: x^ ancho^ altura^ y^ y^ )
.Pantalla/y DEO2 ( ptra: x^ ancho^ alto^ y^ )
( calcular y almacenar el límite vertical )
ADD2 ( ptra: x^ ancho^ límite-y^ )
STH2 ( ptra: x^ ancho^ / pret: límite-y^ )
ADD2 ( ptra: x^ ancho^ limite-y^ )
STH2 ( ptra: x^ ancho^ / pret: limite-y^ )
```
ahora, podríamos también almacenar el ancho y `x`, ya que las necesitamos después en su orden original (primero x, luego anchura )
ahora, podríamos también almacenar el ancho y `x`, ya que las necesitamos después en su orden original (primero x, luego el ancho )
```
STH2 ( ptra: x^ / pret: límite-y^ ancho^ )
STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
STH2 ( ptra: x^ / pret: limite-y^ ancho^ )
STH2 ( ptra: / pret: limite-y^ ancho^ x^ )
```
en teoría, la primera parte de nuestra subrutina podría verse como:
@ -305,23 +305,23 @@ en teoría, la primera parte de nuestra subrutina podría verse como:
ROT2 ( ptra: x^ ^ altura^ y^ )
DUP2 ( ptra: x^ ^ altura^ y^ )
( fijar y inicial )
.Pantalla/y DEO2 ( fijar y inicial, ptra: x^ ^ altura^ y^ )
( fijar `y` inicial )
.Pantalla/y DEO2 ( fijar `y` inicial, ptra: x^ ^ altura^ y^ )
( calcular y guardar límite-y )
ADD2 ( ptra: x^ ^ límite-y^ )
STH2 ( ptra: x^ ^ / pret: límite-y^ )
( calcular y guardar limite-y )
ADD2 ( ptra: x^ ^ limite-y^ )
STH2 ( ptra: x^ ^ / pret: limite-y^ )
( almacenar anchura y `x` )
STH2 STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
( almacenar ancho y `x` )
STH2 STH2 ( ptra: / pret: limite-y^ ancho^ x^ )
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2r ( ptra: x^ / pret: límite-y^ ancho^ )
STH2r ( ptra: x^ / pret: limite-y^ ancho^ )
( recuperar la anchura )
STH2r ( ptra: x^ ancho^ / pret: límite-y^ )
( recuperar el ancho )
STH2r ( ptra: x^ ancho^ / pret: limite-y^ )
;dibuja-tiles-en-fila JSR2
```
@ -333,7 +333,7 @@ podemos pensar que podríamos reemplazar estas instrucciones con STH2kr:
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2kr ( ptra: x^ / pret: límite-y^ ancho^ x^ )
STH2kr ( ptra: x^ / pret: limite-y^ ancho^ x^ )
```
¡pero entonces no podemos recuperar el ancho porque la x sigue en la parte superior de la pila de retorno!
@ -343,35 +343,35 @@ oh, muchas dificultades, pero por el bien del ejemplo de la pila, vamos a seguir
¿cómo podemos poner el ancho en la parte superior de la pila de retorno? tal vez con un intercambio aplicado a la pila de retorno:
```
SWP2r ( ptra: x^ / pret: límite-y^ x^ ancho^ )
SWP2r ( ptra: x^ / pret: limite-y^ x^ ancho^ )
```
entonces podemos recuperar el ancho y utilizarlo:
```
STH2kr ( ptra: x^ ancho^ / pret: límite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: límite-y^ x^ ancho^ )
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:
```
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a y; ptra: y^ y^ / pret: límite-y^ x^ ancho^ )
.Pantalla/y DEO2 ( almacenar nueva `y`; ptra: y^ / pret: límite-y^ x^ ancho^ )
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añade 8 a `y`; ptra: y^ y^ / pret: limite-y^ x^ ancho^ )
.Pantalla/y DEO2 ( almacenar nueva `y`; ptra: y^ / pret: limite-y^ x^ ancho^ )
```
para conseguir el límite en la pila de trabajo para la comparación, tenemos que girar la pila de retorno:
```
ROT2r ( ptra: y^ / pret: x^ ancho^ límite-y^ )
STH2kr ( ptra: y^ límite-y^ / pret: x^ ancho^ límite-y^ )
ROT2r ( ptra: y^ / pret: x^ ancho^ limite-y^ )
STH2kr ( ptra: y^ limite-y^ / pret: x^ ancho^ limite-y^ )
```
pero ah, antes de hacer la comparación y el salto, debemos reordenar la pila de retorno para que se corresponda con la ordenación que teníamos al principio del bucle:
```
SWP2r ( ptra: y^ límite-y^ / pret: x^ límite-y^ ancho^ )
ROT2r ( ptra: y^ límite-y^ / pret: límite-y^ ancho^ x^ )
SWP2r ( ptra: y^ limite-y^ / pret: x^ limite-y^ ancho^ )
ROT2r ( ptra: y^ limite-y^ / pret: limite-y^ ancho^ x^ )
```
ahora podemos hacer la comparación y saltar:
@ -396,36 +396,36 @@ después de todo esto, nuestra subrutina tendría el siguiente aspecto:
ROT2 ( ptra: x^ ^ altura^ y^ )
DUP2 ( ptra: x^ ^ altura^ y^ )
( fijar y inicial )
.Pantalla/y DEO2 ( fijar y inicial, ptra: x^ ^ altura^ y^ )
( fijar `y` inicial )
.Pantalla/y DEO2 ( fijar `y` inicial, ptra: x^ ^ altura^ y^ )
( calcular y almacenar límite-y )
ADD2 ( ptra: x^ ^ límite-y^ )
STH2 ( ptra: x^ ^ / pret: límite-y^ )
( calcular y almacenar limite-y )
ADD2 ( ptra: x^ ^ limite-y^ )
STH2 ( ptra: x^ ^ / pret: limite-y^ )
( almacenar anchura y `x` )
STH2 STH2 ( ptra: / pret: límite-y^ ancho^ x^ )
( almacenar ancho y `x` )
STH2 STH2 ( ptra: / pret: limite-y^ ancho^ x^ )
&bucle-y
( preparar y dibujar la fila )
( recuperar x )
STH2kr ( ptra: x^ / pret: límite-y^ ancho^ x^ )
STH2kr ( ptra: x^ / pret: limite-y^ ancho^ x^ )
( recuperar la anchura )
SWP2r ( ptra: x^ / pret: límite-y^ x^ ancho^ )
STH2kr ( ptra: x^ ancho^ / pret: límite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: límite-y^ x^ ancho^ )
( recuperar el ancho )
SWP2r ( ptra: x^ / pret: limite-y^ x^ ancho^ )
STH2kr ( ptra: x^ ancho^ / pret: limite-y^ x^ ancho^ )
;dibuja-tiles-en-fila JSR2 ( ptra: / pret: limite-y^ x^ ancho^ )
.Pantalla/y DEI2 #0008 ADD2 DUP2 ( añadir 8 a y )
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar límite-y )
ROT2r ( ptra: y^ / pret: x^ ^ límite-y^ )
STH2kr ( ptra: y^ límite-y^ / pret: x^ ancho^ límite-y^ )
( recuperar limite-y )
ROT2r ( ptra: y^ / pret: x^ ^ limite-y^ )
STH2kr ( ptra: y^ limite-y^ / pret: x^ ancho^ limite-y^ )
( reordenar la pila de retorno )
SWP2r ( ptra: y^ límite-y^ / pret: x^ límite-y^ ancho^ )
ROT2r ( ptra: y^ límite-y^ / pret: límite-y^ ancho^ x^ )
SWP2r ( ptra: y^ limite-y^ / pret: x^ limite-y^ ancho^ )
ROT2r ( ptra: y^ limite-y^ / pret: limite-y^ ancho^ x^ )
LTH2 ,&bucle-y JCN ( salta si x es menor que el límite )
@ -436,7 +436,7 @@ RTN
podemos entonces llamarlo de la siguiente manera para obtener un cuadrado de 256x256 lleno de tiles:
```
#0008 #0008 ( x y `y` )
#0008 #0008 ( `x` y `y` )
#0100 #0100 ( ancho y alto )
;tile-fondo
;dibuja-tiles JSR2
@ -454,7 +454,7 @@ declararemos las siguientes etiquetas para nuestras variables, después del RTN
```
( variables )
&alto $2 &ancho $2 &y $2 &x $2 &límite-y $2
&alto $2 &ancho $2 &y $2 &x $2 &limite-y $2
```
ahora, iniciamos la subrutina de la misma manera que antes, estableciendo la dirección para nuestro sprite:
@ -480,21 +480,21 @@ después de estas operaciones las pilas están vacías.
entonces podemos fijar la `y` inicial y calcular el límite vertical, utilizando los valores almacenados en las variables:
```
( establecer y inicial )
( establecer `y` inicial )
,&y LDR2 DUP2 ( ptra: y^ y^ )
.Pantalla/y DEO2 ( ptra: y^ )
( calcular límite-y )
( calcular limite-y )
,&alto LDR2 ( ptra: y^ alto^ )
ADD2 ( ptra: límite-y^ )
,&límite-y STR2 ( ptra: )
ADD2 ( ptra: limite-y^ )
,&limite-y STR2 ( ptra: )
```
nuestro bucle ahora tendría el siguiente aspecto:
```
&bucle-y
( recuperar x y ancho )
( recuperar `x` y ancho )
,&x LDR2
&ancho LDR2
( dibujar fila )
@ -504,7 +504,7 @@ nuestro bucle ahora tendría el siguiente aspecto:
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar el límite vertical )
,&límite-y LDR2
,&limite-y LDR2
LTH2 ,&bucle-y JCN ( saltar si x es menor que el límite )
```
@ -526,17 +526,17 @@ la subrutina completa tendría el siguiente aspecto:
&y STR2
,&x STR2
( establecer y inicial )
( establecer `y` inicial )
,&y LDR2 DUP2 ( ptra: y^ y^ )
.Pantalla/y DEO2 ( ptra: y^ )
( calcular el límite vertical )
,&alto LDR2 ( ptra: y^ alto^ )
ADD2 ( ptra: límite-y^ )
,&límite-y STR2 ( ptra: )
ADD2 ( ptra: limite-y^ )
,&limite-y STR2 ( ptra: )
&bucle-y
( recuperar x y anchura )
( recuperar `x` y ancho )
,&x LDR2
,&ancho LDR2
( dibujar fila )
@ -546,13 +546,13 @@ la subrutina completa tendría el siguiente aspecto:
.Pantalla/y DEO2 ( almacenar la nueva y )
( recuperar el límite vertical )
,&límite-y LDR2
,&limite-y LDR2
LTH2 ,&bucle-y JCN ( saltar si x es menor que el límite )
RTN
( variables )
&alto $2 &ancho $2 &y $2 &x $2 &límite-y $2
&alto $2 &ancho $2 &y $2 &x $2 &limite-y $2
```
como he dicho antes, podemos encontrar aquí algunas oportunidades de optimización.
@ -561,7 +561,7 @@ tal vez el límite vertical puede ser escondido en la pila de retorno como en el
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.
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.
@ -584,7 +584,8 @@ RTN
que podemos llamar simplemente desde nuestra subrutina de iniciación:
```
dibuja-fondo JSR2
;dibuja-fondo JSR2
;dibuja-fondo JSR2
```
=> ./img/screenshot_uxn-background-full.png captura de pantalla mostrando la pantalla varvara cubierta de líneas diagonales.

View File

@ -25,10 +25,12 @@ personalmente, veo en ella las siguientes virtudes:
* á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! sin embargo, noto algunos aspectos que pueden hacerla parecer no tan asequible:
¡todos estos conceotos suenan genial para mí, y espero que para tí también!
sin embargo, noto algunos aspectos que pueden hacerla parecer no tan asequible:
* es programada en un lenguaje ensamblador, uxntal
* utiliza notación {postfix} (alias notación polaca inversa) / está inspirada en máquinas forth
* utiliza notación {postfix} (alias notación polaca inversa) y está inspirada en máquinas forth
la idea de este tutorial es explorar estos dos aspectos y revelar cómo trabajan juntos para dar a uxn su poder con una complejidad relativamente baja.
@ -36,9 +38,9 @@ la idea de este tutorial es explorar estos dos aspectos y revelar cómo trabajan
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 operaciines son indicadas mediante la llamada notación postfija.
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.
> Notación Polaca Reversa (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 [...]
> 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 [...]
=> https://es.wikipedia.org/wiki/Notaci%C3%B3n_polaca_inversa Notación Polaca Inversa - Wikipedia
@ -56,9 +58,9 @@ dónde, leyendo de izquierda a derecha:
* 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 libro Starting Forth tiene algunas ilustraciones grandiosas sobre este proceso de suma:
el libro Starting Forth tiene algunas buenas ilustraciones sobre este proceso de suma:
=> https://www.forth.com/starting-forth/1-forth-stacks-dictionary/#The_Stack_Forth8217s_Workspace_for_Arithmetic The Stack: Forths Workspace for Arithmetic
=> https://www.forth.com/starting-forth/1-forth-stacks-dictionary/#The_Stack_Forth8217s_Workspace_for_Arithmetic The Stack: Forths Workspace for Arithmetic (Inglés)
## de infija a postfija
@ -107,13 +109,13 @@ las palabras binarias de 8 bits, también conocidas como bytes, son los elemento
uxn puede manejar también palabras binarias de 16 bits (2 bytes), también conocidas como cortos, mediante la concatenación de dos bytes consecutivos. vamos a hablar más sobre esto en el segundo día de este tutorial.
los números en uxn son expresados utilizando el sistema hexadecimal (base 16), en el qhe cada dígito (nibble) va de 0 a 9 y luego de 'a' a 'f' (en minúscula).
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.
## el cpu uxn
se ha dicho que el cpu uxn es una remolacha, capaz de ejecutar 32 instrucciones diferentes con tres banderas de modo diferentes.
se ha dicho que el cpu uxn es una betabel, 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.
@ -411,7 +413,7 @@ si la dirección es de 2 bytes de longitud, es asumido que es una dirección de
## runa hex literal
hablemos de otra runa: #.
hablemos de otra runa: #
éste caracter define un "hex literal": es básicamente un atajo para la instrucción LIT.
@ -620,4 +622,3 @@ en el {tutorial de uxn día 2} empezamos a explorar los aspectos visuales de la
# apoyo
si este tutorial te ha resultado de ayuda, considera compartirlo y brindarle tu {apoyo} :)

View File

@ -6,9 +6,7 @@ en esta sección vamos a empezar a explorar los aspectos visuales de la computad
también discutiremos el trabajo con el modo corto o "short" (2 bytes) junto a los de un sólo byte en uxntal.
si todavía no lo has hecho te recomiendo que leas las sección anterior:
=> ./tutorial_de_uxn_día_1.gmi {tutorial de uxn día 1}
si todavía no lo has hecho te recomiendo que leas las sección anterior en el {tutorial de uxn día 1}
# donde están tus cortos?
@ -70,6 +68,8 @@ ahora, si añadimos el sufijo '2' a la instrucción LIT, podríamos escribir en
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:
```
@ -184,7 +184,7 @@ podemos escribirlo de la siguiente manera:
( programa principal )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -322,7 +322,7 @@ el programa completo se vería de la siguiente manera:
( programa principal )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -661,7 +661,7 @@ además, 5, 'a' y 'f' en el nibble bajo dibujarán los píxeles que están "ence
( programa principal )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -703,7 +703,7 @@ el siguiente código dibujará nuestro sprite cuadrado con las 16 combinaciones
( programa principal )
|0100
( establecer colores de sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -934,7 +934,7 @@ el siguiente código mostrará nuestro sprite en las 16 diferentes combinaciones
( programa principal )
|0100
( establecer colores de sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2

View File

@ -24,15 +24,18 @@ el byte de botón codifica en cada uno de sus ocho bits el estado de ocho "boton
numerando los bits de derecha a izquierda, y de 0 a 7, las teclas correspondientes (y los botones de NES) son
* 0: Ctrl (botón A)
* 1: Alt (botón B)
* 2: Shift (botón de selección)
* 3: Esc (botón de inicio)
* 4: Arriba
* 5: Abajo
* 6: Izquierda
* 7: Derecha
+ <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>
+ <tr><td>Derecha</td><td>Izquierda</td><td>Abajo</td><td>Arriba</td><td>Esc ("Start" o iniciar))</td><td>Shift ("Select" o seleccionar)</td><td>Alt (botón B)</td><td>Ctrl (botón A)</td></tr>
+ </table>
& * 0: Ctrl (botón A)
& * 1: Alt (botón B)
& * 2: Shift (botón "Select" o seleccionar)
& * 3: Esc (botón "Start" o iniciar)
& * 4: Arriba
& * 5: Abajo
& * 6: Izquierda
& * 7: Derecha
codificar los estados de los botones de esta manera nos permite presionar y leer muchas de estas teclas al mismo tiempo.
@ -88,7 +91,7 @@ este utiliza el procedimiento de dibujo de sprites que probamos el día anterior
( programa principal )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -121,7 +124,7 @@ ahora, ¿cómo podemos realizar diferentes acciones dependiendo de la tecla que
en primer lugar, tenemos que hacer que nuestro programa sepa qué tecla se ha pulsado para que pueda actuar en consecuencia.
para ello, veamos algunas instrucciones uxntales nuevas.
para ello, veamos algunas instrucciones uxntal nuevas.
# instrucciones de comparación y lógica
@ -160,7 +163,7 @@ AND2, ORA2, EOR2 funcionarán de la misma manera, pero con cortos en lugar de by
### 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 'falso0:
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 )
@ -217,7 +220,7 @@ sólo cuando ambas banderas sean falsas, la bandera resultante será falsa.
### EOR
un EOR 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 sólo 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.
@ -261,12 +264,12 @@ ok, ahora nuestros programas pueden identificar y almacenar en banderas si un va
## instrucciones para 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 -- )
* JMP: "jump" o salto incondicional a la dirección de la pila ( direc -- )
* JCN: "conditional jump" o salto condicional: toma una dirección y un valor de la pila, y si el valor no es 00, salta a la dirección; en caso contrario continúa con la siguiente instrucción ( valor direc -- )
en modo byte, las direcciones que utilizan estas instrucciones son de un byte.
estas direcciones de un byte son relativas y con signo: indican cuántos bytes hay que saltar en la memoria principal desde la posición actual del contador de programa, ya sea hacia delante (positivo) o hacia atrás (negativo). el rango de estas direcciones relativas es de -128 a 127 inclusive.
estas direcciones de un byte son relativas y pueden considerarse positivas y negativas: indican cuántos bytes hay que saltar en la memoria principal desde la posición actual del contador de programa, ya sea hacia delante (positivo) o hacia atrás (negativo). el rango de estas direcciones relativas es de -128 a 127 inclusive.
en modo corto, las direcciones que toman estas instrucciones son absolutas (es decir, de dos bytes de longitud), pero el valor que toma JCN para decidir sigue siendo un byte.
@ -278,8 +281,8 @@ en los días anteriores ya hablamos de algunas de ellas; esta es una recapitulac
* dirección literal en la página cero: .etiqueta (un byte)
* dirección literal en memoria principal: ;label (un corto)
* Dirección literal relativa en la memoria principal: ,label (un byte)
* dirección literal en la memoria principal: :label (un byte)
* dirección literal relativa en la memoria principal: ,label (un byte)
* dirección cruda ("raw") en la memoria principal: :label (un byte)
para definir las etiquetas, utilizamos:
@ -316,9 +319,9 @@ la siguiente subrutina en-controlador ilustra el uso de los saltos, dibujando nu
( establece la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
( dibujar el sprite en el fondo )
( dibuja el sprite en el fondo )
( usando el color 1 para el contorno )
#01 .Pantalla /sprite DEO
#01 .Pantalla/sprite DEO
&fin
BRK
@ -332,7 +335,7 @@ estas direcciones relativas, de un byte, son utilizadas por JCN o JMP.
## saltos condicionales
el siguiente código ilustra el uso de muchas condiciones: el color del sprite cambia en consecuencia si se pulsan las teclas 1, 2 o 3.
el siguiente código ilustra el uso de muchas condiciones: el color del sprite cambia según se pulsen las teclas 1, 2 o 3.
```
@en-controlador
@ -379,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 PRINT-DIGIT del día 1!
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!
# manipulación de la pila
@ -387,14 +390,14 @@ hasta ahora hemos estado usando la pila como un lugar para almacenar operandos d
## instrucciones de pila
uxntal tiene seis instrucciones que actúan sobre los elementos de la pila más cercanos a la parte superior:
uxntal tiene seis instrucciones que actúan sobre los elementos de la pila más cercanos a la parte superior:g
* POP: Eliminar el elemento superior de la pila ( a -- )
* DUP: Duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: Intercambiar; cambiar el orden de los dos primeros elementos de la pila ( a b -- b a )
* NIP: Elimina el segundo elemento superior de la pila ( a b -- b )
* OVR: "Over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: Rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
* POP: eliminar el elemento superior de la pila ( a -- )
* DUP: duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: "swap" o intercambiar; cambiar el orden de los dos primeros elementos de la pila ( a b -- b a )
* NIP: elimina el segundo elemento superior de la pila ( a b -- b )
* OVR: "over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
en modo corto, POP2, DUP2, SWP2, NIP2, OVR2 y ROT2 realizan las mismas acciones pero utilizando cortos en lugar de bytes.
@ -456,7 +459,7 @@ ahora la pila se ve así:
bandera1 tecla <- arriba
```
finalmente podemos proceder a la comparación y al AND
finalmente podemos proceder a la comparación y al AND:
```
#3a LTH ( ¿es menor que 3a? empuja la bandera en la pila )
@ -488,15 +491,15 @@ en el caso que acabamos de discutir la entrada es la tecla que se presiona, pero
esto implica que podríamos escribir la rutina como una macro:
```
ASCII-DIGIT { DUP #2f GTH SWP #3a LTH AND } ( byte -- bandera )
%?DIGITO-ASCII { DUP #2f GTH SWP #3a LTH AND } ( byte -- bandera )
```
y utilizarla con el byte que queramos:
```
#30 ?ASCII-DIGIT ( empuja 01 hacia abajo en la pila )
#20 ?ASCII-DIGIT ( empuja 00 hacia abajo en la pila )
.Controlador/tecla DEI ?ASCII-DIGIT ( empuja la bandera correspondiente a la pila )
#30 ?DIGITO-ASCII ( empuja 01 hacia abajo en la pila )
#20 ?DIGITO-ASCII ( empuja 00 hacia abajo en la pila )
.Controlador/tecla DEI ?DIGITO-ASCII ( empuja la bandera correspondiente a la pila )
```
### duplicados para los condicionales
@ -573,9 +576,9 @@ conoce las máscaras AND a nivel de bits.
una máscara AND es un valor especial que utilizaremos para mantener o perder bits específicos de otro valor dado, como el byte del botón del controlador.
en nuestra máscara AND, pondremos como 1 los bits en las posiciones en las que queramos mantener el valor de los bits de entrada. las posiciones en las que los bits de la máscara sean 0 se convertirán en 0 en la entrada.
en nuestra máscara AND, estableceremos como 1 los bits en las posiciones en las que queramos mantener el valor de los bits de entrada. las posiciones en las que los bits de la máscara sean 0 se convertirán en 0 en la entrada.
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.
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:
@ -592,7 +595,7 @@ AND 0001 0000 ( máscara )
0000 0000 ( resultado )
```
¿qué ocurre si se pulsa el botón de arriba?
¿qué ocurre si se pulsa el botón Arriba?
```
0001 0000 ( botón )
@ -601,7 +604,7 @@ AND 0001 0000 ( máscara )
0001 0000 ( resultado )
```
¿y si se pulsan tanto Up como Ctrl?
¿y si se pulsan tanto Arriba como Ctrl?
```
0001 0001 ( botón )
@ -626,7 +629,7 @@ el siguiente programa uxntal permite dibujar utilizando las teclas de las flecha
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 apilamiento.
observa el uso de las máscaras AND, los saltos condicionales y algunas operaciones de la pila.
```
( dibujar-con-teclado.tal )
@ -637,7 +640,7 @@ observa el uso de las máscaras AND, los saltos condicionales y algunas operacio
( programa principal )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -659,12 +662,12 @@ BRK
&contorno
#01 .Pantalla/sprite DEO ( dibujar contorno )
,&comprobar-filas JMP ( continuar con la comprobación de las filas )
,&comprobar-flechas JMP ( continuar con la comprobación de las flechas )
&relleno
#04 .Pantalla/sprite DEO ( dibujar relleno )
&comprobar-filas
&comprobar-flechas
( usar el byte del botón de la pila )
DUP #10 AND ( aislar el bit 4, correspondiente a Arriba )
,&arriba JCN ( saltar si no es 0 )
@ -682,7 +685,7 @@ BRK
POP
BRK
&abajo
.Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 ( incremento y )
.Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 ( incrementa y )
POP
BRK
&izquierda
@ -690,7 +693,7 @@ BRK
POP
BRK
&derecha
.Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 ( incremento x )
.Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 ( incrementa x )
POP
BRK
BRK
@ -710,22 +713,22 @@ algunas posibilidades para que practiques:
recuerde que .Pantalla/x es una dirección literal en la página cero, es decir, empuja un byte correspondiente a la dirección de la sub-etiqueta Pantalla/x :)
# posibilidades de práctica
# practica posibilidades
¡aquí tienes otras ideas para que practiques con lo que hemos tratado hoy!
¡aquí tienes otras ideas para que practiques con lo que hemos cubierto hoy!
* dibujar un controlador virtual que muestre cuáles de sus botones, mapeados a teclas del teclado, están siendo presionados
* crear una especie de máquina de escribir que dibuje diferentes símbolos y mueva el cursor de dibujo dependiendo de la tecla que se haya pulsado
* dibujar un personaje que cambie su estado según la tecla que se haya pulsado. ¿tal vez utilizar múltiples fichas 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.
* dibujar un personaje que cambie su estado según la tecla que se haya pulsado. ¿tal vez utilizar múltiples tiles para dibujarlo?
* crea un simple tablero de tres-en-raya para dos jugadores: una tecla dibuja una X, otra dibuja una O, y las flechas permiten elegir la casilla a dibujar.
¡ten en cuenta que para un movimiento interactivo suave puede ser mejor utilizar el vector de pantalla que se llama 60 veces por segundo!
¡ten en cuenta que para un suave movimiento interactivo puede ser mejor utilizar el vector de pantalla que se llama 60 veces por segundo!
¡lo cubriremos en profundidad en el próximo día del tutorial!
# instrucciones del día 3
estas son todas las instrucciones uxntales de las que hemos hablado hoy
¡estas son todas las instrucciones uxntal de las que hemos hablado hoy!
## instrucciones de comparación
@ -747,17 +750,16 @@ estas son todas las instrucciones uxntales de las que hemos hablado hoy
## pila
* POP: Quitar el elemento superior de la pila ( a -- )
* DUP: Duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: Intercambio; cambia el orden de los dos primeros elementos de la pila ( a b -- b a )
* OVR: "Over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: Rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
* POP: eliminar el elemento superior de la pila ( a -- )
* DUP: duplicar; empujar una copia del elemento superior ( a -- a a )
* SWP: "swap" o intercambiar; cambiar el orden de los dos primeros elementos de la pila ( a b -- b a )
* NIP: elimina el segundo elemento superior de la pila ( a b -- b )
* OVR: "over" o encima; empuja una copia del segundo elemento superior ( a b -- a b a )
* ROT: rotar; reordenar los tres primeros elementos de la pila de forma que el tercero esté ahora en la parte superior ( a b c -- b c a )
# día 4
en el tutorial uxn día 4 cubrimos el uso del vector de pantalla para crear animaciones, ya sean interactivas o no.
=> ./tutorial_de_uxn_día_4.gmi {tutorial de uxn día 4}
en el {tutorial de uxn día 4} cubrimos el uso del vector de pantalla para crear animaciones, ya sean interactivas o no.
¡también exploramos las posibilidades de usar "variables" en uxntal que pueden ayudarnos a crear programas más elaborados!

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 complejas luchas 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
@ -14,9 +14,7 @@ discutimos el dispositivo de pantalla de varvara en el día 2, pero nos saltamos
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ]
```
ahora que ya tenemos el concepto de vectores de dispositivos en el tutorial de uxn día 3, ¡vamos a entrar de lleno en cómo usar el de la pantalla!
=> ./tutorial_de_uxn_día_3.gmi {tutorial de uxn día 3}
ahora que ya tenemos el concepto de vectores de dispositivos en el {tutorial de uxn día 3}, ¡vamos a entrar de lleno en cómo usar el de la pantalla!
## asignación
@ -25,7 +23,8 @@ 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 un ritmo 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, y/o también podemos utilizarla para otros propósitos relacionados con la temporización.
## una línea que crece
@ -40,7 +39,7 @@ el siguiente programa demuestra un uso básico pero potente del vector pantalla:
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -49,7 +48,7 @@ el siguiente programa demuestra un uso básico pero potente del vector pantalla:
#0008 .Pantalla/x DEO2
#0008 .Pantalla/y DEO2
( establecer vector de pantalla )
( establecer el vector de pantalla )
;en-cuadro .Pantalla/vector DEO2
BRK
@ -72,10 +71,10 @@ aquí, el código para incrementar Pantalla/x es llamado dentro de la subrutina
estos son algunos cambios para que los pruebes y practiques:
* ¿cómo harías que la línea creciera en vertical? y en diagonal?
* ¿cómo harías que la línea creciera en vertical? ¿y en diagonal?
* ¿cómo harías que la línea creciera en sentido contrario?
* ¿Cómo harías que la línea fuera punteada?
* ¿Cómo harías que la línea dejara de crecer en una determinada posición? (¡recuerda los saltos condicionales!)
* ¿cómo harías que la línea fuera punteada?
* ¿cómo harías que la línea dejara de crecer en una determinada posición? (¡recuerda los saltos condicionales!)
* ¿cómo dibujarías utilizando un sprite en lugar de un píxel?
# variables
@ -84,9 +83,9 @@ estos son algunos cambios para que los pruebes y practiques:
merece señalar que muchas de estas posibilidades requieren formas de almacenar y recuperar datos entre fotogramas.
en el ejemplo anterior, estamos usando los puertos de pantalla para las coordenadas x e y como una forma de almacenar las coordenadas del pixel.
en el ejemplo anterior, estamos usando los puertos de pantalla para las coordenadas x e `y` como una forma de almacenar las coordenadas del pixel.
pero ¿qué sucede cuando queremos dibujar diferentes objetos, cada uno con su propio conjunto de coordenadas y otras características que pueden cambiar con el tiempo?
pero, ¿qué sucede cuando queremos dibujar diferentes objetos, cada uno con su propio conjunto de coordenadas y otras características que pueden cambiar con el tiempo?
¡podemos utilizar etiquetas en la memoria del programa para conseguirlo!
@ -104,7 +103,7 @@ sin embargo, no hemos utilizado los datos directamente; hemos enviado su direcci
### etiquetas
podríamos utilizar un sistema similar para almacenar, por ejemplo, las coordenadas x e y en lugar de los datos del sprite:
podríamos utilizar un sistema similar para almacenar, por ejemplo, las coordenadas x e `y` en lugar de los datos del sprite:
```
@pixel-x 0008
@ -117,8 +116,8 @@ o si no quisiéramos iniciarlas aquí, podríamos definirlas de la siguiente man
@pixel-x $2
@pixel-y $2
```
recuerda que $2 crea un pad relativo de dos bytes: esto hace que pixel-y sea una etiqueta para una dirección en memoria dos bytes después de pixel-x. y cualquier código posterior ocurrirá dos bytes después de pixel-y.
recuerda que $2 crea un pad relativo de dos bytes: esto hace que píxel-y sea una etiqueta para una dirección en memoria dos bytes después de pixel-x. y cualquier código posterior ocurrirá dos bytes después de píxel-y.
también podríamos usar etiquetas y sub-etiquetas, de manera muy similar a como definimos los dispositivos y sus puertos:
@ -132,8 +131,8 @@ también podríamos usar etiquetas y sub-etiquetas, de manera muy similar a como
aquí están las dos instrucciones que nos ayudarían:
* LDA: carga y empuja hacia abajo en la pila el valor en la dirección absoluta dada ( dirección -- valor )
* STA: almacena en la dirección absoluta dada el valor dado ( dirección valor -- )
* LDA: "load", carga y empuja hacia abajo en la pila el valor en la dirección absoluta dada ( dirección -- valor )
* STA: "store", almacena en la dirección absoluta dada el valor dado ( dirección valor -- )
como ya hemos comentado, una dirección absoluta siempre tendrá una longitud de dos bytes.
@ -189,7 +188,7 @@ las variables con dirección absoluta funcionan bien para los casos en los que q
sin embargo, uxn tiene un mecanismo mejor para esos casos: ¡la página cero!
como recordará, la página cero consiste en las primeras 256 direcciones de la memoria del programa. normalmente, un programa comienza en la dirección 0100, que es la siguiente dirección después de la página cero.
como recordarás, la página cero consiste en las primeras 256 direcciones de la memoria del programa. normalmente, un programa comienza en la dirección 0100, que es la siguiente dirección después de la página cero.
podemos referirnos a cualquiera de las 256 direcciones de la página cero utilizando un solo byte, en lugar de los dos bytes que se necesitan para las direcciones absolutas.
@ -212,8 +211,8 @@ para referirnos a ellas, utilizaríamos la runa punto (.) para las direcciones l
las instrucciones para cargar (leer) y almacenar (escribir) desde y hacia la página cero son:
* LDZ: carga y empuja hacia abajo en la pila el valor en la dirección de la página cero dada ( dirección -- valor )
* STZ: almacena en la dirección de la página cero el valor dado ( dirección del valor -- )
* LDZ: "load", carga y empuja hacia abajo en la pila el valor en la dirección de la página cero dada ( dirección -- valor )
* STZ: "store", almacena en la dirección de la página cero el valor dado ( dirección del valor -- )
en estas instrucciones, la dirección siempre será de un byte.
@ -221,7 +220,7 @@ en el modo corto, LDZ2 cargará un corto desde la dirección dada, y STZ2 almace
### ejemplos
el siguiente ejemplo consiste en la misma línea que crece, pero ahora utilizando la página cero para almacenar las coordenadas x e y del píxel en lugar de los puertos x e y de la pantalla.
el siguiente ejemplo consiste en la misma línea que crece, pero ahora utilizando la página cero para almacenar las coordenadas x e `y` del píxel en lugar de los puertos x e `y` de la pantalla.
en este caso el programa es más largo, pero puede ser visto como una buena plantilla para tener otras líneas que se comporten de diferentes maneras:
@ -238,7 +237,7 @@ en este caso el programa es más largo, pero puede ser visto como una buena plan
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -247,7 +246,7 @@ en este caso el programa es más largo, pero puede ser visto como una buena plan
#0008 .pixel/x STZ2
#0008 .pixel/y STZ2
( establecer vector de pantalla )
( establecer el vector de pantalla )
;en-cuadro .Pantalla/vector DEO2
BRK
@ -266,9 +265,9 @@ BRK
notemos el uso de la runa literal de dirección de página cero (.) para referirse a la etiqueta .pixel.
además, observe que en el caso de .pixel la dirección se refiere a la página cero, a la que se accede con LDZ/STZ, y en el caso de .Pantalla la dirección se refiere al espacio de direcciones entrada/salida (i/o), al que se accede con DEO/DEI.
además, observe que en el caso de .pixel la dirección se refiere a la página cero, a la que se accede con LDZ/STZ, y en el caso de .Pantalla la dirección se refiere al espacio de direcciones entrada/salida (i/o), al que se accede con DEI/DEO.
### un poco de práctica en la pila
### 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:
@ -283,7 +282,7 @@ esta línea de código contiene la misma cantidad de bytes que la anterior.
una posible desventaja es que podría ser menos legible. pero una posible ventaja es que podría convertirse en una macro:
```
( incrementar un short desde la página cero )
( incrementar un corto desde la página cero )
%PC-INC2 { DUP LDZ2 INC2 ROT STZ2 } ( pc-dirección -- )
```
@ -293,14 +292,14 @@ otra posibilidad que tenemos en uxn y que podría ser más apropiada para las "v
de forma similar a las variables de la página cero, para direccionar estas variables sólo necesitamos un byte.
sin embargo, como estas direcciones se dan como offsets relativos y con signo, 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, sólo 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
las instrucciones para trabajar de esta manera son:
* LDR: carga y empuja hacia abajo en la pila el valor en la dirección relativa dada ( dirección -- valor )
* STR: almacena en la dirección relativa dada el valor dado ( dirección valor -- )
* LDR: "load", carga y empuja hacia abajo en la pila el valor en la dirección relativa dada ( dirección -- valor )
* STR: "store", almacena en la dirección relativa dada el valor dado ( dirección valor -- )
similar a LDZ y STZ, en estas instrucciones la dirección siempre será de un byte.
@ -326,9 +325,9 @@ BRK
@pixel [ &x $2 &y $2 ]
```
nótese el uso de la runa coma (,) para indicar que es una dirección relativa; uxnasm calcula el offset requerido asumiendo que será utilizado en la siguiente instrucción.
nótese el uso de la runa coma (,) para indicar que es una dirección relativa; uxnasm calcula el desfase requerido asumiendo que será utilizado en la siguiente instrucción.
en este caso realmente no podemos duplicar ese offset como hicimos anteriormente con la dirección de página cero, porque es específica de la posición en el código en que fue escrita.
en este caso realmente no podemos duplicar ese desfase como hicimos anteriormente con la dirección de página cero, porque es específica de la posición en el código en que fue escrita.
si declaráramos estas variables como sub-etiquetas de en-cuadro, el código quedaría como sigue:
@ -338,13 +337,13 @@ si declaráramos estas variables como sub-etiquetas de en-cuadro, el código que
,&pixel-x LDR2 .Pantalla/x DEO2
,&pixel-y LDR2 .Pantalla/y DEO2
( dibujar un pixel en el fondo con el color 1 )
( dibujar un píxel en el fondo con el color 1 )
#01 .Pantalla/pixel DEO
( incrementa pixel/x )
( incrementa píxel/x )
,&pixel-x LDR2 INC2 ,&pixel-x STR2
BRK
( variables locales en-cuadro )
( variables locales de en-cuadro )
&pixel-x $2 &pixel-y $2
```
@ -376,7 +375,7 @@ eso daría como resultado un sprite que se mueve y que además deja un rastro:
### sin rastro
ok, eso puede ser útil en algunos casos, pero ¿cómo podemos evitar dejar el rastro?
ok, eso puede ser útil en algunos casos, pero, ¿cómo podemos evitar dejar el rastro?
una posible forma de conseguirlo sería siguiendo este orden de operaciones dentro de la subrutina en-cuadro:
@ -410,7 +409,7 @@ el siguiente programa ilustra los puntos anteriores, haciendo que nuestro cuadra
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -421,7 +420,7 @@ el siguiente programa ilustra los puntos anteriores, haciendo que nuestro cuadra
( fijar la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
( establecer vector de pantalla )
( establecer el vector de pantalla )
;en-cuadro .Pantalla/vector DEO2
BRK
@ -448,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 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.
### posibilidades adicionales
@ -475,7 +474,7 @@ aquí hay algunas preguntas para que reflexiones y pruebes:
## cambio de posición interactivo
cuando usamos el vector controlador, estamos actuando en base a un cambio en el/los botón/es o tecla/s que fueron presionados o liberados. esto puede ser muy útil para algunas aplicaciones.
cuando usamos el vector controlador, estamos actuando en base a un cambio en el/los botón/es o tecla/s que fueron presionados o soltados. esto puede ser muy útil para algunas aplicaciones.
pero, ¿cómo podemos tratar de hacer una acción continua cuando una tecla se mantiene presionada?
@ -487,14 +486,12 @@ en algunos sistemas operativos, si mantenemos una tecla pulsada, ésta dispara e
el siguiente programa nos permite controlar la posición horizontal de nuestro cuadrado mediante las teclas de dirección.
=> ./img/screencap_uxn-moving-square.gif animado que muestra un cuadrado moviéndose horizontalmente en la pantalla, aparentemente controlado por un humano.
=> ./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 del día 3!
=> ./tutorial_de_uxn_día_3.gmi {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-enmovimiento.tal )
( hola-sprite-en-movimiento.tal )
( dispositivos )
|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ]
@ -511,7 +508,7 @@ el siguiente programa nos permite controlar la posición horizontal de nuestro c
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -522,7 +519,7 @@ el siguiente programa nos permite controlar la posición horizontal de nuestro c
( fijar la dirección del sprite )
;cuadrado .Pantalla/direc DEO2
( establecer vector de pantalla )
( establecer el vector de pantalla )
;en-cuadro .Pantalla/vector DEO2
BRK
@ -624,31 +621,31 @@ podemos aplicar esas macros después de incrementar o decrementar. por ejemplo:
otra estrategia de animación consistiría en cambiar el sprite que se dibuja en una posición determinada.
podrías tener una secuencia de sprites/marcos y animarlos ejecutándolos en secuencia
¡podrías tener una secuencia de sprites/fotogramas y animarlos ejecutándolos en secuencia!
## los fotogramas
a efectos prácticos te recomendaría tener un número de fotogramas correspondiente a una potencia de dos, como 2, 4, 8, 16, 32, etc.
para efectos prácticos te recomendaría tener un número de fotogramas correspondiente a una potencia de dos, como 2, 4, 8, 16, 32, etc.
por ejemplo, lo siguiente es una secuencia de ocho sprites de 1bpp que corresponden a una línea diagonal que se mueve desde abajo a la derecha hasta arriba a la izquierda:
```
@animación
&fotograma0 00 00 00 00 01 03
&fotograma1 00 00 00 01 03 06 0c
@animacion
&fotograma0 00 00 00 00 00 00 01 03
&fotograma1 00 00 00 00 01 03 06 0c
&fotograma2 00 00 01 03 06 0c 18 30
&fotograma3 01 03 06 0c 18 30 60 c0
&fotograma4 03 06 0c 18 30 60 c0 80
&fotograma5 0c 18 30 60 c0 80 00 00
&fotograma6 30 60 c0 80 00 00 00
&fotograma7 c0 80 00 00 00 00 00
&fotograma6 30 60 c0 80 00 00 00 00
&fotograma7 c0 80 00 00 00 00 00 00
```
nótese que cada fotograma consta de 8 bytes. eso implica que hay un desplazamiento de 8 bytes entre las direcciones correspondientes a cada subetiqueta.
nótese que cada fotograma consta de 8 bytes. eso implica que hay un desfase de 8 bytes entre las direcciones correspondientes a cada subetiqueta.
por ejemplo, la dirección de &fotograma1 sería 8 bytes más que la dirección de &fotograma0.
los fotogramas que utilizas también podrían estar compuestos por sprites de 2bpp. en ese caso, el desplazamiento entre fotogramas sería de 16 en decimal (10 en hexadecimal) bytes.
los fotogramas que utilizas también podrían estar compuestos por sprites de 2bpp. en ese caso, el desfase entre fotogramas sería de 16 en decimal (10 en hexadecimal) bytes.
## conteo de fotogramas
@ -658,17 +655,18 @@ 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
en la página cero declaramos la etiqueta para nuestro cuentafotogramas. utilizaremos la abreviación "cuentaftg" para ayudar a la legibilidad:
en la página cero declaramos la etiqueta para nuestro cuentafotogramas.
```
( página cero )
|0000
@cuentaftg $1
```
y en la subrutina en-cuadro lo incrementamos:
```
@ -711,9 +709,7 @@ podríamos definir esta operación de módulo rápido como una macro para hacer
%8MOD { #07 AND } ( byte -- byte%8 )
```
si esto no te ha quedado muy claro, te recomiendo que vuelvas a mirar el tutorial de uxn del día 3, en particular la discusión de las operaciones lógicas.
=> ./tutorial_de_uxn_día_3.gmi {tutorial de uxn día 3}
si esto no te ha quedado muy claro, te recomiendo que vuelvas a mirar el {tutorial de uxn día 3}, en particular la discusión de las operaciones lógicas.
## aritmética de punteros
@ -721,32 +717,32 @@ si esto no te ha quedado muy claro, te recomiendo que vuelvas a mirar el tutoria
podríamos usar varios saltos condicionales, o podríamos usar una forma más divertida que se puede llamar aritmética de punteros :)
observa que la subetiqueta para el primer fotograma (fotograma0) de nuestra animación tiene la misma dirección que la etiqueta para toda la animación. y, como ya hemos mencionado, el siguiente fotograma (fotograma1) comienza 8 bytes después.
observa que la subetiqueta para el primer fotograma (fotograma0) de nuestra animación tiene la misma dirección que la etiqueta para toda la animación. y, como ya mencionamos, el siguiente fotograma (fotograma1) comienza 8 bytes después.
la subetiqueta de cada fotograma siguiente está 8 bytes después de la anterior.
la subetiqueta para cada fotograma siguiente está 8 bytes después de la anterior.
o, otra forma de verlo:
* el fotograma 0 es 0 bytes después de la etiqueta de animación
* El fotograma 1 es 8 bytes después de la etiqueta de animación
* El fotograma 2 es de 16 bytes después de la etiqueta de animación
* El fotograma 3 es de 24 bytes después de la etiqueta de animación
* el fotograma0 esta 0 bytes después de la etiqueta de animación
* el fotograma1 esta 8 bytes después de la etiqueta de animación
* el fotograma2 esta 16 bytes después de la etiqueta de animación
* el fotograma3 esta 24 bytes después de la etiqueta de animación
* y así sucesivamente
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 :)
esta cantidad de bytes que separa cada subetiqueta se llama offset.
la cantidad de bytes que separa cada subetiqueta se llama "offset" o desfase.
### calculando el offset
### calculando el desfase
después de aplicar el módulo 8 a nuestro cuentafotogramas podemos multiplicarlo por 8 para obtener el offset respecto a la etiqueta de la animación:
después de aplicar el módulo 8 a nuestro cuentafotogramas podemos multiplicarlo por 8 para obtener el desfase respecto a la etiqueta de la animación:
```
.cuentaftg LDZ ( cargar cuentafotograma )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el desplazamiento )
#08 MUL ( multiplicar por 8 para obtener el desfase )
```
### de byte a corto
@ -755,7 +751,7 @@ nota que hasta ahora hemos estado trabajando con bytes, y todo ha ido bien.
sin embargo, ¡las direcciones absolutas son cortos!
esto significa que tenemos que convertir nuestro offset en un short para poder añadirlo a la dirección de los datos de la animación.
esto significa que tenemos que convertir nuestro desfase en un corto para poder añadirlo a la dirección de los datos de la animación.
una forma de hacerlo es con esta macro que añade un 00 antes del elemento superior de la pila:
@ -768,30 +764,30 @@ nuestro código quedaría de la siguiente manera
```
.cuentaftg LDZ ( cargar cuentafotograma )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el desplazamiento )
#08 MUL ( multiplicar por 8 para obtener el desfase )
A-CORTO ( convertir a corto )
```
otra forma, menos clara pero bastante divertida (y algo más corta en memoria de programa), consistiría en pulsar el 00 antes de que ocurra cualquier otra cosa:
otra forma, menos clara pero bastante divertida (y algo más corta en memoria de programa), consistiría en empujar el 00 antes de que ocurra cualquier otra cosa:
```
#00 ( empujar el byte alto del offset )
#00 ( empujar el byte alto del desfase )
.cuentaftg LDZ ( cargar cuentafotograma )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el offset )
#08 MUL ( multiplicar por 8 para obtener el desfase )
```
### añadiendo el offset
### añadiendo el desfase
añadir este offset a la dirección de nuestra animación es comparativamente sencillo:
añadir este desfase a la dirección de nuestra animación es comparativamente sencillo:
```
.cuentaftg LDZ ( cargar cuentafotogramas )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el desplazamiento )
#08 MUL ( multiplicar por 8 para obtener el desfase )
A-CORTO ( convertir a corto )
;animacion ( obtener la dirección de la animación )
ADD2 ( añadir el desplazamiento a la dirección )
ADD2 ( añadir el desfase a la dirección )
```
y entonces podríamos enviar eso al puerto Pantalla/direc:
@ -837,19 +833,19 @@ la sección "borrar el sprite" no es realmente necesaria en este caso debido a l
( init )
|0100
( set system colors )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
( fijar Pantalla/x y y a la mitad de la pantalla menos 4 )
( fijar Pantalla/x e `y` a la mitad de la pantalla, menos 4 )
.Pantalla/ancho DEI2 MITAD2 #0004 SUB2 .Pantalla/x DEO2
.Pantalla/alto DEI2 MITAD2 #0004 SUB2 .Pantalla/y DEO2
( establecer la dirección del sprite )
;animación .Pantalla/direc DEO2
;animacion .Pantalla/direc DEO2
( establecer vector de pantalla )
( establecer el vector de pantalla )
;en-cuadro .Pantalla/vector DEO2
BRK
@ -865,10 +861,10 @@ BRK
( 2: actualizar la dirección del sprite )
.cuentaftg LDZ ( cargar cuentafotograma )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el desplazamiento )
TO-SHORT ( convertir a corto )
;animation ( obtener la dirección de la animación )
ADD2 ( añadir el offset a la dirección )
#08 MUL ( multiplicar por 8 para obtener el desfase )
A-CORTO ( convertir a corto )
;animacion ( obtener la dirección de la animación )
ADD2 ( añadir el desfase a la dirección )
.Pantalla/direc DEO2 ( establecer la dirección calculada )
( dibujar sprite en el primer plano con el color 2 y 3 )
@ -877,25 +873,25 @@ BRK
( datos del sprite )
@animacion
&fotograma0 00 00 00 00 01 03
&fotograma1 00 00 00 01 03 06 0c
&fotograma0 00 00 00 00 00 00 01 03
&fotograma1 00 00 00 00 01 03 06 0c
&fotograma2 00 00 01 03 06 0c 18 30
&fotograma3 01 03 06 0c 18 30 60 c0
&fotograma4 03 06 0c 18 30 60 c0 80
&fotograma5 0c 18 30 60 c0 80 00 00
&fotograma6 30 60 c0 80 00 00 00
&fotograma7 c0 80 00 00 00 00 00
&fotograma6 30 60 c0 80 00 00 00 00
&fotograma7 c0 80 00 00 00 00 00 00
```
no era tan complicado, ¿verdad? :) este ejemplo incluye muchos conceptos que merecen ser estudiados, ¡así que te invito a leerlo con atención!
para algunas posibilidades de diversión, te invito a dibujar el tile varias veces en diferentes lugares y posiblemente con diferentes modos de rotación! eso puede generar animaciones más interesantes!
para algunas posibilidad divertidas, ¡te invito a dibujar el tile varias veces en diferentes lugares y posiblemente con diferentes modos de rotación! ¡eso puede generar animaciones más interesantes!
o, mejor aún, ¡diseña y utiliza tus propios sprites!
## ¡más despacio!
hasta ahora, todo lo que hemos estado haciendo ha sucedido a 60 cuadros por segundo, ¡eso puede ser demasiado rápido para algunas aplicaciones!
hasta ahora, todo lo que hemos estado haciendo ha sucedido a 60 fotogramas por segundo, ¡eso puede ser demasiado rápido para algunas aplicaciones!
afortunadamente, podemos usar algo de aritmética simple con nuestro cuentafotograma para desacelerar sus efectos.
@ -916,10 +912,10 @@ podemos utilizar estas macros para dividir la frecuencia en nuestro código:
.cuentaftg LDZ ( cargar cuentafotograma )
CUARTO ( dividir entre 4 la frecuencia )
8MOD ( aplicar el módulo 8 para obtener la secuencia entre 0 y 7 )
#08 MUL ( multiplicar por 8 para obtener el desplazamiento )
#08 MUL ( multiplicar por 8 para obtener el desfase )
A-CORTO ( convertir a corto )
;animacion ( obtener la dirección de la animación )
ADD2 ( añadir el offset a la dirección )
ADD2 ( añadir el desfase a la dirección )
.Pantalla/direc DEO2 ( establecer la dirección calculada )
```
@ -929,11 +925,11 @@ ah, ¡mucho mejor!
## potencias de dos no
ten en cuenta que si quieres dividir la frecuencia a números que no son potencias de 2, podrías empezar a ver algunos 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.
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 desplazamiento 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 tamaño reducido 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 sólo 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!
@ -954,9 +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 de mantenimiento.
=> ./tutorial_de_uxn_día_5.gmi {tutorial de uxn 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.
¡también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos!

View File

@ -1,8 +1,8 @@
# 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 de retención.
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.
¡también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos!
# el dispositivo del ratón
@ -20,7 +20,7 @@ vemos este dispositivo definido en uxntal de la siguiente manera:
el byte de estado codifica el estado de activación/desactivación de hasta 8 botones del ratón; uno por bit.
contando de derecha a izquierda, el primer bit corresponde al primer botón del ratón, el segundo al segundo, y así sucesivamente.
contando de derecha a izquierda, el primer bit corresponde al primer botón del ratón, el segundo bit al segundo botón del ratón, y así sucesivamente.
normalmente, en un ratón de tres botones, el primer botón es el izquierdo, el segundo el del medio y el tercero el derecho.
@ -38,9 +38,9 @@ nota que al igual que el dispositivo controlador, este sistema nos permite compr
* 06 cuando se pulsan el segundo y el tercer botón
* 07 cuando se pulsan los tres botones
recuerda que podemos utilizar las máscaras AND, tal y como se introdujo en {tutorial de uxn día 3}, para aislar y evaluar por separado cualquiera de estos bits.
recuerda que podemos utilizar las máscaras AND, tal y como se introdujo en el {tutorial de uxn día 3}, para aislar y evaluar por separado cualquiera de estos bits.
## shorts de desplazamiento
## cortos de desplazamiento
el dispositivo del ratón tiene un par de cortos para indicar si el ratón se está desplazando.
@ -55,6 +55,7 @@ del mismo modo, despx indicará un desplazamiento horizontal
* 0001 cuando se desplaza hacia la derecha
* ffff cuando se desplaza hacia la izquierda
* 0000 cuando no se desplaza
dependiendo del dispositivo, los valores pueden ser mayores que 0001 o menores que ffff dependiendo de la velocidad de desplazamiento.
## vector ratón
@ -73,7 +74,7 @@ el vector del ratón se disparará en cualquiera de los siguientes eventos:
el siguiente es un ejemplo simple que ilustra el uso de los siguientes elementos:
* vector del ratón
* coordenadas x e y del ratón
* coordenadas x e `y` del ratón
* estado del ratón (pulsado o no pulsado)
combinado con un condicional y el dibujo de un sprite.
@ -92,7 +93,7 @@ dibuja nuestro cuadrado en la posición del ratón, cambiando su color cuando se
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -164,7 +165,7 @@ este es un programa que logra dibujar el puntero en la pantalla
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -200,7 +201,7 @@ 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 {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.
@ -216,7 +217,7 @@ eso es aun mas cierto si consideramos que solo estamos dibujando el puntero, y n
crear una macro para todo este código podría ser posible, pero también poco práctico debido a la cantidad de código.
¿tal vez podríamos tener un JMP a otra sección del programa, que al final tiene otro JMP para volver a la posición correspondiente en la subrutina en-raton?
¿tal vez podríamos tener un JMP a otra sección del programa, que al final tiene otro JMP para c vnm a la posición correspondiente en la subrutina en-raton?
de hecho, eso es casi lo que haremos, pero con un elemento adicional de uxn: ¡su pila de retorno!
@ -298,12 +299,12 @@ o un salto absoluto:
BRK
```
la cosa es que si queremos que ocurra algo más después de dibujar el puntero dentro de en-raton, no podemos volver atrás fácilmente.
el detalle esta en que si queremos que ocurra algo más después de dibujar el puntero dentro de en-raton, no podemos atrás fácilmente.
al final de nuestra subrutina dibuja-puntero, necesitaríamos "saltar hacia atrás" así
al final de nuestra subrutina dibuja-puntero, necesitaríamos "saltar hacia atrás" así:
```
;en-raton/volver JMP2
;en-raton/retornar JMP2
```
funcionaría, pero no es la mejor forma.
@ -338,12 +339,11 @@ y en el caso de un salto absoluto:
@en-raton ( -> )
;dibuja-puntero JSR2
( algo más aquí )
BRK
```
## volviendo
## retornando
JSR está empujando la "dirección de retorno" hacia la pila de retorno.
@ -379,11 +379,11 @@ LIT2r 0102 ADDr
ahora volvamos a nuestros saltos :)
## saltando al retorno
## saltando a retorno
como ya hemos discutido, JMP nos permitirá saltar incondicionalmente a la dirección dada en la parte superior de la pila (de trabajo).
JSR o JSR2 empujan hacia abajo en la pila de retorno la dirección absoluta de la siguiente instrucción, un short, para que eventualmente podamos retornar allí.
JSR o JSR2 empujan hacia abajo en la pila de retorno la dirección absoluta de la siguiente instrucción, un corto, para que eventualmente podamos retornar allí.
¿cómo podemos saltar incondicionalmente a esa dirección absoluta que está presente en la pila de retorno?
@ -391,19 +391,19 @@ JSR o JSR2 empujan hacia abajo en la pila de retorno la dirección absoluta de l
¡activando el modo de retorno en la instrucción JMP!
adicionalmente, como las direcciones empujadas por JSR son shorts, necesitamos activar el modo short también:
adicionalmente, como las direcciones empujadas por JSR son cortos, necesitamos activar el modo corto también:
```
JMP2r ( saltar a la dirección absoluta en la parte superior de la pila de retorno )
```
en muchos programas uxntales verás esta instrucción escrita como una macro, RTN ( volver o "return" )
en muchos programas uxntal verás esta instrucción escrita como una macro, RTN ( retornar o "return" ):
```
%RTN { JMP2r }
```
podemos terminar una subrutina usando esta macro para "volver" a la posición en el programa después del JSR correspondiente.
podemos terminar una subrutina usando esta macro para "retornar" a la posición en el programa después del JSR correspondiente.
## ejemplo completo usando subrutinas
@ -426,7 +426,7 @@ este es el programa hola-puntero.tal, pero utilizando dibuja-puntero como subrut
( init )
|0100
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -440,7 +440,7 @@ este es el programa hola-puntero.tal, pero utilizando dibuja-puntero como subrut
BRK
@en-raton ( -> )
;dibujar-puntero JSR2 ( o ,dibujar-puntero JSR )
;dibuja-puntero JSR2 ( o ,dibuja-puntero JSR )
( algo más )
BRK
@ -486,9 +486,9 @@ puede haber otros casos en los que el uso de "variables" tendría un sentido má
habiendo introducido la pila de retorno y el modo de retorno, se nos abre otro mundo de posibilidades: también podemos utilizar la pila de retorno como una pila adicional y temporal, para almacenar algunos valores mientras operamos con otros.
para lograr esto, uxn tiene una instrucción llamada STH, almacenar o "stash".
para lograr esto, uxn tiene una instrucción llamada STH, "stash" o almacenar.
¡esta es la última instrucción de uxn que teníamos que cubrir en esta serie de tutoriales! :)
¡esta es la última instrucción de uxn que teníamos en mente cubrir en este tutorial! :)
## STH
@ -502,20 +502,20 @@ y, como habrás previsto, en el modo corto esta instrucción opera moviendo cort
el siguiente es un ejemplo de subrutina que muestra algunas posibilidades de la pila y el modo de retorno.
se trata de una subrutina que dibuja una línea horizontal de una longitud dada (de 1 a 255 píxeles, es decir, utilizando un byte), partiendo de una coordenada x dada (corto) y utilizando una coordenada y dada (corto).
se trata de una subrutina que dibuja una línea horizontal de una longitud dada (de 1 a 255 píxeles, es decir, utilizando un byte), partiendo de una coordenada x dada (corto) y utilizando una coordenada `y` dada (corto).
estos parámetros se dan como argumentos en la pila de trabajo.
la subrutina utiliza la pila de retorno para "almacenar" uno de estos argumentos mientras trabaja con los otros.
además, tiene un bucle de trabajo! escrito en una de las varias formas de implementarlo :)
además, ¡tiene un bucle de trabajo! escrito en una de las varias formas de implementarlo :)
el estado de las pilas de trabajo (pt) y de retorno (pr) se muestra en los comentarios después de casi cada paso. la parte superior de las pilas se encuentra a su derecha.
un signo de intercalación (^) después de un nombre de valor indica que corresponde a un cortocircuito.
un signo de intercalación (^) después de un nombre de valor indica que corresponde a un corto.
```
@dibujar-linea-horizontal ( x^ y^ longitud -- )
@dibuja-linea-horizontal ( x^ y^ longitud -- )
( inicio )
( pt: x^ y^ longitud / pr: )
@ -558,13 +558,13 @@ RTN
### llamando
para llamar a la subrutina, podrías hacer algo como lo siguiente
para llamar a la subrutina, podrías hacer algo como lo siguiente:
```
#0008 ( empujar x inicial )
.Pantalla/altura DEI2 MITAD2 ( empujar y )
#ff ( empujar longitud de la línea )
;dibujar-linea-horizontal JSR2 ( llamar subrutina )
;dibuja-linea-horizontal JSR2 ( llamar subrutina )
```
### notas
@ -578,13 +578,13 @@ sin embargo, muestra cómo podemos usar estas instrucciones para tener una pila
* hacer que la subrutina dibuje una línea hecha de sprites en lugar de píxeles individuales
* modificar la subrutina para que pueda recibir como argumento (en la pila de trabajo) el color de los sprites o píxeles
* modificar la subrutina para que pueda recibir como argumento (en la pila de trabajo) la dirección del sprite a dibujar
* reescribir la subrutina para que utilice una longitud de corto para la línea, en lugar de un byte.
* reescribir la subrutina para que utilice una longitud de pequeño tamaño para la línea, en lugar de un byte.
# el modo mantener
el último elemento básico de uxntal que nos queda por cubrir es su tercer modo para las instrucciones: el modo mantener o "keep".
el modo mantener se codifica en el 8º bit de un byte de instrucción, contando de derecha a izquierda.
el modo mantener se codifica en el 8vo bit de un byte de instrucción, contando de derecha a izquierda.
en uxntal, indicamos que queremos activar esta bandera añadiendo la letra 'k' al final de un mnemónico de instrucción.
@ -627,7 +627,7 @@ dije entonces que había un conjunto más optimizado, y que lo discutiríamos m
¡ahora es ese momento!
en primer lugar, analicemos lo que ocurre con MOD. está calculando lo que se escribiría en notación infija de la siguiente manera, suponiendo que la barra (/) indica una división entera
en primer lugar, analicemos lo que ocurre con MOD. está calculando lo que se escribiría en notación infija de la siguiente manera, suponiendo que la barra (/) indica una división entera:
```
a - ( a/b )*b
@ -643,10 +643,10 @@ en nuestra macro original, lo que ocurre es lo siguiente:
```
#07 #03 ( pt: 07 03 )
DUP2 ( pt: 07 03 07 03 )
DIV ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
DUP2 ( pt: 07 03 07 03 )
DIV ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
```
¿ves la posibilidad de introducir el modo mantener?
@ -661,9 +661,9 @@ DUP2 DIV es equivalente a... ¡DIVk! ¡una división que no pierde sus operandos
```
#07 #03 ( pt: 07 03 )
DIVk ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
DIVk ( pt: 07 03 02 )
MUL ( pt: 07 06 )
SUB ( pt: 01 )
```
¡de esta manera, nuestra macro puede tener un byte menos!
@ -679,7 +679,7 @@ podemos generalizar este comportamiento para el modo corto, y obtener el conjunt
el modo mantener puede ser útil cuando hacemos comparaciones y no queremos perder los valores originales.
por ejemplo, en nuestra subrutina dibujar-línea-horizontal, teníamos el siguiente conjunto de líneas de código:
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 )
@ -754,7 +754,7 @@ ten en cuenta que puedes cambiar el vector de un dispositivo durante el tiempo d
básicamente, las puertas para las aplicaciones visuales interactivas en el ordenador varvara están completamente abiertas ahora para ti :)
¿crearás juegos? ¿pequeñas aplicaciones, útiles o no? ¿un instrumento para visuales en vivo? ¿programas dirigidos a dispositivos manuales específicos?
¿crearás juegos? ¿pequeñas aplicaciones, útiles o no? ¿un instrumento para visuales en vivo? ¿programas dirigidos a dispositivos de mano específicos?
algunas cosas pueden parecer difíciles de construir, pero afortunadamente, por ahora no hay nada más en el funcionamiento de la máquina que no hayamos cubierto ya.
@ -764,7 +764,7 @@ puedes ir poco a poco, paso a paso, practicando tu manejo de la pila y ejercitan
# instrucciones del día 5
estas son las instrucciones uxntales que hemos discutido hoy! con estas, las hemos cubierto todas!
¡estas son las instrucciones uxntal que hemos discutido hoy! ¡con estas, las hemos cubierto todas!
* JSR: salta incondicionalmente a la dirección de la pila de trabajo, empujando hacia abajo en la pila de retorno la dirección de la siguiente instrucción en memoria
* STH: toma un valor de la pila de trabajo y lo empuja hacia abajo en la pila de retorno. en el modo de retorno, hace lo contrario.
@ -779,8 +779,8 @@ además de utilizar estrategias y fragmentos de código anteriores, cubrimos est
primero, te invito a tomar un descanso!
después, sigue explorando y comparte tus descubrimientos!
después, ¡sigue explorando y comparte tus descubrimientos!
# apoyo
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)
si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :)

View File

@ -22,7 +22,7 @@ en el {tutorial de uxn día 5} hablamos de una forma de crear un bucle para repe
aquí ampliaremos ese procedimiento para que también se repita verticalmente en toda la pantalla.
## configuración
## configurando
empecemos con el siguiente programa como plantilla. incluye los datos para un sprite de 1bpp que consiste en líneas diagonales.
@ -107,7 +107,7 @@ integrando todo ello, podríamos obtener:
nótese el uso de DUP2 para evitar releer el valor de x.
esto debería funcionar ahora! pero vamos a discutir una forma más agradable de hacerlo :)
¡esto debería funcionar ahora! pero vamos a discutir una forma más agradable de hacerlo :)
### una segunda versión, usando la pila
@ -116,7 +116,7 @@ en lugar de leer el ancho de pantalla y la coordenada x cada vez, podríamos usa
después de establecer la dirección del tile, podemos empujar nuestro límite (el ancho de la pantalla) y el valor inicial hacia abajo en la pila:
```
.Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial )
.Pantalla/ancho DEI2 #0000 ( establecer límite y `x` inicial )
```
usaremos ese valor en la parte superior de la pila como coordenada x.
@ -141,14 +141,14 @@ GTH2k ( ¿es ese ancho mayor que x?, o también, ¿es x menor que el ancho? )
,&bucle-x JCN ( salta si x es menor que el límite )
```
cuando terminamos el bucle, tenemos que hacer POP de ambos valores.
cuando terminamos el bucle, tenemos que hacer POP a ambos valores.
usando esta estrategia, obtendríamos el siguiente bucle:
```
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
.Pantalla/ancho DEI2 #0000 ( empujar límite y x inicial )
.Pantalla/ancho DEI2 #0000 ( empujar límite y `x` inicial )
&bucle-x
DUP2 .Pantalla/x DEO2 ( establecer coordenada x )
@ -192,7 +192,7 @@ lo siguiente muestra nuestro programa en contexto, llenando completamente la pri
( dibujar fondo )
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
.Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial )
.Pantalla/ancho DEI2 #0000 ( establecer límite y `x` inicial )
&bucle-x
DUP2 .Pantalla/x DEO2 ( fijar coordenada x )
#03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 )
@ -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,9 +223,9 @@ 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:
podemos usar la misma estructura que antes, pero usando `y`:
```
;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile )
@ -238,8 +238,8 @@ MARGEN-PARED ( establecer `y` inicial )
( - dibujar fila aquí - )
#0008 ADD2 ( )
GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? )
#0008 ADD2 ( incrementa `y` )
GTH2k ( ¿es ese límite mayor que `y`? o también, ¿es `y` menor que el límite? )
,&bucle-y JCN ( salta si `y` es menor que el límite )
POP2 POP2 ( eliminar `y` y el límite )
```
@ -257,7 +257,7 @@ MARGEN-PARED ( establecer `y` inicial )
DUP2 .Pantalla/y DEO2 ( establecer coordenada y )
( dibujar fila )
.Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial )
.Pantalla/ancho DEI2 #0000 ( establecer límite y `x` inicial )
&bucle-x
DUP2 .Pantalla/x DEO2 ( fijar coordenada x )
@ -269,7 +269,7 @@ MARGEN-PARED ( establecer `y` inicial )
,&bucle-x JCN ( salta si x es menor que el límite )
POP2 POP2 ( eliminar x y el límite )
#0008 ADD2 ( )
#0008 ADD2 ( incrementar y )
GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? )
,&bucle-y JCN ( salta si `y` es menor que el límite )
POP2 POP2 ( eliminar `y` y el límite )
@ -291,7 +291,7 @@ ahora podemos envolver estos bucles anidados dentro de una subrutina:
DUP2 .Pantalla/y DEO2 ( establecer coordenada y )
( dibujar fila )
.Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial )
.Pantalla/ancho DEI2 #0000 ( establecer límite y `x` inicial )
&bucle-x
DUP2 .Pantalla/x DEO2 ( establecer coordenada x )
@ -303,20 +303,20 @@ ahora podemos envolver estos bucles anidados dentro de una subrutina:
,&bucle-x JCN ( salta si x es menor que el límite )
POP2 POP2 ( eliminar x y el límite )
#0008 ADD2 ( )
GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? )
#0008 ADD2 ( incrementar y )
GTH2k ( ¿es ese límite mayor que `y`? o también, ¿es `y` menor que el límite? )
,&bucle-y JCN ( salta si `y` es menor que el límite )
POP2 POP2 ( eliminar `y` y el límite )
RTN
```
que podemos llamar simplemente desde nuestra subrutina de inicialización:
que podemos llamar simplemente desde nuestra subrutina de iniciación:
```
;dibuja-fondo JSR2
```
=> ./img/screenshot_uxn-background-full.png captura de pantalla que muestra la pantalla de varvara cubierta de líneas diagonales excepto por un margen en la parte superior e inferior.
=> ./img/screenshot_uxn-background-full.png captura de pantalla que muestra la pantalla varvara cubierta de líneas diagonales excepto por un margen en la parte superior e inferior.
¡lindo!
@ -326,11 +326,11 @@ se habla de varias posibilidades para usar uxntal de esa manera abstracta: yo di
# 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 como dibujar las palas en base a estos parámetros, y también recapitularemos como 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
@ -352,12 +352,12 @@ los datos resultantes son los siguientes:
```
@pala
&tile0 [ 3f 7f e7 c3 c3 c3 c3 00 00 18 3c 3c 3c 3c 3c ]
&tile1 [ fc fe ff ff ff ff ff 00 00 00 00 06 06 ]
&tile2 [ c3 c3 c3 e7 ff ff ff 3c 3c 3c 18 00 00 00 ]
&tile3 [ ff ff ff ff ff ff 06 06 06 06 06 06 ]
&tile4 [ ff ff ff ff ff 7f 3f 00 00 00 00 00 00 ]
&tile5 [ ff ff ff ff ff fe fc 06 06 06 06 1e 3c 00 ]
&tile0 [ 3f 7f e7 c3 c3 c3 c3 00 00 18 3c 3c 3c 3c 3c ]
&tile1 [ fc fe ff ff ff ff ff 00 00 00 00 00 00 06 06 ]
&tile2 [ c3 c3 c3 e7 ff ff ff 3c 3c 3c 3c 18 00 00 00 ]
&tile3 [ ff ff ff ff ff ff 06 06 06 06 06 06 06 06 06 ]
&tile4 [ ff ff ff ff ff 7f 3f 00 00 00 00 00 00 00 00 ]
&tile5 [ ff ff ff ff ff fe fc 06 06 06 06 06 1e 3c 00 ]
```
se pueden obtener estos números leyendo la notación hexadecimal en nasu en la parte superior derecha, primero la columna de la izquierda y luego la de la derecha, o utilizando una herramienta como hexdump con el archivo chr correspondiente:
@ -372,7 +372,7 @@ he dibujado el sprite usando el modo de mezcla 85 como indica nasu, pero lo camb
construyamos una subrutina que dibuje las 6 fichas de la pala en el orden correspondiente.
podríamos tener la subrutina recibiendo como argumentos la posición x e y de su esquina superior izquierda:
podríamos escribir la subrutina recibiendo como argumentos la posición x e `y` de su esquina superior izquierda:
```
@dibuja-pala ( x^ y^ -- )
@ -386,9 +386,9 @@ pero añadamos también un byte de color para el byte del sprite:
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á limpiar la pala antes de moverla, como hemos hecho en días anteriores.
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 sencilla: 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,15 +404,15 @@ 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 en 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 auto byte de 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, y/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.
@ -486,13 +486,13 @@ es posible considerar formas más eficientes de dibujarla. por ejemplo, podríam
@dibuja-sprite ( x^ y^ ancho alto direc* color )
```
crear eso podría ser un buen ejercicio para probar! en este caso me quedaré con el método manual.
crear eso podría ser un buen ejercicio para probar! en este caso me mantendré con el método manual.
lo bueno de que este proceso esté en una subrutina es que podemos "olvidarnos" de su funcionamiento interno y simplemente usarlo :)
## variables y constantes para las palas
reservemos un espacio en la página cero para las coordenadas x e y de cada pala.
reservemos un espacio en la página cero para las coordenadas x e `y` de cada pala.
```
( página cero )
@ -653,14 +653,14 @@ para borrarlos, podemos hacer lo mismo pero usando un byte del sprite correspond
donde COLOR-BORRAR en este caso sería:
```
%COLOR-BORRAR { #40 } ( limpiar el sprite del primer plano )
%COLOR-BORRAR { #40 } ( borrar el sprite del primer plano )
```
¡este es un buen recordatorio para revisar las tablas de los bytes de los sprites en el {tutorial de uxn día 2}!
### actualizar posición
para actualizar la posición de nuestras palas, podemos recurrir al ejemplo hola-sprite-enmovimiento.tal del {tutorial de uxn día 4}.
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.
@ -713,7 +713,7 @@ RTN
### procedimiento completo
integrando todo, nuestra subrutina en el marco se vería como lo siguiente.
integrando todo, nuestra subrutina en-cuadro se vería como lo siguiente.
¡ahora somos capaces de mover nuestras palas!
@ -740,7 +740,7 @@ te invito a que modifiques la subrutina actualiza-palas para que haya un límite
¡ahora vamos a poner la pelota en marcha!
aquí trabajaremos de nuevo con un sprite multi-tile dibujado en relación a las variables x e y para su esquina superior izquierda.
aquí trabajaremos de nuevo con un sprite multi-tile dibujado en relación a las variables x e `y` para su esquina superior izquierda.
adicionalmente, usaremos esta sección para hablar de las estrategias para la detección de colisiones, con las paredes y las palas.
@ -757,10 +757,10 @@ estos son sus datos:
```
@pelota-sprite
&tile0 [ 03 0f 1f 39 70 70 f9 ff 00 00 00 06 0f 0f 06 00 ]
&tile1 [ c0 f0 f8 fc fe ff ff 00 00 00 08 0c 06 06 ]
&tile2 [ ff ff 7f 7f 3f 1f 0f 03 00 00 00 00 18 0f 01 00 ]
&tile3 [ ff ff fe fc f8 f0 c0 06 06 0c 1c 38 f0 c0 00 ]
&tile0 [ 03 0f 1f 39 70 70 f9 ff 00 00 00 06 0f 0f 06 00 ]
&tile1 [ c0 f0 f8 fc fe fe ff ff 00 00 00 00 08 0c 06 06 ]
&tile2 [ ff ff 7f 7f 3f 1f 0f 03 00 00 00 00 18 0f 01 00 ]
&tile3 [ ff ff fe fe fc f8 f0 c0 06 06 0c 1c 38 f0 c0 00 ]
```
podemos definir un par de macros para referirse a sus parámetros:
@ -796,13 +796,13 @@ MITAD2
las coordenadas están listas, así que ahora podemos usarlas dentro de nuestra subrutina.
hagamos que la subrutina reciba el color como argumento, para poder limpiar la pelota como hacemos con las palas:
hagamos que la subrutina reciba el color como argumento, para poder borrar la pelota como hacemos con las palas:
```
@dibuja-pelota ( color -- )
( fijar x e y iniciales )
( fijar x e `y` iniciales )
.pelota/x LDZ2 .Pantalla/x DEO2
.pelota/y LDZ2 . pantalla/y DEO2
.pelota/y LDZ2 .Pantalla/y DEO2
( dibujar tile 0 )
;pelota-sprite/tile0 .Pantalla/direc DEO2
@ -845,14 +845,14 @@ COLOR-PELOTA ;dibuja-pelota JSR2
para el movimiento de la pelota, seguiremos la misma estructura que antes:
* despejar la pelota en la posición actual
* borrar la pelota en la posición actual
* actualizar su posición
* dibujar la pelota en la nueva posición
se vería algo como lo siguiente, y podría sentarse 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-marco )
( dentro de en-cuadro )
( borrar pelota )
COLOR-BORRAR ;dibuja-pelota JSR2
@ -869,13 +869,13 @@ ahora vamos a discutir cómo construir esa subrutina actualiza-pelota :)
además de nuestras variables para llevar la cuenta de la posición de la pelota, deberíamos poder llevar la cuenta de la dirección por-eje en la que se mueve.
un enfoque podría ser tener una bandera para cada x e y que indique si debemos incrementarlos o disminuirlos.
un enfoque podría ser tener una bandera para cada x e `y` que indique si debemos incrementarlos o decrementarlos.
otro enfoque podría ser tener una variable de velocidad para cada x e y, que se cambia de acuerdo a la dirección que queremos que la pelota vaya.
otro enfoque podría ser tener una variable de velocidad para cada x e `y`, que se cambia de acuerdo a la dirección que queremos que la pelota vaya.
utilizaremos este último enfoque con la velocidad, ya que nos ayudará a discutir algunas ventajas de la aritmética de enteros sin signo.
utilizaremos este último enfoque de la variable velocidad, ya que nos ayudará a discutir algunas ventajas de la aritmética de enteros sin signo.
incluimos estas variables en nuestra página cero, complementando las x e y que ya teníamos:
incluimos estas variables en nuestra página cero, complementando las x e `y` que ya teníamos:
```
@pelota [ &x $2 &y $2 &vel-x $2 &vel-y $2 ]
@ -883,7 +883,7 @@ incluimos estas variables en nuestra página cero, complementando las x e y que
### diferentes direcciones
si, por ejemplo, iniciamos vel-x con 1
si, por ejemplo, iniciamos vel-x con 1:
```
#0001 .pelota/vel-x STZ2
@ -895,7 +895,7 @@ podemos hacer que la pelota se mueva hacia la derecha haciendo:
( dentro de actualiza-pelota )
.pelota/vel-x LDZ2 ( obtener vel-x )
.pelota/x LDZ2 ( obtener x )
ADD2 ( sumar ambas cosas )
ADD2 ( sumar ambas )
.pelota/x STZ2 ( almacenar nueva x )
```
@ -952,11 +952,11 @@ para obtener otros "números negativos", observemos lo siguiente: si restamos 1
1 0000 0000 0000 0000: 0000
```
obtenemos 0! fffe funciona efectivamente como "-2"!
¡obtenemos 0! ¡fffe funciona efectivamente como "-2"!
podríamos continuar así obteniendo más y más números "negativos" que funcionan gracias al tamaño restringido de la memoria del ordenador.
volviendo a nuestro código, si inicializamos nuestra velocidad con:
volviendo a nuestro código, si iniciamos nuestra velocidad con:
```
#ffff .pelota/vel-x STZ2
@ -968,7 +968,7 @@ y luego usamos exactamente el mismo código para actualizar la posición:
( dentro de actualiza-pelota )
.pelota/vel-x LDZ2 ( obtener vel-x )
.pelota/x LDZ2 ( obtener x )
ADD2 ( sumar ambas cosas )
ADD2 ( sumar ambas )
.pelota/x STZ2 ( almacenar nueva x )
```
@ -983,18 +983,18 @@ podría tener sentido establecer estos valores como macros:
### implementando el movimiento de la pelota
basándonos en lo que acabamos de discutir, podemos empezar nuestra subrutina de actualización de la pelota con lo siguiente:
basándonos en lo que acabamos de discutir, podemos empezar nuestra subrutina actualiza-pelota con lo siguiente:
```
@actualiza-pelota ( -- )
( obtener velocidad-x y x )
( obtener velocidad-x y `x` )
.pelota/vel-x LDZ2 .pelota/x LDZ2
ADD2 ( sumar ambas cosas )
.pelota/x STZ2 ( guarda la nueva x )
( obtener velocidad-y e y )
( obtener velocidad-y e `y` )
.pelota/vel-y LDZ2 .pelota/y LDZ2
ADD2 ( sumar ambas cosas )
ADD2 ( sumar ambas )
.pelota/y STZ2 ( almacenar nueva y )
RTN
```
@ -1018,13 +1018,13 @@ woohoo! se mueve, pero de momento sale volando :)
## colisiones con las paredes
hemos definido la forma general de actualizar la posición de la pelota dada su velocidad en x e y.
hemos definido la forma general de actualizar la posición de la pelota dada su velocidad en x e `y`.
¡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.
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 sólo la coordenada `y` de la pelota.
como siempre, hay muchas maneras de lograr esto. una podría ser:
@ -1046,7 +1046,7 @@ pista: mira de nuevo las máscaras a nivel de bit discutidas en el {tutorial de
### pared superior
si la pelota golpea la pared superior, significa que su coordenada y es menor que la coordenada y de la pared.
si la pelota golpea la pared superior, significa que su coordenada `y` es menor que la coordenada `y` de la pared.
considerando que hay un margen en la parte superior, podemos hacer esta comprobación de la siguiente manera:
@ -1069,12 +1069,12 @@ considerando que hay un margen en la parte superior, podemos hacer esta comproba
aquí el procedimiento sería similar, pero considerando el tamaño de la pelota.
queremos saber si la coordenada y, más el tamaño de la pelota, es mayor que la coordenada y de la pared inferior.
queremos saber si la coordenada `y`, más el tamaño de la pelota, es mayor que la coordenada `y` de la pared inferior.
la coordenada y de la pared inferior sería la altura de la pantalla, menos el margen de la pared:
la coordenada `y` de la pared inferior sería la altura de la pantalla, menos el margen de la pared:
```
(actualización del interior de la pelota )
(dentro de actualiza-pelota )
&verif-pared-inf
.pelota/y LDZ2 TAM-PELOTA ADD2 ( y + tamaño de la pelota )
.Pantalla/alto DEI2
@ -1088,9 +1088,9 @@ la coordenada y de la pared inferior sería la altura de la pantalla, menos el m
&continuar
```
### código de actualización de la pelota hasta ahora
### actualiza-pelota hasta ahora
nuestra subrutina de actualización de la pelota tiene el siguiente aspecto ahora mismo:
nuestra subrutina actualiza-pelota tiene el siguiente aspecto hasta el momento:
```
@actualiza-pelota ( -- )
@ -1101,7 +1101,7 @@ nuestra subrutina de actualización de la pelota tiene el siguiente aspecto ahor
.pelota/x STZ2 ( guardar la nueva x )
( actualizar y )
( obtener velocidad-y e y )
( obtener velocidad-y e `y` )
.pelota/vel-y LDZ2 .pelota/y LDZ2
ADD2 ( sumar ambas cosas )
.pelota/y STZ2 ( almacenar nueva y )
@ -1159,12 +1159,12 @@ para ello, podemos comprobar si x es menor que la suma del margen y el ancho de
&verif-pala-der
```
una vez que sabemos que eso es cierto, podemos ver si la pelota está dentro del alcance vertical de la pala; la coordenada y de la pelota tiene que estar dentro de un cierto rango relativo a la coordenada y de la pelota.
una vez que sabemos que eso es cierto, podemos ver si la pelota está dentro del alcance vertical de la pala; la coordenada `y` de la pelota tiene que estar dentro de un cierto rango relativo a la coordenada `y` de la pelota.
en especifico, si queremos que la pelota pueda rebotar cuando cualquier parte de la pelota golpee cualquier parte de la pala, la coordenada y de la pelota tiene que ser:
en especifico, si queremos que la pelota pueda rebotar cuando cualquier parte de la pelota golpee cualquier parte de la pala, la coordenada `y` de la pelota tiene que ser:
* mayor que la coordenada y de la pala menos la altura AND de la pelota
* menor que la coordenada y de la pala más la altura de la pala
* mayor que la coordenada `y` de la pala menos la altura AND de la pelota
* menor que la coordenada `y` de la pala más la altura de la pala
si esas dos condiciones se cumplen, entonces podemos establecer una velocidad positiva para x:
@ -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 que 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.
@ -1237,7 +1237,8 @@ para la pala derecha haremos lo mismo que arriba, pero cambiando las comparacion
&verif-pala-der
.pelota/x LDZ2 TAM-PELOTA ADD2 ( pelota-x + tamaño-pelota )
.Pantalla/ancho DEI2 MARGEN SUB2 ANCHO-PALA SUB2
GTH2 ( ¿es la coordenada derecha de la pelota mayor que el ancho de la pantalla - margen - ancho de la pala? )
( ¿es la coordenada derecha de la pelota mayor que el ancho de la pantalla - margen - ancho-pala? )
GTH2
,&x-en-derecha JCN
&fin JMP
@ -1267,7 +1268,7 @@ para la pala derecha haremos lo mismo que arriba, pero cambiando las comparacion
RTN
```
¡eso debería ser todo! ¡puedes encontrar la subrutina de actualización de la pelota completa a continuación!
¡eso debería ser todo! ¡puedes encontrar la subrutina actualiza-pelota completa a continuación!
¡para poder ensamblar y ejecutar el juego, vamos a definir la subrutina reset!
@ -1285,7 +1286,7 @@ aquí solo definiremos una subrutina de reinicio o "reset" que devuelva la pelot
RTN
```
sería interesante tener algún mecanismo para cambiar también la velocidad: tal vez basado en el conteo de cuadros, en la posición de las palas, o cualquier otra cosa que elijas.
sería interesante tener algún mecanismo para también cambiar la velocidad: tal vez basado en el cuentafotogramas, en la posición de las palas, o cualquier otra cosa que elijas.
# el programa completo
@ -1331,7 +1332,7 @@ aquí está todo el código que hemos escrito hoy:
( configuración )
|0100
@configuracion ( -> )
( establecer colores del sistema )
( establecer los colores del sistema )
#2ce9 .Sistema/r DEO2
#01c0 .Sistema/g DEO2
#2ce5 .Sistema/b DEO2
@ -1340,7 +1341,7 @@ aquí está todo el código que hemos escrito hoy:
;en-cuadro .Pantalla/vector DEO2
( dibujar fondo )
;dibujar fondo JSR2
;dibuja-fondo JSR2
( iniciar palas )
MARGEN .izquierda/x STZ2
@ -1363,10 +1364,10 @@ aquí está todo el código que hemos escrito hoy:
BRK
```
## en-marco
## en-cuadro
```
@en-marco ( -> )
@en-cuadro ( -> )
( borrar palas )
.izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2
.derecha/x LDZ2 .derecha/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2
@ -1389,7 +1390,6 @@ BRK
BRK
```
## reset
```
@ -1413,7 +1413,7 @@ RTN
ADD2 ( sumar ambas cosas )
.pelota/x STZ2 ( guardar la nueva x )
( obtener velocidad-y e y )
( obtener velocidad-y e `y` )
.pelota/vel-y LDZ2 .pelota/y LDZ2 ( obtener y )
ADD2 ( sumar ambas cosas )
.pelota/y STZ2 ( guardar la nueva y )
@ -1428,7 +1428,7 @@ RTN
&establecer-vel-pos
PELOTA-VEL-POS .pelota/vel-y STZ2
&continuar JMP
,&continuar JMP
&verif-pared-inf
.pelota/y LDZ2 TAM-PELOTA ADD2 ( y + tamaño-pelota )
@ -1472,9 +1472,10 @@ RTN
&verif-pala-der
.pelota/x LDZ2 TAM-PELOTA ADD2
.Pantalla/ancho DEI2 MARGEN SUB2 ANCHO-PALA SUB2
GTH2 ( ¿es pelota-x + tamaño-pelota mayor que la anchura de la pantalla - margen - ancho-pala? )
( ¿es pelota-x + tamaño-pelota mayor que la anchura de la pantalla - margen - ancho-pala? )
GTH2
,&x-en-derecha JCN
&fin JMP
,&fin JMP
&x-en-derecha
.pelota/y LDZ2 DUP2
@ -1505,7 +1506,7 @@ RTN
```
@dibuja-pelota ( color -- )
( fijar x e y iniciales )
( fijar x e `y` iniciales )
.pelota/x LDZ2 .Pantalla/x DEO2
.pelota/y LDZ2 .Pantalla/y DEO2
@ -1549,9 +1550,9 @@ RTN
DUP #10 AND ( comprobar bit para arriba )
,&izquierda-arriba JCN
DUP #20 AND ( comprobar bit para abajo )
&izquierda-abajo JCN
,&izquierda-abajo JCN
&derecha JMP ( salta si no se ha pulsado ninguno de los dos )
,&derecha JMP ( salta si no se ha pulsado ninguno de los dos )
&izquierda-arriba
.izquierda/y LDZ2 VEL-PALA SUB2 .izquierda/y STZ2
@ -1565,9 +1566,9 @@ RTN
DUP #01 AND ( comprobar bit para A )
,&derecha-arriba JCN
DUP #02 AND ( comprobar bit para B )
&derecha-abajo JCN
,&derecha-abajo JCN
&fin JMP ( salta si no se ha pulsado ninguno de los dos )
,&fin JMP ( salta si no se ha pulsado ninguno de los dos )
&derecha-arriba
.derecha/y LDZ2 VEL-PALA SUB2 .derecha/y STZ2
@ -1646,14 +1647,14 @@ RTN
DUP2 .Pantalla/y DEO2 ( establecer coordenada `y` )
( dibujar fila )
.Pantalla/ancho DEI2 #0000 ( establecer límite `y` x inicial )
.Pantalla/ancho DEI2 #0000 ( establecer límite y `x` inicial )
&bucle-x
DUP2 .Pantalla/x DEO2 ( fijar coordenada x )
#03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 )
#0008 ADD2 ( incrementar x )
GTH2k ( ¿es la anchura mayor que x? o también, ¿es x menor que la anchura? )
,&bucle-x JCN ( salta si x es menor que el límite )
POP2 POP2 ( eliminar x y el límite )
POP2 POP2 ( eliminar `x` y el límite )
#0008 ADD2 ( incrementar y )
GTH2k ( ¿es el límite mayor que `y`? o también, ¿es `y` menor que el límite? )
@ -1694,7 +1695,7 @@ aquí hay algunas posibilidades extra para que practiques y trates de implementa
* 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
* etc!
* ¡etc!
¡comparte lo que termines creando en base a todo esto! :)

View File

@ -20,7 +20,7 @@ sus puertos se definen normalmente de la siguiente manera:
* el corto éxito almacena la longitud de los datos que se han leído o escrito con éxito, o cero si ha habido un error
* el corto nombre es para la dirección de memoria donde se almacena el nombre del archivo (terminado en cero, es decir, con un 00)
* el corto largo es la cantidad de bytes a leer o escribir: ¡no olvidemos que la memoria del programa es ffff más 1 byte de largo, y que el programa mismo se almacena allí!
* el short leer es para la dirección de memoria inicial donde los datos de lectura deben ser almacenados
* el corto 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.
@ -28,31 +28,30 @@ sus puertos se definen normalmente de la siguiente manera:
una operación de lectura se inicia cuando se escribe en el corto `leer`, y una operación de escritura se inicia cuando se escribe en el corto `escribir`.
¡estos pueden parecer muchos campos para manejar, pero veremos que no son demasiado problema!
¡estos pueden parecer muchos detalles para manejar, pero veremos que no son demasiado problema!
## leer un archivo
para leer un archivo, necesitamos saber lo siguiente:
* la ruta del archivo, escrita como una cadena de texto etiquetada en la memoria del programa y terminada por un 00 - esta ruta sería relativa a la ubicación donde se ejecuta uxnemu.
* la cantidad de bytes que queremos leer del archivo: no pasa nada si este número no es igual al tamaño del archivo; puede ser menor o incluso mayor.
* la etiqueta para una sección reservada de la memoria del programa donde se almacenarán los datos leídos
¡y eso es todo!
podemos usar una estructura como la siguiente, donde el nombre del archivo y la memoria reservada están bajo una etiqueta, y la subrutina de carga del archivo bajo otra:
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 ( -- )
;archivo/nombre .Archivo/nombre DEO2 ( dirección de la ruta del archivo )
#00ff .Archivo/largo DEO2 ( intentará leer 255 bytes )
( establecer la dirección de los datos a leer y hacer la lectura )
( establecer la dirección de los datos a leer y hacer lectura )
;archivo/datos .Archivo/lectura DEO2
( comprobar el byte éxito y saltar según corresponda )
Archivo/exito DEI2 #0000 EQU2 ,&failed JCN
Archivo/exito DEI2 #0000 EQU2 ,&fallo JCN
&exito
LIT 'Y .Consola/escribir DEO
@ -69,9 +68,9 @@ RTN
nótese que para el nombre del archivo estamos usando la runa de cadena cruda o `raw` (") que nos permite escribir varios caracteres en la memoria del programa hasta encontrar un espacio en blanco.
en este ejemplo estamos escribiendo un carácter en la consola en función de que el corto `exito` sea cero o no, pero podríamos decidir realizar cualquier acción que consideremos oportuna.
en este ejemplo estamos escribiendo un carácter en la consola en función de que el corto `exito` sea cero o no, pero podríamos decidir realizar cualquier acción que consideremos apropiada.
además, en este ejemplo no nos preocupa realmente cuántos bytes se han leído realmente: ¡tenga en cuenta que esta información se almacena en `Archivo/exito` hasta que se produzca otra lectura o escritura!
además, en este ejemplo no nos preocupa realmente cuántos bytes se han leído realmente: ¡tenga en cuenta que esta información se almacena en Archivo/exito hasta que se produzca otra lectura o escritura!
es importante recordar que, como siempre en este contexto, estamos tratando con bytes crudos.
@ -115,7 +114,7 @@ RTN
¡observe lo similar que es a la subrutina cargar-archivo!
las únicas diferencias, además del uso de `Archivo/escribir` en lugar de `Archivo/leer`, son la longitud del archivo y la comparación para el corto éxito: en este caso sabemos con seguridad cuántos bytes deberían haberse escrito.
las únicas diferencias, además del uso de Archivo/escribir en lugar de Archivo/leer, son la longitud del archivo y la comparación para el corto éxito: en este caso sabemos con seguridad cuántos bytes deberían haberse escrito.
## un breve estudio de caso: el archivo de temas
@ -136,7 +135,7 @@ podríamos adaptar nuestra subrutina anterior para cargar el archivo de temas y
;tema/nombre .Archivo/nombre DEO2 ( establecer la dirección de la ruta del archivo )
#0006 .Archivo/largo DEO2 ( intentará leer 6 bytes )
( establecer la dirección de los datos a leer y hacer la lectura )
( establecer la dirección de los datos a leer y hacer lectura )
;tema/datos .Archivo/cargar DEO2
( comprobar el byte éxito y saltar según corresponda )
@ -192,9 +191,9 @@ RTN
=> https://git.sr.ht/~rabbits/nasu/tree/main/item/src/main.tal código fuente de nasu
# el dispositivo fechahora o "datetime"
# el dispositivo fechahora
el dispositivo fechahora 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 y/o para las visualizaciones del tiempo.
tiene varios campos que podemos leer, todos ellos basados en la hora del sistema actual y la zona horaria:
@ -205,10 +204,10 @@ tiene varios campos que podemos leer, todos ellos basados en la hora del sistema
* el byte año corresponde al número de año de la llamada era común
* el byte mes cuenta los meses desde enero (es decir, enero es 0, febrero 1, etc.)
* el byte día cuenta los días del mes a partir del 1
* Los bytes hora, minuto y segundo corresponden a lo que se espera: sus valores van de 0 a 23, o de 0 a 59 respectivamente.
* ddls (día de la semana) es un byte que cuenta los días desde el domingo (es decir, el domingo es 0, el lunes es 1, el martes es 2, etc.).
* los bytes hora, minuto y segundo corresponden a lo que se espera: sus valores van de 0 a 23, o de 0 a 59 respectivamente
* ddls (día de la semana) es un byte que cuenta los días desde el domingo (es decir, el domingo es 0, el lunes es 1, el martes es 2, etc.)
* dda (día del año) es un byte que cuenta los días desde el 1 de enero (es decir, el 1 de enero es 0, el 2 de enero es 1, etc.)
* eshdv ( es horario de verano) es una bandera, 01 si es horario de verano y 00 si no lo es.
* eshdv (es horario de verano) es una bandera, 01 si es horario de verano y 00 si no lo es.
basándonos en esto, debería ser sencillo utilizarlos. por ejemplo, para leer la hora del día en la pila, haríamos:
@ -224,7 +223,7 @@ tal vez puedas usar estos valores como coordenadas para algunos sprites, o tal v
o ¿qué tal dibujar sprites condicionalmente, y/o cambiar los colores del sistema dependiendo de la hora? :)
¡también puedes utilizar los valores de la fecha y la hora como semillas para generar alguna pseudo-aleatoriedad!
¡también puedes utilizar los valores de la fecha y la hora como semillas para generar algo de pseudo-aleatoriedad!
por último, recuerda que para cronometrar eventos con más precisión que segundos puedes contar las veces que se ha disparado el vector pantalla.
@ -235,10 +234,10 @@ por fin, ¡el dispositivo de audio! o debería decir, ¡los dispositivos de audi
varvara tiene cuatro dispositivos estéreo idénticos (o "canales"), que se mezclan antes de pasar a los altavoces/auriculares:
```
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|40 @Audio1 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|50 @Audio2 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|60 @Audio3 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
|40 @Audio1 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
|50 @Audio2 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
|60 @Audio3 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
```
de forma similar a como en el dispositivo de pantalla podemos dibujar apuntando a direcciones con datos de sprite, en los dispositivos de audio podremos reproducir sonidos apuntando a direcciones con datos de audio (muestras o "samples").
@ -381,17 +380,17 @@ de forma similar a como hemos tratado los sprites, y de forma parecida al dispos
#0100 .Audio0/largo DEO2 ( establecer la longitud de la muestra )
```
la frecuencia a la que se reproduce esta muestra (es decir, a la que la amplitud de la onda toma el valor del siguiente byte) viene determinada por el byte pitch.
la frecuencia a la que se reproduce esta muestra (es decir, a la que la amplitud de la onda toma el valor del siguiente byte) viene determinada por el byte tono.
## pitch
## tono
el byte pitch hace que la muestra comience a reproducirse cada vez que le escribimos, de forma similar a como el byte de sprite realiza el dibujo del sprite cuando le escribimos.
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.
el octavo bit es una bandera: cuando es 0 la muestra se reproducirá en bucle, y cuando es 1 la muestra se reproducirá sólo una vez.
normalmente querremos hacer un bucle de la muestra para generar un tono basado en ella. sólo cuando la muestra sea lo suficientemente larga tendrá sentido no hacer un bucle y reproducirla una vez.
normalmente querremos hacer un bucle de la muestra para generar un tono basado en ella. sólo cuando la muestra sea lo suficientemente larga tendrá sentido no hacer un bucle y reproducirla una vez.
con respecto a los bits para la nota midi, es una buena idea tener una tabla midi alrededor para ver los valores hexadecimales correspondientes a las diferentes notas.
@ -407,20 +406,20 @@ en teoría, parecería que el siguiente programa debería reproducir nuestra mue
( hola-sonido.tal )
( dispositivos )
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
( programa principal )
|0100
;sierra-pcm .Audio0/direc DEO2 ( establecer la dirección de la muestra )
#0100 .Audio0/largo DEO2 ( establecer la longitud de la muestra )
#3c .Audio0/pitch DEO ( establecer el pitch como Do medio )
#3c .Audio0/tono DEO ( establecer el tono como Do medio )
BRK
```
¡no realmente!
para poder escuchar el sonido, necesitamos dos cosas más: ajustar el volumen del dispositivo y ajustar la envolvente ADSL.
¡pero ya casi! para poder escuchar el sonido, necesitamos dos cosas más: ajustar el volumen del dispositivo y ajustar la envolvente ADSL.
## volumen
@ -467,13 +466,13 @@ ok, ¡ahora estamos listos para reproducir el sonido!
## reproduciendo la muestra
¡el siguiente programa tiene ahora los cinco componentes que necesitamos para reproducir un sonido: una dirección de muestra, su longitud, las duraciones de adsr, el volumen, y su pitch!
¡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 )
( dispositivos )
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &pitch $1 ]
|30 @Audio0 [ &vector $2 &posicion $2 &salida $1 &pad $3 &adsr $2 &largo $2 &direc $2 &volumen $1 &tono $1 ]
( programa principal )
|0100
@ -482,17 +481,17 @@ ok, ¡ahora estamos listos para reproducir el sonido!
#ffff .Audio0/adsr DEO2 ( establecer la envolvente )
#ff .Audio0/volumen DEO ( establecer el volumen máximo )
#3c .Audio0/pitch DEO ( establecer el pitch como Do central )
#3c .Audio0/tono DEO ( establecer el tono como Do central )
BRK
```
nota (!) que sólo se reproducirá el sonido una vez, y lo hace cuando se inicia el programa.
### algunos experimentos sugeridos
te invito a que experimentes modificando los valores del ADSR: ¿cómo cambia el sonido cuando sólo hay uno de ellos? ¿o cuando todos son números pequeños o con diferentes combinaciones de duraciones?
también, prueba a cambiar el byte pitch: ¿corresponden con tus oídos los valores midi que esperas?
también, prueba a cambiar el byte tono: ¿corresponden con tus oídos los valores midi que esperas?
¿y cómo cambia el sonido cuando usas una muestra diferente? ¿puedes encontrar o crear otras diferentes?
@ -500,10 +499,10 @@ también, prueba a cambiar el byte pitch: ¿corresponden con tus oídos los valo
una vez que hemos configurado nuestro dispositivo de audio con una muestra, longitud, envolvente ADSR y volumen, podríamos reproducirlo una y otra vez (re)escribiendo un tono en un momento diferente; los demás parámetros pueden dejarse intactos.
por ejemplo, una macro como la siguiente podría permitirnos reproducir una nota de nuevo según el pitch dado en la parte superior de la pila:
por ejemplo, una macro como la siguiente podría permitirnos reproducir una nota de nuevo según el tono dado en la parte superior de la pila:
```
%REPR-NOTA { .Audio0/pitch DEO } ( pitch -- )
%REPR-NOTA { .Audio0/tono DEO } ( tono -- )
```
cuando ocurriera un evento específico, podrías llamarlo:
@ -512,13 +511,13 @@ cuando ocurriera un evento específico, podrías llamarlo:
#3c REPR-NOTA ( reproducir Do central )
```
ten en cuenta que cada vez que escribes un pitch, la reproducción de la muestra y la forma de la envolvente vuelve a empezar, independientemente de dónde se encuentre.
ten en cuenta que cada vez que escribes un tono, la reproducción de la muestra y la forma de la envolvente vuelve a empezar, independientemente de dónde se encuentre.
### algunas ideas
¿qué tal si implementas la reproducción de diferentes tonos presionando diferentes teclas en el teclado? podrías usar nuestros ejemplos anteriores, pero escribiendo un tono en el dispositivo en lugar de, por ejemplo, incrementar una coordenada :)
¿o qué tal si complementas nuestro programa pong de {tutorial de uxn día 6} con efectos de sonido, haciendo que el dispositivo toque una nota cada vez que la pelota rebote?
¿o qué tal si complementas nuestro programa pong del {tutorial de uxn día 6} con efectos de sonido, haciendo que el dispositivo toque una nota cada vez que la pelota rebote?
¿o qué tal si utilizas el vector de la pantalla para cronometrar la reproducción repetitiva de una nota? ¿o qué tal si haces que toque una melodía siguiendo una secuencia de notas? ¿podría venir esta secuencia de un archivo de texto? :)
@ -551,7 +550,7 @@ en cualquier caso, ¡no dudes en compartir lo que crees! :)
aunque no lo creas, ¡este es el final!
¡has llegado al final de la serie del tutorial! ¡felicidades!
¡has llegado al final de este tutorial! ¡felicidades!
¡espero que lo hayas disfrutado y que lo veas como el comienzo de tu viaje uxn!

View File

@ -20,7 +20,7 @@ let's start with the following program as a template. it includes the data for a
( hello-background.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
( macros )

View File

@ -24,6 +24,10 @@ the button byte encodes in each of its eight bits the state of eight different "
numbering the bits from right to left, and from 0 to 7, the corresponding keys (and NES buttons) are:
+ <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>
+ <tr><td>Right</td><td>Left</td><td>Down</td><td>Up</td><td>Esc (Start)</td><td>Shift (Select)</td><td>Alt (B)</td><td>Ctrl (A)</td></tr>
+ </table>
& * 0: Ctrl (button A)
& * 1: Alt (button B)
& * 2: Shift (Select button)
@ -33,11 +37,6 @@ numbering the bits from right to left, and from 0 to 7, the corresponding keys (
& * 6: Left
& * 7: Right
+ <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>
+ <tr><td>Right</td><td>Left</td><td>Down</td><td>Up</td><td>Esc (Start)</td><td>Shift (Select)</td><td>Alt (B)</td><td>Ctrl (A)</td></tr>
+ </table>
enconding the states of the buttons in this way allows us to press and read many of these keys at the same time.
## the key byte
@ -164,7 +163,7 @@ AND2, ORA2, EOR2 will work in the same way, but with shorts instead of bytes.
### AND
the following will push down into the stack a flag that indicates if the key byte is between 30 and 39 inclusive, using 01 to represent 'true', and 00 to represent 'false0:
the following will push down into the stack a flag that indicates if the key byte is between 30 and 39 inclusive, using 01 to represent 'true', and 00 to represent 'false':
```
.Controller/key DEI ( read key and push into the stack )
@ -393,12 +392,12 @@ so far we have been using the stack as a place to store operands of instructions
uxntal has six instructions that act upon elements in the stack closer to the top:
* POP: Remove top element from the stack ( a -- )
* DUP: Duplicate; push a copy of the top element ( a -- a a )
* SWP: Swap; change the order of the top two elements of the stack ( a b -- b a )
* NIP: Remove the top second element of the stack ( a b -- b )
* OVR: Over; push a copy of the second top element ( a b -- a b a )
* ROT: Rotate; reorder the top three elements of the stack so that the third one is now at the top ( a b c -- b c a )
* POP: remove top element from the stack ( a -- )
* DUP: duplicate; push a copy of the top element ( a -- a a )
* SWP: swap; change the order of the top two elements of the stack ( a b -- b a )
* NIP: remove the top second element of the stack ( a b -- b )
* OVR: over; push a copy of the second top element ( a b -- a b a )
* ROT: rotate; reorder the top three elements of the stack so that the third one is now at the top ( a b c -- b c a )
in short mode, POP2, DUP2, SWP2, NIP2, OVR2 and ROT2 perform the same actions but using shorts instead of bytes.
@ -460,7 +459,7 @@ now the stack looks like this:
flag1 key <- top
```
finally we can proceed with the comparison and the AND
finally we can proceed with the comparison and the AND:
```
#3a LTH ( is it less than 3a? push flag into the stack )

View File

@ -192,7 +192,7 @@ as you may recall, the zero page consists of the first 256 addresses of program
we can refer to any of the 256 addresses of the zero page using one byte only, instead of the two bytes that are needed for absolute addresses.
something importat to keep in mind is that the contents of the zero page are not present in uxn roms.
something important to keep in mind is that the contents of the zero page are not present in uxn roms.
this means that a caveat of using variables there, is that in order to initialize them we need to do it during runtime, by storing values from the stack into them.
@ -207,7 +207,6 @@ labels for the zero page would work the same as before; we only need to specify
in order to refer to them, we would use the dot (.) rune for literal zero page addresses, instead of the colon (;) rune for literal absolute addresses.
### instructions: LDZ, STZ
the instructions for loading (reading) and storing (writing) from and to the zero page are:
@ -266,7 +265,7 @@ BRK
note the use of the literal zero page address rune (.) to refer to the .pixel label.
also, note that in the case of .pixel the address is referring to the zero page, accessed with LDZ/STZ, and in the case of .Screen the address is referring to the i/o address space, accessed with DEO/DEI.
also, note that in the case of .pixel the address is referring to the zero page, accessed with LDZ/STZ, and in the case of .Screen the address is referring to the i/o address space, accessed with DEI/DEO.
### a little stack wrangling practice
@ -358,7 +357,7 @@ the use of "variables" will help us now in discussing three different ways of an
* autonomous change of position
* interactive change of position (with keyboard)
* autonomous change of drawn tile
* autonomous change of drawn tile
we will review them separately in order to keep the examples relatively simple and readable.
@ -920,12 +919,10 @@ we can use these macros to divide the frequency in our code:
.Screen/addr DEO2 ( set computed address )
```
=> ./img/screencap_uxn-animation-quarterspeed.gif animation of a diagonal stripe inside a pixelated square. the diagonal moves from bottom right to top left. it moves slower than the previous one.
ah, way better!
## not powers of two
note that if you want to divide the frequency to numbers that are not powers of 2, you might start to see some glitches approximately every 4 seconds: this is due to framecount overflowing and not giving a nice sequence of results for those divisors.
@ -936,7 +933,6 @@ the easiest workaround for these issues would be to use a short-sized framecount
you'd have to adapt the program to work with that size of framecount - nice exercise, i feel and think!
# instructions of day 4
these are all the uxntal instructions that we discussed today!

View File

@ -24,7 +24,8 @@ counting from right to left, the first bit corresponds to the first mouse button
usually, in a three-button mouse, the first button is the left one, the second button the middle one, and the third button the right one.
using a three-button mouse like this, we would have eight possible values for the state byte, for example:
using a three-button mouse like this, we would have eight possible values for the state byte, for example:
* 00 when none of the buttons are pressed
* 01 when only the first button is pressed
* 02 when only the second button is pressed
@ -35,7 +36,7 @@ note that similarly to the controller device, this system allows us to check for
* 03 when the first and second buttons are pressed
* 05 when the first and third buttons are pressed
* 06 when the second and third buttons are pressed
* 07 when the threee buttons are pressed
* 07 when the three buttons are pressed
remember that we can use AND masks, as introduced on {uxn tutorial day 3}, to isolate and evaluate separately any of these bits.
@ -257,6 +258,7 @@ first of all, let's move our pointer drawing subroutine to another label in our
#4a .Screen/sprite DEO
BRK
```
note that we could join the actions of updating the pointer position and sending it to the screen, using a pair of DUP2:
```
@ -273,7 +275,6 @@ this would leave our on-mouse subroutine empty:
BRK
```
### using normal jumps
with what we know already, and depending on the position of draw-pointer with respect to on-mouse, we could do a relative jump:
@ -396,7 +397,7 @@ additionally, as the addresses pushed by JSR are shorts, we need to activate the
JMP2r ( jump to the absolute address at the top of the return stack )
```
in many uxntal programs you will see this instruction written as a macro, RTN (return)
in many uxntal programs you will see this instruction written as a macro, RTN (return):
```
%RTN { JMP2r }
@ -626,7 +627,7 @@ i said then that there was a more optimized set, and that we'd discuss it later.
now is that moment!
first of all, let's analyze what's happening with MOD. it is calculating what would be written in infix notation as follows, assumming that the slash (/) indicates an integer division
first of all, let's analyze what's happening with MOD. it is calculating what would be written in infix notation as follows, assumming that the slash (/) indicates an integer division:
```
a - ( a/b )*b

View File

@ -45,7 +45,6 @@ let's start with the following program as a template. it includes the data for a
#01c0 .System/g DEO2
#2ce5 .System/b DEO2
BRK
@tile-background 1122 4488 1122 4488
@ -676,7 +675,7 @@ all of this can go inside its own subroutine for readability purposes:
```
@update-paddles ( -- )
&left
( left paddle: up and down buttons )
( left paddle: up and down buttons )
.Controller/button DEI
DUP #10 AND ( check bit for up )
,&left-up JCN
@ -1190,7 +1189,7 @@ where bounce-left would be:
and what happens if both conditions are not met at the same time?
we can let the ball keep moving, but checking that it hasn't crossed the left wall, by comparing with 0000.
we can let the ball keep moving, but checking that it hasn't crossed the left wall, by comparing with 0000.
the whole x-in-left code would end up looking like:
@ -1220,7 +1219,7 @@ the whole x-in-left code would end up looking like:
&check-right-paddle
```
finish would be a label at the end of the subroutine, and reset is a subroutine that we will discuss later.
"finish" would be a label at the end of the subroutine, and "reset" is a subroutine that we will discuss later.
this approach of comparing with 0000 is the easiest, but keep in mind that it might not work if you change the ball speed: it could happen that it crosses the wall but with an x coordinate that is never equal to 0.
@ -1303,7 +1302,7 @@ here's all of the code we wrote today!
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &auto $1 &pad $1
&x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|80 @Controller [ &vector $2 &button $1 &key $1 ]
|80 @Controller [ &vector $2 &button $1 &key $1 ]
( macros )
%RTN { JMP2r }