Què és el sdcc? I el Fusion-c?
El sdcc és un compilador creuat (cross compiler) que ens permet programar en el nostre PC (Linux, Windows i Mac) i obtenir un binari que es pot executar en el nostre MSX (i altres plataformes que tenen el Z80 com l'Spectrum, el CPC, etc. Però a nosaltres no ens interessen tant). Això ens permet utilitzar editors moderns per desenvolupar per les nostres màquines del segle passat.
El Fusion-c és una llibreria open-source desenvolupada en sdcc i que té moltes funcions que ens permeten utilitzar les capacitats dels MSX de forma senzilla. A data de 03/05/2023 es pot descarregar la versió 1.2 de la web de Repro-Factory (https://www.ebsoft.fr/shop/) anant a l’apartat de Fusion-C i descarregant Fusion-C Tools i Fusion-C Library que són totalment gratuïts, només cal que ens registrem:
Un cop hem fet la compra (check out) del producte, s’ha d’anar a la nostra compta i entrar a les comandes per clicar l’enllaç a descarregar:
El fitxer MSX_Fusion-C-V1.2.zip conté la llibreria en C i el document FUSION-C-Quick A4 1.2.pdf a on s’explica com fer la instal·lació. Al fitxer Fusion-C_ToolsChain.zip trobem el program hex2bin per passar a binari, l’emulador openMSX, el sdcc 3.6 i la configuració per fer-ho funcionar amb l'editor Sublime.
Què necessitem per a fer els nostres programes?
Un editor
Qualsevol programari que ens permeti editar un fitxer text serà suficient per poder escriure el codi en C de la nostra aplicació. Personalment utilitzo l'emacs, un dels editors més antics, amb les capes del spacemacs. Un altre membre dels MoltSXalats utilitza el Visual Studio. Aquest editor és el que està explicat al llibre de Fusion-c i té la compilació i execució de l'openmsx integrats a l'editor.
El compilador sdcc
Podeu descarregar el compilador per diferents versions i plataformes aquí https://sdcc.sourceforge.net/. Heu d'anar amb molt de compte que la versió que descarregueu correspongui a la plataforma que utilitzeu (Linux, Windows, Mac) i que sigui la mateixa amb la que s'ha compilat el Fusion-c. Per exemple, la versió 1.2 necessita sdcc 3.6.
Per compilar el nostre programa usarem la comanda:
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 {fitxerc}
Què signifiquen tots aquests paràmetres? Anem a explicar el seu significat:
-V serveix per indicar que el volem en mode verbose, d’aquesta manera indica més informació del que està fent.
--code-loc 0x106 és per indicar a partir de quina zona carregarà el binari en memòria quan executem el fitxer .com obtingut. Aquest cas és l’adreça 106, ja que l’adreça 100 és on comenci el codi de programa en el sistema DOS (figura 3.1 https://konamiman.github.io/MSX2-Technical-Handbook/md/Chapter3.html) i els següents 6 bytes són variables per poder fer el retorn del nostre programa a DOS que utilitza el crt0.
--data-loc 0x0 que és l’espai que volem deixar entre el codi i les variables que generi el compilador. Perquè quedi més compacte el fitxer, les posem immediatament després del codi, per això el 0.
--disable-warning 196 no volem que apareguin els advertiments (warnings) relacionats amb el 196=’pointer target lost const qualifier’ i que apareixen amb les funcions printf.
-mz80 indiquem que compili per generar codi binari (opcodes) del processador z80.
--no-std-crt0 serveix per indicar que no utilitzarem el crt0 per defecte de la llibreria sdcc, sinó que farem servir un propi. Aquest crt0 és la part inicial que hem deixat, per això comencem a la 0x106. Més informació sobre el crt0 es pot trobar a https://www.konamiman.com/msx/msx-e.html#sdcc (konamiman) o http://msx.avelinoherrera.com/index_en.html (Avelino Herrera) (no sé quin dels dos és l’original). Hi ha diferents crt0 segons si el binari que volem executar és per ser corregut des de Basic, des de DOS o des de DOS amb paràmetres.
--opt-code-size s’usa per si es vol que el compilador optimitzi per reduir el tamany. Si el que volem és incrementar la velocitat usarem aquest altre paràmetre --opt-code-speed.
fusion-DOS.lib -L ./fusion-c/lib/ també hem d’indicar les diferents llibreries que utilitzem, en aquest cas fusion-DOS.lib i el camí a on la trobarem ./fusion-c/lib després del paràmatre L.
-I ./fusion-c/include/ no ens hem d’oblidar dels includes que s’han indicat dins el fitxer C a compilar. Hem d’indicar també a on el compilador els pot trobar amb el paràmetre -I.
./fusion-c/lib/crt0_msxdos.rel aquest és el crt0 que volem utilitzar en aquesta compilació.
{fitxerc} finalment el nom del fitxer que conté el codi C a compilar.
Esborrar les funcions printf, sprintf, vprintf, putchar i getchar
La llibreria del sdcc que conté les comandes del estàndard C (for, if, int, etc.) conté també les funcions fprintf, sprintf, vprintf, putchar i getchar però aquestes no són les ideals per als nostres MSX, tal i com s’indica a la pàgina 25 del pdf FUSION-C-Quick A4 1.2.pdf que es troba en el .zip descarregat de la web que conté el Fusion-C.
En el cas del Linux s’ha de fer (per altres plataformes mireu el document 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
El camí /usr/share/sdcc/lib/z80/z80.lib és a on hi ha la llibreria z80.lib segons el paquet instal·lat per la distribució OpenSUSE. Altres distribucions, utilitzen altres camins. Podeu fer una cerca amb sudo find / -name “z80.lib”. La comanda sdar està inclosa dins el sdcc i serveix per gestionar les llibreries. Una llibreria no és res més que una agrupació de fitxers rel. Els fitxers rels són binaris obtinguts amb el sdcc. Amb la comanda sdar -d el que estem fent és eliminar aquests fitxers de la llibreria z80. Així quan l’hagi de compilar utilitzarà les funcions del Fusion-C.
Traductor a binari MSX
El binari resultant de la compilació no està en format MSX, està en Intel Hexadecimal. Per fer la traducció utilitzarem el programa hex2bin. En les utilitats del Fusion-C ja hi és. Malauradament la versió per Linux no m'ha funcionat i l'he descarregada d'aquí https://hex2bin.sourceforge.net/.
La comanda que usarem és:
hex2bin -e com nom_fitxer.ihx
El paràmetre -e és el nom que li donarem a l’extensió del fitxer un cop l’hagi convertit en codi per al MSX, en aquest cas com. Finalment hi ha el nom del fitxer amb extensió ihx que hem obtingut a l’hora de compilar amb el sdcc.
L'emulador openMSX
Per fer les proves del codi, sense haver de deixar la plataforma a on estem desenvolupant, utilitzarem l'emulador de codi lliure openMSX. També hi ha altres emuladors, però aquest és open-source i encara rep actualitzacions. A part que té un debugger amb molt bones prestacions.
Per fer una instal·lació de l’openMSX podeu mirar aquest videotutorial https://www.youtube.com/watch?v=6MvhuTiLJqc o seguir les instruccions de la documentació oficial https://openmsx.org/manual/setup.html.
La comanda per executar l'emulador és:
openmsx -machine Panasonic_FS-A1ST -diska cami_nostre_disquet_virtual/
Amb el paràmetre -machine indiquem la màquina MSX que volem emular, en aquest cas un Turbo-R. Recordeu que podreu emular diferents màquines si teniu les ROMs corresponents i la seva descripció en xml al directori machines. El nom d’aquest fitxer xml és el que ha d’anar després del paràmatre -machine.
El segon paràmetre, -diska, serveix per indicar-li que la disquetera és una virtual i que pot llegir els fitxers al camí cami_nostre_disquet_virtual/. Tots els fitxers que posem a aquest directori seran vist per l’MSX emulat, si fem un dir al veurem. Tots els canvis que fem al directori, es veuen traslladats directament a l’emulador, i al revés, al directori. És a dir, que si estan al MSX emulat creo un fitxer i el guardo al que seria el disquet del MSX, apareix immediatament al directori indicat a la plataforma que està executat l’emulador. No cal resetejar l’MSX perquè es vegin els canvis. D’aquesta manera, podrem tenir l’emulador sempre corrent mentre podem anar regravant el nostre fitxer .COM. L’única limitació que té, és que els fitxers que hi ha dins d’aquest directori no poden sobrepassar els 720K (la capacitat que està emulant la disquetera).
Si preferiu un entorn gràfic per executar l’openMSX, ho podeu fer mitjançant catapult (es pot descarregar a la web de l’openMSX (http://openmsx.org/). Recordeu però, indicar que la disquetera sigui un directori del voster ordinador.
Dins la mateixa web, trobarem un enllaç per descarregar el debugger. Aquest no li cal cap paràmetre per executar-lo.
Primer programa en C
Per poder provar tot l'explicat anteriorment, podem crear aquest fitxer en C:
El bloc B1 és on indiquem tots els fitxers .h a on hi ha la definició de les funcions que usarem, en el nostre cas: Screen, Cls, Locate, etc.
Després ve la funció main, que és la que s’executa quan es compila el fitxer. Al block B2 indiquem que es posi a la pantalla 0 i que la netegi. Després (al B3) que a la posició a escriure és la (10,10) i a aquesta coordenada escriurem el text “El meu primer programa en C!”. Esperarem que es premi una tecla per poder continuar. Finalment sortirem del programa i retornarem al DOS en el bloc B4.
Si anomenem aquest fitxer PrimProg.c el podem compilar amb la comanda indicada a l’apartat “El compilador sdcc” substituint {fitxer.C} per PrimProg.c.
De tots els fitxers generats, agafarem el PrimProg.ihx i el passarem a binari amb la comanda hex2bin a on nom_fitxer.ihx serà PrimProg.ihx. Amb això tindrem el fitxer PrimProg.com que el copiarem al directori del paràmetre dska que usem quan executem l’openMSX. Amb el que obtindrem:
Però què són tots aquests fitxers generats?
El compilador sdcc genera molts fitxers a part del .ihx. Tot seguit els passem a explicar:
.asm: és el primer fitxer generat pel compilador i agafa cada línia de codi i la tradueix a llenguatge assemblador. També crea les seccions per les variables (dades) i pel codi.
.sym: llista tots els símbols (variables) i funcions que s’han usat en el .asm.
.rel: és el fitxer compilat amb els opcodes, la traducció del .asm a hexadecimal que entén el processador Z80. En el cas que volguem fer una llibreria o enllaçar a algun altre fitxer en .c és el que haurem d’afegir a la comanda sdcc.
.lst: aquest fitxer uneix els 3 anteriors. Per cada línia de codi pots trobar la seva traducció en línies d’assemblador i cada línia d’assemblador al seu opcode pel Z80 i la quantiat de bytes que ocupa. Un extracte del nostre fitxer PrimProg.lst:
La zona 1 és el tamany que ocupa la instrucció en hexadecimal, la zona 2 és l’opcode (el codi màquina) pel Z80 i la zona 3 és la traducció per cada comanda del nostre codi. Així si ens fixem a la línia 58, 58 ;PrimProg.c:4: Screen(0);, tenim que la línia 4 del nostre programa hi ha la funció Screen(0), a les línies següents 59-63 hi ha la traducció d’aquesta línia a assemblador. A la part 2 corresponent a cada línia tenim el seu opcode.
.lk: és un llistat de les dades que necessita el linker (enllaçador) per unir el nostre binari (.rel) amb els altres binaris de les llibreries que hem utilitzat.
.noi: conté les definicions de les diferents funcions i variables. Suposo que el deu usar l’enllaçador.
.map: la part que ens interessa és la de _CODE que és on trobem totes les funcions del .lst però a la posició que aniran a la memòria del MSX. Pel programa anterior obtenim:
A on observem que la nostra funció principal, el main, es troba a la posició 106 de la memòria
El debugger
Els fitxers .map i .lst ens permetran debugar el nostre codi per si succeeix alguna cosa que no havíem tingut en compte. Anem a veure com funciona el debugger en el nostre primer programa. En el cas de linux el cridarem com openmsx-debugger, i s’obrirà una aplicació com:
El primer cop que l’executem hem de clicar el llamp horitzontal per connectar-lo a algun emulador openMSX que estigui corrent. Si només n’hi ha un es connectarà directament i si n’hi ha més d’un haurem d’escollir a quina instància es connecta. Podem apretar la icona de pausa (dues barres verticals) i observar quin emulador s’ha pausat.
Un cop connectat veurem que les diferents parts de la pantalla s’omplen de dades segons els valors de la memòria i registres de l’openMSX. Hem de tenir en compte, que aquestes només es refresquen quan el debugger està en estat breakpoint (la icona del cercle vermell). Per continuar l’emulació apretarem la icona del costat, la de la persona blava corrent.
Si posem un breakpoint a l’adreça 0x106 apretant el botó Add dins la secció Debug list i escrivnt $106 (les adreces hexadecimals van precedides per $ en el debugger) i correm el nostre programa, obtenim:
I observem que l’emulació està parada a l’adreça 0x106 a on l’opCode és el xor a,a i si mirem el llistat del .lst coincideix amb la primera línia de la funció main. I que les ordres consecutives són també els mateixos opcodes que tenim al fitxer .lst.
Seria possible tenir les línies de codi visibles al debugger?
Com que hi ha una relació directa entre les comandes del .lst i el codi del debugger, ens aniria bé poder entendre quina línia està debugant del nostre codi sense haver de consultar el fitxer .lst.
El debugger ens permet crear etiquetes per diferents adreces de memòria. Per fer-ho, hem d’anar a System i seleccionar l’opció de Symbol Manager, si anem a la pestanya de Address labels i apretem el botó Add, podem crear un text que apareixerà a la part de codi del debugger. Per exemple, creo l’etiqueta Prova_JBD per l’adreça 0x79F0:
L’openMSX debugger té una opció que és carregar tots aquests símbols d’un fitxer. Seria possible traduir la informació del .lst per poder crear un fitxer de símbols que interpreti el debugger? L’script create_sym_debug.py (https://gitlab.com/brossaip/msx_bricks/-/blob/main/create_sym_debug.py) ens permet fer aquesta traducció. Només cal que el cridem com a python3 create_sym_debug.py {NomProg} a on {NomProg} és el nom de fitxer que hem compilat sense l’extensió .c. Així en el nostre cas seria python3 create_sym_debug.py PrimProg i obtindrem el fitxer PrimProg_opmdeb.sym. Aquest fitxer l’importarem al debugger mitjançant les opcions de menú System i seleccionar l’opció de Symbol Manager, i seleccionant la pestanya Symbol files li donem al botó Add per navegar pels fitxers de l’ordinador i trobar el PrimProg_opmdeb.sym. Per carregar-lo li donem al botó Reload all i tindrem les etiquetes del codi en C dins el mapeig de la memòria:
Conclusions
En aquest article s’ha explicat el funcionament del sdcc i la llibreria Fusion-C. S’han descrit les diferents comandes a utilitzar per compilar i com instal·lar un entorn a on poder desenvolupar codi C i ser exportat a un MSX. També s’ha explicat el debugger i la relació de la memòria amb els fitxers generats per l’sdcc.
Aquestes són les bases per poder començar a fer proves en C i veure el seu funcionament en el MSX.
Postdata
També es pot descarregar una versió més actualitzada del Fusion-C al github de l'Eric Boez, que és la versió 1.3, però que ha estat abandonada per la versió 2.0 que esperem que surti en breu.
Comments