Compila y potencia tus juegos en BASIC para Amstrad

FAst BAsic COMpiler (FABACOM)En un microordenador de 64Kb y 3,3 Mhz como el Amstrad CPC la velocidad de ejecución es fundamental en todas nuestras creaciones. Aún más importante resulta si estamos programando videojuegos y queremos que sean jugables. El Locomotive BASIC de los Amstrad CPC es muy potente como lenguaje, pero no deja de ser interpretado, siendo inviablemente lento para muchos posibles desarrollos.

No es necesario bajar a C, Ensamblador o Código Máquina si queremos más velocidad. Existe un compilador de Locomotive BASIC, llamado FABACOM (FAst BAsic COMpiler) que nos puede ayudar a conseguir multiplicar por 8 o 10 la velocidad a la que funcionan nuestros programas en BASIC. Además, al ser específico de Locomotive BASIC, podemos compilar todos nuestros programas hechos anteriormente sin cambiar una sola coma. En este post os descubrimos esta fantástica herramienta, os enseñamos a utilizarla y, además, os añadimos detalles técnicos de uso para sacarle el máximo rendimiento.

FAst BAsic COMpiler (FABACOM)

Portada Hisoft TurboBASIC
Portada Hisoft TurboBASIC

FABACOM es un compilador de Locomotive BASIC desarrollado por Peter Hoepfner para DMV Software en 1988. FABACOM coge un programa escrito en BASIC y lo convierte en un fichero binario de código máquina que puede ser ejecutado directamente en nuestros Amstrad CPC. Al ser un binario, se ejecuta directamente, sin ser interpretado, por lo que la velocidad de ejecución puede ser entre 8 y 10 veces mayor, según los casos.

La gran ventaja de FABACOM respecto a otros compiladores de BASIC de la época es que FABACOM puede utilizar números en coma flotante (números decimales). Otros compiladores de Locomotive BASIC como Laser Basic (Ocean, 1985) o Hisoft Turbo BASIC (Hisoft, 1986) incluyen distintas características como nuevos comandos o editores de sprites, pero no son capaces de manejar la aritmética en punto flotante. Además, FABACOM también soporta operaciones en cassette y disco (escribir y leer ficheros), algo no incluído en Hisoft Turbo BASIC, por ejemplo.

Compilando un programa con FABACOM

Compilar un programa escrito en BASIC con FABACOM es muy sencillito. Vamos a verlo paso a paso:

  1. Descargamos los ficheros que necesitamos:
  2. Arrancamos el emulador e introducimos el disco de FABACOM (Ctrl+F1 en WinAPE y elegimos el fichero)
  3. Escribimos un pequeño programa de prueba en BASIC y lo salvamos con SAVE“prueba.bas”.
    • Aquí tienes un par de programas de ejemplo: anima.bas, pinta.bas
    • Al teclear CAT nos debe aparecer el fichero BC.COM, de 23K, y nuestro programa prueba.bas.
  4. Tecleamos: bc prueba.bas, para compilar nuestro programa.
    • Si nuestro programa tuviera algún error, nos aparecerían mensajes indicándolo. Deberíamos volver al paso 2, corregir el error y volver a salvar el programa prueba.bas
    • Si todo ha ido bien, tendremos un nuevo fichero prueba.bin, que contendrá nuestro programa compilado (puedes comprobarlo tecleando DIR)
  5. Reiniciamos el Amstrad CPC (Ctrl+F9 en WinAPE)
  6. Ya podemos ejecutar el programa compilado, tecleando RUN “prueba.bin”
Ejemplo de compilación con FABACOM
Ejemplo de compilación

Si quieres, puedes probar estos ejemplos en nuestro emulador de CPC online.

Con estos sencillos pasos tendremos una versión binaria de nuestros programas que nos aportará un importante extra de velocidad de ejecución. Este extra de velocidad dependerá mucho de cómo hayamos hecho nuestro programa: puedes comprobarlo con los dos programas de ejemplo que hemos puesto. Distintas formas de programar darán lugar a mejores o peores resultados. Por eso, en las siguientes secciones os explicamos los detalles que debéis tener en cuenta para conseguir los mejores resultados.

Opciones de configuración para mejorar el binario generado

El compilador FABACOM cuenta con 2 modificadores para cambiar la forma en que se genera el fichero compilado. Son los siguientes:

  1. /L : Elimina la información de números de línea del archivo compilado. Si no se pone este modificador, el archivo binario generado almacena información de las líneas de BASIC que se están ejecutando en cada momento. Esta información puede ser útil en caso de errores o terminaciones indebidas de nuestros programas. Casi siempre vamos a querer añadir este modificador para ahorrar memoria y ganar algo de velocidad de ejecución.
  2. /E : No incluye las librerías de control de eventos en el binario generado. Estas librerías se utilizan para el funcionamiento de los temporizadores que usan EVERY y AFTER. Debemos usar este modificador siempre que nuestros programas no utilicen estas instrucciones de temporización. Gracias a este modificador podremos llegar a ahorrar hasta 8-10K de memoria.

Así, por ejemplo, podríamos usar los modificadores de esta forma:

  • bc prueba.bas /L : crea el archivo prueba.bin, sin información de números de línea.
  • bc prueba.bas /E : no incluye la librería de eventos.
  • bc prueba.bas /LE : no incluye números de línea ni librería de eventos en el binario.

Mejorando el rendimiento de nuestros programas con FABACOM

Para poder obtener el máximo rendimiento del compilador FABACOM, hay que tener en cuenta la forma en que funciona internamente. FABACOM tiene una librería de funciones escritas en C y precompiladas en código máquina. Esta librería se añade al fichero binario generado cada vez que compilamos. Esto es muy fácil de comprobar. Podemos verlo con un programa muy simple en BASIC, como este:

10 PRINT "hola":GOTO 10

Resultado de compilación con FABACOM
Resultado de compilación en tamaño de archivo

Como podemos ver en la imagen a la derecha, el binario que resulta de compilar este simple programa de 1K en BASIC nos ocupa 9K, lo que significa que las librerías mínimas que añade FABACOM son 8K. FABACOM añade distintas librerías en función de lo que nuestro programa utilice, por lo que estos +8K pueden variar, llegando a ser hasta +20K en algunos casos. Es por esto que es muy importante decirle a FABACOM que sólo nos añada lo imprescindible, usando los modificadores /L y /E. De hecho, os recomendamos que utilicéis siempre ambos modificadores, compilando con /LE.

Por otra parte, es imprescindible tener en cuenta que FABACOM utiliza el sistema operativo y las rutinas del firmware como soporte. Cuando hagamos un PRINT en nuestro programa, el binario compilado utilizará por debajo las mismas rutinas del firmware para pintar en pantalla que BASIC. Esto significa que la ganancia de velocidad en estas instrucciones no será muy grande, ya que terminaremos usando el código del firmware la mayor parte del tiempo en ambas opciones. Aún así, siempre habrá ganancia.

Donde más vamos a notar ganancia va a ser en las instrucciones que tengan más carga interpretada y menos uso del firmware. Notaremos mucha ganancia en los cálculos con números enteros (de 5 a 6 veces más rápido) y las operaciones en bucles (FOR, WHILE), llamadas a subrutinas (GOSUB), acceso a variables, arrays y cadenas e instrucciones que funcionen directamente sobre la memoria como POKE o PEEK. Sobre todo, y dado que los accesos a memoria mejoran mucho (pues son directos, muchas veces), utilizar arrays y punteros es especialmente valioso y rápido.

El ejemplo más claro de optimización lo tenemos a la hora de pintar cosas en pantalla (algo fundamental en nuestros videojuegos). Aquí podemos dibujar utilizando rutinas gráficas como PLOT y DRAW, utilizando carácteres mediante PRINT, CHR$ y cadenas, o utilizando POKE directamente sobre la memoria de vídeo (entre las posiciones &C000 y &FFFF). PRINT y CHR$ pueden llegar a ser entre 2 y 4 veces más rápidos que PLOT y DRAW, mientras que usar POKE directamente puede ser hasta 8 veces más rápido que PRINT y CHR$. En nuestras pruebas, hemos conseguido aumentar el rendimiento de 75 a 571 Fotogramas por Segundo, dibujando un único sprite de 8×8 píxeles. A continuación tenéis un ejemplo:

Tests de dibujado de sprites compilado con FABACOM
Tests de sprites compilado

Ejemplo online

Pruebas de dibujado de sprites con distintos métodos, compilado con FABACOM.

Limitaciones y fallos del compilador FABACOM

Junto con estas opciones, el compilador FABACOM tiene unas limitaciones que es importante conocer:

  • FABACOM no admite las palabras clave de funcionamiento de sistema en BASIC. La lista completa de palabras clave no soportadas (o con soporte parcial) es esta: AUTO, CHAIN, CHAIN MERGE, CONT, DELETE, EDIT, LIST, LOAD (programa en BASIC), MERGE, RENUM, RESUME NEXT, SAVE (programa en BASIC), TROFF, TRON.
  • Las palabras clave de BASIC 1.1. compilan y funcionan todas en ordenadores con BASIC 1.0., excepto FILL, MASK, y el modo transparente de GRAPHICS PEN, que sólo funcionarán en ordenadores con BASIC 1.1., ya que FABACOM llama a las rutinas del firmware para implementarlas.
  • Debido a que FABACOM hace uso del sistema operativo y el firmware, sólo tendremos disponible la memoria de usuario de BASIC para almacenar nuestro programa. Por tanto, el binario compilado no debe superar los 41K aproximadamente. En caso contrario, podremos obtener un bonito mensaje de Memory Full al ejecutar.

Por otra parte, en los experimentos que hemos realizado con el compilador, hemos podido comprobar que parece tener algunos bugs bastante importantes. Queremos destacar, eso sí, que todas las pruebas que hemos realizado han sido en diversos emuladores. Todavía no hemos podido hacer las pruebas con los ficheros binarios en ordenadores reales, para comprobar si son fallos del compilador o problema de los emuladores.

Los potenciales bugs que hemos encontrado, hasta la fecha, son los siguientes:

  • Portada superman DATAS corruptas
    Corrupción de información almacenada en DATAs con FABACOM (Arriba: versión compilada, abajo: versión original interpretada)
    Problemas con la información almacenada en DATAs: aunque el compilador soporta perfectamente DATA, READ y RESTORE, ante determinadas circunstancias, la información almacenada en las líneas de data parece corromperse. Hemos tenido casos en juegos hechos por nosotros, donde determinados dibujos no aparecen correctamente en la versión compilada por estar corrupta la información almacenada a las líneas DATA. Recomendamos usar carga directa en memoria en lugar de DATA.
  • Problemas con ON x/SQ(x) GOTO/GOSUB: estas construcciones sufren problemas desconocidos dependiendo del tamaño del programa y la disposición del mismo en memoria. En determinadas circunstancias, ON x/SQ(x) GOTO/GOSUB funcionan correctamente y, al hacer modificaciones a otras partes del programa que nada  tienen que ver, empiezan a mostrar un comportamiendo indefinido en la versión compilada. Recomandamos no utilizarlas para mayor seguridad.
  • No es posible leer la cola de sonidos con SQ(x): siempre que se intenta leer la cola de sonidos, se obtiene un 0 como respuesta. Creemos que se debe a que el compilador confunde la palabra reservada SQ con un nombre de variable y cree que SQ(x) es una posición de un array. No podemos asegurarlo, pero en las pruebas realizadas no ha sido posible utilizar SQ(x) para saber el estado de la cola de sonidos.
  • En ocasiones, el “;” no funciona correctamente con PRINT: quizá por algún bug en las librerías de procesado de cadenas de FABACOM, hay ocasiones en que el “;” al final de una orden PRINT no impide que el cursor salte de línea. No ocurre con mucha frecuencia, pero a veces ocurre. En el ejemplo de dibujado de sprites anterior sucede exactamente esto: hay un salto de línea que sólo se produce en la versión compilada con FABACOM, y no en la versión BASIC.

Conclusiones

FABACOM es uno de los pocos compiladores de Locomotive BASIC de los 80 que han llegado hasta nuestros días (en su versión 1.03, que es la que disponemos). Aunque el compilador no es perfecto, y tiene unos cuantos problemas, los resultados obtenidos con el mismo son bastante buenos en cuanto a rendimiento. Por este motivo, merece la pena dedicar un tiempo a entender los detalles del compilador para poder aprovechar al máximo sus capacidades y mejorar enormemente el rendimiento de nuestros programas escritos en BASIC.

Os animamos a probar FABACOM, compilar vuestros programas escritos en Locomotive BASIC y contarnos vuestros resultados. Estamos convencidos de que es una herramienta a la que se le puede sacar mucho jugo y que puede ser muy útil. Si encontráis nuevos bugs, mejores formas de usarlo, trucos, etc., o realizáis análisis que nos permitan entender mejor su funcionamiento, os animamos a que nos lo enviéis para que podamos publicarlo y compartirlo con todos.

Enlaces y descargas

2 comentarios en “Compila y potencia tus juegos en BASIC para Amstrad

    • In this post we were talking about Locomotive BASIC, which is somewhat different from ZX-Basic. We are not planning to devote posts to ZX-Basic for a while (maybe in the future), but it will be very interesting to know about projects related to ZX-Basic or Z80 computing in general :). Good luck on your adventure and, please, let us know about your achievements!

Deja un comentario