agregando traducción de maleza
Merge branch 'main' of https://codeberg.org/maleza/compudanzas into maleza-main
This commit is contained in:
commit
1dfd4712bd
|
@ -0,0 +1,118 @@
|
|||
# tutorial de uxn
|
||||
|
||||
una guía de inicio para programar la computadora uxn, y un pausado complemento a la documentación oficial.
|
||||
|
||||
=> https://wiki.xxiivv.com/site/uxntal.html uxntal
|
||||
=> https://wiki.xxiivv.com/site/uxnemu.html uxnemu
|
||||
|
||||
el tutorial está dividido en 8 días (o secciones), ya que puede ser seguido junto al taller.
|
||||
|
||||
(al día de hoy, este es un trabajo en proceso)
|
||||
|
||||
|
||||
# día 1
|
||||
|
||||
en esta primera sección del tutorial vamos a hablar de los aspectos básicos de la computadora uxn, su paradigma de programación, su arquitectura, y por qué podrías queres aprender a programar en ella.
|
||||
|
||||
también vamos a saltar directo a nuestros primeros programas simples para demostrar conceptos fundamentales que desarrollaremos en los días siguientes.
|
||||
|
||||
=> ./uxn_tutorial_day_1.gmi {uxn tutorial day 1}
|
||||
|
||||
# día 2
|
||||
|
||||
próximamente!
|
||||
|
||||
# índice tentativo
|
||||
|
||||
este índice está aquí y ahora como referencia de la estructura general del tutorial.
|
||||
|
||||
## día 1: aspectos básicos
|
||||
|
||||
* ¿por qué uxn?
|
||||
* notación {postfix}
|
||||
* arquitectura de la computadora uxn
|
||||
* instalación y toolchain
|
||||
* un hola mundo muy básico
|
||||
* etiquetas, macros y runas
|
||||
* un hola mundo mejorado
|
||||
* imprimir un dígito
|
||||
|
||||
instrucciones nuevas: LIT, DEO, ADD, SUB
|
||||
|
||||
=> https://git.sr.ht/~rabbits/uxn/ uxn repo
|
||||
|
||||
## día 2: la pantalla
|
||||
|
||||
* modo corto
|
||||
* colores del sistema
|
||||
* dibujar pixeles
|
||||
* sprites: formato chr, nasu
|
||||
* dibujar sprites sprites
|
||||
* operaciones en la pila
|
||||
* practica: reoetición manual de un sprite
|
||||
|
||||
instructiones nuevas: DEI, MUL, DIV, SWP, OVR, ROT, DUP, POP
|
||||
|
||||
modo nuevo: modo corto
|
||||
|
||||
=> https://wiki.xxiivv.com/site/nasu.html nasu
|
||||
|
||||
## día 3: interactividad con el teclado
|
||||
|
||||
* vector de controlador
|
||||
* control de flujo: condicionales, saltos relativos y absolutos
|
||||
* runas para direcciones
|
||||
* botón y tecla
|
||||
* máscaras bitwise
|
||||
* práctica: mover/cambiar sprite con el teclado
|
||||
|
||||
instrucciones nuevas: EQU, NEQ, JCN, JMP, AND, ORA, EOR, SFT
|
||||
|
||||
## día 4: bucles y animación
|
||||
|
||||
* control de flujo: repetición de un sprite
|
||||
* vector de pantalla
|
||||
* variables: página cero, relativas, absolutas
|
||||
* offsets en direcciones
|
||||
* timing de animación
|
||||
* práctica: sprite animado
|
||||
|
||||
instrucciones nuevas: LTH, GTH, STZ, STR, STA, LDZ, LDR, LDA
|
||||
|
||||
## día 5: interactividad con el mouse
|
||||
|
||||
* dispositivo y vector del mouse
|
||||
* Pila de retorno y modo
|
||||
* subrutinas: parametros, llamada y retorno
|
||||
* práctica: sprite como puntero
|
||||
* práctica: herramienta de dibujo simple
|
||||
|
||||
instrucciones nuevas: STH, JSR
|
||||
|
||||
modo nuevo: modo de retorno
|
||||
|
||||
## día 6: audio
|
||||
|
||||
* el dispositivo de audio
|
||||
* samples como sprites de audio
|
||||
* adsr
|
||||
* pitch
|
||||
* práctica: pequeño instrumento musical
|
||||
|
||||
## día 7: modo de retención y otros dispositivos
|
||||
|
||||
* modo de retención
|
||||
* re-escribiendo código con modo de retención
|
||||
* dispositivo de archivo: guardando y cargando un estado simple
|
||||
* dispositivo de fecha y hora: leyendo fecha y hora
|
||||
* práctica: visualización de fecha y hora
|
||||
|
||||
modo nuevo: modo de retención
|
||||
|
||||
## día 8: tiempo de demo
|
||||
|
||||
* comparte lo que has creado :)
|
||||
|
||||
# soporte
|
||||
|
||||
si este tutorial te ha sido de ayuda, considera compartirli y brindarle tu {apoyo} :)
|
|
@ -0,0 +1,602 @@
|
|||
# tutorial uxn: día 1, aspectos básicos
|
||||
|
||||
hola! en esta primera sección del {tutorial uxn} vamos a hablar de los aspectos básicos de la computadora uxn, su paradigma de programación, su arquitectura, y por que puede que quieras aprender a programarla.
|
||||
|
||||
también saltaremos directo a nuestros primeros programas simples para demostrar conceptos fundamentales que desarrollaremos en los próximos días.
|
||||
|
||||
# ¿por qué uxn?
|
||||
|
||||
o primero que nada... qué es uxn?
|
||||
|
||||
> Uxn es una computadora portable de 8 bit capaz de correr herramientas y juegos simples programable en su propio pequeño lenguaje ensamblador. Es también un ámbito de juego donde aprender habilidades básicas de computación.
|
||||
|
||||
=> https://wiki.xxiivv.com/site/uxn.html XXIIVV - uxn
|
||||
|
||||
i invite you to read "why create a smol virtual computer" from the 100R site, as well:
|
||||
|
||||
=> https://100r.co/site/uxn.html 100R - uxn
|
||||
|
||||
osea que, uxn es una computadora virtual (¿por el momento?) que es lo suficientemente simple como para ser emulada por diversas plataformas de computación viejas y nuevas.
|
||||
|
||||
personalmente, veo en ella las siguientes virtudes:
|
||||
|
||||
* hecha para el largo plazo
|
||||
* hecha para aplicaciones audiovisuales interactivas
|
||||
* arquitectura y set de instrucciones simple (sólo 32 instrucciones!)
|
||||
* primero-offline: funciona localmente y sólo necesitas unos pocos archivos de documentación para avanzar
|
||||
* ámbito de práctica y experimentación de computación dentro de límites
|
||||
* ya ortada 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:
|
||||
|
||||
* es programada en un lenguaje ensamblador, uxntal
|
||||
* utiliza notación {postfija} (alias notación polaca inversa) / 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 complegidad relativamente baja.
|
||||
|
||||
# notación postfija (y la pila)
|
||||
|
||||
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.
|
||||
|
||||
> 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 [...]
|
||||
|
||||
=> https://en.wikipedia.org/wiki/Reverse_Polish_notation Reverse Polish notation - Wikipedia
|
||||
|
||||
## suma postfija
|
||||
|
||||
en notación postfija, la suma de nos números sería escrita de la siguiente forma:
|
||||
|
||||
``` 1 48 +
|
||||
1 48 +
|
||||
```
|
||||
|
||||
dónde, leyendo de izquierda a derecha:
|
||||
|
||||
* el número 1 es empujado en la pila
|
||||
* el número 48 es empujado en la pila
|
||||
* + toma dos elementos de la parte superior de la pila, los suma, y empuja el resultado en la pila
|
||||
|
||||
the book Starting Forth has some great illustrations of this process of addition:
|
||||
|
||||
=> https://www.forth.com/starting-forth/1-forth-stacks-dictionary/#The_Stack_Forth8217s_Workspace_for_Arithmetic The Stack: Forth’s Workspace for Arithmetic
|
||||
|
||||
## de infija a postfija
|
||||
|
||||
expresiones más complejas en notación infija, que requieren paréntesis o reglas de precedencia de operadores (y un sistema más complejo para codificarlas), pueden ser simplificadas mediante notación postfija.
|
||||
por ejemplo, la siguiente expresión infija:
|
||||
|
||||
``` (2 + 16)/8 + 48
|
||||
(3 + 5)/2 + 48
|
||||
```
|
||||
|
||||
pueden ser escritas en notación postfija como:
|
||||
|
||||
``` 3 5 + 2 / 48 +
|
||||
3 5 + 2 / 48 +
|
||||
```
|
||||
|
||||
también podemos escribirla de muchas otras maneras, por ejemplo:
|
||||
|
||||
``` 48 2 3 5 + / +
|
||||
48 3 5 + 2 / +
|
||||
```
|
||||
|
||||
¡asegúrate de que éstas expresiones funcionan y son equivalentes! sólo debes seguir estas reglas, leyendo de izquierda a derecha:
|
||||
|
||||
* si es un número, empujarlo a la pila
|
||||
* si es un operador, tomar dos elementos de la parte superior de la pila, aplicar la operación, y empujar el resultado en la pila.
|
||||
|
||||
nota: en el caso de la división, los operandos siguen el mismo orden de izquierda a derecha. 3/2 sería escrito como:
|
||||
|
||||
``` 3 2 /
|
||||
3 2 /
|
||||
```
|
||||
|
||||
irás descubriendo cómo el uso de la pila puede ser muy poderoso ya que ahorra operandos y/o resultados intermedios sin necesidad de que asignemos explicitamente un espacio en memoria para ellos (por ejemplo, mediante el uso de "variables" en otros lenguajes de programación)
|
||||
|
||||
we'll come back to postfija notation and the stack very soon!
|
||||
¡vamos a volver a la notación postfija y la pila muy pronto!
|
||||
|
||||
# arquitectura de la computadora uxn
|
||||
|
||||
una de las cuestiones de programar una computadora a un nivel bajo de abstracción, como estaremos haciendo con uxn, es que tenemos que conocer y estar atentos a sus funcionamientos internos.
|
||||
|
||||
## 8-bits y hexadecimal
|
||||
|
||||
las palabras binarias de 8 bits, también conocidas como bytes, son los elementos básicos de codificación y manipulación de datos en uxn.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
cada instrucción junto con sus banderas de modo puede ser codificada en una sola palabra de 8 bits.
|
||||
|
||||
todas estas instrucciones operan sobre elementos en la pila, ya sea tomando de ella sus operandos y/o empujando en ella sus resultados.
|
||||
|
||||
we'll be covering these instructions slowly over this tutorial.
|
||||
vamos a ir cubriendo lentamente estas instrucciones durante este tutorial.
|
||||
|
||||
## memoria uxn
|
||||
|
||||
la memoria en la computadora uxn consiste en cuatro espacios separados:
|
||||
|
||||
* memoria principal, con 65536 bytes
|
||||
* memoria de entrada/salida, con 256 bytes dividida en 16 secciines (o dispositivos) de 16 bytes cada uno: 8 bytes para entradas y 8 bytes para salidas.
|
||||
* pila de trabajo, con 256 bytes
|
||||
* pila de retorno, con 256 bytes
|
||||
|
||||
cada byte en la memoria principal posee una dirección de 16 bits (2 bytes) de tamaño, mientras que cada byte en la memoria de entrada/salida posee una dirección de 8 bits (1 byte) de tamaño. ambas pueden ser accedidas de manera aleatoria.
|
||||
|
||||
los primeros 256 bytes de la memoria principal constituyen una sección llamada página cero. esta sección puede ser referenciada por 8 bits (1 byte), y su propósito es almacenar datos durante el tiempo de ejecución de la máquina.
|
||||
|
||||
hay tres instrucciones diferentes para interactuar con cada uno de estos espacios de memoria.
|
||||
|
||||
la memoria principal almacena el programa a ser ejecutado, empezando en el byte 257 (dirección 0100 en hexadecimal). también puede almacenar datos.
|
||||
|
||||
las pilas no pueden ser accedidas aleatoriamente; la máquina uxn se ocupa de ellas.
|
||||
|
||||
## ciclo de instrucción
|
||||
|
||||
el cpu uxn lee un byte por vez de la memoria principal.
|
||||
|
||||
el contador de programa es una palabra de 16 bits que indica la dirección del próximo byte a leer. su valor inicial es la dirección 0100 en hexadecimal.
|
||||
|
||||
una vez que el cpu lee un byte, lo decodifica como instrucción y lo ejecuta.
|
||||
|
||||
la instrucción va a implicar normalmente un cambio en la(s) pila(s), y algunas veces también un cambio en el flujo normal del contador de programa: en lugar de apuntar al siguiente byte en memoria, puede ser apuntado a otro lado, "saltando" de un lugar en memoria a otro.
|
||||
|
||||
# instalación y toolchain
|
||||
|
||||
liste? obtengamos el ensamblador uxn (uxnasm) y emulador (uxnemu) de su repositorio git:
|
||||
|
||||
=> https://git.sr.ht/~rabbits/uxn ~rabbits/uxn - sourcehut git
|
||||
|
||||
estas instrucciones son para sistemas basados en linux.
|
||||
|
||||
si necesitas una mano, encuèntranos en #uxn en irc.esper.net :)
|
||||
|
||||
## instalar SDL2
|
||||
|
||||
para compilar uxnemu, necesitamos instalar la librería SDL2.
|
||||
|
||||
en una terminal en debian/ubuntu, correr:
|
||||
|
||||
``` sudo apt install libsdl2-dev
|
||||
$ sudo apt install libsdl2-dev
|
||||
```
|
||||
|
||||
on en guix:
|
||||
|
||||
``` guix install sdl2
|
||||
$ guix install sdl2
|
||||
```
|
||||
|
||||
## obtener y compilar uxn
|
||||
|
||||
obtengamos y compilemos uxnemu y uxnasm:
|
||||
|
||||
```
|
||||
$ git clone https://git.sr.ht/~rabbits/uxn
|
||||
$ cd uxn
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
si todo fué bién, verás varios mensajes en la terminal y una pequeña ventana con el título uxn, y una aplicación demo: uxnemu está ahora corriendo una "rom" correspondiendo a esa aplicación.
|
||||
|
||||
## controles uxnemu
|
||||
|
||||
* F1 itera entre diferentes niveles de zoom
|
||||
* F2 muestra el debugger en pantalla
|
||||
* F3 toma una captura de pantalla de la ventana
|
||||
|
||||
## usando el toolchain
|
||||
|
||||
verás que luego de compilar uxn, cuentas con tres nuevos archivos ejecutables en el directorio bin/:
|
||||
|
||||
* uxnemu: el emulador
|
||||
* uxnasm: el ensamblador
|
||||
* uxncli: un emulador de consola no interactivo
|
||||
|
||||
puedes ajustar tu $PATH para tenerlos disponibles en todos lados.
|
||||
|
||||
la idea es que para correr un programa escrito en uxntal (el lenguaje ensamblador de uxn), primero tienes que ensamblarlo en una "rom", y luego puedes correr esta rom con el emulador.
|
||||
|
||||
por ejemplo, para correr {darena} que se encuentra en projects/examples/demos/ :
|
||||
|
||||
```
|
||||
ensambla darena.tal en darena.rom
|
||||
$ ./bin/uxnasm projects/examples/demos/darena.tal bin/darena.rom
|
||||
|
||||
correr darena.rom
|
||||
$ ./bin/uxnemu bin/darena.rom
|
||||
```
|
||||
|
||||
¡échale una mirada a los demos disponibles! (¡o no, y empecemos a programar los nuestros!)
|
||||
|
||||
# uxntal y un muy básico hola mundo
|
||||
|
||||
uxntal es el lenguaje ensamblador para la máquina uxn.
|
||||
|
||||
estuvimos hablando antes sobre el cpu uxn y las 32 instrucciones que sabe cómo ejecutar, cada una de ellas codificada como una sola palabra de 8 bits (byte).
|
||||
|
||||
ese lenguaje ensamblador uxntal implica que hay una relación uno a uno mapeando de una instrucción escrita en el lenguaje a una palabra de 8 bit correspondiente que el cpu puede interpretar.
|
||||
|
||||
por ejemplo, la instrucción ADD (suma) en uxntal es codificada como un byte con el valor 18 en hexadecimal, y corresponde al siguiente conjunto de de acciones: toma los dos elementos superiores de la pila, los suma, y empuja el resultado a la pila.
|
||||
|
||||
en sistemas de tipo forth podemos ver el siguiente tipo de notación para expresar los operandos que una instrucción toma de la pila, y el(los) resultado(s) que empuja devuelta a la pila:
|
||||
|
||||
```
|
||||
ADD ( a b -- a+b )
|
||||
```
|
||||
|
||||
esto significa que ADD tome el primer elemento desde arriba 'b', luego toma el siguiente primer elemento 'a', y empuja devuelta el resultado de sumar a+b.
|
||||
|
||||
ahora que estamos en eso, hay una instrucción complementaria, SUB (resta) (opcode 19), que toma los dos elementos superiores de la pila, los resta, y empuja a la pila el resultado:
|
||||
|
||||
```
|
||||
SUB ( a b -- a-b )
|
||||
```
|
||||
|
||||
nota que el orden de los operandos es similar al de la división que discutimos arriba cuando hablamos de notación postfija.
|
||||
|
||||
## un primer programa
|
||||
|
||||
escribamos el siguiente programa en nuestro editor de texto favorito, y guardémoslo como hello.tal:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 LIT 68 LIT 18 DEO
|
||||
```
|
||||
|
||||
ensamblémoslo y corrámoslo:
|
||||
|
||||
```
|
||||
$ ./bin/uxnasm hello.tal bin/hello.rom && ./bin/uxnemu bin/hello.rom
|
||||
```
|
||||
|
||||
veremos una salida con el siguiente aspecto:
|
||||
|
||||
```
|
||||
Assembled bin/hello.rom(5 bytes), 0 labels, 0 macros.
|
||||
Uxn loaded[bin/hello.rom].
|
||||
Device added #00: system, at 0x0000
|
||||
Device added #01: console, at 0x0010
|
||||
Device added #02: screen, at 0x0020
|
||||
Device added #03: audio0, at 0x0030
|
||||
Device added #04: audio1, at 0x0040
|
||||
Device added #05: audio2, at 0x0050
|
||||
Device added #06: audio3, at 0x0060
|
||||
Device added #07: ---, at 0x0070
|
||||
Device added #08: controller, at 0x0080
|
||||
Device added #09: mouse, at 0x0090
|
||||
Device added #0a: file, at 0x00a0
|
||||
Device added #0b: datetime, at 0x00b0
|
||||
Device added #0c: ---, at 0x00c0
|
||||
Device added #0d: ---, at 0x00d0
|
||||
Device added #0e: ---, at 0x00e0
|
||||
Device added #0f: ---, at 0x00f0
|
||||
h
|
||||
```
|
||||
|
||||
la última 'h' que vemos es la salida de nuestro programa. cambia el 68 a, por ejemplo, 65, y verás una 'e'.
|
||||
|
||||
¿qué es lo que está pasando?
|
||||
|
||||
## una instrucción por vez
|
||||
|
||||
acabamos de correr el siguiente programa en uxntal:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 LIT 68 LIT 18 DEO
|
||||
```
|
||||
|
||||
la primera línea es un comentario: los comentarios son encerrados entre paréntesis y debe haber un espacio entre cada paréntesis y el contenido. de manera similar a otros lenguajes de programación, los comentarios son ignorados por el ensamblador.
|
||||
|
||||
en la segunda linea ocurren varias cosas:
|
||||
|
||||
* |0100 : puede que recuerdes este número de antes - este es el valor inicial del contador de programa; la dirección del primer byte que el cpu lee. usamos esta notación para indicar que lo que esté escrito después será escrito en memoria después de esta dirección.
|
||||
* LIT : esta aparece dos veces, y es una instrucción uxn con las siguientes acciones: empuja el siguiente valor en memoria a la pila, y hace que el contador de programa saltee ese byte.
|
||||
* 68 : un número hexadecimal, que corresponde al código ascii del caracter 'h'
|
||||
* 18 : un número hexadecimal, que corresponde a una dirección de entrada/salida: dispositivo 1 (consola), dirección 8.
|
||||
* DEO : otra instrucción uxn, que podemos definir como lo siguiente: escribir el byte dado en la dirección de dispositivo dada, ambos tomados de la pila ( byte dirección -- )
|
||||
|
||||
leyendo el programa de izquierda a derecha, podemos ver el siguiente comportamiento:
|
||||
|
||||
* la instrucción LIT empuja el número 68 a la pila
|
||||
* la instrucción LIT empuja el número 18 a la pila
|
||||
* la instrucción DEO toma el elemento superior de la pila (18) y lo usa como dirección de dispositivo
|
||||
* la instrucción DEO toma el elemento superiir de la pila (68) y lo usa como byte a escribir
|
||||
* la instrucción DEO escribe el byte a la dirección de dispositivo, dejando la pila vacía
|
||||
|
||||
¿y qué es el dispositivo de entrada/salida con la dirección 18?
|
||||
|
||||
mirando en la tabla de dispositivos de la referencia uxnemu, podemos ver que el dispositivo con la dirección 1 en el nibble superior es la consola (entrada y salida estandard), y que la columna con la dirección 8 corresponde a "escritura".
|
||||
|
||||
=> https://wiki.xxiivv.com/site/uxnemu.html uxnemu
|
||||
|
||||
asique, el dispositivo 18 corresponde a "escribir en consola", o salida estandard.
|
||||
|
||||
¡nuestro programa está enviando el valor hexadecimal 68 (caracter 'h') a la salida estandard!
|
||||
|
||||
puedes ver los valores hexadecimales de los caracteres ascii en la siguiente tabla:
|
||||
|
||||
=> https://wiki.xxiivv.com/site/ascii.html ascii table
|
||||
|
||||
## rom ensamblada
|
||||
|
||||
podemos ver que el ensamblador reporta que nuestro programa es de 5 bytes de tamaño:
|
||||
|
||||
```
|
||||
Assembled bin/hello.rom(5 bytes), 0 labels, 0 macros.
|
||||
```
|
||||
|
||||
para el curioso (¡como tu!), podemos usar una herramienta como hexdump para ver sus contenidos:
|
||||
|
||||
```
|
||||
$ hexdump -C bin/hello.rom
|
||||
00000000 01 68 01 18 17 |.h...|
|
||||
00000005
|
||||
```
|
||||
|
||||
01 es el "opcode" correspondiente a LIT, y 17 es el opcode correspondiente a DEO. ¡y ahí están nuestros 68 y 18!
|
||||
|
||||
¡osea, efectivamente, nuestro programa ensamblado presenta una correspondencia uno a uno con las instrucciones que acabamos de escribir!
|
||||
|
||||
|
||||
de hecho, podríamos haber escrito nuestro programa con estos números hexadecimales (el código máquina), y hubiera funcionado igual:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 01 68 01 18 17 ( LIT 68 LIT 18 DEO )
|
||||
```
|
||||
|
||||
tal vez no sea la manera más práctica de programar, pero ciertamente una divertida :)
|
||||
|
||||
puedes encontrar los opcodes de todas las 32 instrucciones en la referencia uxntal
|
||||
|
||||
=> https://wiki.xxiivv.com/site/uxntal.html XXIIVV - uxntal
|
||||
|
||||
## programa hola
|
||||
|
||||
podemos expandir nuestro programa para imprimir más caracteres:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 LIT 68 LIT 18 DEO ( h )
|
||||
LIT 65 LIT 18 DEO ( e )
|
||||
LIT 6c LIT 18 DEO ( l )
|
||||
LIT 6c LIT 18 DEO ( l )
|
||||
LIT 6f LIT 18 DEO ( o )
|
||||
LIT 0a LIT 18 DEO ( newline )
|
||||
```
|
||||
|
||||
si lo ensamblamos y corremos, tendremos un 'hello' en nuestra terminal, usando 30 bytes de programa :)
|
||||
|
||||
¿ok, y... te gusta?
|
||||
|
||||
¿parece innecesariamente complejo?
|
||||
|
||||
veremos ahora algunas virtudes de uxntal que hacen escribir y leer código más "confortable".
|
||||
|
||||
# runas, etiquetas, macros
|
||||
|
||||
las runas son caracteres especiales que indican a uxnasm algún pre procesamiento a hacer al ensamblar nuestro programa.
|
||||
|
||||
## runa de pad absoluto
|
||||
|
||||
ya vimos la primera de ellas: | define un pad absoluto: la dirección donde el siguiente elemento escrito será ubicado en memoria.
|
||||
|
||||
si la dirección es de 1 byte de longitud, es asumido que es una dirección de el espacio de entrada/salidao de la página cero.
|
||||
|
||||
si la dirección d¡es de 2 bytes de longitud, es asumido que es una dirección de la memoria principal.
|
||||
|
||||
## runa hex literal
|
||||
|
||||
hablemos de otra: #.
|
||||
|
||||
éste caracter define un "hex literal": es básicamente un atajo para la instrucción LIT.
|
||||
|
||||
usando esta runa, podemos reescribir nuestro primer programa como:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 #68 #18 DEO
|
||||
```
|
||||
|
||||
nota que sólo puedes usar esta runa para escribir los contenidos de uno o dos bytes (dos o cuatro nibbles).
|
||||
|
||||
el siguiente tendría el mismo comportamiento que el programa de arriba, pero usando un byte menos (en la siguiente sección/día veremos por qué)
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 #6818 DEO
|
||||
```
|
||||
|
||||
importante: recuerda que esta runa (y las otras con la palabra "literal" en su nombre) es un atajo para la instrucción LIT. esto puede prestarse a confusión en algunos casos :)
|
||||
|
||||
## runa de caracter raw
|
||||
|
||||
ésta es la runa de caracter raw: '
|
||||
|
||||
nos permite que uxnasm decodifique el valor numérico de un caracter ascii.
|
||||
|
||||
nuestro "programa hello" luciría de la siguiente manera, usando las nuevas runas que acabamos de aprender:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|0100 LIT 'h #18 DEO
|
||||
LIT 'e #18 DEO
|
||||
LIT 'l #18 DEO
|
||||
LIT 'l #18 DEO
|
||||
LIT 'o #18 DEO
|
||||
#0a #18 DEO ( newline )
|
||||
```
|
||||
|
||||
el "raw" en el nombre de esta runa indica que no es literal, por ejemplo que no agrega una instrucción LIT.
|
||||
|
||||
## runas para etiquetas
|
||||
|
||||
incluso ahora que sabemos que #18 corresponde a empujar la dirección de dispositivo escribir en consola en la pila, por legibilidad y para asegurar nuestro código a futuro es una buena práctica asignar una serie de etiquetas que corresponderán a ese dispositivo y sus sub direcciones.
|
||||
|
||||
la runa @ nos permite definir etiquetas, y la runa & nos permite definir sub etiquetas.
|
||||
|
||||
por ejemplo, para el dispositivo de consola, la manera en que verías esto escrito en programas uxn es la siguiente:
|
||||
|
||||
```
|
||||
|10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ]
|
||||
```
|
||||
|
||||
podemos ver un pad absokuto a la dirección 10, que asigna lo siguiente a esa dirección. dado que la dirección consiste en un sólo byte, uxnasm asume que es para el espacio de memoria de entrada/salida o la página cero.
|
||||
|
||||
luego vemos la etiqueta @Console: ésta etiqueta va a corresponder a la dirección 10.
|
||||
|
||||
los corchetes son ignorados, pero incluidos por legibilidad.
|
||||
|
||||
luego tenemos varias sub etiquetas, indicadas por la runa &, y pads relativos, indicados por la runa $. ¿cómo los leemos/interpretamos?
|
||||
|
||||
* la sub etiqueta &vector tiene la misma dirección que su etiqueta madre @Console: 10
|
||||
* $2 salta dos bytes (podemos leer esto como &vector siendo una dirección a una palabra de dos bytes de longitud)
|
||||
* la sub etiqueta &read tiene la dirección 12
|
||||
* $1 salta un byte (&read sería una dirección para una palabra de 1 byte de longitud)
|
||||
* la sub etiqueta &pad tiene la dirección 13
|
||||
* $5 salta el resto de los bytes del primer grupo de 8 bytes del dispositivo: éstos bytes corresponden a las "entradas"
|
||||
* la sub etiqueta &write tiene la dirección 18 (¡la que ya conocíamos!)
|
||||
* $1 salta un byte (&write sería una dirección para una palabra de 1 byte de longitud)
|
||||
* la subetiqueta &error tiene la dirección 19
|
||||
|
||||
nada de esto sería traducido a código máquina, pero nos asiste al escribir código uxntal.
|
||||
|
||||
la runa para referirse a una dirección literal en la página cero o el espacio de direcciones de entrada/salida, es . (punto), y una / (barra) nos permite referirnos a una de sus sub etiquetas.
|
||||
|
||||
recuerda: al ser una runa de "dirección literal" va a agregar una instrucción LIT antes de la correspondiente dirección :)
|
||||
|
||||
podemos reescribir nuestro "programa hello world" como sigue:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
|
||||
( devices )
|
||||
|10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ]
|
||||
|
||||
( main program )
|
||||
|0100 LIT 'h .Console/write DEO
|
||||
LIT 'e .Console/write DEO
|
||||
LIT 'l .Console/write DEO
|
||||
LIT 'l .Console/write DEO
|
||||
LIT 'o .Console/write DEO
|
||||
#0a .Console/write DEO ( newline )
|
||||
```
|
||||
|
||||
ahora esto empieza a parecerse más a los ejemplos que puedes encontrar en línea y/o en el repositorio uxn :)
|
||||
|
||||
## macros
|
||||
|
||||
siguiendo con la herencia de forth (?), en uxntal podemos definir nuestras propias "palabras" que nos permiten agrupar y reutilizar instrucciones.
|
||||
|
||||
durante el ensamblado, estos macros son (recursivamente) reemplazados por los contenidos de sus definiciones.
|
||||
|
||||
por ejemplo, podemos ver que el siguiente fragmento de código es repetido varias veces en nuestro programa.
|
||||
|
||||
```
|
||||
.Console/write DEO ( equivalent to #18 DEO, or LIT 18 DEO )
|
||||
```
|
||||
|
||||
podemos definir un macro llamado EMIT que tomará de la pila un byte correspondiente a un caracter, y lo imprimirá en la salida estandard. para esto, necesitamos la runa %, y llaves para la definición.
|
||||
|
||||
¡no olvides los espacios!
|
||||
|
||||
```
|
||||
( print a character to standard output )
|
||||
%EMIT { .Console/write DEO } ( character -- )
|
||||
```
|
||||
|
||||
para llamar a un macro, sólo escribimos su nombre:
|
||||
|
||||
```
|
||||
( print character h )
|
||||
LIT 'h EMIT
|
||||
```
|
||||
|
||||
podemos llamar macros dentro de macros, por ejemplo:
|
||||
|
||||
```
|
||||
( print a newline )
|
||||
%NL { #0a EMIT } ( -- )
|
||||
```
|
||||
|
||||
# un hello world más idiomático
|
||||
|
||||
usando todos estos macros y runas, nuestro programa puede terminar luciendo como lo siguiente:
|
||||
|
||||
```
|
||||
( hello.tal )
|
||||
( devices )
|
||||
|10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ]
|
||||
|
||||
( macros )
|
||||
( print a character to standard output )
|
||||
%EMIT { .Console/write DEO } ( character -- )
|
||||
( print a newline )
|
||||
%NL { #0a EMIT } ( -- )
|
||||
|
||||
( main program )
|
||||
|0100 LIT 'h EMIT
|
||||
LIT 'e EMIT
|
||||
LIT 'l EMIT
|
||||
LIT 'l EMIT
|
||||
LIT 'o EMIT
|
||||
NL
|
||||
```
|
||||
|
||||
termina siendo ensamblado en los mismos 30 bytes que los ejemplos de arriba, pero con suerte más legible y mantenible.
|
||||
|
||||
podemos "mejorar" este programa haciendo que un loop imprima los caracteres, pero estudiaremos eso más tarde :
|
||||
|
||||
# ejercicios
|
||||
|
||||
## reubicando EMIT
|
||||
|
||||
en nuestro programa previo, el macro EMIT es llamado justo después de empujar un caracter a la pila.
|
||||
|
||||
¿cómo reescribirías el programa para empujar primero todos los caracteres, y luego "EMIT"ir todos ellos en una secuencia como ésta?
|
||||
|
||||
```
|
||||
EMIT EMIT EMIT EMIT EMIT
|
||||
```
|
||||
|
||||
## imprimir un dígito
|
||||
|
||||
si miras en la tabla ascii, verás que esl código hexadecimal 30 corresponde al dígito 0, 31 al dígito 1, siguiendo hasta el 39 que corresponde al dígito 9.
|
||||
|
||||
define un macro PRINT-DIGIT que toma un número (del 0 al 9) de la pila, e imprime el correspondiente dígito en la salida estandard.
|
||||
|
||||
```
|
||||
%PRINT-DIGIT { } ( number -- )
|
||||
```
|
||||
|
||||
# instrucciones del día 1
|
||||
|
||||
éstas son las instrucciones que cubrimos hoy:
|
||||
|
||||
* ADD: toma los dos elementos superiores de la pila, los suma, y empuja el resultado ( a b -- a+b )
|
||||
* SUB: toma los dos elementos superiores de la pila, los resta, y empuja el resultado ( a b -- a-b )
|
||||
* LIT: empuja el siguiente byte en memoria a la pila
|
||||
* DEO: escribe el byte dado en la dirección de dispositivo dada, tomando ambos de la pila ( byte address -- )
|
||||
|
||||
# day 2
|
||||
|
||||
¡mantente al tanto por las siguientes secciones del {tutorial uxn}!
|
||||
|
||||
# apoyo
|
||||
|
||||
si este tutorial te ha resultado de ayuda, considera compartirlo y brindarle tu {apoyo} :)
|
||||
|
Loading…
Reference in New Issue