Actualité
daedaluskz0

A la recherche du bug SSB perdu ... bis

par
Source: 0635425

Nous vous parlions mardi de la simulation de fragments de code et avec quelles techniques on pouvait faire le parallèle avec l’émulation sur Daedalus. Dans ce nouvelle article, nous allons vous parler des outils que StrmnNrmn, utilise pour trouver le point exact où l’émulation prend la mauvaise voie.

Vous pouvez retrouver l’article de mardi ici.

Dans ce nouvelle article, nous allons vous parler des outils que StrmnNrmn, autre de Daedalus, utilise pour trouver le point exact où l’émulation prend la mauvaise voie.

Daedalus peut être compilé avec un mode synchronisé qui permet de donner l’information du statut de l’émulation au synchroniseur, qui se divise ensuite en deux branches. La branche des producteurs permet de copier ces information sur le disque dur. Tandis que le mode du consommateur compare la lecture du code donné sur ordinateur à ce qui est généré par l’émulateur. Dans le cas d’un bug, le synchroniseur va arrêter la comparaison simultanée pour indiquer exactement où les deux codes ont changés de cap.

Rappelons que cette technique est valable puisqu’un émulateur lancé deux fois dans de mêmes conditions réagira de la même façon. Tous les paramètres de son, graphique, contrôleurs doivent tous être sur la même base. Il est bien normal que reproduire une séquence de fonctions identiques plusieurs fois est impossible, on parle ici d’appuyer sur les même touches plusieurs fois, et de refaire la même séquence en un temps identique. Dans le cas de la branche du producteur, le synchroniseur va enregistrer les commandes appuyées et pour le consommateur, il va tout simplement rejouer la séquence dans l’ordre. Tout ça toujours dans l’optique de la comparaison, ne l’oublions pas ! Il est à noter que d’autres commandes externes peuvent être utilisées de cette façon, comme par exemple le chronomètre.

Vous pouvez donner au synchroniseur le nombre de points de comparaison que vous voulez. Pour les plus petits programmes, il est possible de s’en tirer en regardant chaque ligne de code, mais pour les plus gros programmes vous pouvez aller jusqu’à mettre une balise pour comparer chaque ligne de code pour vérifier sa validité avec toujours le dynarec, qui est le tout, émulé en temps normal.

Notre ami StrmnNrmn utilise fièrement des macros pour identifier où les problèmes surviennent. Une fois activées, elles vont permettre de situer le soucis. Voici un exemple d’un point de synchronisation, où le code est comparé :

u32 pc = gCPUState.CurrentPC;

SYNCH_POINT( DAED_SYNC_REG_PC, pc );

OpCode op;
if( CPU_FetchInstruction( pc, &op ) )
{
CPU_Execute( pc, op );
}

La ligne intéressante dans ce code est la deuxième, où la macro symbolise un chronomètre pour arrêter le temps. Pour les producteurs, cela permet de copier la valeur du code reçu jusqu’à présent, et pour le consommateur il vérifie si cette valeur est bien la même que celle émulé dans des conditions idéales.

Cette partie : DAED_SYNC_REG_PC, permet simplement de nommer le morceau qui a été émulé. Une autre constante permet de prendre le contrôle de ce qui est synchronisé :

enum ESynchFlags
{
DAED_SYNC_NONE = 0x00000000,

DAED_SYNC_REG_GPR = 0x00000001,
DAED_SYNC_REG_CPU0 = 0x00000002,
DAED_SYNC_REG_CCR0 = 0x00000004,
DAED_SYNC_REG_CPU1 = 0x00000008,
DAED_SYNC_REG_CCR1 = 0x00000010,

DAED_SYNC_REG_PC = 0x00000020,
DAED_SYNC_FRAGMENT_PC = 0x00000040,
};

static const u32 DAED_SYNC_MASK(DAED_SYNC_REG_PC);

#define SYNCH_POINT( flags, x, msg )
if ( DAED_SYNC_MASK & (flags) )
CSynchroniser::SynchPoint( x, msg )

Si on veut en obtenir plus de synchronisation, il faudra changer la valeur du morceau :

static const u32 DAED_SYNC_MASK(DAED_SYNC_REG_PC|DAED_SYNC_REG_GPR);

Pour pouvoir le faire, il faut modifier l’émulateur au grand complet, et changer la manière de synchroniser, et donc le programme. C’est évidemment un processus très long, mais qui est très avantageux par la suite puisqu’il permet d’arrêter l’émulation et de supprimer des morceaux de codes inutiles pour augmenter la vitesse de Daedalus.

Le majeur inconvénient avec cette technique est que le processus est extrêmement ralenti. Comme nous vous parlions mardi, ça peut prendre jusqu’à 500 millions d’instructions avant de trouver l’endroit à solutionner. Imaginez vous que cela peut prendre plus de 2147483648 octets, ça commence à faire lourd ! SrmnNrmn compresse ses données pour rendre cette technique beaucoup plus praticable. Il crée aussi les résultats en paquet de 32bit et utilise le synchronisateur sur chacun de ces paquets. En utilisant ces deux techniques, il obtient les mêmes données, mais beaucoup moins redondantes.

Le plus grand avantage de cette technique est que l’optimisation est grandement facilitée. StrmnNrmn va synchroniser une version de Daedalus, qui est connue comme réagissant correctement, puis va la comparer en mode consommateur avec une prochaine révision pour voir si le code correspond bien. Il n’est pas donné à 100% que si la comparaison donne des résultats identique alors la version concoctée est bien sans problèmes.

StrmnNrmn utilise aussi le code de synchronisation pour régler des problèmes de dynarec. Il commence d’abord par désactiver le module principal de tout fonctionnement, soit le dynarec, puis il se crée une base sur quoi travailler en mode consomateur. Il va ensuite redémarrer Daedalus en mode dynarec avec arrêt précis en mode consommateur pour encore une fois comparer les résultats pour y travailler.

C’est exactement de cette façon que StrmnNrmn a trouvé et corrigé le problème d’affichage de Super Smash Bros. En comparant les résultats, il a remarqué qu'une erreur de 2.57 à 7 % sur le nombres de commandes exécutés différenciaient du mode producteur. Après la 387,393,387ème ligne de code, l’émulation allait dans une tout autre direction.

Maintenant qu’il connaissait à quel moment le problème apparaissait, StrmnNrmn a installé quelques points de comparaisons dans le codes avant et après le moment où le code diverge. Voici le log du code correctement émulé et en second lieu celui qui cause soucis :

Count 171f7c35: PC: 80132500: LW ra 0x0024(sp)
Count 171f7c3d: PC: 80131fb8: SW s0 -> 0x0020(sp)
Count 171f7c3e: PC: 80131fbc: CLEAR a0 = 0
Count 171f7c3f: PC: 80131fc0: CLEAR a1 = 0


Count 171f7c35: PC: 80132500: LW ra FP06
Count 171f7c3a: PC: 80132aec: NOP
Count 171f7c3b: PC: 80132af0: SWC1 FP06 -> 0x0018(a0)
Count 171f7c3c: PC: 80132af4: LBU v0 0x80132b24
Count 171f7c3f: PC: 80132b00: ADDIU at = r0 + 0x0009

Dans le bon code, le PC est énumé comme 0x80131fb0 et dans le mauvais chemin il est plutôt écrit 0x80132af0. Si vous avez de bons yeux, vous avez surement aussi remarqué que deux lignes avant, le code émulé va également dans la mauvaise direction en optant pour le Coprocesseur 1 au lieu d’exécuter une commande.

Cela donne une très bonne exemple de la faiblesse possible entourant l’utilisation de cette méthode. C’est uniquement bon aux endroits où vous avez installé une balise pour comparer. La synchronisation n’a jamais remarqué le fait que l’émulation exécutait une intruction différente, .deux lignes plus haut. Dans cette exemple, il a été chanceux que ce passage non-détecté par le synchroniseur soit seulement deux lignes plus haut. Il se pourrait que de telle changement surviennent sans avertissent à des centaines de lignes d’intervalles.

Heureusement, c’est assez simple de rajouter des balises pour détecter ce genre de changement, mais en abuser résulterait à une synchronisateur d’une lenteur impraticable. StrmnNrmn ajoute de plus en plus de balises lorsque nécessaire.

Pour en revenir à Super Smash Bros, il a donc découvert que la rom exécutait une sorte de code pour lui même sans avertissement. Le dynarec obéissait à ces changement, et il en résultait que l’émulation était ratée des centaines de lignes avant de voir le résultat à l’écran.

Devoir gérer ce genre de problème est très dur, la rom va supprimer sa mémoire lorsque qu’elle exécute de nouveaux codes. Quand StrmnNrmn détecte ce genre de problème, il crée un tout nouveau code pour l’occasion. Ça amène un certain gain de vitesse, mais est réellement long à réaliser.

Ironiquement, le problème qui affectait Super Smash Bros n’était pas du à un soucis avec Daedalus. C’était un problème dans la cartouche même de SSB, mais qui n’a jamais été une source de distraction avec la console en main. Pour une raison inconnue, SSB aurait du supprimer le contenu de sa mémoire après avoir exécuté une commande, pour éviter la confusion. La seule raison pour laquelle la rom fonctionne sur console est qu’au moment où la console exécute la commande, la mémoire a déjà été vidé et remplis de nouveau avec les nouvelles instructions.

Nous espérons donc pour StrmnNrmn, qu’il n’y aura pas trop de jeux dans cette état. Nous espérons que vous en avez apris un peu plus sur les coulisses du monde de la programmation avec StrmnNrmn et son émulation Deadalus.

Comptez sur PSPGen pour continuer à vous soumettre le plus de traductions possible ! Et vous serez comme c’est présentement le cas, toujours les premiers informés !

Source : Blog de StrmnNrmn

Retrouvez les précédents articles sur ce sujet :
- A la recherche du bug SSB perdu ...
- DAEDALUS : un point sur Super Smash Bros
- DAEDALUS : un petit point sur l'avancée de la R12.

Commentez cette news en cliquant ICI

Mots-clés

Commenter 6 commentaires

Anonymous
8O 8O 8O 8O 8O 8O 8O 8O ardu l'article, c'est pour les connaisseurs en programmation.
Moi j'ai rien comprit, mais tand pis !!! :wink: :wink:
Signaler Citer
Anonymous
Pas compris grand chose (des que y'a du code, larguée XD). Pas tout lu non plus d'ailleurs (fatigue).

M'enfin, bonne chance a lui, ca doit demander un travail/temps incroyable de faire tout ca O_o
Signaler Citer