Die Vereinigung von Arduino und dem klassischen Prozessor


Retrocomputer sind in unterschiedlichem Maße pingelig. Einige geben sich mit der Emulation zufrieden. Andere bevorzugen FPGAs, denn dann stellt sich heraus, dass es sich nicht um Emulation, sondern um Erholung handelt. Zum Schluss den dritten Prozessor bedienen.

Aber der Prozessor braucht so viel, um zu arbeiten! Wieder das Dilemma: Nehmen Sie echte Chips der gleichen Jahre oder legen Sie alles in das FPGA und lassen Sie den Prozessor draußen? Warum ist FPGA jedoch notwendig? Es lebe die Vereinigung von Arduino und klassischem Prozessor!

Geben Sie Ihrem Arduino ein „zweites Gehirn“ und machen Sie es schlauer.

Ein echter 8-Bit-Mikroprozessor führt Programme aus, während ein Arduino ROM, RAM und einfache Peripheriegeräte emuliert.

Entwerfen Sie virtuelle Peripheriegeräte in der Arduino IDE und führen Sie den Assembler-Code auf dem Mikroprozessor aus. Keine Notwendigkeit, komplexe Schaltungen und Flash-Parallel-ROMs zusammenzubauen.

Unterstützte Mikroprozessoren: 6502, 6809 und Z80 (818581), andere sind unterwegs.

Eine Abschirmung mit einem Mikroprozessor beeinträchtigt nicht die Verbindung anderer Abschirmungen: mit LCDs, Speicherkarten usw.

Zusätzlich zur Selbstassemblierungssprache können Sie versuchen, klassischen Code auf dem Mikroprozessor auszuführen.

Der Mikroprozessor arbeitet zwar mit einer sehr niedrigen Frequenz - etwa 95 kHz, sein genauer Wert hängt von der Optimierung des peripheren Emulationscodes ab.

Die Verteilung des Adressraums wird programmgesteuert in einer Skizze festgelegt. Dem Mikroprozessor können 4 bis 6 kB 8 kB RAM zugewiesen werden, die auf dem Arduino Mega verfügbar sind. Der ROM kann mehr als 200 kB der verfügbaren 256 zuweisen.

Die serielle Arduino Mega-Schnittstelle kann UART emulieren.

Schaltungen, Platinenzeichnungen, Gerber-Dateien finden Sie hier unter CC-BY-SA 4.0. Gleichzeitig muss die Datei README.md angehängt werden, da sie die folgende Warnung enthält:
Schließen Sie die Abschirmung erst an, wenn die periphere Emulationsskizze hochgeladen wurde! Andernfalls ist ein Kurzschluss der Ausgangsleitungen des Mikroprozessors möglich.
Ja, und in der Skizze selbst muss aus demselben Grund etwas sorgfältig überarbeitet werden.

Das Schema des Geräts auf 6502:



Das Schema des Geräts auf 6809:



Schema des Geräts auf dem Z80:



Sie können bereits ausführen:

Auf einem Gerät mit 6502 - Apple I, Woz Monitor + ROM mit BASIC

Auf einem Gerät mit 6809 - Ein Analogon eines selbstgebauten Computers Simon6809 desselben Entwicklers, eines Trainingsmonitors mit Assembler und Disassembler

Auf einem Gerät mit Z80 - bisher nur ein Echotest der seriellen Schnittstelle , mit dem Sie die Leistung des virtuellen 8251 (KR580VV51A) überprüfen können.

Firmware zur Emulation von Peripheriegeräten - unter der MIT-Lizenz.

Kurzbeschreibungen des Handlungsprinzips:

Zum 6502 Gerät

Zum Gerät auf 6809

Zum Gerät am Z80 - in Vorbereitung.

Der Entwickler versucht , Geräte zu verkaufen , jedoch nur in den USA. Es gibt keinen besonderen Grund zum Kauf, da das Schema sehr einfach ist, können Sie es in einer Stunde auf einem Stück des Steckbretts wiederholen.

Es ist geplant, ähnliche Boards auf RCA1802, 68008, 8085 (211821 ,85), 8088 (181088) zu entwickeln. Über K1801BM1 wird nichts gesagt, aber Sie können dem Autor eine solche Idee vorlegen.

Dateien:

Zum Gerät auf 6502: Montageanleitung , Siebdruck , Diagramm

Zum Gerät auf 6809: Montageanleitung , Siebdruck , Diagramm

Zum Gerät am Z80: Montageanleitung , Siebdruck , Diagramm

Betrachten Sie das Zusammenspiel von Arduino und 6502-Gerät. Das Arduino ändert regelmäßig den Pegel am Eingang des Mikroprozessors, der für die Versorgung von Taktimpulsen ausgelegt ist, von Null auf Eins und umgekehrt. Bei jedem Zyklus prüft es, was auf den Steuerleitungen und dem Adressbus geschieht, und liest je nach Situation Informationen vom Datenbus oder sendet sie dorthin. Arduino kann auch IRQ- und NMI-Leitungen steuern, was zu Unterbrechungen führt. Die Abbildung zeigt die Datentypen und die Übertragungsrichtungen:



Die Entsprechung der Arduino-Ports und der Mikroprozessorausgänge ist in der Skizze konfiguriert:

/* 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 

Wir werden jede Maßnahme in folgende Ereignisse unterteilen:

CLK ändert den Status von eins auf null (Abnahme)
CLK befindet sich in einem Zustand von Null
CLK ändert den Zustand von eins auf null (Anstieg)
CLK befindet sich im Einheitszustand
CLK ändert erneut den Status von eins auf null ...

Was passiert in Momenten des Staatsübergangs?

6502 empfängt Taktimpulse am Eingang CLK0, puffert sie und sendet sie an zwei Ausgänge: CLK1 und CLK2. Obwohl alle Ereignisse im Mikroprozessor an CLK1 gebunden sind, nehmen wir an, dass die Verzögerung gering ist, und sie sind an CLK0 gebunden - die Linie, entlang der der Mikroprozessor Taktimpulse von Arduino empfängt. Und nenne das Signal nur CLK.



1. CLK ändert den Status von eins auf null.

2. Der Mikroprozessor gibt eine neue Adresse an den Adressbus und ein Schaltsignal zwischen Lesen und Schreiben an den R / W-Ausgang aus. Aber er ist noch nicht bereit für den Datenaustausch.

3. CLK geht in den Einheitszustand über, und dies bedeutet, dass der Datenaustausch begonnen hat. Wenn dies eine Leseoperation ist, überträgt der Mikroprozessor die Datenbusausgänge in den Eingangszustand und empfängt Daten, und wenn die Schreiboperation, überträgt er sie in den Ausgangszustand und sendet Daten. Und das R / W-Signal schaltet das externe Gerät in den Schreib- oder Lesemodus, das Gegenteil des entsprechenden Zustands des Mikroprozessors.

4. CLK geht auf Null. Jetzt geben weder der Mikroprozessor noch die Eingabe- / Ausgabegeräte etwas an den Datenbus aus. Der Mikroprozessor kann die Datenbusleitung und den R / W-Pin in einen neuen Zustand versetzen.

Eine einfache Erklärung, die für das Kind verständlich ist. Wer nie an diese "Intrigen hinter den Kulissen" denkt, der programmiert nur Mikrocontroller. Auch im Assembler.

Wenn Sie Ihr Peripheriegerät anschließen müssen, muss es Zeit haben, die Daten vorzubereiten, bevor das Gerät (Vorbereitungszeit) in der CLK-Zeile angezeigt wird. Wenn das Gerät dort ist, ändern Sie es nicht. Wenn das Peripheriegerät keine Zeit hat, die Daten vorzubereiten, während CLK Null ist, oder sie ändert, wenn das Gerät vorhanden ist, werden Sie sich lange fragen, warum Ihr Code nicht funktioniert. Da die Taktfrequenz des Mikroprozessors zehn- bis fünfzehnmal niedriger als die Nennfrequenz ist, ist es einfach, diese Anforderung zu erfüllen. Aber es ist notwendig.

Wir müssen also Arduino „beibringen“, Taktimpulse zu erzeugen, kontinuierlich zu überprüfen, was auf dem Adressbus und der R / W-Leitung geschieht, und entsprechend mit dem Datenbus interagieren. Zu diesem Zweck verwendet die Skizze den Timer1-Timer-Interrupt, der Impulse mit einer Frequenz von 95 kHz erzeugt. Arduino arbeitet viel schneller als der Mikroprozessor und kann daher zwischen seinen Uhren alles lesen und vorbereiten. Es ist wichtig sicherzustellen, dass diese Bedingung nach dem Ändern der Skizze weiterhin erfüllt ist.

Hier ist ein Auszug aus der Skizze, der zeigt, wie der CLK von null auf eins geht und was als nächstes passiert:

 //////////////////////////////////////////////////////////////////// // 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; } 

Die Zuweisung des Adressraums kann auf beliebige Weise erfolgen. In einer unveränderten Skizze ist dies dieselbe wie in Apple 1 mit 256 Byte ROM, 8 Kilobyte ROM für BASIC, 4 Kilobyte RAM und 6821 Eingabe- / Ausgabegerät.

 // 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 }; 

RAM wird durch Byte-RAM-Array [RAM_END-RAM_START + 1] emuliert. Es werden zwei PROGMEM-Schlüsselwörter benötigt, damit der Inhalt emulierter ROMs im Flash-Speicher des Mikrocontrollers gespeichert wird.

6821 ist so emuliert, dass die virtuelle Tastatur und Anzeige über das „Terminal“ funktionieren. Woz Monitor und BASIC arbeiten, was der Autor suchte.

Um ein Peripheriegerät zu emulieren, müssen Sie dessen Datenblatt sorgfältig lesen und herausfinden, über welche Register es verfügt und wofür sie bestimmt sind. Der Komfort der Emulation liegt in der Flexibilität, mit der Sie Software-Analoga der Peripherie erstellen können.

E / A-Geräte befinden sich im Adressraum des Mikroprozessors und werden wie Speicherzellen aufgerufen. Um die "Eisen" -Peripheriegeräte wie ein LCD-Display, eine Speicherkarte und eine Tonausgabe zu verwenden, müssen Sie einen Platz im Adressraum zuweisen.

Referenzen:

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

Gehen Sie zu 6809, es enthält:

Zwei 8-Bit-Batterien A und B, die zu einer 6-Bit-Batterie kombiniert werden können
Zwei 16-Bit-Stapelindizes
Adressierung relativ zum Anweisungszähler
Addiere oder subtrahiere automatisch 1 oder 2
Multiplikation von zwei achtstelligen vorzeichenlosen Zahlen
16-Bit-Arithmetik
Übertragung und Austausch von Daten zwischen allen Registern
Schreiben und Lesen aller Register und einer beliebigen Kombination davon

Der Mikroprozessor 6809E (extern) benötigt eine externe Uhr, während der 6809 eine interne hat. Für Hitachi heißen sie 6309E bzw. 6309 und unterscheiden sich von den üblichen darin, dass sie innerhalb der Operation in 32-Bit-Form arbeiten. Es ist jedoch möglich, mit der klassischen Version in den Kompatibilitätsmodus zu wechseln.

Eigentlich begann das gesamte RetroShield-Projekt, weil der Autor seinen selbstgebauten Computer Simon6809 aktualisieren und das Ergebnis Simon6809 Turbo nennen wollte. Es stellte sich jedoch heraus, dass Standard-Logik-Chips für alles, was er dort implementieren wollte, viel erfordern würden. Daher formulierte der Autor die Idee von RetroShield zum ersten Mal speziell in Bezug auf 6809 und dachte erst dann: „Was ist, wenn dasselbe mit anderen Prozessoren dasselbe tut?“.

Das Gerät verwendet natürlich den 6809E, für den eine externe Uhr erforderlich ist, damit es seine Arbeit von außen synchronisieren kann. Die E- und Q-Leitungen für beide Prozessoren haben den gleichen Namen, nur 6809 haben Ausgänge und 6809E haben Eingänge.

Arduino interagiert mit 6809 auf die gleiche Weise wie mit 6502, verfügt jedoch über zwei Takteingänge: E und Q und drei Interrupt-Eingänge: IRQ, FIRQ und NMI.



Dieses Mal ist die Entsprechung zwischen den Arduino-Ports und den Mikroprozessor-Pins wie folgt konfiguriert:

 /* 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 

Wie aus den Diagrammen ersichtlich ist, ist das Signal Q relativ zu E um ein Viertel der Periode verschoben:

Wir werden Q kaum beachten, da alle Ereignisse an E gebunden sind. Und alles passiert so:



  1. E schaltet auf Null. Der Prozessor legt eine neue Adresse auf dem Adressbus fest und ändert den Status der R / W-Leitung.
  2. E schaltet auf eins, der Prozessor wird für den Datenaustausch bereit.
  3. Es spielt keine Rolle, was mit dem Datenbus passiert, solange E eins ist. Hauptsache, die erforderlichen Daten sind dort vorhanden, wenn E auf Null zurückkehrt.
  4. Beim Lesen von Daten muss das E / A-Gerät die erforderlichen Daten an den Datenbus liefern, bevor die Leitung E von eins nach null geht (die minimale Verzögerung wird durch die Zahl 17 im Kreis angezeigt).
  5. Bei der Aufzeichnung muss das E / A-Gerät die Daten in einem Register in der Form fixieren, in der sie sich zum Zeitpunkt E von eins auf null befanden. Der Prozessor wird diese Daten noch früher auf dem Bus bereitstellen - zum Zeitpunkt des Übergangs von Q zu eins (die Nummer 20 im Kreis).
  6. Nach dem Übergang von E zu Null wiederholt sich alles.

Alles, was oben über 6502 über die Notwendigkeit eines Peripheriegeräts (einschließlich eines virtuellen) gesagt wurde, um alle Signale rechtzeitig zu entwickeln, betrifft 6809.

Erzeugung der Signale E und Q, wie im Fall von 6502, mit dem einzigen Unterschied, dass es zwei Signale gibt, und sie müssen gemäß den Graphen geschaltet werden. Und genau so führt eine bei Unterbrechung aufgerufene Unterroutine die Dateneingabe oder -ausgabe zu den erforderlichen Zeitpunkten durch.

Der Adressraum in der unveränderten Skizze wird auf dieselbe Weise verteilt wie auf dem selbstgebauten Simon6809- Computer:

 // 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 }; 

RAM und ROM werden auf die gleiche Weise wie in der 6502-Variante in Arrays gespeichert, mit dem einzigen Unterschied, dass es nur ein Array mit ROM-Daten gibt.

E / A-Geräten werden auch Teile des Adressraums zugewiesen, und sie können entweder virtuell oder real sein. Da Simon6809 eine moderne Maschine ist, die auf einer Vintage-Elementbasis basiert, tauscht sie Daten über FTDI von dem PC aus, auf dem das „Terminal“ ausgeführt wird. Hier wird es emuliert.

Referenzen:

Viele Informationen zu 6809 auf der Arto-Seite
Wikipedia-Artikel über 6809
SWTPc 6809-Systeme
Wikipedia-Artikel zum FLEX-Betriebssystem

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


All Articles