Le 6502 est installé sur une breadboard et j’ai validé son bon fonctionnement. Pour l’instant la valeur 0xEA est câblée en dure le bus. L’objectif d’aujourd’hui et de fournir au CPU des données dynamiques en fonction de l’adresse qu’il demande, de sorte à former un programme qui ait du sens. Classiquement c’est le rôle d’une puce mémoire. Mais dans un premier temps j’expérimente avec Arduino.

Données dynamiques

En plus des 4 lignes d’adresses utilisées jusque là, j’ajoute les 8 lignes du bus de données. Je connecte également le signal read/write pour détecter quand le CPU cherche à lire sur le bus.

Avant d’aller plus loin je retravaille le programme pour afficher les nouvelles données, et je fais également en sorte que ce soit l’Arduino qui génère le signal d’horloge. Ça permet de mieux coordonner les actions sur le bus en fonction de l’horloge, et au passage de pouvoir régler librement la vitesse. Le module à base de 555 que j’utilise, bien que réglable, ne va pas aussi bas que je voudrais.

Ces modifications en place, j’écris un code qui envoie 0xEA sur le bus lorsque le CPU est en lecture.

L'Arduino connecté au bus d'adresse et au bus de données du 6502

L'Arduino écrit 0xEA sur le bus quand il détecte que le 6502 est en lecture

Maintenant que je suis capable d’envoyer des données dynamiques au CPU, je peux essayer de lui faire exécuter une petit programme. Très petit en l’occurrence puisque comme je travaille avec seulement 4 bits d’adresse on ne peut fournir que 16 octets au total, dans lesquels il faut compter les deux bits d’adresse du vecteur reset situés aux adresses 0xC et 0xD. Techniquement j’aurais pu ajouter un fil ou deux ce qui pourrait respectivement doubler ou quadrupler l’espace disponible, mais je trouvais que c’était un bon challenge.

Pour que ce soit intéressant, je choisi des instructions qui provoquent des accès à la mémoire en lecture et en écriture. L’instruction LDA (Load Accumulator) charge dans l’accumulateur la valeur stockée à l’adresse indiquée. ADC (Add with Carry) additionne à l’accumulateur la valeur lue à l’adresse spécifiée. STA (Store Accumulator) écrit le contenu de l’accumulateur en mémoire. Chacun de ces trois instructions prennent en paramètre l’adresse mémoire à utiliser sur deux octets, les poids faibles en premier. Avec 3 instructions on a déjà rempli 9 octets. Immédiatement après je place les deux octets de données qui seront additionnés par le CPU. Il reste juste un octet libre avant le vecteur de reset.

Voici le programme final (données en hexadécimal):

Addr Instruction Données
0x0 LDA $0009 AD 09 00
0x3 ADC $000A 6D 0A 00
0x6 STA $000B 8D 0B 00
0x9   28
0xA   02
0xB    
0xC   00
0xD   00
0xE    
0xF    

Explications :

  1. LDA $0009 : Charger la valeur à l’adresse 0x09 dans l’accumulateur
  2. ADC $000A : Ajouter la valeur à l’adresse 0x0A à la valeur présente dans l’accumulateur
  3. STA $000B : Écrire la valeur présente dans l’accumulateur à l’adresse 0x0B

On devrait donc voir passer sur le bus les événements suivants :

Addr R/W Data Événement
0xC R 00 Lecture du vecteur reset
0xD R 00
0x0 R AD Lecture de la première instruction
0x1 R 09
0x2 R 00
0x9 R 28 Lecture de la donnée
0x3 R 6D Lecture de la deuxième instruction
0x4 R 09
0x5 R 00
0xA R 02 Lecture de la donnée
0x6 R 8D Lecture de la troisième instruction
0x7 R 0B
0x8 R 00
0xB W 2A Ecriture de la donnée

Le résultat correspond bien à ce qui est attendu :

Exécution du premier programme

Le code Arduino est disponible sur GitHub.

Découverte de l’EEPROM

L’Arduino est bien sympatique, mais dans le système final les données sont stockées dans une puce mémoire. J’utilise ici une puce EEPROM AT28C256. C’est une mémoire de 32ko destinée à être utilisée en lecture seule, mais qu’on peut effacer et programmer éléctroniquement.

Je veux commencer par la base, en faisant des lectures et écritures en manipulant directement les signaux de contrôle. Sur une breadboard je connecte huit LEDs au bus de données de la ROM et je force toutes les lignes d’adresse à zéro à l’aide de cavaliers, sauf les quatre bits de poids faible que je connecte avec des fils de connexion repositionnables facilement.

Il y a 3 signaux de contrôle sur la ROM :

Signal Signification Usage
/CE Chip Enabled Active la puce
/OE Output Enabled Active la sortie de données
/WE Write Enabled Déclenche l’écriture

Sur la breadboard je connecte CE à 0, et je gère manuellement OE et WE.

Montage de test de la ROM

Je commence par tester la lecture. Je mets /OE à 0 pour activer la sortie et /WE à 1 pour être en mode lecture, et je fais défiler les adresses en repositionnant les 4 fils prévus à cet effet. Les LEDs affichent le contenu stocké dans la mémoire à chaque adresse.

Voici mon relevé :

Adresse Donnée
0x0000 00
0x0001 FF
0x0002 00
0x0003 FF
0x0004 00
0x0005 FF
0x0006 00
0x0007 FF
0x0008 FF
0x0009 FF
0x000A FF
0x000B FF
0x000C FF
0x000D FF
0x000E FF
0x000F FF

Je teste maintenant une écriture. Le fonctionnement est le suivant : En temps normal (mode lecture) /WE est à 1. Lorsque /WE passe à 0, la puce capture la valeur présente sur le bus d’adresse. Quand /WE repasse à 1 elle capture la donnée et procède à l’écriture dans la mémoire.

Je commence par désactiver la sortie en plaçant /OE à 1. Je configure l’adresse 0, puis je forme la valeur 0xEA sur le bus de données à l’aide de cavaliers, et je vérifie la valeur sur les LEDs. Enfin je fais passer /WE à 0 puis de nouveau à 1.

Je repasse en mode lecture en enlevant les cavalier sur les lignes de données, je remet /OE à 0, et je vois bien la valeur 0xEA apparaître sur les LEDs. Je regarde les autres adresses et constate que je retrouve bien les valeurs relevées précédemment à l’éxception de l’adresse 0 qui affiche désormais 0xEA. Première écriture manuelle réussie !

Je connecte maintenant l’adresse et les données ainsi que les signaux de contrôle à l’Arduino et code un programme pour automatiser les lectures et écritures.

Dans un premier temps le programme parcours séquentiellement les 16 adresses et affiche la données correspondante à chacune. Ça fonctionne bien et je retrouve bien le relevé initial avec la valeur 0xEA à l’adresse 0. Je code ensuite l’écriture, en commençant par une fonction qui écrit un seul octet. Il faut reproduire la séquence des signaux de contrôle que j’ai effectuée à la main juste avant. Ensuite j’ajoute la possibilité d’écrire une séquence de valeurs une à une.

Fonction d'écriture d'un octet dans la ROM

Ecriture des 14 premiers octets puis relecture

Le code Arduino est disponible sur GitHub.

Connexion de l’EEPROM au 6502

L’objectif maintenant est de connecter la ROM au CPU pour que celui-ci lise et exécute le programme. En laissant la ROM sur sa breadboard, je relie désormais les 4 lignes d’adresse et le bus de données sur les broches correspondantes du 6502. Ce faisant je remarque que j’avais du aller un peu vite quand j’ai choisi les quatre lignes d’adresse à manipuler sur la ROM car ce n’était pas du tout les quatre bits de poids faible, bien que c’était mon intention. Il s’agissait en fait des lignes 13, 8, 9 et 11. En réalité ce n’est pas gênant, du moment que je connecte au CPU les mêmes lignes de la même manière, il retrouvera les données aux adresses attendues.

Pour analyser le résulat je choisis de me baser sur les LEDs connectés au bus de données de la ROM. Ce n’est pas forcément le plus pratique, et je pourrais connecter l’Arduino comme précédemment mais je n’ai pas envie d’ajouter encore davantage de fils volants à un montage déjà très chargé. Mais surtout j’aime utiliser toutes les opportunités qui se présentent pour confronter mes prédictions à la réalité. Parfois ça met en valeur des lacunes dans ma compréhension.

Avant de lancer le test je modifie le programme de sorte que le bus reste en mode lecture. Je retire l’instruction STA et restructure un peu l’ordre. Voici le nouveau programme :

Addr Instruction Données
0x0 LDA $0009 AD 09 00
0x3 LDA $000A AD 0A 00
0x6 ADC $000B 6D 0B 00
0x9   28
0xA   02
0xB   0A
0xC   00
0xD   00
0xE    
0xF    

Si tout se passe bien on est sensé observer les données suivantes sur le bus :

Hexa Binaire Explication
000000 0000Vecteur Reset
000000 0000
AD1010 1101Première instruction
090000 1001
000000 0000
280010 1000Donnée
AD1010 1101Deuxième instruction
0A0000 1010
000000 0000
020000 0010Donnée
6D0110 1101Troisième instruction
0B0000 1011
000000 0000
0A0000 1010Donnée

Les premières observations m’ont laissé perplexe. Les résultats n’avaient pas l’air de correspondre à ce qui est attendu, et j’ai commencé à me demander ce qui n’allait pas. J’avais l’impression de reconnaitre le 02 (0000 0010) mais à l’envers, mais je ne voyais pas comment les données auraient pu être inversées. Et puis j’ai réalisé que je lisais les LEDs dans le mauvais sens ! Je m’attendais à voir la même chose que ce que j’avais affiché à l’écran pour référence, mais sur les LEDs les bits de poids faible étaient à gauche et non à droite. J’ai retourné la plaque et là tout semblait correspondre. J’ai filmé la séquence pour analyser les résultat plus rigourseuement, et je confirme que tout correspond parfaitement.

Observation des données qui transitent sur le bus

La suite

Je veux maintenant connecter la ROM au 6502 de manière un peu plus permanente sur la même breadboard, câbler toutes les lignes d’adresse et de données, ainsi que la logique d’activation de la puce. Pour ça il sera utile de pouvoir programmer l’intrégralité de la mémoire disponible, et j’ai quelques idées à ce sujet. Après ça je pourrais commencer à installer la puce d’entrées-sorties qui permettra d’avoir enfin des résultats observables.