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 - réduire la taille des programmes utilisant printf
(Article écrit par Squonk et extrait d'Amiga News - avril 1991)
|
|
print f + puts = 124 octets !
Quelle est la signification de cette équation bizarre ? Bon, je suppose que tous ceux d'entre vous qui ont tapé
un jour un programme en C ont dû au moins recopier le fameux "Hello, World!" du Kernighan & Ritchie.
Ce qui vous a certainement un peu inquiété, c'est la taille de l'exécutable obtenu par rapport à la taille du
source (eh bien, si le C est comme ça...) ! Ces rondeurs proviennent en fait de l'utilisation de "printf".
Voici donc un moyen de passer la taille du "Hello World!" de 5 ko à 2 ko...
Les vieux routards de la galaxie Amiga se rappellent probablement d'un article
de Cédric Beust qui détaillait un moyen de remplacer le "printf" de votre compilateur par un morceau de code
appelant une routine de la ROM (RawDoFmt). L'intérêt ? Puisque la routine de décodage des arguments est en ROM,
elle n'est pas dans votre programme ! Je pousse la solution un peu plus loin en proposant de remplacer purement
et simplement le "printf" original par un bout de code à moi, sans toucher à vos sources. Il suffit de relier
vos programmes en C avec ma routine et vous gagnez 3 ko de code dans l'exécutable !
RawDoFmt
Tout d'abord, on peut se demander d'où provient l'aspect un peu magique de la routine "RawDoFmt" ?
Eh bien , M. Commodore ne nous dit pas toujours tout, puisque cette routine qui est présente depuis le début
de l'Amiga dans les ROM n'est officielle que depuis le 2.0. Elle est utilisée notamment par le "ROM-Wack",
le débogueur en ROM et... par le gourou lui-même ! Devant ce mutisme, il n'y a pas de doute, une seule solution :
à l'assaut (c'est la dernière mode américaine) ! En disséquant la bête, j'ai pu éclaircir le fonctionnement
de cette routine. Voici ses paramètres d'appel :
RawDoFmt(Format, Args, putchar, Buffer)
A0 A1 A2 A3
|
Format : chaîne de formatage type "printf". Il y a quelques restrictions, détaillées dans le source que
je vous soumets :
- Pas de flottants.
- Pas de conversion octale.
- Entier 16 bits par défaut, quelles que soient les options de votre compilateur, 32 bits si "l" est précisé.
Args : pointeur sur un tampon mémoire contenant les arguments pour la conversion, suivant la chaîne de format.
putchar : pointeur sur la fonction utilisateur de sortie de caractères.
Buffer : tampon mémoire à l'usage exclusif de la routine putchar précédente.
Ceci répond donc aux interrogations de Cédric concernant les restrictions de cette routine. mais aussi
me donne quelques idées sur le sujet... Le gros problème d'une routine printf, c'est qu'elle ne connaît
pas la longueur finale de la chaîne à imprimer. Bien sûr. elle peut limiter arbitrairement la longueur
des chaînes, m'enfin... La solution m'est apparue à l'énoncé du dernier argument. En effet,
je croyais que le tampon mémoire passé à RawDoFmt() était utilisé en interne pour des conversions du
type int -> chaîne. Eh bien il s'avère que pour ce type de conversion, RawDoFmt() s'alloue un tampon
dynamique tout seul et ne touche absolument pas au paramètre qui lui est passé dans A3, qui n'est que
transmis à la routine putchar() de l'utilisateur pour chaque caractère à imprimer.
Tampon mémoire
Partant de là, on peut considérer que le format de ce tampon est libre ! L'astuce consiste donc à allouer
dynamiquement un tampon de longueur fixée dans notre "printf' (le code est ainsi réentrant, c'est-à-dire
utilisable par plusieurs utilisateurs en même temps) par un "link" et de réserver le premier mot de ce
tampon comme compteur de caractères. Puisque j'écris également la routine putchar() qui va recevoir ce
tampon, cela va me permettre de tester le dépassement de tampon mémoire et d'imprimer morceau par
morceau si la chaîne est trop longue. Je peux donc traiter virtuellement des chaînes de la longueur de la mémoire physique !
Le rôle de notre routine printf() est donc d'allouer un tampon mémoire dynamique, de préparer les
arguments pour RawDoFmt(), de désallouer le tampon mémoire et de retourner la bonne valeur à l'utilisateur.
La routine putchar() reçoit le fameux tampon mémoire, se contente d'y stocker le caractère qui, lui, est passé
par RawDoFmt() et d'appeler le cas échéant puts() si la chaîne est finie ou si le tampon est plein.
Tant que j'y étais, j'ai retouché la routine puts() de Cédric pour intégrer un strlen() rapide. Sa routine était
déjà très bien, puisque contrairement au printf de mon compilateur, elle peut facilement imprimer dans n'importe
quelle fenêtre et non pas seulement dans la fenêtre de lancement.
Bien que non testé avec les ROM 2.0, mon programme devrait marcher correctement, puisque M.
Commodore gère maintenant la routine RawDoFmt()...
Voici le résultat des courses avec Aztec 3.6 :
J'espère que ce bout de code vous servira autant qu'à moi. Non seulement il permet de gagner 3 ko
sur les programmes utilisant printf(), mais il montre le fonctionnement en assembleur de link/unlk
pour allouer des tampons mémoire dynamiques (je hais les DS.X de l'assembleur !).
|