L'union d'Arduino et du processeur classique


Les rétro-ordinateurs sont disponibles à des degrés divers. Certains se contentent d'émulation. D'autres préfèrent les FPGA, car il ne s'agit pas d'émulation, mais de loisirs. Enfin, servez le troisième processeur.

Mais le processeur a tellement besoin de fonctionner! Encore une fois, le dilemme: prendre de vraies puces des mêmes années, ou tout mettre dans le FPGA, laissant le processeur à l'extérieur? Cependant, pourquoi le FPGA est-il nécessaire? Vive l'union de l'Arduino et du processeur classique!

Donnez à votre Arduino un «deuxième cerveau» et rendez-le plus intelligent.

Un véritable microprocesseur huit bits exécute des programmes, tandis qu'un Arduino émule la ROM, la RAM et les simples périphériques.

Concevez des périphériques virtuels dans l'IDE Arduino et exécutez le code assembleur sur le microprocesseur. Pas besoin d'assembler des circuits complexes et des mémoires flash parallèles flash.

Microprocesseurs pris en charge: 6502, 6809 et Z80 (18581), d'autres sont en cours.

Un blindage avec un microprocesseur n'interfère pas avec la connexion d'autres blindages: avec les écrans LCD, les cartes mémoire, etc.

En plus du langage d'auto-assemblage, vous pouvez essayer d'exécuter du code classique sur le microprocesseur.

Certes, le microprocesseur fonctionnera à une fréquence très basse - environ 95 kHz, sa valeur exacte dépend de l'optimisation du code d'émulation périphérique.

La distribution de l'espace d'adressage est définie par programme dans une esquisse. Le microprocesseur peut être alloué de 4 à 6 Ko de 8 Ko de RAM disponibles sur l'Arduino Mega. La ROM peut allouer plus de 200 Ko sur les 256 disponibles.

Le port série Arduino Mega peut émuler UART.

Les circuits, dessins de planches, fichiers Gerber sont disponibles sous CC-BY-SA 4.0 ici . Dans le même temps, il est nécessaire de joindre le fichier README.md, car il contient l'avertissement suivant:
Ne connectez pas le blindage tant que le croquis d'émulation périphérique n'est pas téléchargé! Sinon, il est possible de court-circuiter les lignes de sortie du microprocesseur.
Oui, et dans l'esquisse elle-même, quelque chose doit être refait avec soin pour la même raison.

Le schéma de l'appareil sur 6502:



Diagramme de l'appareil sur 6809:



Schéma de l'appareil sur le Z80:



Vous pouvez déjà exécuter:

Sur un appareil avec 6502 - Apple I, Woz Monitor + ROM avec BASIC

Sur un appareil avec 6809 - Un analogue d'un ordinateur fait maison Simon6809 du même développeur, un moniteur de formation avec assembleur et démonteur

Sur un appareil avec Z80 - jusqu'à présent, seulement un test d'écho du port série , vous permettant de vérifier les performances du 8251 virtuel (KR580VV51A).

Firmware pour émuler des périphériques - sous licence MIT.

Brève description du principe d'action:

Vers l'appareil 6502

Vers l'appareil sur 6809

Pour l'appareil sur le Z80 - en préparation.

Le développeur essaie de vendre des appareils, mais avec une livraison uniquement aux États-Unis. Il n'y a pas de raison particulière d'acheter, car le schéma est très simple, vous pouvez le répéter sur un morceau de la planche à pain en une heure.

Il est prévu de développer des cartes similaires sur RCA1802, 68008, 8085 (182185), 8088 (181088). A propos de K1801BM1 n'est pas dit, mais vous pouvez lancer une telle idée à l'auteur.

Fichiers:

Vers l'appareil sur 6502: notice de montage , sérigraphie , schéma

Vers l'appareil sur 6809: notice de montage , sérigraphie , schéma

Vers l'appareil sur Z80: notice de montage , sérigraphie , schéma

Considérez l'interaction d'un Arduino et d'un appareil 6502. L'Arduino change périodiquement le niveau à l'entrée d'un microprocesseur conçu pour fournir des impulsions d'horloge de zéro à un et vice versa. À chaque cycle, il vérifie ce qui se passe sur les lignes de contrôle et le bus d'adresses et, selon la situation, lit les informations du bus de données ou les y envoie. Arduino peut également contrôler les lignes IRQ et NMI, provoquant des interruptions. La figure montre les types de données et les directions de leur transmission:



La correspondance des ports Arduino et des sorties du microprocesseur est configurée dans l'esquisse:

/* Digital Pin Assignments */ #define DATA_OUT PORTL #define DATA_IN PINL #define ADDR_H PINC #define ADDR_L PINA #define ADDR ((unsigned int) (ADDR_H << 8 | ADDR_L)) #define uP_RESET_N 38 #define uP_RW_N 40 #define uP_RDY 39 #define uP_SO_N 41 #define uP_IRQ_N 50 #define uP_NMI_N 51 #define uP_E 52 #define uP_GPIO 53 

Nous diviserons chaque mesure en événements suivants:

CLK change l'état de un à zéro (déclin)
CLK est dans un état de zéro
CLK change d'état de un à zéro (augmentation)
CLK est à l'état unitaire
CLK change à nouveau l'état de un à zéro ...

Que se passe-t-il pendant les moments de transition d'état?

6502 reçoit des impulsions d'horloge à l'entrée CLK0, les met en mémoire tampon et les envoie à deux sorties: CLK1 et CLK2. Bien que tous les événements du microprocesseur soient liés à CLK1, nous supposons que le retard n'est pas important et qu'ils sont liés à CLK0 - la ligne le long de laquelle le microprocesseur reçoit des impulsions d'horloge d'Arduino. Et appelez le signal juste CLK.



1. CLK change d'état de un à zéro.

2. Le microprocesseur sort une nouvelle adresse sur le bus d'adresse et un signal de commutation entre la lecture et l'écriture sur la sortie R / W. Mais il n'est pas encore prêt pour l'échange de données.

3. CLK passe à l'état unitaire, ce qui signifie que l'échange de données a commencé. S'il s'agit d'une opération de lecture, le microprocesseur transfère les sorties du bus de données à l'état d'entrée et reçoit des données, et si l'opération d'écriture, il les transfère à l'état de sortie et envoie des données. Et le signal R / W fait passer le périphérique externe en mode écriture ou lecture, à l'opposé de l'état correspondant du microprocesseur.

4. CLK passe à zéro. Maintenant, ni le microprocesseur ni les périphériques d'entrée-sortie ne sortent quoi que ce soit sur le bus de données. Le microprocesseur peut définir la ligne de bus de données et la broche R / W dans un nouvel état.

Une explication simple, compréhensible pour l'enfant. Qui ne pense jamais à ces "intrigues en coulisses", s'il ne programme que des microcontrôleurs. Même en assembleur.

Si vous devez connecter votre périphérique, il doit avoir le temps de préparer les données avant que l'unité (temps de préparation) n'apparaisse sur la ligne CLK, et pendant que l'unité est là, ne la changez pas. Si le périphérique n'a pas le temps de préparer les données alors que CLK est nul, ou les change lorsque l'unité est là, vous vous demanderez longtemps pourquoi votre code ne fonctionne pas. La fréquence d'horloge du microprocesseur étant dix à quinze fois inférieure à la fréquence nominale, il est facile de se conformer à cette exigence. Mais c'est nécessaire.

Ainsi, vous devez «apprendre» à Arduino à générer des impulsions d'horloge, à vérifier en permanence ce qui se passe sur les lignes d'adresse et de lecture / écriture et à interagir avec le bus de données en conséquence. Pour ce faire, l'esquisse utilise l'interruption de temporisation timer1, qui génère des impulsions avec une fréquence de 95 kHz. Arduino fonctionne beaucoup plus rapidement que le microprocesseur, et donc, entre ses horloges, il parvient à tout lire et à tout préparer. Il est important de s'assurer qu'après avoir modifié l'esquisse, cette condition continue d'être remplie.

Voici un extrait de l'esquisse, qui montre comment le CLK passe de zéro à un, et ce qui se passe ensuite:

 //////////////////////////////////////////////////////////////////// // Processor Control Loop //////////////////////////////////////////////////////////////////// // This is where the action is. // it reads processor control signals and acts accordingly. // ISR(TIMER1_COMPA_vect) { // Drive CLK high CLK_E_HIGH; // Let's capture the ADDR bus uP_ADDR = ADDR; if (STATE_RW_N) ////////////////////////////////////////////////////////////////// // HIGH = READ transaction { // uP wants to read so Arduino to drive databus to uP: DATA_DIR = DIR_OUT; // Check what device uP_ADDR corresponds to: // ROM? if ( (ROM_START <= uP_ADDR) && (uP_ADDR <= ROM_END) ) DATA_OUT = pgm_read_byte_near(rom_bin + (uP_ADDR - ROM_START)); else if ( (BASIC_START <= uP_ADDR) && (uP_ADDR <= BASIC_END) ) DATA_OUT = pgm_read_byte_near(basic_bin + (uP_ADDR - BASIC_START)); else // RAM? if ( (uP_ADDR <= RAM_END) && (RAM_START <= uP_ADDR) ) DATA_OUT = RAM[uP_ADDR - RAM_START]; else // 6821? if ( KBD <=uP_ADDR && uP_ADDR <= DSPCR ) { // KBD? if (uP_ADDR == KBD) { ... // handle KBD register } else // KBDCR? if (uP_ADDR == KBDCR) { ... // handle KBDCR register } else // DSP? if (uP_ADDR == DSP) { ... // handle DSP register } else // DSPCR? if (uP_ADDR == DSPCR) { ... // handle DSPCR register } } } else ////////////////////////////////////////////////////////////////// // R/W = LOW = WRITE { // RAM? if ( (uP_ADDR <= RAM_END) && (RAM_START <= uP_ADDR) ) RAM[uP_ADDR - RAM_START] = DATA_IN; else // 6821? if ( KBD <=uP_ADDR && uP_ADDR <= DSPCR ) { // KBD? if (uP_ADDR == KBD) { ... // handle KBD register } else // KBDCR? if (uP_ADDR == KBDCR) { ... // handle KBDCR register } else // DSP? if (uP_ADDR == DSP) { ... // handle DSP register } else // DSPCR? if (uP_ADDR == DSPCR) { ... // handle DSPCR register } } } //////////////////////////////////////////////////////////////// // We are done with this cycle. // one full cycle complete clock_cycle_count ++; // start next cycle CLK_E_LOW; // If Arduino was driving the bus, no need anymore. // natural delay for DATA Hold time after CLK goes low (t_HR) DATA_DIR = DIR_IN; } 

L'allocation de l'espace d'adressage peut être effectuée comme vous le souhaitez, dans une esquisse non modifiée, c'est la même chose que dans Apple 1 avec 256 octets de ROM, 8 kilo-octets de ROM pour BASIC, 4 kilo-octets de RAM et 6821 périphérique d'entrée-sortie.

 // MEMORY LAYOUT // 4K MEMORY #define RAM_START 0x0000 #define RAM_END 0x0FFF byte RAM[RAM_END-RAM_START+1]; // ROMs (Monitor + Basic) #define ROM_START 0xFF00 #define ROM_END 0xFFFF #define BASIC_START 0xE000 #define BASIC_END 0xEFFF //////////////////////////////////////////////////////////////////// // Woz Monitor Code //////////////////////////////////////////////////////////////////// // PROGMEM const unsigned char rom_bin[] = { 0xd8, 0x58, 0xa0, 0x7f, 0x8c, 0x12, 0xd0, 0xa9, 0xa7, 0x8d, 0x11, 0xd0, ... 0x00, 0xff, 0x00, 0x00 }; // BASIC ROM starts at E000 PROGMEM const unsigned char basic_bin[] = { 0x4C, 0xB0, 0xE2, 0xAD, 0x11, 0xD0, 0x10, 0xFB, ... 0xE0, 0x80, 0xD0, 0x01, 0x88, 0x4C, 0x0C, 0xE0 }; 

La RAM est émulée par un tableau de RAM d'octets [RAM_END-RAM_START + 1]. Deux mots clés PROGMEM sont nécessaires pour que le contenu des ROM émulées soit stocké dans la mémoire flash du microcontrôleur.

6821 est suffisamment émulé pour que le clavier virtuel et l'affichage fonctionnent via le «terminal». Woz Monitor et BASIC work, c'est ce que l'auteur recherchait.

Pour émuler n'importe quel périphérique, vous devez lire attentivement sa fiche technique et savoir quels registres il a et à quoi ils servent. La commodité de l'émulation réside dans la flexibilité avec laquelle vous pouvez créer des logiciels analogues de la périphérie.

Les périphériques d'E / S sont situés dans l'espace d'adressage du microprocesseur; ils sont accessibles de la même manière que les cellules de mémoire. Pour utiliser les périphériques "de fer", tels qu'un écran LCD, une carte mémoire, une sortie audio, vous devez allouer une place dans l'espace d'adressage.

Références:

www.6502.org
www.callapple.org/soft/ap1/emul.html
skilldrick.imtqy.com/easy6502
searle.hostei.com/grant/6502/Simple6502.html
wilsonminesco.com/6502primer
SB-Assembler: www.sbprojects.net/sbasm

Passez au 6809, il contient:

Deux batteries A et B à huit bits, qui peuvent être combinées en une batterie à six bits
Deux index de pile 16 bits
Adressage par rapport au compteur d'instructions
Ajouter ou soustraire automatiquement 1 ou 2
Multiplication de deux nombres non signés à huit chiffres
Arithmétique 16 bits
Transfert et échange de données entre tous les registres
Écrire et lire tous les registres et toute combinaison de ceux-ci

Le microprocesseur 6809E (externe) a besoin d'une horloge externe, tandis que le 6809 en a une interne. Pour Hitachi, ils sont appelés respectivement 6309E et 6309, ils diffèrent des habituels en ce qu'ils fonctionnent sous forme 32 bits à l'intérieur de l'opération, mais il est possible de passer en mode de compatibilité avec la version classique.

En fait, tout le projet RetroShield a commencé parce que l'auteur voulait mettre à niveau son ordinateur fait maison Simon6809 et nommer le résultat Simon6809 Turbo. Mais il s'est avéré que les puces logiques standard pour tout ce qu'il voulait y implémenter exigeaient beaucoup. Par conséquent, l'auteur a formulé l'idée de RetroShield pour la première fois en relation avec le 6809, et seulement alors il a pensé: «et si la même chose avec d'autres processeurs faisait de même?».

L'appareil utilise bien sûr le 6809E, qui nécessite une horloge externe, afin de pouvoir synchroniser son travail de l'extérieur. Les lignes E et Q des deux processeurs sont nommées de la même manière, seules 6809 ont des sorties et 6809E ont des entrées.

Arduino interagit avec 6809 de la même manière qu'avec 6502, mais il a deux entrées d'horloge: E et Q, et trois entrées d'interruption: IRQ, FIRQ et NMI.



Cette fois, la correspondance entre les ports Arduino et les broches du microprocesseur est configurée comme suit:

 /* Digital Pin Assignments */ #define DATA_OUT PORTL #define DATA_IN PINL #define ADDR_H PINC #define ADDR_L PINA #define ADDR ((unsigned int) (ADDR_H << 8 | ADDR_L)) #define uP_RESET_N 38 #define uP_E 52 #define uP_Q 53 #define uP_RW_N 40 #define uP_FIRQ_N 41 #define uP_IRQ_N 50 #define uP_NMI_N 51 #define uP_GPIO 39 

Comme le montrent les graphiques, le signal Q est décalé par rapport à E d'un quart de période:

Nous ferons à peine attention à Q, car tous les événements sont liés à E. Et tout se passe comme ceci:



  1. E passe à zéro. Le processeur définit une nouvelle adresse sur le bus d'adresses et modifie l'état de la ligne R / W.
  2. E passe à un, le processeur est prêt pour l'échange de données.
  3. Peu importe ce qui arrive au bus de données tant que E en est un, l'essentiel est que les données requises y soient présentes au moment où E revient à zéro.
  4. Lors de la lecture des données, le périphérique d'E / S doit fournir les données requises au bus de données avant que la ligne E ne passe de un à zéro (le retard minimum est indiqué par le chiffre 17 dans le cercle).
  5. Lors de l'enregistrement, le périphérique d'E / S doit fixer les données dans un registre sous la forme dans laquelle elles se trouvaient au moment où E est passé de un à zéro. Le processeur fournira ces données sur le bus encore plus tôt - au moment du passage de Q à un (le nombre 20 dans le cercle).
  6. Après la transition de E à zéro, tout se répète.

Tout ce qui a été dit plus haut sur 6502 sur la nécessité d'un périphérique (y compris un périphérique virtuel) pour développer tous les signaux à temps concerne 6809.

Génération des signaux E et Q, comme dans le cas du 6502, avec la seule différence qu'il y a deux signaux, et ils doivent être commutés conformément aux graphiques. Et juste comme ça, un sous-programme appelé interruption effectue une entrée ou une sortie de données aux moments requis.

L'espace d'adressage dans l'esquisse non modifiée est distribué de la même manière que dans l'ordinateur Simon6809 fait maison :

 // MEMORY #define RAM_START 0x0000 #define RAM_END 0x0FFF #define ROM_START 0xE000 #define ROM_END 0xFFFF byte RAM[RAM_END-RAM_START+1]; //////////////////////////////////////////////////////////////////// // Monitor Code //////////////////////////////////////////////////////////////////// // static const unsigned char PROGMEM const unsigned char simon09_bin[] = { 0x1a, 0xff, 0x4f, 0x1f, 0x8b, 0x0f, 0x36, 0x7f, 0x01, 0xa5, 0x10, 0xce, ... 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0f, 0xe0, 0x00 }; 

La RAM et la ROM sont stockées dans des tableaux de la même manière que dans la variante 6502, à la seule différence qu'il n'y a qu'un seul tableau avec des données ROM.

Les périphériques d'E / S se voient également allouer des parties de l'espace d'adressage, et ils peuvent être virtuels ou réels. Étant donné que Simon6809 est une machine moderne basée sur une base élémentaire vintage, il échange des données via FTDI depuis le PC sur lequel le «terminal» fonctionne. Ici, il est émulé.

Références:

Beaucoup d'informations sur 6809 sur la page Arto
Article Wikipédia sur 6809
Systèmes SWTPc 6809
Article Wikipedia sur le système d'exploitation FLEX

Source: https://habr.com/ru/post/fr446048/


All Articles