¿Qué es el sdcc? ¿Y el Fusion-c?
El sdcc es un compilador cruzado (cross compiler) que nos permite programar en nuestra PC (Linux, Windows y Mac) y obtener un binario que se puede ejecutar en nuestro MSX (y otras plataformas que tienen el Z80 como el Spectrum, el CPC, etc., aunque no nos interesen tanto). Esto nos permite utilizar editores modernos para desarrollar para nuestras máquinas del siglo pasado.
El Fusion-c es una librería de código abierto desarrollada en sdcc y que tiene muchas funciones que nos permiten utilizar las capacidades de los MSX de forma sencilla. A fecha de 03/05/2023 se puede descargar la versión 1.2 desde la página web de Repro-Factory (https://www.ebsoft.fr/shop/) yendo a la sección de Fusion-C y descargando Fusion-C Tools y Fusion-C Library, que son totalmente gratuitos, solo es necesario registrarse.
Una vez que hayamos completado la compra (check out) del producto, debemos ir a nuestra cuenta y acceder a la sección de pedidos para hacer clic en el enlace de descarga.
El archivo MSX_Fusion-C-V1.2.zip contiene la biblioteca en C y el documento FUSION-C-Quick A4 1.2.pdf que explica cómo realizar la instalación. En el archivo Fusion-C_ToolsChain.zip encontramos el programa hex2bin para convertir a formato binario, el emulador openMSX, el sdcc 3.6 y la configuración para hacerlo funcionar con el editor Sublime.
¿Qué necesitamos para hacer nuestros programas?
Un editor
Cualquier software que nos permita editar un archivo de texto será suficiente para poder escribir el código en C de nuestra aplicación. Personalmente, utilizo emacs, uno de los editores más antiguos, con las capas de spacemacs. Otro miembro de los MoltSXalats utiliza Visual Studio. Este editor es el que se explica en el libro de Fusion-c y tiene la compilación y ejecución de openmsx integrados en el editor.
El compilador sdcc
Pueden descargar el compilador para diferentes versiones y plataformas aquí: https://sdcc.sourceforge.net/. Deben tener mucho cuidado de que la versión que descarguen corresponda a la plataforma que utilizan (Linux, Windows, Mac) y que sea la misma con la que se ha compilado Fusion-c. Por ejemplo, la versión 1.2 necesita sdcc 3.6.
Para compilar nuestro programa, utilizaremos el siguiente comando:
sdcc -V --code-loc 0x106 --data-loc 0x0 --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion-DOS.lib -L ./fusion-c/lib/ -I ./fusion-c/include/ ./fusion-c/lib/crt0_msxdos.rel {filename}
¿Qué significan todos estos parámetros? Vamos a explicar su significado:
-V se utiliza para indicar que lo queremos en modo verbose, de esta manera proporciona más información sobre lo que está haciendo.
--code-loc 0x106 se utiliza para indicar desde qué zona se cargará el binario en memoria cuando ejecutemos el archivo .com obtenido. En este caso, la dirección es 106, ya que la dirección 100 es donde comienza el código del programa en el sistema DOS (figura 3.1 https://konamiman.github.io/MSX2-Technical-Handbook/md/Chapter3.html) y los siguientes 6 bytes son variables para poder regresar a DOS desde nuestro programa, utilizando el crt0.
--data-loc 0x0 es el espacio que queremos dejar entre el código y las variables generadas por el compilador. Para que el archivo sea más compacto, las colocamos inmediatamente después del código, de ahí el 0.
--disable-warning 196 lo indicamos para que no aparezcan advertencias (warnings) relacionadas con el 196=’pointer target lost const qualifier' y que aparecen con las funciones printf.
-mz80 indicamos que compile para generar código binario (opcodes) del procesador z80.
--no-std-crt0 se utiliza para indicar que no utilizaremos el crt0 por defecto de la librería sdcc, sino que utilizaremos uno propio. Este crt0 es la parte inicial que hemos dejado, por eso comenzamos en 0x106. Se puede encontrar más información sobre el crt0 en https://www.konamiman.com/msx/msx-e.html#sdcc (konamiman) o http://msx.avelinoherrera.com/index_en.html (Avelino Herrera) (no sé cuál de los dos es el original). Hay diferentes crt0 según si el binario que queremos ejecutar es para ser ejecutado desde Basic, desde DOS o desde DOS con parámetros.
--opt-code-size se usa si se desea que el compilador optimice para reducir el tamaño. Si lo que queremos es incrementar la velocidad, utilizaremos este otro parámetro --opt-code-speed.
fusion-DOS.lib -L ./fusion-c/lib/ también debemos indicar las diferentes librerías que utilizamos, en este caso fusion-DOS.lib y la ubicación donde la encontraremos ./fusion-c/lib, después del parámetro -L.
-I ./fusion-c/include/ no debemos olvidar los includes que se han indicado dentro del archivo C a compilar. También debemos indicar dónde el compilador puede encontrarlos con el parámetro -I.
./fusion-c/lib/crt0_msxdos.rel este es el crt0 que queremos utilizar en esta compilación.
{filename} finalmente, el nombre del archivo que contiene el código C a compilar.
Eliminar las funciones printf, sprintf, vprintf, putchar y getchar
La biblioteca de sdcc que contiene los comandos del estándar C (for, if, int, etc.) también incluye las funciones fprintf, sprintf, vprintf, putchar y getchar, pero estas no son las ideales para nuestros MSX, como se indica en la página 25 del archivo PDF FUSION-C-Quick A4 1.2.pdf que se encuentra en el archivo .zip descargado desde el sitio web que contiene Fusion-C. En el caso de Linux, se debe hacer lo siguiente (para otras plataformas, consulta el documento Fusion-C-Quick):
sudo sdar -d /usr/share/sdcc/lib/z80/z80.lib vprintf.rel; sudo sdar -d /usr/share/sdcc/lib/z80/z80.lib getchar.rel; sudo sdar -d /usr/share/sdcc/lib/z80/z80.lib putchar.rel; sudo sdar -d /usr/share/sdcc/lib/z80/z80.lib sprintf.rel; sudo sdar -d /usr/share/sdcc/lib/z80/z80.lib printf.rel
La ruta /usr/share/sdcc/lib/z80/z80.lib es donde se encuentra la biblioteca z80.lib según el paquete instalado por la distribución OpenSUSE. Otras distribuciones utilizan otras rutas. Puedes realizar una búsqueda con el comando sudo find / -name "z80.lib". El comando sdar está incluido en sdcc y se utiliza para gestionar las bibliotecas. Una biblioteca no es más que una agrupación de archivos .rel. Los archivos .rel son binarios obtenidos con sdcc. Con el comando sdar -d lo que estamos haciendo es eliminar esos archivos de la biblioteca z80. De esta manera, cuando se compile, se utilizarán las funciones de Fusion-C.
Traductor a binario MSX
El binario resultante de la compilación no está en formato MSX, está en formato Intel Hexadecimal. Para realizar la traducción, utilizaremos el programa hex2bin. Este programa ya está incluido en las utilidades de Fusion-C. Desafortunadamente, la versión para Linux no me ha funcionado y la he descargado de aquí: https://hex2bin.sourceforge.net/. El comando que utilizaremos es:
hex2bin -e output_file.bin input_file.ihx
El parámetro -e es el nombre que le daremos a la extensión del archivo de salida una vez que se haya convertido a código MSX, en este caso, .com. El último parámetro es el nombre del archivo con la extensión .ihx que obtuvimos al compilar con sdcc.
El emulador openMSX
Para probar el código sin salir de la plataforma de desarrollo, utilizaremos el emulador openMSX de código abierto. Hay otros emuladores, pero este es de código abierto y aún recibe actualizaciones. Además, tiene un depurador con características muy buenas. Para instalar openMSX, puedes consultar este tutorial en video o seguir las instrucciones en la documentación oficial.
El comando para ejecutar el emulador es:
openmsx -machine Panasonic_FS-A1ST -diska path_to_our_virtual_disk/
Con el parámetro -machine, indicamos la máquina MSX que queremos emular, en este caso, un Turbo-R. Recuerda que puedes emular diferentes máquinas si tienes las ROM correspondientes y su descripción XML en el directorio de máquinas. El nombre de archivo de este archivo XML debe ir después del parámetro -machine.
El segundo parámetro, -diska, se utiliza para especificar que la unidad de disco es virtual y puede leer archivos desde la ruta especificada como path_to_our_virtual_disk/. Todos los archivos ubicados en este directorio serán visibles para el MSX emulado. Si utilizas el comando dir, los verás. Cualquier cambio realizado en el directorio se reflejará inmediatamente en el emulador y viceversa, en el directorio. Esto significa que si creas un archivo en el MSX emulado y lo guardas en lo que sería el disco MSX, aparecerá inmediatamente en el directorio especificado en la plataforma donde se está ejecutando el emulador. No es necesario reiniciar el MSX para ver los cambios. De esta manera, puedes mantener el emulador en ejecución mientras modificas continuamente tu archivo .COM. La única limitación es que los archivos en este directorio no pueden superar los 720K (la capacidad emulada por la unidad de disco).
Si prefieres un entorno gráfico para ejecutar openMSX, puedes utilizar Catapult (disponible para descargar en el sitio web de openMSX). Sin embargo, recuerda especificar que la unidad de disco es un directorio en tu computadora.
En el mismo sitio web, encontrarás un enlace para descargar el depurador. No requiere ningún parámetro para ejecutarse.
Primer programa en C
Para probar todo lo explicado anteriormente, puedes crear este archivo C:
El bloque B1 es donde indicamos todos los archivos .h donde se encuentran las definiciones de funciones que vamos a utilizar, en nuestro caso: Screen, Cls, Locate, etc. A continuación, está la función principal (main), que se ejecuta cuando se compila el archivo. En el bloque B2, indicamos que se debe establecer la pantalla en 0 y borrarla. Luego, en el bloque B3, especificamos que la posición de escritura es (10,10), y en esta coordenada escribiremos el texto "El meu primer programa en C!" (¡Mi primer programa en C!). Esperaremos a que se presione una tecla para continuar. Finalmente, saldremos del programa y regresaremos a DOS en el bloque B4.
Si nombramos este archivo como PrimProg.c, podemos compilarlo utilizando el comando mencionado en la sección "El compilador sdcc" reemplazando {fitxerc} por PrimProg.c.
De todos los archivos generados, tomaremos PrimProg.ihx y lo convertiremos a binario usando el comando hex2bin, donde nom_fitxer.ihx será PrimProg.ihx. Con esto, obtendremos el archivo PrimProg.com, que copiaremos en el directorio especificado por el parámetro dska al ejecutar openMSX. Esto es lo que obtendremos.
Pero, ¿qué son todos estos archivos generados?
El compilador sdcc genera muchos archivos además del archivo .ihx. Permíteme explicar cada uno de ellos:
.asm: Es el primer archivo generado por el compilador, que toma cada línea de código y la traduce al lenguaje ensamblador. También crea secciones para variables (datos) y código.
.sym: Este archivo lista todos los símbolos (variables) y funciones utilizadas en el archivo .asm.
.rel: Es el archivo compilado con opcodes, que es la traducción del archivo .asm a un código hexadecimal entendido por el procesador Z80. Si queremos crear una biblioteca o enlazar con otro archivo .c, este es el archivo que debemos agregar al comando sdcc.
.lst: Este archivo combina los tres archivos anteriores. Para cada línea de código, se puede encontrar su traducción al lenguaje ensamblador, y cada línea de ensamblador corresponde a su opcode Z80 y al número de bytes que ocupa. Aquí tienes un fragmento de nuestro archivo PrimProg.lst:
La Zona 1 representa el tamaño de la instrucción en hexadecimal, la Zona 2 representa el opcode (código de máquina) para el procesador Z80, y la Zona 3 representa la traducción de cada comando en nuestro código. Por ejemplo, si observamos la línea 58, 58 ;PrimProg.c:4: Screen(0);, indica que en la línea 4 de nuestro programa, se encuentra la función Screen(0), y en las líneas siguientes (59-63), podemos ver la traducción de esta línea al lenguaje ensamblador. En la parte 2, correspondiente a cada línea, tenemos su opcode.
.lk: Este archivo enumera los datos necesarios para el enlazador para vincular nuestro binario (.rel) con otros binarios de las bibliotecas que hemos utilizado.
.noi: Contiene las definiciones de diferentes funciones y variables. Supongo que el enlazador lo utiliza.
.map: La parte que nos interesa es la sección _CODE, donde encontramos todas las funciones del archivo .lst, pero con sus respectivas posiciones de memoria en el MSX. Para el programa anterior, obtenemos:
Donde podemos observar que nuestra función main se encuentra en la posición de memoria 106.
El depurador (debugger)
Los archivos .map y .lst nos permiten depurar nuestro código en caso de que ocurra algo inesperado. Veamos cómo funciona el depurador en nuestro primer programa. En el caso de Linux, lo invocaremos como openmsx-debugger, y se abrirá una ventana de aplicación, como se muestra a continuación:
La primera vez que lo ejecutemos, necesitaremos hacer clic en el rayo horizontal para conectarlo a un emulador de openMSX que esté en ejecución. Si solo hay una instancia del emulador, se conectará automáticamente, pero si hay varias instancias, tendremos que elegir a cuál conectarnos. Podemos hacer clic en el icono de pausa (dos barras verticales) para ver qué emulador se ha pausado. Una vez conectados, veremos que las diferentes partes de la pantalla se llenan de datos según los valores de la memoria y los registros de openMSX. Es importante tener en cuenta que estos valores solo se actualizan cuando el depurador está en el estado de punto de interrupción (indicado por el icono de círculo rojo). Para continuar la emulación, podemos hacer clic en el icono adyacente, el corredor azul. Si establecemos un punto de interrupción en la dirección 0x106 haciendo clic en el botón Agregar en la sección de lista de depuración e ingresando $106 (las direcciones hexadecimales se preceden con $ en el depurador), y luego ejecutamos nuestro programa, obtendremos el siguiente resultado:
Y observamos que la emulación se ha pausado en la dirección 0x106 donde el OpCode es xor a,a, y si miramos el archivo de listado (.lst), coincide con la primera línea de la función principal. Además, las instrucciones consecutivas también son los mismos opcodes que tenemos en el archivo .lst.
Es posible tener las líneas de código visibles en el depurador?
Sí, es posible tener las líneas de código visibles en el depurador. Existe una relación directa entre los comandos en el archivo .lst y el código en el depurador. Sería útil entender qué línea de nuestro código se está depurando sin tener que consultar el archivo .lst.
El depurador nos permite crear etiquetas para diferentes direcciones de memoria. Para hacer esto, debemos ir a System y seleccionar la opción Symbol Manager. En la pestaña Address labels, podemos hacer clic en el botón Add para crear una etiqueta de texto que aparecerá en la sección de código del depurador. Por ejemplo, creemos la etiqueta "Prova_JBD" para la dirección 0x79F0:
El debugger de openMSX tiene una opción para cargar estos símbolos desde un fichero. Es posible traducir la información del archivo .lst para crear un archivo de símbolos que el depurador de openMSX pueda interpretar? El script create_sym_debug.py está diseñado con ese propósito y nos permite realizar la traducción. Puedes encontrar el script en GitLab.
Para utilizar el script, debes ejecutarlo con el comando python3 create_sym_debug.py {NombrePrograma}, donde {NombrePrograma} es el nombre del archivo compilado sin la extensión .c. En este caso sería python3 create_sym_debug.py PrimProg. Al ejecutar el script de esta manera, se generará el archivo PrimProg_opmdeb.sym.
Para importar el archivo de símbolos en el depurador de openMSX, debes ir al menú System, seleccionar la opción Symbol Manager y luego navegar hasta la pestaña Symbol files. Haz clic en el botón Add y busca el archivo PrimProg_opmdeb.sym en tu computadora. Una vez seleccionado el archivo, haz clic en el botón Reload all para cargar los símbolos. Después de esto, tendrás las etiquetas del código C dentro del mapeo de memoria del depurador, lo que facilitará la navegación y comprensión del código durante la depuración:
Resumen
En este artículo hemos explicado el funcionamiento del compilador sdcc y la biblioteca Fusion-C. Hemos descrito los diferentes comandos para la compilación y cómo configurar un entorno para el desarrollo de código en C y su exportación a un MSX. También hemos explicado el depurador y la relación entre la memoria y los archivos generados por sdcc. Estos son los fundamentos para comenzar a experimentar con C y ver cómo funciona en el MSX.
Postdata
También es posible descargar una versión más actualizada de Fusion-C desde el repositorio de GitHub de Eric Boez, que es la versión 1.3. Sin embargo, esta versión ha sido abandonada en favor de la versión 2.0, que esperamos que se lance pronto.
Comments