Author Topic: [Foutrac] Trucs, bidules, machins et généralités oldies  (Read 849410 times)

Online Enker

  • Hero Member
  • *****
  • Posts: 3 910
  • Bonus Pute: 169
Re: [Foutrac] Trucs, bidules, machins et généralités oldies
« Reply #915 on: 10 September, 2025, 17:02:28 »
"Je trouvais que ma machine à café était trop lente et trop bruyante, maintenant elle transforme le marc en or"

Fantastique boulot  ! A publier sur feu-romhack :)

Offline Bodom

  • Hero Member
  • *****
  • Posts: 1 147
  • Bonus Pute: 56
  • Connard chaotique bon
Re: [Foutrac] Trucs, bidules, machins et généralités oldies
« Reply #916 on: 12 September, 2025, 10:57:45 »
Quand j'aurais le temps, je vous ferai un petit dossier.

A la base le romhacking me paraissait être quelque chose d'extrêmement compliqué, mais en réalité, pour qui a quelques bases d'informatique (et même pas de programmation - tant l'assembleur est loin des if then else qu'on connait), c'est très accessible. Et surtout, c'est passionnant de détricoter les mécanismes d'un jeu.

Pour faire un exemple très simple, la suppression de l'apparition des monstres au bout de quelques secondes :

On lance le jeu avec Mame en mode debug, ce qui ouvre en plus du jeu une interface dans laquelle on peut afficher le code asm, la mémoire, et taper des commandes qui permettent de faire moult choses utiles (genre arrêter le programme quand il arrive à une instruction donnée, ou que telle valeur est écrite à tel emplacement mémoire).

On sait que les monstres apparaissent au bout de quelques secondes, et que si on se déplace cela n'arrive pas. Il y a donc forcément un timer quelque part en mémoire. On utilise donc l'outil qui permet de chercher des cheats, comme ce qu'on trouve sur quasiment tous les émulateurs.

On tape donc "cheatinit", cette commande fait simplement un cliché de toutes les valeurs en mémoire. On attend un peu et on tape "cheatnext +", cela va ressortir toutes les valeurs qui ont augmenté depuis le cliché, et et éliminer toutes les autres du cliché initial. En retapant "cheatnext +", cela va ressortir toutes les valeurs qui ont encore monté, et ainsi de suite, jusqu'à arriver à une liste suffisamment restreinte (4 ou 5 adresses). On va donc les regarder ces valeurs en mémoire tout en laissant le jeu tourner, et bingo, on voit qu'à la zone 100CBA, la valeur monte très vite et revient à 0 dès que les monstre apparaissent.

Donc c'est cool, on a la zone du compteur, maintenant on fait quoi avec ça ? Et bien on va voir comment elle est utilisée, avec une commande très pratique, le "watchpoint". On met donc un watchpoint sur 100CB8 en lecture, ce qui va suspendre l'exécution du jeu dès que cette zone est lue par le programme. La commande à peine entrée, le jeu s'arrête et on voit ça au niveau de la zone d'exécution :

Code: [Select]
00C9EE  addq.l  #1, ($cb8,A6)                               52AE 0CB8
00C9F2  move.l  ($cb8,A6), D0                               202E 0CB8
00C9F6  cmp.l   D1, D0                                      B081
00C9F8  bcs     $ca00                                       6506
00C9FA  jsr   $2ff60.l   ($cb8,A6)                          4EB9 0002 FF60
00CA00  rts                                                 4E75

Le code est arrêté à l'instruction C9F2, qui copie la valeur mémoire à 100CB8 dans D0, ce qui a bien déclenché l'arrêt du programme suite à une lecture de cette zone mémoire.

Note : les valeurs mémoire sont rarement notées "en dur" dans le code, mais plutôt sous la forme ($cb8, A6), les registres d'adresse (de A0 à A7) étant chargés au préalable dans le code pour simplifier les opérations et limiter ainsi l'occupation de mémoire. Ici l'adresse 100000 est chargée dans A6 au moment de l'exécution, ($cb8, A6) indique donc l'adresse 100CB8. Par rapport au fait de noter l'adresse en dur, ça fait économiser 1/3 de place dans la ROM, et vu les milliers d'instructions c'était extrêmement important à l'époque.

Bref, le code est ici très simple :

addq.l  #1, ($cb8,A6) : ajoute 1 dans cette zone mémoire
move.l  ($cb8,A6), D0  : copie cette valeur mémoire dans D0
cmp.l   D1, D0  : Compare D0 à D1
bcs     $ca00 : Si c'est plus petit, on saute à l'instruction ca00
jsr   $2ff60.l : appelle la sous-routine qui est située à l'adresse 2ff60
rts : return from subroutine = on quitte la fonction

Note : Les registres de données (D0 à D7) sont des zones mémoires dédiées auxquelles le CPU peut accéder très rapidement, et sont souvent utilisées pour faire des calculs et des comparaisons.

Note 2 : D1 a été défini à 200 un peu plus haut dans le code, on remarque que cette valeur change en fonction des niveaux, pour les niveaux 2 et 3 elle est à 100, ce qui fait apparaître des monstres volants au bout de 256 frames seulement (100 en hexa = 256), soit 4,2 secondes à 60 ips. Pour le niveau 1 c'est à 200 soit 512 frames.

Traduction du code :

A chaque frame, le compteur est incrémenté de 1. Il est ensuite placé dans D0, et comparé à D1. S'il est plus petit alors on saute à l'instruction située à CA00 (le rts), on sort donc de la routine sans rien faire d'autre. S'il est égal par contre, on continue le code et on saute à la sous routine située à 2ff60, qui est donc fort logiquement celle qui fait apparaître les monstres.

Maintenant qu'on a compris le code, on a plusieurs solution pour ne plus les faire apparaître. On peut tout simplement virer la commande addq pour ne plus incrémenter le compteur, mais peut-être est-il utilisé par autre chose, et on a la flemme de chercher. On pourrait aussi chercher un peu plus haut dans l'historique du code ce qui charge la valeur 200 dans D1, trouver depuis où ce 200 est lu, et le modifier en FFFF (65536), ce qui passerait le délai de déclenchement à 18 minutes environ.

Ou alors remettre le compteur à 0 et ne rien faire d'autre, c'est ce que j'ai fait :

Code: [Select]
00C9EE  addq.l  #1, ($cb8,A6)                               52AE 0CB8
00C9F2  move.l  ($cb8,A6), D0                               202E 0CB8
00C9F6  cmp.l   D1, D0                                      B081
00C9F8  bcs     $ca00                                       6506
00C9FA  clr.l   ($cb8,A6)                                   42AE 0CB8
00C9FE  nop                                                 4E71
00CA00  rts                                                 4E75

Le code reste le même (on compare à 200, si c'est en dessous on sort de la routine), mais quand le compteur arrive à 200, j'ai remplacé le jsr par un clr de 100cb8, ce qui remet tout simplement le compteur à 0. Problème : l'instruction jsr prenait 6 octets (4EB9 0002 FF60), le clr en prend 2 (42AE 0CB8), il faut donc remplir l'espace restant car sinon il y aura dans le code 42AE 0CB8 FF60, le clr va bien se faire mais ensuite le CPU va vouloir interpréter FF60 et ça va planter. On met donc l'instruction hyper pratique NOP (No OPeration - pour dire au CPU de ne rien faire) pour combler le vide.

Pourquoi remplir l'espace restant et ne pas mettre directement les instructions suivantes ? Car ça décalerait l'intégralité du reste du code, et comme tout est à base d'adresse plus rien ne fonctionnerait.

Désolé si c'est un peu brouillon, mais j'espère avoir au moins un peu démystifié la chose. Une fois que vous avez compris ça, le reste c'est juste apprendre la petite dizaine d'instructions ASM qui représentent 95% du code, assaisonné d'une pointe de raisonnement logique :)
« Last Edit: 12 September, 2025, 10:59:34 by Bodom »

Offline wonderpanzer

  • Snack of Quality
  • Hero Member
  • *****
  • Posts: 1 765
  • Bonus Pute: 102
  • etre analogique vivant dans une 3r3 num3r1qu3
Re: [Foutrac] Trucs, bidules, machins et généralités oldies
« Reply #917 on: 12 September, 2025, 20:06:12 »
Passionnant : j'espère pouvoir venir avec un pote également  féru de ce jeu au prochain Baugé Halloween que tu nous montre tout celà