Table des matières

Le Sommaire du Quasar Net

Le Langage C

Nous allons ici voir quelles sont les possibilités offertes pour programmer le CPC en langage C. Il y a deux approches très différentes concernant le Langage C. Soit on le voit comme un nouveau jeu pour s'amuser sur son CPC, il n'est alors clairement pas ce qu'il y a de plus performant pour coder ses softs mais c'est ludique d'utiliser le CPC pour ce faire. Soit on l'envisage comme un langage de cross-développement pour coder des softs pour le CPC, on peut alors atteindre un assez bon niveau de performance mais on ne fait plus vraiment du CPC.

Le Langage C en cross-developpement

Il serait intéressant d'ajouter une section parlant du C en cross-développement.

(Un volontaire est recherché pour rédiger cette section)

Le Langage C en natif

Basé sur l'article publié dans Quasar CPC numéro 18, Langage C, par OffseT.

Vous en rêviez depuis des lustres !1) Eh bien Quasar CPC l'a enfin fait ! Voici une rubrique consacrée au Langage C… mais pas n'importe lequel… le Langage C sur CPC et pour CPC ! 100% natif !

En fait, il existe plusieurs solutions pour programmer en C sur CPC, mais celle qui m'a paru être la plus viable consiste à utiliser le Small C. Ce kit de développement est normalement dévolu au CP/M, mais grâce aux bibliothèques spécifiques au firmware développées par Juergen Weber (que vous trouverez sur le site de Genesis8), celui-ci nous permet désormais de créer des exécutables AMSDOS. Attention toutefois, la compilation s'effectue bien sûr toujours sous CP/M.

Le principe

Tout d'abord, sachez que tout le nécessaire pour utiliser le Small C est disponible sur le Quasar Disc 2 (ainsi que tous les listings d'exemple repris dans cet article). Mais, comme je suis bon prince, je vous ai préparé avec amour un petit package prêt à l'usage :

Télécharger le Small C avec toutes les bibliothèques dédiées au CPC, les exemples et les documentations (extrait du Quasar Disc 2).

Et avant de parler de Langage C proprement dit, nous allons plutôt voir comment fonctionne ce kit.

La compilation s'effectue en quatre phases. Toute d'abord, grâce à CC.COM, le source C est transformé en source Macro-Assembleur (fichier .ASM) :

CC -M nomfich

Ensuite, on utilise ZMAC.COM pour assembler ce source assembleur. On génère alors un fichier objet (.OBJ) qui est en quelque sorte un exécutable relogeable.

ZMAC nomfich=nomfich

Chaque .C est à présent transformé en .OBJ et il s'agit maintenant de faire l'édition de lien. C'est-à-dire que l'on va transformer ces .OBJ en exécutables. Chaque programme utilisant des routines provenant de diverses bibliothèques, il va falloir inclure les .OBJ correspondants à celles-ci dans notre .COM (l'exécutable) :

ZLINK nomfich=cpciolib,nomfich,...

Ah ! Le petit blond à lunettes a réagit ! Et il a raison. Le fichier .OBJ principal n'est pas celui du fichier mais celui de la CPCIOLIB de Juergen Weber. En effet, notre programme n'est qu'un sous programme de cette bibliothèque qui nous permet de bidouiller une compilation pour AMSDOS. Le source C de cette bibliothèque est présent sur le Quasar Disc 22) et vous pourrez, si vous le désirez, regarder ça de plus près… il y a pas mal d'assembleur en fait.

Vient ensuite la dernière étape : transformer notre .COM (qui est en fait du code AMSDOS ne fonctionnant absolument pas sous CP/M) en .BIN tout ce qu'il y a de plus standard.

COMAM nomfich

Voilà, nous avons à présent un beau binaire qui se lance tout bêtement par un RUN”nomfich”. Le seul problème est que le binaire ainsi obtenu est obligatoirement logé en &0100 et qu'il ne permet aucun retour au système. Concernant l'allocation en &0100, il est en théorie possible de “linker” les .OBJ ailleurs mais en pratique, ZLINK étant buggé, ce n'est pas possible. Il est en revanche envisageable de réécrire un “linker” plus performant car le format des fichiers .OBJ est parfaitement renseigné dans les fichiers .DOC distribués avec le kit Small C.

En ce qui concerne le retour au système, je ne me suis pas penché sur la question pour le moment, mais je pense qu'en modifiant la CPCIOLIB on doit pouvoir corriger ça.

Le vif du sujet

Plutôt que d'enchainer sur un véritable cours sur le Langage C, alors qu'il en existe des tas déjà disponibles sur l'internet, je vais plutôt simplement vous présenter les bases de fonctionnement.

Vous devez savoir que nous avons essentiellement deux types de fichiers : les .C (comme C, étonnant non ?) et le .H (comme Header). Les premiers sont nos sources C à proprement parler et les seconds, qui sont la plupart du temps associés à des .C et/ou des .OBJ, sont juste là pour déclarer les variables, les routines, etc. de ces derniers. Ah ! Le petit blond à lunettes est perplexe. En fait, il est extrêmement rare qu'un fichier .C se suffise à lui-même ; la plupart du temps il utilise des routines définies dans des bibliothèques externes… et c'est là qu'interviennent les fameux “include”.

Ceux-ci sont là pour indiquer quelles routines externes au source en cours devront être prises en compte lors de la compilation. Un include peut bien évidemment être un fichier C lui-même, mais celà signifie qu'à chaque compilation il va être traité de même que notre fichier C principal. Voilà qui n'est pas très intéressant s'il s'agit d'une bibliothèque de 100ko qui, de toute façon, n'est jamais modifiée. Eh bien c'est là qu'intervient notre .H, celui-ci contient la déclaration de toutes les routines du .OBJ associé et permet donc leur incorporation dans notre fichier principal et ce, seulement à l'édition de lien (ZLINK).

Vous l'autre compris, le rôle d'un fichier .H est donc de permettre l'utilisation de fonctions externes dans notre programme principal et ce, en évitant une recompilation complète systématique. Certains kit de développement sont capables de générer automatiquement les .H à partir de .C ; mais ce n'est malheureusement pas le cas de celui-ci… on est donc contraint de les créer à la main !

Les bibliothèques du Small C

Outre la CPCIOLIB qui contient les principales références aux fonctions firmware, le Small C est accompagné de tout ce qu'il faut pour gérer l'affichage, le calcul, etc.. Contrairement à la CPCIOLIB, le source C de ces bibliothèques n'est pas disponible mais on dispose des .H et des .OBJ associés ce qui est amplement suffisant.

Je ne vais pas vous détailler ici le contenu de toutes ces bibliothèques car elles sont accompagnées d'un fichier .DOC très explicite. Pour ce premier contact avec le Langage C sur CPC, je vais à présent vous initier au Small C en précisant autant que possible les différences avec le C ANSI.

Structure des programmes

Un programme C est découpé en fonctions. Chacune de ces fonctions pouvant accepter un ou plusieurs paramètres mais pouvant également en renvoyer, directement ou via des pointeurs. Une de ces fonctions a un rôle un peu particulier puisqu'elle contient le programme principal : il s'agit de la fonction main(). Le protocole de déclaration des fontions diffère toujours un peu d'un compilateur à l'autre, mais celui du Small C est un peu particulier :

NomDeFonction(liste) déclaration; { corps }

“liste” contient la liste des noms de variables servant de paramètres, et déclaration contient la déclaration de ces variables. Il n'est en effet pas possible de faire le typage des variables dans la déclaration de la fonction elle-même ! Enfin, ce n'est pas très contraignant. En outre, lorsqu'une fonction ne nécessite aucun paramètre, les parenthèses restent obligatoires mais il est inutile d'y glisser un “void” comme cela est souvent préconisé.

Le corps de la fonction est ensuite à insérer entre accolades, comme d'habitude en C. Si vous avez un clavier français et que vous utilisez un éditeur de texte mal (ou pas) configuré, vous devrez utiliser “é” pour ”{” et “è” pour ”}”3).

Déclaration des variables

Qu'avez vous à déclarer ? La déclaration des variables est ici tout à fait standard avec respect des déclarations locales (dans les fonctions) et globales. En revanche, tous les types ne sont pas disponibles et les doubles pointeurs n'existent pas. Ah ! Le petit blond à lunettes ne sait pas ce que c'est qu'un pointeur. Je ne vais pas me lancer dans des explications détaillées dès cette mise en bouche, mais en résumé un pointeur se déclare en faisant précéder le nom de la variable par un “*” et désigne l'emplacement de la variable plutôt que la variable elle-même :

  int a,b; /* Déclaration de deux entier a et b */
  int *p;  /* Déclaration d'un pointeur sur un entier */

Pour conclure avec les variables, lors de leur manipulation, sachez que *pointer” désigne le contenu de la variable pointée par le pointeur pointer et que &variable désigne l'adresse où est stockée le contenu de la variable variable.

Les marqueurs

Comme ne cesse de le bêler le petit blond à lunettes, je ne vous ai pas encore expliqué ce que signifiaient les /* et */ - mais bon, je pense qu'à part lui tout le monde aura compris qu'il s'agit des marqueurs de début et de fin de commentaire. Ça y est, je crois que je l'ai vexé, tant mieux il nous fichera la paix ! Donc, ces commentaires peuvent être insérés n'importe où dans vos source sans aucune contrainte de taille.

Mais il y a bien plus intéressant que les commentaires… tout d'abord il y a le #include nomfich, qui permet d'intégrer un .C ou un .H. Et plus il y a le fameux #define nomconst valeur, qui permet d'attribuer une valeur constante ; pratiquement, lors de la compilations tous les nomconst seront remplacés par la valeur correspondant, ni plus ni moins. Et puis, je gardais le meilleur pour la fin, il y a les #ASM et #ENDASM qui permettent tout simplement d'insérer directement du code assembleur dans vos source C ; lors de la compilation tout ce qui se trouve entre ces deux marqueurs est ignoré et sera traité directement par le macro-assembleur ZMAC.

Les instructions

Le propos n'est pas ici de tout détailler, mais je pense que les listings qui suivent sont relativement explicites. Il y a tout de même quelques détails importants à noter. Concernant la manipulation des variables, les attributions du type “a+=b” ne sont pas reconnues, il faudra donc utiliser “a=a+b”. Mais tout ce qui concerne les ”++a” ou “a++” fonctionne, ouf !

Ensuite, au niveau des boucles, une seule structure est à notre disposition : le WHILE(condition) {…}. Mais en pratique ce n'est pas une très grosse contrainte. En effet, à l'aide des post/pre-incrémentations, il est aisé de remplacer un FOR() par un WHILE() (voir listing d'exemple). Voilà, ce sont les deux plus grosses limitations en dehors des types de variables restreints.

Les programmes d'exemple

Ci-après, vous trouverez trois programmes d'exemple : deux .C et un .H. Le CPCADDON.C est le source C de la bibliothèque perso que j'ai mise en place pour compléter la CPCIOLIB et le CPCADDON.H est le fichier header à inclure dans vos programmes utilisant les fonctions de celle-ci.

Et puis le fichier principal, EXEMPLE.C, vous montrera quelques exemples d'utilisation des boucles, des fonctions et des pointeurs. Celui-ci utilise la bibliothèque CPCADDON pour les fonctions LOCATE() et KEYPRESSED() ainsi que la bibliothèque PRINTF1 pour PRINTF() (voir dans les .DOC présents dans l'archive pour la différence entre le PRINTF() et le PRINTF2()). Mais il a aussi bien évidemment besoin de la CPCIOLIB pour pouvoir générer un exécutation AMSDOS et utiliser les fonctions MODE(), etc..

J'espère que vous parviendrez à vous débrouiller avec ces quelques listings qui, quoiqu'assez élémentaires, mettent en jeu pas mal d'éléments du Langage C en général et du Small C en particulier. Vous trouverez également à côté des listings un makefile "exemple.sub" (qui s'occupe de gérer toute la phase de compilation automatiquement).

Derniers conseils

Avant de vous laisser, il faut que je vous avertisse que si vous oubliez des accolades il se peut que le compilateur bloque (un CTRL+C quitte) sans vous renvoyer de message d'erreur. Ensuite, les noms des fonctions et des variables sont tronqués à 8 caractères ce qui n'empêche pas d'utiliser des noms plus longs pour plus de lisibilité. Enfin, l'utilisation de la bibliothèque CPCADDON induit parfois un “Warning Duplicate Declaration” mais c'est sans conséquence car le compilateur fait tout ce même ce qu'il faut.

Bonnes bidouilles en Small C !

Listings

CPCADDON.H

/*
 
   Fichier Header pour la CPCADDON Library
   pour Quasar CPC 18 - Avril 2000
 
*/
 
#define SET_CUR_TXT 47989
#define TST_KEY 47899
 
#asm
        GLOBAL QLOCATE
        GLOBAL QKEYPRESSED
#endasm

CPCADDON.C

Télécharger le source C (fichier ASCII CPC).

/*
 
   CPCADDON par OffseT 
   pour Quasar CPC 18 - Avril 2000
 
   Exemple de fonctions CPC en complément
   de la CPCIOLIB de Juergen Weber
 
   À utiliser avec le compilateur Small-C
 
compilation :
 
   cc -M cpcaddon
   zmac cpcaddon=cpcaddon
 
*/
 
#include cpciolib.h
#include cpcaddon.h
 
 
int regs[4];        /* AF HL DE BC */
 
 
locate(x,y) int x,y;    /* Fonction LOCATE */
{
 regs[1]=(x*256)+y;
 oscall(SET_CUR_TXT,regs);
}
 
keypressed(a) int *a;   /* Fonction KEYPRESSED */
{
 regs[0]=0;
 oscall(TST_KEY,regs);
 *a=regs[0] >> 8;
}

EXEMPLE.C

Télécharger le source C (fichier ASCII CPC).

/* 
 
(Tout) Petit programme d'exemple en C
par OffseT/Futurs pour Quasar CPC 18
 
Compilation (sous CP/M 3) :
 
   cc -M exemple
   zmac exemple=exemple
   zlink exemple=cpciolib,exemple,printf1,cpcaddon
   comam exemple
 
*/
 
#include cpciolib.h /* Biblioth}que C pour CPC */
#include printf1.h /* Biblioth}que Printf */
#include cpcaddon.h /* Biblioth}que Quasar CPC */
 
noballe(x,y) int x,y; /* Effa\age balle */
{
 locate(x,y);
 printf(" ");
}
 
balle(x,y) int x,y; /* Affichage balle */
{
 locate(x,y);
 printf("o");
}
 
initvdu()  /* Initialisation {cran */
{
 int i;   /* Variable locale */
 
 mode(1);
 i=2;
 locate(i,1);
 
 while(i++<40)
 {
  printf("*");
 }
 i=2;
 locate(i,25);
 
 while(i++<40)
 {
  printf("*");
 }
 i=1;
 locate(1,i);
 
 while(i++<24)
 {
  locate(1,i);
  printf("*");
  locate(40,i);
  printf("*");
 }
}
 
main()   /* Programme Principal */
{
 int x,y;  /* D{claration variables */
 int incx,incy;
 int a;
 
 a=0;   /* Initialisation variables */
 x=10;
 y=10;
 incx=1;
 incy=1;
 
 initvdu();
 
 while(a==0)   /* Boucle */
{
  keypressed(&a);
  noballe(x,y);
  x=x+incx;
  y=y+incy;
  balle(x,y);
  if (x==39) { incx=-1; }
  if (x==2)  { incx=1;  }
  if (y==24) { incy=-1; }
  if (y==2)  { incy=1;  }
}
 mode(2);  /* Fin */
}

Extras

Makefile : exemple.sub

Télécharger le script (format ASCII CPC).

cc -M exemple
zmac exemple=exemple
zlink exemple=cpciolib,exemple,printf1,cpcaddon
comam exemple
1) Quoi ? Qui a dit “non, pas du tout” ?
2) et donc dans l'archive proposée ci-dessus
3) Vive l'ASCII 7 bits !