Schlagwort-Liste: Simulator 4-Bit-Rechner - allgem. Beschreibung - Prozessor - Speicher - E/A-Realisierung - Befehle - Beispiel

E mulierter R echner N r A 1 (ERNA1)

Technische Beschreibung

Eigentlich ist es fr einen Simulator nicht notwendig eine technische Beschreibung zu erstellen, es wird ja ohnehin alles nur simuliert.
Das Ziel ist jedoch, nicht nur die Programmierung auf dieser Ebene kennen zu lernen, sondern gleichzeitig in die Architektur, in den technischen Aufbau, eines Rechners einzudringen. Die Beschreibung geht deshalb sehr weit in die Technik, bis hin zum Mikroprogramm und zu einzelnen Speicherelementen. Das muss man zum Programmieren nicht unbedingt wissen, erleichtert aber das Verständnis fr notwendige Befehlsabläufe.

Sehr nützlich ist, wenn man sich deshalb einwenig in der Digitaltechnik auskennt - die kleinen Kurse kann man gut dazu nutzen.

  1. Digitale Schaltungen - ganz einfach ausprobieren
  2. Digitale Schaltungen: NAND/NOR / DTL - TTL - MOSFET - CMOS
  3. Digitale Schaltungen - RS-Flip-Flop
  4. Digitale Schaltungen - selbst erstellen
  5. Digitale Schaltungen: Flip-Flops/Zähler/Teiler/Register
  6. Digitale Schaltungen: Multiplexer

Wenn man mit der folgenden Beschreibung ein Maschinen-Programm geschrieben hat, ist man ganz nebenbei ein Stück in die Rechnerarchitektur vorgedrungen. Natürlich ist vieles vereinfacht, jedoch werden grundlegende Teile erklärt.

Der Rechner besteht im Wesentlichen aus drei Grundelementen:


Des Weiteren, nicht sogleich sichtbar, sind verschiedene Ausgabevarianten, eine Eingabe und eine Interrupt-Möglichkeit vorgesehen. Das sind teilweise reine Softwarelösungen ohne technischen Hintergrund.

Prozessor RSR

Das Blockschaltbild zeigt den groben Aufbau des Prozessors.

Er besitzt

  • 4 Register und ein Hilfsregister
  • Leitwerk bzw. Steuerwerk
  • ALU
   

Register

Register sind prozessorinterne Speicherplätze mit speziellen Funktionen:

Befehlsregister

Befehlsregister BR ist ein 4 Bit breiter Speicher (es können 4 Bit gespeichert werden), es kann somit genau ein Wert aus dem Hauptspeicher aufnehmen. Das Register wird unidirektional betrieben, d.h. das Register kann nur Werte speichern, es kann aus ihm nicht gelesen werden.
Das Steuerwerk STW interpretiert diesen Wert generell als Befehl und startet ein internes Mikroprogramm zur Abarbeitung des Befehls.
Da es keine Extrakodierung für einen Befehl gibt, wird bei fehlerhafter Reihenfolge des Assemblerprogramms auch ein Datenwert als Befehl interpretiert.

Diesen Fehler muss der Programmierer vermeiden, nicht der Rechner!

Akkumulator kurz Akku

Der Akku ist ein 4 Bit breiter Speicher, er funktioniert bidirektional, man kann auf ihm Werte speichern und auch Werte aus ihm lesen.
Die Darstellung zeigt, dass sowohl der Bus als auch das Rechenwerk Zugriff auf das Register hat. Die Darstellung dazu ist vereinfacht, denn im realen Aufbau wird für die exakten wechselseitigen Zugriffe ein Umschalter gebraucht, das erledigt ein Multiplexer.
Der Akku wird vom Rechenwerk als Arbeitsspeicher, Hilfsspeicher und als Ergebnisspeicher genutzt (siehe Akku).
Aus dem Zustand des Akku können nach bestimmten Operationen, jedoch vorrangig aus dem Rechenwerks, Flags generiert (siehe Flags).
Prinzipiell kann der Akku gleiche Funktionen wie ein Speicherplatz im Hauptspeicher übernehmen, jedoch wird er bei der Durchführung von Operationen planmäßig überschrieben, so dass der Akku nicht für allgemeine Speicherzwecke genutzt werden kann.

Befehlszählregister oder kurz Befehlszähler BZ

Die Bezeichnung ist etwas unpassend, denn das Register zählt nicht die Befehle, organisiert die Adressenverwaltung des Hauptspeichers.

Üblicherweise braucht man zwei solcher Systeme, denn man muss zum Einen die Befehlsadresse bereitstellen, zum anderen aber auch die Adresse der Daten. In diesem kleinen System wird ein anderer Weg beschritten, indem zwischen beiden Zählern umgeschaltet wird, das Leitungssystem zum Speicher aber nur einmal angelegt wird. Das geht in der Realität auch, bereitet aber andere Probleme.

Der Befehlszähler BZ und der Hilfsbefehlszähler BZH sind jeweils 8 Bit breite Register (Speicher), BZ ist ein unidirektionaler Speicher, BZH ein bidirektionaler.

Warum 8 Bit breit?

Aus der Anzahl der Bits ist die Anzahl der möglichen Kombinationen bestimmt, also 24 gleich 16 Kombinationen gleich 16 Werte (0 15). Damit könnte man 16 Adressen für den Speicher ausgeben, das ist sicher zu wenig! Deshalb wurde die Anzahl der Bits verdoppelt, man kann somit auf 28 Kombinationen gleich 256 Werte zugreifen das könnte schon mal reichen.
Man muss sich aber im Klaren darüber sein, dass das Mehraufwand bei der Nutzung wird, es muss also immer zweimal in den Speicher gegriffen werden bis die Adresse komplett ist. Wie nun daraus eine sinnvolle Anordnung der 256 Speicherplätze erfolgen sollte ist in der Beschreibung des Hauptspeichers festgehalten. Die so gestalteten Speicher sollten immer die n-fache Breite des Basissystems haben (8, 12, 16 das gibt es real), was sicher einzusehen ist, denn ein z.B. 7 Bit breites Register macht noch viel mehr Arbeit.
Bei den Bildern zur Demonstration der Register BZ und BZR wird der Ausgang von BZH nur im ersten Bild dargestellt, in der Folge aber weggelassen.

Aufbau der Register

Beide Register sind in jeweils 4 Bit Speicher unterteilt, die einzeln beschrieben werden können (Verbindung zum Bus). Die Teile werden jeweils als höherwertiger Teil (hw) und niederwertiger Teil (nw) der Adresse bezeichnet. Aus dem hw des BZ wird die Zeile und aus dem nw die Spalte des Hauptspeichers abgeleitet (siehe Hauptspeicher).
Nur der BZ dient zur Adressierung des Speichers.

Der BZ (8 Bit) besitzt weiterhin die Eigenschaft immer um 1 weiter zu zählen (daher die Bezeichnung Zähler). Das kann man mit spezieller Registergestaltung erreichen oder man muss das Rechenwerk, einschließlich eines kleinen Programms, bemühen!

Es muss natürlich erreicht werden, dass

0000 1111 + 1 = 0001 0000

wird, also über die 4 Bit hinaus gerechnet wird.
Eine weitere Leistung beider Register ist, dass der 8 Bit Inhalt komplett getauscht werden kann.

Die Arbeitsweise soll an einem kleinen Beispiel dargestellt werden:

Die Adresse des BZ zeigt z.B. auf den Speicherplatz 20 (BZ = 0010 0000 beide 4
Bits werden einzeln interpretiert (es wird im hexadezimalen System gerechnet))
An der Stelle im Speicher (20) steht eine Bef.-Kodierung zum Transport eines Wertes in den Akku (siehe Befehlsliste)
Die Adresse, wo der Wert steht, ist in den beiden folgenden Speicherplätzen gespeichert, (21) = nw der Adr. des Wertes (1); (22) = hw der Adr. (3)
Der nächste Befehl steht auf Speicherplatz 23

Die Befehlskodierung wurde bereits in das Befehlsregister transportiert, BZ wurde um 1 auf 21 erhöht. Nun muss die Adresse des Wertes bereitgestellt werden.
Das kann nicht im BZ erfolgen, denn die Adresse 21 muss dort bestehen bleiben.
Jetzt kommt das BZH in Aktion:


Es wird der Schalter 4 geschlossen, damit wird der Wert vom Speicherplatz 21 auf BZHnw gespeichert. Nächster Schritt:


Der Schalter 4 wird wieder geöffnet und der Inhalt von BZ um 1 erhöht. Damit wird Speicherplatz 22 ausgewählt und das ist wie vereinbart der hw-Teil der Adresse des Wertes.
Nächster Schritt:


Jetzt wird der Schalter 3 geschlossen und der Wert von Speicherplatz 22 wird auf BZHhw gespeichert. Eigentlich haben wir nun die Adresse, jedoch muss wegen Einhaltung der Reihenfolge der BZ noch um 1 erhöht werden und zeigt damit auf den nächsten Befehl. Außerdem muss der Schalter 3 wieder geöffnet werden, damit auf BZHhw kein anderer Wert gespeichert wird. Nächster Schritt:


Wäre der Schalter nicht offen, würde jetzt der Inhalt von Speicherplatz 23 auf BZHhw gespeichert werden, das ist falsch, denn das ist der nächste Befehl. Ein weiterer Schritt ist jedoch noch wichtig:


Wir müssen die Adresse des Wertes (31) auswählen, das kann aber nur das BZ, also müssen BZ und BZH (komplett 8 Bit) getauscht werden (das ist wieder ein neues Problem lassen wir aber so stehen). Jetzt kann BZ beliebig verändert werden, die Adresse des nächsten Befehls ist im BZH gerettet! Ist der Befehl komplett bearbeitet, wird einfach wieder BZ mit BZH getauscht und es geht ordnungsgemäß weiter.

Flag-Register

In Abhängigkeit vom aktuellen Ergebnis des Rechenwerkes bzw. des Akkumulators wird das Flagregister verändert
Es wurden 4 Flags, die exakt an einer Stelle auf dem 4 Bit Speicher stehen vereinbart:

V C N Z

Mit der Bezeichnung:

An dieser Stelle muss man einige Erklärungen zu diesen Flags geben.
Grundsätzlich geben sie die Möglichkeit eine Programmverzweigung vorzunehmen, ohne diese wären also nur Geradeaus-Programme möglich.
Zum anderen kann sich der Programmierer über bestimmte Zustände bei der Bearbeitung informieren, d.h. Die Flags werden unabhängig vom Willen des Programmierers gesetzt, er muss sie bei Bedarf entsprechend auswerten!

Carry
Das Carry wird gesetzt, wenn der Hardwarebereich des Rechners überschritten wird, also

1111 + 0011 = 0010
Das Ergebnis ist 2, aber es gibt einen Übertrag C = 1. Ob man nun darauf reagiert oder nicht,der Rechner arbeitet unabhängig davon ohne Probleme weiter.

Zero
Das Zero wird gesetzt, wenn alle Bits des Ergebnisses 0 sind, also

1111 + 0001 = 0000
Das ist z.B. beim Rückwerts-Zählen wichtig, man hat das Ende erreicht.

Die beiden folgenden Flags werden nur der Vollständigkeit halber mit genannt. Sie stellen Informationen bereit, um mit negativen Werten rechnen zu können, d.h. wieder sie werden unabhängig von der Nutzung gesetzt.

Negativ
Um einen Wert als negativ zu kennzeichnen wird (üblicherweise geht eben auch anders) das 1.Bit eines Werten auf 1 gesetzt. Das ganze ist jedoch weit aus komplizierter, denn wir arbeiten nun mit negativen Werten nicht mit positiven Werten mit negativem Vorzeichen. Also:

0111 + 0010 = 1001
Interessieren keine negativen Zahlen, dann ist das der Wert 9, werden negative Zahlen genutzt, dann haben wir einen negativen Wert. Frage ist, wie groß ist denn nun der Wert?
Das ist etwas kompliziert:
Man bildet das 2-er Komplement der Zahl:
1001 => 0110 : 1-er Komplement (tauschen von 1 und 0)
0110 + 0001 = 0111 : 2-er Komplement = 1-er Komplement + 1
Das ist nun der positive Wert des Ergebnisses, also in uns vertrauter Form -7!

Noch ein Beispiel:

0111 + 0001 = 1000
0111 : 1-er Komp. von 1000
0111 + 0001 = 1000 : 2-er Komp.
Das Ergebnis ist -8.

1111 + 0001 = 0000
0000 : 1-er Kompl. Von 1111
0000 + 0001 = 0001 : 2-er Komp., also -1 + 1 = 0 das entspricht auch unseren Erwartungen

Allgemeiner gilt: 7 + 1 = -8 ... -1 + 1 = 0 usw.

Das hat meist schon jeder Programmierer festgestellt, Endlos-Zählschleifen bringen keine Probleme, denn der Wert wird irgendwann negativ und dann 0 und dann geht es wieder von vorne los. Und der Wertebereich geht in diesem Fall von -8 0 +7, das ist so richtig!

Überlauf Overflow V bzw. O
Es ist gar nicht so einfach das Problem zu beschreiben, es ist auch oft falsch. Überlauf bedeutet, dass der Wertebereich überschritten wird. Dieses Beispiel hatten wir schon:

0111 + 0001 = 1000

Diese Überschreitung kann man am Einfachsten durch Vergleich des Carrys mit dem internen letzten Übertrag ermitteln, denn immer wenn diese unterschiedlich sind, gibt es einen Überlauf:
   0111
+ 0001
---------
  01110     Übertrag Vorstelle
    1000     Summe Stelle

Nicht die Änderung des Vorzeichens ist ein Überlauf, denn beim 2. Beispiel gibt es auch eine Änderung:

   1111
+ 0001
---------
  11110     Übertrag Vorstelle
    0000     Summe Stelle

Die Realisierung der genannten Flags, es gibt viel mehr Flags, ist technisch nicht schwierig und sollte mit Kenntnissen der Digitaltechnik jedem möglich sein:


Zero lässt sich mit einem NOR realisieren, denn 0 v 0 v 0 v 0 = 0 und die Negation ist 1.
Das Vorzeichen entspricht einfach der ersten Stelle, wird einfach übernommen.
Das Carry ist der letzte Übertrag und das Overflow erhält man aus der Antivalenz von Carry und letztem Übertrag.
Damit ist auch klar, dass C und V nur im Addierer zu ermitteln sind, bei der Aktion, die Schaltung N und Z kann man auch an ein Register, z.B. den Akku, anfügen, dann könnte man auch den dort vorhandenen (geladenen) Wert dahingegen abfragen, man braucht aber entsprechende Befehle.

Rechenwerk ALU

Die Bezeichnung Rechenwerk für diesen Block ist eigentlich zu wenig, besser ist die Bezeichnung Arithmetisch-logische Einheit, denn neben den arithmetischen Operationen muss dieser Block auch logische Operationen ausführen können. Unser kleiner Prozessor soll neben den arithmetischen Funktionen Addition und Subtraktion auch die logischen Operationen AND, OR und XOR ausführen können.

An dieser Stelle soll eine kleine Erklärung zur
Funktion dieser Einheit erfolgen.
Die ALU braucht zumeist 2 Werte zur Bearbeitung.
Das Problem ist aber nun, dass beide Werte im
Hauptspeicher stehen und zur gleichen Zeit über
den Datenbus bereitgestellt werden müssten, das
geht nicht. Beim BZ haben wir schon gesehen,
dass nur eine Speicheradresse bereitgestellt
werden kann.
Die Lösung ist, die Werte hintereinander über
den Bus zu transportieren. Das bedeutet aber,
dass der erste Wert im Prozessor gespeichert
werden muss. Diese Funktion übernimmt der Akku,
Schalter 3 geschlossen Multiplexer auf Bus.
Ist der Wert Übertragen, wird Schalter 3 wieder
geöffnet.
Der zweite Wert wird vom Speicher auf den Bus
gelegt, wird nun Schalter 1 geschlossen, kann
die ALU darauf zugreifen, der erste Wert steht
am Akku ebenfalls bereit.
   

Das Ergebnis kommt oben heraus und wird über den umgeschalteten Multiplexer wieder dem Akku zugeführt. Die Problematik liegt auf der Hand, mit Bereitstellung des Ergebnisses muss der Akku-Ausgang weggeschaltet werden und der Speicher wird neu beschrieben. Das geht nur mit bestimmter Taktfolge.
Jetzt besteht die Möglichkeit das Ergebnis mit einem weiteren Wert aus dem Speicher zu verknüpfen oder das Ergebnis über Schalter 2 auf den Bus zu legen und dann im Hauptspeicher zu speichern.

An diesem Beispiel sieht man, dass ein Reihe von Steuersignalen zum ordnungsgemäßen Ablauf erzeugt werden müssen und genau das ist die Aufgabe des Steuerwerkes!

Steuerwerk / Leitwerk

Das Steuerwerk, auch als Leitwerk bezeichnet, wird im Simulator nur teilweise exakt nachgebildet. In diesem Teil erfolgt die Steuerung des Ablaufes der Bearbeitung und die Verbindung von Soft- zur Hardware.

Es ist ein wesentliches Teil zur Funktion eines Rechners.

Zur Abarbeitung eines Befehls wird ein vorbereitetes Mikroprogramm gestartet. Die Bezeichnung Programm ist an dieser Stelle etwas hoch gegriffen, es ist nur das schrittweise Abarbeiten eines Schaltwerks, was z.B. die schon genannten Schalter steuert, so wie ein Schrittschaltwerk in einer Waschmaschine, mit dem in jedem Schritt genau definierte Aufgaben erledigt werden. Das sind sehr simple Aufgaben, wie Wasserventil auf, Heizung an usw. so auch im Mikroprogramm, eigentlich nur das Bedienen von Schaltern.

Um diesen Ablauf etwas zu verdeutlichen, soll einmal ein Maschinenbefehl zum Transport des Inhaltes eines Speicherplatzes des Hauptspeichers in den Akku sehr ausführlich beschrieben werden:


Der Befehl steht auf 3 Speicherplätzen im Hauptspeicher. Das ist richtig der erste Speicherplatz 20 enthält den Befehlscode, in diesem Fall ist das 0 (0000).
Die Frage ist nun, wo denn der Wert steht?
Unser Rechner hat kein weiteres Register, also muss er im Hauptspeicher stehen. Da gibt es zwei Möglichkeiten: Die Adresse muss, das haben wir oben so festgelegt, 2 mal 4 Bit (2 Nibble) also 1 Byte lang sein. Das kriegen wir nur hin, indem dem Befehlscode 2 Werte in den nächsten beiden Speicherplätzen folgen. Die Werte hat der Programmierer da hin zu bringen!
Festgelegt wird, dass der erste folgende Speicherplatz den niederwertigen Teil der Adresse beinhaltet, der zweite den höherwertgen.
Der nächste Befehl steht somit 3 Speicherplätze nach dem jetzigen Befehl, auf Platz 23
Der Ablauf ist oben beschrieben.
Da der BZ schon auf 20 steht wird der Speicherplatz ausgewählt.
Nun wird der Inhalt auf den Daten-Bus gelegt (der Schalter vom Speicher zum Bus geschlossen), im nächsten Schritt der Eingang vom BR ebenfalls (wieder Schalter zu). Nun wird der Wert übertragen. Damit alles wieder in den Ursprung versetzt wird, müssen die beiden Schalter wieder geöffnet werden dieser Vorgang mit anderen Speichern wiederholt sich nun mehrmals (siehe Tabelle).
Die ersten 6 Befehle, man muss nun unterscheiden zwischen Makrobefehlen, das ist der Befehl einen Wert in den Akku zu transportieren und den Mikrobefehlen, die den Makrobefehl in kleinen Schritten realisieren, also nun besser, die ersten 6 Mikrobefehle wiederholen sich bei jedem Befehl, dann steht der Befehlscode im BR und dann erst beginnt die Bearbeitung des eigentlichen Befehls.

Die Transporte erfolgen offensichtlich etwa immer nach gleichem Schema:

Da gib es noch zwei Probleme:

Dazu müssen wir einfach noch tiefer ins System steigen und die Hardware zum Speichern bemühen:


Speicher sind elektronisch gesehen Latch, in diesem Fall nutzen wir taktzustandsgesteuerte D-Latch (haben einen Eingang, bei D = 1 wird 1 gespeichert, bei D = 0, 0 aber nur wenn T = 1 ist, sonst ist er im Speicherzustand; weitere Informationen findet man unter Digitale Schaltungen Grundlagen / D-Latch ). Am Ausgang Q steht der gespeicherte Wert bereit, kann aber nicht auf den Bus, da die Schalter offen sind.
Nun dürfte klar sein, schaltet man den Ausgang Q auf den Bus ist der Speicher Sender (Quelle), schaltet man D auf den Bus ist er Empfänger (Senke).

Um nun herzufinden wie denn der Datentransport erfolgt, führen wir das Beispiel weiter:


QC wird auf den Bus gelegt, der Schalter von QC wird geschlossen, damit steht überall die 1 bereit, nur alle anderen Schalter sind offen.


Schalter DA wird geschlossen. Die 1 liegt an D, Speicher übernimmt ihn aber noch nicht, da T immer noch 0 ist.


Die 1 an D ändert nun den Zustand von A. Damit ist aber das zweite Problem geklärt, der Ausgang eines Speichers steuert den Eingang eines anderen Speichers. Hat man 4 solcher Anordnungen dann ist für unseren Rechner der Transport des Wertes erfolgt es gibt keinen Extrabefehl, man muss nur die Einschwingzeit abwarten und das macht man durch das Taktsignal, das ja nicht zugleich mit den Schaltern gesetzt wird.

Nun dürfte auch klar sein, dass der Inhalt des Empfängers durch den neuen Wert überschrieben wird, der alte Wert geht verloren und aber der Sender seinen Wert nach wie vor behält.
Die nächsten Schritte sind sicher auch klar, es muss das Taktsignal auf 0 gestellt werden, der Schalter DA und QC geöffnet werden. Das erfolgt alles zumeist nacheinander. Die neue Situation sieht nun so aus:


Der Elektroniker fragt sich sofort, wie solche Schalter realisiert werden. Das könnte ein Relais sein, das ist aber viel zu groß und zu langsam, wäre aber trotzdem gut gleich.
Elektronische Schalter gehen mit logischen UND-Gliedern:


Das ist relativ einfach zieht aber ein neues Problem hinter sich:

    Unser Beispiel oben zeigt aber nun, dass wir genau das tun was verboten ist:

Niemals Ausgänge parallel schalten!

Genau das tun wir aber mit der Anordnung, z.B. werden an den Ausgängen von QC und QB unterschiedliche Pegel angeboten. Da die Ausgänge relativ niederohmig sind, wird also durch beide ein zu großer Strom fließen, der die AND- Glieder zerstören wird. Deswegen zuvor die Aussage ein Relais wäre gut, das hat kein Problem damit. Wir brauchen eine andere Lösung.


Die Losung ist, die Gatter am Ausgang mit einem Transistor zu versehen der nicht komplett beschaltet ist, beim bipolaren Transistor lässt man den Kollektor-Widerstand weg, man spricht dann von einer Open-Kollektor-Schaltung:

    Die Kollektoren der Gatter werden alle parallel geschaltet. Wird ein Transistor mit 1 angesteuert, wird sein Widerstand klein, er legt den Ausgang auf 0. Negiert man das Signal kommt 1 heraus, das ganze wirkt nun wie ODER. Genau das wollen wir, der oder der Ausgang legt eine 1 auf den Bus, 0 zur selben Zeit geht nicht.

Noch nicht geklärt ist wie die Schalter ausgewählt und angesteuert werden. Wir wissen schon, dass Schalter geschlossen eine 1 sein muss, nur dann wird der Wert durchs AND-Gatter durchgestellt. Und wir haben oben schon mal angedeutet, dass ein Schrittschaltwerk gebraucht wird. Das kann man folgendermaßen lösen:


Man legt ein Kreuzschienensystem an. Die Zeile ist dabei die Teilleistung die für die Realisierung des Befehls erbracht werden soll. Genauer, eine Zeile realisiert einen Mikrobefehl, alle notwendigen Mikrobefehle zusammen sind ein Mikroprogramm zur Realisierung eines Makrobefehls.
An den Kreuzungspunkten gibt es mögliche Verbindungen zu den senkrechten Leitungen (Spalten). Diese Leitungen führen genau zu den AND-Gattern, die als Schalter fungieren.
Wenn also auf die erste Zeile eine 1 gelegt wird kommt bei S2 eine 1 heraus, also Schalter zu.
Es funktioniert noch nicht richtig, das Problem ist links schon mal gezeigt:

   

Die Verbindung zwischen Zeile 2 und Spalte 3 sorgt dafür, dass wenn nur Zeile 1 ausgewählt ist auch die Zeile 2 eine 1 erhält und damit geben alle Leitungen eine 1 aus.
Die Lösung geht mit Dioden in der Verbindung, nun wird Zeile 2 nicht mehr versorgt, da die Diode in Leitung 23 jetzt sperrt.
Ok Problem gelöst, nun muss man ein Leitungssystem für alle Schalter aufbauen, das ist für unseren kleinen Rechner schon ganz schön groß, für große Rechner gewaltig.
Für den Transport des 1.Wertes nach BZHnw könnte folgendes System gelten:


Das ist nun unser Schrittschaltwerk, der Reihe nach wird eine 1 links an die Zeilen gelegt. Die Spalten sind die Schalter. Hier wurde schon minimiert, denn der Akku taucht nur einmal auf, aber zwei Schalter sind notwendig. Dafür aber die Festlegung zum oder vom Bus. Vor Ort wird nun eine kleine Logik gebraucht, denn z.B. der Akku wird Sender (Ausgang an Bus) wenn Akku=1 UND zum Bus=1 ist. Eine weitere Vereinfachung geht, wenn auch die Register codiert werden, mit 4 Bit schafft man ja 16 Möglichkeiten, das Register muss dann aber vor Ort durch einen Multiplexer wieder ermittelt werden. Was besser ist, kann man so nicht sagen.
  1. (Mikrobefehl) schaltet den Speicherplatz <BZ> als Quelle an den Bus
  2. schaltet BZHnw als Senke an den Bus
    warten für Übertragung
  3. BZhnw vom Bus trennen
  4. Speicher <BZ> vom Bus trennen

Im unteren Teil wird das Programm binär beschrieben (1 Verbindung; 0 keine). Damit kann man den Mikrobefehl durch eine Dualzahl beschreiben, besser ist es jedoch Hexadezimal:

Zeile 2: 00 1101 0001 0000 0000(2)=> 0d100(16)

Nicht geklärt ist, wie die Weiterschaltung in die nächste Zeile erfolgt?
Das kann ein Zähler sein, das kann auch das System selbst erledigen, denn der erste Teil in der Zeile ist schon die nächste Adresse, in Zeile 1 zeigt er schon auf Adresse 2 usw. Das erfolgt mit einer getakteten speziellen Elektronik, anderenfalls würde das System mit großem Tempo durchlaufen.


Noch etwas ist zum Steuerwerk zu vermerken. Aus der Kodierung des Wertes im Befehlsregister wird im Steuerwerk in einer Tabelle der Startwert für das jeweilige Mikroprogramm ermittelt, dann beginnt die Abarbeitung. In der höheren Programmiersprache würde das ein Array erledigen, auf Array-Platz 0 steht die Adresse für das Mikroprogramm zur Realisierung von LDA, LDA aus der Sicht des STW ein Makrobefehl.

Soviel zum Steuerwerk des Rechners.

Befehlsliste

Mit den Bezeichnungen:
   


Das ist alles an Befehlen die man zur Programmierung nutzen kann.
Wie das geht, soll unten gezeigt werden.

Hauptspeicher / Speicher

    Der Hauptspeicher besteht aus
16 x 16 = 256 Speicherplätzen zu je 4 Bits (1 Nibble).
Diese Speicherplätze werden als Matrix angeordnet. Jeder Speicherplatz lässt sich lesen und bespeichern. Die Funktion ist für alle Plätze gleich.
Allerdings gibt es ein paar zusätzliche Festlegungen, die dann im Prinzip doch eine Einschränkung der freien Nutzung einiger Speicherplätze nach sich ziehen. Z.B. wird als erste Aktion beim Start eines Programms der Inhalt der Speicherplätze fe (nw) und ff (hw) in den BZ geladen, das entspricht der Startadresse. Damit besteht die Möglichkeit das Programm ab einer beliebigen Stelle im Speicher beginnen zu lassen das ist sehr gut.
Auf den Speicherplätzen fa und fb wird vom Mikroprogramm die Rücksprungadresse bei der Nutzung eines Unterprogramms oder Interruptroutine die Adresse für die weitere Bearbeitung abgelegt.
Auf den Plätzen fc und fd steht für einen möglichen Interrupt die Startadresse der Service-Routine.

Noch eine Bemerkung zur Rücksprungadresse (fa, fb), es gibt nur die Möglichkeit eine Adresse zu speichern, heißt, es kann nur ein Unterprogramm zur Zeit bearbeitet werden. Wie sonst üblich, ein Stack zur Speicherung, ist nicht vorhanden, der Speicherplatz fehlt dazu.

Die Speicherplätze 00 bis 08 sind für die Ein- und Ausgabe vorgesehen:

Die Funktion wird unten beschrieben.

    Die Matrixanordnung der Speicherplätze macht sich sehr gut, denn der Speicherplatz befindet sich immer am Kreuzungspunkt von Zeile und Spalte, also Speicherplatz 20 liegt in der zweiten Zeile und der Null-ten Spalte. Zeile und Spalte sind ja bereits getrennt im BZ angelegt, so dass die Schalter für die Speicher mit einer AND-Funktion ausgewählt werden können. Die Auswahl der Zeilen und Spalten erfolgt mit einem Multiplexer 1 aus 16.

Ein- und Ausgabe

Das wird in der Rechentechnik mit zwei Varianten gelöst:

  1. An den Bus werden Einrichtungen wie Speicher geschaltet, sie werden genauso adressiert, der Wert darf aber nicht in den Speicher gelangen. Deshalb wird eine weitere Leitung zur Auswahl, ob Speicher oder EA-Teil gebraucht, aber auch weitere Befehle zur Ansteuerung.
    Da wir unsere 16 Befehle voll ausnutzen, geht das nicht mehr!
  2. Die zweite Lösung zwackt einige Adressen vom Speicher für die Ausgabe ab. Das kostet nun wieder Speicherplatz, wir opfern jeweils einen Speicherplatz für die Ein- und Ausgabe <00> Eingabe;<01> Ausgabe. Die Speicherplätze <02> bis <08> sind beliebig nutzbar, dienen in bestimmten Fällen aber auch als Ausgabe!

Eingabe <00>
Die Eingabe erfolgt im Polling-Verfahren.
Wird der Wert vom Speicherplatz <00> mit dem Befehl LDA 0 0 in den Akku transportiert, erwartet das System eine Tastatur-Eingabe. Das Programm führt diesen Befehl erst aus, wenn eine Taste betätigt wurde, transportiert ihn in den Akku.
Entsprechend der 4 Bit Darstellung können nur Werte (Tasten) 0 9 a b c d e f übernommen werden, alle anderen Tasten werden negiert, der alte Wert aus <00> wird übernommen!
Die Eingabe wird nicht durch Enter bestätigt.

Ausgabe <01>

Die Ausgabe sieht im Prinzip einfach aus, hat es aber in sich, hier wurde mehr als zunächst sichtbar, eingebracht. Es sollten nicht nur einstellige Zahlen der Eingabe wieder ausgegeben werden können, sondern mehrstellige, in verschiedenen Zahlensystemen und auch kurze Texte!
Das geht folgendermaßen:
An der Ausgabe hängt symbolisch eine Ausgabeeinheit, die weitere Möglichkeiten der Ausgabe organisiert:

    Prinzipiell stehen 10 Stellen für die Ausgabe zur Verfügung. Standardmäßig wird die Stelle 0 bedient, dort landen alle Zahlen. Will man die anderen Stellen beschreiben, muss zuvor die Position verändert werden.
Das geht mit Zwei-Wort-Ausgaben, wir haben ja noch die Werte a bis f zur Verfügung.

- Position einstellen

1. Ausgabe: Ausgabe Zeichen "A
2. Ausgabe: Ausgabe der Position (0...9)

z.B.

1. A -> <01>
2. 2 -> <01> Position 2, also 3. Spalte von rechts

Alle weiteren Ausgaben, sofern nicht die Position verändert wird, erfolgen nun an Position 2.

- Ausgabe Sonderzeichen

Um eine weitere Gestaltung der Ausgabe zuzulassen, sind Sonderzeichen vereinbart worden.
Die Ausgabe erfordert wieder 2 Aktionen:

1. Ausgabe: Ausgabe Zeichen B
2. Ausgabe: Ausgabe einer Zahl im Bereich (0...9)
Die Zahlen bewirken folgende Zeichen:

Zahl:       0 1 2 3 4 5 6 7 8 9
--------------------------------
Zeichen: + - * /  .  :  ?  < >(9 = Leerzeichen)

Die Ausgabe erfolgt an der vereinbarten Position.
z.B.
  1. A -> <01>
  2. 9 -> <01>
  3. B -> <01>
  4. 6 -> <01> in der 1. Spalte von links wird ein ? ausgegeben.

- Ausgabe Textzeichen

Hier wird wie bei den Sonderzeichen verfahren:

1. Ausgabe: Ausgabe Zeichen C
2. Ausgabe: Ausgabe einer Zahl im Bereich (0...9)
Die Zahlen bewirken folgende Zeichen:

Zahl:       0 1 2 3 4 5 6 7 8 9
--------------------------------
Zeichen:  a b c d e f h F M I

z.B. auf <01> wird die angegeben Zeichenfolge nacheinander gespeichert:

A 9 C 7 A 8 1 A 6 B 1 A 5 B 8

Als Ergebnis erscheint im Fenster: F1 ->

Wider Erwarten ist mit der Ausgabe eine Menge an Informationen darstellbar.

Symbolische Ausgabe-Einheiten (Versuche)

Die so gestaltete Ausgabe lässt eine Menge Programmieraufgaben zu, die sichtbare Ergebnisse bringen. Aber, es sind noch zwei weitere Möglichkeiten vorgesehen. Es werden Einheiten mit Quasi-LED-Balken zur Ansteuerung bereit gestellt. Die Ansteuerung ist einfach über die Speicherplätze <02> <08> möglich. Wird auf einem dieser Speicherplätze eine 1 gespeichert so wird in der aktivierten Einheit der LED-Balken angesteuert, er wird rot dargestellt, bei 0 grau.
Die Einheiten werden als Versuche bezeichnet und mit Eingabe von V1 oder V2 aktiviert. Mit V0 werden sie wieder beseitigt.
Und so sieht der Versuch V2 aus:

   

Die Zahlen in den Balken zeigen die Adresse des Speicherplatzes mit dem sie verbunden sind. Als ein Programmierbeispiel kann man hier schön die duale Darstellung der Zahlen darstellen.

Versuch V1:


Hiermit kann man denn mal eine 7-Segmentanzeige programmieren. Die Zahlen in den Balken sind wieder die Adressen der angeschlossenen Speicherplätze.

Nun sollte genügend Übungsmaterial vorhanden sein.

Programmieren mit dem Rechner

Ein so primitiver Rechner - wie soll das gehen?

Folgende Aufgabe gilt es zu lösen:

Es soll von 1 bis 15 gezählt und ausgegeben werden.

Nimmt man eine höhere Programmiersprache, z.B. Pascal, so kann man das Problem sofort lösen:

Program
(*--- zaehlen und ausgeben ---*)
var i : byte;
begin
    for i:=1 to 15 do
        writeln(i);
end.

Genutzt wird eine Zählschleife. Um dieses Problem in die Maschinensprache umzusetzen, müssen die leistungsstarken Konstruktionen, wie eben die for-Schleife, aufgelöst werden. Man kann diese Auflösung auch in Pascal vornehmen.
Im ersten Schritt setzen wir eine Bedingungsschleife ein:

Program b1_2;
(*--- zaehlen> und ausgeben ---*)
var i : byte;
begin
    i:=1;
    repeat
        writeln(i);
        i:=i+1;
    until i>15;
end.

Der Programmierer muss sich nun bereits um das Erhöhen des Wertes i kümmern.
Eine weitere Aufschlüsselung geht, wenn man sich von den Schleifen-Konstruktionen trennt. Das hat jedoch sofort den Einsatz von Sprunganweisungen zur Folge, was man ja eigentlich nicht tun sollte, aber Maschinensprachen kennen meist keine Schleifenkonstruktionen. Das Programm könnte sich wie folgt ändern:

Program b1_3;
(*--- zaehlen und ausgeben ---*)
label m1;
var i : byte;
begin
      i:=1;
m1: writeln(i);
      i:=i+1;
      if i<=15 then goto m1;
end.

In einer if-Anweisung wird entschieden, ob ein Rücksprung notwendig ist oder das Programm beendet werden kann. Allerdings wurde dazu die Abfrage geändert (i>15 in i<=15). Sollte diese Umkehrung auf Grund des Befehlsvorrates des Prozessors nicht möglich sein, dann kann das Problem mit einem weiteren Sprungbefehl auch gelöst werden:

Program b1_4;
(*--- zaehlen und ausgeben ---*)
label m1,m2;
var i : byte;
begin
      i:=1;
m1: writeln(i);
      i:=i+1;
      if i>15 then goto m2;
      goto m1;
m2:
end.

Diese Lösung nehmen wir als Basis für die Realisierung der Aufgabe mit unserem Rechner:

Program b1_5
;--- zaehlen und ausgeben ---
ldk1;Akku wird mit dem Wert 1 geladen
spa02;Akku-Inhalt (also 1) auf Speicherpl. 02 speich.
m1:spa01;Akku-Inh. auf Speicherpl. 01 speich. - Ausgabe
add02;Akku = Akku + Inh. Speicherpl. 02 ( A = A + 1)
spcm2;Sprung zu m2 wenn C = 1 also größer als 15
spm1;wenn nicht, dann weiter bei m1
m2:spm2;Rechner bleibt in der Zeile (weil kein Ende)

Die letzte Zeile dient lediglich als Ende der Bearbeitung, da kein Haltebefehl vorhanden ist. Die Zeilen zuvor entsprechen sehr gut dem Pascal-Programm b1_4.

Ein gewichtiges Problem gibt es aber noch - diese symbolische Beschreibung kann der Rechner nicht lesen, er braucht die Zahlenfolgen in hexadezimaler Schreibweise!
Dieses muss nun der Nutzer mit Hilfe der Befehlsliste selbst durchführen

Jetzt ist es wichtig, den Speicherbelegungsplan zu kennen, denn bestimmte Speicherplätze haben für ausgewählte Befehle eine Speicherfunktion, z.B. kann das Programm nicht bei 00 beginnen, das war die Eingabe.
Legen wir also fest, es geht bei 20 (hex) los. Sinnvoll ist es, eine Tabelle anzulegen:
1.Befehl:

ldk 1 - nach Befehlsliste: 1 1 ab Adresse 20

Besser schreibt man:

ldk 1 20 1 1

Da dieser Befehl 2 Wörter beansprucht, muss die nächste Adresse bei 22 (hex) liegen und es kann der nächste Befehl übersetzt werden usw.

ldk 1     20 1 1
spa 02     22 2 2 0 <- Code=2; nw=2; hw=0; Adr=adr+3
m1: spa 01     25 2 1 0 <- Speicherpl.01 - Ausgabe
add 02     28 3 2 0
spc m2     2B D 1 3 <- wenn Bed. erfüllt, Sprung zu Adr.31
sp m1     2E C 5 2 <- m1 hat die Adr. 25
m2 sp m1     31 C 1 3 <- bleibt in der Zeile

Damit haben wir unser Maschinenprogramm, die fett geschriebenen Zahlenfolgen sind nun ab Speicherplatz 20 in den Speicher zu schreiben.

Ab <20>: 1 1 2 2 0 2 1 0 3 2 0 D 1 3 C 5 2 C 1 3

Das ist nun für unseren Simulationsrechner das Maschinen-Programm. Es sollte nun auch deutlich werden, wenn das Programm nicht mit Adresse 20 sondern 21 gestartet wird, kommt Unsinn heraus

In der realen Computerwelt sieht das nicht wesentlich anders aus. Der Zeichensatz eines Computers verwendet 8 Bit. Stellt man nun das Maschinenprogramm einer exe-Datei mit einem Textsystem dar, wird die Bitfolge in 8-er Schritten dargestellt. Da das Textsystem nun versucht alle 8 Bit Kombinationen darzustellen, kommt in der Regel unlesbares Zeug heraus.
Im Folgenden wurde einmal ein Stück von avcenter.exe dargestellt

MZ ÿÿ ¸ @  ­º ´ ÍLÍ!This program cannot be run in DOS mode.

$ )`ÖmòmòmòJÇòÐNddygdyqdyvdyadòmó¿[1]òdyxsSfdycRichmò PE L â¹êK à [1]
:[1] æ@ ° @ [1]  Zk [1] o @ ðL  ø* @ ° Ä
¯ @ .text ¹ < `.rdata h; ° <   @ @.data x' ð Ü @ À.rsrc o p ú @ @.reloc Êo p j @ B j¸C è¸[1] høêC EìPèo½ 3öFuüPÿuèû uðÆEü EìPèÆ EèW[1]  j8¸C èp[1] ¾€E 3ÛSÎuðè) ]üE¼PÇ€E üíC $E (E ,E è°a ÆEüE¼PÎèã( 8]èuhRz@ ÿè²C Y]üE¼PèÒa MüÿÆè×[1] ÃVñVè öD$tVè€( YÆ^ j ¸,zC èÕ[1] uÇüíC ¾¤ ÿtÏè;¿ WèI( Y¨ ÉtjÿMüÿÎèK( èh[1] Â é ¸¨ëC ÃUì8 l$ü¡ðD 3Å8 j|¸¨C è[1] Ù3ÿWè"( ÷' YYEì}ü;Çt5ÄE øè° 3ÿë[1]3ÀMüÿ¤ ;Çu3Àéµ óèð ÀtîE Pè` ÇEü E PËè¯' è^d ÀtèF[1] hüîC EèP¤ è ÆEü[1] ³¤ WPÎÿ

E ÀtÎè ÆEüMèÿ¨»C j\èF' YEìÆEü ;ÇtPèÑa ë[1]3ÀÆEü¨ ;ÇuMüÿE Pè3` éFÿÿÿWWÈÿR H ÿ|

E ÀtWjh°ëC èá& Wÿì²C €}Êu

¨ Æ@0€}Ð tSèó ëÛÿsHMäÿ »C ÆEü ¾¼îC VhüëC Mäÿ$»C VhìC Mäÿ$»C MäÿpºCCH;Çuf98tOj ÿL

Man findet sogar lesbaren Text darin und auch manchmal Versions-Nr. usw. Das kann man natürlich auch ändern, nicht mit Word höchstens mit einem einfachen Editor, aber es gibt Kontrollmechanismen, die das Programm dann garantiert nicht mehr laufen lassen.

Monitor / Simulator

Unser Programm ist fertig, aber wie wird es in unseren Simulationsrechner gebracht?

An dem Wort Simulator sollte man sich nicht stören, denn simuliert wird nur der Prozessor. Die Bedienebene in der angegeben Art ist bei Systemen zur Entwicklung und Testung von Maschinenprogrammen für industriellen Prozessoren üblich.
Man bezeichnet sie auch als Monitor.
Das Monitorprogramm für unseren Prozessor wurde bewusst kurz gehalten, was natürlich Konsequenzen für den Komfort hat. Der Monitor tritt wie folgt in Erscheinung:

    Das soll der Bildschirm sein, bzw. ein DOS-Fenster (die Proportionen stimmen nicht). Die Zeilen stehen am unteren Rand des Bildschirmes. Die Bedienung erfolgt ausschließlich über Tasten.

Die gekennzeichneten Zeichen lösen die genannte Funktion aus. Wollen wir unser Programm eingeben, so stellt man mit M den Kursor auf Adresse.
Jetzt wird 20 eingegeben (diese Eingabe, wie auch bei Wert, muss mit <ET> abgeschlossen werden).
Mit W Kursor auf Wert stellen und 1 (für ldk eingeben s.o.)
Dann mit + auf nächste Adresse (oder mit M Adresse eingeben), W und Wert eingeben usw.
Die Speicherbelegung müsste danach wie folgt aussehen:

    0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 1 1 2 2 0 2 1 0 3 2 0 D 1 3 C 5
3 2 C 1 3 0 0 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 6 ... usw.

Würde das Programm nun so gestartet werden, kommt sicher großer Unsinn heraus, denn der Prozessor holt vom Speicherplatz <FE> und <FF> die Startadresse des Programms. Da diese noch 00 ist, geht es bei <00> los, d.h. es wird eine Eingabe erwartet usw.
Wir müssen also noch auf <FF> den Wert 2 speichern. Die Startadresse ist dann <20>.

Mit Run oder Trace wird nun das Programm gestartet. Auf dem Bildschirm erscheint folgendes Bild:


Bei Run läuft das Programm ohne Pause ab. Bei Trace hält der Rechner nach jedem Schritt an. Mit jeder Taste, außer Q und I, wird der nächste Schritt ausgeführt.
Q beendet Run oder Trace, I löst einen Interrupt aus. Beide Anweisungen werden erst nach Abarbeitung des momentanen Befehls ausgeführt.
Je nach erreichtem Stand werden die Werte der Register verändert und angezeigt. Der Bef-Z zeigt bereits auf die nächste Adresse im Speicher. Zwischenzeitlich nimmt er auch andere Werte an, dann wird die Adresse im Hilfsregister gespeichert.

Um die Arbeit etwas zu vereinfachen, kann mit Save und Load der Inhalt des Speichers in eine Datei gespeichert oder von dort gelesen werden.
An diese Stelle sollte man schon darauf achten, dass die gespeicherten Programme die Erweiterung .SIM haben sollten, das wird später gebraucht, dann ist alles kompatibel!


Da beim Speichern mit der höheren Programmiersprache PASCAL als kleinste Einheit Byte (8 Bit) verwendet werden kann, werden beim Speichern die Nibbles rechts eingeordnet, damit entstehen unsinnige Zeichen (01 ist ein Gesicht usw. Lesbare Zeichen beginnen erst ab Wert 20 (Leerzeichen). In dem Viewer sieht man, dass das Programm ab Adresse 20 beginnt und bei 33 endet (der Viewer ist noch aus DOS-Zeiten, habe nichts anderes zur Anzeige gefunden). Generell wird aber immer der gesamte Hauptspeicher mit 256 Plätzen abgespeichert.
Q beendet den Monitor (Simulator).
Arbeitet man das oben angegebene Programm ab, gibt es mit der Ausgabe Probleme. Diese sind bedingt durch den Mechanismus der Ausgabe (siehe Beschreibung E/A-Einheit).

Simulator 2

Der ist natürlich viel besser als der eben genannte.


Wie beim Simulator 1 gibt es gleiche Bedienmöglichkeiten. Neben der Möglichkeit das Maschinenprogramm zu laden kann man es auch eintippen, was hier sehr viel einfacher ist, denn der Speicher wird mit ausgegeben. Mit <M> kann nun direkt der Speicherplatz angesteuert und der Wert eingegeben werden:


Die Bilder zeigen schon den gesamten Rechner und die ausgewählten Leitungen und Speicherplätze werden entsprechend farbig gekennzeichnet.
Die Darstellung zeigt die Nutzung des Versuches 2 und das Programm im Speichert wandelt die eingegebene Zahl in eine binäre Darstellung um, Beispiel ist die Zahl 5 zu 0101.
Unter dem Prozessor werden auch wieder die Quasi-Mikrobefehle ausgegeben.
Und wenn man genau hinschaut, ist der Speicher noch nicht voll es geht noch mehr.

Nutzung Simulator
Der Simulator 2 ist unter der speziell für diesen Prozessor erarbeiteten Bedienoberfläche"asm_4816.exe" erreichbar. Die Nutzung des Systems, einschließlich Simulator wird auf der Seite "Simulator ERNA" vorgestellt.
Der Simulator steht als Programm "tr4.exe" zur Verfügung.

Was noch fehlt ist das maschinelle Erzeugen des Maschinenprogramms aus einer für uns lesbaren Quelle, das tut ein Assembler .
Ein Dis-Assembler (auch Re-Assembler) ist auch vorhanden, er vermag ansatzweise ein Maschinenprogramm in eine Quelle zurückwandeln das Ding macht immer Probleme.
Weiterhin ist ein übergeordnetes Steuersystem zum Aufrufen der Teilkomponenten vorhanden. Alle diese Dinge sollen in einem Extrateil beschrieben werden.

Alle Programme stehen unter erna_dat.htm zur Verfügung.

zurück zur Start-Seite   /   weiter Assembler