
Note: l'utilizzo del seguente articolo, originariamente apparso su BitPlane, è stato gentilmente concesso dagli Autori della rivista suddetta. Le tematiche trattate riguardano lo sviluppo in C sotto MorphOS 0.4 per Amiga PowerUP ma sono applicabili in toto anche alla programmazione di MorphOS su piattaforme Pegasos.
Introduzione alla programmazione in C con VBCC sotto MorphOS
Sin dai tempi dell'uscita delle prime schede PowerPC per Amiga, se il sogno di ogni utente è sempre stato quello di vedere un nuovo sistema operativo in grado di sfruttarne la potenza, il sogno più grande per ogni sviluppatore è stato quello di poter generare da subito del codice per le nuove CPU e vedere finalmente "decollare" i propri programmi. Purtroppo, prima dell'esistenza di MorphOS, scrivere applicazioni per PowerPC su Amiga era un compito piuttosto ingrato e demotivante: si doveva innanzitutto scegliere quale dei due kernel esistenti supportare, ovvero PowerUP o WarpOS, una convivenza notoriamente problematica. Accedere alle funzioni di AmigaOS, ancora interamente in codice 68K, comportava perdite di prestazioni e preoccupazioni aggiuntive, e spesso il potente PowerPC era relegato a "coprocessore" da usarsi solo per le routine più critiche al fine di evitare i tanto temuti "context switches". Inoltre le scelte in fatto di compilatore erano abbastanza limitate: da una parte il costoso Storm-C PPC con le sue peculiarità, dall'altra parte il SAS-C, di cui era stato tentato un aggiornamento molto embrionale per produrre eseguibili compatibili PowerUP. Molti si sono rimboccati le maniche da subito affezionandosi all'uno o all'altro kernel, altri più diffidenti hanno preferito aspettare e guardare con un occhio vigile. Ora che esiste MorphOS la CPU è una sola, uno solo è il kernel, e tutti questi problemi svaniscono come per magia. E' solo necessario sapere da che parte iniziare e conoscere alcuni piccoli accorgimenti, dopodichè tutto sembrerà un gioco da ragazzi. Questo articolo vuole appunto essere un tutorial essenziale, specialmente rivolto all'ultima categoria di programmatori citata, verso la scrittura del primo "Hello World PPC" completamente nativo per MorphOS.
E ovviamente, una volta «salutato il mondo», si può procedere alla sua edificazione!
Perchè VBCC?
Il compilatore "ufficiale" scelto dal team di MorphOS è (tanto per cambiare!) il GCC, e così pure per il GCC è concepito quasi tutto il materiale presente nell'archivio per sviluppatori (MorphOS_dev, presente nella sezione di download del sito www.morphos.net). Noi abbiamo scelto il VBCC non per anticonformismo ma in quanto più amichevole come ambiente per l'utente Amiga medio, che non conosce le strane sintassi del mondo Unix/Linux nè vuole intasare il proprio HD con decine di MB di files o perdersi in archivi sparsi un pò per tutta la rete. Va inoltre ricordato che l'installazione di GCC per i non esperti è un lavoro veramente demotivante e non pochi si sentono scoraggiati sin dall'inizio: il motivo reale è che attualmente non è ancora disponibile un pacchetto di sviluppo basato su GCC "plug'n'play", contenente ovvero tutta la miriade di files indispensabili per il suo utilizzo. VBCC è un compilatore C freeware di ottimo livello, conforme ISO/ANSI, completo di ottimizzatore e multi-targeted: oltre a disporre degli eseguibili nativi per diversi kernel, tra cui MorphOS, è in grado di generare binari per AmigaOS 68K, PowerUP, WarpOS e MorphOS, il tutto in modo estremamente semplice ed «Amiga-friendly».
Installiamo il VBCC
Gli archivi che ci servono sono due. La prima è la distribuzione generica (vbcc.lha), la seconda (vbcc_MorphOS.lha) contiene gli eseguibili in formato Elf e alcuni file di configurazione. Entrambi gli archivi sono prelevabili dalla home page di Frank Wille, coautore del progetto assieme a Volker Barthelmann e curatore dei vari backend (devnull.owl.de/~frank/vbcc_e.html) oppure da aminet (dev/c).
Sul sito di Wille è però presente anche l'aggiornamento alle ultime versioni 0.8a e
0.8b. L'albero della distribuzione principale è così composto: 1) doc, contiene i manuali di tutti gli eseguibili, compresa una guida al VBCC esauriente e molto ben fatta disponibile anche in formato PDF, di cui consigliamo vivamente la lettura. 2) Config, contiene gli script di configurazione con le opzioni del compilatore, disponibili per i vari backend. 3) bin, contiene gli eseguibili come il frontend, il compilatore, l'ottimizzatore, l'assemblatore, il linker. 4) Targets, contiene vari cassetti con gli includes e le linker libs per i diversi target.
Allo scopo di creare un'installazione idonea allo sviluppo sotto MorphOS possiamo procedere come segue. Scompattiamo l'archivio principale in una directory che chiameremo VBCC, e l'archivio speciale per MorphOS in una directory temporanea (magari in RAM: ). Copiamo i 9 eseguibili dalla directory bin/ di quest'ultimo sopra quelli già presenti in VBCC/bin/ (oppure rimpiazziamo l'intera directory bin). Copiamo il file config/vc.config dell'archivio MorphOS in VBCC/config/ (possiamo anche cancellare tutti gli altri: il file di configurazione vero e proprio si deve chiamare vc.config, gli altri hanno valore di archivio). Ora abbiamo bisogno di creare alcuni assign. Per renderli permanenti aggiungiamo le seguenti linee alla nostra User-Startup
Assign vbcc: [la directory che avete appena creato]
Assign C: vbcc:bin ADD
Assign vbccmos: vbcc:targets/ppc-morphos
Assign vincludemos: vbccmos:include
Assign vincludemos: Include: ADD
Assign vlibmos: vbccmos:lib
Ovviamente partiamo dal presupposto che abbiate già un compilatore installato nel vostro sistema con tanto di assign "include:" agli headers standard di AmigaOS. Altrimenti è necessario procurarsi questi ultimi dai NDK Amiga ufficiali e creare l'assign PRIMA delle righe sopra elencate. Il preprocessore del VBCC cercherà prima i files in vincludemos:, se presenti, altrimenti procederà al caricamento di quelli standard. Passiamo all'esame il contenuto della directory VBCC/targets/ppc-morphos. Qui sono presenti due directory, Include e Lib. Lib contiene le linker libs, usate per la creazione di eseguibili per MorphOS, in formato "arch" (.a).
Include contiene gli headers adeguati per l'accesso al sistema operativo (proto/) e alle linker libs del VBCC. Per completare il lavoro di installazione abbiamo però ancora bisogno di 2 files, emulregs.h e emulinterface.h; qui il discorso purtroppo si complica perchè essi sono presenti nell'archivio MorphOS-dev in formato .diff (emulinclude/includenew.diff), e per la loro creazione avremmo bisogno di un bel pò di comandi unix installati. Per chi non fosse in grado di farlo, comunque, gli autori ci hanno permesso gentilmente di includerli nel CD della rivista (vedi BitPlane CD n.5.) In particolare, emulregs.h ha dovuto essere leggermente modificato per funzionare correttamente con il VBCC; tutto questo non deve essere considerato come una limitazione del VBCC: il vero problema è che il GCC in realtà supporta un grande numero di sintassi proprietarie non facenti parti del linguaggio C standard ANSI! Entrambi i files devono essere copiati in una directory che creeremo, vincludemos:emul/.
Partenza!
Ora potete posizionarvi in una directory di lavoro di vostra scelta e aprire una shell con CD su di essa, e cercheremo di generare il nostro primo eseguibile PPC. Creiamo un file Test.c che non potrà certamente essere diverso da quanto segue ;)
#include <stdio.h>
int main()
{
printf("Hello World PPC!");
return(0);
}
Ora digitiamo
vc test.c
e in un attimo, se non ci sono stati errori, viene creato il file a.out.
Si tratta ovviamente di un eseguibile .elf per MorphOS: proviamo a cliccarlo o lanciarlo da shell ed ecco il saluto del nostro PowerPC!
Quello che noi abbiamo appena fatto è stato invocare VC, il frontend di VBCC: si tratta di un programma che, basandosi sul file di configurazione vc.config, invoca i vari comandi in bin/ necessari ai diversi stadi della creazione di un eseguibile. Sarebbe fuori luogo e impossibile elencare ora tutti i vari parametri che possono essere passati a vc, come quelli di ottimizzazione, e rimandiamo per questo alla lettura del manuale.
Ricordiamoci che a differenza del SAS-C e di altri compilatori, il modulo di startup usato dal VBCC NON inizializza la console se il programma viene lanciato da icona (leggi: in modalità Workbench), a meno che in quest'ultima non venga specificato SHELL con il gadget StartFrom. Proviamo ora qualcosa di più "cospicuo":
#include <proto/intuition.h>
#include <proto/exec.h>
int main()
{
struct Window *w = OpenWindowTags(NULL,
WA_Title, "Please close me!",
WA_CloseGadget, TRUE,
WA_DragBar, TRUE,
WA_InnerWidth, 300,
WA_InnerHeight, 100,
WA_IDCMP, IDCMP_CLOSEWINDOW,
TAG_END);
if(w)
{
WaitPort(w->UserPort);
ReplyMsg(GetMsg(w->UserPort));
CloseWindow(w);
return(0);
}
else return(20);
}
e digitiamo
vc test.c -lauto -lamiga -amiga-align
lanciamo l'eseguibile a.out così generato ed ecco il PPC aprire la nostra finestra! Niente di più facile, vero ? Cerchiamo di capire cos'è successo.
I comandi "-l" di sopra vengono passati al linker VLink per unire al nostro programma le librerie libauto.a e libamiga.a presenti in targets/ppc-morphos/lib/. La sintassi è -lxxx, ove la linker library si chiami libxxx.a. lauto.a ci offre le basi delle librerie standard, Intuition ed Exec nel nostro caso, evitandoci quindi di doverle aprire manualmente. libamiga.a ci permette di accedere alle funzioni dell'OS usate nell'esempio tramite le sue stubs, mentre l'opzione -amiga-align è necessaria ogniqualvolta si debba accedere a strutture di sistema. Vediamo di chiarire questi due punti in modo dettagliato.
Occhio all'allineamento!
Per default e per ragioni di velocità, ogni moderno compilatore (tra cui anche il VBCC) allinea gli elementi di strutture alla longword (32 bit) e come tali ne fa riferimento. Purtroppo all'interno del nostro OS molte strutture, residui dei tempi in cui le CPU lavoravano a 16 bit, non rispettano ancora questa regola. E' quindi necessario, quando si deve accedere a strutture del sistema operativo, usare l'opzione -amiga-align per non richiedere allineamenti superiori a 16 bit, anche se questo costringe il PPC ad un certo degrado di prestazioni, pena sicuri crash. Un rimedio potrebbe essere suddividere il programma in diversi moduli, e compilare quelli più time-critical senza l'opzione suddetta, evitando di compiere in questi ultimi accessi diretti alle strutture dell'OS.
Emulazione e Traps
E' giunto il momento di chiarire il meccanismo chiave di emulazione usato da MorphOS, ovvero il modo in cui convivono trasparentemente parti di codice PPC e parti di codice 68K. Fintanto che non abbiamo bisogno di lanciare manualmente nuove task o scrivere routine eseguibili dall'input.device (hooks e dispatchers) potremmo anche trascurare questi dettagli e fare a meno dei due headers citati in precedenza. Ma come fare se il nostro nuovo codice nativo deve invocare funzioni di librerie e/o di AmigaOS ancora non rimpiazzate da equivalenti native ? Per essere sinceri questo è l'unico caso in cui si può verificare che un programma PPC debba invocare del codice 68K, a meno di scrivere del codice misto, ma ora non ce n'è più alcuna ragione. Il registro R2 del PPC è usato per convenzione come un registro "speciale" (come potrebbe esserlo l'A7 per il 68K).
MorphOS usa questo registro per puntare ad una struttura pubblica chiamata EmulHandle.


Quando si tratta di funzioni di AmigaOS oppure di librerie esterne, noi (teoricamente) non sappiamo se contengano già codice PPC o ancora codice 68K, quindi dovremo usare sempre questo meccanismo. Se poi il codice invocato è nativo, allora si avrà un "contro-contextswitch", di cui ancora non possiamo fare a meno per ovvie ragioni. La libamiga ci evita dunque di dover creare enormi includes fatti di macro o "inlines" o implementare questo meccanismo manualmente poichè contiene al suo interno tutto il codice necessario. Ovviamente noi dovremo adoperare i protos appositamenti forniti, che NON fanno uso di direttive #pragma. Con l'aggiornamento 0.8b, comunque, anche VBCC inizia a fare uso degli "Inlines" per la chiamata in modo direct alle funzioni di libreria di AmigaOS, ma da un punto di vista del programmatore la cosa è da considerarsi assolutamente trasparente (sono sempre i file proto/ del VBCC a svolgere ogni compito di inclusione necessario). Cosa succede invece quando l'emulatore deve eseguire codice
PPC e tornare cioè in modalità nativa ? Accade l'esatto contrario. Allo scopo viene messa a disposizione un'istruzione 68K illegale chiamata TRAP_LIB, del valore di 0xFF00, definita in emul/emulinterface.h.

Argomenti e registri
I nostri PowerPC dispongono di 32 registri interi a 32 bit (R0-R31) e altrettanti in virgola mobile a 64 bit (F0-F31). Secondo le convenzioni imposte dalla V4 ABI (Application Builder Interface) i registri R0, R3-R12, F0-F13 possono essere usati come "scratch", ovvero modificati all'interno di una funzione, tutti gli altri devono essere preservati. Lo standard prevede inoltre che i primi 8 argomenti interi di una funzione trovino posto sui registri da R3 ad R10, mentre i primi 8 in virgola mobile nei registri da F1 a F8. Tutti gli argomenti successivi verranno passati sullo stack (puntato da R1). I valori di ritorno saranno posti in R3 (ed R4 per le long long) o in F1. In teoria il programmatore C non dovrebbe preoccuparsi di questi dettagli, se non fosse per la scrittura di funzioni vararg: ora non è più possibile dichiarare tali funzioni come __stdarg e andare a ricercare gli argomenti sullo stack a partire dall'indirizzo del primo, per le ragioni appena spiegate; si dovrà fare ricorso alle apposite macro presenti in stdarg.h. Se invece stiamo scrivendo una funzione PPC che potrebbe essere chiamata dall'OS o in generale dal 68K emulato, e per la quale dobbiamo rispettare un determinato layout di registri, dovremo prelevare tali valori a mano dalla struttura EmulHandle. Un tipico esempio è costituito ancora una volta dalla scrittura di un hook, la cui funzione sarà dichiarata, per pura correttezza sintattica, senza argomenti. Quindi:
ULONG __saveds HookFunc(register __a0 struct Hook *hook,
register __a1 APTR msg,
register __a2 struct RastPort *rp)
{
[your code]
}
#§codeend
dovrà essere scritta come:
#§codestart
ULONG __saveds HookFunc(void)
{
struct Hook *hook = REG_A0;
APTR msg = REG_A1;
struct RastPort *rp = REG_A2;
[your code]
}
dove REG_An sono delle macro definite in emul/emulregs.h atte a prelevare i rispettivi registri del 68K emulato.
Esistono tuttavia alcuni casi specifici in cui è di vitale importanza poter disporre di un puntatore alla lista lineare degli argomenti passati ad una funzione; tipicamente questo avviene nella stesura di wrappers (funzioni che chiamano altre funzioni a scopo di aggiungere features o creare "patches") a funzioni var args che accettano TAGS (i.e OpenWindowTags()) o comunque un numero variabile di argomenti (i.e implementazioni di DoMethod()). In questi casi l'uso delle macro in stdarg.h non può essere sufficiente, in quanto tramite esse andrebbe scandita la lista degli argomenti a priori, ricostruita e quindi utilizzato il suo indirizzo - una cosa che non sempre è fattibile o efficiente. Esiste quindi un semplice trucco, che consiste nel riempire (se necessario) i primi 10 argomenti (ovvero i registri da R3 a R10) e iniziare a passare dall'undicesimo argomento il primo degli argomenti variabili, che verrà posto sullo stack. Ecco un semplice esempio di wrapper ad OpenWindowTags():
struct Window *_MyOpenWindowTags(ULONG[8],ULONG first_tag, ...)
{
[do something]
return(OpenWindowTagList(NULL,(struct TagItem *)&first_tag));
}
#define MyOpenWindowTags(first_tag, ...) _MyOpenWindowTags(0,0,0,0,0,0,0,0,first_tag,__VA_ARGS__)
Con una certa pratica nell'uso del C sarà possibile adattare lo stesso meccanismo anche a funzioni var arg leggermente differenti. Questo apparente "spreco" di registri inutilizzati in realtà non dovrebbe costituire un grosso problema: tanto compilatore quanto ottimizzatore dovrebbero essere abbastanza intelligenti nel riconoscere che i registri in questione in realtà NON vengono utilizzati, e quindi non dovrebbero venire "allocati".
Conclusioni
Come abbiamo appena visto sono davvero poche le fondamenta necessarie per iniziare la programmazione di applicazioni PowerPC sotto MorphOS.
Ovviamente per ragioni di spazio non ci è stato possibile approfondire proprio tutti i dettagli, anche quelli più specifici, ma speriamo che quanto appena esposto possa essere utile almeno come inizio per molti potenziali programmatori disorientati, felici utenti di MorphOS ma forse un pò scoraggiati dall'idea di dover usare per forza l'ostico GCC.
Ricordiamo che esiste una mailing list pubblica di supporto ai programmatori (http://groups.yahoo.com/group/MorphOS-Dev) e che Frank Wille si è sempre dimostrato disponibile e gentile come non molti in quanto a chiarimenti, richieste d'aiuto o bug reports. Se avete problemi con l'uso del VBCC non esitate dunque a contattarlo:
<frank@phoenix.owl.de>
Concludiamo ricordando che MorphOS dispone internamente di un certo numero di estensioni e nuove funzioni rispetto ad AmigaOS standard, principalmente all'interno di exec, dos e cybergraphics.
Si tratta però ancora di funzioni non documentate (attualmente gli autori di MorphOS hanno deciso di non rilasciare alcun NDK completo al pubblico) e questo, se da un lato può sembrare penalizzante, può per certi versi garantire ancora una notevole compatibilità tra MorphOS e AmigaOS (chi non fa uso di tali "estensioni" ha ancora la garanzia che il proprio codice potrà essere portato senza problemi anche sotto AmigaOS standard). I "core developers" di MorphOS, tuttavia, invitano accoratamente i programmatori a limitarsi alla compatibilità con AmigaOS 3.1 e non fare assolutamente affidamento su tutto ciò che riguarda le modifiche apportate in AmigaOS 3.5 e 3.9, principalmente per quanto riguarda icon.library, workbench.library e ReAction. Se ancora MorphOS offre una compatibilità con questi sistemi è da prendersi come bonus a tutti gli effetti, poichè tutto lascia credere che nei prossimi mesi (o anni ?) ci troveremo di fronte ad una triste scissione (MorphOS userà Ambient come desktop ufficiale, MUI come gui standard e API proprietarie per la gestione delle icone), nella speranza che questa imminente biforcazione non ci porterà a due OS sempre più diversi e sempre più incompatibili, a tutto svantaggio di sviluppatori e (di conseguenza) utenti. Al momento della scrittura di questo articolo siamo altresì in attesa del rilascio di AmigaOS 4, di cui assolutamente nessun dettaglio è pubblicamente noto circa la API usata e il meccanismo di emulazione 68K. Avremo davanti a noi 3 sistemi sempre più differenti da supportare ? O forse ne sopravviverà solamente uno a rompere questa disorientante anarchia ? Noi tutti speriamo di poterlo sapere (e prendere le realtive decisioni in fatto di sviluppo) in tempi ragionevoli... Per adesso non mi resta che augurare a voi tutti un Buon Lavoro: portate, gente, portate!
Riferimenti consigliati:
(MorphOS development guidelines and overview documentation)
http://zapek.meanmachine.ch/morphos/morphos.pdf (MorphOS Feature List)
http://www.blachford.info/morphos/morphos-full-features-list.pdf (MorphOS in detail)
http://www.blachford.info/morphos/morphos_in_detail.pdf (Frank Wille home page, VBCC)
devnull.owl.de/~frank/vbcc_e.html