Obligement - L'Amiga au maximum

Mercredi 20 septembre 2017 - 07:40  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

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

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


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


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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - interfacer le C et l'assembleur (1ère partie)
(Article écrit par Cédric Beust et extrait d'A-News (Amiga News) - janvier 1990)


Loin de moi l'idée de raviver l'éternelle querelle qui oppose les inconditionnels de l'un et l'autre. Au contraire, j'espère montrer dans les lignes qui suivent qu'il est non seulement possible de les interfacer simplement sur l'Amiga, mais que cela peut apporter un gain en efficacité non négligeable.

Les protagonistes

Quelques précisions sur les produits dont je parle tout d'abord. J'utilise le Lattice 5.02 et Devpac 2. Ceci dit, n'importe quel compilateur C devrait faire l'affaire car l'appel des fonctions dans ce langage se fait de manière standard. Si votre compilateur ne les respecte pas, c'est que c'est un mauvais compilateur et il est urgent d'en changer ! En ce qui concerne l'assembleur, vous pouvez également utiliser le MetaComCo ou n'importe quel autre qui dispose des directives XREF et XDEF. Une option pour nommer le module courant ne serait pas non plus inutile, mais j'en reparlerai plus tard.

L'intérêt

Quel peut être l'intérêt d'utiliser simultanément les deux langages ? Il y pour cela plusieurs raisons. Chacun a des avantages et des inconvénients.

Pour le C
  • Avantages : rapidité de mise au point, entrées/sorties faciles avec printf, putc, etc.
  • Inconvénients : oblige parfois à des initialisations lourdes pour respecter le multitâche.
Pour l'assembleur
  • Avantages : rapidité d'exécution, accès facile aux registres système.
  • Inconvénients : relecture difficile, bogues nombreux à l'écriture, mise au point fastidieuse.
Ceci est très loin d'être une liste exhaustive et les différences ne sont en fait pas aussi marquées que je le laisse apparaître dans ce tableau, mais cela va me servir à illustrer mon propos. L'intérêt est donc de ne conserver de chaque langage le côté qui lui est agréable, en laissant de côté les inconvénients.

Fixons-nous un but à atteindre afin que tout cela ne soit pas trop théorique. Il vous est sûrement arrivé d'avoir à écrire une petite routine qui a besoin d'afficher du texte dans la fenêtre courante. L'écrire en C semble être une bonne idée mais la seule utilisation de "printf" ou "putc" ajoute facilement 2 ko au code produit. Il est assez irritant d'avoir un exécutable de 5 ko alors que les opérations que vous faites ne devraient en théorie pas dépasser 1 ko (exemple : la routine "avail.c" livrée avec le Lattice).

Il serait donc intéressant de disposer d'une routine d'impression qui serait très simple (impression de chaînes pour commencer) et qui prendrait très peu de place. Je vous propose donc de créer votre propre bibliothèque C (un .lib) qui contiendrait votre routine d'impression sur écran, la routine en question n'excéderait pas 150 octets... J'irai même plus loin en vous proposant une nouvelle routine "sprintf" qui vous permettra de faire des sorties formatées (c'est-à-dire avec %d, %s, etc.) pour une taille du même ordre. Par la même occasion, nous apprendrons à nous passer du startup c.o. Alléchant, n'est-ce pas ?

Assez bavardé, nous avons du pain sur la planche ! Quelques notions sont indispensables pour commencer.

Généralités

Comment le C passe-t-il ses paramètres ? Il utilise pour ce faire une méthode universelle à tous les compilateurs : le passage par la pile. Lorsque le compilateur rencontre une instruction :

fonction(a,b,c,d);

...il pousse sur la pile d, c, b et enfin a, après quoi, il fait un saut à la routine "_fonction" et dès son retour, il rétablit la pile en l'incrémentant de la taille des paramètres passés. Le compilateur pourrait donc produire un code comme celui-ci :

C et assembleur

Pourquoi 16 ? Parce que j'ai supposé que chaque paramètre était représenté sur quatre octets. Ce n'est nullement indispensable (il faut malgré tout que la taille soit un nombre pair d'octets) mais c'est beaucoup plus simple pour les calculs.

Plaçons-nous maintenant à la place de la fonction appelée. Comment peut-elle récupérer ses paramètres ? C'est très simple, il suffit d'aller les chercher au bon endroit sur la pile. Mais n'oubliez pas qu'une nouvelle adresse s'est empilée après l'appel : celle du retour. Le premier paramètre ne se trouve donc pas en 0(a7) mais en 4(a7). Reprenons l'exemple précédent : la routine "_fonction" agirait probablement de la façon suivante :

C et assembleur

En fait, ce modèle n'est pas tout à fait exact. Il faut absolument garder intacts les registres d2-d7 et a2-a6 (d0, d1, a0 et a1 peuvent être utilisés et détruits). En toute rigueur, il faudait sauver les registres que nous utilisons sur la pile, et décaler donc les accès d'autant. Dans cet exemple, nous aurions à sauvegarder d2 et d3, soit une taille de 8 octets :

C et assembleur
C et assembleur

Je laisse de côté l'instruction "link" qui est utilisée pour créer des variables locales, et ajoute un nouveau décalage dans l'adressage.

Comprenez-vous pourquoi les paramètres sont poussés dans l'ordre inverse d'apparition ? L'explication est simple : le C autorise à ses fonctions d'avoir un nombre variable de paramètres. Songez à "printf" : le nombre de paramètres est directement lié à la chaîne de formatage. Par exemple, une chaîne "Résultat: %d = %d" imposera clairement un "printf" à trois paramètres. Si la chaîne était poussée la première, la fonction appelée serait incapable de la retrouver dans la pile.

Exemple : soit l'instruction printf("%d %d",a,b). Voyons comment se présentent les piles en utilisant un mode de passage différent :

C et assembleur

Dans le premier exemple, comment la fonction saura-t-elle que la chaîne de formatage se trouve en 12(a7) ? En utilisant la deuxième méthode, elle sait avec certitude qu'elle doit regarder en premier lieu en 4(a7). Puis elle en déduira que deux paramètres suivent et ira donc les chercher en 8(a7) et 12(a7) (elle connaît naturellement leur taille).

Finissons cette introduction en regardant le problème inverse : comment appeler une fonction C à partir de l'assembleur ? Voici comment procéder :

1. Définir à l'aide de XREF les fonctions C à appeler, et que l'assembleur doit donc ignorer. C'est la tâche de l'éditeur de liens de la trouver.

2. Respecter les conventions d'appel décrites ci-dessus.

3. Ne pas oublier de lier avec la bibliothèque qui contient les fonctions utilisées.

Un petit exemple : utilisation de printf (encore lui !) :

C et assembleur
C et assembleur

Pour faire marcher cet exemple, utiliser "blink" de la façon suivante :

blink lib:c.o,a.o to a lib lib:lc.lib

Pas de problèmes, ça marche mais vous avez vu la taille ? Environ 6000 octets. Ça fait beaucoup pour une routine d'affichage, vous ne trouvez pas ? Ceci est dû au fait que "printf" est une fonction très complexe qui autorise des formatages très divers. De plus, elle fait appel à des symboles définis dans c.o (d'où l'obligation d'utiliser le nom "_main") et des routines autres que "printf" dans lc.lib.


[Retour en haut] / [Retour aux articles] [Article suivant]