Fabriquer un lecteur de carte mémoire Nintendo 64 (2)
Les spécifs un peu détaillés du bordel
Ce billet va compiler, plus ou moins dans l'ordre, toutes les informations et hack que j'ai pu glaner ça et là pour tenter de reconstituer un lecteur de carte mémoire Nintendo 64. C'est donc, pour le moment en tout cas, le seul endroit où l'on pourra avoir une idée complète du fonctionnement de la manette N64 et pas, comme on le voit souvent, du statut des sticks et des boutons ou de comment fonctionne le Rumble Pack.
Un périphérique série
Dans la grande tradition de Nintendo (faire le moins cher possible mais qui fonctionne), la manette N64 est un simple périphérique série avec seulement 3 broches : la terre, l'alimentation (entre 3 et 3.8V, mais 3.3 est la valeur recommandée) et une ligne de donnée bidirectionnelle.
Source : Instructables.com
Les bits qui passent sur la ligne fonctionnent quand même d'une manière un peu particulière :
Source : Nintendo 64 to PS/2 Mouse Translator
Pour transmettre un 1, il faut mettre la ligne en LOW (pas de tension) pendant une microseconde, puis la mettre en HIGH (tension) pendant 3 microsecondes. Pour un zéro, c'est le contraire : 3 microsecondes de LOW pour une microseconde de HIGH. La réception de données fonctionne exactement de la même manière. Maintenant, tout se résume à une histoire de bits et de timing.
Sors ton bit
La manette et le slot pour la carte mémoire sont a priori assez stupide (je dis a priori parce que j'ai pas encore tâté la bête à 100%). La manette reçoit une série d'instructions suivie d'un bit stop (un dernier bit inséré à la fin de la chaîne de contrôle). Elle répond 2 microsecondes après le bit stop, envoit un bit stop puis reste silencieuse pendant 200 microsecondes (impossible de l'interroger).
Une compilation de différente source (un projet pour brancher des manettes Gamecube sur des consoles N64, le code source de Mupen64+, l'un des rares émulateurs libres de la console et diverses informations glânées sur des fora) permettent de dégager le comportement approximatif ci-dessous.
Envoyer 0x00 initialise la manette (je ne sais pas bien à quoi cela correspond mais probablement l'initialisation du stick analogique en position neutre par exemple). La console répond des choses (pas très intéressantes) pour savoir quel accessoire est branché sur la manette (Rumble Pak, Controller Pak, Transfer Pak, etc).
Envoyer 0x01 demande à la console l'état des boutons. Elle renvoit alors 16 bits qui correspondent aux boutons (1 pour enfoncé) : A, B, Z, Start, Pad Haut, Pad Bas, Pad Gauche, Pad Droit, rien, rien, L, R, C-Haut, C-Bas, C-Gauche et C-Droit ; puis elle renvoit 16 bits correspondant à la position (axe X et axe Y) du stick analogique.
Envoyer 0x02 provoque une lecture sur le contacteur de la manette. Les deux octets suivants correspondent à une adresse sur le contacteur. Les adresses en question semblent correspondre au mappage suivant :
- 0x0000 -> 0x7FFF : les adresses mémoires de la carte mémoire ;
- 0x8000 : identification et initialisation ;
- 0xC01B : contrôle du moteur du Rumble Pak.
La manette envoit systématiquement 32 octets de données en réponse lorsque l'on interroge une adresse de la carte mémoire. A priori, interroger autre chose provoque l'envoi de message d'erreur divers (pas forcément toujours clair d'ailleurs…). Bref, lire la carte mémoire en entier devrait consister à envoyer des codes 0x02 puis des adresses de 0x00, 0x00 à 0x7F, 0xFF et lire les 32 octets donnés à chaque réponse. Il ne reste en théorie plus qu'à tout mettre au format brut dans un fichier.
Envoyer 0x03 provoque une écriture sur le contacteur de la manette. En fait, « écriture » est un bien grand mot. Ça ressemble plus à une fonction à tout faire. Par exemple envoyer 0x03 suivi de 0xC0, 0x1B suivi d'une série de 1fait vibrer le Rumble Pak (une série de 0 arrête les vibrations). Envoyer 0x03 suivi de 0x80, 0x01 renvoit une valeur qui semble être un checksum des données venant juste d'être écrites. Apparemment l'adresse du statut pour un Controller Pak (0x8001) correspond à un checksum après une écriture et à 0x00 pour signaler que c'est bien un Controller Pak.
L'adressage en lui-même semble contenir un petit trick permettant de s'assurer que l'adresse est lue sur des bornes précises de 32 octets (tous les 0x0020 adresses et pas en plein milieu !). L'émulateur Mupen64+ effectue les opérations suivantes sur les 2 octets envoyés pour obtenir l'adresse à lire dans la mémoire :
int address = (OCTET1 << 8) | OCTET2; // permet de calculer l'adresse sur 16 bits au lieu de 2 fois 8 bits
address &= 0xFFE0; // masque permettant de supprimer les éventuelles irrégularités dans les lectures
Cela semble d'ailleurs être confirmé par les traces : 02 00 35 provoque une lecture de 32 octets en 0100 sur la carte mémoire. C'est très probablement le rôle du GAL situé sur la carte mémoire entre la SRAM et les pins.
Bref, la suite au prochain numéro avec le fonctionnement de base sur une carte Arduino !