Obligement - L'Amiga au maximum

Dimanche 26 septembre 2021 - 14:10  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Quizz
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Trucs et astuces
 · Articles divers

 · Articles in english


Twitter

Suivez-nous sur Twitter




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

 · Menu des galeries

 · BD d'Amiga Spécial
 · Caricatures Dudai
 · Caricatures Jet d'ail
 · Diagrammes de Jay Miner
 · Images insolites
 · Fin de jeux (de A à E)
 · Fin de Jeux (de F à O)
 · Fin de jeux (de P à Z)
 · Galerie de Mike Dafunk
 · Logos d'Obligement
 · Pubs pour matériels
 · Systèmes d'exploitation
 · Trombinoscope Alchimie 7
 · Vidéos


Téléchargement

 · Documents
 · Jeux
 · Logiciels
 · Magazines
 · Divers


Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : Amiga C Manual - Introduction
(Article écrit par Anders Bjerin et extrait d'Amiga C Manual - mars 1990)


Note : traduction par Serge Hammouche.

0.1 Introduction

Dans ce chapitre, nous allons voir comment les compilateurs C fonctionnent, comment utiliser le compilateur Lattice C, rappeler des points importants en C, finalement expliquer comment utiliser les bibliothèques de l'Amiga et parler des trois différents types de mémoire. Comme vous l'avez remarqué, ce chapitre vous donne une petite introduction au C, aux compilateurs et à l'Amiga.

0.2 Comment compiler et éditer des liens

Le principe des compilateurs C est que vous écrivez un document (habituellement appelé "code source"), qui est lu en deux phases par le compilateur C. La première fois, le compilateur C crée un fichier de travail, appelé "quad file". Le quad file est alors compilé une deuxième fois en un "object file".

Le fichier objet contient essentiellement du code machine exécutable mais aussi des références à des fonctions/variables externes dans des fichiers objet et des bibliothèques. Ainsi, avant de pouvoir lancer le programme, nous devons y lier tous les fichiers objet et bibliothèques en un seul programme. C'est ce que fait l'éditeur de liens.

---------------
| Programme.c |   Code Source
---------------
       |
       |          1. Première phase de compilation (lc1)
       V
---------------
| Programme.q |   Quad file
---------------
       |
       |          2. Deuxème phase de compilation (lc2)
       V
---------------
| Programme.o |   Fichier Objet
---------------
       |
       |          3. Édition de liens (blink)
       V
---------------
|  Programme  |   Programme exécutable
---------------

0.2.1 Le code source

Quand vous voulez écrire des programmes en C, il vous faut un éditeur/ traitement de texte pour créer le "code source". Le code source ressemble à un document normal sauf qu'il est écrit en suivant des règles particulières. Presque tous les éditeurs/traitements de texte sont utilisables. Vous pouvez même utiliser le MEMACS qui vous est fourni à l'achat de votre Amiga (Memacs est sur la disquette "Extras".) Voici un exemple de code source :

#include <stdio.h>

main()
{
  printf("Hello!\n");
}

Quand vous avez écrit le code source, vous devez le sauvegarder. Le standard est de terminer les noms de fichiers par l'extension ".c". Ainsi, si vous voulez appeler votre programme "hello", le code source doit être appelé "hello.c".

0.2.2 La première phase de compilation

Une fois le code source sauvegardé, vous pouvez lancer la première phase de compilation. Si vous utiliser le compilateur Lattice C, utilisez simplement la commande "lc1". Pour compiler notre exemple, il suffit de taper "lc1 hello.c", et le compilateur va alors créer un fichier quad appelé "hello.q".

0.2.3 La deuxième phase de compilation

Pour lancer la deuxième phase de compilation, vous utilisez la commande "lc2". Pour compiler notre exemple, vous n'avez qu'à taper "lc2 hello.q", et le compilateur va alors créer un fichier objet appelé "hello.o".

Si vous voulez lancer la première et la deuxième passe du compilateur en une seule fois, vous pouvez utiliser la commande "lc". Ainsi, si vous tapez "lc hello.c", la première et la deuxième phase de compilation seront toutes deux exécutées (lc lance lc1 et lc2 automatiquement).

0.2.4 L'édition de liens

Comme nous l'avons déjà vu, le fichier quad ne contient pas que des instructions en langage machine, il comporte aussi des appels à des fonctions externes dans des bibliothèques et d'autres fichiers objet. Avant de pouvoir exécuter le programme, vous devez donc lier le tout grâce à l'éditeur de liens. Lattice C utilise un éditeur de liens appelé "blink".

Tous les programmes que vous écrivez doivent normalement être liés avec la routine de démarrage "c.o", et les deux bibliothèques : "lc.lib" et "amiga.lib".

Le fichier objet et les deux bibliothèques sont dans le répertoire "lib" de la deuxième disquette du Lattice C. Ce répertoire est normalement déjà assigné à "LIB:", donc si vous voulez accéder aux fichiers, tapez simplement "LIB:c.o" par exemple.

Pour la majorité des programmes, appelez l'éditeur de liens suivant cette syntaxe (ceci montre comment lier notre exemple "hello") :

blink LIB:c.o, hello.o  TO hello  LIB LIB:lc.lib, LIB:amiga.lib
        [A]      [B]       [C]    [D]     [E]          [F]

[A] Le fichier objet "c.o" doit toujours être placé avant tous les autres fichiers objet. Ce fichier se trouve dans l'unité logique "LIB:".

[B] Après le fichier de démarrage "c.o", vous pouvez placer tous les fichiers objet que vous voulez lier ensemble (pensez à mettre une virgule entre chacun d'eux). Dans cet exemple, nous n'avons qu'un fichier objet, "hello.o".

[C] Après la liste des fichiers objet, tapez "TO" suivi du nom qui sera celui du programme exécutable. Dans cet example, nous voulons que notre programme s'appelle "hello".

[D] Presque tous les programmes doivent être liés à des bibliothèques. Le mot-clef "LIB" indique à blink que les noms de fichier suivants sont des noms de bibliothèques (pensez à mettre une virgule entre chaque nom de bibliothèque).

Ne confondez pas le mot-clef "LIB" avec l'unité logique "LIB:". "LIB" indique à l'éditeur de liens qu'une liste des noms de bibliothèques va suivre, tandis que l'unité logique "LIB:" indique à AmigaDOS sur quelle unité et dans quel répertoire trouver certains fichiers.

[E] La bibliothèque "lc.lib" doit être l'avant-dernière bibliothèque de la liste (la bibliothèque se trouve dans l'unité logique "LIB:").

[F] La bibliothèque "amiga.lib" doit être la dernière bibliothèque de la liste (la bibliothèque se trouve dans l'unité logique "LIB:").

Si vous voulez appeler à la fois la première et la deuxième passe du compilateur et lier le fichier objet avec les bibliothèques standard, vous pouvez utiliser la commande "lc" avec le paramètre "-L". Ainsi, si vous voulez compiler et lier le programme avec une seule commande, vous n'avez qu'à taper :

lc -L hello.c

La commande "lc" appelle la première et la deuxième passe du compilateur, et le paramètre "-L" signifie que le code objet final doit être lié avec les bibliothèques standard.

Si vous voulez aussi que l'éditeur de liens cherche la bibliothèque mathématique standard, ajoutez simplement la lettre "m" après le "-L" :

lc -Lm 

Notre exemple "hello" ne nécessite pas la bibliothèque mathématique mais si vous faites un quelconque calcul, vous devez normalement l'inclure. Tous les exemples de ce manuel seront compilés et liés sans problème avec la commande :

lc -Lm ExempleX.c

0.3 Les programmes C

Ce manuel s'adresse à tous ceux ayant quelques notions de C et désirant programmer l'Amiga. Si vous savez écrire un programme qui affiche "hello!" à l'écran, et que vous n'êtes pas complètement embrouillé par les pointeurs et les structures, je ne pense pas que vous ayez de problèmes en lisant ce manuel. Tous les exemples ont étés écrits avec autant de clarté possible et j'ai évité plusieurs raccourcis afin de les rendre plus facilement compréhensibles.

Néanmoins, avant de commencer le vrai manuel, je pense qu'il est préférable de rappeler d'importantes particularités du C.

0.3.1 Fichier d'en-tête #Include

Les premières choses que vous trouverez dans un programme C sont les commandes "#include". Elles indiquent au compilateur qu'il faut lire certains fichiers (fichiers d'en-tête) avant de compiler le code et peuvent aussi contenir des définitions de constantes/structures/macros, etc. Les fichiers sont normalement référencés comme "fichier d'en-tête" ("header files") et ont pour cette raison l'extension ".h".

Il y a deux types de fichiers d'en-tête ; les vôtres et les définitions standard du système. Si vous incluez vos propres fichiers d'en-tête, mettez le nom du fichier entre guillemets :

#include "mydefinitions.h"

Le compilateur va alors chercher votre fichier dans le "répertoire courant". Si au contraire vous incluez des définitions standard du système, mettez le nom du fichier entre des signes de comparaison, le compilateur les cherchera alors dans l'unité logique "INCLUDE:" :

#include <intuition/intuition.h>

Notez qu'il n'y a pas de point-virgule après les #include ! C'est parce que les fichiers d'en-tête sont analysés par un macro préprocesseur (compris dans la première phase de compilation, lc1) et n'ont en fait rien à voir avec le compilateur C.

Plus loin dans le manuel nous inclurons souvent le fichier intuition.h qui contient les définitions de plusieurs structures (telles Window, Border, Gadget, etc.) et quelques constantes et macros importantes. Celui-ci et tous les autres fichiers en-têtes standard du système sont compressés sur la deuxième disquette du Lattice C. Si vous voulez voir ce qu'ils définissent vraiment, vous pourrez trouver les fichiers non compressés sur la quatrième disquette du Lattice C.

0.3.2 #Define

Après les #include viennent les #define. La commande #define vous permet non seulement déclarer les constantes et les chaînes de remplacement, mais aussi de déclarer vos propres macros.

Il faut se souvenir que la première chaîne définie est remplacée par la deuxième par le macro préprocesseur. Le premier espace sépare la première chaîne de la deuxième. Notez le sous-tiret entre MAX et WIDTH, et les guillemets à Anders Bjerin :

#define MAX_WIDTH 150
#define AUTHOR "Anders Bjerin"

Le macro préprocesseur remplacera toutes les chaînes MAX_WITH par 150, et toutes les chaînes AUTHOR par "Anders Bjerin".

S'il y a des parenthèses dans la première chaîne, alors vous déclarez une macro :

#define SQUARE(x) x*x

La première chaîne est remplacée normalement par la deuxième mais tous les éléments (séparés par des virgules s'il y en a plusieurs) entre parenthèses sont recopiés dans la nouvelle formule. Une telle ligne :

nr = SQUARE(4);

...sera remplacé comme suit :

nr = 4*4;

Il est donc important de se rappeler que le préprocesseur considère tout comme des chaînes et une telle ligne :

nr = SQUARE(4+1);

...sera remplacé comme suit :

nr = 4+1*4+1

Le carré (square) de 4+1 est 25 (5*5) mais nr est égal à 9. Ce qui prouve que vous devez être très prudent quand vous définissez vos macros. Pour résoudre ce problème, vous n'avez qu'à rajouter des parenthèses autour des x :

#define SQUARE(x) (x)*(x)
nr = SQUARE(4+1);

...sera alors remplacé comme suit :

nr = (4+1)*(4+1);

Même ainsi, la macro n'est pas encore parfaitement définie. Considérez cette expression :

nr = 16 / SQUARE(2);

Elle sera alors remplacée comme suit :

nr = 16 / (2)*(2);

16 divisé par 2 au carré donne 16/4, qui est égal à 4 mais nr est égal à 16 (16/2*2). La solution à tous ces problèmes est de mettre des parenthèses autour de chaque élément, plus des parenthèses autour de l'expression elle-même :

#define SQUARE(x) ((x)*(x))
nr = 16 / SQUARE(2);

...sera alors remplacé comme suit :

nr = 16 / ((2)*(2)); /* OK */

Dans les macros, vous devez penser à :
  1. Mettre des parenthèses autour de chaque élément.
  2. Mettre aussi des parenthèses autour de l'expression elle-même.
Voici une liste des macros couramment utilisées :
  • #define MAX(x,y) ((x)>(y)?(x):(y))
  • #define ABS(x) ((x)<0?-(x):(x))
  • #define ANSWER(c) ((c)=='y'||(c)=='Y'?1:0)
Pensez, si vous avez écrit des constantes/macros intéressantes, vous pouvez toutes les mettre dans un fichier en-tête et l'inclure à chaque fois que vous en aurez besoin.

0.3.3 Les autres commandes du préprocesseur

#include et #define sont les deux commandes du préprocesseur les plus couramment utilisées, mais il en existe cinq autres qui peuvent être utilisées conjointement à la commande #define :
  1. Vous pouvez vérifier que quelque chose a été défini et alors définir autre chose : (#ifdef).
  2. Vous pouvez vérifier que quelque chose n'a pas été défini et alors définir autre chose : (#ifndef).
  3. Vous pouvez mettre un "else" en relation avec un #ifdef/ifndef : (#else).
  4. Vous pouvez marquer la fin d'un #ifdef/#ifndef : (#endif).
  5. Vous pouvez annuler une définition précédente : (#undef).
Voici des exemples qui vous montrent comment utiliser les commandes du préprocesseur :

#define BIG 1

#ifdef BIG
  #define HEIGHT 100  /* Si BIG est défini, HEIGHT et WIDTH */
  #define WIDTH  300  /* sont définis à 100 et 300.         */
#else
  #define HEIGHT  50  /* Si BIG n'est pas défini, HEIGHT et */
  #define WIDTH   25  /* WIDTH sont définis à 50 et 25.     */
#endif

Ces commandes sont très pratiques quand vous #incluez plusieurs fichiers, et que vous voulez vérifier que quelque chose a été défini. Si ça n'est pas le cas, vous pouvez inclure d'autres fichiers et ainsi de suite...

#ifndef MY_DEFINITIONS      /* Si MY_DEFINITIONS n'est pas défini */
  #include "mydefinitions"  /* nous incluons "mydefinitions".     */
#endif

La dernière commande du préprocesseur, #undef, annule la dernière définition :

#define HEIGHT 200  /* HEIGHT est défini à 200.            */
#define HEIGHT 350  /* HEIGHT est redéfini à 350.          */
#undef HEIGHT       /* HEIGHT la dernière définition est   */
                    /* annulée et est redéfinie à 200      */
                    /* comme précédemment.                 */
#undef HEIGHT       /* HEIGHT n'est plus défini.           */

0.3.4 Les fonctions

Les compilateurs C s'attendent généralement à trouver les définitions des fonctions (quel que soit le type de donnée que la fonction retourne) avant qu'il n'y soit fait référence. Comme la fonction main() est la première et qu'elle contient probablement des références à d'autres fonctions situées plus loin, nous devons déclarer les fonctions avant la fonction main(). Quand une fonction est définie avant d'être déclarée, il faut mettre un point-virgule après. Le compilateur comprend alors que nous avons seulement défini une fonction et que nous la déclarerons par la suite :

#include <stdio.h>

void main();             /* Ne retourne rien.                         */
int lit_rayon();         /* Retourne une valeur entière.              */
float calcule_aire();    /* Retourne une valeur en virgule flottante. */
void affiche_aire();     /* Ne retourne rien.                         */

void main()
{
  int rayon;
  float aire;

  rayon = lit_rayon();
  aire = calcule_aire( rayon );
  affiche_aire( aire );
}

int lit_rayon()
{
  int r;

  printf( "Entrez le rayon: " );
  scanf( "%d", &r );

  return( r );
}

float calcule_aire( r )
int r;
{
  float a;

  a = 3.14 * r * r;

  return( a );
}

void affiche_aire( a )
float a;
{
  printf( "L'aire est %f de unités carrées.\n", a );
}

0.3.5 Les variables

Voici les types de données standard du Lattice C :

-------------------------------------------------------------------
|  TYPE            | BITS |  Valeur Minimale  |  Valeur Maximale  |
|-----------------------------------------------------------------|
|  char            |   8  |             -128  |              127  |
|  unsigned char   |   8  |                0  |              255  |
|  short           |  16  |          -32 768  |           32 767  |
|  unsigned short  |  16  |                0  |           65 535  |
|  int             |  32  |   -2 147 483 648  |    2 147 483 647  |
|  unsigned int    |  32  |                0  |    4 294 987 295  |
|  long            |  32  |   -2 147 483 648  |    2 147 483 647  |
|  unsigned long   |  32  |                0  |    4 294 987 295  |
|  float           |  32  |          ±10E-37  |          ±10E+38  |
|  double          |  64  |         ±10E-307  |         ±10E+308  |
-------------------------------------------------------------------

Attention, ces types de données peuvent être différents sur d'autres C, méfiez-vous quand vous les utilisez. Pour être sûr que la taille des variables soit la même sur différents systèmes, il y a une liste spéciale de définitions de variable. Vous n'avez qu'à inclure le fichier "exec/types.h" et vous avez une liste fiable de types de variables. Le fichier définit, entre autres choses, ces types de données couramment utilisés :

Type de donnée Amiga  Type de donnée Lattice C  Description
-------------------------------------------------------------------------

LONG                  long                      Signé 32 bits
ULONG                 unsigned long             Non signé 32 bits
LONGBITS              unsigned long             32 bits manipulation

WORD                  short                     Signé 16 bits
UWORD                 unsigned short            Non signé 16 bits
WORDBITS              unsigned short            16 bits manipulation

BYTE                  char                      Signé 8 bits
UBYTE                 unsigned char             Non signé 8 bits
BYTEBITS              unsigned char             8 bits manipulation

VOID                  void                      Rien
STRPTR                *unsigned char            Pointeur sur une chaîne
CPTR                  ULONG                     Pointeur mémoire absolu

TEXT                 unsigned char              Text
BOOL                 short                      Boolean (Le fichier
                                                défini aussi les deux
                                                mots TRUE = 1 et
                                                FALSE = 0)
-------------------------------------------------------------------------

Voici une liste de types de données qui ne doivent plus être utilisés :

-------------------------------------------------------------------------
APTR               *STRPTR                Pointeur mémoire absolu
                                          (impropre, utilisez CPTR !)
SHORT              short                  Signé 16-bits   (WORD)
USHORT             unsigned short         Non signé 16-bits (UWORD)
-------------------------------------------------------------------------

Voyez le fichier "exec/types.h" de la quatrième disquette Lattice C pour plus d'informations. Ce fichier est automatiquement inclus quand vous incluez le fichier d'en-tête "intuition.h".

0.3.6 Les classes d'allocation des variables

Il y a six types principaux de classes d'allocation des variables :
  1. Automatic
  2. Formal
  3. Global
  4. External Static
  5. Internal Static
  6. Register
0.3.6.1 Automatic

Les variables Automatic sont déclarées dans les fonctions, précédées du mot-clef "auto". Les variables qui sont déclarées dans une fonction sans être précédées d'un mot-clef sont considérées comme des variables Automatic. De la place sur la pile leur est réservée et elles ne peuvent être utilisées que par la fonction dans laquelle elles ont été déclarées. Quand la fonction prend fin, la mémoire est automatiquement restituée à la pile.

Voici un petit programme récursif. L'utilisateur peut entrer autant de valeurs qu'il veut jusqu'à ce qu'il entre 0 et alors tous les nombres sont affichés dans l'ordre inverse.

#include <stdio.h>

main()
{
  lit_nombre();
}

lit_nombre()
{
  auto int nombre; /* <== Le voilà !             */
                   /* Le mot-clef "auto" est facultatif. */

  printf( "Entrez un nombre (0: Quitter) =>" );
  scanf( "%d", &nombre );
  if( nombre )
    lit_nombre();
  else
    printf( "%d\n", nombre );
}

0.3.6.2 Formal

Très ressemblant aux variables Automatic. De la mémoire est réservée dans la pile, elles ne peuvent être utilisées que par la fonction dans laquelle elles ont été déclarées, la mémoire est automatiquement restituée à la pile quand la fonction prend fin. La seule différence est qu'elles sont les "paramètres" de la fonction et sont déclarées après les parenthèses mais avant les accolades.

#include <stdio.h>

void main();
void affiche_nombre();

void main()
{
  affiche_nombre( 3.142 );
}

void affiche_nombre( nombre )
float nombre;                /* <== Le voila! */
{
  printf( "Le nombre est %f!\n", nombre );
}

0.3.6.3 Global

Les variables Global sont déclarées en dehors des fonctions et sont donc accessibles de n'importe quel endroit du programme. De la mémoire est réservée dans les segments ("hunks") BSS ou DATA et ne peut pas être désallouée. Toutes les données non initialisées sont mises dans les segments BSS, tandis que les données initialisées sont mises dans les segments DATA.

int nombre;        /* Stocké dans le segment BSS.  */
int largeur = 12;  /* Stocké dans le segment DATA. */

Les informations sur les variables Global sont mises dans le fichier objet afin que les autres modules puissent accéder aux variables. Si une variable est déclarée comme variable Global (déclarée en dehors des fonctions) dans le module A, la variable doit être déclarée comme une variable External Global (déclarée en dehors des fonctions et précédée du mot-clef "extern") dans le module B.

Module A

#include <stdio.h>

void main();

char nom = "Andirs Bjerin";  /* Déclare cette variable comme une */
                             /* variable Global (DATA)           */
void main()
{
  affiche_nom();
}

Module B

#include <stdio.h>

void affiche_nom();

extern char nom;   /* Indique au compilateur que cette variable existe */
                   /* déjà mais est déclarée dans un autre module.    */
void affiche_nom()
{
  nom[ 3 ] = 'e';
  printf("Programmeur: %s\n", nom );
}

Pour compiler ces deux modules et les lier, vous n'avez qu'à :
  1. Compiler (lc1 et lc2) Module A: lc ModuleA.c.
  2. Compiler (lc1 et lc2) Module B: lc ModuleB.c.
  3. Lier les deux modules objets avec d'autres bibliothèques, etc. :
blink LIB:c.o, ModuleA.o, ModuleB.o TO program
LIB LIB:lc.lib, LIB:amiga.lib

0.3.6.4 External Static

Comme les variables Global, mais ne peuvent être utilisées qu'à l'intérieur du même module. Toutes les fonctions de ce module auront accès à ces variables mais aucune fonction d'un autre module ne peut y accéder. La mémoire est réservée dans les segments BSS ou DATA mais aucune information sur ces variables n'est placée dans le fichier objet.

Pour déclarer une variable External Static vous n'avez qu'à ajouter le mot-clef "static" avant un nom de variable Global.

static nombres[ 20 ];

0.3.6.5 Internal Static

Comme les variables External Static sauf qu'elles sont déclarées dans les fonctions et ne peuvent être utilisées que par ces fonctions. La particularité des variables Internal Static est que la mémoire n'est pas désallouée quand la fonction prend fin et les valeurs mémorisées ne sont pas modifiées. La mémoire est réservée dans les segments BSS ou DATA. Une variable Internal Static n'est initialisée qu'une fois et peut par la suite être modifiée mais pas désallouée.

#include <stdio.h>

void main();
void compteur();

void main()
{
  int boucle;

  for( boucle = 0; boucle < 20; boucle++ )
    compteur();
}

void compteur()
{
  static nombre = 1; /* Nous déclarons et initialisons une variable  */
                     /* Internal Static. Comme le nombre est une     */
                     /* variable Static elle ne sera initialisée que */
                     /* la première fois que nous appellerons la     */
                     /* fonction (DATA)                             */

  nombre++;

  printf("nombre: %d\n", nombre );
}

0.3.6.6 Register

Les variables Register sont comme les variables Automatic sauf que vous demandez au compilateur si la variable peut être autorisée à utiliser un des registres du processeur. C'est très pratique si vous utilisez beaucoup une variable, dans une boucle par exemple. Néanmoins, il n'est pas certain que vous pourrez réserver un registre. Si vous avez de la chance le compilateur arrivera à réserver un registre pour la variable mais ça n'est pas toujours possible. La variable Register sera alors transformée en une variable Automatic normale.

#include <stdio.h>

void main();

void main()
{
  register int boucle; /* <== La voilà ! */

  for( boucle = 0; boucle < 10000; boucle++)
    ;
}

0.3.7 Les pointeurs

Les pointeurs sont déclarés comme les variables normales avec cependant une différence importante. Nous ajoutons un signe "*" devant le nom :

int a;  /* "a" est une variable Integer.            */
int *b  /* "b" est un pointeur de variable Integer. */

Si nous voulons obtenir l'adresse d'une variable nous ajoutons un "&" devant le nom de la variable. "a" contient une valeur Integer tandis que "&a" est l'adresse mémoire de cette variable. Si nous voulons que le pointeur "b" pointe sur la variable "a", il suffit d'écrire :

b = &a; /* "b" contiendra l'adresse de la variable "a". */

Il faut se rappeler que "b" est un pointer de variable ("b" peut pointer sur n'importe quelle variable Integer), tandis que "&a" est le pointeur constant ("&a" sera toujours l'adresse de "a").

0.3.8 Les structures

Je n'essayerai pas de tout expliquer sur les structures, mais je vais quand même vous donner un bref aperçu de la façon de les utiliser.

0.3.8.1 Comment déclarer une structure

Vous déclarez un type structure comme suit :
  1. Tapez le mot-clef "struct" plus un nom pour le type structure.
  2. Mettez tous les types de variables avec leurs noms entre accolades.
Si vous voulez déclarer un type structure appelé "personne" constitué d'un champ Integer, appelé "age" et de deux champs float appelés "taille" et "poids", écrivez :

struct personne
{
  int age;
  float taille, poids;
}

Vous pouvez mettre vos déclarations de structures d'usage courant dans un fichier d'en-tête et l'inclure quand vous en aurez besoin. Quand nous programmerons l'Amiga nous utiliserons de nombreuses structures déclarées qui sont principalement dans le fichier d'en-tête "intuition.h".

0.3.8.2 Comment modifier les champs d'une structure

Quand vous avez déclaré un type structure, vous pouvez déclarer des variables et des pointeurs structure. Pour déclarer une variable structure appelée "anders" et un pointeur structure appelé "programmeur" vous écrivez :

struct personne anders;

struct personne *programmeur;

Vous pouvez maintenant initialiser la variable structure "anders". Pour ce faire, écrivez le nom de la variable structure, suivi d'un point, et enfin écrivez à quel champ vous voulez accéder. Ainsi, si vous voulez mettre l'âge dans la variable structure "anders" à 20, la taille à 1.92, et le poids à 74.5, écrivez :

anders.age = 20;
anders.taille = 1.92;
anders.poids = 74.5;

0.3.8.3 Pointeurs et structures

Si vous voulez initialiser le pointeur "programmeur" pour qu'il pointe sur la structure "anders", écrivez :

programmeur = &anders;

Attention : "anders" est la variable structure. "&anders" est l'adresse de la variable structure.

Quand le pointeur pointe sur la structure, vous pouvez commencer à contrôler et modifier les paramètres de cette structure. Pensez à ne pas mettre un point entre le nom du pointeur et celui du champ mais un signe "->", ceci afin que le pointeur "programmeur" pointe sur le champ "taille": programmeur -> taille, etc.

if( programmeur -> age == 20 )
  /* Le programmeur à 20 ans. */

programmeur -> poids -= 5; /* Le programmeur est au régime. */

0.3.9 Les conversions

Si vous voulez convertir un type de donnée en un autre, il faut effectuer une conversion ("casting"). Pour convertir une variable WORD en une variable LONG vous devez écrire :

WORD nombre;
LONG data;

data = (LONG) nombre;

La commande (LONG) indique au compilateur C que le prochain type de donnée doit être converti en une variable de type LONG.

Quand vous convertissez différents types de variable, vous n'avez normalement pas besoin de faire une conversion. Dans le cas précédent, vous auriez pu supprimer la commande (LONG) mais comme ça confirme à tout lecteur qu'il n'y à pas d'erreur dans le code, il est préférable de la laisser.

Cependant, quand vous convertissez différents types de pointeurs, vous devez utiliser la méthode de conversion. Comme les erreurs les plus courantes en C viennent des pointeurs, les compilateurs C sont souvent très rigoureux quant à savoir quel pointeur peut pointer sur quelles variables/structures, etc.

Si vous avez déclaré un pointeur comme un pointeur mémoire normal CPTR (programmeur) et vous voulez qu'il pointe sur une structure (personne), le compilateur va être embrouillé.

struct personne anders;
CPTR programmeur;

programmeur = &anders;

Un pointeur mémoire et un pointeur sur une structure sont en fait identiques mais ce paranoïaque de compilateur refuse de l'admettre et nous envoie un avertissement du genre :

Exemple.c 27 Warning 30: pointers do not point to same type of object

Pour indiquer au compilateur que les deux différents types d'adresses mémoire sont en fait les mêmes, vous devez faire une conversion. Vous devez indiquer au compilateur que le pointeur sur la structure anders (&anders) est en fait un pointeur mémoire (CPTR).

programmeur = (CPTR) &anders;

0.4 Les bibliothèques

Plusieurs fonctions spéciales ont été déjà écrites pour vous et placées dans différentes "bibliothèques". Si vous voulez accéder à l'une de ces fonctions, vous n'avez qu'à "ouvrir" la bonne bibliothèque. Vous ouvrez une bibliothèque en appelant la fonction OpenLibrary() et elle retourne un pointeur sur la structure bibliothèque.

Il y a deux types de bibliothèques, les bibliothèques ROM qui sont placées en ROM (étonnant, non ?) et les bibliothèques sur disquette qui sont placées sur la disquette de démarrage ("System Disk").

0.4.1 Les bibliothèques ROM

Les bibliothèques ROM sont toujours accessibles (elles sont dans la ROM) mais doivent quand même être ouvertes avant que vous n'utilisiez leurs fonctions. Voici la liste des bibliothèques ROM :
  • Graphics : cette bibliothèque contient toutes les fonctions ayant un rapport quelconque avec les graphismes : affichage (View, ViewPorts et RastPorts), sprites (Sprites matériels/logiciels et BOB), texte et polices, etc. Le chapitre 10 et quelques exemples des chapitres 1 à 9 utilisent cette bibliothèque.
  • Layers : cette bibliothèque contient les routines qui gèrent l'écran de façon à ce que les différents "layers" (plans) puissent être utilisés et se chevaucher les uns les autres.
  • Intuition : cette bibliothèque s'occupe de "l'interface utilisateur" et fonctionne avec les bibliothèques Graphics et Layers. Gère les écrans/fenêtres/gadgets/requêtes/menus, etc. Les chapitres 1 à 9 expliquent l'utilisation de cette bibliothèque.
  • Exec : s'occupe des mémoire/tâches/bibliothèques/périphériques logiques/ressources/ports d'entrées/sorties, etc.
  • DOS : AmigaDOS. Contient les routines d'ouverture/fermeture/lecture/écriture des fichiers, etc.
  • MathFFP : Motorola Fast Floating Point Routines. Gère les additions, soustractions, divisions, multiplications, comparaisons, conversions, etc. (simple précision).
  • RAM-LIB : s'occupe de la disquette RAM.
  • Expansion : gère les connecteurs d'extension.
0.4.2 Les bibliothèques sur disquette

Si un programme ouvre une bibliothèque sur disquette, AmigaDOS va la chercher dans l'unité logique "LIBS:", qui a été assignée au répertoire "libs" de la disquette système et va alors charger toute la bibliothèque en RAM. Si la bibliothèque a déjà été chargée en RAM par une autre tâche, Exec utilisera alors la bibliothèque existante. Cela prouve l'efficacité des bibliothèques en mémoire puisque plusieurs programmes peuvent utiliser le même code.

Voici la liste des bibliothèques sur disquette :
  • Translator : cette bibliothèque traduit des chaînes en anglais en chaînes phonétiques.
  • Diskfont : gère les polices de caractères sur disquette.
  • Icon : s'occupe des "icônes" utilisées par le Workbench.
  • Mathtrans : contient des fonctions mathématiques comme sin, cos, log, ln, carré, puissance, etc.
  • Mathieeedoubbas : comme la bibliothèque ROM "MathFFP" mais en double précision.
0.4.3 Ouverture et fermeture des bibliothèques

Comme vu précédemment, vous ouvrez une bibliothèque en appelant la fonction OpenLibrary(). Elle retourne un pointeur sur une structure bibliothèque et ce pointeur doit être stocké dans une variable pointeur avec un nom spécial. Si vous ouvrez par exemple la bibliothèque Intuition, le pointeur doit être appelé "IntuitionBase" et si vous ouvrez la bibliothèque Graphics, le pointeur doit être appelé "GfxBase".
  • Synopsis : pointer = OpenLibrary( name, version );
  • pointer : (struct Library *). Pointeur sur une structure bibliothèque. Si vous ouvrez la bibliothèque Intuition, c'est un pointeur sur une structure IntuitionBase, et il doit être appelé "IntuitionBase". Si vous ouvrez la bibliothèque Graphics, c'est un pointeur sur une structure GfxBase et il doit être appelé "GfxBase". Il vous faudra effectuer des conversions.
  • name : (char *) Pointeur sur une chaîne contenant le nom de la bibliothèque. La bibliothèque Intuition s'appelle "intuition.library" et la bibliothèque Graphics s'appelle "graphics.library".
  • version : (long) Vous pouvez préciser ici que l'utilisateur requiert une version donnée (ou supérieure) de le bibliothèque pour exécuter le programme. Si n'importe quelle version vous convient, prenez simplement 0. La version de bibliothèque la plus élevée actuellement est 34.
Chaque bibliothèque qui a été ouverte doit être fermée avant que le programme ne prenne fin. Vous fermez une bibliothèque en appelant la fonction CloseLibrary() avec le pointeur sur la bibliothèque comme unique paramètre. Les bibliothèques ROM n'ont pas vraiment besoin d'être fermées puisqu'elles sont en ROM et n'occupent pas de précieuse mémoire mais fermez-les quand même. Suivez toujours cette règle : fermez tout ce que vous avez ouvert !
  • Synopsis : CloseLibrary( pointer );
  • pointer : (struct Library *). Pointeur sur la structure bibliothèque qui a été auparavant initialisée par un appel d'OpenLibrary().
Dans ce manuel, nous ne parlerons que des fonctions des bibliothèques Intuition et Graphics. Voici une partie d'un programme qui ouvre et ferme la bibliothèque Intuition :

struct IntuitionBase *IntuitionBase;

main()
{
  /* Ouvre la bibliothèque Intuition (n'importe quelle version): */
  IntuitionBase = (struct IntuitionBase *)
    OpenLibrary( "intuition.library", 0 );

  if( IntuitionBase == NULL )
    exit(); /* Impossible d'ouvrir la bibliothèque Intuition! */

  ... ...

  /* Ferme la bibliothèque Intuition: */
  CloseLibrary( IntuitionBase );
}

Voici une autre partie de programme qui cette fois ouvre et ferme la bibliothèque Graphics :

struct GfxBase *GfxBase;

main()
{
  /* Ouvre la bibliothèque Graphics (n'importe quelle version): */
  GfxBase = (struct GfxBase *)
    OpenLibrary( "graphics.library", 0 );

  if( GfxBase == NULL )
    exit(); /* Impossible d'ouvrir la bibliothèque Graphics! */

  ... ...

  /* Ferme la bibliothèque Graphics: */
  CloseLibrary( GfxBase );
}

0.5 La mémoire

L'Amiga 1000 peut être étendu jusqu'à un total de 9 Mo de mémoire. Cette mémoire peut être divisée en trois types différents :

Mémoire Chip : les 512 premiers ko sont normalement appelés "mémoire Chip", et le processeur, les coprocesseurs et les autres puces de l'Amiga peuvent y accéder. Tous les graphismes/sons, etc. qui sont utilisés par les puces (Blitter, etc.) doivent s'y trouver !

Mémoire Slow : les 512 ko suivants (la mémoire supplémentaire qui se trouve dans l'Amiga) auxquels seul le processeur peut avoir accès et qui ne peuvent donc pas être utilisés pour stocker des données graphiques et sonores. Ils sont appelés "Slow" car bien que les puces n'y aient pas accès, ils peuvent y faire des interruptions. Si vous avez un écran en haute résolution et/ou avec beaucoup de couleurs, cette mémoire sera ralentie car les puces lui voleront des cycles mémoire.

Il y a un nouveau jeu de puces qui permet aux coprocesseurs et aux puces d'utiliser aussi cette mémoire, ce qui signifie qu'elle peut alors contenir des données graphiques/sonores, etc. Néanmoins, les Amiga OCS ont encore les anciennes puces, et cette mémoire ne peut pas être utilisée pour les données graphiques/sonores.

Mémoire Fast : le troisième type de mémoire est constitué des 8 Mo suivants, qui ne sont accessibles qu'au processeur principal et ne sont pas ralentis par les puces. Cette mémoire s'appelle donc "mémoire Fast".

Il faut se rappeler que toutes les données graphiques et sonores (toutes les données utilisées par les puces) doivent être stockées en mémoire Chip ! Il y a trois façons de le faire :
  1. Si vous utilisez le compilateur Lattice C V5.00 ou plus, vous pouvez mettre le mot-clef "chip" avant le nom des données et le compilateur les placera automatiquement en mémoire Chip. Par exemple : UWORD chip graphics_data[] = { ... };
  2. Si vous utilisez le compilateur Lattice C V4.00, vous pouvez compiler le code source avec le commutateur "-acdb" qui chargera toutes les données initialisées (DATA) en mémoire Chip lors du chargement du programme. Par exemple : lc -acdb -Lm Example.c
  3. Allouer de la mémoire Chip en appelant la fonction AllocMem() et y copier toutes les données.
Il y a d'autres méthodes, comme lancer le programme Atom, etc., mais je ne les expliquerai pas ici. Voyez vos propres manuels pour plus d'informations.


[Retour en haut] / [Retour aux articles] [Chapitre suivant : les écrans]