Dans ce deuxième article du projet CHIPcon j'explique ce qu'est une machine virtuelle et plus spécifiquement celle du projet.
Mais d'Abord
Avant de débuter le sujet principal voici un photo du montage du prototype de CHIPcon v1.0. et un vidéo de la console en action.
Émulateur, interpréteur et machine virtuelle
Quel est la différence entre ces 3 termes. On utilise le mot émulateur pour désigner un logiciel qui implémente le fonctionnement d'un circuit matériel, par exemple si on veut faire tourner un logiciel pour un MCU particulier mais qu'on a pas le MCU en main on peut le faire tourner à l'intérieur d'un émulateur logiciel qui fonctionne sur le PC. Ainsi l'environnement MPLABX offre des émulateurs pour chaque MCU PIC 8 bits vendu par Microchip. On peut donc tester un logiciel avant de le mettre en mémoire flash du MCU.
On utilise le terme interpréteur pour désigner le compilateur d'un langage de haut-niveau qui au lieu de générer du code binaire pour un CPU génère du code pour une machine virtuelle (bytecode) et exécute ce programme sur cette machine virtuelle. Par exemple Java et Python compile pour une machine virtuelle et non du binaire pour un microprocesseur.
Une machine virtuelle est un logiciel qui exécute du bytecode pour un modèle d'exécution particulier. Cette machine est semblable à un microprocesseur sauf qu'elle n'est pas gravé dans le silicium, d'où le qualificatif de virtuelle.
Par modèle d'exécution on entend l'ensemble des registres, de l'espace mémoire et des instructions que peut exécuter cette machine.
CHIP-8 et SCHIP sont des machines virtuelles qui partagent le modèle d'exécution suivant.
- Une banque de 16 registres de 8 bits notés V0-VF. Le registre VF est utilisé pour sauvegarder l'indicateur de débordement (carry flag).
- Un compteur ordinal pouvant adresser 4096 octets (12 bits)
- Un pointeur de donnée pouvant adresser 4096 octets (12 bits)
- Une minuterie de délais de 8 bits cadencé à 60Hertz.
- Une générateur de tonalité avec compteur de duré de 8 bits cadencé à 60Hertz.
- L'espace d'adressage mémoire est de 4096 octets, mais les programmes à exécuter doivent-être chargés à l'adresse 512 (0x200). Leur taille est donc limité à 3584 octets.
- Le jeux d'instructions de la machine virtuelle est codé sur 16 bits
- Le système possède une police de caractère hexadécimal 3x5 pixels.
- Le système possède une police de caractère décimal de 8x10 pixels.
- CHIP-8 a une capacité graphique de 64x32 pixels (mémoire vidéo 256 octets).
- SCHIP a une capacité graphique de 128x64 pixels (mémoire vidéo de 1024 octets).
- SCHIP a une compatilibilité ascendante avec CHIP-8 sauf pour l'instruction 0NNN (syscall).
- SCHIP ajoute 10 instructions au jeux d'instructions de CHIP-8 qui a 35 instructions.
- SCHIP possède une deuxième banque de 16 registres appellée RPL. Un transfert de données peut-être fait entre les 2 banques.
Jeux d'instructions de SCHIP
Dans la table ci-bas NNN représente un nombre de 12 bits en hexadécimal (000-FFF).
KK représente une constante de 8 bits en hexadécimal (00-FF).
X et Y représentent un registre V en hexadécimal (0-F).
Les instructions suivies d'un * sont spécifiques à SCHIP.
Les instructions suivies de ** sont spécifiques à CHIPcon.
OPCODE | Mnémonique | Description |
---|---|---|
00CN* | SCD N | défile l'affichage vers le bas de N lignes. |
00E0 | CLS | Efface l'affichage. |
00EE | RET | Quitte une sous-routine. |
00FB* | SCR | Défile l'écran vers la droite de 4 pixels. |
00FC* | SCL | Défile l'écran vers la gauche de 4 pixels. |
00FD* | EXIT | Fin de programme, quitte la machine virtuelle. |
00FE* | LOW | Désactive le mode SCHIP, retour au mode CHIP-8 de 64x32 pixels. Instruction ignorée par CHIPcon. |
00FF* | HIGH | Active le mode étendu, 128x64 pixels. Instruction ignorée par CHIPcon qui demeure toujours dans ce mode. |
1NNN | JP NNN | Saute à l'adresse NNN. |
2NNN | CALL NNN | Exécute la sous-routine à l'adresse NNN. |
3XKK | SE VX, KK | Saute l'instruction suivante si VX == KK |
4XKK | SNE VX, KK | Saute l'instruction suivante si VX <> KK |
5XY0 | SE VX, VY | Saute l'instruction suivante si VX == VY |
6XKK | LD VX, KK | VX := KK |
7XKK | ADD VX, KK | VX := VX + KK |
8XY0 | LD VX, VY | VX := VY |
8XY1 | OR VX, VY | VX := VX or VY |
8XY2 | AND VX, VY | VX := VX and VY |
8XY3 | XOR VX, VY | VX := VX xor VY |
8XY4 | ADD VX, VY | VX := VX + VY, VF := carry |
8XY5 | SUB VX, VY | VX := VX - VY, VF := not borrow |
8XY6 | SHR VX | VX := VX shr 1, VF := carry |
8XY7 | SUBN VX, VY | VX := VY - VX, VF := not borrow |
8XYE | SHL VX | VX := VX shl 1, VF := carry |
9XY0 | SNE VX, VY | Saute l'instruction suivante si VX <> VY |
9XY1** | TONE VX, VY | Fais entendre une note de la gamme tempérée. VX note entre 0-F. 0=DO4, F=RÉ5#. VY durée. |
9XY2** | PRT VX, VY | imprime une chaîne texte à l'écran. Les coordonnées du coin supérieur gauche sont indiquées par VX,VY. Le texte est pointé par I. I est incrémenté. |
9XY3** | PIXI VX, VY | Inverse le pixel aux coordonnées indiquées par VX,VY. |
9XY5** | TONE VX, VY, WAIT | Fais entendre une note de la gamme tempérée. VX note entre 0-F. 0=DO4, F=RÉ5#. VY durée. Attend la fin de la note avant de poursuivre. |
ANNN | LD I, NNN | I := NNN |
BNNN | JP V0, NNN | saute à l'adresse NNN+V0 |
CXKK | RND VX, KK | VX := nombre aléatoire and KK |
DXYN* | DRW VX, VY, N | Affiche un sprite de N-octets aux coordonnées d'écran VX, VY. Le contenu du sprite se trouve à l'adresse débutan M(I). VF := 1 si il y a collision. Si N==0 indique un sprite de 16x16 pixels. |
EX9E | SKP VX | Saute l'instruction suivante si la touche dont la valeur est indiquée dans VX est enfoncée. |
EXA1 | SKNP VX | Saute l'instruction suivante si la touche dont la valeur est indiquée dans VX n'est pas enfoncée. |
FX07 | LD VX, DT | VX := valeur de la minuterie délais. |
FX0A | LD VX, K | Attend qu'une touche sois enfoncée et met sa valeur dans VX. |
FX15 | LD DT, VX | minuterie de délais := VX, elle est décrémentée jusqu'à zéro 60 fois par seconde. |
FX18 | LD ST, VX | minuterie son := VX, La minuterie est décrémentée, le son s'arrête lorsqu'elle atteint zéro. |
FX1E | ADD I, VX | I := I + VX |
FX29 | LD F, VX | VX contient une valeur entre 0 et 15. La valeur du registre I est ajusté au début du sprite qui représente ce caractère dans la table 3x5. |
FX30* | LD LF, VX | VX contient une valeur entre 0 et 9. La valeur du registre I est ajusté au début du sprite qui représente ce caractère dans la table 8x10. |
FX33 | LD B, VX | Met à l'adresse M(I)..M(I+2) la valeur BCD du nombre qui est dans VX. |
FX55 | LD [I], VX | Enregistres les valeurs des registres V0..VX dans la mémoire RAM en débutant à l'adresse M(I). |
FX65 | LD VX, [I] | Charge les V0..VX à partir de la mémoire RAM en débutant à l'adresse M(I). |
FX75* | LD R, VX | Sauvegarde les registres V0..VX dans la banque de registres RPL. |
FX85* | LD VX, R | Charge les registres V0..VX à partir des registres RPL. |
Implémentation de la VM SCHIP sur CHIPcon.
Pour suivre cette description référez-vous au dépôt github du projet CHIPcon. tout les fichiers y sont.
Cette machine virtuelle n'ayant que 43 codes opérationnels est simple à implémenter. Les états du modèle d'exécution sauf pour la mémoire RAM sont rassemblés dans une seule variable appellée vms. Le type de donnée qui définie cette structure est déclaré dans le fichier d'entête chip8.h et le code 'C' de la machine est dans le fichier chip8.c.
// structure de donnée définissant le modèle d'exécution de la VM
typedef struct vm_state{
uint16_t pc; // compteur ordinal
uint16_t ix; // pointeur de données
uint8_t sp; // pointeur de la pile
uint8_t var[16]; // banque de registres
uint8_t rpl[16]; // banque de registres SCHIP
union {
uint16_t opcode; // dernier code machine exécuté
struct{
uint8_t b1; // octet fort du code
uint8_t b2; // octet faible du code
};
};
uint16_t stack[32]; // pile des retours
uint8_t src_mem:1 ; // indicateur de source du sprite
uint8_t debug:1; // pour support déboguage
uint8_t trace:1; // pour support déboguage
}vm_state_t;
La fonction uint8_t schip(uint8_t flags) exécute le code qui est enregistré dans la SRAM à partir de l'adresse 0x200. Deux octets de code sont lues pour chaque instruction. Comme le code opérationnel est répartie dans 2 champs séparés, il y a d'abord une phase appelée décodeur d'instruction qui rassemble les 2 champs dans la variable uint16_t code. code est ensuite utilisé par le switch de la phase exécution. Il s'agit d'une organisation classique pour une machine virtuelle. Il n'y a pas de difficulté particulière, une simple lecture du fichier chip8.c devrait permettre de comprendre le fonctionnement de la machine virtuelle.
La VM transige avec l'affichage par les interfaces des modules tvout et text, avec l'unité son par l'interface de celui de tone, avec le clavier via l'interface de keypad et avec la mémoire sram par l'interface du module sram.
liens
CHIPcon partie 1dépot githup du projet.
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8
Aucun commentaire:
Publier un commentaire
Remarque : Seuls les membres de ce blogue sont autorisés à publier des commentaires.