Framework Retro para programación C con SDCC

Objetivo: desarrollar un entorno de programación de proyectos retro en C/Ensamblador para todo tipo de ordenadores de 8 o 16 bits que carecen de él. En una primera etapa quiere cubrirse la plataforma MSX (con todas sus variantes) para ir más tarde incorporando otras (Spectrum, Amstrad CPC, Amiga, C-64). Si un software para un microprocesador de 8 bits es desarrollado en 2013, entonces la única razón para no usar herramientas de 2013 es que quiera desarrollarse en la propia plataforma como un ejercicio arqueológico.

Hemos decidido que nuestro entorno debe disponer de las siguientes herramientas:
  • Sistema operativo Linux, para poder distribuir el sistema completo como una máquina virtual sin violar licencias o como paquete para ser instalado en cualquier máquina. También para tener un acceso mejor a las herramientas de programación open-source más actuales sin hacerle “injertos” al sistema operativo (léase Min-GW o CygWin).
  • Compiladores C orientados a sistemas con poca memoria.
  • Sistema de control de versiones, para poder publicar los cambios de nuestro software, marcar versiones, distribuirla a beta-testers, codificar en comunidad, etc.
  • Sistema de gestión de incidencias: para poder concentrar y asignar las tareas a realizar, los bugs a resolver, guardar información sobre su resolución, etc. Es deseable que el sistema de control de incidencias tenga wiki, foros, capacidad de subir ficheros, pueda mantener varios proyectos a la vez, etc. Y también que permita conectarse en caliente con los repositorios y pueda vincularse la información del wiki y de las incidencias con el propio código, especialmente con los comentarios del código.
  • Sistema de generación automática de documentación a partir del código. Similar a JavaDoc o Doxygen.
  • Sistema de test unitario, similar a JUnit, UnitTest, etc.
  • Entorno de desarrollo integrado que intente conjugar todas éstas herramientas (Eclipse, Netbeans...).
  • Sistemas de automatización de compilación/test inteligentes tipo Makefile, o más evolucionado.
    (seguir ampliando)
  • Y, por supuesto, emuladores configurados (o quasi-configurados si hay algún tema legal por ahí) para probar los desarrollos sin demasiada complicación.

Plataforma Linux

En éste momento se está desarrollando sobre una máquina virtual VirtualBox (www.virtualbox.org) que ejecuta un Bodhi Linux (www.bodhilinux.com).

Las notas que acompañan a la distribución pueden encontrarse en:

Readme RetroWB Bodhi

Compilador C

Buscaremos siempre los compiladores más adecuados a nuestra máquina. En el caso inicial (MSX) hemos considerado el más adecuado el SDCC, que probablemente será el escogido para cualquier micro que contenga el Z80 como procesador principal.

SDCC

SDCC es un compilador C especialmente indicado para pequeños dispositivos (de ahí su nombre Small Devices C Compiler). Es un avanzado compilador de hoy en día que se mantiene gracias a su uso para robótica y pequeños microcontroladores como los PIC o los 8051.
La lista de microcontroladores que maneja es la siguiente:
SDCC : mcs51/gbz80/z80/z180/r2k/r3ka/ds390/pic16/pic14/TININative/ds400/hc08/s08
Al estar preparado para Z80 permite ser usado para desarrollar programas para MSX, Spectrum, Amstrad CPC, etc.
Genera código en ensamblador que es fácilmente consultable y modificable, lo que nos ayuda, además, a aprender cómo funciona el ensamblador. También nos permite encapsular sentencias en ensamblador para realizar partes de rutinas que sean más fáciles o más óptimas en ese lenguaje.

SDCC vs GNU C

La versión actual es la 3.2.0. que data de 2012, por lo que se trata de un proyecto vivo que incorpora (sin dejar de vista su orientación a pequeños dispositivos) los últimos avances o tendencias.
Cabe remarcar que al disponer de un preprocesador actual nos permite introducir directivas y alternativas de compilación en el código fuente de la misma manera que se realiza en GNU. También al ser un proyecto que crece en la sombra de GNU C, busca tener las máximas similitudes en el tema de sintaxis o de argumentos de compilación (por ejemplo introducir un #define desde la línea de compilación se hace con el argumento -D), lo que minimiza las veces que debamos recurrir a documentación específica para aprender algo que luego no nos vaya a servir para programar ordenadores o microcontroladores más potentes.

En el caso del MC68000, corazón de los Atari ST y de los Amiga, el surtido de compiladores C de su época era
extenso y muy optimizado, por lo que la franja que queda entre el crosscompiling con GNU C y el “retro-compiling” con un C de la época es demasiado pequeño, y no existe una alternativa como la del SDCC. En ese caso, si un programador busca alto rendimiento, tiene que lidiar con compiladores muy eficientes pero obsoletos, y muchas veces tiene que invertir mucho tiempo en saber si una opción es posible realizarla con ese compilador y saber cómo se ejecuta.

Nuestro propósito es no perder de vista la sinergia con GNU C, que está dotado de unas herramientas de test y debug muy interesante para depurar gran parte de nuestro código, si conseguimos mantener una alta compatibilidad con Linux. Si el 80% de un programa es ANSI C y manejo de datos y memoria, podemos intentar mantener esos módulos compatibles entre SDCC y GNU para así poder, por ejemplo, ejecutar tests automáticos, reutilizar código en ambos sentidos, o debugar paso a paso buscando un puntero que se pierde o una conversión mal resuelta.
También podemos (como se hace en la programación de microcontroladores de control de sistemas con altas exigencias de seguridad) utilizar definiciones de tipos propias, como UI_8 (unsigned int de 8 bits), en vez de los tipos de C (unsigned int). De esa manera al tratar de debugar en Linux una rutina C no veremos diferencias entre un número que se desborda en un MSX (los enteros de un MSX son de 8 bits por defecto) y que no se desborda en un Linux (con enteros seguramente de 32 bits si no se especifica lo contrario).

Otra de las razones por las que nos interesa mantener nuestro código lo más parecido al GCC es la de los entornos de programación. Tanto Eclipse como Netbeans proporcionan facilidades tremendas para refactorizar, debugar, documentar y organizar proyectos software. También para disparar baterías de test automáticos. Éstas herramientas realizan el “syntax highlight” sobre el código, alertando de conversiones erróneas o de funciones no declaradas. Merece la pena invertir en desarrollar funciones simuladas o “stub” protegidas por una directiva de compilación para que el entorno nos ayude todo lo posible a

Retrolib: Árbol de directorios con librerías de los ordenadores retro

Queremos ir juntando las diferentes librerías C/Ensamblador de cada ordenador en nuestro entorno de programación. Habrá rutinas de carácter general, otras dependientes del procesador central, otras dependientes del sistema operativo del ordenador, de la familia, del modelo concreto. También habrá librerías dependientes del chip de sonido, del de vídeo, etc.

Si creamos un árbol de directorios determinado, podemos realizar una compilación de proyectos utilizando un Makefile configurable. En una primera versión podríamos tener unos ejemplos de proyecto funcionando que podrían copiarse para crear nuevos proyectos. En el nuevo proyecto en un fichero el usuario indicaría la ubicación de sus fuentes y mediante variables escogería el ordenador al que va orientado (p.e. MSX), la versión de dicho ordenador (p.e. MSX1), el “target” de la compilación (p.e. cartucho ROM o comando para sistema MSX-DOS), y la variante.

Un ejemplo de variante es la siguiente: en MSX utilizando las librerías publicadas y documentadas por Avelino (poner bien ésta cita) tendríamos “startup básico” que es compatible con todos los sistemas MSX-DOS o “startup avanzado”, que permite invocar comandos con argumentos al estilo usual (argc, argv), pero que es menos compatible. En el caso de los cartuchos ROM, podríamos tener la configuración básica de cartuchos hasta 32Kb o hablar de los MegaROM.

Deberíamos también tener un sistema de “módulos” que podamos activar o desactivar con flags en nuestros Makefiles. Con éstos módulos podríamos introducir características propias de periféricos tipo Yamaha Music Module, Philips FM-Pac, MIDI, etc

Primera propuesta para la estructura

La forma más práctica y realista de desarrollar una arbol de directorios ideal es partir de una idea y madurarla contra ejemplos de proyectos reales y que deban compilar. La propuesta actual tiene la siguiente forma:
  • proyecto1
    • src (fuentes del usuario)
      • main.c
      • func1.c
      • func2.c
    • lib (librería del usuario)
      • modulo1
        • include
        • src
        • lib
      • modulo2
        • ...
    • build (directorio para la compilación)
      • Makefile
      • dist (directorio para el/los ejecutables) y sus ficheros de configuración/despliegue
  • retrolib (librería retro unificada)
    • cpus
      • cpu1 (p.e. z80)
        • include
        • src
        • lib
      • cpu2
        • include
        • src
        • lib
      • ...
    • computers (podrían estar cada uno dentro de su CPU, pero de momento está bien ahí)
      • msx (en el primer nivel está la “familia”)
        • system (librerías de MSX globales)
          • include
          • src
          • lib
          • msx.mk
        • versions
          • msx1
            • include
            • src
            • lib
            • msx1.mk
          • msx2
            • include
            • src
            • lib
            • msx2.mk
        • targets
          • com
            • include
            • src
            • lib
            • com.mk
            • variants
              • standard_startup
                • include
                • src
                • lib
                • standard_startup.mk
              • advanced_startup
                • include
                • src
                • lib
                • advanced_startup.mk
          • rom
            • include
            • src
            • lib
            • rom.mk
            • variants
              • rom32k
                • include
                • src
                • lib
                • rom32k.mk
              • megarom
                • include
                • src
                • lib
                • megarom.mk
      • spectrum
        • system
          • include
          • src
          • ...

En cada fichero mk están partes del makefile que se incluyen en función de lo que los usuarios vayan especificando en el Makefile del proyecto. La idea de hacerlo así es intentar que cada proyecto se compile siempre desde sus fuentes, para intentar que el linkador no incorpore funciones que no vayamos a necesitar, como ocurriría si linkáramos librerías precompiladas.

Estructura de Makefiles

Acompañando a la estructura de directorios, existirá una estructura de ficheros *.mk que serán incluídos por el Makefile de proyecto según las opciones que el usuario escoja en dicho makefile. Así, en prácticamente cada directorio de cada nodo del árbol encontramos una estructura que se repite una y otra vez: los directorios include, src y lib y el correspondiente *.mk con un nombre igual que el nodo.

Por ejemplo, si un usuario está desarrollando una aplicación MSX, compatible con toda la gama (sin decantarse por ninguna versión msx1 o msx2, con un target de fichero com (ejecutable de MSX-DOS) y con el startup avanzado, al seleccionar esas opciones en el Makefile se le incluirán los *.mk correspondientes:

../../retrolib/cpus/z80/z80.mk
../../retrolib/computers/msx/msx.mk
../../retrolib/computers/msx/targets/com/com.mk
../../retrolib/computers/msx/targets/com/variants/advanced/advanced.mk

Dentro de éste último nos podemos encontrar que se añaden más argumentos a los flags de compilación, por ejemplo:

STARTUPFLAGS += --no-std-crt0 $(COMPPATH)/targets/$(TARGET)/startups/$(STARTUP)/lib/crt0msx_msxdos_advanced.rel --code-loc 0x0178

mientras que si hubiésemos tomado la opción “standard” se habría incluído el fichero

../../retrolib/computers/msx/targets/com/variants/standard/standard.mk

de contenido:
STARTUPFLAGS += --no-std-crt0 $(COMPPATH)/targets/$(TARGET)/startups/$(STARTUP)/lib/crt0msx_msxdos.rel --code-loc 0x0107

Aquí también pueden encontrarse tareas de compilación, etc.

Makefile de proyecto

Programación en comunidad

Sistemas de control de versiones

Uso local

Uso básico

Uso concurrente

Opciones disponibles

SVN
Mercurial
Git

Sistemas de control de incidencias

Forjas de proyectos

...

Agradecimientos

Queremos agradecer a los verdaderos creadores del contenido, que son los que han mantenido durante éstos años el desarrollo de 8 bits vivo.

Para empezar:
  • Txinto quiere agradecer a Alberto Orante su pasión y sus fantásticas herramientas para MSX.
Y luego en general:

Miscelánea