Menu Général
Le Langage C
avec le 68HC11 et Devmic11ACPS


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

Généralités Historique
 
 
Les qualités du langage Le langage C est Notion de filière de développement On désigne ainsi l’ensemble des outils qui interviennent entre le texte source C et le code objet téléchargé dans le microcontrôleur. Sont mis en oeuvre successivement :l’éditeur de texte, le préparateur (ou préprocesseur), le compilateur C, L’assembleur,

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


 
  Composition du programme C
 
 
Un programme en C utilise 2 zones mémoire principales :

- 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 :

è Attention au choix entre minuscules et majuscules : l'identificateur "MaFonction" n'est pas l'identificateur "mafonction".
 
  Variables Une variable est donc une portion réservée de la mémoire RAM à laquelle on a donné un nom. La réservation se fait lors de la déclaration. En C, toute variable doit être déclarée. Plusieurs types de variables existent en C .Voici les types utilisables dans la filière DevMic : Entiers Les 4 types " Entiers " de DevMic sont :
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 :

int x ; /* variable signée 16 bits [-32768 à +32767 ] */

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 (Borland), une option permet de rendre le type "char" non signé.Dans DevMic, il n'est jamais signé.

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    

  Vecteurs   Un vecteur est un tableau (matrice) de taille et de nombre de dimensions quelconque.

(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
 
 

Chaines de caractères Ce sont des vecteurs de dimension 1 dont les champs sont des caractères (type char). Le dernier élément est le caractère nul.
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 */
 
 

Evaluation booléenne d'une variable (8 bits au minimum)
 
 
Dans une expression booléenne, une variable est dite vraie (true) si elle est non nulle. Elle est dite fausse (false) si elle est nulle.

® 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 FAUX 0

#define ON 1

#define OFF 0

#define VRAI 1

Variables Permanentes (Globales) et Temporaires (Locales) Une variable globale ( ou permanente ) est déclarée en entête du programme. Elle est valide pendant toute la durée d'exécution du programme car elle fait l'objet d'une réservation mémoire permanente.

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).
 
 


 
 
è N’oublions pas qu’en C, tout le code exécutable est dans les fonctions
 
  Fonctions Une fonction est un sous-programme qui travaille sur des paramètres d'entrée et qui renvoie un paramètre de sortie.

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
 
 

Expressions du langage C Une expression est composée d’au moins une variable ( ou constante numérique ) et d'au moins un opérateur.

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 */
 
 

Opérateurs unaires. Ils 'admettent qu'un seul opérande.
 
  Négation : " - ". Change le signe de l'opérande.

Exemple : a = 10 ;

b = - a ; /* b = - 10 */

è Il est évident que b doit être un entier signé.
 
 

Complémentation logique : " ! " . Complémente une variable booléenne.

Exemple : a = 5 ;

b = !a /* b = 0 */

a = 0 ;

b = !a /* b = 1 */
 
 

Complémentation à un : " ~ " .

Change tous les bits de l'opérande.

Exemple : char a, b ;

a = 5 /* a = 0000 0101 soit 5 */

b = ~a /* b = 1111 1010 soit 255 - 5 */

Incrémentation : " ++ " . Ajoute 1 à l'opérande.

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

Décrémentation : " -- " . Retranche 1 à l'opérande. Indirection : " * " . Accède au contenu d'une adresse ( pointeur ).

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
 
 

Adresse : " & " . Renvoie l'adresse mémoire d'une variable.

Exemple :

a = 3 ; // positionne a à 3

ptr = &a ; // pointeur ptr contient l'adresse de a

*ptr = 10 ; // positionne a à 10
 
 

Opérateurs binaires.

Ils ont deux opérandes.

Addition : " + " .

Effectue la somme algébrique des opérandes.

Exemple : a = b + 3; /* a contient la somme de b et de 3 */

a = 5 + 3 ; /* le compilateur remplace (5+3) par 8 */

Soustraction : " - " . Effectue la différence des opérandes.

Exemple a = b - 3 ; /* a = différence de b et de 3 */

Multiplication : " * " . Effectue le produit des opérandes.

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)

Division : " / " . Effectue le quotient des opérandes.

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.

Modulo " % " Renvoie le reste de la division entière des opérandes.

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".
 
 

ET binaire : "&" . Effectue un ET logique bit à bit entre les opérandes.

Exemple a = 5 ; /* a = 0000 0101 */

b = 6 ; /* b = 0000 0110 */

c = a & b ; /* c = 0000 0100 soit c = 4 */

ET logique : " && " . Renvoie "VRAI" si les 2 opérandes sont vrais et "FAUX" dans les 3 autres cas.

è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

OU binaire : " | " . Effectue un OU logique bit à bit entre les opérandes.

Exemple

a = 5 ; /* a = 0000 0101 */

b = 6 ; /* b = 0000 0110 */

c = a | b ; /* c = 0000 0111 soit c = 7 */

OU logique : " || " . Renvoie "VRAI" si 1 ou 2 opérandes sont vrais et "FAUX" dans l'autre cas.

è 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 */

OU exclusif binaire : " ^ " . Effectue un OU exclusif bit à bit entre les opérandes.

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 */

Décalage de bits à gauche : " << " . Décale vers la gauche tous les bits du 1er opérande.Le nombre de décalages est donné par le 2ème opérande.

Exemple :

a = 11 ; /* a = 0000 1011 soit a = $b */

b = a << 3 ; /* b = 0101 1000 soit b = $58 */
 
 

Décalage de bits à droite : " >> " . Décale vers la droite tous les bits du 1er opérande. Le nombre de décalages est donné par le 2ème opérande.

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++ );

}

Egalité : " == " . Renvoie VRAI si les opérandes sont égaux et FAUX s'ils sont différents.

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.

Non égalité : " != " . Renvoie VRAI si les opérandes sont différents et FAUX s'ils sont égaux.

Exemple

b = ( a != 2 ) /* b vaut 0 si a = 2 , sinon b vaut 0 */

Plus grand que : " > " . Renvoie VRAI si le 1er opérande est supérieur au 2ème et FAUX dans le cas contraire.

Exemple

a = ( 10 > 20 ) /* a = 0 */

Plus petit que : " < " . Renvoie VRAI si le 1er opérande est inférieur au 2ème et FAUX dans le cas contraire.

Exemple

a = ( 10 < 20 ) /* a = 1 */

Plus grand que ou égal à : " >= " . Renvoie VRAI si le 1er opérande est supérieur ou égal au 2ème et FAUX dans le cas contraire.

Exemple

if ( a >= 2 )

a = 2 ; /* limite a à 2 */
 
 

Plus petit que ou égal à : " <= " . Renvoie VRAI si le 1er opérande est inférieur ou égal au 2ème et FAUX dans le cas contraire.

Exemple

if ( a <= 2 )

a = 2 ; /* minimise a à 2 */

Affectation : " = " . Place la valeur du 2ème opérande dans le 1er.

è 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 */

Auto affectation . C'est une écriture condensée liée à une opération et une affectation.

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

  Délimiteurs et séparateurs Délimiteur d’expression : ";" . Ce n'est pas le passage à la ligne suivante qui marque la fin d'une instruction, mais le point virgule.

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 .
 
 

Séparateur de variables : " , " . La virgule est utilisable dans une déclaration multiple pour séparer les symboles .

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 */

Les parenthèses " ( " et " ) " . Elles ont 3 rôles : Les crochets " [ " et " ] " . Ils permettent d'indexer une variable de type vecteur (tableau).

Exemple :

vecteur[2][1] = 57 ; /* l'élément (2,1) = 57 */

a = pointeur[3][3] ; /* élément (3,3) dans a */

Délimitation d'une bloc Une séquence comprenant plusieurs instructions doit être délimitée par les accolades " { " et " } " .

è 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).
 
 

Opérateur conditionnel : " ? " et " : " . Il permet de faire un choix entre 2 expressions en fonction d'une condition

<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 ;

}

Les itérations en C
 
 
La boucle while.

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*/

}

La boucle do ... while.

do

<Séquence>

while (<condition> );

Ainsi, La condition est évaluée aprés l'éxécution

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.
 
 

La boucle for . for (<initialisation> ; <condition> ; <opération> )

<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 */

}

Sauts dans une boucle

Instruction "break" .

Elle permet de sortir immédiatement d'une boucle au moyen d’un saut inconditionnel à l’instruction qui suit la boucle.

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.

Instruction "continue" . Elle permet de ne pas exécuter la séquence comprise entre "continue" et la fin de la boucle tout en poursuivant le bouclage.

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
 
 
 
 
 
 
 
 
 
 

Les Alternatives en C Alternative simple : " if " ... " else " .

if (<condition>)

<Séquence1>

else

<Séquence2> ;

Exemple

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 :

On soignera l'indentation pour préserver la lisibilité du programme.

Exemple

if (x > 80 )

if (y > 25)

break;

else

y++;

else

x++;
 
 

Alternative multiple : "switch" et "case" Une seule variable entraîne des choix multiples :

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; /**/ }

}

}
 
 

Exemples de programmes : exemples d'alternatives /* if <condition> <instruction 1> else <instruction> */

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 */

}

}

On peut aussi utiliser l'expression conditionnelle : /* <condition> ? <instruction1> : <instruction2 > */

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);

}
 
 

Inclusion d'une séquence en assembleur dans un texte en C unsigned int sauve;

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 */

}
 
 

3 manières de faire clignoter une LED placée entre 2 sorties du port A */
 
 

Utilisation de la fonction poke

#include std11.h

#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 */

}

Utiliser un pointeur sur le port A. #include std11.h

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.

èPendant que le programme principal fait clignoter une LED placée entre 2 sorties du port A , l'interruption RTI envoie des caractères vers le PC.

#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 */

}

Utilisation du convertisseur analogique numérique /* Simple voltmètre sur AN0 */

#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)

printf("Tension lue sur AN0 : %d mV",Ux); /* afficher sur écran */ *adctl = 0 ; // démarrer nouvelle conversion delay(500); // attendre pour stabiliser l'image }

/* 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

Aspect fonctionnel Conçue avec des objectifs

pédagogiques, la filière C

intégrée au logiciel DevMic

peut se décomposer en 3

fonctions :

La production du code (F1) :
 
  La mise au point des programmes Le débogueur Source C de

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 :
 
 
 
 
 
 
 
 

Le préprocesseur Aspect fonctionnel Le préprocesseur est un programme qui travaille sur des textes et fournit un texte.
 
 


 
 

Macrocommandes : Si une ligne :

#define chaine1 chaine2

est rencontrée, le préprocesseur remplace chaine1 par chaine2

Le texte de la macro ne peut dépasser une ligne.

Les macros ne sont pas paramètrées. Ainsi, si l’on écrit :

#define Somme(a,b) (a+b)

----------------------

-----------------------

s = Somme(5,2) ; on ne retrouvera pas le nombre 7 dans s car a et b ne seront pas remplacés par 5 et 2.

Il aurait fallu écrire :

#define Somme(a,b) (a+b)

-----------------------

-----------------------

a = 5 ;

b = 2 ;

s = Somme(a,b) ;

Dans les fichiers d’en-tête (.h) fournis, les variables formelles (celles des macros)

s’appellent X,Y et Z.

® Se souvenir qu’en C, il faut distinguer minuscules et majuscules.
 
 

Compilation conditionnelle On dispose de 3 directives de compilation " #ifdef ", " #else " et " #endif ".

#define chaine1 /* si cette ligne est présente, */

#ifdef chaine1 texte1 /* texte1 est compilé */

#else

texte2 /* sinon, texte2 est compilé */

#endif

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

Décomposition de printf : La fonction printf (placer à l’écran une chaîne formattée ) est, de par ses possibilités, complexe, volumineuse et gourmande en mémoire (RAM). Pour des raisons pédagogiques, le traitement des chaînes formatées se fait avec la fonction putstr (plus simple) tandis que celui des expressions associées se fait avec les fonctions putnum et putdec. Le préprocesseur effectue la décomposition.
 
 
® Formatée: qui comprend des caractères de contrôle (tabulation, passage à la ligne etc...)
 
  Le compilateur C Aspect fonctionnel Il produit, à partir d’un texte C préparé par le préprocesseur, un texte assemblable qui peut être consulté à tout moment.

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.

Variables Le type char est un entier 8 bits non signé. (option de TC)

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.
 
 

® Le modificateur " unsigned " est toujours utilisable :

é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 modificateur " signed " n’est pas reconnu.

Le transtypage n’est pas accepté par le compilateur.

Types de variables non implémentés : Ne sont pas reconnus par le compilateur : Modificateurs de variable non implémentés :

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é.
 
 

Localisation en RAM : Les variables globales sont placées dans un segment positionné par l’utilisateur ( configuration).

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).
 
 

Constantes

Nombres :

1234 représente un nombre décimal 0..65535

0b1010 représente un nombre binaire (non portable)

0x12ab représente un nombre hexadécimal 0..$ffff

‘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.
 
 
 
 

Caractères spéciaux : \a représente le caractère BEL ($7) : signal sonore

\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

Fonctions Les fonctions se déclarent en style moderne uniquement. Le style classique n’est pas autorisé.

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.

Ces 2 fonctions sont donc incorrectes :

truc(char x) void truc(char x)

{ {

x *= 2 ; x *= 2 ;

return x ;

} }
 
 

Mots-clé non reconnus L’instruction "  goto "  n’a pas été implémentée car jugée déstructurante. Pour la même raison, il n’est pas possible de déclarer un label ou de numéroter les lignes du programme.

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)++ ;

--------------

Support des interruptions Le modificateur " interrupt " est utilisable devant une déclaration de fonction pour indiquer au compilateur qu’il s’agit d’un sous-programme d’interruption. Ce mot-clé n’est pas portable (ANSI) mais il est utilisé sur Turbo-C (Borland).

Il faut également indiquer au compilateur quel est le vecteur d’inter utilisé.

La syntaxe est : interrupt Vecteur : NomFonction

"Vecteur" représente l’adresse du vecteur d’inter.

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éé.

® Le vecteur RESET est systématiquement positionné sur l’adresse d’exécution du programme. Ceci permet de rendre le système autonome :

®programme et vecteurs en ROM, pile et variables en RAM interne.

Opérateurs 10 Opérateurs ( par ordre de priorité décroissante)
 
Identificateur
( )

[ ]

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
=

*=

/=

%=

+=

-=

&=

^=

|=

<<=

>>=

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


  Assembleur en ligne : L’utilisateur peut à tout moment écrire une séquence en langage assembleur, sous réserve que le compilateur et l’éditeur de textes soient avertis du changement de langage..

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)

unsigned x ;

void main()

{ x = 10 ;

asm

lda x ;10 minutes

tempo jsr UneMinute ;appel fonction déclarée plus bas

deca

bne tempo

endasm
} void UneMinute() { delay(60000) ; /* 1 mn = 60 000 ms */ }
 
 

®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 ‘.

Gestion des erreurs de compilation En langage C, la gestion des erreurs est une tâche délicate. L’oubli d’un " } ", par exemple, peut entraîner des dizaines d’erreurs . Le programmeur non expérimenté risque d’être désorienté et perdu dans le dédale des causes d’erreur possibles.

Pour éviter ceci, le compilateur

® La référence à une fonction qui n’existe pas en bibliothèque ne peut être détectée comme erreur de compilation. Elle ne sera détectée qu’au moment de l’édition de liens. Fonctions Externes

Définition

Une telle fonction est un sous-programme écrit de préférence en assembleur et déjà assemblé.

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
et d’autres non :
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 :

Ex : delay Ex : enable, poke Ex : tolower ® La fonction printf est une exception car elle est décomposée par le préprocesseur.

Chaque fonction est décrite en détail dans la partie ‘librairie de fonctions’.

Passage des paramètres aux fonctions précompilées Les paramètres d’entrée sont passés par la pile :

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 à [SP)

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

" return truc ; " engendrera l’instruction "  ldd truc "
 
  Action de l’éditeur de liens
L’éditeur de liens travaille sur les trames S1S9 du programme principal et des fonctions externes de la bibliothèque.

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.
 
 

Ecrire une fonction de la bibliothèque : Ecrire une fonction externe à partir d’un sous-programme existant est une tâche aisée. Il faut cependant respecter quelques règles : Choix du langage Ecrire la fonction en assembleur engendrera un code plus compact que si elle est écrite en C. On réservera le langage C au fonctions complexes Fonction translatable La fonction écrite doit être translatable (relogeable) : pas de sauts absolus, pas " d’indirection absolue ".

L’adressage immédiat est accepté :

ldx ?#MonMessage

jsr ?putstr+3

consulter le source de la fonction abort.

Appel d’autres fonction externes

Si une fonction externe utilise d’autres fonctions de la bibliothèque, le nom de ces fonctions doit être précédé d’un " ? " pour indiquer à l’assembleur que ce symbole n’est pas référencé dans le texte source.
 
 

Exemple :

ldx #2500 ;1ms avec quartz 8 MHz

jsr ?delay+3 ;branchement au 4ème octet de

;la fonction externe " delay "
 
 

Acquisition des paramètres d’entrée Si la fonction utilise des paramètres d’entrée, se souvenir que ceux ci ont été empilé dans l’ordre donné par le programme appelant et que le pointeur de pile a été décrémenté de la somme des tailles de ces paramètres. Chaque paramètre est transmis sur 16 bits (2octets). Ainsi, après une instruction " tsx " ,
 
 
    1. par exemple, pour 1 paramètre transmis :
tsx tsx

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

®Consulter les exemples fournis dans ..\librairie\fonctions C.
 
  Le nombre de paramètres d’entrée doit être fixe.
Cela vient du fait que le compilateur n’accepte pas le modificateur " register ". Le nombre de paramètres ne peut être passé à la fonction dans un registre.

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.

Préserver le registre Y Le registre Y indexe les variables locales. Si la fonction écrite utilise ce registre, elle doit le sauvegarder dans la pile : debut tsx

pshy

------------ ; le corps de la fonction utilise Y

------------

puly

rts