Pour construire un gratte-ciel, il faut des fondations solides. Pour faire des prouesses en émulation, nous allons nous aussi débuter par des choses simples. Ça ne sert à rien d’attaquer la Nintendo DS ou la PSP en premier, nous allons plutôt progresser lentement mais sûrement.
Critère de sélection
Dans tous les domaines où l’on évolue, il est toujours préférable de procéder du plus facile au plus compliqué.
Cette règle reste valable pour l’émulation et une « machine » qui paraît appropriée pour débuter est la Chip 8.
En effet, avec un faible nombre d’instructions (35 pour être précis) et un rendu graphique de 64 × 32 pixels en noir et blanc, la Chip 8 est l’un des meilleurs supports pour débuter en émulation. En plus de sa facilité d’implémentation, un bon nombre de jeux Chip 8 sont dans le domaine public, ce qui limite (supprime même) les problèmes liés à la légalité.
Pourquoi avoir mis « machine » entre guillemets ? La Chip 8 ne serait-elle pas une console conventionnelle ? Qu’est-ce que la Chip 8 en réalité ?
La Chip 8
Définition
La Chip 8 est en réalité un langage interprété qui a été utilisé sur le RCA TELMAC-1800 et le COSMAC VIP en 1977. Elle est constituée d’un ensemble d’instructions qui permettent une programmation facile pour lesdites machines.
Remodelée, la Chip 8 fut utilisée plus tard pour créer des calculatrices graphiques. D’ailleurs, plusieurs jeux ont été développés pour ce système et on ne manquera pas d’en discuter plus tard.
Caractéristiques de la Chip 8
Voici les principales caractéristiques de la Chip 8. Le document est une traduction de la présentation de Wikipédia.
La mémoire
Les adresses mémoire de la Chip 8 vont de 0x200
à 0xFFF
(l’hexadécimal revient), faisant ainsi 3 584 octets. La raison pour laquelle la mémoire commence à partir de 0x200
est que sur le VIP et Cosmac Telmac 1800, les 512 premiers octets sont réservés pour l’interpréteur. Sur ces machines, les 256 octets les plus élevés (0xF00-0xFFF
sur une machine 4K) ont été réservés pour le rafraîchissement de l’écran, et les 96 octets inférieurs (0xEA0-0xEFF
) ont été réservés pour la pile d’appels, à usage interne, et les variables.
Les registres
La Chip 8 comporte 16 registres de 8 bits dont les noms vont de V0
à VF
(F = 15
, encore de l’hexadécimal). Le registre VF
est utilisé pour toutes les retenues lors des calculs.
En plus de ces 16 registres, nous avons le registre d’adresse, nommé I
, qui est de 16 bits et qui est utilisé avec plusieurs opcodes qui impliquent des opérations de mémoire.
La pile
La pile sert uniquement à stocker des adresses de retour lorsque les sous-programmes sont appelés. Les implémentations modernes doivent normalement avoir au moins 16 niveaux.
Les compteurs
La Chip 8 est composée deux compteurs. Ils décomptent tous les deux à 60 hertz, jusqu’à ce qu’ils atteignent 0.
- Minuterie système : cette minuterie est destinée à la synchronisation des événements de jeux. Sa valeur peut être réglée et lue.
- Minuterie sonore : cette minuterie est utilisée pour les effets sonores. Lorsque sa valeur est différente de zéro, un signal sonore est émis. Sa valeur peut être réglée et lue.
Les contrôles
L’entrée est faite avec un clavier qui possède 16 touches allant de 0 à F. Les touches « 8 », « 4 », « 6 » et « 2 » sont généralement utilisées pour l’entrée directionnelle.
Le graphique
La résolution de l’écran est de 64 × 32 pixels, et la couleur est monochrome. Les dessins sont établis à l’écran uniquement par l’intermédiaire de sprites, qui font 8 pixels de large et avec une hauteur qui peut varier de 1 à 15 pixels. Les sprites sont codés en binaire. Pour une valeur de 1, le pixel correspondant est allumé et pour une valeur 0, aucune opération n’est effectuée. Si un pixel d’un sprite est dessiné sur un pixel de l’écran déjà allumé, alors les deux pixels sont éteints. Le registre de retenue (VF) est mis à 1 à cet effet.
Liste des instructions
La Chip 8 possède 35 opcodes, qui sont tous de deux octets de long. Ils sont énumérés ci-dessous, en hexadécimal et avec les symboles suivants :
- NNN : adresse de 12 bits ;
- NN : constante de 8 bits ;
- N : constante de 4 bits ;
- X et Y : identifiant registre de 4 bits.
Rappel : un opcode est la valeur lue à partir de la mémoire.
0NNN (sys NNN
)
Appelle le programme de la RCA 1802 à l’adresse NNN (voir plus bas).
00E0 (cls
)
Efface l’écran.
00EE (ret
)
Retourne à partir d’une sous-fonction.
1NNN (jmp NNN
)
Effectue un saut à l’adresse NNN.
2NNN (call NNN
)
Exécute le sous-programme à l’adresse NNN.
3XNN (skeq VX, NN
)
Saute l’instruction suivante si VX est égal à NN
4XNN (skne VX, NN
)
Saute l’instruction suivante si VX et NN ne sont pas égaux.
5XY0 (skreq VX, VY
)
Saute l’instruction suivante si VX et VY sont égaux.
6XNN (ld VX, NN
)
Définit VX à NN.
7XNN (add VX, NN
)
Ajoute NN à VX (le registre VF n’est pas modifié).
8XY0 (mov VX, VY
)
Définit VX à VY.
8XY1 (or VX, VY
)
Définit VX à VY OR VY.
8XY2 (and VX, VY
)
Définit VX à VX AND VY.
8XY3 (xor VX, VY
)
Définit VX à VX XOR VY.
8XY4 (addr VX, VY
)
Ajoute VY à VX. Le registre VF est mis à 1 quand il y a un dépassement de mémoire (carry) et à 0 quand il n’y en a pas.
8XY5 (sub VX, VY
)
Retire VY à VX. Le registre VF est mis à 0 quand il y a un emprunt, à 1 quand il n’y en a pas.
8XY6 (shr VX
)
Décale (shift) VX à gauche de 1 bit. Le registre VF est fixé à la valeur du bit de poids fort de VX avant décalage.
9XY0 (skrne VX, VY
)
Saute l’instruction suivante si VX et VY ne sont pas égaux.
ANNN (ldi NNN
)
Affecte NNN à I.
BNNN (jmpi NNN
)
Saute à l’adresse NNN + V0.
CXNN (rnd VX, NN
)
Définit VX à NN AND R avec R un nombre tiré au hasard (entre 0 et 255).
DXYN (drw VX, VY, N
)
Dessine un sprite aux coordonnées (VX, VY). Le sprite a une largeur de 8 pixels et une hauteur en pixels N. Chaque rangée de 8 pixels est lue comme codée en binaire à partir de l’emplacement mémoire I. I ne change pas de valeur après l’exécution de cette instruction.
EX9E (skpr VX
)
Saute l’instruction suivante si la touche dont la valeur est stockée dans VX est pressée.
EXA1 (skup VX
)
Saute l’instruction suivante si la touche dont la valeur est stockée dans VX n’est pas pressée.
FX07 (movdt VX
)
Définit VX à la valeur du compteur système.
FX0A (kwait VX
)
Attend l’appui sur une touche, puis stocke la valeur de cette touche dans VX.
FX15 (lddt VX
)
Définit le compteur système à VX.
FX18 (ldst VX
)
Définit le compteur sonore à VX.
FX1E (addi VX
)
Ajoute VX à I. Le registre VF est mis à 1 quand il y a overflow (I + VX > 0xFFF), et à 0 sinon.
FX29 (ldspr VX
)
Définit I à l’emplacement du caractère stocké dans VX. Les caractères 0-F (en hexadécimal) sont représentés par une police 4x5.
FX33 (bcd VX)
Stocke le code décimal représentant VX dans la mémoire (dans I, I + 1, I + 2).
FX55 (stor VX
)
Stocke V0 à VX en mémoire à partir de l’adresse I.
FX65 (read VX
)
Remplit V0 à VX avec les valeurs de la mémoire à partir de l’adresse I.
La RCA 1802
L’instruction 0NNN correspond à « appeler le programme de la RCA 1802 à l’adresse NNN ».
Cette instruction ne nous intéresse pas. En réalité, la RCA 1802 est un microprocesseur 8 bits qui a été utilisé dans certains micro-ordinateurs et consoles de jeu tels que la RCA Studio II. Ce microprocesseur possédait un jeu d’instructions intégrées que les nouvelles implémentations ignorent. Nous allons donc faire de même.
Mais à quoi nous avancent tous ces détails ?
Il ne faut pas paniquer si cette description semble un peu obscure, nous allons expliquer ligne par ligne tout ce qui a été dit ci-dessus. C’est grâce à ce document que nous allons programmer notre émulateur ; nous allons le traduire en langage machine.
Pour créer l’émulateur, nous donnerons des explications par rapport à une citation dans la présentation de la Chip 8 puis le code C pour réaliser cela. Pour bien comprendre, il vaut mieux essayer de créer son propre code à partir des explications, quitte à jeter un coup d’œil au code proposé si besoin.
Nous pouvons maintenant ouvrir un éditeur, dès le prochain chapitre, nous allons commencer à programmer !