8-Bit Computer auf Steckbrettern mit einem WDC-65C02 Prozessor

Der MOS-6502 Prozessor startete die Heimcomputer-Revolution. Er war das Herzstück des Apple II, des Commodore 64, des Nintendo Entertainment Systems (NES) und vielen anderen Geräten. Western Design Center brachte 1983 eine verbesserte Version des 6502 raus, den WDC 65C02. Dieser hat unter anderem ein komplett statisches Design, man kann die Computer-Clock also beliebig langsam laufen lassen. Welchen besseren Prozessor gibt es also, um selbst mal einen Computer zu basteln? Das dachte sich der englisch-sprachige YouTuber Ben Eater, der genau das gemacht hat. Ich wollte das dann auch mal ausprobieren, um den Ablauf der Prozesse in einem Computer besser verstehen zu können (und weil es natürlich schon cool ist, einen 8-Bit Computer auf Steckbrettern zu bauen!)

8-bit Computer auf Steckbrettern mit WDC-6502 Prozessor

Die Verwendeten Teile

In Deutschland kommt man leider nicht ohne weiteres an einen WDC-65C02 ran. Diesen hab ich nicht bei Reichelt, Conrad und Co gefunden. Daher habe ich den direkt von Mouser aus den USA bestellt. Da zahlt man bei einem Bestellwert unter 50€ satte 20€ Versand, es ist also sinnvoll die 50€ voll zu machen. Ich hab einfach alle Teile doppelt bestellt, dann kann auch mal was kaputt gehen. Man braucht einige Widerstände, Kondensatoren, LEDs, mindestens drei Steckbretter und natürlich auch einiges an Steckbrücken. Damit am Ende alles möglichst ordentlich aussieht, lohnt es sich die Steckbrücken selber zu machen. Dazu eignet sich isolierter Schaltdraht mit 0,20 mm² Querschnitt. Die wichtigsten Teile sind allerdings:

  • W65C02 Prozessor
  • W65C22 Versatile Interface Adapter, zum Anschließen von Peripheriegeräten
  • AT28C256 EEPROM, 32K x 8, als Speicher für die Programme
  • CY6226N SRAM, 32K x 8, als Arbeitsspeicher für den Computer
  • Ein LCD-Display Modul (in meinem Fall E164B-NLW mit 4 x 16 Zeichen)
  • Logik-Chips (AND, XOR, NAND, ...), am besten mit Schmitt-Trigger (geht aber meistens auch ohne)
  • Einen Kristall-Oszilattor (z.B. 1 MHz) oder 555-Timer Chips
Beim beschaffen der Teile muss man auf einige Dinge achten:
  1. Das richtige Gehäuse: Da der PC auf Steckbrettern gebaut wird, benötigen bei allen Komponenten DIP oder DIL Gehäuse und keine SMD-Teile (SO-Gehäuse)
  2. Die richtigen Betriebsspannungen: Alle Teile sollten für 5V DC geeignet sein. Bei den Logikgates vor allem darauf achten, dass die angegebenen Spannungen für logisch "High" und logisch "Low" mit den anderen Komponenten zusammenpassen. Ich hatte NAND Gates gekauft, bei deren minimale Ausgangsspannung bei logisch "High" zu niedrig war und von den anderen Teilen als "Low" interpretiert wurde.
  3. Geeignete Kabel für die Steckbrücken: Die Kabel sollten einen Querschnitt von ca 0,20mm² haben und aus einem einzelnen Draht mit Isolierung bestehen (also keine Litzen!). Am besten man holt sich auch Kabel in verschiedenen Farben, um den Überblick nicht so schnell zu verlieren.
Man benötigt zudem:
  • Eine 5V DC Stromversorgung
  • Einen EEPROM Programmierer
  • Einen Logik-Analyser

... oder man nutzt einfach einen Raspberry Pi 4, mit dem man all das auf einmal hat (und für sehr viel weniger Geld!). Alternativ kann man auch einen Arduino nutzen, ich hatte jedoch schon einen Raspberry Pi 4 da, der sich mit seinen 26 General Purpose Input-Output (GPIO) vielseitig einsetzen lässt.

Aufbau des Computers

Wer hier genaues wissen will sollte sich auf jeden Fall Ben Eater's Videoreihe dazu anschauen, da wird alles bestens erklärt!

Die Clock ⏰

Als erstes benötigt man einen Taktgeber (Clock), der das Timing aller im Computer ablaufenden Prozesse steuert. Um wirklich Schritt für Schritt sehen zu können was passiert, sollte die Taktfrequenz niedrig und variabel sein. Mit einem 555-Chip und einem Potentiometer lässt sich schnell eine astabile Kippstufe mit variabler Frequenz (bei mir 0,5-200 Hz) bauen. Für noch mehr Kontrolle baut man am besten noch eine Monostabile Kippstufe (auch: Taster) um den Takt per Knopfdruck anzugeben. Alle Schalter und Taster sollten dabei bestenfalls "debounced" werden, da es sonst zu mehreren Pulsen pro Knopfdruck kommen kann.

Adress-Dekodierung 🔀

Der Prozessor hat 16-Adresspins und kann damit 2^16 = 65536 Adressen (von 0 bis FFFF) ansteuern. Beim Start sucht er an Adresse FFFC und FFFD nach dem "Reset-Vektor", der Anfangsadresse des Programms. Daher sollte der EEPROM (der das Programm enthalten soll) an dieser Adresse erreichbar sein. Mit ein paar Logik-Gates baut man dann einen Adress-Dekodierer, welcher an die Chip-Enable Pins von EEPROM, RAM und IO-Adapter angeschlossen wird. In meinem Fall sind die Adressen wie folgt verteilt:

  • 0x0000 - 0x5FFF: SRAM
  • 0x6000 - 0x6FFF: IO-Adapter 1
  • 0x7000 - 0x7FFF: IO-Adapter 2
  • 0x8000 - 0xFFFF: EEPROM

Die Adapter brauchen zwar jeweils nur 15 Adressen, belegen hier aber 4096. Das klingt zwar verschwenderisch, jedoch spart man sich so komplexität und Logik-Gates bei der Adress-Dekodierung. Der einzige Nachteil ist, dass man nur noch xxxx KB statt xxxxKB Arbeitsspeicher zur Verfügung hat, was aber bei den Programmen die hier laufen werden kein Problem sein wird.

Zusammenbau

Hat man die Clock gebaut, kann man dem eigentlichen Computer anfangen. Eigentlich muss man erstmal "nur" alle Pins des Prozessors so anschließen, dass dieser aktiviert ist (also 5V, GND, BUS Enable, Ready, Clock und Interrupt Request). Danach kann man den EEPROM (Electronically Erasable Programmable Read-Only Memory) anschließen und Adress- und Daten/Input-Output Pins von EEPROM und Prozessor mit einander verbinden. Das gleiche macht man auch mit dem SRAM (Static Random Access Memory). Das "Static" ist hierbei sehr wichtig, es bedeuted der Speicher verliert seine Informationen nicht einfach so (Dynamic RAM muss man quasi öfter "auffrischen", womit man sich bei einem Homemade-Computer nicht rumschlagen will!).
Vor dem verbauen (noch besser: vor dem kaufen) eines Teils sollte man das Datenblatt lesen, um sicherzustellen dass es mit den anderen Funktionieren wird. Am wichtigsten sind hierbei die Timing-Diagramme für Schreib- und Leseprozesse.

Programmierung & Debugging mit einem Raspberry Pi

EEPROM Programmieren

Hat man alles aufgebaut, kann man Anfangen Programme zu schreiben. Ein Programm besteht dabei immer aus einem OP-Code (z.B. "ldx": etwas ins X-Register laden, "inx": den Wert im X-Register um 1 erhöhen, etc) gefolgt von einem Argument. Das kann (je nach OP-Code) entweder eine Adresse oder ein Zahlenwert sein. Schließlich muss man eine Binär-Datei erstellen, die immer aus dem Binärwert des Op-Codes und dem Binärwert des Arguments besteht. Der Prozessor arbeitet die Anweisungen dann der Reihe nach ab.
Den Maschinencode per Hand zu schreiben ist natürlich sehr aufwendig, weshalb man einen Assembler nutzt (hier: vasm). Dann kann man den Programmcode in einer Textdatei schreiben und ihn dann zu einer Binärdatei assemblen lassen. Jetzt muss man nur noch die Datei auf den EEPROM spielen.
Dazu wird, wie angekündigt, ein Raspberry Pi 4 genutzt. Für diesen habe ich ein Python-Programm geschrieben, welches den EEPROM lesen, resetten und manuell oder mit einer Datei beschreiben kann. Das Programm findet ihr in meinem 6502 GitHub Repository.

EEPROM Simulieren

Da man bei jeder Änderung des Codes den EEPROM neu beschreiben muss, können dessen Pins schnell kaputt gehen wenn man ihn oft zwischen Programmier-Steckbrett und Computer umsteckt. Dafür habe ich einen "EEPROM Simulator" für den Raspberry Pi geschrieben. Anstelle des EEPROMs verbindet man den RPi über die GPIO Pins an den Adress- und Datenbus des Computers und startet das Programm. Der Computer kann dann das Programm vom Raspberry Pi lesen anstatt vom EEPROM.

Monitoring des Computers

Der Raspberry Pi kann auch als Logik-Analyzer eingesetzt werden, um genau sehen zu können was auf dem Adress- und Datenbus ist sowie ob der Prozessor gerade vom BUS liest oder schreibt. Das Programm ist ebenfalls im GitHub-Repo.

Anschließen eines LCD-Displays 🖥

Um den Computer einen Sinn zu geben, sollte er irgendwie mit dem Nutzer interagieren können. Dazu braucht man eine Art Display. Für den Anfang reichen 8 LEDs um einzelne Bytes darzustellen, allerdings will man später schon coolere Sachen machen. Dafür eignet sich dann kleines LCD-Display, wie man es z.B. von Radios oder alten Kassen kennt. Das Display lässt sich über 11 Pins ansteuern, 8 IO-Pins und 3 Control Pins. Dazu werden die IO-Pins an Port B des W65C22 IO-Adapters angeschlossen, die Control Pins an Port A. Der Rest erfolgt über Software. Mein Assembly-Code zur Darstellung eines 64-Zeichen langen Textes ist ganze 80 Zeilen lang geworden. Dafür hat er auch einige Features: Automatische Zeilenumbrüche indem die Zahl der gesendeten Zeichen gezählt und die RAM-Adresse des Displays daran angepasst wird. Außerdem funktioniert der Code bei allen Clock Speeds, da vor jeder Anweisung an das Display geprüft wird, ob es noch beschäftigt ist (Busy-Flag-Check). Der Code ist auch im GitHub-Repo.