Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
cursos:z88dk:sprites3 [10-08-2007 10:19]
sromero
cursos:z88dk:sprites3 [24-02-2020 20:25]
falvarez
Línea 1: Línea 1:
 +Publicado originalmente en [[https://magazinezx.speccy.org/13/z88dk.html|MagazineZX número 13]] (enero 2006)
  
 ====== Sprites en Z88DK (III): La librería SpritePack ====== ====== Sprites en Z88DK (III): La librería SpritePack ======
 +
  
  
Línea 9: Línea 10:
 En artículos anteriores de la revista hemos visto como utilizar las funciones proporcionadas de base con la librería z88dk para la creación de sprites, aplicándolas para crear la semilla de lo que podría ser nuestro propio juego de coches. Sin embargo, nos encontrábamos con un par de contratiempos que, aunque no se indicaron de forma explícita, los podríamos descubrir a la hora de crear nuestras propias aplicaciones. Por una parte, la complejidad del diseño de los propios sprites; de hecho, nos teníamos que basar en una aplicación (sólo para Linux) para la creación de los mismos. Por otra parte, cambiar los atributos de color y fondo de los sprites, así como transparencias, es un tema no especialmente sencillo que ni siquiera se trató. En artículos anteriores de la revista hemos visto como utilizar las funciones proporcionadas de base con la librería z88dk para la creación de sprites, aplicándolas para crear la semilla de lo que podría ser nuestro propio juego de coches. Sin embargo, nos encontrábamos con un par de contratiempos que, aunque no se indicaron de forma explícita, los podríamos descubrir a la hora de crear nuestras propias aplicaciones. Por una parte, la complejidad del diseño de los propios sprites; de hecho, nos teníamos que basar en una aplicación (sólo para Linux) para la creación de los mismos. Por otra parte, cambiar los atributos de color y fondo de los sprites, así como transparencias, es un tema no especialmente sencillo que ni siquiera se trató.
  
-Para hacernos la vida más fácil a la hora de programar usando sprites disponemos de la librería Sprite Pack; la página principal del proyecto es http://www.geocities.com/aralbrec/. Se trata de piezas de código escritas en ensamblador, con una interfaz que puede ser usada desde C para su compilación con las herramientas de z88dk. Esto en cristiano quiere decir que podremos llamar a las funciones de la librería Sprite Pack desde nuestros programas z88dk de forma totalmente transparente, pero teniendo la seguridad de que el código será lo más eficiente posible.+Para hacernos la vida más fácil a la hora de programar usando sprites disponemos de la librería **Sprite Pack**; la página principal del proyecto es http://www.geocities.com/aralbrec/. Se trata de piezas de código escritas en ensamblador, con una interfaz que puede ser usada desde C para su compilación con las herramientas de z88dk. Esto en cristiano quiere decir que podremos llamar a las funciones de la librería Sprite Pack desde nuestros programas z88dk de forma totalmente transparente, pero teniendo la seguridad de que el código será lo más eficiente posible.
  
-Sprite Pack es una librería multipropósito, no centrada únicamente en la creación de sprites. Se compone de varios módulos distintos: creación de sprites, rutinas de entrada, intersecciones de rectángulos, tipos abstractos de datos, compresión de datos, API de interrupciones, etc. No solo podemos utilizarla con el Spectrum, sino que en teoría podríamos emplear todo este código en cualquier plataforma Z80, aunque existen dos módulos demasiado específicos que solo dispondremos para Spectrum y Timex, el de creación de sprites y el de lectura de dispositivos de entrada.+Sprite Pack es una librería multipropósito, no centrada únicamente en la creación de sprites. Se compone de varios módulos distintos: creación de sprites, rutinas de entrada, intersecciones de rectángulos, tipos abstractos de datos, compresión de datos, API de interrupciones, etc. No solo podemos utilizarla con el Spectrum, sino que en teoría podríamos emplear todo este código en cualquier plataforma Z80, aunque existen dos módulos demasiado específicos que solo dispondremos para //Spectrum// //Timex//, el de creación de sprites y el de lectura de dispositivos de entrada.
  
 Durante los siguientes artículos nos centraremos especialmente en el módulo de sprites, con la idea de ser utilizado en el seno de nuestras aplicaciones z88dk para Spectrum, aunque durante el desarrollo de nuestros ejemplos tendremos que ir usando también algunas pequeñas partes del resto de los módulos. Vamos a dar un pequeño paso atrás con respecto a entregas anteriores, pues vamos a aprender de nuevo a dibujar sprites desde el principio, aunque utilizando Sprite Pack en esta ocasión. Podría parecer que esto es un poco inútil, pero ya veremos más adelante que las ventajas de utilizar esta capa por encima de z88dk no tardarán en llegar. Durante los siguientes artículos nos centraremos especialmente en el módulo de sprites, con la idea de ser utilizado en el seno de nuestras aplicaciones z88dk para Spectrum, aunque durante el desarrollo de nuestros ejemplos tendremos que ir usando también algunas pequeñas partes del resto de los módulos. Vamos a dar un pequeño paso atrás con respecto a entregas anteriores, pues vamos a aprender de nuevo a dibujar sprites desde el principio, aunque utilizando Sprite Pack en esta ocasión. Podría parecer que esto es un poco inútil, pero ya veremos más adelante que las ventajas de utilizar esta capa por encima de z88dk no tardarán en llegar.
 +
 +
  
 ===== Instalación ===== ===== Instalación =====
Línea 23: Línea 26:
 Tras obtener el fichero comprimido, lo descomprimimos en cualquier directorio de trabajo. El resultado será la carpeta z88dk, en cuyo interior encontraremos a su vez otro directorio llamado work, en el que en último lugar descubriremos un nuevo directorio llamado splib2, en el que nos deberemos situar para realizar la compilación. Tras obtener el fichero comprimido, lo descomprimimos en cualquier directorio de trabajo. El resultado será la carpeta z88dk, en cuyo interior encontraremos a su vez otro directorio llamado work, en el que en último lugar descubriremos un nuevo directorio llamado splib2, en el que nos deberemos situar para realizar la compilación.
  
-Al tratarse de una librería para el manejo, entre otras cosas, de sprites de la pantalla, debemos comprobar que el código de la librería se va a compilar para las características peculiares del Spectrum antes de continuar. Lo único que tendremos que hacer será editar el fichero SPconfig.def, que es el fichero de configuración de la compilación, y asegurarnos de que el valor de DISP_SPECTRUM es 1 y el valor del resto de variables DISP_ (DISP_HICOLOUR, DISP_HIRES y DISP_TMXDUAL) es 0.+Al tratarse de una librería para el manejo, entre otras cosas, de sprites de la pantalla, debemos comprobar que el código de la librería se va a compilar para las características peculiares del Spectrum antes de continuar. Lo único que tendremos que hacer será editar el fichero SPconfig.def, que es el fichero de configuración de la compilación, y asegurarnos de que el valor de DISP_SPECTRUM es 1 y el valor del resto de variables DISP_ (**DISP_HICOLOUR, DISP_HIRES** **DISP_TMXDUAL**) es 0.
  
 Sprite Pack incluye un fichero Makefile.bat que creará la librería en una máquina Windows. Solamente sería necesario en este tipo de entorno que se abriera una ventana de MS-DOS, se acudiera al directorio donde está el código fuente, y tras inicializar las variables de entorno que permiten compilar con z88dk, teclear Makefile. En Linux es un poco más complicado, y deberemos modificar ese archivo para que funcione. Para ahorrar trabajo al lector, se incluye un fichero Makefile.sh, equivalente al Makefile.bat para Windows, que puede ser usado en Linux junto a este artículo. No se debe olvidar el proporcionar permiso de ejecución a dicho fichero para que lo podamos utilizar. El fichero sp.lst, que se encuentra en el directorio raíz del código fuente de la librería (junto a makefile.bat) tambien debe ser modificado para Linux; junto a este artículo se adjunta igualmente este archivo para poder ser utilizado en este sistema. Sprite Pack incluye un fichero Makefile.bat que creará la librería en una máquina Windows. Solamente sería necesario en este tipo de entorno que se abriera una ventana de MS-DOS, se acudiera al directorio donde está el código fuente, y tras inicializar las variables de entorno que permiten compilar con z88dk, teclear Makefile. En Linux es un poco más complicado, y deberemos modificar ese archivo para que funcione. Para ahorrar trabajo al lector, se incluye un fichero Makefile.sh, equivalente al Makefile.bat para Windows, que puede ser usado en Linux junto a este artículo. No se debe olvidar el proporcionar permiso de ejecución a dicho fichero para que lo podamos utilizar. El fichero sp.lst, que se encuentra en el directorio raíz del código fuente de la librería (junto a makefile.bat) tambien debe ser modificado para Linux; junto a este artículo se adjunta igualmente este archivo para poder ser utilizado en este sistema.
  
-Sea cual sea el sistema operativo que utilicemos, como resultado obtendremos un nuevo fichero splib2.lib, que deberemos copiar al interior del directorio lib\clibs dentro de nuestro directorio de instalación de z88dk. A su vez, deberemos copiar el fichero spritepack.h en el directorio include, también dentro del directorio de isntalación de z88dk. ¡Ya tenemos la librería lista para ser usada!. Solo será necesario añadir la línea #include "spritepack.h" en cada uno de los archivos .c en los que queramos hacer uso de las funcionalidades de la libreria, y compilarlos añadiendo -lsplib2 como uno de los parámetros de zcc.+Sea cual sea el sistema operativo que utilicemos, como resultado obtendremos un nuevo fichero **splib2.lib**, que deberemos copiar al interior del directorio lib\clibs dentro de nuestro directorio de instalación de z88dk. A su vez, deberemos copiar el fichero **spritepack.h** en el directorio include, también dentro del directorio de isntalación de z88dk. ¡Ya tenemos la librería lista para ser usada!. Solo será necesario añadir la línea //#include "spritepack.h"// en cada uno de los archivos .c en los que queramos hacer uso de las funcionalidades de la libreria, y compilarlos añadiendo //-lsplib2// como uno de los parámetros de zcc. 
  
 ===== Nuestro primer ejemplo ===== ===== Nuestro primer ejemplo =====
Línea 34: Línea 38:
 Sprite Pack tiene una forma particular de actualizar la pantalla. Realizar una actualización completa y simultánea de toda ella simultáneamente haría necesario el uso de rutinas en ensamblador demasiado específicas, lo cual chocaría con el objetivo real de la librería, que se pretende que sea multiplataforma. Es por ello que lo que realmente se hace es actualizar únicamente las porciones de la pantalla donde se ha producido algún cambio. Se dirá que aquellas partes de la pantalla que queramos redibujar deberán ser invalidadas. Aquellas partes que no queramos redibujar serán regiones validadas. Luego lo veremos con un ejemplo y lo entenderemos mejor, pero de momento nos debemos hacer a la idea de que nunca podremos hacer juegos con scroll total, ya sea horizontal o vertical, con esta librería. Deberemos centrarnos en juegos de plataformas o juegos limitados donde toda la acción se desarrolla sin scroll. Sprite Pack tiene una forma particular de actualizar la pantalla. Realizar una actualización completa y simultánea de toda ella simultáneamente haría necesario el uso de rutinas en ensamblador demasiado específicas, lo cual chocaría con el objetivo real de la librería, que se pretende que sea multiplataforma. Es por ello que lo que realmente se hace es actualizar únicamente las porciones de la pantalla donde se ha producido algún cambio. Se dirá que aquellas partes de la pantalla que queramos redibujar deberán ser invalidadas. Aquellas partes que no queramos redibujar serán regiones validadas. Luego lo veremos con un ejemplo y lo entenderemos mejor, pero de momento nos debemos hacer a la idea de que nunca podremos hacer juegos con scroll total, ya sea horizontal o vertical, con esta librería. Deberemos centrarnos en juegos de plataformas o juegos limitados donde toda la acción se desarrolla sin scroll.
  
-Otros conceptos que deberemos tener claros son el de backtile y sprite. Comprender la diferencia entre ambos es necesario para desarrollar nuestras aplicaciones. La pantalla está dividida en un array de tamaño 32x24 de celdas de caracteres (a su vez de tamaño 8x8). Cada una de estas celdas podrá contener sólo un backtile y uno o más sprites. Los backtiles se pueden entender como el "fondo", y en realidad se trata de UDGs coloreados de tamaño 8x8, que son escritos en la pantalla de la misma forma en la que lo son los UDGs en BASIC. Por otra parte, los sprites podrán ocupar cualquier posición de la pantalla, en incrementos de un pixel, y su tamaño podrá ser mayor que 8x8, aunque no tendremos tanta libertad como con z88dk y únicamente podrán tener tamaños múltiplos, tanto en número de filas como de columnas, de 8.+Otros conceptos que deberemos tener claros son el de **backtile** **sprite**. Comprender la diferencia entre ambos es necesario para desarrollar nuestras aplicaciones. La pantalla está dividida en un array de tamaño 32x24 de celdas de caracteres (a su vez de tamaño 8x8). Cada una de estas celdas podrá contener sólo un backtile y uno o más sprites. Los backtiles se pueden entender como el "fondo", y en realidad se trata de UDGs coloreados de tamaño 8x8, que son escritos en la pantalla de la misma forma en la que lo son los UDGs en BASIC. Por otra parte, los sprites podrán ocupar cualquier posición de la pantalla, en incrementos de un pixel, y su tamaño podrá ser mayor que 8x8, aunque no tendremos tanta libertad como con z88dk y únicamente podrán tener tamaños múltiplos, tanto en número de filas como de columnas, de 8.
  
-De momento nos centraremos en los backtiles. La función básica para dibujarlos es sp_PrintAtInv, que además de dibujar el carácter con el color de tinta y papel que deseemos, invalidadá la celda donde haya sido dibujado para que se redibuje al actualizar la pantalla. Existe otra función, sp_PrintAt, que hace exactamente lo mismo, pero sin invalidar la celda donde el backtile es dibujado. Todos los cambios en la pantalla se realizarán de forma simultánea al llamar a la función sp_updateNow.+De momento nos centraremos en los backtiles. La función básica para dibujarlos es **sp_PrintAtInv**, que además de dibujar el carácter con el color de tinta y papel que deseemos, invalidará la celda donde haya sido dibujado para que se redibuje al actualizar la pantalla. Existe otra función, **sp_PrintAt**, que hace exactamente lo mismo, pero sin invalidar la celda donde el backtile es dibujado. Todos los cambios en la pantalla se realizarán de forma simultánea al llamar a la función **sp_updateNow**.
  
 Veamos un ejemplo. El siguiente código se encarga de dibujar letras 'x' en posiciones al azar de la pantalla, cambiando el color de la tinta y el papel, también al azar. Veamos un ejemplo. El siguiente código se encarga de dibujar letras 'x' en posiciones al azar de la pantalla, cambiando el color de la tinta y el papel, también al azar.
  
- +<code c> 
-#include  +#include <stdlib.h> 
-#include +#include <spritepack.h>
  
 #pragma output STACKPTR=61440 #pragma output STACKPTR=61440
Línea 68: Línea 72:
    }    }
 } }
 +</code>
  
 Veamos línea por línea de qué va todo esto. Las dos primeras se corresponden con las sentencias #include necesarias; en este caso la librería estándar y el archivo de cabecera de la librería Sprite Pack. Veamos línea por línea de qué va todo esto. Las dos primeras se corresponden con las sentencias #include necesarias; en este caso la librería estándar y el archivo de cabecera de la librería Sprite Pack.
  
-La sentencia #pragma deberemos incluirla siempre al principio de nuestros programas escritos para la librería Sprite Pack, antes del método main. Lo que se dice con ella es que se desea comenzar la ejecución del programa con el puntero de la pila del Z80 apuntando a la dirección 61440. Al hacerlo de esta forma, evitaremos que la función sp_initialize destruya cierta información de la memoria.+La sentencia **#pragma** deberemos incluirla siempre al principio de nuestros programas escritos para la librería Sprite Pack, antes del método main. Lo que se dice con ella es que se desea comenzar la ejecución del programa con el puntero de la pila del Z80 apuntando a la dirección 61440. Al hacerlo de esta forma, evitaremos que la función sp_initialize destruya cierta información de la memoria.
  
 Las primeras líneas de código en el interior de main(), entre el primer #asm y el último #endasm tendremos que ponerlas también siempre en nuestros programas que usen Sprite Pack. No es necesario entrar en detalle, pero diremos que este código tiene algo que ver con deshabilitar interrupciones para que ciertas funciones como sp_Invalidate funcionen. Las primeras líneas de código en el interior de main(), entre el primer #asm y el último #endasm tendremos que ponerlas también siempre en nuestros programas que usen Sprite Pack. No es necesario entrar en detalle, pero diremos que este código tiene algo que ver con deshabilitar interrupciones para que ciertas funciones como sp_Invalidate funcionen.
  
-Y por fin comienzan las líneas interesantes. La función sp_Initialize es necesaria para que el módulo de sprites de la librería comience a funcionar. En este caso, los parámetros hacen que la pantalla se inicialice escribiendo espacios en blanco, con color de fondo (paper) blanco y tinta negra (ink).+Y por fin comienzan las líneas interesantes. La función **sp_Initialize** es necesaria para que el módulo de sprites de la librería comience a funcionar. En este caso, los parámetros hacen que la pantalla se inicialice escribiendo espacios en blanco, con color de fondo (paper) blanco y tinta negra (ink).
  
-A continuación nos introducimos en el bucle principal, que se va a ejecutar de forma ininterrumpida por toda la eternidad (a menos que detengamos la ejecución del programa). La estructura es muy sencilla. Primero se llama a sp_UpdateNow para que se redibujen las celdas de la pantalla donde hubo algún cambio. A continuación se obiene un número al azar entre 1 y 10 (mediante la función rand()), y en función de su valor llamaremos a sp_PrintAtInv con unos parámetros u otros.+A continuación nos introducimos en el bucle principal, que se va a ejecutar de forma ininterrumpida por toda la eternidad (a menos que detengamos la ejecución del programa). La estructura es muy sencilla. Primero se llama a **sp_UpdateNow** para que se redibujen las celdas de la pantalla donde hubo algún cambio. A continuación se obiene un número al azar entre 1 y 10 (mediante la función rand()), y en función de su valor llamaremos a s**p_PrintAtInv** con unos parámetros u otros.
  
 El método sp_PrintAtInv dibuja un backtile en la pantalla e invalida la posición donde dicho backtile ha sido colocado, para que la celda correspondiente sea redibujada tras llamar a sp_UpdateNow. Los dos primeros parámetros indican la posición del backtile (que también los obtenemos al azar), a continuación el color de papel y de tinta (con la misma sintaxis que en el caso de la función sp_Initialize) y por último indicamos el carácter a escribir. Este carácter se corresponde con un UDG, como en BASIC. De momento no hemos asociado ningún UDG a la letra x, por lo que se mostrará en pantalla será la letra x apareciendo en posiciones al azar, teniendo o bien color de tinta rojo y papel cyan o al revés. El método sp_PrintAtInv dibuja un backtile en la pantalla e invalida la posición donde dicho backtile ha sido colocado, para que la celda correspondiente sea redibujada tras llamar a sp_UpdateNow. Los dos primeros parámetros indican la posición del backtile (que también los obtenemos al azar), a continuación el color de papel y de tinta (con la misma sintaxis que en el caso de la función sp_Initialize) y por último indicamos el carácter a escribir. Este carácter se corresponde con un UDG, como en BASIC. De momento no hemos asociado ningún UDG a la letra x, por lo que se mostrará en pantalla será la letra x apareciendo en posiciones al azar, teniendo o bien color de tinta rojo y papel cyan o al revés.
  
-La última línea del bucle, sp_Pause, introduce un pequeño retardo. Actúa exactamente igual que el comando PAUSE de BASIC. +La última línea del bucle, **sp_Pause**, introduce un pequeño retardo. Actúa exactamente igual que el comando PAUSE de BASIC.
-Espectaculares efectos gráficos conseguidos con la librería Sprite Pack +
-Espectaculares efectos gráficos conseguidos con la librería Sprite Pack+
  
-NUESTRO PRIMER EJEMPLO CON UN SPRITE+{{ cursos:z88dk:z88dk8_1.png |Espectaculares efectos gráficos conseguidos con la librería Sprite Pack}}
  
-En esta sección vamos a dibujar un sprite simple sobre un fondo un poco más complejo. Para comprender cómo podemos crear un fondo más complejo, hemos de recordar que los backtiles pueden entenderse como si fueran UDGs de BASIC; sin embargo, hasta ahora solo hemos utilizado caracteres alfanuméricos. ¿De qué forma definimos gráficos para estos backtiles? 
  
-Mediante la función sp_TileArray asociamos un determinado UDG 8x8 a un carácter, de tal forma que al escribir dicho carácter como backtile en la pantallaaparecerá su UDG correspondienteEsta función recibe como parámetro el carácter al que queremos asociar el UDG y un array de tipo uchar con la definición del UDG. Ese array contendrá un valor hexadecimal por cada fila del UDG, utilizando la misma notación que vimos en capítulos anteriores para la creación de sprites de tamaño 8x8 con z88dk. Por ejemplo, observemos el siguiente código:+===== Nuestro primer ejemplo con un Sprite ===== 
 + 
 + 
 +En esta sección vamos dibujar un sprite simple sobre un fondo un poco más complejo. Para comprender cómo podemos crear un fondo más complejohemos de recordar que los backtiles pueden entenderse como si fueran UDGs de BASIC; sin embargohasta ahora solo hemos utilizado caracteres alfanuméricos¿De qué forma definimos gráficos para estos backtiles?
  
 +Mediante la función **sp_TileArray** asociamos un determinado UDG 8x8 a un carácter, de tal forma que al escribir dicho carácter como backtile en la pantalla, aparecerá su UDG correspondiente. Esta función recibe como parámetro el carácter al que queremos asociar el UDG y un array de tipo uchar con la definición del UDG. Ese array contendrá un valor hexadecimal por cada fila del UDG, utilizando la misma notación que vimos en capítulos anteriores para la creación de sprites de tamaño 8x8 con z88dk. Por ejemplo, observemos el siguiente código:
  
 +<code c>
 uchar fondo[] = {0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa}; uchar fondo[] = {0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa};
  
 sp_TileArray(' ', fondo); sp_TileArray(' ', fondo);
 sp_Initialize(INK_WHITE | PAPER_BLACK, ' '); sp_Initialize(INK_WHITE | PAPER_BLACK, ' ');
 +</code>
  
 Con la primera línea creamos un array llamado fondo que contendrá la definición de un UDG de tipo "tablero de ajedrez" (un pixel negro, un pixel blanco, un pixel negro, un pixel blanco, y así sucesivamente). Con la instrucción sp_TileArray asociamos este UDG al carácter de espacio en blanco, de tal forma que cuando en la línea siguiente llamamos a sp_Initialize, el fondo se llenará de copias del UDG asociado a dicho carácter. De esta forma tan sencilla podremos tener fondos más detallados. Con la primera línea creamos un array llamado fondo que contendrá la definición de un UDG de tipo "tablero de ajedrez" (un pixel negro, un pixel blanco, un pixel negro, un pixel blanco, y así sucesivamente). Con la instrucción sp_TileArray asociamos este UDG al carácter de espacio en blanco, de tal forma que cuando en la línea siguiente llamamos a sp_Initialize, el fondo se llenará de copias del UDG asociado a dicho carácter. De esta forma tan sencilla podremos tener fondos más detallados.
Línea 101: Línea 109:
 ¿Y cómo definimos un sprite? De forma mucho más fácil a la vista en artículos anteriores, en los que se usaba z88dk sin ningún añadido. Nada más simple que una notación como la siguiente: ¿Y cómo definimos un sprite? De forma mucho más fácil a la vista en artículos anteriores, en los que se usaba z88dk sin ningún añadido. Nada más simple que una notación como la siguiente:
  
 +<code c>
 #asm #asm
  
Línea 115: Línea 123:
  
 #endasm #endasm
 +</code>
  
 Como se puede observar, volvemos a hacer uso de código ensamblador empotrado en el interior de nuestro código C, entre las directivas #asm y #endasm. En este caso concreto estamos definiendo un único sprite 8x8, para el cual necesitaremos dos bloques de bits de ese tamaño. El de la izquierda es el dibujo en sí mismo del sprite, que en nuestro caso en una pequeña carita con la boca abierta. El bloque de la derecha indica las transparencias; en aquellas posiciones donde el valor sea 1, el sprite será transparente y se verá lo que haya en el fondo. Evidentemente, es necesario que dichas posiciones tengan un valor 0 en el bloque de la izquierda. Como se puede observar, volvemos a hacer uso de código ensamblador empotrado en el interior de nuestro código C, entre las directivas #asm y #endasm. En este caso concreto estamos definiendo un único sprite 8x8, para el cual necesitaremos dos bloques de bits de ese tamaño. El de la izquierda es el dibujo en sí mismo del sprite, que en nuestro caso en una pequeña carita con la boca abierta. El bloque de la derecha indica las transparencias; en aquellas posiciones donde el valor sea 1, el sprite será transparente y se verá lo que haya en el fondo. Evidentemente, es necesario que dichas posiciones tengan un valor 0 en el bloque de la izquierda.
Línea 120: Línea 129:
 A este bloque de datos que marcan un sprite de 8x8 le hemos llamado sprite1. Este nombre nos servirá para referenciar este código ensamblador desde el código C. Para ver cómo dibujar el sprite en la pantalla, nada mejor que un pequeño ejemplo: A este bloque de datos que marcan un sprite de 8x8 le hemos llamado sprite1. Este nombre nos servirá para referenciar este código ensamblador desde el código C. Para ver cómo dibujar el sprite en la pantalla, nada mejor que un pequeño ejemplo:
  
- +<code c> 
-#include +#include <spritepack.h>
 #pragma output STACKPTR=61440 #pragma output STACKPTR=61440
  
Línea 133: Línea 142:
 extern uchar sprite1[]; extern uchar sprite1[];
 uchar fondo[] = {0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa}; uchar fondo[] = {0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa};
- 
  
  
Línea 149: Línea 157:
 main() main()
 { {
-           struct sp_SS *bicho;+       struct sp_SS *bicho;
  
 +       #asm
 +       di
 +       #endasm
 +       sp_InitIM2(0xf1f1);
 +       sp_CreateGenericISR(0xf1f1);
 +       #asm
 +       ei
 +       #endasm
  
-           #asm +       sp_TileArray(' ', fondo); 
-           di +       sp_Initialize(INK_WHITE | PAPER_BLACK, ' '); 
-           #endasm +       sp_Border(BLACK); 
-           sp_InitIM2(0xf1f1); +       sp_AddMemory(0, 255, 14, 0xb000);
-           sp_CreateGenericISR(0xf1f1); +
-           #asm +
-           ei +
-           #endasm+
  
-           sp_TileArray(' 'fondo); +       bicho = sp_CreateSpr(sp_MASK_SPRITE1, sprite1, 1, TRANSPARENT); 
-           sp_Initialize(INK_WHITE | PAPER_BLACK' '); +       sp_MoveSprAbs(bicho, sp_ClipStruct, 0, 10150, 0);
-           sp_Border(BLACK); +
-           sp_AddMemory(0, 255140xb000);+
  
-           bicho = sp_CreateSpr(sp_MASK_SPRITE, 1, sprite1, 1, TRANSPARENT); +       sp_UpdateNow();
-           sp_MoveSprAbs(bicho, sp_ClipStruct, 0, 10, 15, 0, 0);+
  
-           sp_UpdateNow(); +       while(1);
- +
-           while(1);+
 } }
  
Línea 187: Línea 194:
  
 #endasm #endasm
 +</code>
  
 Comentemos línea por línea el programa. La sentencia #pragma ya la conocemos del ejemplo anterior, así que no hace falta que nos detengamos en ella. A continuación se define una estructura de la forma siguiente: Comentemos línea por línea el programa. La sentencia #pragma ya la conocemos del ejemplo anterior, así que no hace falta que nos detengamos en ella. A continuación se define una estructura de la forma siguiente:
  
 +<code c>
 extern struct sp_Rect *sp_ClipStruct; extern struct sp_Rect *sp_ClipStruct;
 #asm #asm
Línea 196: Línea 204:
 ._sp_ClipStruct         defw SPCClipStruct ._sp_ClipStruct         defw SPCClipStruct
 #endasm #endasm
 +</code>
  
 Esta estructura tan confusa está sacada de spritepack.h y define un rectángulo que cubre toda la superficie de la pantalla. Existen muchas definiciones como ésta dentro de dicho fichero de cabecera. Para qué sirven lo veremos mucho más adelante, cuando tratemos el tema de las colisiones. De momento deberemos saber que lo necesitamos para poder dibujar el sprite en la pantalla. Esta estructura tan confusa está sacada de spritepack.h y define un rectángulo que cubre toda la superficie de la pantalla. Existen muchas definiciones como ésta dentro de dicho fichero de cabecera. Para qué sirven lo veremos mucho más adelante, cuando tratemos el tema de las colisiones. De momento deberemos saber que lo necesitamos para poder dibujar el sprite en la pantalla.
  
-La línea extern uchar sprite1[]; crea el array que contendrá la información del sprite. Este array se llena con los datos definidos entre las cláusulas #asm y #endasm al final del programa, después de la etiqueta ._sprite1, y contendrá la definición del sprite, de la misma forma en la que en la línea siguiente se crea un array de tipo uchar llamado fondo con la definición de un UDG para asociarlo al backtile de fondo.+La línea //extern uchar sprite1[]//; crea el array que contendrá la información del sprite. Este array se llena con los datos definidos entre las cláusulas #asm y #endasm al final del programa, después de la etiqueta ._sprite1, y contendrá la definición del sprite, de la misma forma en la que en la línea siguiente se crea un array de tipo uchar llamado fondo con la definición de un UDG para asociarlo al backtile de fondo.
  
 El siguiente código: El siguiente código:
  
 +<code c>
 void *my_malloc(uint bytes) void *my_malloc(uint bytes)
 { {
Línea 212: Línea 221:
 void *u_malloc = my_malloc; void *u_malloc = my_malloc;
 void *u_free = sp_FreeBlock; void *u_free = sp_FreeBlock;
 +</code>
  
 entra dentro de lo que estaremos obligados a insertar en nuestro programa cada vez que queramos añadirle sprites, aunque no entendamos muy bien de qué se trata. Se obtiene a partir del módulo de manejo de memoria de Sprite Pack y sirve para que el programa pueda obtener memoria bajo demanda. Esto es necesario debido a la forma en que la pantalla es actualizada al usar esta librería. Además de las estructuras creadas por el programador, la librería creará otras propias durante la ejecución y la memoria para dichas estructuras se obtendrá por medio de esta función. entra dentro de lo que estaremos obligados a insertar en nuestro programa cada vez que queramos añadirle sprites, aunque no entendamos muy bien de qué se trata. Se obtiene a partir del módulo de manejo de memoria de Sprite Pack y sirve para que el programa pueda obtener memoria bajo demanda. Esto es necesario debido a la forma en que la pantalla es actualizada al usar esta librería. Además de las estructuras creadas por el programador, la librería creará otras propias durante la ejecución y la memoria para dichas estructuras se obtendrá por medio de esta función.
  
-Y por fin comienza el método main. En primer lugar definimos la variable que va a contener el sprite que vamos a mostrar por pantalla. Esta variable es de tipo struct sp_SS, una estructura que contiene diversa infomación sobre un sprite, como su localización y su tamaño. A continuación, entre las directivas #asm y #endasm, el código ensamblador necesario en cada uno de nuestros programas Sprite Pack del que hablamos en la sección anterior.+Y por fin comienza el método **main**. En primer lugar definimos la variable que va a contener el sprite que vamos a mostrar por pantalla. Esta variable es de tipo **struct sp_SS**, una estructura que contiene diversa infomación sobre un sprite, como su localización y su tamaño. A continuación, entre las directivas #asm y #endasm, el código ensamblador necesario en cada uno de nuestros programas Sprite Pack del que hablamos en la sección anterior.
  
 Y continuamos con las dos líneas siguientes, cuyo funcionamiento ha sido explicado anteriormente: Y continuamos con las dos líneas siguientes, cuyo funcionamiento ha sido explicado anteriormente:
  
 +<code c>
 p_TileArray(' ', fondo); p_TileArray(' ', fondo);
 sp_Initialize(INK_WHITE | PAPER_BLACK, ' '); sp_Initialize(INK_WHITE | PAPER_BLACK, ' ');
 +</code>
  
-La línea posterior es nueva, y permite definir el color del borde de la pantalla (como la instrucción BORDER de BASIC). A continuación otra línea un tanto complicada, correspondiente a la llamada a la función sp_AddMemory, con la que se reserva memoria para los sprites. En concreto estamos reservando 255 bloques de 14 bytes a partir de la dirección 0xb000 que se sabe que está libre. Para cada sprite deberíamos reservar un par de bloques de 14 bytes, por lo que 255 es algo desproporcionado para nustro ejemplo. Si nos vemos apurados de memoria podemos reservar menos en este paso.+La línea posterior es nueva, y permite definir el color del borde de la pantalla (como la instrucción BORDER de BASIC). A continuación otra línea un tanto complicada, correspondiente a la llamada a la función **sp_AddMemory**, con la que se reserva memoria para los sprites. En concreto estamos reservando 255 bloques de 14 bytes a partir de la dirección 0xb000 que se sabe que está libre. Para cada sprite deberíamos reservar un par de bloques de 14 bytes, por lo que 255 es algo desproporcionado para nustro ejemplo. Si nos vemos apurados de memoria podemos reservar menos en este paso.
  
 Las líneas que más nos interesan de este ejemplo son las siguientes: Las líneas que más nos interesan de este ejemplo son las siguientes:
  
 +<code c>
 bicho = sp_CreateSpr(sp_MASK_SPRITE, 1, sprite1, 1, TRANSPARENT); bicho = sp_CreateSpr(sp_MASK_SPRITE, 1, sprite1, 1, TRANSPARENT);
 sp_MoveSprAbs(bicho, sp_ClipStruct, 0, 10, 15, 0, 0); sp_MoveSprAbs(bicho, sp_ClipStruct, 0, 10, 15, 0, 0);
 +</code>
  
-Con la primera de ellas creamos el sprite en si mismo. El resultado de la llamada se almacenará en la variable de tipo struct sp_SS bicho declarada anteriormente, que usaremos en el resto de métodos con los que queramos manipular dicho sprite. El primer parámetro (para el que nosotros hemos usado MASK, pero que podría ser XOR, OR o LOAD) indica la forma en la que el sprite se dibuja en la pantalla. El tipo MASK es el más lento de dibujar, y utiliza una máscara para determinar que porciones del sprite serán transparentes (tal como se ha visto anteriormente). Los tipos OR y XOR se corresponderían con los ya vistos en artículos anteriores. El siguiente parámetro indica el número de rejillas 8x8 que forman el sprite (se hablará de esto más adelante). A continuación, el nombre del array de tipo uchar que contiene la definición del sprite. En tercer lugar, el plano que ocupará el sprite en pantalla. El valor de este parámetro podrá valer entre 0 y 63. Cuanto mas bajo sea este valor más cerca del usuario se encontrará el sprite, de tal forma que sprites con valores bajos del tercer parámetro serán situados "encima" de sprites con valores altos, tapándolos. El cuarto parámetro tiene que ver con el color, y lo veremos también más adelante. Hemos usado el valor TRANSPARENT para que no influya en el color del fondo.+Con la primera de ellas creamos el sprite en si mismo. El resultado de la llamada se almacenará en la variable de tipo struct sp_SS bicho declarada anteriormente, que usaremos en el resto de métodos con los que queramos manipular dicho sprite. El primer parámetro (para el que nosotros hemos usado MASK, pero que podría ser XOR, OR o LOAD) indica la forma en la que el sprite se dibuja en la pantalla. El tipo **MASK** es el más lento de dibujar, y utiliza una máscara para determinar que porciones del sprite serán transparentes (tal como se ha visto anteriormente). Los tipos OR y XOR se corresponderían con los ya vistos en artículos anteriores. El siguiente parámetro indica el número de rejillas 8x8 que forman el sprite (se hablará de esto más adelante). A continuación, el nombre del array de tipo uchar que contiene la definición del sprite. En tercer lugar, el plano que ocupará el sprite en pantalla. El valor de este parámetro podrá valer entre 0 y 63. Cuanto mas bajo sea este valor más cerca del usuario se encontrará el sprite, de tal forma que sprites con valores bajos del tercer parámetro serán situados "encima" de sprites con valores altos, tapándolos. El cuarto parámetro tiene que ver con el color, y lo veremos también más adelante. Hemos usado el valor TRANSPARENT para que no influya en el color del fondo.
  
-Situaremos el sprite creado sobre la pantalla mediante el uso de la función sp_MoveSprAbs, que desplaza nuestro personaje a una posición absoluta de la pantalla. Podríamos utilizar sp_MoveSprRel, que acepta el mismo número de parámetros, con la diferencia de mover el sprite a una posición relativa a la actual. Por lo tanto, siempre usaremos sp_MoveSprAbs para colocar al sprite en su posición inicial.+Situaremos el sprite creado sobre la pantalla mediante el uso de la función **sp_MoveSprAbs**, que desplaza nuestro personaje a una posición absoluta de la pantalla. Podríamos utilizar **sp_MoveSprRel**, que acepta el mismo número de parámetros, con la diferencia de mover el sprite a una posición relativa a la actual. Por lo tanto, siempre usaremos sp_MoveSprAbs para colocar al sprite en su posición inicial.
  
 Como primer parámetro, el sprite a mover. Como segundo parámetro, el rectángulo que hace referencia a toda la pantalla definido anteriormente. El tercer parámetro hace referencia a la animación, y tampoco hablaremos de él en esta ocasión. El cuarto y el quinto, al bloque de la pantalla donde situaremos el sprite. Decíamos anteriormente que la pantalla estaba dividida en bloques de 8x8; pues bien, con estos dos parámetros indicamos la celda donde colocaremos nuestro sprite. Para conseguir una colocación más exacta, podemos usar los dos últimos parámetros, correspondientes al offset. Indican cuantos píxeles mover hacia la derecha y hacia abajo, respectivamente, a partir de la celda cuya posición es la indicada por los dos parámetros anteriores. Como primer parámetro, el sprite a mover. Como segundo parámetro, el rectángulo que hace referencia a toda la pantalla definido anteriormente. El tercer parámetro hace referencia a la animación, y tampoco hablaremos de él en esta ocasión. El cuarto y el quinto, al bloque de la pantalla donde situaremos el sprite. Decíamos anteriormente que la pantalla estaba dividida en bloques de 8x8; pues bien, con estos dos parámetros indicamos la celda donde colocaremos nuestro sprite. Para conseguir una colocación más exacta, podemos usar los dos últimos parámetros, correspondientes al offset. Indican cuantos píxeles mover hacia la derecha y hacia abajo, respectivamente, a partir de la celda cuya posición es la indicada por los dos parámetros anteriores.
  
-Por último, con sp_UpdateNow() redibujamos las porciones de la pantalla que lo necesiten, y con while(1) dejamos nuestro programa en constante ejecución, hasta el resto de los días, o hasta que lo detengamos. El resultado se puede observar en la siguiente captura de pantalla. +Por último, con **sp_UpdateNow()** redibujamos las porciones de la pantalla que lo necesiten, y con while(1) dejamos nuestro programa en constante ejecución, hasta el resto de los días, o hasta que lo detengamos. El resultado se puede observar en la siguiente captura de pantalla. 
-Un terrible ser intergaláctico espera ansioso la orden de atacar + 
-Un terrible ser intergaláctico espera ansioso la orden de atacar+{{ cursos:z88dk:z88dk8_2.png |Un terrible ser intergaláctico espera ansioso la orden de atacar}} 
 + 
 + 
 + 
 + 
 +===== Un sprite grande y nervioso =====
  
-UN SPRITE GRANDE Y NERVIOSO 
  
 En este apartado veremos un pequeño ejemplo que introduce algunos conceptos interesantes, como la creación de sprites de un tamaño superior a 8x8 y el uso del movimiento relativo. Vamos a hacer aparecer una criatura en nuestra pantalla que se mueva de forma nerviosa y aleatoria. En este apartado veremos un pequeño ejemplo que introduce algunos conceptos interesantes, como la creación de sprites de un tamaño superior a 8x8 y el uso del movimiento relativo. Vamos a hacer aparecer una criatura en nuestra pantalla que se mueva de forma nerviosa y aleatoria.
Línea 249: Línea 265:
 Por ejemplo, mostramos como quedaría un sprite con un tamaño de dos bloques de alto por dos bloques de ancho listo para ser usado con Sprite Pack: Por ejemplo, mostramos como quedaría un sprite con un tamaño de dos bloques de alto por dos bloques de ancho listo para ser usado con Sprite Pack:
  
 +<code c>
 #asm #asm
  
Línea 291: Línea 307:
  
 #endasm #endasm
 +</code>
  
 Como se puede observar, definimos dos columnas para el sprite, cada una de ellas formada a su vez por dos sprites de tamaño 8x8, incluyendo su máscara de transparencias. Para utilizar el sprite en el código deberíamos hacer algo similar a esto: Como se puede observar, definimos dos columnas para el sprite, cada una de ellas formada a su vez por dos sprites de tamaño 8x8, incluyendo su máscara de transparencias. Para utilizar el sprite en el código deberíamos hacer algo similar a esto:
  
 +<code c>
 extern uchar bicho1[]; extern uchar bicho1[];
 extern uchar bicho2[]; extern uchar bicho2[];
Línea 306: Línea 323:
          sp_MoveSprAbs(spriteBicho, sp_ClipStruct, 0, 10, 15, 0, 0);          sp_MoveSprAbs(spriteBicho, sp_ClipStruct, 0, 10, 15, 0, 0);
 } }
 +</code>
  
-Se debe hacer uso, en primer lugar, de sp_CreateSpr para asignar la primera columna del sprite a la variable de tipo struct sp_SS, y en segundo lugar, de sp_AddColSpr, tantas veces como columnas adicionales debamos añadir. En este caso, se ha usado un valor de 2 para el segundo parámetro de sp_CreateSpr, indicando que cada columna del sprite tendrá un tamaño de dos bloques de 8x8. Efectivamente, este segundo parámetro indica el número de bloques que tendrá cada columna de nuestro sprite; por eso en nuestro primer ejemplo le dimos valor 1. Una vez se ha creado el sprite con sp_CreateSpr y se han añadido las columnas correspondientes con sp_AddColSpr, se podrá tratar la estructura sp_SS resultante como un todo, tal como se demuestra en la llamada a sp_MoveSprAbs que sigue a las dos líneas anteriores.+Se debe hacer uso, en primer lugar, de **sp_CreateSpr** para asignar la primera columna del sprite a la variable de tipo **struct sp_SS**, y en segundo lugar, de **sp_AddColSpr**, tantas veces como columnas adicionales debamos añadir. En este caso, se ha usado un valor de 2 para el segundo parámetro de sp_CreateSpr, indicando que cada columna del sprite tendrá un tamaño de dos bloques de 8x8. Efectivamente, este segundo parámetro indica el número de bloques que tendrá cada columna de nuestro sprite; por eso en nuestro primer ejemplo le dimos valor 1. Una vez se ha creado el sprite con sp_CreateSpr y se han añadido las columnas correspondientes con sp_AddColSpr, se podrá tratar la estructura sp_SS resultante como un todo, tal como se demuestra en la llamada a sp_MoveSprAbs que sigue a las dos líneas anteriores.
  
 Es importante destacar que todas las columnas de un mismo sprite deben de ser definidas de forma contigua en la memoria. Esto se traduce en que tenemos que definirlas de forma contigua también en nuestro código. Es importante destacar que todas las columnas de un mismo sprite deben de ser definidas de forma contigua en la memoria. Esto se traduce en que tenemos que definirlas de forma contigua también en nuestro código.
Línea 313: Línea 331:
 Sin embargo, hay algo que hasta ahora no hemos tenido en cuenta, y es debido a que no hemos movido nuestros sprites por la pantalla. Cuando trasladamos sprites usando el pixel y no el bloque como unidad de medida (los dos últimos parámetros de sp_MoveSprAbs y sp_MoveSprRel servían para esto) veremos como los sprites no son correctamente dibujados; solo se redibuja la parte del sprite más a la izquierda que cabe dentro de una misma celdilla de la pantalla. Un truco para evitar esto es crear sprites un poco más anchos y más altos de lo que realmente necesitamos. Para ello, añadimos una nueva columna en blanco, y en cada columna, un nuevo bloque en blanco al final. En el caso concreto de nuestro sprite 2x2 anterior, deberíamos definirlo de esta forma: Sin embargo, hay algo que hasta ahora no hemos tenido en cuenta, y es debido a que no hemos movido nuestros sprites por la pantalla. Cuando trasladamos sprites usando el pixel y no el bloque como unidad de medida (los dos últimos parámetros de sp_MoveSprAbs y sp_MoveSprRel servían para esto) veremos como los sprites no son correctamente dibujados; solo se redibuja la parte del sprite más a la izquierda que cabe dentro de una misma celdilla de la pantalla. Un truco para evitar esto es crear sprites un poco más anchos y más altos de lo que realmente necesitamos. Para ello, añadimos una nueva columna en blanco, y en cada columna, un nuevo bloque en blanco al final. En el caso concreto de nuestro sprite 2x2 anterior, deberíamos definirlo de esta forma:
  
 +<code c>
 #asm #asm
  
Línea 401: Línea 419:
  
 #endasm #endasm
- +</code>
  
 Por lo tanto, nuestro sprite 2x2 se convierte en un sprite 3x3 al añadir una nueva columna a la derecha y un nuevo bloque en la parte inferior de cada columna. Nuestro código a la hora de usar el sprite debería ser en este caso algo más parecido a esto: Por lo tanto, nuestro sprite 2x2 se convierte en un sprite 3x3 al añadir una nueva columna a la derecha y un nuevo bloque en la parte inferior de cada columna. Nuestro código a la hora de usar el sprite debería ser en este caso algo más parecido a esto:
  
 +<code c>
 extern uchar bicho1[]; extern uchar bicho1[];
 extern uchar bicho2[]; extern uchar bicho2[];
Línea 419: Línea 437:
          sp_MoveSprAbs(spriteBicho, sp_ClipStruct, 0, 10, 15, 0, 0);          sp_MoveSprAbs(spriteBicho, sp_ClipStruct, 0, 10, 15, 0, 0);
 } }
 +</code>
  
 Como queda patente, indicamos que el tamaño en bloques de cada columna es 3 al llamar a la función sp_CreateSPr, y además se llama dos veces a sp_AddColSpr para añadir la segunda y la tercera columna a nuestro bicho. Como queda patente, indicamos que el tamaño en bloques de cada columna es 3 al llamar a la función sp_CreateSPr, y además se llama dos veces a sp_AddColSpr para añadir la segunda y la tercera columna a nuestro bicho.
  
-A continuación se muestra un ejemplo completo, con nuestro sprite moviéndose al azar por la pantalla, por medio de la función sp_MoveSprRel:+A continuación se muestra un ejemplo completo, con nuestro sprite moviéndose al azar por la pantalla, por medio de la función //sp_MoveSprRel//:
  
 +<code c>
 +#include <spritepack.h>
 +#include <stdlib.h>
  
-#include  
-#include  
 #pragma output STACKPTR=61440 #pragma output STACKPTR=61440
  
Línea 587: Línea 607:
  
 #endasm #endasm
 +</code>
  
 Ya deberíamos entender la práctica totalidad de este código, que mueve un sprite al azar por la pantalla, por lo que tan solo haremos dos apuntes: Ya deberíamos entender la práctica totalidad de este código, que mueve un sprite al azar por la pantalla, por lo que tan solo haremos dos apuntes:
Línea 593: Línea 614:
     * Se ha añadido un bloque vacío antes de definir nuestro sprite, al final del código anterior; esto es así porque debemos asegurarnos de que haya un bloque en blanco encima de cada columna (manías del ensamblador generado por Sprite Pack).     * Se ha añadido un bloque vacío antes de definir nuestro sprite, al final del código anterior; esto es así porque debemos asegurarnos de que haya un bloque en blanco encima de cada columna (manías del ensamblador generado por Sprite Pack).
  
-El terrible ser intergaláctico se ha hecho mayor y es más nervioso que antes... +{{ cursos:z88dk:z88dk8_3.png | El terrible ser intergaláctico se ha hecho mayor y es más nervioso que antes...}}
-El terrible ser intergaláctico se ha hecho mayor y es más nervioso que antes...+
  
 ===== ¿Y ahora qué? ===== ===== ¿Y ahora qué? =====
Línea 604: Línea 624:
  
 En el siguiente artículo añadiremos colores a nuestros sprites y aprenderemos a moverlos con el teclado. También veremos como borrar de forma efectiva un sprite. Si el espacio lo permite (y si no es así, que no cunda el pánico, pues se verá en números posteriores de la revista) codificaremos un simple juego funcional con todo lo aprendido. En el siguiente artículo añadiremos colores a nuestros sprites y aprenderemos a moverlos con el teclado. También veremos como borrar de forma efectiva un sprite. Si el espacio lo permite (y si no es así, que no cunda el pánico, pues se verá en números posteriores de la revista) codificaremos un simple juego funcional con todo lo aprendido.
 +
  
 ===== Enlaces ===== ===== Enlaces =====
  
  
-    * [[http://www.speccy.org/magazinezx/revistas/13/src/z88dk8_codigo.tar.gz|Código fuente]+    * [[https://magazinezx.speccy.org/13/src/z88dk8_codigo.tar.gz|Código fuente]]