Sommaire
Historique ...........................................................2
Qualités du langage .......................................... 2
Filière de développement ................................ 2
Composition du programme .............................. 3
Variables ........................................................... 3..5
Fonctions ........................................................... 6
Expressions ....................................................... 7..12
Délimiteurs .........................................................12..13
Itérations ........................................................... 14..17
Alternatives ....................................................... 18..19
Exemples de programmes C ............................. 20
Filière intégrée .................................................. 23
Préprocesseur ................................................... 24
Compilateur ....................................................... 25
Opérateurs ........................................................ 28
Assembleur en ligne .......................................... 29
Fonctions externes ............................................30
Editeur de liens .................................................
31
1. Initiation au langage
le lieur (ou éditeur de liens), le chargeur, le lanceur et éventuellement le débogueur
Exemple de filière C : Filière C du logiciel intégré DevMic
- la zone des VARIABLES est un bloc RAM où sont stockées des données manipulées par le programme.
- la zone des FONCTIONS est un bloc ROM qui recevra le code exécutable du programme. Déclarer une variable ou une fonction, c’est informer le compilateur de son existence.
è Les noms des objets que l'on utilise ( variables, fonctions ) sont des identificateurs. Leur écriture doit :
8 bits | 16 bits | |
signé | short | int |
non signé | char | unsigned int |
On voit que, par défaut, un entier ( int ) est signé et sur 16 bits.
Exemple de déclarations :
short y; /* variable signée 8 bits [-128 à +127 ] */
char z ; /* variable non signée 8 bits [0 à 255] */
unsigned int a ; /* variable non signée 16 bits [0 à 65535] */
unsigned b ; /* aussi 16 bits et non
signée [0 à 65535] */
Dans "Turbo-C", il faut, à partir du menu principal , lancer la commande:
OPTION/ COMPILATEUR/ GENERATION DE CODE/ CARACTERES UNSIGNED
è Les identificateurs ne doivent pas être des mots réservés. Dans DevMic, les 22 mots réservés sont:
asm | case | continue | char | default | break |
do | enable | for | if | interrupt | disable |
int | else | poke | pokew | return | short |
unsigned | switch | void | while |
(La limite correspond à la place qu’il occupe en mémoire).
Les éléments (champs) du vecteur sont tous de même type.
Exemple : int machin[3] ;
Un vecteur nommé 'machin' contenant 4 entiers ( int ) est déclaré. Le premier élément porte toujours l'indice 0
(Ce qui n’est pas le cas en Pascal).
Les 4 éléments du vecteur machin sont :
machin[0], machin[1], machin[2] et machin[3].
autres exemples
int truc [2][3] ;
char cste [5] = { 1, 2, 3, 4, 5 } ;
int array[] = { 10, 11, 12 } ;
Une chaîne de caractères se déclare comme un tableau de caractères de dimension 1 :
char chaîne[12]
èAttention au débordement hors des limites du tableau.
Aucun contrôle n'est effectué et si une donnée est écrite hors des limites du vecteur, elle est perdue (pour le tableau) et , en général, elle écrase d'autres variables.
èPosition en mémoire des éléments d'un tableau multidimensionnel :
Soit le vecteur : int tab[2][3][4] ;
Son adresse est tab = tab[0][0][0] . C'est l'adresse de son 1er élément.
tab[0][0][1] est à l'adresse tab+ 2
tab[0][0][2] tab+ 4
I | l | f | a | i | t | b | e | a | u | \0 |
è ne pas confondre le caractère ASCII nul \0 avec le zéro décimal '0' dont le code ASCII est $30.
Pointeurs
Un pointeur est une adresse mémoire qui peut être utilisée pour accéder à une donnée. Sur les petits systèmes un pointeur est un nombre non signé de 16 bits. Manipuler un pointeur, c'est manipuler l'adresse d'une donnée ( adressage indirect ). L'opérateur d’indirection est : " * ".
Exemple : char *porta ; /* pointeur 16 bits sur un octet */
porta = 0x1000 ; /* initialisation du pointeur */
porta = 0x80 ; /* placer $80 … l'adresse $1000 */
® Le complément booléen de 1 est 0.Le complément de 0 est 1 ou tout autre entier non nul
èExtrait de du fichier d'en-tête ..\Librairie\Fichiers d’en tête C\std11.h :
#define ON 1
#define OFF 0
#define VRAI 1
Si elle est initialisée lors de sa déclaration, elle devient une constante et sera placée en ROM. On ne pourra donc plus changer sa valeur. Exemples d'initialisation :
char a = 57 ;
int porta = 0x1000 ;
char chaîne[] = { 'T' , 'R' , 'U' , 'C' } ;
char *chaineptr = "TRUC" ;
Une variable locale ( ou temporaire ) est déclarée au début d'une fonction. Son existence est limitée à la durée d’exécution de cette fonction. Elle est donc ignorée par les autres fonctions. Elle peut ( bien que cette façon de procéder soit déconseillée ), porter le même nom qu'une variable globale ou qu'une autre variable locale se trouvant dans une autre fonction. En C, une variable locale est logée au dessus de la pile.
è Dans DevMic, elle est indexée sur le registre Y.
Un paramètre d'entrée est une variable locale ( donc visible seulement à l'intérieur de la fonction ).
Un paramètre d'entrée
est initialisé par la fonction appelante ( la fonction "main" ,
par exemple).
Dans un programme C , il n'y a QUE des fonctions ( pas de programme principal ! ).
L'une des fonctions doit s'appeler "main". Elle est appelée lors du lancement du programme et c'est elle qui appellera les autres fonctions. En C, on ne peut pas inclure une fonction dans une autre. (contrairement à Pascal).
Une fonction est déclarée comme le paramètre de sortie qu'elle retourne :
truc(int p1, char p2..)
{
--------------
--------------
return ( ..... ) ;
}
Exemple d'une fonction faisant la somme de 2 entiers :
int somme( int n1, n2 ) /* déclaration fonction et paramètres */
{
return n1 + n2 ; /* corps de la fonction */
}
Cette fonction pourra être appelée dans l'expression :
résultat = somme ( 1, 2 );
qui passera les paramètres "1" et "2" à la fonction "somme"; laquelle renverra ( 1 + 2 ) . La variable "resultat" contiendra finalement le nombre 3.
è Dans DevMic, le paramètre de sortie est passé par l'accumulateur D
® Une fonction peut ne renvoyer aucun paramètre
Voici pour terminer un petit programme complet : ( addition.c )
/* fonction principale */
main()
{
int a, b, c ;
a = 1 ;
b = 2 ;
c = somme( a, b );
printf("Le résultat est %d\n",c); /* "%d" : afficher c en décimal et "\n" : aller à la ligne suivante. */
}
/* et notre fameuse fonction "somme" */
int somme ( char n1, n2 )
{
return n1 + n2 ;
}
Voici l'extrait du fichier assembleur généré par le compilateur pour la fonction "somme" :
*--- 017: Somme(char x,y)
Somme
tsy indexer les paramètres d'entrée sur Y
*--- 019: return ( x + y );
ldb 5,y
addb 3,y
rts
è Remarquer que les ligne C sont numérotées et apparaissent en commentaire
è Aucun contrôle de débordement (pas de retenue) n'est effectué.
è
Rappel : Le paramètre
de sortie est contenu dans l'accumulateur D
Exemple d'expressions :
x = - y ; /* opérateur "=" et "-" */
c = a + b ; /* opérateurs "+" et "=" */
c += a ; /* range (c+a) dans c */
c = a + c ; /* idem */
Exemple : a = 10 ;
b = - a ; /* b = - 10 */
è Il est évident que
b doit être un entier signé.
Exemple : a = 5 ;
b = !a /* b = 0 */
a = 0 ;
b = !a /* b = 1 */
Change tous les bits de l'opérande.
a = 5 /* a = 0000 0101 soit 5 */
b = ~a /* b = 1111 1010 soit 255 - 5 */
Exemples :
a = 15 ;
b = 15 ;
c = ++a ; /* pré -incrémentation : a = (a+1) puis c = a */
d = b++ ; /* post-incrémentation : d = b puis b = (b+1)*/
è Aprés exécution, c = 16 alors que d = 15
Exemple :
char *porta ; /* crée un pointeur sur un port */
pia = 0x1000 ; /* positionne le pointeur à $1000 */
*porta = 255 /* toutes les sorties à 1 */
x = *porta ; /* placer les entrées dans x */
Autre exemple :
char *MemoireCourante ;
MemoireCourante = 0x100 ; /* Définition de l'adresse pointée */
*MemoireCourante+=10 ; /* ajouter 10 à son contenu*/
MemoireCourante+=10 ; /* ajouter 10 à l'adresse */
Extrait du texte assembleur correspondant :
*--- 005: MemoireCourante=0x100;
ldd #256 [$100]
std MemoireCourante
*--- 006: *MemoireCourante+=10 ;
ldx MemoireCourante ;Pointeur dans registre d'index
ldb 0,x
addb #10 [$a,%1010]
stb 0,x
*--- 007: MemoireCourante+=10 ;
ldd MemoireCourante
addd #10 [$a,%1010]
std MemoireCourante
rts
Exemple :
a = 3 ; // positionne a à 3
ptr = &a ; // pointeur ptr contient l'adresse de a
*ptr = 10 ; // positionne a à
10
Ils ont deux opérandes.
Addition : " + " .
Exemple : a = b + 3; /* a contient la somme de b et de 3 */
a = 5 + 3 ; /* le compilateur remplace (5+3) par 8 */
Exemple a = b - 3 ; /* a = différence de b et de 3 */
Exemple a = b * 3 ; /* a contient 3 fois la valeur de b */
è Dans DevMic, la multiplication est optimisée car pour le microcontrôleur, il est plus facile de traiter (2*x) que de traiter (3*x)
Exemple a = b / 3 ; /* a contient le tiers de b */
èEn général, si a est un entier, ( 3 * a ) ne redonnera pas b.
Exemple a = 7 % 3 ; /* a = 7 - ( 3 * 2 ) soit 1 */
è Le µC 68HC11 est pourvu de l'instruction IDIV qui place le reste de la division entière de D par X dans D.
Celle ci est évidemment utilisée
lorsque l'on a recours à l'instruction C "Modulo".
Exemple a = 5 ; /* a = 0000 0101 */
b = 6 ; /* b = 0000 0110 */
c = a & b ; /* c = 0000 0100 soit c = 4 */
èVRAI correspond à une valeur non nulle et FAUX correspond à 0.
è Si le premier opérande contient "FAUX", le 2ème n'est pas évalué.
Exemple c = a && b ;
/* c est vrai si a et b sont vrais , sinon c est faux */
/* c = 1 si (a <> 0) et (b <> 0) , sinon c = 0 */
è Par défaut , VRAI correspond à 1
Exemple
a = 5 ; /* a = 0000 0101 */
b = 6 ; /* b = 0000 0110 */
c = a | b ; /* c = 0000 0111 soit c = 7 */
è Si le premier opérande contient "VRAI", le 2ème n'est pas évalué
Exemple :
c = a || b ; /* c = 1 si a ou b sont non nuls , sinon c = 0 */
Exemple :
a = 5 ; /* a = 0000 0101 */
b = 6 ; /* b = 0000 0110 */
c = a ^ b ; /* c = 0000 0011 soit c = 3 */
d = ! (a^b) ; /* fonction coïncidence */
Exemple :
a = 11 ; /* a = 0000 1011 soit a = $b */
b = a << 3 ; /* b = 0101 1000
soit b = $58 */
Exemple
a = 11 ; /* a = 0000 1011 */
b = a >> 3 ; /* b = 0000 0001 soit b = 1 */
Autre exemple : compteur Johnson (chenillard) sur port b :
main()
{
char n ;
while(1)
poke( PORTB , 1 >> n++ );
}
Exemple
b = ( a == 2 ) ; /* b vaut 1 si a = 2 , sinon b vaut 0 */
if ( a = 2 ) /* Piège : a est d'abord testé, puis est affecté par la valeur 2. Il aurait fallu */
/* écrire : if ( a == 2 ) */
b = 0 ;
èBien que les parenthèses ne soient pas indispensables, elles améliorent la lisibilité du programme.
Exemple
b = ( a != 2 ) /* b vaut 0 si a = 2 , sinon b vaut 0 */
Exemple
a = ( 10 > 20 ) /* a = 0 */
Exemple
a = ( 10 < 20 ) /* a = 1 */
Exemple
if ( a >= 2 )
a = 2 ; /* limite a à 2 */
Exemple
if ( a <= 2 )
a = 2 ; /* minimise a à 2 */
è Le premier opérande ne peut pas être une constante.
Exemple
a = 5 ; /* place la valeur 5 dans a */
b = ( 2 == 3 ); /* b = 0 car ( 2==3 ) est faux */
Si l'on écrit a = a + b ; l'écriture de "a" intervient 2 fois, ce qui est inutile.
On pourra écrire :
a += b ; /* d'abord a+b , ensuite
a = (a+b) */
Auto affectation | Affectation normale | |
a += 1 | a = a + 1 | Affectation-somme |
b -= 2 | b = b - 2 | Affectation-différence |
c *= 3 | c = c * 2 | Affectation-produit |
d /= 4 | d = d / 4 | Affectation-quotient |
e %= 5 | e = e % 5 | Affectation-modulo |
f &= 6 | f = f & 6 | Affectation-et |
g |= 7 | g = g | 7 | Affectation-ou |
h ^= 8 | h = h ^ 8 | Affectation-ou exclusif |
i <<= 9 | i = i << 9 | Affectation-décalage à gauche |
j >>= 10 | j = j >> 10 | Affectation-décalage à droite |
Bien qu'il soit possible de placer plusieurs instructions sur une même ligne, il faut se l'interdire pour préserver la lisibilité du programme.
è
Remarquer les " ; " dans les exemples précédents .
Exemples :
MaFonction( short a , b ) ; /* 2 paramètres d'entrée */
int a , b , c ; /* 3 entiers signés déclarés */
int tableau[2] = { 5 , 8 }; /* 2 éléments initialisés */
Exemple :
vecteur[2][1] = 57 ; /* l'élément (2,1) = 57 */
a = pointeur[3][3] ; /* élément (3,3) dans a */
è En Pascal , les accolades délimitent les commentaires !
Le Bloc délimité devient ainsi indivisible.
Exemple :
if ( x == 5 )
{
b += a ; /* tout le bloc est conditionné */
c -= a ;
}
d = b + c ; /* est toujours exécutée */
Ne pas confondre Délimitation d'un bloc et Indentation :
Dans l'exemple ci-dessus, les lignes conditionnées sont décalées vers la droite pour indiquer au lecteur (et non au compilateur) qu'elles sont dépendantes de la ligne "if...". Cet exemple pourrait aussi être présenté comme suit :
if ( x == 5 ) {
b += a ; /* tout le bloc est conditionné */
c -= a ; }
d = b + c ; /* est toujours exécutée */
La position des accolades est la même pour le compilateur. Seule l'indentation renseigne le lecteur du programme sur la dépendance des lignes conditionnées.
è
Dans DevMic, l'indentation est obtenue au moyen de la
touche de tabulation (TAB).
<condition> ? <expression1> : <expression2>
Si condition est vraie, <expression1> est évaluée, sinon c’est <expression2> qui est évaluée.
Exemple :
/* cette fonction convertit une minuscule en majuscule */
short maj(char chr)
{
if ( chr >= 'a' ) && ( chr <= 'z' )
return chr - 64 ;
else
return chr ;
}
Explications : Si le code ASCII du caractère est compris entre 97 et 122, (‘a’..’’z’), on lui retranche 64 pour obtenir le code de la majuscule correspondante (‘A’..’Z’) et on sort de la fonction. Si le code est déjà celui d’une majuscule, on se contente de sortir de la fonction.
On aurait pu écrire :
/* cette fonction convertit une minuscule en majuscule */
short maj(char chr)
{
return ( chr >= 'a' ) && ( chr <= 'z' ) ? chr - 64 : chr ;
}
while (<condition>)
<Sequence> ;
Tant que la condition est vraie, la séquence est exécutée.
La condition est évaluée
avant l’exécution
Exemple
while ( x < x )
y = 0 ; /* ne sera jamais exécutée */
while ( 3 ) ; /* provoque un bouclage infini */
/* Remarquer que la séquence est vide */
while( x ) /* calcule y = y * 10x */
{
x-- ;
y *= 10 ;
}
Autre exemple d'utilisation de while : affiche les entiers de 0 à 10
main()
{
char x ;
x = 0 ;
while ( x <= 10 )
printf ("x = %d\n",x++) ; /* affiche x en décimal, l'incrémente et passe à la ligne suivante*/
}
do
<Séquence>
while (<condition> );
de la séquence.
Celle-ci est donc éxécutée au moins une fois.
do
y = 0 ; /* sera exécutée une fois */
while ( 0 ) ;
®Les mots clé "do" et "while" sont les délimiteurs de la séquence. On peut donc se passer des accolades.
® L'instruction
"do..while" du langage C ressemble à l'instruction "Repeat..Until"
du langage Pascal mais, attention, l'évaluation de la condition
est coplémentaire d'un langage à l'autre.
<Séquence>
La séquence est éxécutée tant que la condition est vraie.
En général, l’opération agit directement ou indirectement
sur la condition.
Exemples :
for( a = 0 ; a <= 10 ; a++ ) /* pour 0 <= a <= 10 ) */
for( ; a <= 10 ; a++ ) /* pas d'initialisation */
for( a = 0 ;; a++ ) /* pas de condition: boucle infinie*/
for( a = 0 ; ++a <= 10 ;) ; /* pas d'opération finale */
for( ;;) ; /* écriture simple
pour boucle infinie */
Voici un petit programme complet :
/********** affiche les nombres entiers de 0 … 10 ************/
main()
{
char x ;
for( x = 0 ; x <= 10 ; x++ )
printf ("x = %d\n",x) ; /* x en décimal et passe à la ligne *
}
On aurait pu écrire aussi :
/************ affiche les nombres entiers de 0 à 10 **********/
main()
{
char x ;
for( x = 0 ; x <= 10 ;)
printf ("x = %d\n",x++) ; /* incrémenter x */
}
Ou encore :
main()
{
char x ;
x = 0 ; /* initialiser x */
for( ; x <= 10 ;)
printf ("x = %d\n",x++) ; /* incrémenter x */
}
Pour n'afficher que les valeurs paires :
main()
{
char x ;
for( x = 0 ; x <= 10 ; x++) /* incrémenter x */
printf ("x = %d\n",x++) ; /* incrémenter x */
}
Instruction "break" .
Exemple :
for ( a = 0 ; a <= 10 ; a++ ) {
if ( a == 5 )
break ;
printf (" a = %d\n", a ) ; }
è Dans ce programme, le bouclage est interrompu quand a == 5. .Seuls les nombre de 0 à 4 sont affichés.
Il s’agit là aussi d’un saut inconditionnel .
Exemple :
for ( a = 0 ; a <= 10 ; a++ ) {
if ( a == 5 )
continue ;
printf (" a = %d\n", a ) ;
}
è
Dans ce programme, les nombres de 0 à 4 et de 6 à 10 sont
affichés. La boucle est cependant effectuée 11 fois
if (<condition>)
<Séquence1>
else
<Séquence2> ;
if ( a < 12 )
a += 2 ;
else
b = 0 ;
Autre exemple :
main()
{
int x ;
for ( x = 1 ; x < 49 ; x++ ) /* pour x allant de 1 à 48 */
{
if ( ! ( x % 2 )) /* si x est pair */ printf("\t"); /* 1 tabulation */
else
printf("\n"); /* sinon à la ligne */
printf("%d=$%x",x,x) ; /* x en décimal et en hexa */
}
}
è Emboîtement de if : On peut placer des instructions if en cascade :.
if...if...else if...else...else if
Règle :toujours associer le "else" au "if " sans "else" le plus proche.
On peut établir un petit diagramme :
Exemple
if (x > 80 )
if (y > 25)
break;
else
y++;
else
x++;
switch(<Variable>)
case c1 : <Sequence1> /*exécutée si Variable == c1 */
case c2 : <Sequence2> /*exécutée si Variable == c2 */
case c3 : <Sequence3> /*exécutée si Variable == c3 */
----------------------------------------------------------------------
case cn : <Sequencen> /exécutée si Variable == cn */
default : <SequenceParDefaut> /*exécutée si aucune des conditions ci-dessus n’est vraie.*/
On voit que si une séquence est exécutée, toutes les suivantes le sont également sauf si elle se termine par l’instruction break.
La séquence par défaut n’a de sens que si la séquence n (au moins) se termine par break.
La variable de contrôle est du type entier, les ci
sont des constantes numériques.
Exemple : programme \hc11\c\src\switch.c
#define VALEUR 1 /* placer ici les valeurs donnée à x */
char x;
void main()
{
putchr('>');
x=VALEUR;
switch(x) {
case 1 : { putchr('1');
/* break; /**/ }
case 2 : { putchr('2');
/* break; /**/ }
case 3 :
case 4 : { putstr("34");
/* break; /**/ }
default : { putstr("def");
/* break; /**/ }
}
}
main()
{
short ch ;
int x ;
for ( x = 1 ; x < 49 ; x++ ) /* pour x allant de 1 à 48 */
{
if (x == 2 * (x/2)) /* si x est pair */
ch = 10 ; /* ligne suivante */
else
ch = 9 ; /* sinon tabulation */
printf("%d=$%x%c",x,x,ch) ; /*afficher x en décimal puis hexa */
}
}
main()
{
short ch ;
int x ;
for ( x = 1 ; x < 49 ; x++ ) /* pour x allant de 1 à 48 */
{
(x == 2 *(x/2)) ? ch = 10 : ch = 9; /* expression conditionnelle */
printf("%d=$%x%c",x,x,ch) ; /* x en décimal puis en hexa */
}
}
è
Que donne l'expression
conditionnelle en assembleur ?
èLe préprocesseur de DevMic décomposera la fonction " printf " comme suit :
/* printf("%d=$%x%c",x,x,ch) ;*/
{
putdec(x); /* affichage de x en décimal */
putstr(‘’$’’);
putnum(x,16); /* affichage de x en hexadécimal */
putchr(ch);
}
void main()
{putstr("Contenu du port a : ");
asm /**** début de la partie "ASSEMBLEUR" ****/
registres equ $1000
porta equ 0
portb equ 4
masque equ 1
pshx
ldx #registres
lda porta,x ;lire le port A
sta portb,x ;le recopier sur port b
sta sauve ;le sauver
bclr portb,x masque ;faire passer PB0 à 0
pulx
endasm /****** On repasse en langage C *******/
putnum(sauve,2); /* l'afficher en binaire */
}
Utilisation de la fonction poke
#include regis.h
char porta ;
void main()
{
porta = 0xAA ;
while(VRAI) { /* répéter indéfiniment. VRAI est défini dans std11.h */
poke(PORTA,porta); /* PORTA est défini dans regis.h */
porta = ~porta; /* complément binaire */
delay(50); } /* tempo 200 ms */
}
unsigned char *porta ; /* pointeur 16 bits sur port A */
void main()
{
porta = 0x1000; /* adresse du port A */
*porta = 0xAA;
for(;;) { /* répéter indéfiniment */
*porta = ~*porta ;
delay(100); }
Utiliser l’interruption temps réel RTI.
#include std11.h
#include inter.h
#include regis.h
#define PERIODE 3 /* 0, 1 , 2 ou 3 */
unsigned short *porta ; /* pointeur 16 bits sur port A */
void main()
{
porta = PORTA; /* adresse du port A */
*porta = 0x55; /* une sortie sur 2 à 1 */
poke(PACTL,PERIODE); /* période d'appel de l'inter RTI */
poke(TMSK2,0x40); /* démasquage local inter RTI */
enable(); /* démasquage global des inter */
for(;;) { /* répéter indéfiniment */
*porta = ~*porta;
delay(200); }
}
void interrupt VRTI:rtii() /* VRTI est le vecteur de RTI déclaré dans inter.h */
{
putchr('*'); /* envoyer caractère vers le PC */
poke(TFLG2,0x40); /* faire retomber le drapeau d'inter */
}
#define ADCTL 0x1030
#define ADR1 0x1031
#define OPTION 0x1039
char *adctl,*adr1,*option;
unsigned int Ux ;
void main()
{
/* positionnement des pointeurs */
adctl = ADCTL ;
adr1 = ADR1 ;
option = OPTION ;
*option = 0b10010011; /* activation pompe de charge */
for(;;)
{
putchr(12) ; // effacer l'écran
// acquérir la conversion précédente et la convertir en mV
// Ux = ( *adr1 * 196 ) / 100 ; /* (5000 / 255) = (196 / 100)
/* Exercice : modifier et compléter ce programme pour :
1. Afficher la tension en Volt
2. sortir sur un afficheur LCD */
Filière C intégrée
pédagogiques, la filière C
intégrée au logiciel DevMic
peut se décomposer en 3
fonctions :
DevMic permet de suivre
l’exécution du programme en
en surveillant les variables.
Les principales fonctions
permettant le suivi
d’exécution dans le texte
source sont :
#define chaine1 chaine2
est rencontrée, le préprocesseur remplace chaine1 par chaine2
Les macros ne sont pas paramètrées. Ainsi, si l’on écrit :
#define Somme(a,b) (a+b)
-----------------------
Il aurait fallu écrire :
#define Somme(a,b) (a+b)
-----------------------
-----------------------
b = 2 ;
s = Somme(a,b) ;
s’appellent X,Y et Z.
® Se
souvenir qu’en C, il faut distinguer minuscules et majuscules.
#define chaine1 /* si cette ligne est présente, */
#else
texte2 /* sinon, texte2 est compilé */
Cette structure est utile pendant la phase de mise au point d’un programme.
® Si texte2 n’existe pas, on peut se passer de #else
Tous les symboles ne sont pas encore précisés. En particulier, les adresses des fonctions prises en bibliothèque sont encore inconnues.
Complexe et volumineux, le processus de compilation n’est pas décrit ici.
Certaines options (adresses du programme et des variables, position de la pile, production de commentaires ...) influencent son comportement.
® La définition de ces adresses incombe au lieur.
Le type short est un entier 8 bits signé .
Le type int est un entier 16 bits signé .
Le modificateur unsigned , utilisé seul , défini un entier 16 bits non signé .
Les pointeurs sont des mots de 16 bits non signés.
Les tableaux sont constitués des types ci-dessus.
char *chaine déclare un pointeur (adresse 16 bits) sur chaîne.
char t[2][3] déclare un vecteur
(tableau) de 6 octets.
écrire " unsigned short machin ; " revient à écrire " char machin ; "
écrire " unsigned char machin ; " revient aussi à écrire " char machin ; "
écrire " unsigned int machin ; " déclare un double octet non signé.
Le transtypage n’est pas accepté par le compilateur.
Le compilateur ne travaillant que sur un seul module-source (hormis les fichiers inclus), les modificateurs " extern " et " static " sont inutiles.
La vitesse d’exécution des codes-objet
n’étant pas le critère prioritaire, le modificateur " register
" n’est pas implémenté.
Les variables locales sont placées au dessus de la pile et indexées sur le registre Y.
La position de la pile au lancement
du programme doit être choisie par l’utilisateur (initialisation
du pointeur de pile dans le menu Options/programmes).
Nombres :
0b1010 représente un nombre binaire (non portable)
‘x’ représente le code ascii du caractère x
‘’abc’’ représente l’adresse d’une chaîne de caractères.
La représentation des nombres
en Octal n’est pas implémentée car trop peu utilisée.
\b représente le caractère BS ($8) : retour arrière
\f représente le caractère FF ($c) : saut de page
\n représente le caractère LF ($a) : aller à la ligne
\r représente le caractère CR ($d) : retour chariot
\t représente le caractère HT ($9) : tabulation horizontale
\’ représente le caractère ‘ ($2c) : apostrophe
\ ‘’ représente le caractère ‘’ ($22) : guillemet
On écrira : machin(short x ; int y)
{ ---
et non pas : machin( x , y )
short x ;
int y ;
{ ---
Si aucun paramètre n’est retourné par la fonction, le modificateur " void " est obligatoire. Son omission déclenchera un message d’erreur.
Inversement, une fonction déclarée avec " void " ne peut pas avoir de paramètre de sortie.
truc(char x) void truc(char x)
x *= 2 ; x *= 2 ;
return x ;
} }
Le mot clé " sizeof " n’est pas reconnu car son utilité sur un petit système est discutable ; Le transtypage étant interdit ,
on ne pourra pas écrire, par ex. :
void machin( char truc )
{
(short truc)++ ;
--------------
Il faut également indiquer au compilateur quel est le vecteur d’inter utilisé.
La syntaxe est : interrupt Vecteur : NomFonction
exemple :
void interrupt XIRQ : machin()
déclare la fonction machin() appelée depuis le vecteur XIRQ.
®Etudier l’exemple " Demo Inter IRQ.c " et analyser le texte assembleur créé.
®programme et vecteurs en ROM, pile et variables en RAM interne.
|
[ ] |
Appel de fonction
Indice de tableau |
Opérateur unaire |
~ + - ++ -- & * |
Négation
logique
Complément binaire Plus unaire Moins unaire Préincrément ou postincrément Prédécrément ou postdécrément Adresse de Indirection |
Opérateur binaire |
/ + - << >> < <= > >= == != & ^ | && || ? : |
Multiplication
Division Plus binaire Moins binaire Décalage à gauche Décalage à droite Strictement inférieur Inférieur ou égal Strictement supérieur Supérieur ou égal Egal Différent ET entre bits OU exclusif entre bits OU entre bits ET logique OU logique condition |
|
*= /= %= += -= &= ^= |= <<= >>= |
Affectation
simple
Affectation produit Affectation quotient Affectation reste Affectation somme Affectation différence Affectation ET entre bits Affectation OU EXCLUSIF entre bits Affectation OU entre bits Affectation décalage à gauche Affectation décalage à droite |
Le passage en assembleur se fait par le mot réservé " asm ". A partir de cet instant, la syntaxe est exactement celle de l’assembleur intégré. Le retour au langage C est provoqué par la rencontre du mot réservé " endasm " ou du mot " c " .
On peut, à l’intérieur de la séquence en assembleur, faire référence à tout symbole (constante, variable, fonction ) déclarée dans la zone ‘langage C’.
D’une manière générale, on ne peut pas faire référence à une " variable " qui n’a pas d’adresse en mémoire ( Ex. variables locales ).
Les appels de fonctions externes se font en adressage étendu.
® Se souvenir qu’en assembleur, le passage de paramètres à un sous-programme n’est pas automatisé !
® Se souvenir qu’en C , le paramètre de sortie est toujours du type int et se trouve dans l’accumulateur D.
Dans la séquence assembleur , aucune erreur n’est détectée pendant la compilation. Une erreur éventuelle sera détectée et renvoyée par l’assembleur.
Exemple : tempo longue (10 minutes)
void main()
asm
tempo jsr UneMinute ;appel fonction déclarée plus bas
bne tempo
®Dans cet exemple, la fonction UneMinute sert uniquement à demander au compilateur d’effectuer le passage de paramètre à la fonction delay , pour ne pas avoir à le faire en assembleur.
® Voir la partie ‘passage de paramètres aux fonctions ‘.
Pour éviter ceci, le compilateur
Définition
Il figure en général dans le répertoire ..\librairie\Fonctions C sous forme d’une trame S1S9. Il doit être translatable (relogeable) car l’éditeur de lien le placera à une adresse adjacente au reste du programme.
Nous fournissons une bibliothèque de fonctions externes dont certaines sont standard :
abort | abs | atoi | disable | enable | exit | getchr |
getstr | isalnum | isalpha | iscntrl | isdigit | isgraph | islower |
isprint | ispunct | isspace | isupper | max | memcpy | memset |
min | peek | poke | peekw | pokew | printf | putchr |
putstr | sqrt | strcat | strcpy | strlen | tolower | toupper |
delay | geti2c | initlcd | itoa | putclcd | puti2c | putlcd | putnum | putdec |
® Consulter l’aide pour avoir le détail de chaque fonction
Ou point de vue de leur localisation, on peut les classer en 3 groupes :
Chaque fonction est décrite en détail dans la partie ‘librairie de fonctions’.
Si une fonction est appelée par : " machin(p1,p2,....,pn) " ,le compilateur, avant d’écrire " jsr machin ", provoquera l’empilement de p1, p2,....pn dans cet ordre et en donnant à tous les paramètres une taille de 16 bits.
Ainsi, après empilement, on retrouve :
pn-1 à [SP + 2]
pn-2 à [SP + 4]
p1 à [SP + 2(n-1)]
A l’appel de la fonction, SP est décrémenté
de 2 unités (sauvegarde de PC ). Pn pourra donc être
récupéré à (SP+2) , Pn-1 à
(SP+4) etc...
paramètre de sortie
En langage C, une fonction peut retourner 0 (void) ou 1 paramètre.
S’il existe, celui-ci sera toujours retourné dans l’accumulateur D. Ainsi, la ligne
L’assembleur intégré place le nom de la fonction externe dans la trame S1S9
Si une fonction externe en appelle une autre, le nom de cette dernière doit être précédé d’un " ? " ( Voir chapitre 4 ).
Les tâches de l’éditeur de liens sont donc de :
détecter dans la trame du programme principal les appel s aux fonctions externes de la bibliothèque.
retrouver dans le texte source assembleur le nom de chaque fonction appelée.
placer la trame de chaque fonction à la suite de celle du programme.
calculer l’adresse de chaque fonction et la placer dans la trame du programme principal.
Un enchaînement récursif de ces tâches autorise l’appel d’une fonction par une autre fonction externe.
Ce traitement complexe aboutit à une trame S1S9 standard utilisables par d’autres outils que ceux de la filière.
la trame de chaque fonction étant relogée à la suite du bloc déjà traité doit être translatable.
L’éditeur de liens renseigne
l’utilisateur sur les actions effectuées et montre, en fin de travail,
la trame obtenue, l’adresse de chaque bloc et l’arbre hiérarchique
des liaisons.
L’adressage immédiat est accepté :
ldx ?#MonMessage
jsr ?putstr+3
Appel d’autres fonction externes
Exemple :
ldx #2500 ;1ms avec quartz 8 MHz
jsr ?delay+3 ;branchement au 4ème octet de
;la fonction externe " delay "
lda 3,x ;octet dans A ldd 2,x ;mot dans D
<corps de la fonction> <corps de la fonction>
ldd... ;param de sortie ldd ... ;param de sortie
rts rts
Une exception cependant : la fonction printf accepte un nombre de paramètres d’entée variable car elle est décomposée par le préprocesseur.
pshy
------------
rts