Retour Les premier pas

C'est un dimanche pluvieux. Avachi devant votre écran de TV, qui ne fait que de couper vos pubs préférées par des séries débiles et ricaines, vous vous dites : Tiens, si aujourd'hui je faisais un super programme méga-génial sur mon Miga, avec tout plein de couleurs, de fenêtres et de gadgets et de ..." Mais non! Ca vous revient maintenant: vous ne savez pas programmer l'Amiga. Horreur, malheur!
Non car tel le Zorro moyen, me voici accourir pour vous apporter un peu de lumière dans ce monde de brutes, où Billou a fermé la fenêtre...

ATTENTION: Certains des listings de cet article peuvent planter. C'est pourquoi sauvegardez ce qui doit l'être avant de les exécuter! ! Bon, c'est pour les besoins de l'initiation (elle a bon dos !) et uniquement dans cet article, les suivants ne poseront plus de problème... enfin normalement ;-D

Bien choisir son destrier: Le compilo

Le C a toujours été le langage de prédilection sur Amiga, c'est pourquoi un large choix de compilateurs s'offre à nous, qu'ils soient commerciaux ou dans le DP. Evidemment, chaque programmeur dira que le compilo qu'il utilise est le meilleur, cependant, pour le néophyte qui n'est pas encore équipé, voici ceux que je préconise. Ce sont simplement ceux installés sur mes Amiga et dont je parlerai souvent dans mes articles...

  1. La Maserati, son excellence GCC.

    Vous avez un Amiga accéléré (68020 ou plus), beaucoup de mémoire (au moins 8 Mo, 10 de préférence), de la place sur votre disque dur ou un CD-ROM et vous savez ce qu'est un compilateur C : GCC est celui qu'il vous faut !
    Pour le même prix, c'est-à-dire rien, il est gratuit, vous aurez droit au C, C++, Objective C, Ada et même Fortran. Conçu à l'origine pour Unix, vous le retrouverez comme compilateur natif des OS libres comme Linux, NetBSD, FreeBSD, ... mais il existe aussi des portages sur des Unix commerciaux (HP-UX, AIX, Solaris, Digital Unix, ... en fait, je ne crois pas qu'il existe un Unix récent sur lequel il n'ait pas été porté!). On le retrouve aussi sur Vax/VMS, Mac, ST et même sur les Prehistoric Computers !
    Outre le fait qu'il soit toujours plaisant d'utiliser le même compilo d'une machine à l'autre, je lui trouve de nombreux avantages dont :

    Comme inconvénients on notera:

    Allez, première astuce : par défaut, il inclut les informations de débugage dans les exécutables qu'il génère, l'option '-s' ajoutée dans sa ligne de commande diminuera la taille de vos programmes de plusieurs dizaines de Ko. De même, vous pouvez virer sans problème les Hunks "Debug" et "Symbols" de ses propres exécutables ce qui, en plus de libérer de la place sur le disque, accélérera les compilations, le chargement en mémoire...

  2. La deudoche, Dice

    Ce fut mon premier compilo C sur Amiga. On préférera aller le chercher sur Aminet car la dernière archive en date contient la version professionnelle avec TOUTES les sources ! Elle comporte une GUI très bien faite et une interface AREXX complète.
    Relisez mes articles dans les anciens AmigaNews (qui sont aussi présents sur ce site) et vous verrez qu'on peut même l'utiliser sur un simple Amiga 68000 muni d'1 Mo de RAM et sans disque dur !
    Comme avantage supplémentaire, la doc est très bien foutue et comporte une initiation pour débutant, accompagnée d'exemples pour la création d'une librairie, d'un device. Il est aussi très bien intégré au système et gère pour vous l'ouverture automatique des librairies, les arguments du WorkBench... Bon, il contient aussi quelques bugs et l'exécutable n'est pas vraiment optimisé.

  3. Mention spéciale pour le Lattice (ou SAS C)

    Ce vieux compilo, bien que plus vendu depuis un bail, continue à être supporté par ses développeurs qui fournissent régulièrement des patchs. Il possède un avantage indéniable : Commodore l'a utilisé pour développer le système, on trouve donc beaucoup d'exemples de programmation l'utilisant.

Le premier programme

Le voici, le voilà, la hantise de tous les débutants : le fameux Hello World !
En voici le source:
#include <stdio.h>

int main(){
	printf("Salut tout le monde.\n");

	return 0;
}

On compile ici avec GCC, et on l'exécute depuis un shell : "Ça marche !"

Génial, on a réussi à compiler un programme tout con, que même un PC sait faire! Et en plus ça fonctionne ...
"Décidément, on se moque vraiment de notre gueule !! N'importe quoi !" allez vous dire ! Je répond : Que nénni ! Exécutez le maintenant depuis le Workbench ... juste pour voir.
3 Possibilités:

  1. Vous utilisez Dice.
    Il ne se passe ... rien (décidément, c'est vraiment du vol cet article !).
  2. Vous utilisez le Lattice ou GCC.
    Une fenêtre s'ouvre affichant votre fameux message (non ? génial !)
  3. Vous avez pas de chance, visite du Guru. Je crois que c'était le cas avec l'Aztec C.

Explication : Comme vous le savez, le printf() va afficher ses arguments sur le flux de sortie standard, le très connu 'stdout'. Le problème est que si le programme est lancé depuis le WorkBench, ce stdout n'existe pas (ben ouais, on les affiche où ces messages?), d'où le plantage dans le cas de l'Aztec. Le SAS et GCC ont contourné le problème en créant une fenêtre de sortie : Bon c'est une bonne idée mais ça explique pas pourquoi un simple
int main(){
	return 0;
}
génère déjà un exécutable de plusieurs Ko : Il faut du code pour la gérer cette fenêtre !

Regardez bien la doc de votre compilo, elle vous indiquera comment le supprimer et diminuer ainsi la taille de votre exécutable.
Dice, lui, a créé deux entrées dans le programme : le classique main() lorsqu'il s'exécute depuis un shell ou un wbmain()lorsque c'est depuis le Workbench. Si l'utilisateur n'en fournit pas une, il utilise celle par défaut qui ne fait que terminer le programme... C'est elle qui s'exécute dans notre cas.

Moralité : Dans un programme devant s'exécuter depuis le Workbench, de printf(), puts() et dérivés tu n'utiliseras ...

Notre Guru a nous

Après avoir fait ce que tout le monde sait faire, innovons et créons notre propre générateur de... Guru. La fonction DisplayAlert() permet en effet de générer un écran noir, avec un cadre rouge ou jaune (à partir du KS 2.0 uniquement), avec le ou les messages de notre choix.
Voici le listing tant attendu :
#include <exec/types.h>
#include <clib/intuition_protos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(int ac, char **av){
	if(ac<2){
		puts("Syntaxe : MonGuru 'message'");
		exit(EXIT_FAILURE);
	} else {
		int longueur=strlen(av[1]);
		char buff[longueur + 5];    /* Buffer de travail */

			/* Initialisation du buffer */
		*((short *)buff) = 50;      /* position en abscisse (X) */
		buff[2] = 17;               /* position en ordonnées (Y) */
		strcpy(&buff[3],av[1]); /* Copy du message */
		buff[4+longueur]=0;         /* Rien d'autre a afficher */

			/* Notre Guru */
		DisplayAlert(RECOVERY_ALERT,buff,30);

		exit(EXIT_SUCCESS);
	}
}

Evidemment, on ne va pas programmer comme des porcs (on n'est pas chez M$),les deux premières lignes permettent d'inclure le prototype de DisplayAlert() ainsi que les définitions associées. En particulier, notez bien le premier <exec/types.h>, qui définit tous les types couramment utilisés par le système. Les autres includes sont classiques.

Le prototype de la fonction est le suivant :
BOOL DisplayAlert( unsigned long type, UBYTE *message, unsigned long hauteur );
avec

Vous pouvez maintenant compiler le listing. Si une erreur de compilation s'affiche, l'explication arrive, sinon, lancez-le ... après avoir sauvegardé tout ce qu'il y avait à sauvegarder !
Il se peut que ça marche tel quel (!), mais vous avez plus de (mal)chance que ça se termine par le réveil du vrai Guru...

Comme vous le savez certainement, l'AmigaDos n'est pas un bloc monolithique, genre le fameux 'kernel' cher aux UNIX, mais est composé de plusieurs librairies partagées dont chacune a une fonction précise. Si ça plante, il faut regarder la compilation avec GCC pour en trouver la raison.

Tiens, manquerait-il quelque chose ?
DisplayAlert() est une fonction qui fait partie d'Intuition.librairie, et pour l'appeler, le compilo a besoin de savoir où se trouve cette librairie. Hé oui, contrairement à ce qui se passe avec les machines 8 bits, dans l'Amiga rien (ou presque), n'est fixé à priori. A chaque boot de la machine, les librairies se placent où le système leur dit.
DisplayAlert() se trouve donc à une adresse qui est relative à IntuitionBase, que nous devons initialiser, sinon ...Guru.

Voici le nouveau listing, qui fonctionne lui!
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>   
	/* Ces 3 là ne nous servent à rien mais évitent */
#include <exec/semaphores.h>
	   /* des erreurs à la compilation */
#include <exec/io.h>       
	   /* dans exec_protos.h */
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

struct ExecBase        *SysBase;
struct IntuitionBase   *IntuitionBase=NULL;

void fini(){
	if(IntuitionBase){
		CloseLibrary((struct Library *)IntuitionBase);
		IntuitionBase = NULL;
	}
}

int main(int ac, char **av){
	SysBase = *(struct ExecBase **)4;   /* Recherche d'exec */

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))){
		puts("Impossible d'ouvrir intuition.");
		exit(EXIT_FAILURE);
	}
	atexit(fini);

	if(ac<2){
		puts("Syntaxe : MonGuru 'message'");
		exit(EXIT_FAILURE);
	} else {
		int longueur=strlen(av[1]);
		char buff[longueur + 5];    /* Buffer de travail */

			/* Initialisation du buffer */
		*((short *)buff) = 50;      /* position en abscisse (X) */
		buff[2] = 17;               /* position en ordonnées (Y) */
		strcpy(&buff[3],av[1]); /* Copy du message */
		buff[4+longueur]=0;         /* Rien d'autre a afficher */

			/* Notre Guru */
		DisplayAlert(RECOVERY_ALERT,buff,30);

		exit(EXIT_SUCCESS);
	}
}

La seule partie logiciel fixe dans le système est l'adresse d'exec, c'est-à-dire la librairie qui s'occupe du système (allocation mémoire, chargement des librairies, et autres ...). Ben oui, on a beau être relatif à fond, il faut toujours garder une référence.
L'adresse où se trouve Exec est placée à l'adresse 4 de la mémoire et doit être stockée dans une variable nommée... SysBase (logique non?).
On peut ensuite ouvrir Intuition.

Apprenez-lui le caniveau
on doit rendre le système comme on l'a trouvé : Libérer la mémoire, fermer les écrans et les fenêtres... sont des choses auxquelles on pense. N'oubliez pas de fermer aussi les librairies ouvertes!
C'est ici le rôle de la fonction fini() appelé par un atexit(). On est ainsi sûr que quoi qu'il se passe, elle sera appelée.

Notez que OpenLibrary(), outre le nom, demande aussi un entier, qui est le numéro minimum de version de la librairie. Si elle est antérieure, la fonction échoue en renvoyant un NULL. On verra l'utilité plus tard lorsque nous utiliserons des spécificités du 2.0 ou du 3.0. C'est aussi une des forces de l'Amiga par rapport à d'autres systêmes utilisant des librairies partagées : Ici l'AmigaOS nous impose d'avoir une compatibilité ascendante des versions des librairies. Ceux qui programment ou qui gèrent un parc sous windaube savent de quoi je veux parler : l'installation d'un nouveau programme entraine généralement l'écrasement de librairies systême et on met trois semaines à récupérer un truc un minimum stable ... BBBhhhhhaaaaaaa !!!!.
Avec l'Amiga, pas de problème : tout programme d'installation doit (devrait) verifier la version de la librairie.

Ha lala, s'ils réfléchissaient un minimum chez micro$hiot !!!
Pour en revenir a notre listing, avec une version à 0, n'importe quelle version fonctionne et ça devrait même marcher avec un Workbench 1.0 !!!

On notera aussi dans ce listing le cas particulier d'exec. Nous ne l'avons pas ouverte, mais trouvée grâce à son adresse. Ne jamais tenter de la refermer, sinon ... bon vous connaissez la suite !

IInutile de préciser que ce programme ne fonctionne que depuis un Shell, vous l'aurez deviné tous seul. Non ? Bon, d'accord pour cette fois : Il y a un puts() qui utilise le stdout. En plus, mais vous ne pouviez pas le savoir, les arguments du Workbench ne sont pas traités de la même façon.

"Tout bon programmeur doit être un peu feignant !"
C'est chiant de devoir ouvrir nous-mêmes les librairies, non? Beaucoup de compilos permettent de les ouvrir et de les fermer automatiquement, sans intervention du programmeur.

Dans tous les cas, lisez la doc de votre compilo.

Attention : c'est comme tout, en abuser se révèle dangereux, car à force, vous allez oublier de le faire. Et quand il s'agit d'une librairie rajoutée (genre req.librairie), le compilo ne saura pas comment se dépatouiller tout seul...

Bon, voilà, c'est fini pour aujourd'hui.

Grand merci à mes très courageux correcteurs :

Evidemment, comme ceci est le premier article de cette serie, il ne devrait pas y avoir de "Courriers des lecteurs". Voici cependant quelques questions qui m'ont été posées par mes (très) courageux correcteurs :