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 - interfacer le C et l'assembleur (2e partie)
(Article écrit par Cédric Beust et extrait d'A-News (Amiga News) - février 1990)
|
|
Suite de notre excursion dans les arcanes du compilateur C (le premier article est ici).
Cette fois, on passe à des choses un peu plus complexes, accrochez vos ceintures !
Après vous avoir fait saliver avec le précédent article, il est temps de vous expliquer un peu comment nous allons
procéder pour obtenir des routines "puts" et "sprinf" plus courtes. Nous allons écrire leur code en
assembleur (rassurez-vous, c'est très court) et les assembler pour produire un code objet (puts.o et sprintf.o).
J'ai choisi de les appeler du même nom que celui de bibliothèque lc.lib car cela procure un avantage de taille. Lors de la
compilation et de l'édition de liens, l'éditeur de liens prend les fonctions dans l'ordre de leur apparition dans les
bibliothèques mentionnées. Si nous appelons notre bibliothèque mylib.lib, le simple fait de mettre ce nom avant lc.lib
dans la ligne de commande de blink obligera celui-ci à prendre nos fonctions et pas celles de lc.lib. Pour puis,
cela ne posera aucun problème, les paramètres étant identiques. Il faudra quelque peu modifier les appels de sprintf
pour les adapter à notre version, mais c'est trois fois rien.
Autrement dit, il suffira de remplacer :
blink lib:c.o,a.o to a lib lib:lc.lib lib:amiga.lib
|
...par :
blink lib:c.o,a.o to a lib lib:mylib.lib lib:lc.lib lib:amiga.lib
|
...pour voir baisser votre code de façon significative. En fait, vous pourrez même omettre c.o. Mais j'y reviendrai.
Description de puts.o
Il n'y a pas plus simple : il s'agit tout bêtement d'utiliser la fonction Write() de la dos.library. Celle-ci est supposée
ouverte et DosBase doit se trouver à un emplacement appelé _DOSBase. Cette fonction utilise _strlen de la bibliothèque
lc.lib (cela oblige donc à lier également avec lc.lib). Rassurez-vous, _strlen est très petit (on ne peut pas faire
plus petit) et si cela vous déplaît d'utiliser une autre bibliothèque, vous n'avez qu'à transférer son code dans
mulib.lib.
Avant d'aller plus loin, parlons un peu de la création de bibliothèques avec Lattice. Il existe une fonction qui
s'occupe de tout ça : oml (Object Module Librarian). Vous pouvez en avoir une description complète en faisant
"oml <nombidon". Par exemple, pour avoir la liste de toutes les fonctions dans lc.lib, tapez :
Vous pouvez en extraire le ".o" que vous voulez en remplaçant le "1" par un "x" et en spécifiant son nom :
Et pour désassembler le module ainsi extrait : "omd strlen".
Comme vous voyez, tout ceci est d'une extrême simplicité. La commande pour ajouter une fonction à une bibliothèque
(et éventuellement créer cette bibliothèque) est :
oml lib.mylib.lib a puts.o
|
Une contrainte à ce propos : il faut nommer le module que vous ajoutez, ce qui est fait sous Devpac
avec la directive IDNT (idem sous MetaComCo). Vous en savez maintenant suffisamment pour taper votre
première fonction de bibliothèque. Voici donc le listing de puts.s :
Une petite remarque avant d'aller plus loin : en toute rigueur, il faudrait adresser _DOSBase relativement
au pointeur de base (registre a4 sur Lattice). J'ai rencontré quelques problèmes quand j'ai voulu le
faire. Cela semble marcher comme ça mais méfiez-vous pour l'écriture d'autres fonctions de bibliothèque.
C'est bien beau tout ça, mais nous ne sommes pas affranchis pour autant de l'utilisation de printf pour faire
des sorties formatées... Que nenni ! Passons à la fonction suivante.
Description de sprintf.o
La force de notre nouvelle fonction sprintf repose sur le fait qu'elle utilise une fonction en ROM
qui fait le formatage : il s'agit de RawDoFmt. C'est une fonction très peu documentée et
l'apprivoiser m'a causé bien des maux de tête causés par la visite fréquente de notre pote le Guru.
Mais je l'ai dressée et elle m'obéit désormais au doigt et à l'oeil. Voyons sa syntaxe :
RawDoFmt(format, args, putchar, destination)
a0 a1 a2 a3
|
format : chaîne de formatage conforme au printf de C.
args : pointeur sur un tableau de longs représentant les arguments.
putchar : fonction de filtrage des caractères. En entrée, d0 contient le caractère à filtrer, et en
sortie le caractère est mis dans (a3)+. Vous en faites ce que vous voulez entre-temps, le plus simple étant
de le recopier tel quel.
destination : endroit où mettre la chaîne formatée.
L'analogie avec la syntaxe de sprintf est immédiate :
sprintf(destination,format,args)
|
Il suffit donc de réordonner les paramètres et d'ajouter la routine putchar(). Voici le listing de sprintf.s :
Pour ceux qui s'obstineraient à ne pas utiliser d'includes en assembleur (ils sont dans l'erreur la plus totale.
Ce n'est pas parce que Seka donne de mauvaises habitudes qu'il faut rester dans l'erreur. Utilisez les includes
et vos listings seront beaucoup plus lisibles !), le décalage de RawDoFmt dans Exec est de -522.
Comme vous l'aurez peut-être remarqué, la nouvelle syntaxe est légèrement différente. Par exemple...
...devra devenir :
long arg[2];
...
arg[0]=a;
arg[1]=b;
sprintf(var,"%d %d",arg)
|
Le nouveau sprintf est légèrement plus restreint que l'ancien mais je suis incapable de dire jusqu'à quel point.
Il faudrait pour le déterminer connaître exactement les limites de RawDoFmt. Jusqu'à maintenant, rien ne m'a
jamais fait défaut dans cette nouvelle version.
Comment supprimer le startup c.o ?
C'est a priori une chose difficile car c.o, lc.lib et amiga.lib sont très liés et le fait d'utiliser une
fonction de l'un d'eux oblige presque à coup sûr à recourir aux deux autres. Mais nous avons fait un grand pas
dans ce sens et je vais vous montrer que c'est maintenant devenu possible. Il n'y a plus grand-chose à faire
étant donné que nos puts et sprintf sont devenus très indépendants et à part strlen, ils ne font appel à aucune
routine extérieure.
Nous pouvons donc dès à présent nous passer de c.o à une condition : il s'agit du fameux registre de base a4.
Celui-ci n'est initialisé qu'au début de main. Or, nous n'avons plus de telle fonction (ou plus exactement,
nous ne sommes plus obligés d'en avoir une). Il faut donc obliger le compilateur à initialiser a4 dès l'entrée,
ce qui ce fait avec l'option "-y" sur lc. Ajoutons également l'option "-v" pour éviter de tester les
dépassements de pile à chaque appel de fonction et nous pouvons affirmer que le code que nous obtenons
commence à être compact...
Et pour finir...
Un exemple de programme C qui est tout tout tout petit...
Pour compiler ce programme, il suffit de le lancer par "execute a.c".
Remarquez l'utilisation de proto/exe,c.h afin de préciser comment trouver les registres utilisés lors de l'appel
de fonctions en ROM (nous n'avons plus d'amiga.lib, rappelez-vous !) et l'absence de fonction main().
Le code obtenu fait 432 octets. Pas mal, non ? Combien aurait fait l'équivalent avec printf, puts et sprintf ?
Et voilà pour mylib.lib. Elle ne contient pour le moment que deux fonctions mais je suis sûr que vous ne manquerez
pas d'idées pour l'étoffer.
|