Traduction de routine ASM en C
Traduction de routine ASM en C
Bonjour à tous!
J'écris ce post à la suite de la lecture d'un tutorial sur l'ASM PSX écrit par Skeud.
Il y est écrit (en gros) qu'une des meilleures façons d'écrire une routine de décompression similaire à celle d'un jeu X est de la traduire directement de l'ASM vers un autre langage de plus haut niveau (eg : le C, mais n'entrons pas dans le sujet du choix de tel ou tel langage
).
Sur le principe, je suis d'accord avec cette affirmation, et en serait un grand partisan, le seul problème que je rencontre, c'est sa mise en pratique. Je m'explique (enfin, je vais essayer):
Dans une routine ASM (je bosse sur une routine NES, bref) il y a souvent des accès à la RAM, des manipulations de registres, etc... Ces actions se font selon moi 'in vivo', c'est à dire lorsque le code s'exécute depuis un émulateur, ou une machine. Bref, il y a des actions qui ne peuvent se calculer et se gérer uniquement que lorsque les jeux est en marche.
De là vient mon problème : comme l'on va retranscrire du code ASM en un autre langage, comment peut-on arriver à gérer tout ces accès de manière à ce que l'ensemble fonctionne correctement et que l'on obtienne le résultat désiré?
La seule réponse que j'ai trouvé pour l'instant est de créer une sorte d'interpréteur de code, mais là on rejoint le côté "je monte mon propre émulateur", côté qui ne me convient pas vraiment
Donc si quelqu'un pouvait m'éclaircir sur ce point, je lui en serait infiniment reconnaissant, et aura une descendance féconde jusqu'à la IXème génération (non, ce n'est pas un message subliminal :d)
J'écris ce post à la suite de la lecture d'un tutorial sur l'ASM PSX écrit par Skeud.
Il y est écrit (en gros) qu'une des meilleures façons d'écrire une routine de décompression similaire à celle d'un jeu X est de la traduire directement de l'ASM vers un autre langage de plus haut niveau (eg : le C, mais n'entrons pas dans le sujet du choix de tel ou tel langage

Sur le principe, je suis d'accord avec cette affirmation, et en serait un grand partisan, le seul problème que je rencontre, c'est sa mise en pratique. Je m'explique (enfin, je vais essayer):
Dans une routine ASM (je bosse sur une routine NES, bref) il y a souvent des accès à la RAM, des manipulations de registres, etc... Ces actions se font selon moi 'in vivo', c'est à dire lorsque le code s'exécute depuis un émulateur, ou une machine. Bref, il y a des actions qui ne peuvent se calculer et se gérer uniquement que lorsque les jeux est en marche.
De là vient mon problème : comme l'on va retranscrire du code ASM en un autre langage, comment peut-on arriver à gérer tout ces accès de manière à ce que l'ensemble fonctionne correctement et que l'on obtienne le résultat désiré?
La seule réponse que j'ai trouvé pour l'instant est de créer une sorte d'interpréteur de code, mais là on rejoint le côté "je monte mon propre émulateur", côté qui ne me convient pas vraiment

Donc si quelqu'un pouvait m'éclaircir sur ce point, je lui en serait infiniment reconnaissant, et aura une descendance féconde jusqu'à la IXème génération (non, ce n'est pas un message subliminal :d)
Désolé, je ne posterai pas d'extrait de code car :
- ça prendrai de la place inutilement;
- ça n'est pas nécessaire selon moi;
(- je n'en ai pas sous la main actuellement
)
Par contre je peux rapidement t'expliquer le truc.
La routine du jeu "Destiny Of An Emperor" démarre à l'adresse $D173 (adresse NES) (à force du lire du code, on s'en rappelle).
Donc à partir de là, plein d'instructions sont exécutées, saut dans une autre partie de la rom, changement des registres,...
Mon problème se sont toutes les petites instructions du type :
LDY $0056
STX $0319,Y
...
Fondamentalement, je les comprends et peu m'en tirer lors de l'exécution du code, par contre, pour traduire ce genre de truc, je sèche...
Voilà tout ce que je peux fournir pour l'instant.
Si ça permet pas d'avancer, je ferai ce soir une trace du code lors d'une décompression, mais ça me fait mal d'attendre jusqu'à ce soir pour avoir une réponse
- ça prendrai de la place inutilement;
- ça n'est pas nécessaire selon moi;
(- je n'en ai pas sous la main actuellement

Par contre je peux rapidement t'expliquer le truc.
La routine du jeu "Destiny Of An Emperor" démarre à l'adresse $D173 (adresse NES) (à force du lire du code, on s'en rappelle).
Donc à partir de là, plein d'instructions sont exécutées, saut dans une autre partie de la rom, changement des registres,...
Mon problème se sont toutes les petites instructions du type :
LDY $0056
STX $0319,Y
...
Fondamentalement, je les comprends et peu m'en tirer lors de l'exécution du code, par contre, pour traduire ce genre de truc, je sèche...
Voilà tout ce que je peux fournir pour l'instant.
Si ça permet pas d'avancer, je ferai ce soir une trace du code lors d'une décompression, mais ça me fait mal d'attendre jusqu'à ce soir pour avoir une réponse

Le plus simple c'est d'avoir un buffer en C qui contienne une copie de la mémoire NES. Ce qui donne :
LDY $0056 => Y = mem[0x56];
STX $0319,Y => mem[0x319 + Y] = X;
Etc...
Ton code pour extraire devra "juste" initialiser la mémoire comme elle l'est dans le jeu NES. Faut juste identifier les bonnes adresses essentielles ^^
LDY $0056 => Y = mem[0x56];
STX $0319,Y => mem[0x319 + Y] = X;
Etc...
Ton code pour extraire devra "juste" initialiser la mémoire comme elle l'est dans le jeu NES. Faut juste identifier les bonnes adresses essentielles ^^
Sinon, tu peux créer une variable par zone mémoire. L'avantage sur la solution de niorphis est que tu évites le mélange des tailles (du genre des données de 8 bits et de 16 bits). Cela n'a pas d'importance sur la NES, mais en a sur la SNES.
Si tu ressens l'envie de travailler, assieds-toi, et attends qu'elle te passe....
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
- Jes
- Pom pom pom
- Messages : 5822
- Inscription : 24 févr. 2002, 14:05
- Localisation : Siège social de BessaB
- Contact :
Mais c'est quoi cette méthode à la mords-moi-le-noeud 
Tu veux faire un décompresseur pour ton jeu? Fort bien, analyse le code et déduis-en l'algorithme générale. Après, implémente l'algo en C. Traduire le code directement en C sans l'avoir compris, c'est vraiment une très très très très mauvaise façon de faire et de programmer. De toute façon, tu dois avoir compris l'algorithme de décompression pour pouvoir faire un compresseur plus tard. Tu peux t'aider du code source assembleur pour faire ton décompresseur en C bien sûr (les deux se ressembleront forcément) mais ne fais pas un interpréteur!!
Ce que j'ai vu là:
C'est vraiment, mais alors VRAIMENT, immonde 

Tu veux faire un décompresseur pour ton jeu? Fort bien, analyse le code et déduis-en l'algorithme générale. Après, implémente l'algo en C. Traduire le code directement en C sans l'avoir compris, c'est vraiment une très très très très mauvaise façon de faire et de programmer. De toute façon, tu dois avoir compris l'algorithme de décompression pour pouvoir faire un compresseur plus tard. Tu peux t'aider du code source assembleur pour faire ton décompresseur en C bien sûr (les deux se ressembleront forcément) mais ne fais pas un interpréteur!!
Ce que j'ai vu là:
Code : Tout sélectionner
LDY $0056 => Y = mem[0x56];
STX $0319,Y => mem[0x319 + Y] = X

Lol, tu dis que ce code est immonde, mais celui de mon jeu est pire
J'avais pensé à la solution d'Orphis, mais voilà je buggais au niveau de l'initialisation des valeurs clés.
Je pense que je vais passer quelques heures de plus à essayer d'extirper de cet amas de codes hexadécimal, l'ombre d'une logique depuis longtemps oubliée

J'avais pensé à la solution d'Orphis, mais voilà je buggais au niveau de l'initialisation des valeurs clés.
Je pense que je vais passer quelques heures de plus à essayer d'extirper de cet amas de codes hexadécimal, l'ombre d'une logique depuis longtemps oubliée

Ok, je vais préparer ça par contre pour des raisons de commodités, comment le présenter ?
Car en fait, la routine de décompression est très, très longue (changement de bank en fonction de l'octet lu, variation des instructions en fonction de l'octet lu,...).
Bref, si j'assemble l'ensemble des chemins possibles et imaginables (j'ai oublié de dire que la routine en elle-même je l'ai comprise quand même), ça nous fait plusieurs extraits de codes variant de 6Ko, à 45Ko!
Car en fait, la routine de décompression est très, très longue (changement de bank en fonction de l'octet lu, variation des instructions en fonction de l'octet lu,...).
Bref, si j'assemble l'ensemble des chemins possibles et imaginables (j'ai oublié de dire que la routine en elle-même je l'ai comprise quand même), ça nous fait plusieurs extraits de codes variant de 6Ko, à 45Ko!
C'est une méthode immonde mais au moins ça permet d'obtenir des données avec lesquelles on peut bosser. Quand j'ai bossé sur le décompresseur de ToD, Skeud m'avait passé la routine traduite en C et l'adresse mémoire de la routine dans le jeu. J'ai donc pu décompresser quelques textes avec mon décompresseur que j'ai obtenu en analysant le code et comparer avec la référence sortie de l'ASM traduit en C. Ce fut vraiment très pratique pour corriger des bugs quand à l'époque, je connaissais pas trop le domaine.
- Jes
- Pom pom pom
- Messages : 5822
- Inscription : 24 févr. 2002, 14:05
- Localisation : Siège social de BessaB
- Contact :
MouahahahahahahaMoogle a écrit :asm {
[ton code ASM ici]
}
(bah quoi ca marche)

Bein en gros, rassemble toutes les infos que tu as sur le jeu: tables, pointeurs, routines. Tu fous ça dans un fichier txt que tu uploades quelque part. Mais en fait, y a des chances que tu aies mal découpé la routine de décompression, parce qu'elles sont généralement assez courtes. Tu sais quel type de compression est utilisé pour ce jeu?Ridculle a écrit :Ok, je vais préparer ça par contre pour des raisons de commodités, comment le présenter ?
Car en fait, la routine de décompression est très, très longue (changement de bank en fonction de l'octet lu, variation des instructions en fonction de l'octet lu,...).
Bref, si j'assemble l'ensemble des chemins possibles et imaginables (j'ai oublié de dire que la routine en elle-même je l'ai comprise quand même), ça nous fait plusieurs extraits de codes variant de 6Ko, à 45Ko!
C'est pas vraiment de la compression maintenant que j'y pense, mais plutôt un script avec des références.
Du style, on lit le script, octet par octet.
Si l'octet lu est supérieur à 0x7F, alors il va falloir extraire une chaine dans un endroit particulier de la rom (dans une autre bank).
Le problème, c'est que ce texte à aller chercher est disséminé un peu partout dans la rom est en fonction de l'octet lu, il y a plusieurs cas possibles.
C'est donc pas vraiment de la décompression... mais c'est aussi chiant à gérer
Du style, on lit le script, octet par octet.
Si l'octet lu est supérieur à 0x7F, alors il va falloir extraire une chaine dans un endroit particulier de la rom (dans une autre bank).
Le problème, c'est que ce texte à aller chercher est disséminé un peu partout dans la rom est en fonction de l'octet lu, il y a plusieurs cas possibles.
C'est donc pas vraiment de la décompression... mais c'est aussi chiant à gérer

Tu nous la balance ta routine? 
Colle-là quelquepart où on peut mettre du code.
Sinon, comme le disait Orphis, traduire directement et "bêtement" l'algorithme permet de l'étudier plus facilement, et surtout, d'avoir une version extrêmement fidèle au code originel (normal).
Ensuite, si on doit faire l'opération inverse, il est évident qu'une maitrise totale de l'algo est nécessaire, et cette première approche le permet plus facilement.

Colle-là quelquepart où on peut mettre du code.
Sinon, comme le disait Orphis, traduire directement et "bêtement" l'algorithme permet de l'étudier plus facilement, et surtout, d'avoir une version extrêmement fidèle au code originel (normal).
Ensuite, si on doit faire l'opération inverse, il est évident qu'une maitrise totale de l'algo est nécessaire, et cette première approche le permet plus facilement.
Si tu ressens l'envie de travailler, assieds-toi, et attends qu'elle te passe....
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
- Jes
- Pom pom pom
- Messages : 5822
- Inscription : 24 févr. 2002, 14:05
- Localisation : Siège social de BessaB
- Contact :
Tu trouveras ça dans la littérature spécialisée dites du "romhacking" mais effectivement je n'ai jamais entendu parlé de DTE/MTE autre part, même dans la documentation anglaise. Vu le contexte l'usage du mot "MTE" est bien pertinent. De toute manière, on s'en foutLoki a écrit :Tu ne trouvera pas ça dans la littérature spécialisé, on a des mots en français et on appele ça un dictionnaire!Jes a écrit :Bah c'est une variante de MTE donc

Je ne savais pas qu'une traduction conforme en C d'une routine assembleur pouvait aider à sa compréhension (faut croire que la notation C est plus facile?). Du reste, les debuggers sont justement là pour pouvoir étudier facilement le déroulement d'une routine ingame, donc pour moi ça constitue une perte de temps plus qu'autre chose. Maintenant à chacun sa méthode, mais je ne conseillerai jamais ça à un débutant (ni même à quelqu'un d'autre en fait).Skeud a écrit :Sinon, comme le disait Orphis, traduire directement et "bêtement" l'algorithme permet de l'étudier plus facilement, et surtout, d'avoir une version extrêmement fidèle au code originel (normal).
J'ai écrit permettait de l'étudier. Les debuggers de compilateurs C seront toujours plus faciles à manipuler que snes9x version debug. Par exemple, avoir dans une seule fenêtre deux emplacements mémoires différents. Ca permet aussi de travailler sur différentes zones du jeu qui utilisent la même routine, sans avoir à lancer le jeu.Jes a écrit : Je ne savais pas qu'une traduction conforme en C d'une routine assembleur pouvait aider à sa compréhension (faut croire que la notation C est plus facile?). Du reste, les debuggers sont justement là pour pouvoir étudier facilement le déroulement d'une routine ingame, donc pour moi ça constitue une perte de temps plus qu'autre chose. Maintenant à chacun sa méthode, mais je ne conseillerai jamais ça à un débutant (ni même à quelqu'un d'autre en fait).
Après, si tu arrives à comprendre le code rien qu'en regardant la routine, ou en passant des heures à lancer/relancer l'emu et le jeu, tant mieux

Et je rajoute encore : cette méthode est la meilleure quand on veut éviter les bugs!
Si tu ressens l'envie de travailler, assieds-toi, et attends qu'elle te passe....
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
A mon niveau, on ne croit pas, on sait. (Docteur Helmut Perchut)
- Jes
- Pom pom pom
- Messages : 5822
- Inscription : 24 févr. 2002, 14:05
- Localisation : Siège social de BessaB
- Contact :
Ca doit faire six ans que je fais de l'ingénurie inverse et je ne me suis jamais amusé à traduire un code assembleur directement en C. Selon moi, la méthode à suivre est relativement évidente: tracer le code, extraire la routine du log et commencer à l'étudier, avec une feuille de papier et un crayon. Sauf quelques rares cas, il n'y a pas besoin de relancer 50x l'émulateur et placer 32 points d'arrêt pour étudier la routine. Du moment que tu as le contenu des registres en regard (bein comme dans un log normal quoi) ça suffit. Je me répète mais je ne vois vraiment pas l'intérêt de traduire ça en C. Surtout pour une routine d'une centaine de ligne (et c'est quasi tout le temps le cas) qu'est-ce qu'un super méga debugger de la mort qui tue apporte vraiment? Voir deux zones mémoires différentes dans une même fenêtre? Euh ouais, t'es bien avancé avec ça. Perso quand j'étudie une routine, je me concentre sur le code et pas sur les données (d'où la non-nécessité de vérifier ingame le contenu d'une zone mémoire x ou y). A partir d'assez peu d'information sur l'état de la mémoire au début de la décompression, tu peux voir quelques zones-clés vont évoluer.
D'ailleurs, il y a quelques temps j'ai fais analyser à Bahabulle (qui débute en assembleur) une petite routine de 30-50 lignes (une décompression LZ). Je ne citerai pas le jeu, il n'a pas encoré été annoncé je pense. Il a fait ça sans debugger (d'ailleurs je pense pas qu'il savait comment s'en servir
) et sans traduction C. Je l'ai juste aidé à "débrouissailler" le code et à déduire en français ce que faisait tel ou tel groupe d'instructions. Ca a duré quelques heures mais au final il s'en est tiré. Je suis vraiment curieux de connaître son avis sur l'utilité de faire une traduction C avant l'analyse. Baha, si tu nous écoutes... 
D'ailleurs, il y a quelques temps j'ai fais analyser à Bahabulle (qui débute en assembleur) une petite routine de 30-50 lignes (une décompression LZ). Je ne citerai pas le jeu, il n'a pas encoré été annoncé je pense. Il a fait ça sans debugger (d'ailleurs je pense pas qu'il savait comment s'en servir


- BahaBulle
- Bub'n'Bob Pawa!
- Messages : 6496
- Inscription : 06 août 2002, 09:34
- Localisation : Sur une bulle
- Contact :
Euh... personnellement, j'aurais jamais pensé à reprogrammer une routine en C (ou n'importe quoi d'autre) pour 
Comme Jes l'a dit, j'ai pris la routine qu'il m'a donné, et écrit ce que faisait chaque ligne à côté. Après, avec un peu de réflexion (beaucoup beaucoup pour moi
), tu comprends la routine. Et après, tu fais le décompresseur.
Je trouve que ça fait double emploi.

Comme Jes l'a dit, j'ai pris la routine qu'il m'a donné, et écrit ce que faisait chaque ligne à côté. Après, avec un peu de réflexion (beaucoup beaucoup pour moi

Je trouve que ça fait double emploi.