Programmiersprache/Entwicklung der Programmiersprachen und Johnny-Simulator/Ein einfaches Modell: Unterschied zwischen den Seiten

Aus ZUM-Unterrichten
< Programmiersprache(Unterschied zwischen Seiten)
main>Classow
Keine Bearbeitungszusammenfassung
 
(typo)
Markierung: 2017-Quelltext-Bearbeitung
 
Zeile 1: Zeile 1:
__NOTOC__
{{Fortsetzung|
==Was findet ihr auf dieser Seite?==
vorher=Aufbau eines Rechners|vorherlink=Johnny-Simulator/Aufbau eines Rechners|
# Timeline
weiter=Vom Makrobefehl zu den Mikrobefehlen – das Steuerwerk|weiterlink=Johnny-Simulator/Vom Makrobefehl zu den Mikrobefehlen – das Steuerwerk|
# Die Anfänge des Programmierens
}}
# Die ersten Computer und ihre Instruktionen
 
# Die ersten höheren Programmiersprachen
Wie schon im [[Johnny-Simulator#Vorwort|Vorwort]] gesagt, sieht man einem modernen Rechner seine Funktion nicht an. Um besser zu verstehen, wie Rechner funktionieren, greifen wir deshalb auf eine Simulation zurück. Der Simulator „Johnny“ ist nach dem genialen Entwickler der heutigen Rechnerstruktur, {{wpde|John von Neumann}} (1903-1957) benannt: Privat war von Neumann für seine Partys berühmt und sein Spitzname war wohl auch deshalb „Good Time Johnny“, worauf der Name des Simulators anspielt.
# Objektorientierung
 
# Programmiersprachen in der Welt von heute
Der Johnny-Simulator konzentriert sich auf CPU mit Rechenwerk und Steuerwerk und den Arbeitsspeicher. Die Benutzungsoberfläche entspricht dabei etwa dem obigen Blockdiagramm:
# Was hast du gelernt? → Quiz
 
# Literaturverzeichnis
[[Datei:Pd_Johnny_hidden_CU.png | 500px]]
<br>
 
<br>
 
Beim Start ist die Struktur des Steuerwerks (''Control Unit'') noch verdeckt, um die Dinge zunächst einfach zu halten. Der Inhalt des RAM kann im Simulator gelöscht, aus einer Datei geladen bzw. in eine Datei geschrieben werden.
 
 
Vieles ist beim Johnny-Simulator einfacher als bei einem richtigen Rechner. So rechnen praktisch alle Rechner im Binärsystem, während der Simulator in unserem gewohnten Dezimalsystem rechnet. Auch die Funktion des Bus-Systems ist gegenüber echten Rechnern vereinfacht: Normalerweise können sich Bus-Syteme keine Daten merken, sondern sind lediglich Verbindungswege.
 
All diese Vereinfachungen sollen den Benutzern des Simulators die Bedienung vereinfachen, die Grundprinzipien des Rechners bleiben jedoch erhalten.
 
Der Simulator kann vom [http://informatik.bildung-rp.de/werkzeuge-und-software/digitaltechnik-rechnerarchitektur.html Bildungsserver]
des Landes Rheinland-Pfalz, vom [http://www.heise.de/download/johnny-1172728.html Server des Heise-Verlags] oder direkt vom [http://sourceforge.net/projects/johnnysimulator/ SourceForge-Server] ohne Kosten heruntergeladen werden. Das Programm ist ein OpenSource-Projekt und kann unter der Programmierumgebung [[Lazarus]] selbst weiterentwickelt werden.
 
=== Zahlen im Arbeitsspeicher (RAM) ===
Betrachten wir zunächst den Arbeitsspeicher. Dieser ist im Simulator als eine Tabelle dargestellt, was seiner tatsächlichen Struktur recht nahe kommt. Jede Zeile der Tabelle hat eine Nummer, die so genannte Adresse (Spalte „Adr“). Statt von Zeilen spricht man auch von Speicherstellen. In jeder Speicherstelle kann eine Zahl zwischen 0 und 19999 abgelegt werden. Aus technischen Gründen sind dabei die 10000er- und die 1000er-Stelle in einer gesonderten Spalte „Hi“, die 100er-, die 10er- und die 1er-Stelle „Lo“ abgelegt. Die hinteren beiden Spalten „Asm“ und „Op“ werden später eine Rolle spielen, sie sind aber nur Kommentare. Der eigentliche RAM-Inhalt sind die Spalten „Hi“ und „Lo“.
 
Die Inhalte der einzelnen Speicherstellen kann man (im Simulator) entweder durch Anklicken der entsprechenden Zeile, oder aber über das Bus-System des Simulators ändern.
 
[[Datei:Pd_Johnny_RAM.png | 300px]]
 
 
Zunächst muss die richtige Zeile, also Adresse gewählt werden. Dies tut man, indem man die Adresse der betreffenden Zeile am so genannten Adressbus oben links im Textfeld eingibt, mit dem nebenstehenden Knopf diese Zahl auf den Adressbus legt. Danach legt man analog die Zahl, die in den RAM geschrieben werden soll, auf den Datenbus und drückt auf den Knopf „db->ram“.
 
=== Rechnen mit Johnny ===
Betrachten wir nun das Rechenwerk (Arithmetic Logic Unit). Diese kann mit Daten, die auf dem Datenbus liegen, rechnen. Kernstück des Rechenwerks ist der so genannte Akkumulator (lat.: „Sammler“). In ihm sammeln sich die Rechenergebnisse an. Drückt man z.B. auf die Taste plus, so wird der Wert auf dem Datenbus zum Wert des Akkumulators addiert und das Ergebnis wieder im Akkumulator gespeichert. Johnny beherrscht von sich aus nur die beiden Rechenoperationen plus und minus, alles andere muss man ihm sozusagen beibringen (mehr dazu später).
Der Wert des Akkumulators kann auch wieder auf den Datenbus kopiert werden (mit acc->db). Daneben gibt es noch ein paar andere Möglichkeiten, den Akkumulator zu manipulieren, die Sie aber auch leicht selbst herausfinden können.
Wichtig für später ist auch die Kontrolllampe links neben dem Akkumulator. Diese zeigt an, ob der Inhalt des Akkumulators gerade den Wert 0 hat.
 
[[Bild:Pd_Johnny_ALU.png | 200px]]
 
{{Aufgabe|
'''Herunterladen und Inbetriebnahme von JOHNNY'''
Falls der Simulator "JOHNNY" auf Ihrem Rechner noch nicht installiert ist, laden Sie ihn von einer der oben genannten Download-Seiten herunter. Unter Windows-Betriebssystemen muss "JOHNNY" muss nicht installiert werden, es muss lediglich die heruntergeladene ZIP-Datei in einen beliebigen Ordner ausgepackt und die Datei "johnny.exe" ausgeführt werden.
 
'''Anmerkung für Linux-Benutzer:'''<br>
Wenn Sie "Johnny" unter einem Linux-Betriebssystem ohne Emulation betreiben möchten, kann man die Quelldateien herunterladen und unter Verwendung der Entwicklungsumgebung [[Lazarus]] kompilieren.
}}
 
== Mikrobefehle im Handbetrieb ==
Bis hierher hat Ihnen das Skript nur Dinge beschrieben. Vieles jedoch können Sie auch selbst herausfinden.
 
 
'''a) Die Bedeutung eniger Mikrobefehle'''
 
Die Knöpfe des Simulators entsprechen Aktionen, die man als „Mikrobefehle“ bezeichnet; sie sind die kleinsten Befehlseinheiten, mit denen der Prozessor gesteuert werden kann.Probieren Sie die folgenden Mikrobefehle aus und ordnen Sie die Lösungen zu:
 
<div class="lueckentext-quiz">
{|
| <tt>db->ram</tt>|| <strong>Businhalt auf die aktive Adresse des RAM legen</strong>
|-
| <tt>ram->db</tt>|| <strong>RAM-Inhalt der aktiven Adresse auf den Bus legen</strong>
|-
| <tt>acc:=0</tt> || <strong>Akkumulator auf 0 setzen</strong>
|-
| <tt>acc++</tt> || <strong>Inhalt des Akkumulators um 1 erhöhen</strong>
|-
| <tt>acc--</tt> || <strong>Inhalt des Akkumulators um 1 erniedrigen</strong>
|-
| <tt>db->acc</tt> || <strong>Inhalt des Datenbusses in den Akkumulator kopieren</strong>
|-
| <tt>plus</tt> || <strong>Inhalt des Datenbusses zum Akkumulator hinzuaddieren</strong>
|-
| <tt>minus  </tt> || <strong>Inhalt des Datenbusses vom Akkumulator subtrahieren</strong>
|-
| <tt>acc->db</tt> || <strong>Inhalt des Akkumulators auf den Datenbus kopieren</strong>
|}
</div>
 
'''b) Rechnen mit Mikrobefehlen'''
 
Angenommen, die Adresse „42“ würde bereits auf dem Adressbus stehen. Welche Mikrobefehle muss man nacheinander ausführen, um den Wert in der Speicherstelle mit der Adresse 42 zu verdoppeln (unabhängig davon, welche Zahl vorher gerade im Akku steht).
 
Zum Testen mit dem Simulator sollte man vorher die Speicherstelle 42 mit einem Wert >0 belegen, denn sonst kann man die Verdopplung natürlich nicht gut erkennen.
 
{{Lösung versteckt|
ram->db<br>
db->acc<br>
plus<br>
acc->db<br>
db->ram
}}
 
== Makrobefehle als praktische Abkürzung ==
=== Wiederkehrende Befehlsfolgen ===
{{Aufgabe|'''Mikro- und Makrobefehle'''
 
Beschreiben Sie in der mittleren Spalte, mit welcher Folge von Aktionen man mit dem Simulator die Zahlen in den Speicherstellen 10,11 und 12 addieren und dann die Zahl in Speicherstelle 14 davon subtrahieren kann. Das Ergebnis soll in der Speicherstelle mit der Adresse 15 abgespeichert werden. (Bei den letzten drei Zeilen kann auch eine andere Lösung als die "Musterlösung" richtig sein, aber diese Lernumgebung ist nicht ganz so flexibel ;-).
 
Neben den schon bekannten Mikrobefehlen brauchen wir zusätzlich noch eine Anweisung wie
„Lege Adressse ... auf den Adressbus“
wobei „...“ für die entsprechende Adresse, also die Nummer der Zeile im RAM steht.
 
 
Ordnen Sie danach in der rechte Spalte "Abkürzungen" wie <tt>TAKE 10</tt> oder <tt>SUB 14</tt> für die Befehlsfolgen in der mittleren Spalte zu.}}
 
 
<div class="lueckentext-quiz">
{|
| 1|| <strong>Lege Adresse 10 auf den Adressbus</strong> || <strong>TAKE 10</strong>
|-
| 2|| <strong>ram->db</strong>
|-
| 3|| <strong>db->acc</strong>
|}
----
{|
|-
| 4|| <strong>Lege Adresse 11 auf den Adressbus</strong> || <strong>ADD 11</strong>
|-
| 5|| <strong>ram->db</strong>
|-
| 6|| <strong>plus</strong>
|}
----
{|
|-
| 7|| <strong>Lege Adresse 12 auf den Adressbus</strong> || <strong>ADD 12</strong>
|-
| 8|| <strong>ram->db</strong>
|-
| 9|| <strong>plus</strong>
|}
----
{|
|-
| 10|| <strong>Lege Adresse 14 auf den Adressbus</strong> || <strong>SUB 14</strong>
|-
| 11|| <strong>ram->db</strong>
|-
| 12|| <strong>minus</strong>
|}
----
{|
|-
| 13|| <strong>Lege Adresse 15 auf den Adressbus</strong> || <strong>SAVE 15</strong>
|-
| 14|| <strong>acc->db</strong>
|-
| 15|| <strong>db->ram</strong>
|}
</div>
 
 
Man nennt ein Programm bestehend aus solchen Abkürzungen auch Maschinenprogramm, die einzelnen Befehle darin Makrobefehle, da sie mehrere Mikrobefehle zu größeren Einheiten zusammenfassen.
 
=== Der Speicher als Programmspeicher ===
Am Arbeitsauftrag 5 merkt man: Maschinenprograme aus Makrobefehlen lassen sich leichter verstehen und vor allem viel kompakter hinschreiben als eine Folge von Mikrobefehlen. Die Frage ist nur: wo genau hinschreiben. In den ersten Rechnern wie dem Zuse Z3 waren die Programme z.B. auf Lochstreifen gespeichert. [http://de.wikipedia.org/wiki/John_von_Neumann John von Neumann] hatte die Idee, nicht nur Zahlen sondern auch Programme in ein und demselben Arbeitsspeicher abzulegen. Man spricht in diesem Zusammenhang von der [http://de.wikipedia.org/wiki/Von-Neumann-Architektur von-Neumann-Architektur].
 
Wie aber lassen sich Makrobefehle im Speicher (RAM) ablegen? Wir sehen, dass der RAM nur Zahlen – in unserem Simulator zwischen 0 und 19999 – ablegen kann.
 
Wie also kann man einen Befehl wie ADD 12 im RAM speichern?
 
Zunächst überlegt man sich hierfür, dass dieser Befehl eigentlich zweigeteilt ist, einmal in die eigentliche Operation „ADD“ und dann in die Adresse, auf die sich der Befehl bezieht. Der Speicher unseres Simulationsrechners enthält 1000 Speicherstellen mit Adressen zwischen 0 und 999. Um die Adressen darzustellen brauchen wir also die 1er-, die 10er- und die 100er-Stelle einer Zahl. Die verbliebenen zwei Stellen, die 1000er- und die 10000er-Stelle können wir verwenden, um die Operation selbst zu beschreiben, nicht mit einer Zeichenfolge wie „Add“ sondern mit einer Zahl etwa 20000.
 
Damit wird aus dem Befehl „ADD 12“ nun einfach die Zahl „20012“. Damit man sich als Benutzer des Simulators die Zahlen für die einzelnen Operationen nicht merken muss, kann man bei der Eingabe von Werten in den RAM ein Auswahlmenü anklicken, dass direkt die einzelnen Operationen mit ihren entsprechenden Zahlen zur Auswahl stellt.
 
[[Datei:pd_Johnny_Edit_RAM.png | 400px]]
 
Auf diese Weise kann man ohne Auswendiglernen der entsprechenden Zahlen (der so genannten Operation Codes, kurz Opcodes genannt) ein Maschinenprogramm eingeben. Neben dem eigentlichen Speicherinhalt, der – wie wir wissen – nur aus den Zahlen besteht, zeigt der Simulator als Kommentar die zugehörigen Makrobefehle an.
 
[[Datei:Pd_Johnny_RAM_Macro_Program.png | 300px]]
 
== Erste Maschinenprogramme ==
 
{{Aufgabe|Geben Sie das folgende Maschinenprogramm ab der Adresse 000 ein}}
 
 
{| style="border-spacing:0;"
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 000:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| TAKE
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 001:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| ADD
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 011
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 002:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| ADD
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 012
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 003:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| SUB
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 014
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 004:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| SAVE
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 015
 
|}
 
Testen Sie das Programm, in dem sie unter den verschiedenen Adressen 10,11,12 und 14 verschiedene Zahlen vor dem Programmlauf speichern.
 
 
''' b) Weitere Befehle'''<br>
Neben den Befehlen TAKE, ADD, SUB und SAVE gibt es noch einige andere. Finden Sie anhand des folgenden Programms heraus, was die darin enthaltenen Befehle bewirken und kreuzen Sie die richtige Beschreibung der Funktion an.
 
 
 
{| style="border-spacing:0;"
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 000:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| INC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 001:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| INC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 002:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| DEC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 003:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| INC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 004:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| INC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 005:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| NULL
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|}
 
 
<quiz display="simple">
{<tt>INC<Adresse> </tt>}
- Erniedrigt die Speicherzelle mit der Adresse um 1
+ Erhöht die Speicherzelle mit der Adresse um 1
- Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
- Setzt die Speicherzelle mit der Adresse auf den Wert 0
 
 
{<tt>DEC<Adresse> </tt>}
+ Erniedrigt die Speicherzelle mit der Adresse um 1
- Erhöht die Speicherzelle mit der Adresse um 1
- Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
- Setzt die Speicherzelle mit der Adresse auf den Wert 0
 
 
{<tt>NULL<Adresse> </tt>}
- Erniedrigt die Speicherzelle mit der Adresse um 1
- Erhöht die Speicherzelle mit der Adresse um 1
- Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
+ Setzt die Speicherzelle mit der Adresse auf den Wert 0
</quiz>
 
 
=== Sprünge und Bedingungen ===
Bisher sind unsere Programme immer von einem Befehl zum nächsten gegangen. Für kompliziertere Aufgaben reicht das jedoch nicht. Wie Sie wahrscheinlich aus anderen Programmiersprachen wissen, muss es auch Verzweigungen (if ... then ... else ...) oder Schleifen (etwa while ... do ...) geben können.
 
{{Aufgabe|'''a) Kreuz und quer durch's Programm'''
 
Testen Sie das folgende Programm und finden Sie heraus, was die Befehle JMP und TST bewirken. Setzen Sie vor Ausführung des Programms die Speicherstelle Nr. 10 auf den Wert 5 und die Speicherstelle Nr. 11 auf den Wert 42.
}}
 
{| style="border-spacing:0;"
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 000:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| INC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 001:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| DEC
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 002:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| TST
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 010
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 003:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| JMP
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 001
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 004:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| TAKE
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 011
 
|-
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 005:
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| SAVE
| style="background-color:#e6e6ff;border:none;padding:0.097cm;"| 012


==Timeline==
|}
Beschreiben Sie die Funktion der beiden Befehle JMP und TST in kurzer Form:


==Die Anfänge des Programmierens==
In der Zeit bevor es Computer gab, konnten die Menschen nicht anders als jeden Handgriff und jeden Arbeitsschritt selber zu machen. Um repetitive Aufgaben nicht immer wiederholen zu müssen, erfand man schon in der industriellen Revolution Maschinen. Diese Maschinen nahmen den Menschen viele Arbeiten vor allem im handwerklichen Bereich ab. So gab es z.B. auch Webstühle, die schon ein einfaches Muster in den Stoff weben konnten. Joseph-Marie Jacquard war ein französischer Erfinder, der versuchte die Musterwebtechnik zu mechanisieren, sodass diese nicht mehr durch Menschen auszuführen sei. 1804 erfand Jacquard letztendlich den ersten programmierbaren Webstuhl. Dieser Webstuhl konnte nun auch kompliziertere Muster durch seine Programmierung weben. Alle Informationen inklusive der Programmierung, die der Webstuhl brauchte, waren auf Lochkarten geschrieben, welche man in den Webstuhl steckte.


[[File:Lochstreifen_Wikipedia.jpg|400px|Lochstreifen mit Text]]
<quiz display="simple">
<br><br>
{<tt>JMP<Adresse> </tt>}
Aber nicht nur im handwerklichen Bereich schritt die Programmierung voran. Ein paar Jahre später heiratete Ada Lovelace William King. Dieser unterstützte, förderte sie und ermöglichte ihr Zugang zu Wissen in Form von Bibliotheken, die sie vorher nie hatte. Denn Frauen hatten im 19. Jahrhundert noch kein Zutritt zu jeglicher Form von Bildung. Lovelace’s Ehemann ging in Bibliotheken und schrieb für sie Artikel ab. Da Lovelace und ihr Mann in der Royal Society verkehrten, dauerte es nicht lang bis sie von dem Plan von Charles Babbage erfuhr. Babbage’s Plan war es, eine Rechenmaschine zu bauen, die heute als Vorläufer des Computers gilt. Lovelace war fasziniert und übersetzte einen Artikel über Babbage’s „Analytical Machine“. Diese Maschine wurde 1837 von Babbage veröffentlicht und war eine Differenzmaschine für die Lösung polynominaler Funktionen. Danach fing sie an, an einem Algorithmus zu arbeiten, mit dessen Hilfe diese Maschine die Bernoulli-Zahlen hätte berechnen können. Ada Lovelace schaffte es in den Jahren 1842 – 1845 den Algorithmus zur Berechnung der Bernoulli-Zahlen zu entwickeln. Obwohl Babbage’s Pläne für die „Analytical Machine“ fehlschlugen, haben wir heutzutage doch ein ziemlich genaues Bild für diese Maschine. Sie wäre doch Lochkarten wie der Webstuhl viele Jahre zuvor programmiert worden. Die Programmiersprache auf diesen Lochkarten ähnelt den bis heute verwendeten Assemblersprachen sehr. (Bild)
- Die Speicherstelle an der Adresse springt um eins nach oben.
+ Das Programm spingt in seiner Ausführung an den Befehl, der an der Adresse steht.
- An der Adresse springt das Programm aus der Ausführung heraus.
- Testet, ob die Speicherstelle an der Adresse den Wert 0 hat und überspringt in diesem Fall den nächsten Befehl.
- Testet, ob die Speicherstelle an der Adresse größer als 1 ist und springt in diesem Fall an den Anfang des Programms.


[[File:Hollerith_punched_card.jpg|400px]]
{<tt>TST<Adresse> </tt>}
- Die Speicherstelle an der Adresse springt um eins nach oben.
- Das Programm spingt in seiner Ausführung an den Befehl, der an der Adresse steht.
- An der Adresse springt das Programm aus der Ausführung heraus.
+ Testet, ob die Speicherstelle an der Adresse den Wert 0 hat und überspringt in diesem Fall den nächsten Befehl.
- Testet, ob die Speicherstelle an der Adresse größer als 1 ist und springt in diesem Fall an den Anfang des Programms.


==Die ersten Computer und ihre Instruktionen==
</quiz>


1946 entwickelten John Mauchly und John Presper Eckert den ersten ENIAC Computer. Auf dem ENIAC waren nur Hardwareverbindungen möglich.


[[File:Eniac.jpg|400px|rechts|ENIAC Computer]]
'''b) Malnehmen - eigentlich gar nicht eingebaut'''


Nun wird es komplizierter: Konstruieren Sie ein Maschinenprogramm, das die Zahlen in den Speicherstellen 10 und 11 miteinander multipliziert und das Ergebnis in der Speicherstelle 12 abspeichert.


Der Computer wurde programmiert, in dem die Verbindungen mit Leitungen geschlossen werden. Jedes Mal, wenn die Programmierung für den Computer geändert werden sollte, musste der Programmierer gucken, wo welche Verbindungen stecken und sie umstecken. In den Speicher dieses Computers konnten nur Daten eingegeben werden. Erst nachdem der Mathematiker von Neumann eine neue Architektur beschrieben hatte, mussten keine neuen Leitungen mehr gelegt werden sondern es konnte ein Programm im Speicher gespeichert werden.  
Tipp: Das Programm soll folgendes tun:
1936 entwickelten Alonzo Church und Stephen Kleene das Lambda-Kalkül (λ-Kalkül). Das Lambda-Kalkül ist eine universelle abstrakte Programmiersprache. Es lässt sich mit dem Lambda-Kalkül alles ausdrücken, was sich mit einer modernen Programmiersprache ausdrücken lässt. Es ist außerdem die formale Grundlage für viele Programmiersprachen, wie z.B. Scheme und Lisp. In den häufig verwendeten und gut ausgebauten Programmiersprachen ist er meistens zusätzlich als Modul oder Bibliothek verwendbar.
* Die Speicherstelle 12 zunächst auf 0 setzen.
<br><br><br><br>
* Den Wert der Speicherstelle 12 in den Akku holen, den Wert der Speicherstelle 10 addieren das Ergebnis und wieder in Speicherzelle 12 abspeichern.
* Nach einer solchen Addition den Wert der Speicherstelle 11 um 1 erniedrigen.  
* Das Verfahren ab dem 2. Punkt so lange wiederholen Sie, bis die Speicherstelle 11 den Wert 0 hat.


==Die ersten höheren Programmiersprachen==
Testen Sie Ihr Programm, ob es auch funktioniert. Hierfür sollten die Speicherstellen 10 und 11 auf kleine, von Null verschiedene Werte gesetzt werden, also z.B. 5 und 3.


Konrad Zuse entwickelte 1945/1946 die erste höhere Programmiersprache, die er „Plankalkül“ nannte. Plankalkül umfasst Zuweisungen, Funktionsaufrufe, bedingte Anweisungen, Schleifen, Gleitkommaarithmetik, Felder und zusammengesetzte Datentypen. Erstmals wurde Plankalkül 1972 komplett veröffentlicht. Praktisch verwendet wurde Plankalkül nicht aber es hat historische Bedeutung. Denn es hat die nachfolgenden Programmiersprachen sehr beeinflusst. Die nächste Programmiersprache, die entwickelt wurde und heute in neueren Versionen immer noch benutzt wird, ist Fortran. Fortran steht für „FORmula TRANslator“. Es wurde 1954 von John W. Backus und einem Team aus Entwicklern und Programmierern entworfen. Heutzutage ist es eine objektorientierte Programmiersprache, die insbesondere für numerische Berechnungen genutzt wird. Eine Sprache, die heute auch noch verbreitet ist, ist LISP. LISP ist eine Abkürzung für „LISt Processor“. LISP wurde 1959 von John McCarthy et al. entworfen. Sie entstand am MIT (Massachusetts Institute of Technology) in Anlehnung an das Lambda-Kalkül. LISP beeinflusste eine Reihe von Sprachen, u.a. Smalltalk, Python und Haskell.
{{Lösung versteckt|
Eine wichtige Programmiersprache, die den weiteren Verlauf beeinflusst hat, ist Algol 58 bzw. Algol 60. Algol kürzt „ALGOrithmic Language“ ab. Algol 58 wurde 1958 von einem internationalen Komitee während einer Tagungsreihe entworfen, die Algorithmen dienen sollte. Die Programmiersprache wurde noch einmal überarbeitet mit dem Revised Report für Algol 60. Algol schrieb gegen den Trend der Zeit Laufzeitprüfungen vor, die erst später gewürdigt und nachgeahmt wurden.
<pre>
Das „Hallo Welt“-Programm in Algol 60:
000 : NULL 012
001 : TAKE 012
002 : ADD  010
003 : SAVE 012
004 : DEC  011
005 : TST  011
006 : JMP  001
007 : HLT  000
</pre>
(Der Befehl <tt>HLT 000</tt> stoppt den Programmablauf im Simulator ab).
}}


{{Kasten blass|<tt>'COMMENT' HALLO, WELT PROGRAMM IN ALGOL 60;<br>'BEGIN'<br>  &ensp; &ensp; &ensp; OUTSTRING(2, '('HALLO, WELT')');<br>'END'</tt> }}


1959 wurde die Urform der Backus-Naur-Form (BNF) dargestellt. Die BNF ist eine kompakte, formale Metasprache zur Darstellung kontextfreier Grammatiken. Fast alle Programmiersprachen, die nach der BNF entwickelt wurden, benutzten sie um ihre Programmiersprache darzustellen. Es gab das erste Mal die Möglichkeit, Gültigkeitsbereiche zu definieren. Es gibt viele Varianten der BNF. Die bekanntesten Varianten sind die erweiterte Backus-Naur-Form (EBNF) und die angereicherte Backus-Naur-Form (ABNF). IBM entwickelte in den 60er Jahren PL/I. PL/I steht für „Programming Language One“. In PL/I wurde versucht, die Sprachen Algol, Fortran und Cobol zu vereinigen. PL/I wurde v.a. auf IBM-Großrechnern eingesetzt. IBM musste vor PL/I für jeden Computer andere Sprachen verwenden. Deswegen war es ganz wichtig für IBM, dass PL/I entwickelt wurde. Es gibt aber auch Versionen für Windows und UNIX. Das "Hello World"-Programm in PL/I:
'''c) Dividieren - einfache Version'''
Schreiben Sie ein Programm, das eine Zahl durch eine andere Zahl teilt. Betrachten Sie dabei zunächst nur den Fall, dass die größere Zahl in Speicherzelle 10 tatsächlich durch die kleinere in Speicherzelle 11 ganzzahlig teilbar ist.
<br>
 
'''d) Größer, kleiner oder gleich'''
Wir betrachten zwei Zahlen in den Speicherstellen 10 und 11. JOHNNY soll entscheiden, ob die Zahl in Speicherstelle 10 größer ist als die in Speicherstelle 11, gleich groß ist, oder aber kleiner. Je nachdem soll in Speicherstelle 12 nach der Untersuchung der Wert 1, 2 oder 3 stehen.<br>
Tipp: <math>a-b=a+1-b-1</math>
<br>


{{Kasten blass|<tt>/* Hello World in PL/1 */<br>Hello: procedure options(main);<br> &ensp; &ensp; &ensp; put skip list('Hello World!');<br>end Hello;</tt> }}
'''e) Dividieren - komplizierte Version'''
Schreiben Sie ein Programm, das eine Zahl durch eine andere Zahl teilt. Und zwar in der Art, wie wir vermutlich alle das in der Grundschule gelernt haben: <br>17:5 = 3 Rest 2.<br>
Die Zahl in Speicherstelle 10 soll dividiert werden durch die Zahl in Speicherstelle 11. Der ganzzahlige Quotient soll danach in Speicherstelle 12, der Rest in Speicherstelle 13 gespeichert sein.
Tipp: Vielleicht hilft Ihnen das Ergebnis aus Aufgabe d)


Einen ganz anderen Grund hingegen hatten die Entwickler von BASIC. BASIC steht für „Beginner’s All-purpose Symbolic Instruction Code“. Es wurde 1964 von John C. Kemeny and Thomas E. Kurtz am Dartmouth College entworfen. Es wurde konzipiert, um den Studierenden den Einstieg in die Programmierung mit Algol und Fortran zu erleichtern. Es wurde also auch mit dem Ziel entworfen, leicht erlernen zu sein.


==Objektorientierung==
=== Programme, die sich selbst ändern ===
Wie bereits gesagt, ist das besondere an von Neumanns Idee zur Speicherung von Programmen, dass Programmcode („Was soll gemacht werden?“) und Programmdaten („Womit soll etwas gemacht werden?“) in ein und dem selben Arbeitsspeicher gespeichert sind.


Objektorientierung beschreibt eine Sichtweise auf komplexe Systeme, bei der ein System durch das Zusammenspiel kooperierender Objekte beschrieben wird. Diese Denkweise und die ersten Begriffe zeigten sich in Simula 67. Simula setzt sich aus „SIMUlation“ und „LAnguage“ zusammen. Simula wurde für Simulationszwecke entwickelt und war erstmals mit Compiler im Jahre 1964 einsetzbar. Simula diente außerdem als Vorbild für C++. Dies ist das "Hello World"-Programm in Simula:
Mehr noch: Das Programm besteht selbst aus Daten, der einzige Unterschied ist, dass diese Daten, die einem Programm entsprechen, von der Maschine als Befehle interpretiert und ausgeführt werden.  


{{Kasten blass|<tt>! Hello World in Simula;<br>BEGIN<br> &ensp;&ensp;&ensp; OutText("Hello World!");<br>&ensp;&ensp;&ensp; OutImage;<br>END</tt> }}
Dies hat eine wichtige Konsequenz, die zunächst ein wenig abwegig klingt: Programme können sich selbst (oder andere Programme im Arbeitsspeicher) verändern, indem sie die entsprechenden Daten im Speicher manipulieren. Compiler z.B. lesen einen Text in einer höheren Programmiersprache und übersetzen ihn in ein Maschinenprogramm. So etwas geht in dieser Weise nur dank der von-Neumann-Architektur.


Die objektorientierten Konzepte hatten großen Einfluss auf die weitere Entwicklung von Programmiersprachen. Eine andere wichtige Programmiersprache mit Objektorientierung ist Smalltalk. Smalltalk wurde seit den frühen 70er Jahren im Xerox Palo Alto Research Center entwickelt aber erst in den 80er Jahren der Öffentlichkeit allgemein freigegeben. Smalltalk wurde als voll dynamisches System angelegt, bei dem man Objekte interaktiv erzeugen und ändern kann. Smalltalk kann Codeschnipsel als Objekt behandeln und das hilft Smalltalk dabei, diese Codeschnipsel wie jedes andere Objekt zu verwenden. Mit Smalltalk war außerdem erstmals eine Integration einer Sprache in eine innovative Benutzeroberfläche, die eine echte Interaktion ermöglichte, vorgesehen. Speziell zu Ausbildungszwecken wurden Ende der 60er und Anfang der 70er Jahre Pascal erfunden. Pascal wurde von Niklaus Wirth und Kathleen Jensen entwickelt und sie benannte die Sprache nach dem französischen Mathematiker und Philosophen Blaise Pascal. Pascal übertraf FORTRAN und ALGOL 60 in soweit, dass Pascal universell einsetzbar war. Die damaligen Versionen von FORTRAN und ALGOL waren dies noch nicht. Im Gegensatz zu BASIC, welches auch eine Programmiersprache zu Ausbildungszwecken ist, ist Pascal aber sehr viel genauer. Pascal zwingt den Programmierer dazu, diszipliniert und strukturiert zu entwickeln. Pascal wurde später weiter entwickelt und in 1978 wurde eine neue umbenannte Version Modula-2 veröffentlicht. Modula-2 und weitere Pascal Versionen existierten aber weiterhin gleichzeitig. Es existierten sehr viele Programmiersprachen in den 70er Jahren. Das Verteidigungsministerium der USA fand, dass sie zu viele Programmiersprachen intern nutzen (ca. 450 Sprachen) und entschieden, dass sie nur noch eine Sprache für alle Projekte verwenden sollten. Es war aber gar nicht so einfach, eine Sprache zu finden, die alle nötigen Kriterien abdeckte. Denn viele Programmiersprachen waren nicht standardisiert sondern vom Anbieter abhängig. Deshalb wurde Ada entworfen. Ada wurde benannt nach Ada Lovelace, die Frau, die den Algorithmus für die Bernoulli-Zahlen entwickelt hatte. Die Entwicklung von Ada wurde vom US-Verteidigungsministerium unterstützt und gefördert. Ada’s Erscheinungsbild ähnelt Pascal und Modula-2. Sie gilt als die erste standardisierte Hochsprache.
Einen Compiler zu bauen würde den Rahmen dieses Kurses sprengen, allerdings wollen wir uns ein Programm anschauen, das tatsächlich sich selbst, also seinen eigenen Programmcode ändert:


{{Kasten blass|<tt>-- Hello World in Ada<br>with Text_IO;<br>procedure Hello_World is<br><br>begin<br>&ensp;&ensp;&ensp; Text_IO.Put_Line("Hello World!");<br>end Hello_World;</tt> }}
Zum Vergleich das "Hello World"-Programm in Ada<br>




Zwei Sprachen, die Hand in Hand gehen, sind C und C++. C++ ist die objektorientierte Erweiterung von C. C wurde in 1972 entworfen für das Betriebssystem Unix. Es setzte sich gegenüber BASIC für allgemeine Anwendungsprogramme durch. Ab 1979 wurde C++ dann von Bjarne Strousturp bei AT&T als Erweiterung von C entwickelt. C++ ermöglichte eine effiziente und maschinennahe Programmierung. Sie wollten C mit der Erweiterung eines Klassenkonzepts mit Datenkapselung entwickeln. C++ erschien letztendlich 1985.
{{Aufgabe|'''a) Mut zur Selbst-Veränderung'''


{{Kasten blass|<tt>// Hello World in C++ (pre-ISO)<br>#include <iostream.h><br>main()<br>{<br>&ensp;&ensp;&ensp;cout << "Hello World!" << endl;<br>&ensp;&ensp;&ensp; return 0;<br>}</tt> }}
Untersuchen Sie das folgende Programm und beschreiben Sie seine Funktion:}}
Das "Hello World"-Programm in C++ <br>


==Programmiersprachen in der Welt von heute==


Die Objektorientierung setzte sich als Standard immer weiter durch und weitere Versionen älterer Programmiersprachen wurden mit Objektorientierung ergänzt. Es führte oft zu Problemen, wenn Sprachen, die nicht für Objektorientierung „gebaut“ wurden, mit objektorientierten Erweiterungen erweitert werden. Es gab zu der Zeit keine Sprache, die sowohl prozedurale als auch objektorientierte Programmiermöglichkeiten stellte. Eiffel war ein früher Versuch in diese Richtung. Heute nimmt man in diesem Zuge meistens Java. Java wurde in den Jahren 1991 bis 1992 im Auftrag des US-amerikanischen Computerherstellers Sun Microsystems entwickelt. James Gosling was der Hauptentwickler von Java. Java sollte u.a. eine einfache objektorientierte, verteilte Programmiersprache sein, die sowohl sicher als auch leistungsfähig ist. C#, eine weitere Erweiterung von C und C++, verfolgt ähnliche Ziele wie Java.
[[Datei:Pd_Johnny_Selfmodifying.png | 200px]]


Mit dem Internet in den 1990er Jahren kamen auch Skript-Sprachen wie z.B. HTML (Hypertext Markup Language) und PHP („Personal Home Page Tools“). HTML wurde das erste Mal am 13. März 1989 von Tim Berners-Lee am Cern in Genf festgelegt. Die Urversion von HTML wurde in 1992 veröffentlicht. PHP ist eine Skriptsprache, die eine an C angelehnte Syntax hat. PHP wird hauptsächlich zur Erstellung von dynamischen Webseiten oder Webanwendungen verwendet. Es wurde erstmals 1995 von Rasmus Lerdorf entwickelt und veröffentlicht.
''' b) Ein weiteres Programm, das sich verändert'''


{{Kasten blass|<tt><?php<br>&ensp;&ensp;&ensp;// Hello World in PHP<br>&ensp;&ensp;&ensp;echo 'Hello World!';<br>?></tt> }}
Modifizieren Sie das Programm so, dass es nicht ständig ein und die selbe Zahl schreibt, sondern fortlaufende Zahlen, also z.B. 42, 43, 44, 45 usw. Außerdem soll das Programm diese Zahlen nur in die ungeraden Speicherstellen ab 100 schreiben, also in die 101, die 103 usw.


Das Internet ist eine völlig neue Grundlage für das Programmieren und für Software. So rückten Aspekte wie Codesicherheit und Portabilität weiter in den Vordergrund. Es gab allerdings keine fundamentalen Neuerungen in den Programmiersprachen aber es wurde mehr wert auf automatische Speicherbereinigung und starke oder statische Typisierung gelegt. Obwohl die objektorientierten Programmiersprachen an Beliebtheit keineswegs verloren haben, wurden noch weitere erfunden und es wurden auch graphische Notationsformen, wie z.B. UML (Unified Modelling Language), entworfen, die sich durch ein erhöhtes Abstraktionslevel auszeichnen.
{{Lösung versteckt|
<pre>
000 : TAKE 010
001 : SAVE 101
002 : INC  001
003 : INC  001
004 : INC  010
005 : DEC  011
006 : TST  011
007 : JMP  000
---
010 : 42
011 : 13
</pre>


==Was hast du gelernt? → Quiz==
}}


==Literaturverzeichnis==
{{Fortsetzung|
vorher=Aufbau eines Rechners|vorherlink=Johnny-Simulator/Aufbau eines Rechners|
weiter=Vom Makrobefehl zu den Mikrobefehlen – das Steuerwerk|weiterlink=Johnny-Simulator/Vom Makrobefehl zu den Mikrobefehlen – das Steuerwerk|
}}
{{Johnny-Simulator}}

Aktuelle Version vom 1. September 2019, 06:47 Uhr

Wie schon im Vorwort gesagt, sieht man einem modernen Rechner seine Funktion nicht an. Um besser zu verstehen, wie Rechner funktionieren, greifen wir deshalb auf eine Simulation zurück. Der Simulator „Johnny“ ist nach dem genialen Entwickler der heutigen Rechnerstruktur, John von NeumannWikipedia-logo.png (1903-1957) benannt: Privat war von Neumann für seine Partys berühmt und sein Spitzname war wohl auch deshalb „Good Time Johnny“, worauf der Name des Simulators anspielt.

Der Johnny-Simulator konzentriert sich auf CPU mit Rechenwerk und Steuerwerk und den Arbeitsspeicher. Die Benutzungsoberfläche entspricht dabei etwa dem obigen Blockdiagramm:

Pd Johnny hidden CU.png


Beim Start ist die Struktur des Steuerwerks (Control Unit) noch verdeckt, um die Dinge zunächst einfach zu halten. Der Inhalt des RAM kann im Simulator gelöscht, aus einer Datei geladen bzw. in eine Datei geschrieben werden.


Vieles ist beim Johnny-Simulator einfacher als bei einem richtigen Rechner. So rechnen praktisch alle Rechner im Binärsystem, während der Simulator in unserem gewohnten Dezimalsystem rechnet. Auch die Funktion des Bus-Systems ist gegenüber echten Rechnern vereinfacht: Normalerweise können sich Bus-Syteme keine Daten merken, sondern sind lediglich Verbindungswege.

All diese Vereinfachungen sollen den Benutzern des Simulators die Bedienung vereinfachen, die Grundprinzipien des Rechners bleiben jedoch erhalten.

Der Simulator kann vom Bildungsserver des Landes Rheinland-Pfalz, vom Server des Heise-Verlags oder direkt vom SourceForge-Server ohne Kosten heruntergeladen werden. Das Programm ist ein OpenSource-Projekt und kann unter der Programmierumgebung Lazarus selbst weiterentwickelt werden.

Zahlen im Arbeitsspeicher (RAM)

Betrachten wir zunächst den Arbeitsspeicher. Dieser ist im Simulator als eine Tabelle dargestellt, was seiner tatsächlichen Struktur recht nahe kommt. Jede Zeile der Tabelle hat eine Nummer, die so genannte Adresse (Spalte „Adr“). Statt von Zeilen spricht man auch von Speicherstellen. In jeder Speicherstelle kann eine Zahl zwischen 0 und 19999 abgelegt werden. Aus technischen Gründen sind dabei die 10000er- und die 1000er-Stelle in einer gesonderten Spalte „Hi“, die 100er-, die 10er- und die 1er-Stelle „Lo“ abgelegt. Die hinteren beiden Spalten „Asm“ und „Op“ werden später eine Rolle spielen, sie sind aber nur Kommentare. Der eigentliche RAM-Inhalt sind die Spalten „Hi“ und „Lo“.

Die Inhalte der einzelnen Speicherstellen kann man (im Simulator) entweder durch Anklicken der entsprechenden Zeile, oder aber über das Bus-System des Simulators ändern.

Pd Johnny RAM.png


Zunächst muss die richtige Zeile, also Adresse gewählt werden. Dies tut man, indem man die Adresse der betreffenden Zeile am so genannten Adressbus oben links im Textfeld eingibt, mit dem nebenstehenden Knopf diese Zahl auf den Adressbus legt. Danach legt man analog die Zahl, die in den RAM geschrieben werden soll, auf den Datenbus und drückt auf den Knopf „db->ram“.

Rechnen mit Johnny

Betrachten wir nun das Rechenwerk (Arithmetic Logic Unit). Diese kann mit Daten, die auf dem Datenbus liegen, rechnen. Kernstück des Rechenwerks ist der so genannte Akkumulator (lat.: „Sammler“). In ihm sammeln sich die Rechenergebnisse an. Drückt man z.B. auf die Taste plus, so wird der Wert auf dem Datenbus zum Wert des Akkumulators addiert und das Ergebnis wieder im Akkumulator gespeichert. Johnny beherrscht von sich aus nur die beiden Rechenoperationen plus und minus, alles andere muss man ihm sozusagen beibringen (mehr dazu später). Der Wert des Akkumulators kann auch wieder auf den Datenbus kopiert werden (mit acc->db). Daneben gibt es noch ein paar andere Möglichkeiten, den Akkumulator zu manipulieren, die Sie aber auch leicht selbst herausfinden können. Wichtig für später ist auch die Kontrolllampe links neben dem Akkumulator. Diese zeigt an, ob der Inhalt des Akkumulators gerade den Wert 0 hat.

Pd Johnny ALU.png


Aufgabe

Herunterladen und Inbetriebnahme von JOHNNY Falls der Simulator "JOHNNY" auf Ihrem Rechner noch nicht installiert ist, laden Sie ihn von einer der oben genannten Download-Seiten herunter. Unter Windows-Betriebssystemen muss "JOHNNY" muss nicht installiert werden, es muss lediglich die heruntergeladene ZIP-Datei in einen beliebigen Ordner ausgepackt und die Datei "johnny.exe" ausgeführt werden.

Anmerkung für Linux-Benutzer:
Wenn Sie "Johnny" unter einem Linux-Betriebssystem ohne Emulation betreiben möchten, kann man die Quelldateien herunterladen und unter Verwendung der Entwicklungsumgebung Lazarus kompilieren.

Mikrobefehle im Handbetrieb

Bis hierher hat Ihnen das Skript nur Dinge beschrieben. Vieles jedoch können Sie auch selbst herausfinden.


a) Die Bedeutung eniger Mikrobefehle

Die Knöpfe des Simulators entsprechen Aktionen, die man als „Mikrobefehle“ bezeichnet; sie sind die kleinsten Befehlseinheiten, mit denen der Prozessor gesteuert werden kann.Probieren Sie die folgenden Mikrobefehle aus und ordnen Sie die Lösungen zu:

db->ram Businhalt auf die aktive Adresse des RAM legen
ram->db RAM-Inhalt der aktiven Adresse auf den Bus legen
acc:=0 Akkumulator auf 0 setzen
acc++ Inhalt des Akkumulators um 1 erhöhen
acc-- Inhalt des Akkumulators um 1 erniedrigen
db->acc Inhalt des Datenbusses in den Akkumulator kopieren
plus Inhalt des Datenbusses zum Akkumulator hinzuaddieren
minus Inhalt des Datenbusses vom Akkumulator subtrahieren
acc->db Inhalt des Akkumulators auf den Datenbus kopieren

b) Rechnen mit Mikrobefehlen

Angenommen, die Adresse „42“ würde bereits auf dem Adressbus stehen. Welche Mikrobefehle muss man nacheinander ausführen, um den Wert in der Speicherstelle mit der Adresse 42 zu verdoppeln (unabhängig davon, welche Zahl vorher gerade im Akku steht).

Zum Testen mit dem Simulator sollte man vorher die Speicherstelle 42 mit einem Wert >0 belegen, denn sonst kann man die Verdopplung natürlich nicht gut erkennen.

ram->db
db->acc
plus
acc->db
db->ram

Makrobefehle als praktische Abkürzung

Wiederkehrende Befehlsfolgen

Aufgabe

Mikro- und Makrobefehle

Beschreiben Sie in der mittleren Spalte, mit welcher Folge von Aktionen man mit dem Simulator die Zahlen in den Speicherstellen 10,11 und 12 addieren und dann die Zahl in Speicherstelle 14 davon subtrahieren kann. Das Ergebnis soll in der Speicherstelle mit der Adresse 15 abgespeichert werden. (Bei den letzten drei Zeilen kann auch eine andere Lösung als die "Musterlösung" richtig sein, aber diese Lernumgebung ist nicht ganz so flexibel ;-).

Neben den schon bekannten Mikrobefehlen brauchen wir zusätzlich noch eine Anweisung wie „Lege Adressse ... auf den Adressbus“ wobei „...“ für die entsprechende Adresse, also die Nummer der Zeile im RAM steht.


Ordnen Sie danach in der rechte Spalte "Abkürzungen" wie TAKE 10 oder SUB 14 für die Befehlsfolgen in der mittleren Spalte zu.


1 Lege Adresse 10 auf den Adressbus TAKE 10
2 ram->db
3 db->acc

4 Lege Adresse 11 auf den Adressbus ADD 11
5 ram->db
6 plus

7 Lege Adresse 12 auf den Adressbus ADD 12
8 ram->db
9 plus

10 Lege Adresse 14 auf den Adressbus SUB 14
11 ram->db
12 minus

13 Lege Adresse 15 auf den Adressbus SAVE 15
14 acc->db
15 db->ram


Man nennt ein Programm bestehend aus solchen Abkürzungen auch Maschinenprogramm, die einzelnen Befehle darin Makrobefehle, da sie mehrere Mikrobefehle zu größeren Einheiten zusammenfassen.

Der Speicher als Programmspeicher

Am Arbeitsauftrag 5 merkt man: Maschinenprograme aus Makrobefehlen lassen sich leichter verstehen und vor allem viel kompakter hinschreiben als eine Folge von Mikrobefehlen. Die Frage ist nur: wo genau hinschreiben. In den ersten Rechnern wie dem Zuse Z3 waren die Programme z.B. auf Lochstreifen gespeichert. John von Neumann hatte die Idee, nicht nur Zahlen sondern auch Programme in ein und demselben Arbeitsspeicher abzulegen. Man spricht in diesem Zusammenhang von der von-Neumann-Architektur.

Wie aber lassen sich Makrobefehle im Speicher (RAM) ablegen? Wir sehen, dass der RAM nur Zahlen – in unserem Simulator zwischen 0 und 19999 – ablegen kann.

Wie also kann man einen Befehl wie ADD 12 im RAM speichern?

Zunächst überlegt man sich hierfür, dass dieser Befehl eigentlich zweigeteilt ist, einmal in die eigentliche Operation „ADD“ und dann in die Adresse, auf die sich der Befehl bezieht. Der Speicher unseres Simulationsrechners enthält 1000 Speicherstellen mit Adressen zwischen 0 und 999. Um die Adressen darzustellen brauchen wir also die 1er-, die 10er- und die 100er-Stelle einer Zahl. Die verbliebenen zwei Stellen, die 1000er- und die 10000er-Stelle können wir verwenden, um die Operation selbst zu beschreiben, nicht mit einer Zeichenfolge wie „Add“ sondern mit einer Zahl etwa 20000.

Damit wird aus dem Befehl „ADD 12“ nun einfach die Zahl „20012“. Damit man sich als Benutzer des Simulators die Zahlen für die einzelnen Operationen nicht merken muss, kann man bei der Eingabe von Werten in den RAM ein Auswahlmenü anklicken, dass direkt die einzelnen Operationen mit ihren entsprechenden Zahlen zur Auswahl stellt.

Pd Johnny Edit RAM.png

Auf diese Weise kann man ohne Auswendiglernen der entsprechenden Zahlen (der so genannten Operation Codes, kurz Opcodes genannt) ein Maschinenprogramm eingeben. Neben dem eigentlichen Speicherinhalt, der – wie wir wissen – nur aus den Zahlen besteht, zeigt der Simulator als Kommentar die zugehörigen Makrobefehle an.

Pd Johnny RAM Macro Program.png

Erste Maschinenprogramme

Aufgabe
Geben Sie das folgende Maschinenprogramm ab der Adresse 000 ein


000: TAKE 010
001: ADD 011
002: ADD 012
003: SUB 014
004: SAVE 015

Testen Sie das Programm, in dem sie unter den verschiedenen Adressen 10,11,12 und 14 verschiedene Zahlen vor dem Programmlauf speichern.


b) Weitere Befehle
Neben den Befehlen TAKE, ADD, SUB und SAVE gibt es noch einige andere. Finden Sie anhand des folgenden Programms heraus, was die darin enthaltenen Befehle bewirken und kreuzen Sie die richtige Beschreibung der Funktion an.


000: INC 010
001: INC 010
002: DEC 010
003: INC 010
004: INC 010
005: NULL 010


1 INC<Adresse>

Erniedrigt die Speicherzelle mit der Adresse um 1
Erhöht die Speicherzelle mit der Adresse um 1
Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
Setzt die Speicherzelle mit der Adresse auf den Wert 0

2 DEC<Adresse>

Erniedrigt die Speicherzelle mit der Adresse um 1
Erhöht die Speicherzelle mit der Adresse um 1
Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
Setzt die Speicherzelle mit der Adresse auf den Wert 0

3 NULL<Adresse>

Erniedrigt die Speicherzelle mit der Adresse um 1
Erhöht die Speicherzelle mit der Adresse um 1
Verschiebt den Inhalt der Speicherstelle mit der Adresse auf einen Platz weiter hinten
Setzt die Speicherzelle mit der Adresse auf den Wert 0


Sprünge und Bedingungen

Bisher sind unsere Programme immer von einem Befehl zum nächsten gegangen. Für kompliziertere Aufgaben reicht das jedoch nicht. Wie Sie wahrscheinlich aus anderen Programmiersprachen wissen, muss es auch Verzweigungen (if ... then ... else ...) oder Schleifen (etwa while ... do ...) geben können.


Aufgabe

a) Kreuz und quer durch's Programm

Testen Sie das folgende Programm und finden Sie heraus, was die Befehle JMP und TST bewirken. Setzen Sie vor Ausführung des Programms die Speicherstelle Nr. 10 auf den Wert 5 und die Speicherstelle Nr. 11 auf den Wert 42.

000: INC 010
001: DEC 010
002: TST 010
003: JMP 001
004: TAKE 011
005: SAVE 012

Beschreiben Sie die Funktion der beiden Befehle JMP und TST in kurzer Form:


1 JMP<Adresse>

Die Speicherstelle an der Adresse springt um eins nach oben.
Das Programm spingt in seiner Ausführung an den Befehl, der an der Adresse steht.
An der Adresse springt das Programm aus der Ausführung heraus.
Testet, ob die Speicherstelle an der Adresse den Wert 0 hat und überspringt in diesem Fall den nächsten Befehl.
Testet, ob die Speicherstelle an der Adresse größer als 1 ist und springt in diesem Fall an den Anfang des Programms.

2 TST<Adresse>

Die Speicherstelle an der Adresse springt um eins nach oben.
Das Programm spingt in seiner Ausführung an den Befehl, der an der Adresse steht.
An der Adresse springt das Programm aus der Ausführung heraus.
Testet, ob die Speicherstelle an der Adresse den Wert 0 hat und überspringt in diesem Fall den nächsten Befehl.
Testet, ob die Speicherstelle an der Adresse größer als 1 ist und springt in diesem Fall an den Anfang des Programms.


b) Malnehmen - eigentlich gar nicht eingebaut

Nun wird es komplizierter: Konstruieren Sie ein Maschinenprogramm, das die Zahlen in den Speicherstellen 10 und 11 miteinander multipliziert und das Ergebnis in der Speicherstelle 12 abspeichert.

Tipp: Das Programm soll folgendes tun:

  • Die Speicherstelle 12 zunächst auf 0 setzen.
  • Den Wert der Speicherstelle 12 in den Akku holen, den Wert der Speicherstelle 10 addieren das Ergebnis und wieder in Speicherzelle 12 abspeichern.
  • Nach einer solchen Addition den Wert der Speicherstelle 11 um 1 erniedrigen.
  • Das Verfahren ab dem 2. Punkt so lange wiederholen Sie, bis die Speicherstelle 11 den Wert 0 hat.

Testen Sie Ihr Programm, ob es auch funktioniert. Hierfür sollten die Speicherstellen 10 und 11 auf kleine, von Null verschiedene Werte gesetzt werden, also z.B. 5 und 3.

000 : NULL 012
001 : TAKE 012
002 : ADD  010
003 : SAVE 012
004 : DEC  011
005 : TST  011
006 : JMP  001
007 : HLT  000

(Der Befehl HLT 000 stoppt den Programmablauf im Simulator ab).


c) Dividieren - einfache Version Schreiben Sie ein Programm, das eine Zahl durch eine andere Zahl teilt. Betrachten Sie dabei zunächst nur den Fall, dass die größere Zahl in Speicherzelle 10 tatsächlich durch die kleinere in Speicherzelle 11 ganzzahlig teilbar ist.

d) Größer, kleiner oder gleich Wir betrachten zwei Zahlen in den Speicherstellen 10 und 11. JOHNNY soll entscheiden, ob die Zahl in Speicherstelle 10 größer ist als die in Speicherstelle 11, gleich groß ist, oder aber kleiner. Je nachdem soll in Speicherstelle 12 nach der Untersuchung der Wert 1, 2 oder 3 stehen.
Tipp:

e) Dividieren - komplizierte Version Schreiben Sie ein Programm, das eine Zahl durch eine andere Zahl teilt. Und zwar in der Art, wie wir vermutlich alle das in der Grundschule gelernt haben:
17:5 = 3 Rest 2.
Die Zahl in Speicherstelle 10 soll dividiert werden durch die Zahl in Speicherstelle 11. Der ganzzahlige Quotient soll danach in Speicherstelle 12, der Rest in Speicherstelle 13 gespeichert sein. Tipp: Vielleicht hilft Ihnen das Ergebnis aus Aufgabe d)


Programme, die sich selbst ändern

Wie bereits gesagt, ist das besondere an von Neumanns Idee zur Speicherung von Programmen, dass Programmcode („Was soll gemacht werden?“) und Programmdaten („Womit soll etwas gemacht werden?“) in ein und dem selben Arbeitsspeicher gespeichert sind.

Mehr noch: Das Programm besteht selbst aus Daten, der einzige Unterschied ist, dass diese Daten, die einem Programm entsprechen, von der Maschine als Befehle interpretiert und ausgeführt werden.

Dies hat eine wichtige Konsequenz, die zunächst ein wenig abwegig klingt: Programme können sich selbst (oder andere Programme im Arbeitsspeicher) verändern, indem sie die entsprechenden Daten im Speicher manipulieren. Compiler z.B. lesen einen Text in einer höheren Programmiersprache und übersetzen ihn in ein Maschinenprogramm. So etwas geht in dieser Weise nur dank der von-Neumann-Architektur.

Einen Compiler zu bauen würde den Rahmen dieses Kurses sprengen, allerdings wollen wir uns ein Programm anschauen, das tatsächlich sich selbst, also seinen eigenen Programmcode ändert:



Aufgabe

a) Mut zur Selbst-Veränderung

Untersuchen Sie das folgende Programm und beschreiben Sie seine Funktion:


Pd Johnny Selfmodifying.png

b) Ein weiteres Programm, das sich verändert

Modifizieren Sie das Programm so, dass es nicht ständig ein und die selbe Zahl schreibt, sondern fortlaufende Zahlen, also z.B. 42, 43, 44, 45 usw. Außerdem soll das Programm diese Zahlen nur in die ungeraden Speicherstellen ab 100 schreiben, also in die 101, die 103 usw.

000 : TAKE 010
001 : SAVE 101
002 : INC  001
003 : INC  001
004 : INC  010
005 : DEC  011
006 : TST  011
007 : JMP  000
---
010 : 42
011 : 13