Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : C - Transformer un programme C en assembleur
(Article écrit par Denis Jarril et extrait d'Amiga News Tech - juillet 1991)
|
|
De nombreux lecteurs nous posent la même question : ne disposant pas de compilateur C, comment profiter des
merveilleux programmes C de Frédéric Mazué, Stéphane Schreiber et autre Pascal Amiable ?
Quelqu'un d'un tant soit peu mercantile aurait certainement répondu : abonnez-vous à une formule avec disquette...
Mais l'ANT, comme à son habitude, va beaucoup plus loin que ça et vous propose carrément de vous prendre par la main,
en décortiquant pour vous une (la ?) manière de s'y prendre. Et comme un exemple concret est toujours plus parlant
que de longs discours, j'ai choisi d'illustrer mon propos avec un très vieux programme paru dans l'ANT numéro...
6 (Commodore Revue numéro 17) : le "sectorisateur"
de la société Loriciel.
Sectorisateur ?
Un rapide rappel sur le but et l'utilité de ce programme : présenté dans l'ancienne rubrique ViewPort, dans laquelle
des éditeurs livraient leurs astuces de programmation, et programmé par Jean-Pierre Vitulli, Sectorise permettait
d'écrire sur une suite de secteurs de la disquette placée en DF0:, par le biais du trackdisk.device, un fichier AmigaDOS
quelconque (image, musique, programme exécutable...).
Préparatifs
De quoi a-t-on besoin pour "transformer" un programme C en son équivalent assembleur ? Tout simplement d'un bon assembleur.
J'entends par là "macro-assembleur", c'est-à-dire capable de jongler avec des fichiers includes et, éventuellement, de
produire un code reliable plutôt qu'exécutable. Comme vous devriez maintenant en avoir l'habitude, c'est le Devpac II
qui a été choisi, mais l'assembleur du Lattice, le DP a68k ou même l'ancêtre MetaComCo feront l'affaire.
Un peu de documentation ne fera pas de mal non plus et pour une fois, la Bible De L'Amiga (deuxième édition,
celle à la couverture bleue) conviendra parfaitement. Mes précédents articles dans l'ANT
(utiliser amiga.lib,
mélange C/assembleur) ne seront non plus pas de trop. Quelques connaissances,
même rudimentaires, du langage C seront également les bienvenues.
Problèmes
A quel type de problème se heurte-t-on dans ce cas-là ? Le plus gros concerne les structures du C,
intensivements utilisées sur l'Amiga. En effet, alors que le compilateur C sait d'office la taille des champs
des structures, l'assembleur ne la connaît absolument pas, et qui plus est, ne se plaindra pas le moins du
monde si vous référencez un mot long alors que le champ est de la taille d'un octet. D'où l'utilité de la
documentation dont il est question dans le paragraphe précédent.
Un autre problème concerne les paramètres des programmes ; alors que la startup des programmes écrits en
C contient une routine qui décompose la ligne de commande du CLI pour remplir le tableau argv et donner
sa valeur correcte à argc, rien de tel n'existe en assembleur. De même, le C dispose d'une bibliothèque
de fonctions de base (printf, str..., fprintf, etc.) dont l'assembleur ne dispose pas. Une première solution
consiste donc à réécrire de telles routines en assembleur : on obtient ainsi un code plus compact, répondant
parfaitement à nos besoins, mais au prix d'un travail supplémentaire fastidieux. Une seconde solution, par
ailleurs celle que j'ai retenue ici, consiste à relier la startup et la bibliothèque C avec le programme
assembleur principal. Enfin, une troisième possibilité, celle que j'utilise le plus souvent par fainéantise
consiste à se débrouiller comme on peut pour les obtenir à un moment ou à un autre.
Enfin, dernière difficulté, le choix des variables locales et des variables globales. Là, tout dépend de
l'importance de ces variables. Par exemple, un pointeur sur une zone de mémoire allouée devra être conservé,
histoire de rendre cette mémoire au système en fin de programme. Par contre, si cette allocation a lieu dans
un sous-programme et que la libération correspondante survient à la fin de ce même sous-programme, on aura
tout avantage à conserver ce pointer dans un registre An, voire sur la pile dans le pire des cas. Dans
le même ordre d'idées, un compteur de boucle for() sera avantageusement placé dans un registre Dn, même
s'il a été déclaré global dans le programme C.
Pointeurs et tableaux
Le C est un langage orienté "pointeurs". Cela signifie qu'il offre tout un tas de facilités pour leur gestion
(déclaration, changement de type (casting), arithmétique, etc.). En assembleur, un pointeur est une adresse
32 bits, un point c'est tout. Considérez l'exemple suivant, par ailleurs totalement inutile :
Transcrire une telle fonction en assembleur ne posera pas de problème particulier, pour peu que l'on se
rappelle que toto pointe un tableau de mots de 16 bits, et qu'en tant que tel, le compilateur C l'incrémente
de deux octets lorsqu'il rencontre l'instruction toto++, et de six octets lorsqu'il rencontre toto + 3.
Le code assembleur de cette même fonction ressemblera donc à quelque chose du genre :
Si la comparaison post-incrémentée se charge pour nous de maintenir a1 aligné sur des mots, l'addition finale
devra prendre ce fait en compte.
Complexité
Comme tout langage évolué qui se respecte, le C permet d'écrire en une seule ligne de programme plusieurs
opérations, que le compilateur se chargera de décortiquer en une suite d'instructions assembleur cohérente,
suivant la priorité des différents opérateurs utilisés, l'utilisation ou non de parenthèses, etc. Par exemple,
la ligne suivante, que l'on rencontre très souvent dans des programmes en C sur l'Amiga :
L'ordre des opérations est le suivant :
- Initialiser a1 et d0 pour l'appel à OpenLibrary.
- Appeler OpenLibrary.
- Sauver le résultat dans la variable GfxBase.
- Si ce résultat est nul, aller à CleanExit.
- Sinon, continuer le programme.
Ça, c'était un cas facile. Considérez maintenant l'instruction :
Déjà moins évident, non ? La suite des opérations est la suivante :
- Prendre le pointeur fl_Volume dans la structure MyLock.
- Le convertir du BCPL au C.
- Y puiser le pointeur dl_Name.
- Le convertir du BCPL au C.
Et encore, ce ne sont pas là les cas les plus ardus. Il faudra apporter une attention toute particulière
aux boucles du genre for() et while( ), dont la condition d'arrêt peut souvent inclure un ou plusieurs appels
à un ou des sous-programmes.
Trêve de bavardages
Pour faciliter un peu les choses, j'ai inclus en commentaire du source assembleur, les lignes de source
C telles qu'elles apparaissaient dans le listing original. A vous d'en faire bon usage.
|