Hallo, ich begrüße Sie zur Vorlesung Rechenorganisation.
Wir sind zur Zeit bei der Instruktionssatzarchitektur ISA.
Ich hatte das letzte Mal gesagt, dass es hier nicht um eine Harte
Architektur geht.
Es geht darum, wie sieht ein Instruktionssatz aus?
Wie ist der zusammengebaut?
Und was war der Hintergrund für eine Instruktionssatzarchitektur?
Was hatte ich da als Hauptgrund genannt, warum es Sinn macht, so etwas
festzulegen?
Unter anderem gibt es viele Gründe.
Also der wichtigste Grund dabei war, man kann es auf verschiedenen
Hardware -Plattformen implementieren.
Es gibt also unterschiedliche Mikroprozessoren, die ein und dieselbe
Instruktionssatzarchitektur ausführen können.
Das heißt, man legt diese Architektur also fest und der Hardware
-Entwickler ist dann völlig frei zu entscheiden, wie ein
Mikroprozessor intern aussieht, solange er diese
Instruktionssatzarchitektur ausführen kann.
Es gibt dann natürlich Unterschiede.
Also man kann es ausführen, aber was könnte dann zum Beispiel
unterschiedlich sein?
Also sozusagen, was legt die Instruktionssatzarchitektur da nicht
fest?
Was sie gerade festlegt, habe ich versucht zu sagen.
Und was legt sie nicht fest?
Worüber gibt sie keine Auskunft?
Genau, sehr gut.
Also wieviel Takte, wie schnell läuft das Ganze, was hat das für eine
Performance?
Also ich habe hin und wieder mal Beispiele gegeben, habe gesagt, da
wird irgendwo so ein Load gemacht, das dauert vielleicht 18
Taktzyklen.
Da habe ich immer gesagt, vielleicht, weil ich es eben auch nicht
weiß, weil es davon abhängt, auf welche Architektur das dann wirklich
läuft.
Also das können wir dem Code der Instruktionssatzarchitektur nicht
ansehen.
Das ist so gewollt.
Man kann also sozusagen größere oder kleinere Prozessoren bauen.
Die können all dasselbe ausführen.
Aber, wie so richtig gesagt, die Performance ist unterschiedlich.
Und zum Beispiel auch Dinge wie Leistungsverbrauch und solche Dinge
können auch unterschiedlich sein.
Also wir haben hier eine ganz klare Abgrenzung von Dingen.
Instruktionssatzarchitektur auf der einen Seite, Hardwarearchitektur
auf der anderen.
Das ist also ganz wichtig.
Wir lernen also nicht Dinge, die spezifisch für eine Architektur sind,
auch wenn wir dafür hin und wieder ein paar Beispiele geben.
Übrigens ist das Mikro überall gut zu verstehen.
Ich habe die Lautstärke jetzt so versucht einzustellen, dass es gut
ist.
Es klingt für mich okay von vorne.
Wie sieht es vorne hinten aus?
Zu laut, zu leise?
Also wenn es nicht okay ist, sagen Sie es uns.
Gut, dann haben wir angefangen und haben uns mit Ausführungsmodellen
beschäftigt.
Und da hatte ich gesagt, die Ausführungsmodelle, die hängen natürlich
dann davon ab, was an Architektur vorhanden ist.
Aber das sind sehr Standard-Dinge, die hier aufgelistet sind.
So etwas hat jede Architektur.
Aber da ist nichts Besonderes dabei.
Entsprechend der Ausführungsmodelle hatte ich dann die Frage gestellt,
wie sieht das denn aus, wenn ich einen Code habe, wie sieht das
Adressformat aus?
Also wie viele Adressen brauche ich zum Beispiel, um eine Zahl zu
addieren?
Da bräuchte ich erstmal ein Ziel und zwei Quellen.
Und wie schlägt sich das Ganze dann nieder im Adressformat?
Das war die große Preisfrage.
Und da sind wir dann hergekommen und haben gesagt, gut, drei klingt
plausibel, das ist offensichtlich sozusagen, aber es geht auch mit
zwei, mit eins und sogar mit null.
Das heißt, ich kann ein Adressformat haben, wo eben null Adressen
angegeben sind.
Das sind natürlich die Operanten dann irgendwo vorhanden.
Nur der Trick dabei ist, das Ganze ist implizit.
Und das wollen wir uns nochmal anschauen.
Die Folie, das hatten wir uns schon angeschaut, gehe ich nochmal
drüber.
Drei Adressformat, es werden zwei Zahlen addiert, zwei Register, zwei
Quellregister und ein Zielregister zum Beispiel.
Dann kann man das Ganze mit zwei haben, das hatten wir alle schon.
Dann hatten wir das Ganze mit eins und hier haben wir dann aufgehört
in der letzten Vorlesung.
Hier war die Sache dabei, dass das Ganze sich immer auf den
Akkumulator bezieht.
Also bei Default auf den Akkumulator und zusätzlich in diesem Fall
hier auf ein Speicherregister.
Add A heißt dann also, nehme die Adresse, den Inhalt der Adresse
Speicher A zusammen mit dem Akku und schreibe es in den Akku.
Und hier haben wir auch das, was man unter Überdeckung versteht,
überdeckte Adressierung.
Der Akku, der wird als Ziel hier und als Quelle benutzt.
Dann schauen wir uns mal an, wie das mit Null geht, das Ganze.
Hier haben wir einen Stack.
Und was wir hier machen, wir addieren zwei Werte vom Stack und
schreiben das wieder auf den Stack.
Ein Stack, was ist das?
Ich versuche das mal anzumalen.
Wir haben einen Wert.
Nehmen wir das wie einen Wert, den wir auf den Stack legen.
Um den Stack zu anschaulichen, was es ist, schreiben wir noch was
drauf.
Das ist also ein Stapel, wir schreiben da noch was drauf, das ist ein
weiterer Wert.
Wir schreiben noch was drauf, ein weiterer Wert.
Dann kann es natürlich auch sein, wir holen was runter.
Dann würden wir das hier oben zuerst runterholen und dann wird das
Ganze gelöscht.
Oder wir schreiben wieder was drauf.
Also machen wir das nochmal.
Wir haben drei Einträge auf dem Stack, haben das drauf geschoben.
Dann zum Beispiel würden wir was wegnehmen.
Holen wir runter, dann nehmen wir wieder was weg.
Dann schreiben wir wieder was drauf.
Dann wieder was wegnehmen und so weiter.
Also Sie sehen das Prinzip.
Ein Stack ist etwas, was nach dem Prinzip folgt,
das in First Out.
Macht das Sinn?
Also das, was ich zuletzt auf den Stack hier drauf geschrieben habe,
hole ich mir als erstes wieder runter.
Nach diesem Prinzip funktioniert dann der Add-Befehl.
Das ist nämlich, den Top of Stack nehme, los was da drunter ist, diese
beiden addiere und dann es zurück auf den Stack schreibe.
Also in diesem Fall würde das heißen, ich nehme das plus das und
schreibe es wieder hier zurück.
Das heißt diese Addition jetzt.
Also alles implizit angegeben, man muss halt wissen, was es heißt.
So jetzt hätte ich hier gesagt, Last in First Out.
Gibt es auch andere Dinge, offensichtlich.
Was ich zuletzt drauf schreibe, das ist ein ganz wichtiges Prinzip,
wenn Sie es nicht verstanden haben, müssen Sie fragen natürlich.
Was ich zuletzt hier drauf schiebe, hole ich mir als erstes wieder
runter, weil ich nur von oben drauf zugreife.
Und in Englisch halt Last in First Out.
Meine Frage ist, gibt es andere Prinzipien?
Oder ist das immer so?
Gibt es nur Stacks?
Ich lege irgendwas oben drauf, hole es mir von oben runter.
First in First Out, genau, also sowas gibt es auch.
Also es gibt auch sowas, wo man etwas, was man zuerst reinschiebt oder
speichert, als erstes wieder rauskommt.
Was wäre das?
Das wäre zum Beispiel ein Register, was so aussieht.
Nehmen wir an, ich habe so ein Register.
Nehmen wir an, ich habe so ein paar Speicherzählen.
Jedes Quadrat oder Rechteck soll eine Speicherzähle darstellen.
Und dann würde ich in das Register hier einen Wert reinschreiben, hier
kommen die Werte raus.
Und jedes Mal, wenn ich einen Wert hier reinschiebe, passiert
Folgendes.
Der von hier geht nach da.
Und das heißt, das Ganze wird in diesem Beispiel von links nach rechts
durchgeschoben.
Wie Sie sehen, was hier zuerst reinkommt, kommt hier auch wieder
zuerst raus.
Und das ganze Prinzip nennt sich dann entsprechend First In First Out.
Also First In First Out.
Da haben wir jetzt keinen Befehl für.
Ich wollte es nur mal zeigen, damit dieses Prinzip, dieses Last In
First Out, dann auch klar wird.
Oder um einfach zu sehen, es gibt auch andere Dinge, wie man Daten
speichern kann.
Gut, dann haben wir das verstanden.
Dann haben wir uns angeschaut, die verschiedenen Adressformate und
Beispiele dafür.
Das sind natürlich immer Beispielbefehle, da gibt es noch mehr
Befehle.
Aber vor allen Dingen wollen wir uns jetzt auch mal ein Programm
anschauen, das für diese verschiedenen Architekturen hier geschrieben
ist.
Ich kann nämlich für all diese Architekturen das Gleiche machen.
Ich habe öfters das Beispiel, wo irgendwas addiert wird.
Die Frage ist natürlich jetzt, wie mache ich es?
Mache ich es so, mache ich es so, mache ich es so oder mache ich es
so?
Und die Frage, die da vorkommt ist, wie mache ich das überhaupt?
Wie schaut das dann überhaupt aus?
Und dafür haben wir so ein kleines Beispiel hier irgendwo.
Schauen wir mal, hier genau.
Hier haben wir einfach nur ein Beispiel, wo ein Programm, das ist
vielleicht ein bisschen übertrieben, wo ein paar Zahlen hier addiert
werden.
Und diese Zahlen werden addiert in diesen verschiedenen Formaten, in
diesen verschiedenen Adressformaten, die mit verschiedenen
Architekturen, wie wir es eben gesehen haben, verbunden, also
assoziiert sind.
Hier geht es nur um Register, Register.
Ich gehe es nicht im Einzelnen durch, ich versuche es Ihnen
anzudeuten.
Das ist eigentlich offensichtlich.
Hier mache ich folgendes, ich habe den Wert A, der aus dem Speicher
kommt, lade den Register, dasselbe mache ich mit dem Wert B.
So, und jetzt habe ich genau das, hierum geht es uns, nämlich um das
Adressformat 3, also Register, Register, und entsprechend drei
Operanten hier in diesem Fall.
Hier addiere ich die jetzt, und jetzt kommt ein Store-Befehl, jetzt
schreibe ich es ins Speicher zurück, dann hole ich mir wieder was vom
Speicher usw.
und führe den zweiten Teil dieser Berechnung hier.
So, und jetzt kann man das Ganze mit Register-Speicher machen.
Register-Speicher, diese Namen beziehen sich immer jeweils auf diesen
Befehlen hier, auf diesen Hauptbefehl, auf die Addition.
Register, Register, hier geht es nur um Register.
Register-Speicher, hier habe ich jetzt offensichtlich in dem Befehl,
hier ist er, Add B und Register.
Also hier haben wir einen Befehl, einen Additionsbefehl, der in der
Lage ist, auf den Hauptspeicher zuzugreifen, nämlich mit dem B und auf
einen Register und hier entsprechend eine Addition auszuführen.
Hier passiert das Ganze über einen bestimmten Speicher, nämlich über
den Akku.
Das heißt, hier habe ich irgendeinen Register und hier ist das
Implizite immer der Akku.
Das heißt, der Akku dient dafür, den Input für den Befehl zu haben und
dient dann dafür auch, das Ergebnis darauf abzuspeichern.
Und wenn man hier das nachverfolgt, das hier einfach gemacht, A, B, C
sind immer dieselben, dann kann man das hier nachvollziehen.
Hier zum Kellerspeicher kann man vielleicht noch Folgendes sagen, da
gibt es dann eben bestimmte Befehle, da sagt man nicht, dass man
irgendwas speichert, also lädt oder speichert.
Bei dem Stack sagt man, man macht einen Push oder Pop.
Das heißt, man bringt es auf den Stack drauf oder bei dem Pop holt es
von dem Stack herunter.
Sie sehen, hier haben wir wieder den Befehl, die Addition, die sich
auf den Stack bezieht, nämlich bei diesem Beispiel, was wir hier
hatten, oder bei dem Stack, den wir hier hatten, wo war es?
Dann nehme ich die zwei hier oben, den und den, und schreibe es dann
wieder da oben drauf.
Gibt es hierzu Fragen oder irgendwelche Kommentare?
Ist das soweit klar, warum da überhaupt der Unterschied ist?
Ich meine, erstmal ist es ein Unterschied, weil man die Architektur
hat, man kann verschiedene Dinge ausnutzen, da sind natürlich riesige
Unterschiede in dem Programm drin.
Machen wir es einfach mal so, was fällt Ihnen hier zu den
Unterschieden auf?
Weil das sollte man schon immer herausstellen.
Es gibt hier Unterschiede und die Frage als Programmierer würde sich
ja stellen, tue ich es jetzt nach A, B, C oder D?
Wenn ich viele Optionen habe, dann heißt das nicht, dass ich zufällig
dabei vorangehe, sondern mir irgendwas dabei überlege.
Und zwar haben diese verschiedenen Optionen verschiedene
Eigenschaften.
Ein paar sieht man, ein paar sieht man direkt mit dem Auge, sage ich
mal, die Eigenschaften.
Ein paar andere sieht man nicht.
Also was sieht man mit dem Auge hier?
Von Unterschieden.
Also was sieht man?
Jeder sieht irgendwas.
Was sieht man hier?
Was ist der Unterschied?
Diesen Kürzer, genau.
Was so viel heißt wie, wenn ich dieses Programm im Speicher
abspeichere, dann benötige ich weniger Speicherplatz.
Speicherplatz ist limitiert, gerade bei eingebetteten Systemen, wo ich
kleine Geräte habe, die sehr billig sein müssen, vielleicht nur ein
paar Cent kosten, dann habe ich nicht viel Speicher und dann kann es
durchaus Sinn machen, das kürzere Programm zu wählen, weil der
Speicher vielleicht nur ein paar hundert Bytes groß ist.
Das ist einfach kürzer.
Die anderen wiederhole ich nochmal, weil das eben schon in anderen
Antworten auch gefallen ist.
Natürlich gilt auch hier wieder, dass die Ausführungszeit
unterschiedlich groß ist.
Und als generelle Anleitung kann ich hierzu sagen, dass solche
Befehle, die auf den Speicher zugreifen, wie hier oder hier, das Load
usw., die sind besonders zeitaufwendig.
Das heißt, das ist typischerweise nicht zu machen mit einem
Taktzyklus, sondern es dauert typischerweise viele Taktzyklen, weil
ich außerhalb des Prozessors gehen muss, auf den Hauptspeicher und mir
von dort die Daten holen muss und vielleicht kann ich zur Zeit gar
nicht auf den Hauptspeicher zugreifen, weil irgendein anderer
Prozessor, wir haben ja auch Multi-Core, Multi-Prozessor-Systeme, die
zur selben Zeit auf den Speicher zugreifen, vielleicht muss ich
warten, vielleicht habe ich Wartezyklen.
Also als generelle Regel geht, Dinge zu vermeiden, Befehle zu
vermeiden, die viel auf den Hauptspeicher zugreifen.
Natürlich, wenn ich große Datensätze habe, habe ich überhaupt keine
Möglichkeit, das zu vermeiden, weil nicht alles in den Prozessor
reinpasst, auf den Registersatz.
Aber wenn ich die Möglichkeit habe, mache ich es so nah an der
Berechnung wie möglich.
Also wenn die Daten, die ich berechnen möchte, mit denen ich irgendwas
tun möchte, die ich verbinden möchte, wenn die möglichst nah an dem
Ort sind, wo sie verarbeitet werden, das sind die Faustregel, dann ist
es auch am günstigsten.
Dann ist es am günstigsten für die Ausführungszeit, dann ist es am
günstigsten für den Leistungsverbrauch zum Beispiel, es hat also sehr
viele Vorteile.
Das Problem ist ganz einfach, dass die Ressourcen, die nah an dem Ort
sind, wo eben ausgeführt wird, die sind sehr limitiert.
Also muss ich in meinem Programm, wenn ich irgendetwas programmiere,
stellt sich typischerweise nicht die Frage, ob ich A, B, C oder D
wähle, sondern es stellt sich die Frage, für welchen Teil des
Programmes wende ich das an, für welchen Teil des Programms das und
für welchen Teil des Programms das.
Das heißt, jedes Programm hätte eigentlich im besten Fall eine
Mischung aus verschiedenen Stilen der Programmierung hier, um alles,
was ich zur Verfügung habe, möglichst gut einzusetzen.
Es würde also keinen Sinn machen, nur dies zu benutzen, weil ich
denke, ich habe auf dem Stack nicht viel Platz.
Na gut, so viel Platz ich habe, den kann ich dann entsprechend
benutzen.
Warum nicht?
Also das Intelligente ist sozusagen Programmieren, bzw.
so intelligent steckt letzten Endes ein Compiler, der das hier
abnimmt.
Sie würden dann typischerweise in Hochspare programmieren, also ein C
z.B., C++, und der Compiler würde sich überlegen, ob er das, das, das
oder das hier annimmt.
Natürlich hat der Compiler auch nicht die Möglichkeit, sich das alles
zu überlegen, denn bestimmte Daten, die sind halt mal irgendwo im
Hauptspeicher und die müssen da geholt werden.
Und das ist dann ganz einfach ein Fakt, da hat man dann keine
Freiheitskarten mehr.
Aber ich hoffe, Sie kriegen zumindest eine Idee, dass das sinnvolle
Optionen sind und es nicht geht darum, sich für eines zu entscheiden,
sondern dass das Sinnvolle darin besteht, alles möglichst sinnvoll
zusammen einzusetzen.
Gut.
Und da wir jetzt natürlich unterschiedliche Adressformate und so ein
Zeugs alles haben, dann sind natürlich auch die Codierungen der
Befehle unterschiedlich.
Das heißt, es wäre natürlich einerseits schön, wenn ich sage, ich habe
ein Befehlswort, das hat immer 32 Bits, und das Bit 17 bis 21 gibt mir
immer den Opcode wieder, und das Bit 21 bis 29 gibt mir immer z.B.
den Datenwert eines Quelloperanten oder irgend sowas.
Das wäre schön, wenn ich nur ein Befehlsformat hätte und darin alles
codieren könnte.
Aber ich kann das nicht.
Und so sagen wir uns jetzt später noch ansehen, warum das nicht so
ist.
Einerseits ist es natürlich so, wir haben verschiedene Datentypen.
Da möchte ich nicht nochmal im Einzelnen drauf eingehen, das hatten
wir das letzte Mal schon.
Aber hier ist es nochmal aufgelistet mit Hinblick darauf, ich habe
verschiedene Datentypen und da ich verschiedene Datentypen habe, sieht
mein Befehlsformat unterschiedlich aus.
Es macht einen Unterschied, ob ich in einem Befehlswort einen Byte
unterbringen muss oder ein Wort von zwei Byte oder vier Byte.
Also es gibt unterschiedliche Datentypen, das wissen wir schon.
Und wir haben auch Gleitkammerzahlen das letzte Mal gesehen.
Auch sowas gibt es.
Natürlich kann ein Prozessor auch mit Gleitkammerzahlen rechnen, wenn
es dafür keinen Befehl gibt, also keinen Hardwarebefehl.
Das heißt keine ALU, die eine Addition direkt ausführt, sondern eine
Operation mit einer Gleitkammerzahl, die kann natürlich auch
zurückgeführt werden auf Addition.
Also der Punkt hier ist, selbst wenn die Hardware keine Multiplikation
mit Gleitkammerzahlen kann, dann kann ich das zum Beispiel, da brauche
ich noch ein bisschen mehr für, da brauche ich auch Shifter und solche
Sachen, Adderer und so weiter, kann ich das mit Bewerkstelligen.
Der Nachteil natürlich ist, wenn ich so ein Hardware Hartford Trated
nicht zur Verfügung habe, dauert es sehr viel länger.
Keine Frage, da gibt es dann Mikroprogrammationen.
Gut, also wir hatten so Datenformate von 8-Bit, 16-Bit, 64-Bit, 32-Bit
und so weiter.
Hier braucht man nicht drüber gehen, das ist alles selbsterklärend.
Das zeigt eigentlich nur, wie typischerweise Daten in einem Speicher
oder in einem Speicherwort abgelegt sind.
Das heißt, wenn ich zum Beispiel ein Datenformat habe, wenn ich ein
Byte habe, und möchte das ablegen im Speicher, dann sieht das
typischerweise so aus, und ich sage typischerweise, dass das least
significant bit ganz rechts ist.
Sie erinnern sich, wir hatten das neulich schon gemacht, da hat man
gesagt MSB, LSB.
Also das Bit, das den geringsten Wert hat, also 2 hoch 0 hier in
diesem Falle, steht ganz rechts, das ist einfach eine Konvention.
Und so gilt es dann auch, wenn man zum Beispiel zwei Worte oder zwei
Bytes in einem Wort abspeichert.
Das untere Wort, also das low byte, steht rechts, also unten, rechts
ist unten in diesem Falle, und das nächst höherwertige kommt
schließlich links an, und so weiter.
Und so gilt das dann für Worte auch.
Das sind Dinge, das sind Konventionen, die muss man sich einmal merken
und weiß es dann letztendlich.
Und wenn ein Prozessor das anders tut, dann würde das im Datenblatt
eines Prozessors, also im Datenblatt oder Datenbuch, würde das
beschrieben werden.
Also das so viel dazu, wie Daten im Rechner abgespeichert werden,
beziehungsweise wie der Prozessor die Daten interpretiert.
Also der Mikroprozessor interpretiert da so, hier finde ich mein
niedrigstes Byte, hier das höchste Byte, und so weiter und so fort.
Das ist die Interpretation.
Wenn meine Interpretation als Programmierer anders ist als die des
Mikroprozessors, dann ist klar, dann habe ich ein falsches Ergebnis
vorliegen.
Deswegen muss ich das wissen.
So, hier sind jetzt nochmal Datentypen.
Es gibt natürlich Bits.
Was ist ein Bit?
Wie wird das adressiert?
Oder ein Bitvektor?
Ein Bitvektor ist eben eine Abfolge von Bits.
Wofür man das braucht, sieht man dann als Programmierer.
Da kann man die verschiedensten Dinge mitmachen.
Da haben wir dann später auch noch Beispiele für.
Vorzeichen, Lose, Dual, Zahl, Zweierkompliment.
Das Zweierkompliment haben wir das letzte Mal gesehen.
Wird so dargestellt, wie da rechts das LSB, links das MSB,
Vorzeichenbit und so weiter.
Dann hat man das BCD, da möchte ich gar nicht so nah drauf eingehen,
weil wie gesagt, das ist eigentlich ein Datenformat, was man sehr
selten noch benutzt.
Oftmals aus Kompatibilitätsgründen.
Wir hatten das neulich schon herausgefunden, das hatte jemand von
Ihnen gesagt, das ist an sich viel weniger effizient, weil ich kann
mit einer BCD-Zahl neun Ziffern, brauche ich nur darzustellen, aber
ich könnte 16 verschiedene von 0 bis 15 darstellen.
Das heißt, der Rest, also ungefähr ein Drittel davon, sind Ressourcen,
die nicht genutzt wurden.
Also von daher ist BCD nicht etwas, was sehr effizient ist.
Gut, dass man die dann noch in verschiedener Art und Weise abspeichern
kann, sei auch gegeben, aber interessiert jetzt an sich nicht weiter.
Hier speichert man Gleitkommazahlen ab, auch letzten Endes
Konventionen.
Es geht nicht darum, das letzten Endes auswendig zu lernen, außer man
hat eben eine Aufgabe, muss damit rechnen, dann muss man es wissen.
Ansonsten variiert das natürlich.
Aber es variiert natürlich mit der Größe, der Genauigkeit.
Aber wie viel Bit für Charakteristik und Mantisse hier entsprechend
angegeben sind, das hatte ich gesagt, das kann man sich nicht einfach
so überlegen, das kann sich der Mikroprozessor auch nicht so einfach
überlegen.
Das wird normiert in der IEEE-Norm.
Und Sie erinnern sich auf den letzten Foliensatz, da hatte ich oben
hingeschrieben, welche IEEE-Norm das letzten Endes ist.
Das ist nicht willkürlich festgelegt, das ist normiert, sodass man die
Bits, die man zur Verfügung hat, so darstellen kann, dass man einen
sehr guten Kompromiss findet, zwischen wie groß ist der bewährte
Bereich, den ich darstellen kann, und die Genauigkeit letztendlich.
Also es geht um die Größe, die maximale Größe einer Zahl, die ich
darstellen kann, die maximal kleinste Zahl natürlich auch, also
Wertebereich, und wie genau kann ich das Ganze darstellen.
Danach ist dann aufgeteilt worden, wie man die Charakteristik, also
wieviel von diesen 64 Bit, die ich hier habe, ich für die
Charakteristik pendiere, und wieviel für die Mantisse.
Also hinter diesen Zahlen, das sind natürlich auch keine willkürlichen
Zahlen, da steckt selbst die Mathematik drin.
Gut, was haben wir noch?
Bitfelder, kann man an sich auch nicht viel zu sagen, außer dass man
natürlich die Bitfelder im Speicher so ablegen kann, dass sie zum
Beispiel auf Byte-Grenzen liegen.
Muss aber nicht sein, wenn ich mit Bitfeldern rechne, dann kann es
sich oftmals anbieten, dass ich ein Offset habe, das heißt ein
Bitfeld, da habe ich halt ein Bit gesetzt oder nicht gesetzt, das ist
die Definition eines Bitfeldes, also von hintereinander, nacheinander
auch von den Bits, und dann kann ich natürlich noch sowas haben wie
ein Offset.
Also ich kann sagen, ich meine hier nicht eine 1 oder eine 0, sondern
ich meine eine 365 oder eine 366 oder irgend so was, also ich habe die
Möglichkeit, hier noch ein Offset zum Beispiel anzugeben.
Und dann muss ein Bitfeld eben nicht an einer Byte-Grenze beginnen,
das kann irgendwo beginnen.
Typischerweise gilt als Regel, da war auch jemand bei mir nach der
Vorlesung, da war so die Frage mit Bitfeldern und so weiter,
typischerweise gilt die Regel, man sollte nicht annehmen, dass ein
bestimmter Datentyp eine bestimmte Länge hat.
Und zu tun, sage ich mal, ein Wort hat 16 Bit, und da ich ja angeblich
weiß, dass ein Wort 16 Bit hat, tue ich mal so und behandle das als
Bitvektor.
Dann habe ich 16 Bit zur Verfügung und tue da irgendwas dran.
Das läuft fatal schief, wenn nämlich auf einem anderen Rechner dann
das entsprechende Datum ist 32 Bit groß und nicht 16, wie ich es
vorher angenommen habe, oder umgekehrt ist es nur 8 Bit groß.
Also wenn ich mit Bitvektoren arbeiten möchte, dann sollte ich auch
eine Datenstruktur nehmen, die eben Bitvektor heißt.
Und die weiß dann, wie groß die Datentypen sind, die da drunter
liegen, und dann funktioniert das auch.
Also nie, sage ich mal, Dinge annehmen, die unterschiedlich sein
können und einen Integerwert als einen 16 Bit Bitvektor oder sowas
behandeln.
Ich sage das, weil ich eben auch schon so Dinge gesehen habe, aber das
ist ein super Fehler.
Gut, also soviel zu einem Bitvektor.
Dann gibt es Strings.
Was sind Strings?
Das sind Ketten von Zeichen.
Ketten von Zeichen, zum Beispiel ein Wort, kann als String dargestellt
werden.
Das sind die einzelnen Buchstaben, die ASCII-Codes haben, wie wir das
letztes Mal gesagt hatten, und die werden zusammengebunden als ein
String.
Und das ist eigentlich die Hauptbenutzung von Strings, dass man zum
Beispiel Worte, Schrift oder irgend sowas, Zeichenketten darstellt,
Zeichenketten auch bearbeitet.
Man kann sich ja auch überlegen, dass man Operationen auf
Zeichenketten hat.
Zum Beispiel zwei Zeichenketten addieren.
Oder drei Zeichenketten addieren.
Was würde das zum Beispiel heißen?
Ich habe drei Worte, ich addiere die und dann habe ich einen Satz.
Zum Beispiel.
Und da gibt es dann auch wieder Bibliotheken, die eben auf Strings
arbeiten.
Da brauche ich mich dann nicht mehr, um das einzelne Byte eines
Strings zu kümmern und mit dem irgendetwas zu tun, also dieses zu
verschieben.
Das können wir einfach addieren.
Es gibt spezielle Operationen für Strings, für Zeichenketten.
Das ist also sehr sinnvoll.
Das heißt, das ist auch eine Abstraktionsebene, um effizienter
arbeiten zu können.
Ein technisch gesehen ist ein String eine Abfolge, eine
zusammenhängende Serie von Bytes, die im Speicher typischerweise
hintereinander abgelegt sind.
Aber das soll mich nicht interessieren.
Gut.
Hier ist jetzt einfach mal so ein Beispiel.
Eine Inseleichtektur mit 32 Bit, die es irgendwann mal gab in den 80er
Jahren, als man damit angefangen hat 32 Bit zu haben.
Und ich möchte hier nicht durchgehen.
Es macht überhaupt keinen Sinn, zu lernen, was das Ding für Features
hat.
Außer, dass Sie die ganzen Dinge, die wir jetzt zuvor besprochen
haben, nämlich Integer und Zweierkomplementarstellung, BCD, Packed
oder nicht, Pointer hatten wir auch schon, Strings, Floating Point,
das kann dieser Rechner alles.
Das sind alles Features dieses Rechners.
Und er kann auch Bytes, Halbworte, Vollworte und so weiter.
Und das nur so als Beispiel, was die Leistungsfähigkeit eines
Mikroprozessors ist, und das vor 30 Jahren.
Also das war die erste IA32, das war die erste Inseleichtektur, das
war der Intel 386, der der erste 32 Bit, Voll-32 Bit Mikroprozessor
war.
Gut.
Dann habe ich Speicher.
Jetzt haben wir zwei verschiedene Dinge.
Erstmal haben wir die verschiedenen Datenformate und die nächste
Frage, die kommt, wie ist das Ganze denn im Speicher wirklich
abgespeichert oder wie greife ich darauf zu?
Die typische Weise, Daten im Speicher abzulegen oder adressieren, ist
über das Byte.
Das heißt, ich habe eine Adresse im Speicher für jedes Byte.
Das heißt, selbst wenn ich nur mit Worten rechne, also 32 Bit oder 16
Bit oder von mir aus 64 Bit, kann ich auf jedes Byte zugreifen.
Also das ist gar nicht so offensichtlich, denn ich muss ja eine
Adresse generieren, um zum Beispiel 32 Bit adressieren zu können.
Um 32 Bit, das sind 4 Gigabyte, da brauche ich entsprechend 32 Bit, um
das zu adressieren.
Wenn ich auf Byteadresse adressiere.
Warum ist das nicht so offensichtlich?
Zum Beispiel Wort, wortorganisierter Speicher.
Nehmen wir mal an, wir haben ein Wort.
Hier steht, hier ist ein Wort, das 32 Bit.
Und wir wollen nicht die Möglichkeit haben, auf jedes Byte
zuzugreifen, sondern nur als Wort.
Und Wort ist 32 Bit.
Wie viel weniger Bit brauche ich dann, um auf den Speicher
zuzugreifen?
Also ich brauche 32 Bit, um 4 Gigabyte zu adressieren.
Und ich brauche wie viel Bit, um nur auf der Wortgrenze zu
adressieren, also ein 32 Bit Wort zu adressieren.
Also wenn ich ein Byte nicht mehr adressieren möchte, dann kann ich
mir natürlich auch irgendetwas einsparen.
Eine Idee, wie viele Bits ich da einsparen kann, bei der Adressierung.
Sie müssen so sehen, die Adressierung, das geht ja nicht im Spielchen
im Sinne von, ach, dann mache ich es mit ein paar weniger Bitten, dann
bin ich happy.
Sie müssen so sehen, letzten Endes 2 Bit ist eine ganze Menge.
Denn ich brauche 2 Träte sozusagen, von dem Prozessor zum
Hauptspeicher.
Und wenn da schon 32 sind, dann macht 2 oder 4, mehr oder weniger,
einen riesigen Unterschied.
Denn so ein Bus hat dann auch eine richtige Breite, der nimmt ganz
einfach Chipfläche weg, ich kann damit sparen.
Ich behaupte jetzt einfach, wenn ich nur 32 Bit Worte adressieren
möchte, dann brauche ich weniger Bits dafür.
Also ich frage jetzt nicht, das ist halt Hausaufgabe.
Also wie viel weniger Bits?
Ich brauche für, wenn ich einen Speicher habe von 4 Gigabyte, brauche
ich 32 Bit, also 2 hoch 32, um auf jedes Byte zuzugreifen.
Und Sie haben jetzt gesagt,
also mit 8 sind das Problem.
Ich habe es vielleicht nicht richtig ausgedrückt.
Das Problem ist, wir haben jetzt zwei Dinge mit 32.
Deswegen sage ich es nochmal, 32 soll einerseits sein, 2 hoch 32 ist
die Größe des Speichers.
Und 32 Bit ist auch die Größe dieses Wort, was in dem Speicher
gespeichert ist.
Das heißt, ich brauche, um einen Speicher der Größe 2 hoch 32 zu
adressieren, brauche ich 32 Bit.
Wenn ich auf jedes einzelne Byte zugreifen will.
Aber wenn ich jetzt nur auf einen Block von 4 Bytes zugreifen will,
also nicht nur auf jedes einzelne zugreifen, sondern nur noch auf
einen Block von 4 Bytes, nämlich 32 Bit, dann brauche ich weniger Bit,
um dort zuzugreifen.
Also um es so zu sagen, als Beispiel,
ich habe hier, ich habe das, ich habe hier so ein Wort.
Das ist das eine Wort.
Und dann habe ich im Speicher noch viele solcher Worte.
Und jedes dieser Worte, es geht natürlich noch weiter, jedes dieser
Worte soll 4 Bytes haben.
Also hier habe ich ein Byte, hier habe ich ein Byte, hier habe ich ein
Byte, hier habe ich ein Byte und das ist dann ein Wort.
Dann habe ich hier ein Byte, hier ein Byte, hier ein Byte, hier ein
Byte und das ist dann ein Wort.
Und von diesen Bytes, von diesen hier, das sind Bytes, da möchte ich 2
auf 32 Stück haben, also 4 Milliarden, 2 auf 32, 4 Giga.
Und wenn ich dann jedes einzelne dieser 4 Milliarden, also wir haben
hier eine Reihe von 4 Milliarden, so groß ist der Speicher.
Wenn ich hier 4 Milliarden von habe und möchte auf jedes einzelne hier
zugreifen können, dann brauche ich 32 Bit.
Und was ich vorher sagen wollte, das ist nicht ganz klar geworden, wie
viel Bit brauche ich, wenn ich nur hier zugreifen muss, nur hier
zugreifen muss, nur hier zugreifen muss.
Also ich habe nicht mehr die Ambition, genau hier zuzugreifen oder
hier zuzugreifen, ich will nur noch an diesen Punkten zugreifen.
Dann brauche ich weniger Bit, das war meine Frage.
Also ich brauche 32 minus ein paar Bit.
Genau, also ich brauche 2 weniger Bit.
Also ich habe das nicht so ganz richtig eben erklärt und jetzt haben
Sie es auch sehr gut verstanden.
Ich brauche 2 weniger Bit.
Und das macht einen riesigen Unterschied.
Das macht einen riesigen Unterschied, nämlich ich habe 2 weniger
Leitungen auf dem Prozessor zum Hauptspeicher, ich habe einen
kleineren Leistungsverbrauch, ich habe mehr Platz für andere Dinge.
Ich kann zum Beispiel Register unterbringen, nicht der ganze Platz
geht mit Bitlines verloren, also mit Leitungen, mit Vertratung
sozusagen.
Und davon wird natürlich auch Gebrauch gemacht.
Jetzt gibt es einen Nachteil, jetzt gibt es einen Nachteil dieser
Sache.
Jetzt haben wir eben genau das, wir greifen entweder hier drauf zu,
hier drauf zu oder hier drauf zu.
Was ist jetzt der Nachteil davon, den ich mir dadurch jetzt einfange?
Genau, sehr richtig.
Also es wurde gesagt, wenn ich nur mit Daten rechne, die nur ein Byte
belegen, und das passiert ja, ich führe ja durchaus Arithmetik aus,
die nur ein Byte benötigen, dann muss ich hier ein Byte abspeichern
und ich kann auf dieses und dieses und dieses überhaupt nicht einzeln
zugreifen.
Das heißt, ich vergeude meinen Speicher.
Ich lege hier ein Byte ab, kann auf dieses zugreifen, das ist leer,
das ist leer, das ist leer.
Hier liegt was, das ist leer, das ist leer und das ist leer.
Sie sehen, man hat also hier wieder sich den folgenden Nachteil mit
eingekauft.
Was nimmt man jetzt, was hat man?
Die große Preisfrage ist natürlich dann wieder ein Trade-off, wie man
sagt, wie man irgendwo eingeben muss.
Aber ich wollte hier nur klar machen, es gibt verschiedene Arten und
Weisen der Adressierung und diese Adressierungen können Sinn machen
oder können auch zum Nachteil sein.
Gut, jetzt schauen wir uns was an.
Das ist das Data Alignment, das habe ich jetzt gerade erklärt.
Die ausgerichteten Daten, Lücken nutzen.
Sehen Sie, hier steht genau das, was Sie gesagt haben.
Es werden Daten verschwendet, beziehungsweise hier werden Lücken
genutzt oder eben nicht genutzt.
Eine andere Frage ist noch, wie legt man die Bytes im Speicher
überhaupt ab?
Eben, vor 10 Minuten ging es noch darum, wie sind die Bits organisiert
im Speicher, wie gesagt, von rechts nach links, von 2 hoch 0 bis 2
hoch n-1.
Und hier ist die Frage, wie ist ein Wort, also 32 Bit, was aus 4 Byte
besteht, zum Beispiel, wie sind diese Bytes im Speicher organisiert?
Und in dem Fall ist es wieder, wie wir es eben gelernt haben, hier ist
jetzt jedes ein Byte.
Jetzt nicht verwechseln, das geht hier nicht mit.
Hier ist das niederwertigste Byte, dann das höher und höher und hier
das höchstwertige Byte.
Sie sind dann auch von rechts nach links angeordnet.
Und das Ganze, hier ist schon rot angestrichen, ich unterstreiche es
nochmal kurz, um Sie darauf hinzuweisen.
Dies nennt sich Little Indian.
Das letzte Ende ist einfach nur Name.
Es gibt dann noch eine Folie nach dem Motto, wo kommt der Name
überhaupt her dafür.
Aber es spielt keine Rolle, wenn in einem Speicher die Worte so
angeordnet sind, dass das niederwertigste Byte rechts ist und dann so
aufsteigen, wie es hier geschrieben ist, dann nennt man das Little
Indian.
Und das ist dann entsprechend so kodiert.
Das ist nur die Kodierung dafür, also 0, 1, 2, 3, 4, 5, 6, 7, so ist
das kodiert.
Man muss es ja irgendwie adressieren, wenn man es will.
Also, wenn es ein Little Indian gibt, gibt es offensichtlich auch noch
etwas anderes.
Und zwar den Big Indian.
Und da ist es einfach umgekehrt.
Das größte Byte ist hier rechts abgespeichert, dann kommt das kleinere
und so weiter.
Und das kleinste ist dort abgespeichert, wo das MSB ist.
Man kann auch wieder fragen, okay, warum macht man das so oder so?
Das ist einfach eine Konvention.
Hier könnte ich jetzt nicht argumentieren, dass man sagt, man braucht
weniger Bits, um zu adressieren.
Das stimmt nicht.
Man vergeudet Speicher, bestimmt letzten Endes auch nicht.
Das ist einfach andersherum.
Es kommt darauf an, wie man die Adressberechnungsarithmetik intern
aufbaut.
Ob man von oben nach unten, von unten nach oben zählt und so.
Das sind Kleinigkeiten, aber es sind nicht Dinge, die hier jetzt
offensichtlich wären, wo man sagen könnte, es hat einen
offensichtlichen Vorteil.
Was man sich hier nur merken muss, ist Folgendes.
Es gibt Prozessoren, die funktionieren nach diesem Prinzip.
Und es gibt Prozessoren, die funktionieren nach diesem Prinzip.
Und es ist natürlich auch wieder klar, ich habe einen Op-Code, ich
habe einen Maschinen-Code, und ich speichere den ab im Binärformat.
Also den vollkompilierten Code speichere ich ab.
Es ist klar, dass was hier läuft, nicht da läuft, nicht umgekehrt.
Die Bytes werden dann ganz anders interpretiert.
Das ist also nicht kompatibel.
Das muss man wissen.
Es ist nicht kompatibel, es gibt verschiedene Vorteile, sind jetzt
nicht notwendigerweise offensichtlich von dem, was wir zur Zeit hier
im Wissen haben und hier beurteilen.
Okay, das lese ich jetzt nicht vor, das können Sie sich selber
durchlesen.
Woher kommt der Name?
Adressierungsarten.
Jetzt kommen wir zu dem Punkt, wir sind jetzt beim nächsten Teil der
Instruktionssatzarchitektur.
Jetzt ist die Preisfrage, wie greife ich auf die Daten zu?
Wie komme ich an die Daten ran, die im Speicher liegen?
Da gibt es eine ganze Menge.
Ich kann natürlich unterschiedliche Daten haben.
Ich kann mir vorstellen, ich habe eine Konstante.
Stellen Sie sich vor, Sie haben im Programm eine Konstante Pi.
Die brauche ich eigentlich nicht im Register abzulegen, als
Programmierer, denn Pi ist immer Pi.
Oder es gibt auch andere Zahlen, die sind halt fix.
Warum soll ich die als Variable im Speicher abgeben?
Brauche ich nicht.
Dann kann ich das direkt in den Code reinschreiben, Pi als feste Zahl.
Und das kann ich dann eben auch machen.
Ich kann zum Beispiel direkt eine Konstante angeben.
Und dann wäre die Adressierung auch entsprechend.
Ich brauche also nicht erst auf einem Register zuzugreifen.
Da ist die Konstante Teil des Codes.
Und damit habe ich dann auch eine Adressierungsart, die sich auf eine
Konstante bezieht.
Das mag Ihnen trivial erscheinen, hat aber einen Unterschied in der
Art und Weise, wie ich auf das Datum zugreife.
Und Sie werden auch hier wieder sehen, die ganzen Adressierungsarten,
die wir hier lernen, unterscheiden sich signifikant darin, wie
schwierig es ist, auf ein Datum zuzugreifen.
Und das jetzt schon mal, um es vorwegzunehmen, das kommt dann alles
noch in den Folien, aber ich möchte nicht so gerne durch die Folien
durchgehen, ich möchte dann noch so mit Ihnen sozusagen arbeiten.
Wenn ich adressiere, dann ist ja damit irgendetwas, irgendein Aufwand
verbunden.
Also, ich kann auf einen Speicher zugreifen, auf einen Hauptspeicher,
ich kann auf einen Register zugreifen, ich greife auf einen konstanten
Wert zu.
Was passiert denn dann?
Was passiert denn dann, was ich als Programmierer eigentlich gar nicht
mitkriege?
Als Programmierer sage ich nur, ich will den Inhalt des Registers, ich
will den Inhalt des Speichers, ich will, dass diese Konstante mit zwei
multipliziert wird oder irgend sowas.
Aber was macht denn der Rechner, was ich als Programmierer, also
meinem Algorithmus, den ich schreibe, überhaupt nicht mitbekomme?
Was passiert denn da?
Und das führt dann letzten Endes auch zu der Frage, warum ich unter
Umständen unterschiedliche Adressierungsmodi habe.
Also, was macht eine ANU, die Daten haben will?
Wo kommen die her?
Woher weiß die ANU, wo die Daten herkommen?
Also, wenn ich als Programmierer sage, was muss der Rechner tun?
Muss der irgendwas tun?
Muss der vielleicht zusätzlich was tun, um A und B miteinander zu
addieren?
Muss der noch irgendetwas anderes tun, um das zu bewerkstelligen?
Also gibt es irgendwas, das ist eigentlich die große Frage hier, gibt
es irgendwas, wenn wir uns diese Sache hier anführen, wo war das
Beispiel?
Hier ist das Beispiel.
Das sehe ich als Programmierer.
Und das, denke ich, ist alles, was der Rechner hier letzten Endes tut,
aber was tut der Rechner zusätzlich noch, um dies wirklich
auszuführen?
Das ist eigentlich die Frage.
Also irgendwelche Vorschläge.
Was passiert da?
Es geht um Adressen.
Wo kommen die Adressen her, frage ich.
Wo kommen die her?
Sehr gut, es ist ganz einfach, die Adressen müssen berechnet werden.
Es ist so einfach wie, die Adressen müssen berechnet werden.
Die Adressen müssen berechnet werden, die sind nicht da.
Die müssen berechnet werden.
Das heißt, es gibt, irgendwo im Rechner gibt es zum Beispiel einen
Multiplizierer oder eine ALU, die macht nichts anderes, als Adressen
zu berechnen.
Die steht für mich als Programmierer gar nicht zur Verfügung, um zwei
Zahlen zu addieren oder sonst was zu tun, sondern da gibt es eine AGU,
Adress Generation Unit, das heißt eine Einheit, die die Adressen
berechnet.
Und die Adressberechnung, die dauert natürlich offensichtlich auch
selber wieder Zeit.
Der Trick bei der Sache ist zumindest, dass ich Folgendes tue.
Ich habe Recheneinheiten, um meine Aufgabe zu berechnen, meinen
Algorithmus zu berechnen, und ich habe Recheneinheiten, die ich als
Programmierer nicht sehe, die berechnen gleichzeitig die Adressen.
Aber es muss halt gemacht werden, das wollte ich hier nur sagen.
Und da es gemacht werden muss, und wie es gemacht werden muss, das
bestimmen die Adressierungsarten.
Deswegen beschäftigen wir uns unter anderem mit Adressierungsarten.
Natürlich haben wir auch deshalb unterschiedliche Adressierungsarten,
und zwar hauptsächlich, weil wir ein Programm schreiben möchten, was
möglichst elegant ist.
Ich kann ein Programm schreiben, um ein Programm zu schreiben,
benötige ich relativ wenige Befehle.
Um ein effizientes Programm zu schreiben, benötige ich sehr viel mehr.
Effizient heißt dabei, kann dabei auch heißen, kann dabei mehr heißen.
Es kann heißen einerseits, das Programm soll schnell ausführen, wenn
es einmal kompiliert ist, oder ich als Programmierer soll wenig Zeit
haben, dieses Programm zu schreiben.
Das ist ja auch durchaus ein Faktor.
Wenn ich primitive Befehle habe, muss komplexe Operationen ausführen,
und mein Instruktionssatz unterstützt mich nicht dazu, dann sitze ich
ewig da, um zum Beispiel eine Größenordnungstransformation
auszurechnen.
Wenn ich aber Befehle habe, die mir Teile davon schon zum größten Teil
abnehmen, zum Beispiel wie elegant ich den Speicher adressiere, dann
kann ich das viel schneller machen.
Also das ist die Idee dabei, warum ich verschiedene Adressierungsmodi
habe.
Wir werden im Folgenden durch alle Adressierungsmodi durchgehen.
Ich werde die erläutern und wollte Ihnen als Vorab schon die
Informationen geben.
Diese gibt es, um mehr Optionen zu haben, um den Programmierer zu
entlasten.
Und wir werden dann auch sehen, wie die Berechnungen, welche die
Berechnungen sind, die zur Adressgenerierung benötigt werden.
Und das schauen wir uns dann im Folgenden an.
Aber erstmal, es gibt verschiedene Adressen, schauen Sie mal.
Hier gibt es eine Programmadresse.
Warum gibt es überhaupt verschiedene Adressen?
Das ist auch ein ganz wichtiges Konzept.
Wenn ich hier von Adressen rede, dann meine ich nicht nur, da ist eine
Adresse, sondern es gibt verschiedene Arten von Adressen.
Und die Frage müssen Sie beantworten.
Warum ist eine Adresse im Programm eine andere als eine Adresse, die
dann letzten Endes, die ich mit einem Oszilloskop zum Beispiel am
Speicher am Bus feststellen könnte?
Warum ist die im Programm, im Software-Code eine andere?
Die nenne ich mal, oder die haben wir hier mal genannt,
Programmadressen.
Warum gibt es verschiedene Adressen?
Da müssen Sie mir jetzt helfen, das wollte ich jetzt selber nicht
sagen.
Also der Programmierer kann nicht wissen, wo sich das Programm während
der Laufzeit im Speicher befindet.
Das weiß ich nicht.
Ich schreibe ein Programm und als Programmierer würde ich angeben,
eine Adresse würde ich sagen, bitte springe, wenn die Bedingung falsch
ist, bitte springe an das Ende des Programmes.
Dann wäre meine Adresse, die ich angebe, Ende des Programmes.
Und da steht vielleicht so ein Label und dann steht dann End of
Programme oder irgend sowas.
Und das ist meine Adresse, die ich als Programmierer angebe.
Aber was hat das jetzt mit dem Speicher zu tun?
Speicher ist irgendwo ein Stück Hardware, da steht das dann.
Oder gehen wir einen Schritt voraus.
Der Prozessor, sagen wir mal so, der Prozessor kennt ja eigentlich
nicht mein Programm.
Der kennt eine Abfolge von Opcodes, von Adressen, weiß, was er mit
diesen Adressen machen soll, wie er diese verknüpfen muss.
Aber dem Prozessor ist ja sozusagen ein Programm als solches überhaupt
nicht bekannt.
Das sind ja nur Opcodes und Adressen und Verknüpfungen, die da
stattfinden.
Also, es gibt offensichtlich eine Programmadresse, die ich als
Programmierer angebe, wie Sie richtig gesagt haben.
Und es gibt eine Prozessadresse.
Das ist die effektive Adresse, die der Prozessor verwendet.
Ja, der Prozessor verwendet die.
Aber ich habe noch mehr Fragen an Sie.
Der Prozessor verwendet die.
Jetzt behaupte ich, es gibt noch andere Adressen.
Gibt es Adressen, die selbst der Prozessor nicht kennt?
Wenn ein Programmierer, der kennt die Adressen nicht, wie Sie richtig
gesagt haben, und dann wird das transformiert in Adressen, die der
Prozessor kennt, nennt sich Prozessadresse.
Und dann behaupte ich, es gibt Adressen, die kennt nicht mal der
Prozessor.
Warum?
Auf der Prozessor weiß nicht alles sozusagen.
Na gut, die Antwort ist natürlich so ein bisschen ähnlich, wie Sie sie
eben genannt haben.
Im Falle des Programmierers ist es analog dazu.
Also, Sie müssen schon weiterhelfen.
Das ist einfach.
Das kann man sich einfach überlegen.
Warum weiß der Prozessor die Adresse nicht?
Nicht unbedingt.
Warum gibt es noch eine andere Art von Adressen?
Keine Idee.
Das war doch sehr gut, was Sie eben gesagt haben.
Jetzt muss man sich nur sozusagen in den Prozessor reindenken.
Die Sichtweise des Prozessors.
Was ist die Sichtweise des Prozessors?
Wir haben doch eben gesehen, was ein Prozessor ist.
Ein Prozessor weiß zum Beispiel, mein Speicher ist 4 Gigabyte groß.
Das weiß der Prozessor, weil er eine bestimmte Anzahl von Adressbits
hat, um diesen Speicher zu adressieren.
Das weiß der.
Und dafür braucht er seine Prozessadresse.
Aber was weiß der Prozessor nicht?
Das ist die Frage.
Oh, virtuelle Adressen.
Super, genau.
Es gibt so Dinge wie virtuelle Adressen.
Es gibt Adressen, das sind virtuelle Adressen, mit denen kann man
rechnen.
Die sind dann offensichtlich noch nicht die physikalischen Adressen.
Das heißt, wenn ich das mal so frei übersetzen darf, es gibt Adressen,
die kann der Prozessor nicht wissen, weil der Prozessor weiß ja nicht,
in welches Computersystem er eingebaut ist.
Wenn ein Prozessor 32 Bit hat, um den Hauptspeicher zu adressieren,
dann heißt das ja noch lang nicht, dass ich mir einen Computer
zusammenbaue, der genauso viel Speicher hat, der kann ja weniger
Speicher haben.
Und vielleicht kann der Computer, den ich habe, sogar mehr Speicher
haben, als was der Prozessor adressieren kann.
Das kann ich mir auch vorstellen.
Der hat mehr oder weniger Speicher, der Prozessor weiß es nicht.
Der rechnet einfach so und tut so, als hätte er einen Speicher, der
immer 4 Gigabyte groß ist.
Der kann kleiner oder größer sein.
Und dann ist völlig richtig, wie Sie sagten, dann gibt es so Dinge wie
virtuelle Adressen, physikalische Adressen.
Das sind dann wirklich die physikalischen, die dann die Speicherzellen
dastehen, wo mein Bit wirklich drin steht.
Und darüber geht es über virtuelle Adressen, das schauen wir uns alle
später in Vorlesungen nochmal an.
Es ist aber jetzt schon wichtig zu wissen, denn das ist der Grund
dafür, dass ich Programmadressen habe, dass ich Prozessadressen habe
und dass ich, wie man zusammenfassend sagt, auch Maschinenadressen
habe.
Gibt es dazu noch Fragen?
Also wir haben natürlich noch nicht genau erklärt, was das ist, aber
es ist klar, dass es dafür eine Hierarchie gibt, dass das Sinn macht.
Gut, damit haben wir also drei verschiedene Arten von Adressen.
Und jetzt wollen wir uns mal angucken, bevor wir in 4.3 zu
Adressierungsarten gehen, wollen wir uns jetzt überlegen, welche Arten
von Befehlen gibt es denn?
Und das hätte ich jetzt vielleicht eher ausblenden sollen, aber
vielleicht können wir es so einfach erklären.
Es gibt zum Beispiel Transportbefehle.
Nennen Sie mir einen Transportbefehl.
Hat man heute schon auf irgendeiner Folie drauf.
Was ist ein Transportbefehl?
Ein Beispiel für ein Transportbefehl.
Mit Beispielen lässt sich es einfacher merken.
Das ist der Trick.
Also wenn jetzt einer von Ihnen was nennt, können sich das alle später
besser merken.
Ein Load, genau.
Wir haben eben ein Load gesehen, wo ein Wert in den Speicher, von dem
Hauptspeicher in das Register geladen wurde.
Das ist ein Transportbefehl.
Was passiert da?
Ein Datum wird je laden, wird verschoben im Computersystem vom
Hauptspeicher in ein Register.
Das nennt man ein Transportbefehl.
Es wird also etwas transportiert.
Ein Datum wird transportiert.
Arithmetische, logische Befehle.
Also ein paar Beispiele dafür.
Eins hatte ich dauernd genannt, arithmetisch ist ein Add.
Was sind noch andere arithmetische, logische Befehle?
Ein Add hatte ich genannt, genau.
Was könnte man sich...
Subtraktion, genau.
Was noch?
Also ein paar Sachen sammeln.
Man kann sich besser merken, wenn man sich Dinge sammelt, wo man
sie...
Was sind arithmetische, auch logische Befehle?
Ein bitweises Und, also eine Undverknüpfung von Bit.
Bitweise zum Beispiel über ein Wort, richtig.
Was noch?
Man kann regieren und so weiter.
Dann gibt es Biberotationsbefehle.
Das sagt eigentlich der Name.
Was wird da geschoben oder was wird da rotiert?
Und warum könnte es...
Genau.
Ich kann Bits nach links oder rechts verschieben.
Ich habe ein Wort und verschiebe in einem Takt zum Beispiel alle Bits
nach links oder rechts.
Sie erinnern sich, es geht rechts bei 2 hoch 0 los.
Und jetzt verschiebe ich das, nehmen wir an, ich verschiebe das...
alles nach rechts um einen Bitwert.
Was erreiche ich dadurch?
Was bedeutet das im Sinne einer Operation?
Alle Bits eines Wortes um einen Bit nach rechts verschoben.
Was heißt das?
Eine Division gleich 2, genau.
Beziehungsweise eine Multiplikation, wenn ich es nach links mache.
Also das wären zum Beispiel Schiebebefehle.
Dann gibt es auch noch Rotationsbefehle.
Da wäre zum Beispiel die Frage, wenn ich das jetzt nach rechts
verschiebe, dann foliere ich ja unter Umständen ein Bit.
Wenn ich es durch zwei teile, dann bleibt eins über.
Und was tue ich mit dem, der über ist?
Da gibt es zum Beispiel die Möglichkeit, ich rotiere das, ich nehme
das Bit, was über bleibt, und lasse es von links wieder reinkommen,
zum Beispiel.
Wie weit das sinnvoll ist, oder was ich damit für Operationen machen
kann, ist eine andere Sache, da kann man was mit machen, aber das wäre
dann ein Rotationsbefehl.
Ich schmeiße das Bit also nicht weg, was ich rausrotiert habe, was ich
rausgeschoben habe, sondern ich rotiere es und nutze es.
Dann gibt es Dinge wie Multimediabefehle.
Das gab es hauptsächlich, das gibt es mittlerweile seit 25, 30 Jahren
oder so.
Irgendwann hat man sich überlegt, mit Computern möchte man mehr machen
wollen als Textverarbeitung oder irgendwelche wissenschaftlichen
Berechnungen.
Vielleicht kann man das auch für Multimedia einsetzen, was heute
eigentlich fast der Hauptanwendungsbereich ist, bei verschiedenen
Anwendungen, und dafür bestimmte Befehle haben.
Also auch sowas gibt es.
Zum Beispiel eine Fast-Fourier-Transformation, da kommen so Dinge vor
wie Multipliant Add.
Das heißt, eine Multiplikation, eine Addition, wird dort immer wieder
aufeinanderfolgend ausgeführt.
Und wenn ich jetzt eine Operation habe, die das sozusagen zusammentut,
ohne das Datum zwischendurch abzuspeichern, dann habe ich natürlich
was gewonnen.
Das heißt, ich habe eine Folge von Befehlen, die ich als neuen Befehl
definiere, und damit habe ich Zwischenschritte wie Abspeichern
gespart.
Und Multimedia-Befehle zum Beispiel tun solche Dinge.
Kleidkammerbefehle haben wir gesehen, Programmsteuerbefehle.
Das ist halt ein Jump zum Beispiel, ich springe irgendwo ein Programm
hin.
Da kann ich so Dinge haben, bedingte, unbedingte Sprünge.
Also sowas kennen wir eigentlich schon.
Was wäre zum Beispiel ein Systemsteuerbefehl?
Was wäre ein Systemsteuerbefehl?
Also System ist der Mikroprozessor.
Was wäre ein Systemsteuerbefehl?
Das hat man eigentlich noch nicht.
Aber wem fällt was zu ein, was es sein könnte?
Was muss gesteuert werden im System?
Es geht jetzt nicht um das Programm offensichtlich, wo ich eine
Schleife habe, wo ich irgendwo hinspringe und da das Programm steuere,
sondern es geht um Systemsteuerbefehle.
Den Computer runterzufahren.
Okay, ich würde sagen, dass es ein Systemsteuerbefehl ist.
Das ist richtig.
Da müsste man eine große Sequenz von Befehlen haben, was teilweise
auch das Betriebssystem tut.
Sie haben sozusagen schon zwei, drei Schritte weiter gedacht.
Das ist völlig richtig.
Ein naheliegender Systemsteuerbefehl wäre, wenn man dem Mikroprozessor
sagt, wir haben zwei Zahlen miteinander addiert und da gab es einen
Overflow.
Das heißt, der Wertebereich der Addition ist überschritten worden und
da könnte ich dem Rechner sagen, wir haben hier einen Overflow.
Wir können das nicht berechnen.
Dann sage ich also dem System, wir können das nicht berechnen.
Es ist besser, ich sage das, wir können das nicht berechnen, als wenn
es dann passiert und es kommt ein falsches Ergebnis raus.
Oder es könnte doch ein Systemsteuerbefehl sein.
Wenn man daran denkt, ein System kann ja ein eingebettetes System
sein, was irgendetwas tut zu einer Zeit und dann kommt irgendetwas
Wichtigeres.
Dann müsste das System auch irgendwie steuern.
Interrupt, genau.
Interrupt heißt offensichtlich Unterbrechung.
Ich unterbreche mein Programm so, wie es eigentlich gerade ausgeführt
hat, also mein Algorithmus, der den größten Wert in einem Feld findet,
eine FFT ausführt oder irgendetwas.
Das unterbreche ich.
Das unterbreche ich und führe ein neues Programm fort.
Warum?
Weil dieses unterbrechende Programm oder dieses Signal eine höhere
Priorität hat.
Das gibt es, also höhere Prioritäten.
Fällt Ihnen irgendein Beispiel ein aus dem täglichen Leben, wo wir ein
System haben, wo ein Interrupt Sinn machen könnte?
Maus und Tastatur, sehr gut.
Zum Beispiel, ich gebe, ich tippe an meiner Tastatur oder mache da
irgendwas mit der Maus, also betätige meine Eingabe, aber das mache
ich ja nicht die ganze Zeit.
Das heißt, es wäre eigentlich dumm, wenn der Rechner sich die ganze
Zeit damit beschäftigen müsste, lebt der Benutzer oder tippt der
gerade irgendwas.
Das heißt, es wäre eigentlich besser, wenn das Gerät selber sagt, die
Tastatur, oh, jetzt kommen wieder ein paar Zeichen, jetzt müssen wir
gerade mal die andere Ausführung, was auch immer da im Hintergrund
läuft, unterbrechen, um diese Zeichen, die gerade eingetippt wurden,
dem Programm zuzuführen.
Was wären noch andere Sachen?
Ich behaupte sogar, es gibt Dinge, die sind lebensnotwendig.
Ja, hier ist vielleicht nicht so lebensnotwendig, das war ein sehr
gutes Beispiel, aber...
Airbags, super, genau, sowas wollte ich eigentlich hören.
Also man fährt im Auto, man fährt im Auto und ja, was macht man im
Auto?
Man fährt, das System beschäftigt sich damit, vielleicht Sprit zu
sparen, vielleicht hört man Musik oder irgend so was.
Das heißt, da finden natürlich Berechnungen statt.
Womit ich nicht rechne, ist, dass ich vielleicht sehr scharf bremsen
muss.
Dass es irgendeinen Sensor gibt, der sagt, es steht ein Aufprall bevor
oder hat gerade stattgefunden, ein Airbag muss ausgelöst werden.
Und das ist ganz klar.
Und das ist so ein eigentlicher Sinn von so einem Interrat, dass nun
alle Rechenressourcen gestoppt werden.
Und dass ich diesem Interrat die höchste Priorität gebe und sage,
alles, was da läuft, ist nicht so wichtig, jetzt muss der Airbag
ausgelöst werden.
Das ist ein sehr gutes Beispiel für einen Interrat.
Typischerweise gibt es in einem Programm, gibt es in einem
Mikroprozessor Interrats verschiedener Priorität.
Also man kann sich vorstellen, es gibt 10 verschiedene Ebenen von
Prioritäten.
Die niedrigste Priorität hätte zum Beispiel mein Programm, der Music
Player, der da spielt.
Und dann gibt es viele andere Dinge im Hintergrund, die sehr viel
wichtiger sein können.
Zum Beispiel irgendwo zwischendrin könnte es sein, die Batterie geht
gleich zur Neige.
Wird ein Programm aufgerufen, was mir dann irgendeinen Ton auslöst
oder in einem Modus 4 etwas weniger Energie benötigt.
Oder die letzte Energie im Rechner benutzt, im Laptop benutzt, sodass
ich den Rechner gerade noch runterfahren kann.
Und die Tabellenkalkulation, die ich im Hintergrund gemacht habe, die
geht mir nicht verloren, die wird gerade noch abgespeichert, zum
Beispiel.
Es gibt also viele Beispiele für Systemsteuerbefehle.
Es gibt auch Dinge wie Synchronisationsbefehle, das lasse ich mal an
der Stelle.
Das sehen wir dann alle später in der weiteren Vorlesung.
Also nicht heute, aber dann in den nächsten Wochen.
Gut, jetzt hatten wir also Folgendes.
Wir haben verschiedene Befehlsformate, weil wir verschiedene Art und
Weisen haben,
wie wir das hier zum Beispiel sehen.
3-Adress-Format, 2-Adress-Format, 1-Adress-Format, 0-Adress-Format.
Haben wir gesehen, das will ich nicht nochmal wiederholen.
Aber, da es das gibt, brauche ich auch verschiedene Befehlsformate.
Und hier sind jetzt zum Beispiel so verschiedene Befehlsformate.
Und was Sie jetzt hier sehen ist, wir haben wieder 32-Bit, 0 bis 31.
Und wir haben jetzt in diesem Fall drei verschiedene Quellenformate.
Das sind die Typen von Befehlsformaten.
Das heißt, der Typ I, J, R, warum die so heißen, erschließt sich
später noch.
Und diese Typen werden genommen für verschiedene Adressierungsmodi,
für verschiedene Arten, um ein Befehl darzustellen.
Es gibt zum Beispiel Befehle, die haben nur ein Op-Code.
Der Op-Code, das kann ich sagen, was das ist, ich möchte nicht alles
erklären.
Aber der Op-Code zum Beispiel sagt, in welchen Befehlen sich handelt.
Der Op-Code sagt zum Beispiel, es ist eine Add-Operation.
Das sagt mir jetzt noch nicht unbedingt, was die Register dafür sind.
Aber es sagt mir zumindest, hier wird die ALU gebraucht, um eine
Addition zu machen.
Oder der Op-Code sagt mir, dies ist eine Vergleichs-Operation.
All dies ist in dem Op-Code gespeichert.
Und Sie sehen, wir haben hier 6-Bit für den Op-Code.
Wir haben also 2 auf 6 verschiedene Möglichkeiten, ein Op-Code
darzustellen.
Also offensichtlich können wir hiermit 64 verschiedene Arten von
Befehlen codieren.
Und wie gesagt, das heißt noch lange nicht, das sagt noch lange nichts
darüber aus, wie ich das Ganze adressiere.
So, jetzt habe ich hier den Op-Code.
Und jetzt kann ich mir vorstellen, ich habe einen Befehl, der braucht
nichts anderes als ein Op-Code und wie hier steht, ein Target.
Was könnte denn so ein Befehl sein, der nur das braucht?
Also offensichtlich braucht er hier kein Register.
Er braucht nur, was hier als Target steht.
Also er muss nur sagen, was der Befehl tut und braucht dazu so ein
Target.
Was wäre so ein Befehl?
Mehr braucht er nicht.
Wir haben ja eben die verschiedenen Arten Befehle gesehen.
Diese Liste von zwei Folien, da gab es die verschiedenen Arten von
Befehlen.
Und ja, der gehört offensichtlich zu einer Art dieser Befehle hier.
Also zum Beispiel ein Jump Befehl.
Ich springe im Programm.
Ich springe im Programm und sage, hier steht Op-Code Jump.
Ich springe.
Ich springe ans Anfang der Schleife.
Ich springe.
Und Target ist dann schon die aufgelöste Adresse.
Das ist dann schon die Maschinenadresse, die wir letzten Endes
brauchen.
Das heißt, die hat der Compiler, Assembler, die haben da schon da
reingeschrieben.
Und hier stehen dann für 26-Bit die Sprungadresse zum Beispiel.
Oder hier habe ich den Typ, dass ich hier ein Register wählen kann.
Hier addiere ich zum Beispiel, hier wähle ich ein Register.
Und da habe ich 2 auf 5.
Offensichtlich hat dieser Rechner hier 32 verschiedene Register.
Die ich hier adressieren kann.
Und ich habe noch eine Konstante von 16-Bit.
Die auch eine der Quellen ist.
Also ich möchte nicht im Einzelnen durchgehen, wie das Ganze codiert
ist.
Aber ich möchte Ihnen nur so viel sagen.
Es gibt einen Grund dafür, dass wir verschiedene Arten von
Befehlsformaten haben.
Um die verschiedenen Arten von Befehlen, die wir haben, codieren zu
können.
Macht das Sinn?
Wie würden Sie die Frage beantworten, wenn jemand sagt, naja, ok.
Ich kann mir doch auch überlegen, bitte zuhören, ich frage Sie jetzt.
Ich kann mir doch auch überlegen, aus diesen ganzen Feldern, die ich
hier brauche, um einen Befehl zu codieren, das ist einfach so, kann
ich mir doch auch überlegen, das alles zusammen in ein Wort
reinzuschreiben.
Kann ich das oder kann ich das nicht?
Das beantwortet jetzt die Frage, warum wir da drei Stück von haben.
Kann ich es oder kann ich es nicht?
Oder könnte ich es?
Also Fakt ist gegeben, ich brauche diese drei Formate, um diese
verschiedenen Befehlsarten darstellen zu können.
Und Frage ist, warum brauche ich dann aber diese drei Befehlsformate?
Warum kann ich das nicht irgendwie alles zusammen codieren?
Das ist die Frage.
Warum kann ich es nicht alles zusammen reinpacken?
Kann ich es oder könnte ich es oder was ist der Drehtor?
Wenn man das verstanden hat, dann weiß man auch, warum es drei
verschiedene ist.
Es könnte auch vier verschiedene geben, darum geht es mir nicht.
Aber warum gibt es mehrere verschiedene Befehlsformate?
Irgendeine Idee.
Warum gibt es...
Warum kann ich das nicht alles zusammen irgendwie reinpacken?
Naja, das ist halt ganz einfach.
Wenn ich alles zusammen in ein Wort reinpacke, was passiert denn dann?
Hier habe ich 32 Bit.
Da brauche ich 64 Bit vielleicht oder 48 Bit oder irgend so was.
Ich kriege es halt nicht in 32 Bit rein.
So einfach ist die Antwort zu dieser Frage.
Jetzt könnte aber die nächste Antwort gleich wieder kommen und sagen,
okay, dann nehme ich halt 64 Bit und dann habe ich aber nur ein
Befehlsformat.
Dann steht hier das Immediate, hier kommt dann links das Target dran,
da drüben beim Fenster irgendwo das ID und so weiter.
Dann habe ich halt so ein riesenlanges Ding.
Das wäre die andere Lösung, um dieses Problem hier zu lösen.
Aber da habe ich wieder welchen Nachteil?
Der wurde eben in einem anderen Zusammenhang, wurde genau die Antwort
zu dieser Frage hier gegeben.
Ich verschwende von Ihnen.
Ich verschwende einen großen Teil des Platzes.
Ich verschwende einen großen Teil des Platzes, wenn ich 64 Bit nehme.
Ich würde für die 64 Bit oftmals eben nur drei, vier Felder benutzen
und der Rest wäre leer sozusagen.
Der Vorteil ist, ich habe ein Befehlsformat von einem 64 Bit
Befehlsformat, der zwar viel Speicher verschwendet, aber welchen
Vorteil hätte das dann wieder?
Welchen Vorteil hat es, anstatt einem Befehlsformat drei zu haben?
Was ist das für ein Vorteil?
Die ganze Sache, es geht hier immer darum, dass Sie nicht möchten,
dass Sie irgendetwas auswendig lernen und das irgendjemand bestimmt
hat aus irgendeinem Grund.
Das hat alles irgendwelche Gründe und die möchte ich versuchen Ihnen
zu vermitteln und ich denke, es ist eigentlich trivial.
Also wer sagt das?
Warum kodiere ich, zumindest in diesem Beispiel hier nicht, warum
kodiere ich nicht alles in ein langes Wort hinein?
Ich verschwende Speicher, aber warum würde ich es tun?
Was würde wieder dafür sprechen, dass ich es tue?
Ich verschwende zwar Speicher, aber eine
Vermutung, eine vage Vermutung.
Es wird keiner dafür bestraft, irgendwas Falsches zu sagen.
Also, ich muss das Ganze dekodieren.
Stellen Sie sich Folgendes vor, Sie bekommen einen Befehl, der Rechner
bekommt diesen Befehl hier, der kommt aus dem Speicher und jetzt muss
irgendjemand sagen im Speicher, filtere mir den Op-Code heraus,
filtere mir den Register-Code heraus, filtere mir das Emitted heraus.
Also filtern, sage ich mal, so Umgangssprachen.
Der Ausdruck dafür heißt dekodieren.
Das ist ein Wort mit 32 Bits, da sind 0 und 1 gesetzt und ich muss das
dekodieren, ich muss das interpretieren, ich muss sagen, zeige mir
diese 6 Bits, damit ich weiß, um welche Operation es handelt.
Zeige mir diese 5 Bits, damit ich weiß, um welchen Registerinhalt es
sich handelt.
Das heißt, irgendjemand im Rechner, eine Hardware, die sogenannte
Dekodier -Logik, die teilt dieses Wort jetzt auch in verschiedene
Gruppen, nämlich in den, den, den und den und weiß dann, ja, es
handelt sich um eine Operation, die eine Addition ist, es handelt sich
um das Register 5 und das Register 7 und die beiden sollen jetzt
addiert werden oder irgend so was.
Das heißt, es gibt eine Dekodier-Logik.
Und die Antwort zu der Frage von eben ist, warum mache ich da nicht
ein riesengroßes Befehlswort und kodiere all dieses Zeugs dort hinein,
ist, dann bräuchte ich nur eine Dekodier-Logik für ein Befehlsformat.
Hier brauche ich drei verschiedene Dekodier-Logiken für drei
verschiedene Befehlsformate.
Sie sehen, wir haben wieder einen Trade-Off.
Wir sparen darin ein, nur eine Dekodier-Logik zu haben, dafür
vergeuden wir beim 64-Bit-Wort viel an Speicher, da wir immer nur drei
oder vier Felder benötigen, wie wir hier sehen.
Und andererseits mache ich es eben so, ich vergeude keinen Speicher,
ich habe nur kleine Befehlsworte von 32 Bit, aber wie Sie sehen, ist
hier alles bis zum letzten Bit ausgenutzt.
Wir haben diesen Trade-Off.
Darum geht es.
Und jetzt gebe ich Ihnen noch eine andere Antwort zu dem.
Es ist nämlich gar nicht so, dass man sich dauernd dafür entscheidet.
Es gibt tatsächlich Rechner, die haben nur ein Befehlswort.
Die packen das tatsächlich alles in ein Befehlswort rein.
Um genau diesen Vorteil Rechnung zu tragen, zu sagen, ich möchte nur
eine Dekodier-Logik haben, ich möchte sehr wenig Hardware haben, dann
interessiert mich nicht mal so viel, dass ich damit viel Speicher
belege.
Hier ist folgender Trade-Off.
Da sagt man zum Beispiel, der Mikroprozessor, der soll einfacher
organisiert sein, da möchte ich weniger Dekodier-Logik haben, aber
dafür brauche ich einen größeren Speicher.
Und das Problem verweise ich sozusagen auf den Speicher in diesem
Falle.
Kleiner Prozessor, großer Speicher, oder umgekehrt.
Das könnte man vielleicht so ganz einfach zusammenfassend hier so
sagen.
Also, es gibt verschiedene Befehlsformate.
Mehr sollte die Folie eigentlich gar nicht sagen.
Hier gehe ich jetzt nicht durch.
Sowas ist für zu Hause, wo man sich überlegt, welches Bit heißt jetzt
wie.
Das lese ich jetzt hier nicht vor.
Gut, und so sieht das Ganze dann zum Beispiel aus im Rand eines
Beispiels.
Ich habe ein Add mit Ziel- und Quellregistern.
Ich weiß, wie viele Bits dafür benötigt werden.
Das heißt, dies gibt letzten Endes an, welches Register gemeint ist.
2 hoch 5, 32.
Also vielleicht steht da drin eine 17, dann ist das Register 17.
Und dann heißt das, addiere den Wert des Register 17 und so weiter.
So ist das zu interpretieren.
Hier habe ich immediate, das heißt, hier habe ich teststudiert, eine
Adresse oder einen Konstantenwert.
Wie ich eben sagte, ich habe zum Beispiel den Wert Pi oder irgend
sowas, eine Konstante, die immer halt auch als Konstante in dem
Programm behandelt wird, kann ich direkt hier reinprogrammieren.
Beziehungsweise nicht ich, das macht der Compiler gut.
Und so viel eigentlich schon zu den verschiedenen Arten von
Befehlsformaten.
Jetzt haben wir noch 10 Minuten, jetzt fangen wir noch ein bisschen an
mit Adressierungsarten.
Das heißt, wir sind immer noch bei der Instruktionssatzarchitektur.
Wir haben uns zuvor angeguckt, wie sieht ein Befehlsformat aus.
Und jetzt das, wo ich anfangs schon darauf hinwies, was sind denn die
Adressierungsarten?
Da will ich jetzt gar nicht mehr so viel sagen, das wäre eine
Wiederholung.
Wir möchten adressieren, wir möchten auf den Speicherzug greifen, auf
den Hauptspeicher, auf die Register und so weiter.
Und um dies effizient zu machen, brauche ich verschiedene Arten der
Adressierung.
Und das schauen wir uns einfach mal an.
Und hier ist es auch rot, ich habe das Eingang schon erzählt.
Es hängt damit zusammen, wie viel Programmieraufwand ich da
reinstecke.
Das möchte ich wirklich noch mal unterholen, auch wenn ich es schon
gemacht habe.
Ich kann alles mit einer Handvoll von Befehlen programmieren.
Trotzdem habe ich 60 oder 120 oder so viele Befehle in einem Rechner.
Und die noch kombiniert mit den ganzen Adressierungsarten, die ich
habe.
Man kann sich vorstellen, wie viele verschiedene Möglichkeiten es
zumindest theoretisch gibt, ein Programm zu schreiben.
Ich kann ein Programm schreiben, wo ich natürlich nicht ein Befehl
zweimal vorkomme oder so.
Es hängt mit Programmierzeit zusammen.
Ich möchte effizient programmieren.
Also sozusagen meine Zeit als Programmierer ist wertvoll, die kostet
Zeit, die kostet Geld.
Da unterstützen mich die Programmierungsarten.
Aber die Programmierungsarten haben auch eine Auswirkung darauf, wie
schnell das Programm am Ende ausgeführt werden kann.
Läuft das schneller, läuft das langsamer.
Das heißt, es sind also eine ganze Menge Dinge, die daran hängen,
welche Adressierungsart ich von den vorhandenen auswähle.
So, wie funktioniert das?
Jetzt haben wir genau das, was wir eben hatten.
Wir haben verschiedene Arten von Adressen.
Wir haben die Adresse im Programm, die nur der Programmierer kennt.
Wir haben die effektive, logische Adresse, die nur der Prozessor
kennt.
Und dann gibt es noch die physikalische Adresse, die nicht mal der
Prozessor kennt.
Das waren die drei verschiedenen Arten der Adressen.
So, Adressen müssen aber berechnet werden.
Und was wir hier machen wollen, Adressberechnung, geht hierum.
Geht hierum, diese zu berechnen.
Wir möchten diese Adresse berechnen, hier die.
Und zwar, die Frage ist, wie kommt man von hier nach hier?
Das ist die Frage.
Das wollen wir berechnen.
Jetzt können Sie natürlich fragen, wie komme ich auf die physikalische
Adresse?
Die Vorlesung geht noch bis Sommer.
Wir werden uns auch überlegen, wie man von der logischen, effektiven
Adresse auf die physikalische kommt.
Das hängt dann offensichtlich nicht mehr mit dem Prozessor zusammen,
das hängt damit zusammen, wie mein Hauptspeicher aussieht.
Wie groß mein Hauptspeicher ist.
Also alles das, was der Prozessor nicht weiß.
Und da gibt es auch so Dinge, und Sie hatten das da oben eben gesagt,
es gibt so Dinge wie virtuelle Adressierung.
Und das schauen wir uns in ein paar Wochen natürlich auch noch an.
Heute, beziehungsweise in der nächsten Vorlesung geht es darum, wie
komme ich von hier nach hier?
Und wie sieht das dazu im Programm aus?
Was hier steht, habe ich an sich schon alles gesagt.
Und jetzt ist der Baum der ganzen Adressierungsarten.
Jetzt kann man sich überlegen, wow, eine ganze Menge.
Adressierungsarten, grob kategorisiert, kleiner kategorisiert.
Also, das sind alles Adressierungsarten, die ich natürlich nicht mit
allen Befehlen kombinieren kann, aber je nach Prozessor schon zum
größten Teil.
Sie sehen, Sie haben eine Riesenauswahl an Möglichkeiten, hier Ihren
Algorithmus zu schreiben.
Jetzt fangen wir erstmal mit ein paar Stück an.
Registeradressierung, um es ganz einfach zu machen.
Dann kommen die einstufige Adressierung, und hier wird es ein bisschen
haarig mit der zweistufigen.
Das müssen wir dann sehr gut durchgehen, dass wir es auch alle
verstehen.
So, Registeradressierung, implizite Adressierung, Fleckadressierung,
explizite, für jedes dieser Punkte habe ich eine Folie.
Ich habe immer zwei Folien.
Die eine Folie gibt erstmal generell an, wie es in exemplar
-schreibweise aussieht.
Also, MNemo, das ist der Code.
In einem Semplar, da steht sowas, wie zum Beispiel, das haben wir die
ganze Zeit schon benutzt, aber haben es noch nicht so genannt.
Da haben wir zum Beispiel so ein addr oder sowas.
Was ist das überhaupt?
Das ist ein sogenannter Mnemonic, nennt man das.
Also sozusagen eine Abkürzung.
Das ist eine Abkürzung.
Add, oder Sub, oder Mult, oder Shift-R, Logical-Shift-R, oder sowas.
Diese Buchstabenkombination ist eine Abkürzung, die sich Mnemonic
nennt.
Ein bisschen schwierig auszusprechen.
Und hier haben wir eben die Schreibweise für die implizite
Adressierung.
Die geht auf den Akkumulator.
Und hier steht, und das wollen wir ja finden, wie wird die effektive
Adresse berechnet.
Die effektive Adresse ist kodiert im OpGrid.
Das heißt, die Berechnung, in Anführungszeichen, die ist so einfach,
die brauche ich aus dem OpGrid nur rauszuschneiden, nur rausholen.
Also das wäre genau diese Adressierung.
Schauen Sie, diese Adressierung von der Folie hier irgendwo vorne.
Diese Adressierung wäre das hier.
Die Adresse geht als Bitwert schon im Codeword drin.
Die brauche ich nur rauszuextrahieren.
Und damit meine ich diese Adressierung hier.
Die ist im OpGrid enthalten, ich hole sie einfach raus.
So, und hier ist ein Beispiel dafür.
Hier haben wir jetzt wieder diesen Mnemonic LSRA.
Das ist einfach eine Konvention, die in einem Datenbuch
festgeschrieben ist.
Und hier steht Logical Shift Write Accumulator.
Und die macht genau das, was eine Kommilitone von Ihnen eben gesagt
hat.
Nämlich, wir schieben die Werte, die Bitwerte eines Registers, in
diesem Falle nach rechts.
Also ich mal das mal an, damit das auch klar ist, was damit gemeint
ist.
Ich habe den Akku, ich habe den Akku, das ist der Akku, der aus 8 Bits
besteht, usw.
Und was ich hier mache ist, ich schiebe dieses Bit von hier nach hier,
dieses Bit von hier nach hier.
Stop, da steht da Write, da steht Write.
Ich verschiebe das Ganze.
Nichts anderes passiert hier.
Logical Shift Write Akku.
Und das heißt, das bezieht sich offensichtlich auf den Akkumulator.
Das Register in diesem Falle ist das spezielle Register der
Akkumulatoren.
Und hier ist jetzt nochmal angegeben, wir haben auf jeden dieser
Folien eine Darstellungszeichnung, damit man sieht, was im Rechner
abgeht, was dort passiert, um diese, wie wir gesagt haben, die erste
Adresse nochmal, die heißt die effektive logische Adresse zu
berechnen.
Gut, jetzt haben wir mit der Adressierung hier kurz angefangen, Sie
haben zumindest gesehen, es gibt hier noch eine ganze Menge und das
werden wir dann das nächste Mal durch.
Bis zum nächsten Mal, tschüss.