J'espère que ce texte va vous aider.
Premièrement vous savez que le code original était écrit en C.
En C, l'exécution d'un programme c'est l'exécution de la routine principale "main": le programme commence avec cette routine, et il se termine lorsque cette routine retourne.
Les librairies ont parfois besoin d'initialisation, donc souvent le "main" est appelé par une autre routine, qu'on n'a pas besoin de traduire si on a des librairies équivalentes dans le langage qu'on utilise.
En C, les variables locales et les paramêtres sont soit dans des registres soit sur la pile, et les variables globales sont à des adresses fixes en mémoire. L'appel de sous-routine suit une convention d'appel qui est assez uniforme, mais certains compilateurs permettent de changer de convention d'un routine à l'autre.
Voici un code du même genre que ce qui est généré par mon compilateur C mais ce ne sont pas les mêmes registres qui sont utilisés.
addi r8,r8,FFFFFFD8h ; 00001000 st r4,00000010h(r8) ; 00001004 st r5,00000014h(r8) ; 00001008 st r6,00000018h(r8) ; 0000100C st r10,0000001Ch(r8) ; 00001010 addi r6,r1,00000000h ; 00001014 addi r4,r6,00000000h ; 00001018 addi r5,r4,00000000h ; 0000101C addi r1,r6,00000000h ; 00001020 addi r2,r5,00000000h ; 00001024 la r10,00001118h ; 00001028 brl r10,r10 ; 0000102C add r3,r9,r5 ; 00001030 shra r4,r3,01h ; 00001034 sub r9,r4,r5 ; 00001038 lar r7,0000101Ch ; 0000103C brmi r7,r9 ; 00001040 addi r9,r5,00000000h ; 00001044 ld r4,00000010h(r8) ; 00001048 ld r5,00000014h(r8) ; 0000104C ld r6,00000018h(r8) ; 00001050 ld r10,0000001Ch(r8) ; 00001054 addi r8,r8,00000028h ; 00001058 br r10 ; 0000105C addi r8,r8,FFFFFFE8h ; 00001060 st r10,00000010h(r8) ; 00001064 la r3,00000009h ; 00001068 st r3,00001154h ; 0000106C ld r1,00001154h ; 00001070 la r10,00001000h ; 00001074 brl r10,r10 ; 00001078 st r9,00001150h ; 0000107C ld r1,00001150h ; 00001080 la r10,000010A0h ; 00001084 brl r10,r10 ; 00001088 addi r9,r0,00000000h ; 0000108C ld r10,00000010h(r8) ; 00001090 addi r8,r8,00000018h ; 00001094 br r10 ; 00001098
Supposons que vous avez déjà regardé le programme pour déterminer les choses suivantes (en regardant le comportement des routines de manière générale):
- Le "main" serait fort probablement la routine qui commence à 00001060.
- À l'adresse 00001118 se trouve une routine qui fait la division entière
r1/r2 et met le résultat dans r9 (on n'a pas besoin de la traduire, la division
existe déjà dans le langage que vous utilisez).
- Àl'adresse 000010A0 se trouve une routine qui affiche le nombre dans
r1 en base 10 (existe aussi dans votre langage).
On va commencer par le "main". La routine commence à l'adresse 00001060 et se termine à l'adresse 0000109C.
Essayons de donner des noms aux variable, et faisons une traduction directe vers du C.
Les commentaires montrent comment on pourrait penser, mais ce n'est pas toujours
ce qu'il y a de mieux à trouver.
// voir plus bas pour savoir pourquoi les variables sont d'un certain type int d1150, d1154; // le nom est "d" suivi de l'adresse où se trouve la variable globale main() // 00001060 { int *a; int b, c, d, e; a = a - 6; // FFFFFFE8h c'est -24, qui est 6 entiers (de 32 bits). "a" se trouve dans "r8" // on utilise "a" comme pointeur, et on écrit un mot de 32 bits, donc ça doit être un pointeur sur des entiers a[4] = b; // on écrit à l'adresse 10h+a, les entiers sont de 32 bits donc c'est l'entier 4 (en C le premier élément d'un tableau c'est l'élément 0). "b" se trouve dans "r10" // comme "b" n'est pas initialisé depuis le début de la routine, ça doit être un paramêtre c = 9; // "c" est dans "r3" d1154 = c; // on écrit à une adresse absolue, ça doit être une variable globale d = d1154; // "d" est dans "r1" f1000(); // on fait un appel de sous-routine, r1 semble être le paramêtre, puisque pour les routines connues r1 était le premier paramêtre. L'adresse de retour est mise dans r10 d1150 = e; // "e" est dans "r9" printf("%d", d1150); // 000010A0 est appelé après avoir mis d1150 dans r1, on traduit par l'affichage correspondant dans notre langage. b = a[4]; a = a + 6; // branche à "b" }
Maintenant, à la fin on branche à "b" qui vient de a[4] qui est écrit au début et qui vient de possiblement un paramêtre, selon les commentaires. Donc, soit c'est un paramêtre qui contient un pointeur vers une fonction (peu probable), ou que c'est l'adresse de retour (três probable). Et "a" ressemble étrangement à une pile (donc r8 serait le pointeur de pile). On va se servir de la pile de C dans le code final, plutôt que d'un pointeur.
Maintenant la routine f1000:
int f1000(int b) { int a, c, d, e, f; // "r8" c'est notre pile, donc on ne traduit pas directement les accès utilisant "r8" // on sauve: 10h(r8) <- r4; 14h(r8) <- r5; 18h(r8) <- r6; 1Ch(r8) <- r10 (qui est notre adresse de retour); a = b; // "a" est dans "r6", "b" est dans "r1". "r1" a été modifié la dernière fois par la routine appelante, donc c'est bien un paramêtre. c = a; // "c" est dans "r4" do { d = c; // "d" est dans "r5" // on fait un appen à 1118h en mettant "a" dans "r1" et "d" dans "r2" e = a/d; // "e" est dans "r9" f = e + d; // "f" est dans "r3" c = f>>1; e = c - d; // ici on écrase "r9" donc on aurait pu écrire directement "f = a/d + d;" plus haut, sans passer par "e" } while(e < 0); // ici on écrit dans "r9" et il est utilisé par le "main" donc c'est la valeur de retour return d; // on restaure: r4, r5, r6, r10 }Avec tout ce qu'on sait maintenant, on peut réécrire en plus beau:
int d1150, d1154; int f1000(int a) { int c, d; c = a; do { d = c; c = (a/d + d)>>1; } while(c - d < 0); return d; } main() { d1154 = 9; d1150 = f1000(d1154); printf("%d", d1150); }On aurait aussi pu se rendre compte que f1000 est une racine carrée entière.