J’ai un systĂšme basĂ© sur le 6502 que je peux programmer librement. L’objectif maintenant est d’ajouter des LEDs et des boutons pour pouvoir Ă©crire des programmes interactifs.

Le rĂŽle du 6522

Le 6502 seul n’est pas en mesure de piloter directement des LEDs et des boutons, car les seules choses qu’il peut faire directement c’est lire et Ă©crire sur les bus d’adresse et de donnĂ©es. Pour piloter une LED ou lire un bouton il faut un systĂšme intermĂ©diaire qui s’interface avec la LED ou le bouton d’un cĂŽtĂ©, et communique avec le CPU via le bus de l’autre cĂŽtĂ©.

Le 6522 est une puce d’interfaçage conçue pour fonctionner avec le 6502. Son nom en anglais est Versatile Interface Adatper, je l’appelle donc “VIA” pour faire court. Le VIA propose plusieurs fonctions complĂ©mentaires, notamment des timers, mais ce qui nous intĂ©resse ici ce sont les deux ports parallĂšles d’entrĂ©e-sortie. Il s’agit de 16 lignes qui peuvent ĂȘtre configurĂ©es individuellement en entrĂ©e ou en sortie, et qu’on va pouvoir lire ou Ă©crire depuis le CPU.

Le VIA possĂšde 16 registres de contrĂŽle dans lesquel le CPU va lire ou Ă©crire via le bus de donnĂ©es pour rĂ©aliser les fonctions souhaitĂ©es. Pour sĂ©lectionner le registre dans lequel on veut lire ou Ă©crire, on utilise le bus d’adresse. Comme on a que 16 registres, 4 lignes d’adresse suffisent.

Liste des registres du 6522 (extrait de la datasheet)

Chip Select et espace d’adressage

Jusqu’à maintenant on avait qu’un seul composant connectĂ© aux bus d’adresse et de donnĂ©es : l’EEPROM. On va maintenant ajouter le VIA, et il faut donc s’assurer que le CPU puisse communiquer avec chaque composant sans perturber les autres ou ĂȘtre perturbĂ© par leur prĂ©sence sur le bus. Pour ça on utilise le Chip Select (aussi appelĂ© Chip Enabled). C’est un signal qui permet d’activer le composant avec lequel le CPU cherche Ă  communiquer Ă  un instant donnĂ©. Chaque composant possĂšde une ou plusieurs entrĂ©e pour ce signal, et ne prennent le contrĂŽle du bus que lorsque le signal les y autorise. Le reste du temps les pins connectĂ©es au bus sont Ă©lectroniquement dĂ©connectĂ©es du bus (dans ce qu’on appelle un Ă©tat de haute impĂ©dance).

Pour dĂ©terminer quel composant doit ĂȘtre actif on utilise le bus d’adresses. Avec 16 lignes d’adresse le CPU peut produire 65 536 adresses diffĂ©rentes. Ces 65 536 adresses constituent ce qu’on appelle “l’espace d’adressage”, et l’idĂ©e est de dĂ©couper cet espace d’adressage en plusieurs plages et d’attribuer une plage Ă  chaque composant. Pour ça on utilise des lignes d’adresse judicieusement choisies pour gĂ©nĂ©rer le signal Chip Select pour chaque composant.

Jusqu’à maintenant j’avais forcĂ© le Chip Select (signal /CE) de l’EEPROM pour qu’elle soit toujours active. L’intĂ©gralitĂ© de l’espace d’adressage lui Ă©tait par consĂ©quent attribuĂ©e. La ROM possĂšde seulement 15 lignes d’adresse (32k adressable), et on avait laissĂ© la 16e ligne dĂ©connectĂ©e. Par consĂ©quent quand le CPU utilise une adresse dans la plage 0x0000-0x7FFF, ça correspond directement Ă  une adresse dans la ROM. Mais c’est Ă©galement le cas pour les adresses 0x8000-0xFFFF car si on ne prend en compte que les 15 premiers bits de l’adresses, les adresses sont identiques. Du point de vue du CPU le contenu de la ROM apparait dupliquĂ©.

Binaire Hexadécimal
0000 0000 0000 0000 0x0000
0111 1111 1111 1111 0x7FFF
1000 0000 0000 0000 0x8000
1111 1111 1111 1111 0xFFFF

La 16e ligne d’adresse (A15) Ă©tant inutilisĂ©e pour adresser les donnĂ©es dans la ROM, on peut l’utiliser pour le Chip Select. Si on la connecte directement sur l’entrĂ©e /CE de la ROM, Ă©tant donnĂ© que la puce est activĂ©e si le signal est Ă  0, celle-ci ne rĂ©pondra plus qu’aux adresse dans la plage 0x0000-0x7FFF. On a ainsi allouĂ© la plage 0x0000-0x7FFF Ă  l’EEPROM et libĂ©rĂ© le reste pour d’autres composants. Cependant, rappelons-nous qu’au dĂ©marrage le CPU interroge les adresses 0xFFFC et 0xFFFD (vecteur reset), et dans cette configuration de la mĂ©moire ça ne fonctionnerait pas. On pourrait rajouter un composant dont la tĂąche est de rĂ©pondre spĂ©cifiquement Ă  ces deux adresses, mais on devrait alors veiller Ă  mettre Ă  jour la valeur en fonction d’oĂč on place le dĂ©but du programme dans la ROM. Le plus simple est en fait d’allouer la plage 0x8000-0xFFFF Ă  la ROM. On peut faire ça en inversant A15 (avec une porte logique NOT) avant de le connecter sur /CE. Ainsi l’adresse 0x7FFC dans la ROM correspondra Ă  l’adresse 0xFFFC du point de vue du CPU. En revanche, l’adresse 0x0000 de la ROM se retrouve Ă  l’adresse 0x8000 du point de vue du CPU. Il faudra donc ĂȘtre vigilant lors de l’écriture du programme.

On peut maintenant rĂ©flĂ©chir Ă  l’espace mĂ©moire Ă  allouer au VIA. On sait dĂ©jĂ  que cette plage doit ĂȘtre comprise dans la plage 0x0000-0x7FFF. On pourrait allouer toute la plage, et tant qu’on ajoute pas d’autres composants sur le bus ça ne pose pas de problĂšme. Mais je prĂ©vois d’ajouter au moins une puce de RAM par la suite, donc essayons de faire mieux que ça. Pour rappel le VIA n’a que 16 registres, la plage d’adresse n’a donc besoin de contenir au minimum que 16 adresses. Elle peut Ă©videmment ĂȘtre plus grande que ça.

Je commence Ă  ĂȘtre Ă  court de breadboards, et celles dĂ©jĂ  utilisĂ©es sont dĂ©jĂ  bien chargĂ©es, je voudrais donc une solution qui utilise un minimum de connexions et un minimum de composants. Le VIA possĂšde deux signaux Chip Select : CS1 et /CS2. La puce est activĂ©e lorsque CS1 est Ă  1 et /CS2 Ă  0. On sait dĂ©jĂ  que le VIA doit ĂȘtre dĂ©sactivĂ© lorsque la 16e ligne d’adresse (A15) est Ă  1, puisque ça correspond Ă  la plage allouĂ©e Ă  la ROM. On peut donc connecter A15 sur /CS2. Si on s’arrĂȘte lĂ  on a effectivement allouĂ© la plage 0x0000-0x7FFF au VIA. Imaginons maintenant qu’on branche A14 sur CS1. Le VIA ne serait alors actif que lorsque la 16e ligne d’adresse (A15) est Ă  0 et la 15e (A14) est Ă  1, ce qui correspond Ă  la plage d’adresses 0x4000-0x7FFF. Si on ajoutait A13 avec un ET on rĂ©duirait Ă  la plage 0x6000-0x7FFF. Et si on ajoutait encore A12 ça donnerait 0x7000-0x7FFF. Plus on rĂ©duit la plage mĂ©moire allouĂ©e et plus on laisse de l’espace pour autre chose, mais plus ça demande de logique pour le Chip Select.

Binaire Hexadécimal
0000 0000 0000 0000 0x0000
0100 0000 0000 0000 0x4000
0110 0000 0000 0000 0x6000
0111 0000 0000 0000 0x7000
0111 1111 1111 1111 0x7FFF
1000 0000 0000 0000 0x8000
1111 1111 1111 1111 0xFFFF

Je choisis de rester simple pour l’instant et de m’arrĂȘter Ă  A14. Ça permet de connecter CS1 et /CS2 diretement sans introduire de logique supplĂ©mentaire, et le compromis en terme d’allocation de l’espace d’adresse est ok pour l’instant.

Résumé des connexions à faire :

Ligne d’adresse Connexion
A15 /CE de la ROM via une porte NOT
A15 /CS2 du VIA
A14 CS1 du VIA

Montage de l’EEPROM avec Chip Select et test du programme

Je veux installer le VIA et la logique du Chip Select de maniĂšre semi permantente sur une grande breadboard que j’assemble avec celle du 6502, mais je n’ai plus de grande breadboard. J’en ai commandĂ© des nouvelles, mais en attendant de les recevoir je fais un montage temporaire sur une petite breadboard, en faisant les connexion avec des lignes Dupont. Ça ne me laissera pas assez de Duponts pour monitorer les bus avec l’Arduino, mais tant pis.

Pour faire simple j’utilise une puce 74LS04 qui contient 6 inverseurs et je la place sur la breadboard du 6502 aprĂšs l’espace rĂ©servĂ© Ă  la ROM. C’est temporaire car je prĂ©voie de mettre la RAM Ă  cet endroit par la suite. J’alimente le 74LS04, j’amĂšne A15 sur l’entrĂ©e du premier inverseur, je retire le fil de connexion qui maintenait /CE de la ROM Ă  la masse, et je branche la sortie de l’inverseur Ă  la place.

L'inverseur qui permet de générer le Chip Select pour la ROM

Avant de passer Ă  l’installation du VIA et immobiliser les fils de connexion, il faut reprogrammer la ROM. Pour Ă©viter de reprogrammer si jamais ça ne fonctionne pas comme prĂ©vu du premier coup (ce qui demande pour l’instant de recĂąbler l’Arduino Ă  chaque fois, ce n’est pas trĂšs pratique), j’essaie de faire un programme dont les effets sont reconnaissables sans ambiguitĂ©. J’imagine un programme qui allume 3 LED avec un motif alternatif 010-101. Il n’est pas nĂ©cessaire de faire appel Ă  un compteur ou un timer, avec une horloge suffisamment lente on verra le motif clignoter sans soucis mĂȘme si les instructions se suivent directement sans dĂ©lai.

C’est le moment de se pencher plus en dĂ©tails sur le fonctionnement du VIA. Pour configurer les pins du port A en sortie il faut Ă©crire dans le registre 3. Un bit Ă  1 dans ce registre configure le pin correspondant en sortie. Ensuite pour dĂ©finir la valeur sur le pin il faut Ă©crire dans le registre 1. Vue le mapping mĂ©moire que nous avons fait prĂ©cĂ©demment, l’adresse Ă  utiliser pour les registres 1 et 3 sont respectivement 0x4001 et 0x4003.

Basiquement notre programme ressemble donc à ça :

  1. Ă©crire 1111 1111 (0xFF) Ă  l’adresse 0x4003
  2. Ă©crire 0000 0010 (0x02) Ă  l’adresse 0x4001
  3. Ă©crire 0000 0101 (0x05) Ă  l’adresse 0x4001
  4. recommencer Ă  2.

Registres de contrĂŽle des ports A et B du 6522 (extrait de la datasheet)

En pseudo code machine ça donne ça :

  1. LDA #$FF
  2. STA $4003
  3. LDA #$02
  4. STA $4001
  5. LDA #$05
  6. STA $4001
  7. JMP $XXXX

Explications: Pour Ă©crire une valeur donnĂ©e dans un registre du VIA, on charge d’abord la valeur dans l’accumulateur, puis on envoie la valeur de l’accumulateur Ă  l’adresse du registre. L’instruction JMP permet de dĂ©placer le pointeur d’exĂ©cution Ă  l’adresse indiquĂ©e. Ici on veut revenir Ă  l’instruction LDA #$02 pour former une boucle infinie. Pour dĂ©finir l’adresse Ă  donner Ă  l’instruction JMP il faut connaĂźtre Ă  quelle adresse exacte se situe l’instruction visĂ©e. Pour ça il faut :

  • savoir Ă  quelle adresse commence le programm dans la mĂ©moire
  • combien d’octets occupent les instructions qui prĂ©cĂšdent
  • prendre en compte l’allocation de l’espace d’adressage dĂ©fini prĂ©cĂ©demment

Pour faire simple on peut placer le programme Ă  l’adresse 0x0000 dans la mĂ©moire, ce qui correspond Ă  0x8000 du point de vue du CPU. Il faudra donc penser Ă  placer cette valeur dans le vecteur reset.

Nous avons dĂ©jĂ  rencontrĂ© l’instruction STA prĂ©cĂ©demment. Le code correspondant est 0x8D, et l’adresse est passĂ©e dans les deux octets suivants, les poids faibles en premier. Nous avons Ă©galement dĂ©jĂ  rencontrĂ© l’instruction LDA, mais nous avions utilisĂ© la version qui prend une adresse, alors qu’ici nous voulons lui passer la valeur directement. On aurait pu mettre la donnĂ©e en mĂ©moire et utiliser LDA avec l’adresse correspondante, mais pourquoi faire compliquĂ©. Dans la datasheet on trouve que le code pour LDA en mode d’adressage immĂ©diat est 0xA9, et la valeur est indiquĂ©e dans l’octet qui suit. Regardons maintenant l’instruction JMP. Elle existe aussi dans plusieurs mode d’adressage, ici nous voulons le mode “adressage absolu” qui prend une adresse. Le code correspondant est 0x4C, et l’adresse est indiquĂ©e sur les deux octets suivants, poids faibles en premier comme d’habitude.

Voici le programme final :

Adresse CPU Adresse ROM Donnée
0x8000 0x0000 A9 FF
0x8002 0x0002 8D 03 40
0x8005 0x0005 A9 02
0x8007 0x0007 8D 01 40
0x800A 0x000A A9 05
0x800C 0x000C 8D 01 40
0x800F 0x000F 4C 05 80
0xFFFC 0x7FFC 00
0xFFFD 0x7FFD 80

On voit que l’instruction LDA #$02 si retrouve Ă  l’adresse 0x8005, c’est donc cette valeur qu’on donne au JMP. Je programme la ROM avec le setup dĂ©fini prĂ©cĂ©demment, et je la remet en place sur le circuit.

Le programme de test du VIA écrit sur la ROM

Avant d’installer le VIA je vĂ©rifie que le Chip Select fonctionne comme prĂ©vu et que le programme est bon. Je branche l’Arduino sur le bus et observe les donnĂ©es, toujours avec uniquement les 4 premiĂšres lignes d’adresse. Voici le rĂ©sultat :

Adresse théorique Adresse observée R/W Donnée
0xFFFc 0xc R 0x00
0xFFFd 0xd R 0x80
0x8000 0x0 R 0xa9
0x8001 0x1 R 0xff
0x8002 0x2 R 0x8d
0x8003 0x3 R 0x03
0x8004 0x4 R 0x40
0x4003 0x3 W 0xff
0x8005 0x5 R 0xa9
0x8006 0x6 R 0x02
0x8007 0x7 R 0x8d
0x8008 0x8 R 0x01
0x8009 0x9 R 0x40
0x4001 0x1 W 0x02
0x800a 0xa R 0xa9
0x800b 0xb R 0x05
0x800c 0xc R 0x8d
0x800d 0xd R 0x01
0x800e 0xe R 0x40
0x4001 0x1 W 0x05
0x800f 0xf R 0x4c
0x8010 0x0 R 0x05
0x8011 0x1 R 0x80
0x8005 0x5 R 0xa9
0x8006 0x6 R 0x02
0x8007 0x7 R 0x8d
0x8008 0x8 R 0x01
0x8009 0x9 R 0x40
0x4001 0x1 W 0x02
0x800a 0xa R 0xa9
0x800b 0xb R 0x05
0x800c 0xc R 0x8d
0x800d 0xd R 0x01
0x800e 0xe R 0x40
0x4001 0x1 W 0x05
0x800f 0xf R 0x4c
0x8010 0x0 R 0x05
0x8011 0x1 R 0x80

Tout est conforme.

Montage du VIA

Je peux maintenant enfin installer le VIA. Je place le 6522 sur une petite breadboard, et je connecte tout de suite VDD et VSS. J’ajoute 3 LEDs Ă  cĂŽtĂ©s avec leur rĂ©sistance et je connecte les LEDs aux 3 premiĂšres sorties du port A. Je passe en revue le reste des pins. Il faut connecter :

  • les 8 lignes de donnĂ©es (D0 Ă  D7)
  • les 4 lignes adresses (A0 Ă  A15)
  • le signal d’écriture/lecture (/RW)
  • la clock (PHI2)
  • le reset (/RES)
  • CS1 et /CS2

Nous n’utilisions pas le signal d’écriture/lecture (/RW) jusqu’à prĂ©sent puisque la ROM n’est utilisĂ©e qu’en lecture. Mais le VIA peut-ĂȘtre utilisĂ© en lecture et Ă©criture, mĂȘme si notre programme de test ne fait pour l’instant qu’écrire. Les signaux d’horloge et de reset peuvent ĂȘtre repris directement sur ceux du 6502. Je branche donc tout ça, l’Arduino fournit le signal d’horloge comme d’habitude.

Je lance, fait un reset, mais ça ne fonctionne pas, les diodes restent Ă©teintes. Ça me laisse perplexe. Je teste d’abord les LEDs en amenant directement le 5V dessus et elles s’allument bien. J’observe ensuite les valeurs en sortie du VIA avec Arduino, et elles restent toutes Ă  0. De la mĂȘme maniĂšre je vĂ©rifie les signaux CS1, /CS2, la clock, et /RW, et tout semble correcte. /RW est Ă  1 la plupart du temps pour indiquer une lecture, et passe Ă  0 de temps en temps pendant exactement un cycle. Idem pour CS1 et /CS2 qui prennent bien les valeurs attendues.

J’entreprends alors d’essayer de piloter le VIA directement avec l’Arduino pour vĂ©rifier que j’arrive Ă  le faire fonctionner en envoyant manuellement des commandes et confirmer que le programme de test envoie bien les bonnes commandes. Sur la breadboard je force /CS1 Ă  1, /CS2 Ă  0, et /RW Ă  0. Avec le code je fais un reset en mettant Ă  /RES Ă  0 et en faisant quelques cycles d’horloge, puis j’envoie les octets pour configurer le port A, mais toujours rien de visible. J’essaie avec le port B et toujours rien. À ce moment je commence Ă  soupçonner mon VIA d’ĂȘtre dĂ©fectueux.

Je tente de lire les entrĂ©es sur les ports. J’ajoute /RW dans le code pour pouvoir lire/Ă©crire, et je connecte directement le 5V sur une des pins d’entrĂ©es. LĂ  je rĂ©alise que les LEDs sont toujours connectĂ©es au port A alors que je teste le port B. Je corrige ça et relance le test prĂ©cĂ©dent mais toujours rien. Je fais le teste de lecture et je lis 0. Ça me laisse vraiment perplexe.

Connection du VIA directement Ă  l'Arduino

Extrait du code de test du VIA

Le code Arduino est disponible sur GitHub.

La suite

Je n’arrive pas Ă  faire fonctionner le 6522, mais en revoyant la vidĂ©o de Ben Eater consacrĂ©e Ă  son installation j’ai l’idĂ©e de construire un latch comme il l’a montrĂ© dans cette vidĂ©o, et d’essayer de l’interfacer avec le CPU. C’est un exercice intĂ©ressant. Si ça fonctionne ça permettra d’avoir dĂ©jĂ  une sortie pilotable par le 6502, et donc d’avoir enfin des rĂ©sultats observables directement et d’écrire des programmes qui font quelque chose.