CODINGSOURCES ★ Récupérer une musique dans un jeu CPC par Tom et Jerry GPA 1993,2001 ! ★

Récupérer une musique dans un jeu CPC|Tom et Jerry GPA 1993,2001)Coding Sources
  • Publié dans "Le petit Electro Jack illustré", numéro 9, 1993
  • Retapé et enrichi le 30/06/01 pour Iron !
  • Dédié à Trebor 45, alias Robert Mathieu.

A la demande de Trebor 45, je vais vous faire un petit topo sur l'art et la maniere de récupérer une musique dans un jeu commercial ou une démo. Ayant pas mal pratiqué cet exercice (via les compilations Music Pack !), je peux vous faire part de mon expérience dans le domaine (cela fait un peu ancien combattant !).
Soyez indulgent envers mon style d'écriture, il n'est pas simple de faire un article sur ce sujet sans repéter 30 fois les mots musique, player, etc...

Avant de parler de la marche à suivre, il faut préciser deux choses :
 

  • une interface style le Hacker est chaudement recommandée (jadis disponible pour CPC et CPC plus chez Duchet Computers). A défaut, procurez-vous une Ramcard et récupérez la ROM du Hacker ! Les bases évoquées ici peuvent être utilisées avec un moniteur comme le Super Monitor Razormaid d'Antoine, mais cela compliquera beaucoup les choses...
  • une connaissance minimale de l'assembleur Z80 est nécessaire, toutes les musiques de qualité (sauf celles de Michel Windogradoff !) étant programmées avec ce langage. Si vous ne pratiquez pas, il est toujours temps de vous y mettre !

1) Un peu de théorie...
Une musique en assembleur se compose de deux parties bien distinctes :

  • Le player : en francais, la routine qui "joue" la musique. Il comporte quasiment systématiquement trois sub-routines, celle initialisant la musique (Init), celle la jouant (Exec), et celle l'arrêtant (Stop).
  • Les datas, qui comprennent non seulement les notes, mais aussi les redéfinitions des sons (en gros, les commandes ENV et ENT en Basic). Ces données peuvent éventuellement être compactées (ex : AY player de Madram). 

Les musiques dans les jeux sont en général jouées sous interruption. Cela signifie que le player est appelé à intervalle régulier par l'intermédiaire d'une fonctionnalité spéciale du microprocesseur Z80, le coeur de nos CPC. Deux cas de figure peuvent se présenter : 

  • Le programmeur utilise une interruption programmée (en passant par le vecteur &BCD7). C'est en pratique très rare.
  • Le programmeur utilise l'interruption système RST &38 en la détournant ou en se l'appropriant totalement (la quasi-majorité des jeux, et quelques rares démos).

Parfois, les musiques ne sont pas jouées par l'intermédiaire d'une interruption (beaucoup de démos dont les Music Pack sont concernées). L'interruption système RST &38 est alors "bloquée" et ne sert qu'à faire de la synchronisation vidéo pour utiliser des techniques nécessitant un timing précis, comme de la rupture ou des rasters.

Le code d'une musique peut se trouver n'importe où en mémoire. C'est là un des avantages de l'assembleur, mais dans ce cas précis, cela nous complique la vie. Les données peuvent aussi bien se loger dans la RAM Basic que dans la RAM video, les zones réservées au système ou dans les bank mémoire supplémentaires pour les CPC ayant plus de 64ko. Cela peut donc poser problème pour les trouver. Heureusement que le CPC n'a pas 4mo de mémoire en standard !

En général, le player et les datas d'une musique sont rangés dans la mémoire de façon contigue, mais certains programmeurs sadiques trouvent une délectation suprême à disperser le tout. D'autres poussent même le vice jusqu'a morceler les données un peu partout ! Ne les maudissons pas et supposons qu'ils sont contraints d'agir ainsi pour économiser de la place mémoire. M'enfin, cela n'arrange pas nos petites affaires !!!

Vous l'avez compris, récupérer une musique suppose donc de rechercher son player et ses données !

2) La quête du player

C'est la partie la plus délicate de notre périple. Il nous faut localiser la routine jouant la musique. Les différentes techniques présentées permettent dans la majorité des cas de la trouver.

2.1) Rechercher les opérations effectuées sur le RST &38.

Pour pouvoir utiliser à ses fins ce vecteur d'interruption, le programmeur doit modifier son adresse de saut (JP &B941 sur un CPC 6128). Il doit donc changer le contenu des cases mémoire &39 et &3A par une ou des affectations mémoire. En recherchant les chaînes hexadecimales &39,&00 ou &38,&00, nous avons donc une chance de trouver des bouts de code dans ce style :
 

LD HL,&xxxx LD HL,&39 LD A,&C3 etc...
LD (&39),HL LD (HL),&xx LD (&38),A -
- INC HL LD HL,&xxxx -
- LD (HL),&xx LD (&39),HL -

Evidemment, il y a toujours des originaux pour faire autrement, en utilisant par exemple la commande LDIR ou les registres d'index.

Si vous trouvez quelque chose ressemblant à ce que nous venons de voir, notez l'adresse pokée en &39 et allez voir ce qu'il y a. La plupart du temps, vous allez trouver une routine qui sauvegarde tous les registres du Z80 avec des commandes PUSH, puis lance quelques CALL après de petits tests (JR Z, etc..). Exécutez en mode direct ces CALL après les avoir éventuellement étudiés. Si un bruit continu sort des entrailles de votre CPC, c'est presque gagné ! Notez l'adresse du CALL et testez cette hypothétique musique en tapant une de ces routines :

a) La musique est jouable directement 
boucle CALL &BD19
- CALL music ' (l'adresse que vous avez notée, on suit ?)
- JP boucle

b) La musique n'est pas jouable avec les interruptions ou est logée à un endroit réservé au système :
- DI
- LD HL,&38
- LD (HL),&C9
boucle LD B,&F5
boucle2 IN A,(C)
- RRA
- JR NZ,boucle2
- CALL music
- JP boucle

Si vous entendez une musique, le player est trouvé !

2.2) Rechercher des bouts de routine sauvegardant les registres du Z80

Si la méthode RST &38 s'est révèlée vaine, tout n'est pas perdu. Je ne vais pas expliquer dans les détails la notion d'interruption, mais une contrainte qu'elle pose au programmeur va nous intéresser. Ce dernier est obligé de sauvegarder certains registres du Z80 avant de faire quoi que ce soit.
Comme en prime, le codeur est un animal bête et discipliné qui reproduit ce qu'il a appris, il va sauvegarder ces registres dans un ordre donné :

PUSH AF &F5
PUSH BC &C5
PUSH HL &E5
PUSH DE &D5

On recherche les valeurs correspondant à ces mnémoniques et le tour est joué ! Puis l'on recommence ce que nous avons vu précedemment (analyse et tests des CALL). Attention, une sauvegarde des registres ne correspond pas forcément a une routine jouant ensuite une musique...

2.3) Le Hacker à la rescousse !

Vous avez lamentablement échoué dans vos précédentes recherches. Il existe une solution pour vérifier définitivement que la musique n'utilise pas l'interruption RST &38, le mode ALTERNATE du Hacker. Cette commande permet de faire tourner dans les bank supplémentaires du CPC (version 128ko obligatoire donc !) un programme. Contrairement à ce que prétend la documentation de cette interface, cette commande tourne aussi sur CPC 6128 , mais en mode "aveugle" (on ne voit pas ce que l'on tape au clavier). Enorme avantage, une fois le programme stoppe par le bouton magique, les banks ne sont pas initialisées. Il suffit de connecter ensuite la bank &C4 (bank 1) et de désassembler en &4038 pour trouver l'adresse de saut de l'interruption &38 du programme arrêté, sympathique non ? A partir de la, si cette adresse semble intéressante, on recommence la procédure décrite en 2.1).

Si vous êtes l'heureux utilisateur d'une Multiface II, vous n'avez pas besoin de cette astuce, un arrêt du programme et l'édition de la RAM en &38 suffit.
Cette méthode n'est évidemment utile que si le jeu contenant la musique convoitée n'occupe pas les bank supplémentaires...

Pour faire ce genre de tests, vous pouvez aussi vous servir d'un émulateur comme Caprice, qui permet de geler un programme et voir le contenu de sa mémoire...

2.4) Rechercher la routine envoyant des données au PSG

Dans toute musique, le programmeur est obligé d'utiliser une routine qui envoie des données au processeur sonore du CPC. Il existe bien dans le firmware du CPC un vecteur système servant à cela (&BD34), mais il n'est jamais utilisé. Les programmeurs de musiques préfèrent utiliser leur propre routine. Cette dernière est grosso modo toujours la même et se déroule comme suit (exemple : routine d'une musique Soundtrakker).

LD B,&F4
OUT (C),A
LD B,&F6
IN A,(C)
OR &C0
OUT (C),A
AND &3F
OUT (C),A
LD B,&F4
OUT (C),C
LD B,&F6
IN A,(C)
AND &80
OUT (C),A
OUT (C),C

Le but n'est pas ici d'expliquer le fonctionnement de la routine. Il suffit de savoir qu'un bout de code dans ce style sert soit à envoyer des données au PSG, soit à lire l'état du clavier, ou lire des données provenant du lecteur de cassette (je vois deja les puristes s'insurger !).
En cherchant des chaînes hexadécimales 06 F4 (LD B,&F4) et 06 F6 (LD B,&F6), on a donc des chances de trouver ce bout de programme.
A partir de la, il faut "remonter" dans le code, trouver quelles routines l'utilisent, pour finalement retrouver le player ! C'est certe compliqué, mais cela fonctionne souvent.

2.5) Le désassemblage pur et dur !

Le programmeur de jeu dont vous convoitez la musique est un être raffiné ; dernière solution, la plus laborieuse, il vous faut étudier le programme depuis son adresse d'exécution. Pas de recette miracle, seules de la perséverance et un peu de chance vous permettront d'arriver à vos fins...

Bien, à partir de maintenant, je considère vous vous avez trouvé le player (pas de panique si ce n'est pas encore bien clair, nous verrons deux exemples par la suite). Avant de passer au nettoyage de la musique (c'est à dire ne garder que les données utiles), il nous faut trouver les adresses d'init du ou des thèmes qui la composent, et pour les perfectionnistes, celle qui l'arrête. En pratique, on peut se passer fort bien de cette derniere sous Basic, un CALL &BCA7 faisant l'affaire (pitié, pas de PRINT CHR$(7), ce que l'on voit souvent dans des vieilles mauvaises démos allemandes).

Là, pas de recette miracle, il faut noter que souvent, cette adresse n'est pas bien loin de la routine jouant la musique. On retrouve alors un appel à cette routine "près" de la portion de code détournant le RST &38.
A vos désassembleurs !

Pour les musiques ayant plusieurs thèmes, en général, les programmeurs utilisent un registre (souvent A) pour définir quel air doit être joué. Parfois, il faut poker des valeurs en mémoire (les musiques de Tiny Williams, par exemple). Bref, il faut avoir du flair !

3) Le grand ménage

Il faut maintenant récupérer les données associées au player que vous avez débusque ! Là encore, pas de méthode miracle, mais quelques recommandations à suivre donnant souvent de bons résultats.

La première chose consiste à parcourir la mémoire à partir du player pour trouver une zone de 00 conséquentes ou une routine n'ayant rien à voir avec la musique. Cette technique primaire est assez efficace car souvent, le player et les données sont situées l'un après l'autre, particulièrement si votre musique se trouve dans une bank de mémoire supplémentaire, ou dans la zone réservée au système (&A67B à &BFFF pour le 6128).

Sinon, il faut procéder par tâtonnement, en sauvegardant tout d'abord une large zone mémoire, puis en la "dégraissant" (remplir des bouts avec des 00) au fur et à mesure. Je vous le concède, ce n'est pas bien passionnant ni tres épanouissant techniquement. En plus, c'est souvent long !
La longueur usuelle pour une musique simple varie entre &A00 et &1000, mais peut être plus grande chez certains éditeurs (Krisalis, par exemple !).

Petite anecdote, la musique la plus lassante (pour être poli) que j'ai nettoyée est celle de Turbo out run. Elle ne comporte pas moins de 8 blocs de données différents, situés un peu partout dans la mémoire. De quoi devenir dingue !

Voilà, après ces explications, "ripper" une musique de jeu devrait vous sembler facile. Euh.. non ? Bon, deux exemples vous montreront que c'est plus simple que cela en à l'air.

4) Ripper la musique de 'Outrun'avec le Hacker

Nous allons récupérer ensemble la musique d'un jeu très célèbre, Outrun. C'est vrai, la conversion sur nos micros de cette borne d'arcade est vraiment mauvaise. La musique est néanmoins assez mignonne sur CPC, en plus, elle est facile à récupérer !

a) La recherche du player

après avoir chargé le jeu, on l'arrête avec le hacker au niveau de la page de présentation. On commence la recherche avec la première méthode (cf 2.1)).

* Taper 'S' puis la chaîne hexadécimale 38 00
> Le Hacker trouve trois fois cette chaîne aux adresses &107, &4CA4, &83BE.

* Désassembler un peu avant ces adresses (&100, &4CA0,&83B0 par exemple) grâce à la commande 'D' et étudier ces zones de code.

Oh miracle, la première adresse (&107) a bien une action sur le RST &38 en pokant l'adresse &17C en &39.

* Désassembler en &17C.
> Nous trouvons d'abord deux instructions qui sauvegardent des registres (ca ne vous rappelle rien ?), puis deux appels à deux routines :

CALL C,&8000
CALL &99B

* On exécute le premier CALL en mode direct.
> Le CALL &8000 se traduit par un son strident et continu. Le player est trouve ? Vérifions...

* Taper la routine suivante en &BE80 puis faire un CALL &BE80
 
boucle CALL &BD19
- CALL &8000
- JP boucle

> Normalement, vous devriez entendre la musique d'Outrun ! Le plus dur est fait.

b) Recherche de la sub-routine d'initialisation

On revient à l'endroit ou nous avions trouvé une instruction travaillant sur le RST &38 (en &107).

* Désassembler de nouveau à partir de &100 et chercher un CALL effectué pas trop loin de la case mémoire &8000 (adresse d'exécution précedemment trouvée).
> On trouve le code suivant :

LD A,&1
CALL &80B9

Cet appel se fait à priori dans la zone mémoire où se trouve la musique... Testons cette routine à l'aide d'un CALL (ne pas oublier de mettre le registre A a la valeur 1). Evidemment, rien ne se passe. Il faut exécuter la routine jouant la musique pour en vérifier le résultat.
Décidement, nous avons beaucoup de chance, nous avons trouvé du premier coup la bonne adresse, la musique est jouée à partir de son début.
Comme nous savons qu'il y a deux thèmes sur Outrun, cherchons un éventuel autre appel à la routine d'initialisation (CALL &80B9 pour ceux qui ne suivent pas !).
Aie, c'est le désert total, on n'a rien trouvé de plus...

A priori, vu ce que nous avons trouvé précedemment, il semblerait que c'est le registre A qui détermine la musique à initialiser. Il ne nous reste qu'à faire des essais avec différentes valeurs pour ce registre, en partant de 0. La chance (ou le talent ?) nous sourie encore, car un CALL &80B9 avec A=0 initialise l'autre thème du jeu.
Les plus curieux pourront lancer la routine avec A=3 ou A=4, par exemple, vous entendrez alors des bruitages !

c) Le nettoyage

* L'exécution de la musique étant en &8000, nous allons éditer la mémoire a partir de cette adresse et "remonter" (commande 'e' puis flèche vers le haut).
> Nous voyons une petite zone de &00 avant l'adresse d'exécution. Cela permet de supposer que l'adresse d'exécution correspond au debut de la musique.

* Cherchons maintenant la "fin" du morceau, en descendant dans la mémoire (flèche vers le bas). En &8F80, nous retrouvons une autre zone de &00.
> Nous allons travailler à partir de ces deux bornes.

* Sauvegarder la zone dans un fichier (OUTRUN.MUS, par exemple).
* Retourner sous Basic pour effacer la mémoire
* Revenir sous le prompt du Hacker, puis charger en mémoire le fichier OUTRUN.MUS.
* Tester la musique en l'écoutant jusqu'à la fin.
Dans notre cas, tout se passe bien.

* Nous pouvons donc recommencer l'opération en effacant une petite portion de la zone RAM occupée par le fichier (par exemple, de &8E00 à &8F80).

> Dans cet exemple précis, tout rognage du fichier OUTRUN.MUS se traduit par des pertes de données (sons bizarres, voix ne jouant plus, ou avec des rythmes décales, etc...). La longueur définitive de notre fichier est donc :

&8F7F - &8000 = &F80

Victoire, la musique d'Outrun est mise en fichier et utilisable dans vos créations les plus folles ! Il faut quand même noter que cette extraction est facile. Pour quelqu'un de bien entrainé, la chose est règlée en moins d'un quart d'heure. Cela constitue neanmoins un excellent exercice pour les profanes...

5) Ripper la musique de "Dizzy down the rapids" avec le Hacker

Et hop, une commande spéciale d'Iron. Nous allons voir maintenant un deuxième exemple. La victime est cette fois-ci un jeu de Codemasters dans le style de Toobin, avec l'incontournable Dizzy ! La description sera un peu plus lapidaire que pour Outrun, mais devrait suffire pour y arriver.

a) La recherche du player

* Arrêter le jeu sur la page du menu avec le Hacker puis rechercher la chaîne hexadécimale 38 00 en mémoire :
> On trouve 3 adresses : &6833, &7297, &AA76

* Désassembler un peu avant ces adresses (&6820, &7290, &AA70)
> Rien ne semble correspondre à du code.

* Rechercher la chaîne hexadecimale 39 00 en mémoire :
> On trouve une seule adresse : &2F43

* On désassemble en &2F30.
> On trouve du code intéressant :

&2F3F LD HL,&2F0E
&2F42 LD (&39),HL

* Désassembler en &2F0E.
> Il y a une routine sauvegardant des registres, puis lançant avec des tests trois CALL : &2C31, &23E6 et &6F17.

* On exécute les trois adresses :
> Rien ne se passe pour les deux premières. Avec &6F17, un souffle se fait entendre...

* Tester l'adresse avec la routine suivante en &BE80 :
 
boucle CALL &BD19
- CALL &6F17
- JP boucle

Oh, une belle musique retentit !

b) Recherche de la sub-routine d'initialisation

* On reprend le desassemblage en &2F30 en cherchant un CALL dans la zone des &6F17.
> Rien.

* On étudie les CALL situes après &24F2 (la commande pokant le RST &38) en desassamblant à chaque adresse, toujours pour rechercher un éventuel appel dans la zone pres de &6F17.

En &2F6B, on trouve un CALL &3363, qui exécute le code suivant :

CALL &6E3D
RET

* On essaie cette adresse. Il ne se passe rien. Par précaution, on désassemble en &6E3D. La routine ressemble pourtant à une routine d'initialisation. Elle est cependent un peu courte, et est suivie d'une autre routine, en &6E44.

* On recherche des appels à cette routine (&6E44)
> deux adresses sont trouvées : &335C et &3361. Tiens, c'est très proche de ce que nous avions trouvé plus haut...

* En désassemblant (on n'arrête pas ici !) un peu avant &335C, on trouve ceci :
 
&335A XOR A
&335B JP &6E44
&335E LD A,&1
&3360 JP &6E44

> Hum, ca ressemble à une routine d'initialisation, ca... On teste !

Trouvé ! En prime, on a aussi la routine d'arrêt, qui est &6E3D ! En général, on trouve plutôt d'abord la routine d'initialisation.

c) Le nettoyage de printemps.

* On étudie le code un peu avant &6E30. A priori, le début de la musique est bien en &6E3C, car le "visuel" de la mémoire avec Edit (permet de voir une plage de la RAM sous forme de valeurs hexadecimale) ressemble à un sprite (valeurs se reproduisant à des intervalles constants).

* On se déplace maintenant vers le haut de la mémoire. A partir de &7C3E, il y a des zeros partout !
> On suppose alors que la musique est située entre &6E3D et &7C3D.
Il n'est en fait même pas nécessaire de faire des tests, car nos précedents désassemblages nous ont appris que la routine d'arrêt de la musique allait en &7C14. Si on désassemble à partir de là, on trouve une routine qui se termine en... &7C3D !

Voila, la musique est prête à etre sauvegardée. Cet exemple est, vous l'avez constaté, un peu plus complexe que pour Outrun. Il constitue un bon exemple de ce à quoi l'on est en général confronté.

Récupérer une musique dans un jeu CPC par Tom et Jerry GPA 1993,2001 !

★ ANNÉE: ???
★ AUTEUR: TOM et JERRY/GPA

CPCrulez[Content Management System] v8.75-desktop/c
Page créée en 065 millisecondes et consultée 978 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.