vendredi 31 octobre 2014

CHIPcon partie 7, le boitier

J'ai complété la fabrication du boitier pour CHIPcon. Entièrement fabriqué à partir d'une feuille de plastique noir récupérée d'une horloge pendule et de colle.

Voici un deuxième vidéo pour démontrer l'effet du bouton game speed sur la vitesse d'exécution des jeux. Ici il s'agit du jeux blinky, clone de Packman créé par Hans Christian Egeberg.


liens

CHIPcon partie 1, présentation du projet.
chipcon partie 2, machine virtuelle
CHIPcon partie 3, module SRAM
CHIPcon partie 4, tvout
CHIPcon partie 5, clavier et émulateur PC.
CHIPcon partie 6, interface carte SD
dépot githup du projet. mise à jour le 2014-10-24, utilitaire chip-dasm (schip dé-assembleur).
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8
Page de Roland Riegel concernant sa librairie MMC/SD/SDHC.

mardi 28 octobre 2014

CHIPcon partie 6, interface carte SD

Dans cette partie je documente l'interface avec la carte SD. Pour l'accès à la carte SD j'ai utilisé la librairie créé par Roland Riegel.

Il y a plusieurs librairie SD/SDHC/MMC disponible pour les atMega, j'ai choisie celle de Roland Riegel pour sa facilité d'utilisation. Pour l'intégrer à mon projet j'ai eu très peut de travail à faire. S'il avait fallu que je cré ma propre librairie le temps travail aurait plus que doublé. Chaque module de la librairie a un fichier *_config.h qu'il suffit d'adapter à ses besoins.

Pour commencer dans le fichier hardware.h de mon projet j'ai ajouter la définition suivante:


#define __CHIP_COMP__ 1
J'ai ensuite utiliser cette définition pour contrôler une compilation conditionnelle de certaines variables dans les fichiers sd_raw_config.h et fat_config.h. Je ne détaillerai pas l'interface de cette librairie. Je ne l'ai d'ailleurs pas étudier en détail. J'ai plutôt créé une interface entre cette librairie et CHIPcon en m'inspirant d'un exemple d'application fourni par Roland Riegel. Mon interface comprend les fichiers suivants: filesys.h et filesys.c
  • uint8_t fs_mount()
    Vérifie s'il y a une carte dans le support et si c'est le cas monte le système de fichier. Retourne 1 en cas de succès et 0 autrement. Un seul système de fichier peut-être ouvert à la fois.
  • void fs_umount()
    Ferme le système de fichier.
  • uint8_t fs_open_dir(char *dir_name)
    Ouvre un répertoire. Un seul répertoire peut-être ouvert à la fois. Retourne 1 en cas de succès et 0 autrement.
  • void fs_close_dir()
    Ferme un répertoire.
  • uint8_t fs_open_file(char *file_name)
    Ouvre un fichier. Un seul fichier peut-être ouvert à la fois. Retourne 1 en cas de succès et 0 en cas d'échec.
  • void fs_close_file()
    Ferme le fichier ouvert.
  • uint8_t fs_load_file(uint16_t file_no)
    Charge le contenu du fichier dans la mémoire SRAM. Retourne le nombre d'octets chargés.
  • uint8_t fs_read_dir(struct fat_dir_entry_struct *dir_entry)
    Retourne l'entrée suivante du répertoire. Retourne 0 à la fin de la liste.

CHIPcon n'affiche que la liste des fichiers qui se trouve dans le répertoire racine. Notez qu'il affiche le nom de tous les fichiers. Donc si l'utilisateur sélectionne un fichier qui n'est pas un binaire SCHIP la machine virtuelle schip() va plantée à l'exéctuion d'un tel fichier laissant une message CRASH! BAD OPCODE à l'écran.

La librairie de Roland Riegel est configurée pour utilisation en lecture seule afin de sauver de l'espace code. Tel quel elle consomme environ 5Ko de flash.


liens

CHIPcon partie 1, présentation du projet.
chipcon partie 2, machine virtuelle
CHIPcon partie 3, module SRAM
CHIPcon partie 4, tvout
CHIPcon partie 5, clavier et émulateur PC.
dépot githup du projet. mise à jour le 2014-10-24, utilitaire chip-dasm (schip dé-assembleur).
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8
Page de Roland Riegel concernant sa librairie MMC/SD/SDHC.

dimanche 26 octobre 2014

CHIPcon partie 5, clavier et émulateur PC

Cette partie documente le module keypad constitué des 2 fichiers keypad.h et keypad.h.

Agencement du clavier

La mojorité des jeux CHIP-8/SCHIP que j'ai trouvé dans l'internet ont été écris pour fonctionner dans des émulateurs tournant sous Windows. Donc la disposition du clavier est différente. Certains émulateurs sont configurés en pensant à l'utilisation du pavé numérique d'autres utilisent certaines lettres du pavé alphanumérique. Par exemple l'émulateur Fish'n chip utilise les touches à gauche du clavier alphanumérique. La correspondance avec le clavier COSMAC VIP est représenté en caractères gras.

clavier QWERTY
1 12 23 34 C
Q 4W 5E 6R D
A 7S 8D 9F E
Z AX 0C BV F

CHIPcon utilise un clavier Grayhill 96BB2-056-R représenté ici à côté du clavier COSCMAC VIP. On voie donc que la disposition est différente. Pour CHIPcon [*] correspond à [E] et [#] correspond à [F]. Donc lorsqu'on télécharge un jeux pour l'utiliser avec CHIPcon on doit tenir compte de ces différences.

interface keypad

  • void keypad_init()
    Initialisation matérielle du clavier.
  • uint8_t keypad_read()
    Lecture d'une touche avec anti-rebond.
  • uint8_t wait_key()
    Attend qu'une touche soit enfoncée et retourne sa valeur. Lecture avec anti-rebond.
  • void prompt_key()
    Affiche le message "any key..." et attend une touche.
  • uint8_t keypad_break()
    Vérifie si les touches [*] et [#] sont enfoncées en même temps. Retourne vrai si c'est le cas. Utilisé pour sortir de la fonction schip() lorsqu'un programme est bloqué dans une bouche infinie.
  • uint8_t key_down(uint8_t key)
    Vérifie si la touche key est enfoncée. Il n'y a pas d'anti-rebond, le programme SCHIP doit gérer lui-même l'anti-rebond.

Outils de développement

Dans le répertoire tools du projets il y a 2 utilitaires.

cvt-chip est utilisé pour convertir un fichier binaire SCHIP en fichiers source *.c et *.h dans le but d'intégrer un jeux dans la mémoire flash de CHIPcon. Voici un exemple pour sokoban.sc.

commande pour convertir le fichier binaire.


cvt-chip.exe sokoban.sc
Gènère les fichiers sokoban.h

sokoban.c

Le texte


"Sokoban by Hap\n"
"keys:\n"
"5 up\n"
"8 down\n"
"7 left\n"
"9 right\n"
"A select\n"
Doit-être ajouter manuellement. Ce texte défile à l'écran avant le démarrage du jeux.

Un autre utilitaire est chip-dasm qui est un dé-assembleur. A partir d'un fichier binaire SCHIP il génère un fichier lisible par l'humain ou chaque instruction est représenté par un mnémonique. Voici ce que ça donne avec le fichier field.ch8

D'autres utilitaires sont disponibles en téléchargement sur http://www.chip.com. En autre MegaCHIP devkit 1.0. Ainsi que l'émulateur fish'n chip


liens

CHIPcon partie 1, présentation du projet.
chipcon partie 2, machine virtuelle
CHIPcon partie 3, module SRAM
CHIPcon partie 4, tvout
dépot githup du projet. mise à jour le 2014-10-24, utilitaire chip-dasm (schip dé-assembleur).
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8

samedi 25 octobre 2014

CHIPcon partie 4, tvout

Dans cet article je décris le fonctionnement du module tvout du projet CHIPcon.

tvout

Le module tvout est constitué des fichiers tvout.h et tvout.c. Ce module est responsable de la génération du signal NTSC et comprends les fonctions d'interface suivantes:

  • void tvout_init()
    Initialisation du module.
  • void plot(int8_t x, int8_t y, optype op)
    Desssine un point à la position x,y. optype peut prendre une des valeurs suivantes: WHITE, BLACK, INVERT.
  • int8_t put_sprite(uint8_t x, uint8_t y, uint8_t n, const uint8_t *sprite, int8_t memory)
    Desssine un sprite. x,y sont les coordonnées du coin supérieur gauche. Le sprite a 8 pixels de large par n pixels en hauteur. memory indique si le sprite est en mémoire flash (FLASH_MEM) ou en RAM (RAM_MEM).
  • int8_t put_big_sprite(uint8_t x, uint8_t y,const uint8_t *sprite)
    Dessine un sprite de 16x16 pixels. Les coordonnées sont le coin supérieur gauche.
  • void cls()
    Efface l'écran.
  • void scroll_down(uint8_t lines)
    Défile l'écran vers le bas d'un nombre de lignes entre 0 et 31.
  • void scroll_up(uint8_t lines)
    Défine l'écran vers le bas d'un nombre de lignes entre 0 et 31.
  • void scroll_right(uint8_t pixels)
    Défile l'écran vers la droite d'un nombre de pixels entre 0 et 8.
  • void chip_scroll_right()
    Défile l'écran vers la droite de 4 pixels. Optimisé pour l'engin schip()
  • void scroll_left(uint8_t pixels)
    Défile l'écran vers la gauche d'un nombre de pixels entre 0 et 8.
  • void chip_scroll_left()
    Défile l'écran vers la gauche de 4 pixels. Optimisé pour l'engin shcip().
  • void screen_save()
    Sauvegarde l'écran dans la SRAM.
  • void screen_restore()
    Restaurate l'écran à partir de la SRAM.

Génération du signal NTSC

La façon la plus efface de générer un signal NTSC sur un MCU en utilisant le moins de cycles CPU possible est d'utiliser une minuterie avec un output compare en mode PWM pour le signal de synchroniation. Pour sérialiser les pixels contenu dans le video_buffer un périphérique SPI fait l'affaire. l'atMega328P permet de configuré le module USART en mode SPI c'est donc de cette façon que j'ai procédé car le module SPI lui-même est utiliser pour interfacer la carte SD et la SRAM.

Il n'y a qu'une seule interruption qui se produit lorsque le compteur du TIMER1 atteint la valeur du registre OCR1B. Ce qui se produit à la fin de l'impulsion de synchronisation. Il y a donc une interruption à chaque ligne horizontale soit à toute les 63,5µsec.

Cette interruption fait fonction de céduleur de tâche. La tâche à exécuter est déterminée par le numéro ligne.


liens

CHIPcon partie 1, présentation du projet.
chipcon partie 2, machine virtuelle
CHIPcon partie 3, module SRAM
dépot githup du projet. mise à jour le 2014-10-24, utilitaire chip-dasm (schip dé-assembleur).
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8

vendredi 24 octobre 2014

CHIPcon partie 3, module SRAM

Dans cet article je discute de l'utilisation de la mémoire RAM à interface SPI Microchip 23LC512

Cette mémoire RAM à interface SPI est vraiment pratique puisqu'elle ne requiert que 4 GPIO, SCK, MISO, MOSI et SELECT. Et comme l'atMega328P possède un périphérique SPI l'utilisation en est très simple. C'est le même périphérique SPI qui est utilisé pour la carte SD chacun ayant un SELECT différent.

Le 23LC512-IP possède 64Ko de RAM, j'aurais pu utiliser un 23K640-IP (8Ko) pour un coût moindre, mais j'ai utilisé ce que j'avais en main. Le 23LC512 a 3 modes de fonctionnement, BYTE, PAGE et SEQUENTIAL. Pour cette application il est configuré en mode séquentiel. Ce mode permet de lire ou d'écrire un bloc de mémoire de n'importe quel dimension avec une seule commande, alors qu'en mode BYTE on ne peut lire ou écrire qu'un octet par commande. En mode PAGE, 32 octets peuvent-être lus ou écris et ces pages sont alignées modulo 32. Donc si on envoie la commande READ 0x0003 et qu'on lit 32 octets, les octets lus seront aux adresses 3-31 et 0-2 et non 3-34. Pas de problème de cette sorte avec le mode SEQUENTIAL.

Évidemment on ne peut stocker de code binaire dans cette SRAM sauf si on l'utilise avec des MCU à architecture Von Neumann comme les MSP430 par exemple. Dans ce cas on pourrait charger des routines de la SRAM vers la RAM du MCU pour les exécuter. Quoi que pour une telle application une mémoire EEPROM ou flash SPI serait plus utile. Dans le cas d'un atMega328P on ne peut utiliser cette méthode mais c'est néanmoins utile comme espace de stockage de données temporaire. Dans l'application CHIPcon la SRAM est utiliser de 3 façons.

  1. stockage de la liste des noms de fichiers jeux pour utilisation par la routine display_page() du fichier chipcon.c
  2. tvout utilise la SRAM pour sauvegarder un écran temporairement afin d'afficher les informations de débogage pendant le mode TRACE. Voir les routines screen_save() et screen_restore() dans tvout.c ainsi que la routine print_vms() dans chip8.c.
  3. La mémoire SRAM contient le programme SCHIP à exécuter. Le programme est chargé à l'adresse 512 (0x200).

Pour le détail de l'implémentation voir les fichiers sram.h et sram.c.


liens

CHIPcon partie 1
chipcon partie 2, machine virtuelle
dépot githup du projet.
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8

jeudi 23 octobre 2014

CHIPcon partie 2, machine virtuelle

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.
Si on fabriquait cette machine dans du silicium son diagramme bloc ressemblerais à ceci.

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.

OPCODEMnémoniqueDescription
00CN*SCD Ndéfile l'affichage vers le bas de N lignes.
00E0CLSEfface l'affichage.
00EERETQuitte une sous-routine.
00FB*SCRDéfile l'écran vers la droite de 4 pixels.
00FC*SCLDéfile l'écran vers la gauche de 4 pixels.
00FD*EXITFin de programme, quitte la machine virtuelle.
00FE*LOWDésactive le mode SCHIP, retour au mode CHIP-8 de 64x32 pixels. Instruction ignorée par CHIPcon.
00FF*HIGHActive le mode étendu, 128x64 pixels. Instruction ignorée par CHIPcon qui demeure toujours dans ce mode.
1NNNJP NNNSaute à l'adresse NNN.
2NNNCALL NNNExécute la sous-routine à l'adresse NNN.
3XKKSE VX, KKSaute l'instruction suivante si VX == KK
4XKKSNE VX, KKSaute l'instruction suivante si VX <> KK
5XY0SE VX, VYSaute l'instruction suivante si VX == VY
6XKKLD VX, KKVX := KK
7XKKADD VX, KKVX := VX + KK
8XY0LD VX, VYVX := VY
8XY1OR VX, VYVX := VX or VY
8XY2AND VX, VYVX := VX and VY
8XY3XOR VX, VYVX := VX xor VY
8XY4ADD VX, VYVX := VX + VY, VF := carry
8XY5SUB VX, VYVX := VX - VY, VF := not borrow
8XY6SHR VXVX := VX shr 1, VF := carry
8XY7SUBN VX, VYVX := VY - VX, VF := not borrow
8XYESHL VXVX := VX shl 1, VF := carry
9XY0SNE VX, VYSaute l'instruction suivante si VX <> VY
9XY1**TONE VX, VYFais 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, VYimprime 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, VYInverse le pixel aux coordonnées indiquées par VX,VY.
9XY5**TONE VX, VY, WAITFais 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.
ANNNLD I, NNNI := NNN
BNNNJP V0, NNNsaute à l'adresse NNN+V0
CXKKRND VX, KKVX := nombre aléatoire and KK
DXYN*DRW VX, VY, NAffiche 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.
EX9ESKP VXSaute l'instruction suivante si la touche dont la valeur est indiquée dans VX est enfoncée.
EXA1SKNP VXSaute l'instruction suivante si la touche dont la valeur est indiquée dans VX n'est pas enfoncée.
FX07LD VX, DTVX := valeur de la minuterie délais.
FX0ALD VX, KAttend qu'une touche sois enfoncée et met sa valeur dans VX.
FX15LD DT, VXminuterie de délais := VX, elle est décrémentée jusqu'à zéro 60 fois par seconde.
FX18LD ST, VXminuterie son := VX, La minuterie est décrémentée, le son s'arrête lorsqu'elle atteint zéro.
FX1EADD I, VXI := I + VX
FX29LD F, VXVX 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, VXVX 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.
FX33LD B, VXMet à l'adresse M(I)..M(I+2) la valeur BCD du nombre qui est dans VX.
FX55LD [I], VXEnregistres les valeurs des registres V0..VX dans la mémoire RAM en débutant à l'adresse M(I).
FX65LD VX, [I]Charge les V0..VX à partir de la mémoire RAM en débutant à l'adresse M(I).
FX75*LD R, VXSauvegarde les registres V0..VX dans la banque de registres RPL.
FX85*LD VX, RCharge 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 1
dépot githup du projet.
page www.chip.com rassemblant beauceaup d'information sur CHIP-8/SCHIP/MegaCHIP
article de wikipedia sur CHIP-8

mardi 21 octobre 2014

CHIPcon partie 1, présentation

CHIPcon est une console de jeux peut coûteuse et facile à assembler soi-même, pour les jeux écris en super CHIP. Dans cette première partie je fais un rappel historique du langage CHIP pour ensuite présenter le schéma électronique de la console. Le circuit est basé sur un atMega328P-PU avec une sortie NTSC monochrome.

Présentation historique

  • En 1970 l'ingénieur Joseph Weisbecker (1932-1990) conçoit un processeur 8 bits à architecture Von Neumann RISC.
  • Au début de 1976 la compagnie RCA met sur le marché un microprocesseur nommé CDP-1802. Ce microprocesseur est en technologie CMOS. Son architecture est basé sur le processseur défini par Joseph Weisbecker. Il contient 16 registres de 16 bits d'usage général et 4 registres de 4 bits P,N,I,X. Et un registre de 8 bits T. Les registres de 16 bits peuvent-être divisés en 2 registres de 8 bits, on a donc 32 registres de 8 bits. Il n'y a pas de compteur ordinal dédié, n'importe quel des 16 registres de 16 bits peut-être sélectionné pour cette fonction. C'est la valeur de P qui détermine lequel est le compteur ordinal. Un appel de sous-routine se fait en initialisant un registre avec l'adresse de la sous-routine pour ensuite modifier le registre P pour définir ce registre comme compteur ordinal. Lorsque la sous-routine a terminée son travail elle réinitialise P avec la valeur originale pour revenir au programme appelant. Ce microprocesseur a été utilisé dans plusieurs sondes spatiales dont la sonde Galileo. Ce microprocesseur a un surnom COSMAC (Co mplementary S ymmetry M onolithic A rray C omputer)
  • Dans l'édition de août 1976 de Popular electronics, Joseph Weisbecker présente le COSMAC ELF. Petit ordinateur disponible en kit à faible coût. Ces kits étaient vendu par Netronics et Quest Electronics.
    • Processeur CDP-1802
    • RAM statique de 256 octets
    • 11 commutateurs à bascule et 1 à pression pour la saisie et l'exécution du code.
    • 1 affichage 7 segments à 2 digits
    • 1 LED contrôlé par la sortie Q.
    • fréquence maximale du cristal 3.2Mhz
  • Juillet 1977, Weisbecker ajoute un générateur graphique CDP-1861 au ELF. Ce dernier génère un signal monochrome de 64x128pixels.
  • En 1977 RCA entre sur le marché des micro-ordinateurs avec le COSMAC VIP (photo). Avec 2Ko de RAM en kit de base, celle-ci pouvait être augmenté à 4Ko sur la carte principale.
    • CDP-1861/CDP-1864 pour sortie NTSC monochrome.
    • 2Ko RAM en base, 4Ko possible sur carte mère.
    • Clavier hexadécimal
    • Ajout possible de 2 connecteurs pour cartes d'options.
    • Fréquence d'oscillateur 1,76Mhz.
  • Dans l'édition de décembre 1978 de BYTE, Joseph Weisbecker présente CHIP-8, un système pour simplifier la programmation de jeux sur le COSMAC VIP. Il s'agit d'un interpréteur qui exécute du bytecode. Les instructions sont encodées sur 16 bits. L'affichage est de 64x32 pixels.
  • 1990 Erik Bryntse cré un descendant de CHIP-8 baptisé SCHIP (Super CHIP). L'interpréteur est conçu pour fonctionner sur une calculatrice HP-48 et l'affichage est de 128x64 pixels. SCHIP reconnait les programmes CHIP-8 sauf pour l'instruction 0xxx qui est un appel de sous-routine en code machine, non pertinent sur la HP-48.
  • Plusieurs émulateurs CHIP-8/SCHIP/MégaCHIP ont été écris pour fonctionner sur PC. Il existe aussi un assembleur qui permet de programmer en mnémoniques plutôt qu'en code hexadécimal. Voir le site de David Winter chip.com

CHIPcon

CHIPcon est donc une console de jeux rétro qui implémente un interpréteur SCHIP sur un microcontrôleur atMega328P-PU. Cette console permet de charger et jouer tout fichier binaire en bytecode SCHIP. J'en ai trouvé plusieurs sur le site de David Winter, www.chip8.com

Caractéristique de la console

  • microcontrôleur atMega328P-PU format DIP-28.
  • expansion RAM SPI de 64Ko Microchhip 23LC512.
  • Support carte SD (pilotes écris par Roland Riegel).
  • Fréquence oscillateur 16Mhz.
  • graphiques 128x64pixels, sortie vidéo NTSC monochrome.
  • Sortie son monofréquence sur prise RCA PHONO, ou sur piezo speaker.
  • Quelques jeux en mémoire programme du MCU lorsqu'il qu'il n'y a pas de carte SD dans le support.
  • Interface Humain-machine par clavier hexadécimal comme le COSMAC VIP.
  • Potientiomètre pour contrôler la vitesse d'exécution de l'interpréteur SCHIP.

Le montage permanent n'étant pas complété voici une photo du montage sur carte sans soudure.

Schématique

Liste matériel

  • Z1    PIEZO speaker (option 2)
  • C1    electrolytic 100µF/16V
  • C2,C3    18pF ceramic NPO
  • C6,C7,C8,C9,C10    100nF ceramic (CMS)
  • C5,C10    1µF ceramic (CMS)
  • C11    10µF/16V electrolytic
  • CON1    2.1 mm BARREL_JACK
  • D1    diode 1N4148
  • D2    LED 5mm/20ma
  • J1,J2    RCA phono jack
  • k1    KEYPAD 16 touches, Grayhill 96BB2-056-R
  • P1    réceptacle SD card
  • P2    connecteur 2x3 100mil mâle
  • R1    4k7
  • R2    150R
  • R3    620R
  • R4    470R
  • R5,R6,R7,R8    10K
  • R9    10R
  • R10    100R
  • RV1    10K potentiomètre linéaire
  • SW1    bouton momentanné N.O.
  • SW2    commutateur alimentaion S.P.S.T
  • U1    MCU ATMEGA328P-PU
  • U2    SRAM 23LC512
  • U3    LDO LT1117-3,3
  • X1    cristal 16Mhz (TXC 9B-16.000MEEJ-B)
  • 8015-1 Circbord    carte à points Vector pour montage final. 10cm x 10cm

Le modèle mémoire des programmes CHIP-8/SCHIP est de 4Ko et les 512 premiers octets étaient réservés pour le système sur le COSMAC VIP. Donc le binaire du jeux est chargée en mémoire à l'adresse 512 (0x200) et a une taille maximale de 3584 octets. Le 328P-PU n'a que 2Ko de RAM dont 1Ko est occupé par la mémoire vidéo. Ce n'est donc pas suffisant pour SCHIP. Microchip vend des mémoire RAM statique à interface SPI. Le 23LC512 contient 64Ko de RAM. Les jeux sont chargée dans cette SRAM à l'adresse 0x200 et exécutés à partir de cette mémoire externe. Le vidéo NTSC est généré par 2 périphériques. La minuterie TMR1 de 16 bits génère le signal de synchronisation par PWM et le USART configuré en mode SPI est utilisé pour sérialiser les pixels vidéo à partir du video_buffer situé dans la RAM du atMega328P-PU. Le périphérique SPI principal est utilisé pour communiquer avec la SRAM externe et la carte SD.

Au départ je me demandais si l'utilisation de la SRAM externe comme mémoire d'exécution pour l'interpréteur SCHIP serait suffisamment rapide, la vitesse du clock SPI maximale étant de Fosc/2 soit 8Mhz. Il s'est avéré à l'usage qu'en fait pour certains jeux l'interpréteur était trop rapide. Il ne faut pas oublier que le COSMAC VIP original fonctionnait à 1,76Mhz et que le CPU CDP-1802 utilise 16 cycles d'oscillateur par instruction, donc un maximum de 110000 instructions par seconde sur le COSMAC VIP. Comme CHIP-8 est un interpréteur exécutant plusieurs instructions 1802 par bytecode les programmes CHIP-8 étaient encore plus lent que ça. L'atMega328P lui fonctionne à 16Mhz et la majorité des instructions s'exécute en 1 seul clock. Donc même si l'interpréteur nécessite le transfert de 5 octets sur l'interface SPI pour lire une seule instruction on est encore à 8Mhz/8/5=200000 instructions lues par seconde. Bien sur le nombre d'instructions par secondes est beaucoup plus bas que ça car une bonne partie du temps CPU est utilisé par le générateur vidéo. Quoi qu'il en soit j'ai du ajouter un potentiomètre au circuit dont la lecture est faite par le périphérique ADC 10 bits sur le canal 1 (broche 24). La valeur lue sur le potentiomètre détermine le nombre d'instructions que l'interpréteur exécute par intervalle vertical sync (16,7 msec).

Pour la sortie audio il y a 2 options. La première option consiste à sortir le signal sur un connecteur RCA phono et utiliser le téléviseur comme amplificateur audio. La deuxième option est d'utiliser simplement un petit haut-parleur piézo monté sur la carte du circuit.

Le support pour carte SD est optionnel si on ne l'utilise pas l'utilisation de la mémoire flash de l'atMega328P-PU tombe à 50% environ ce qui libère plus d'espace pour y mettre des jeux.

Avec cette option installée au démarrage le firmware vérifie la présence d'une carte SD dans le support. s'il y a une carte il lit le répertoire racine et affiche à l'écran la liste des fichiers jeux disponibles. l'utilisateur sélectionne le jeux désiré et celui-ci est alors transféré de la carte vers la SRAM et exécuté.

S'il n'y pas de carte dans le support le firmware affiche la liste des jeux enregistrés dans la flash du MCU. Le jeux sélectionné est transféré de la flash du MCU à la SRAM pour exécution.

Dans le prochain article je vais commencer la présentation du firmware en commençant par l'interpréteur super CHIP. D'ici là j'aurai sans doute le temps de compléter le montage sur la carte à points et peut-être filmer un vidéo de démonstration.


liens

  • Article CHIP-8 sur wikipedia anglais et français.
  • Page de David Winter beaucoup d'information disponible sur cette page et des jeux à télécharger.
  • Page consacré au COSMAC ELF, histoire, archives logicielles et photos.
  • Page consacré à CHIP-8 sur revival studio.
  • Vous pouvez télécharger à partir de cette page l'émulateur pour PC Fish'n chip. je l'ai testé et il est fonctionnel sur windows 7.

mercredi 8 octobre 2014

Belle astuce

Je suis toujours en train de travailler sur un projet utilisant un atMega328 et je regarde régulièrement le code assembleur généré par le compilateur. Aujourd'hui je suis tombé sur un morceau de code assembleur qui m'a laissé perplexe. A prime abord ça ne semblais rimé à rien! En le relisant avec attention j'ai fini par comprendre, voici le code assembleur en question tel que je l'ai commenté. Une variable de 8 bits est chargée dans le registre R24. R24 est additionnée à lui-même, puis mis à zéro par l'instruction eor r24, r24. Ensuite on additionne encore R24 à lui-même, avant d'enregistrer le résultat dans une autre variable. Si on ne porte très attention à ce qui ce passe c'est plutôt étonnant, mais en fait c'est une belle astuce pour faire ceci:


// code C original
  vms.var[15]=(vms.var[x]&128)>>7;
Donc R24 est additionné avec lui-même avec l'instruction adc r24, r24. Si le bit 7 de r24 était à 1 avant cette opération le Carry bit sera à 1 après l'addition. On met r24 à zéro et ensuite on additionne r24+r24+Carry. Donc si le carry est à 1 r24 = 1 sinon il égal 0. C'est ce qu'on voulais avoir dans vms.var[15], c'est à dire l'état du bit 7 de vms.var[x].

Beaucoup de travail d'optimisation a été fait sur ce compilateur. Bravo! à ces créateurs.

lundi 6 octobre 2014

Cherchez l'erreur

Il était tard et j'avais travaillé toute la journée sur ce projet. Après avoir écris la routine ISR suivante et l'avoir compilée j'ai examiné immédiatement le code assembleur généré pour en estimer le temps d'exécution.

code source
code assembleur généré
Première réaction: C'est court parfait! Non en fait ce n'est pas parfais du tout. Rapidement je constate que le compilateur a laissé tomber une bonne partie du code source! Le code résultant aurais du être ceci:
code assembleur généré, une fois mon erreur corrigée.
Lorsqu'un compilateur laisse tomber du code source c'est pour optimiser. Ici tout le bloc if a été supprimé dans la première version du code. Je regardais mon code source pourtant si simple sans voir mon erreur. Je me suis dis qu'il était tard et que c'était la fatigue qui me rendais aveugle. J'ai donc fermer l'ordinateur en remettant ça au lendemain.

Je m'appraîtais à me mettre au lit lorsque j'ai compris mon erreur. Je vous laisse regarder le code source et trouver par vous même où est l'erreur.

samedi 4 octobre 2014

GCC, optimisation et portabilité

Je travaille présentement sur un projet utilisant un atMega328 avec Atmel studio 6. Dans cet article je discute des différents niveaux d'optimisation utilisés par GCC (GNU C Compiler) ainsi que des problèmes de portabilité causé par la taille variable du type de donnée int.

Optimisation

Atmel studio comme MPLABX utilise GCC comme compilateur et par défaut lorsqu'on cré un projet le niveau d'optimisation est -Os. À ce niveau GCC tente de créer le code le plus compacte possible. Comme les microcontrôleurs sont en général limités en mémoire, c'est logique d'utiliser cette optimisation. Donc lors de la première compilation de mon projet j'ai obtenu un code très compact. Malheureusement ça ne fonctionnais pas. Le projet utilise une routine de service d'interruption qui doit avoir un délais de réponse rapide (low latency), mais le résultat n'était pas à la hauteur des besoins. Après avoir examiner le code assembleur (fichier *.lss dans output files du projet), j'ai été sidéré de voir la quantité de code utilisé en préambule par la routine. C'est là que j'ai décidé de changer le niveau d'optimisation pour -O3 dans les propriétés du projet. Là ça fonctionnais mais la taille du code avait grossis plus que je ne l'aurais cru!

Voici la taille du code généré pour mon projet (non complété) selon les différents niveaux d'optimisation:

  • -O0 Ne compile pas! affiche l'avertissement suivant:
    Warning 8 #warning "Compiler optimizations disabled; functions from <util/delay.h> won't work as designed"
  • -O1
    Program Memory Usage : 4652 bytes 14,2 % Full
    Data Memory Usage : 1604 bytes 78,3 % Full
  • -O2
    Program Memory Usage : 4728 bytes 14,4 % Full
    Data Memory Usage : 1604 bytes 78,3 % Full
  • -O3
    Program Memory Usage : 5514 bytes 16,8 % Full
    Data Memory Usage : 1604 bytes 78,3 % Full
  • -Os
    Program Memory Usage : 4434 bytes 13,5 % Full
    Data Memory Usage : 1604 bytes 78,3 % Full

Donc lorsqu'on veut optimiser pour la taille on utilise -Os. Mais si on veut optimiser pour la vitesse on utilise -O1,-O2 ou -O3. Chacun prenant plus d'espace de code. Cette augmentation du code est du au fait que pour augmenter la vitesse le compilateur transforme certaines sous-routines en inline même si on ne l'a pas spécifié et aussi il peut dérouler certaines boucles simple avec petit nombre de répétitions.

Au final j'ai décidé de compiler mon projet en -O2, à ce niveau le délais de l'ISR étant suffisamment cours.

Portabilité du code

Comme je ne veut pas avoir à réécrire le même code chaque fois que je change de microcontrôleur je réutilise des fichiers d'autres projets. C'était le cas pour ce projet. Mais lorsque j'ai adapter le code j'ai eu quelques problèmes du au fait que le type de donnée int peut varié de taille d'une plateforme à l'autre. Ainsi sur PIC32MX un int est 32 bits mais sur un AVR il est de 16 bits.

Pour rendre un code source 'C' plus portable il est préférable de préciser la taille des entiers utilisés et dans ce but d'inclure #include <stdint.h> dans le projet. Ce fichier contient des définitions de différentes tailles d'entiers tel que:

  • int8_t entier sur 8 bits et sa version non signé uint8_t.
  • int16_t entier sur 16 bits et sa version non signé uint16_t.
  • int32_t entier sur 32 bits et sa version non signé uint32_t.
En utilisant ces types d'entiers au lieu de simplement utiliser int on rend le code plus facile à porter d'une plateforme à l'autre. Autre avantage c'est qu'on peut sauver de l'espace RAM. En effet si je sais que la valeur de ma variable ne dépassera pas 255 je peut utilisé uint8_t au lieu de unsigned int qui va occupé 2 octets de RAM. Et de plus comme les AVR sont des MCU 8 bits les opérations sur les int demandes plus d'instructions. Donc sur un MCU 8 bits utilisé int8_t ou uint8_t lorsque le domaine de la variable le permet, sauve de l'espace RAM, de l'espace de code et augmente la rapidité des opérations sur cette variable.