So, ich hatte am Mittwoch her quasi aufgehört,
mit der kurzen Vorstellung von einigen Rechenarchitekturen,
da jetzt eben gesagt, im Prinzip also eine Möglichkeit,
klassisches Pipelining, da werden wir ihn rechnen,
also auch noch genauer kennenlernen, ist dann also mit,
hier ist dann also die zuhörige Hardware-Struktur,
wenn man das also jetzt auch so ein bisschen vergleicht mit dem,
was jetzt eben vielleicht schon von dem Dekor in dem Praktikum bekannt ist,
beziehungsweise bei dem,
was ich sonst also auch immer so ein bisschen gezeigt hatte und nicht,
dann sieht man eben hier schon mal so als groben Unterschied.
Es gibt hier eben getrennt Instruktions- und Datenspeichern.
Hier ist dann sowas wie Registerbank, Alu, Programmzähler,
hier irgendwelche Adressberechnungen und dazwischen sind dann eben immer hier entsprechend breite Register.
Das ist dann das, was dann nachher sage ich für dieses Pipelining verantwortlich ist,
also sprich die sukzessive Abarbeitung der einzelnen Schritte in diesem Befehlsabarbeitungszyklus.
Wie gesagt, das wird uns dann im übernächsten Kapitel dann noch genauer begegnen.
Deswegen würde ich da jetzt also hier auch gar nicht weiter noch irgendwie darauf eingehen.
Ja, wir hatten ja hier eigentlich jetzt angefangen,
also zum Befehlssatz was zu machen. Also hier ist dann mit Befehlsformaten nochmal so kurz skizziert,
einfach nur als ein, so ein Beispiel,
weil ich hatte ja gesagt, also im Prinzip geht es ja darum,
dass man in seinem Befehlswort das logisch in mehrere Teile unterteilt,
nämlich einmal sowas, wie jetzt also den eigentlichen Befehl,
also das, was ein Obcode ist.
Und dann irgendwelche Teile, mit denen ich,
also entweder Emediate-Wert darstelle, Emediate-Werte darstelle,
mit denen ich jetzt also irgendwelche Register adressieren kann,
so wie es jeder Fall ist, oder mit denen man eben irgendwelche Speicher-Adressen beschreibt.
Und wie gesagt, hier als zum Beispiel dann eben dafür
war es also quasi so ein typischer Selbstbefehl jetzt als Alu-Befehl wäre,
also in diesem Fall geht es darum,
dass also zwei Register voneinander suprahiert werden und das Ganze dann eben in einem dritten Register landet.
Nicht dazu gibt es dann eben hier irgendwie so ein Tal,
wo also quasi der Obkur drinsteht,
dann eben die drei Register und gut, hier ist es jetzt eben so,
dass also hier nur gesagt wird, es ist ein Alu-Befehl,
hier steht dann darin, welche Operation ausgeführt wird in diesem Fall,
also eine Subtraktion.
Ne, und dann gibt es dort noch als Besonderheit
sowas wie solche Schiffwagenheiten
Wie gesagt, das als ein Beispiel,
anderes Beispiel hier dann eben für so ein Emediate-Format,
wo man dann eben sieht,
es soll hier ein Speicherzugriff erfolgen, also sprich ein Ladebefehl,
ist dann eben hier in dem Obcode beschrieben,
dann habe ich das Zielregister irgendwo mit drin, ich weiß so,
also jetzt also von dem Speicher quasi an Wert gelesen in dieses Zielregister transferiert wird.
So, und jetzt habe ich natürlich das Problem bei dieser Art von Befehlsformaten,
wenn ich jetzt sage, ich habe ein 32-Bit-Maschine,
also alles ist irgendwie 32-Bit-breit und ich kann noch 32-Bit-Speicheradressen haben.
Und der Befehl selber ist aber auch nur 32-Bit-breit und
ich brauche hier also beispielsweise dann eben schon 16-Bits für,
beziehungsweise also minimal, also irgendwie sowas wie 11-Bits,
nämlich also hier, um zu beschreiben,
es sollen Speichertransfer stattfinden und wo landet das?
Dann habe ich für die Adresse natürlich nicht mehr
die komplette Wortbreite bei euch. Das heißt,
also da arbeitet man dann immer
mit verkürzen im Mediath werden
So eine Eigenheit ist, bei diesem Befehlssatz in einem Nips-Prozessor,
ist halt dann eben, dass ich zwar, also nur eine Kurzadresse zulasse,
dass dann aber im Prinzip implizit dann immer noch eine Basisadresse aus einem Register genommen wird.
Das heißt, das, was also eigentlich für die Adressberechnung stattfindet,
ist, dass die Adresse, von der ich lese,
eben hier in diesem allen Register steht und ich hier dann eben noch einen zusätzlichen Offset mit angeben kann,
sodass ich daraus dann also quasi die Adresse aus Inhalt von diesem Register plus diesem Offset ergeben kann.
Gut, als zweites Beispiel, jetzt so für Befehlsformate,
hier erstmal der Allgemeine Fall M.Core,
im Praktikum kennen wir das dann in verkürzter Version als Deko.
Auch Ende der 90er Jahre von Motorola entwickelt, 32 Betriffsarchitektur,
vergleichsweise sehr einfach, also mit Programmzähler, Ansatz, Standardregister,
im Carif Fleck und eben vergleichsweise klein Befehlssatz. Mich ist dann eben auch ganz gut geeignet für irgendwelche kleinen Systeme beziehungsweise eingebettete Systeme. Gut,
und davon abgeleitet ist dann eigentlich, sagen wir jetzt,
der Prozessor oder diese Architektur,
die dann immer so immer in dem Rechnerstrukturenpraktikum eingesetzt wird,
also dieser Deko,
der dann also quasi jetzt mit nur 16 Witcrowd-Breite arbeitet,
dann eben auch Programmzähler, 15 oder 16 Standardregister,
ein Carry Flag hat und dann eben ein paar sehr einfache Befehle,
mit denen man dann aber eben auch schon so einige kleine Hand programmieren kann,
also bis zu irgendwelchen klassischen kleinen Beispielen von irgendwelchen rekursiven Aufrufen,
also Türme von Hornau ist dann dort also quasi eine der Übungsaufgaben,
die dann beispielsweise gelöst werden sollen.
Gut, hier nur nochmal die Referenzen darauf,
wenn sich da nochmal weiter vertiefen wollen, vorbereiten wollen,
Sachen aus dem Praktikum nahearbeiten wollen.
Gut, hier auch nur aufgelistet nochmal,
also im Prinzip 16 Standardregister, Programmzähler,
sowas wie ein einzelnes Einzelnes,
ein Bit Flag für einen Carry
Und dann gibt es eben hier noch, war es jetzt
also für den Programmierer, nicht direkt zugreifbar ist.
Der Befehlszähler, oder das Befehlsregister, also es ist ein
Struction-Register, also Befehlszähler ist er ja hier.
Und dann eben noch ein Satz von Registern,
die benutzt werden, um quasi auf den Speicher zuzugreifen,
nämlich einmal Abreißregister, Datenregister beim Lesen,
Datenregister beim Schreiben und hier noch irgendwelche Enable-Leitungen,
um den Speicher quasi einzuschalten
Nee, und da gibt es dann eben auch vergleichsweise einfachen Befehlssatz.
Ich hatte ja arm Mittwoch schon grob vorgestellt,
wenn man da sowas wie so Befehlsätze hat,
was man da eigentlich generell an Befehlen braucht.
Nicht nur so Alu-Befehle, Sprünge und, und, und.
Also hier dann eben auch mal so die einfachen Sprünge,
dann spare ich halt Zugriffe, bedingt,
du unbedingt gesprünge und eben irgendwas um Unterprogramme aufzurufen.
Gut, hier sieht man jetzt also auch nochmal,
wie dann beispielsweise das befindet
Feelsformat aussieht. Also wie gesagt, mal 16 Bild ist man da
natürlich vergleichsweise eingeschränkt, kann also nur relativ wenig mitmachen.
In diesem Fall werden dann eben vier Bild für einen Obcode benutzt.
Bei Alu-Befehlen habe ich dann quasi nur zwei Registeradressen,
dann eben irgendwie sowas wie so kleine Emediate,
Load on Store-Befehle und eben irgendwas, um jetzt also irgendwie eine Sprange zu machen.
Aber auch hier sieht man eigentlich immer dieses Grundprinzip jetzt von dem,
was eigentlich so ein Befehlsatz ausmacht, dass man eben versucht,
also sein Wort in irgendwelche gleichartigen Scheiben oder Bereiche A einzuteilen und zu sagen,
okay, das ist mein Obcode, das ist eben die Adresse,
das ist dann eben, was jetzt noch überbleibt,
was sie dann eben zum Beispiel von Emidiate nehmen kann,
wenn das eben ein Befehl ist,
wo dort also wirklich jetzt ein expliziter Wert steht.
Gut, im folgenden, vielleicht nochmal kurz die möglichen Adressierungsarten vorstellen.
Mit Adressierung ist jetzt gemeint, die Fragestellung,
wenn ich jetzt irgendwelche Befehle habe, also wie gesagt,
allgemeiner Fall, Alu-Befehl,
woher kommen die Operanten
Ich war letztendlich, kann ich sagen, die können aus
dem Hauptspeicher kommen. Die können aus irgendeinem Register kommen.
Oder die können ein Immmediate-Wert sein. Das heißt,
die stehen einfach direkt als Binärstring im Befehlswort direkt mittleren.
Nicht dann wird noch unterschieden, jetzt bei den Befehlen oder Befehlsätzen,
wie viele Operanten ein Befehl dann jeweils hat.
Demzufolge kann man dann auch immer von 0, 1,
2 und 3 Adressmaschinen reden.
Wie die Operanten arrassiert werden
Auch da gibt es sehr viele sehr unterschiedliche Varianten.
Hier sollen jetzt also wirklich nur so die allereinfachsten, ganz allgemein vorgestellt werden.
Also da geht es jetzt nicht darum, sich zu merken,
also bei dem der und der Prozessor Architektur mache ich das so,
sondern eigentlich nur das Wissen,
was ist also der Unterschied zwischen Emediate direkter oder indirekter Adressierung,
solche Sachen.
Und diese Eigenschaften und letztendlich natürlich nun also der Befehlssatz dahinter,
ist eigentlich das, was man nimmt,
um jetzt also rechne Architekturen dann entsprechend zu unterscheiden
Gut, bei der Avaresierung, beziehungsweise bei dem Zugriff auf die Operanten, das,
was wir dann eigentlich auch immer noch mal im Hinterkopf behalten sollten,
ist diese Aussage, nämlich also dass der Zugriff auf den Hauptspeicher eben vergleichsweise deutlich langsamer ist,
als der Zugriff auf den Register.
Das führt in der Praxis jetzt eben eigentlich dazu,
dass man sagt, also alles,
was aktuelle Architekturen machen bzw. womit die rechnen, ist,
dass eigentlich Alu-Befehle immer nur auf Registern rechnen und ich dann eben explizit lade und Speicherbefehle habe,
also Load and Store, die man eben nutzt,
um vom Hauptspeicher zum Register, Datentransfer zu machen, beziehungsweise wieder zurück.
Von der Grundidee oder von der Grundarchitektur her ist es zwar so,
dass wenn ich jetzt sage,
ich habe jetzt also hier irgendwie einen X86-Prozessor drin, von dem,
was er als Befehlssatz implementiert hat und was dort gemacht werden kann,
kann jeder Befehl direkt auf den Hauptspeicher auch zugreifen und von dort aus,
also auch in irgendeiner Alu-Operation ausführen.
Ich glaube, das ist eben irgendwo so die historische Erbe,
wo man jetzt sagen muss, okay,
in den 1980er Jahren waren die Prozessoren deutlich langsamer.
Okay, da war der Hauptspeicher zwar auch langsamer,
aber da war hier dieser Geschwindigkeitsunterschied eben nicht so groß,
beziehungsweise da war man überhaupt froh,
dass man also irgendwie was hatte, was rechnen konnte.
Und da hat es jetzt eben nicht gestört,
wenn ich jetzt bei irgendeinem Alu-Befehl erstmal so auf
den Hauptspeicher warten musste,
bis die Operanten
geholt werden konnten
So, das können heutzutage alle X86-Prozessoren auch immer noch,
wenn man sich aber eben den Assembler-Code ansieht,
der aus irgendeinem C-Compiler oder irgendeinem anderen Compiler rauskommt,
werden diese ganzen Befehle beziehungsweise diese Adressierungsarten nirgendwo mehr benutzt,
weil inzwischen eigentlich alles, was dort gerechnet wird,
eben auch immer nach diesem Load-Store-Prinzip verfährt.
Das heißt, vom Hauptspeicher in irgendwie die folgenden Register
die Daten transferieren und dann eben die Alu-Befehle immer nur direkt
auf den Registern ausführen
Gut, ja, wenn man sich jetzt ansieht,
mit diesen Adressierungsarten, was soll dort passieren? Wie gesagt,
bei so typischen arathmetischen Operationen kann man ja sagen,
also im Maximalfall möchte ich eigentlich drei unterschiedliche Variablen ansprechen können,
nämlich zwei Quellen und irgendwie ein Ziel,
nicht demzufolge möchte man dann eigentlich sowas haben wie Aditionen von
Register 4 und Register 5 und das landet im Register 2.
Und das, wo sich jetzt eben die Befehlsätze bzw.
die Adressierungsarten unterscheiden, ist Woher kommen die Operanten?
Sind das wie hier Register oder sind das eben irgendwelche Speicheradressen?
Wo soll das Resultat hin? Also ist das auch Register
oder ist das auch ein Speicher? Also auch das lässt
der Befehlssatz eigentlich zu. Und demzufolge kann man da dann
eben entsprechend die Architekturen so ein bisschen unterteilen. Wie gesagt,
letztendlich hängt das dann immer von der konkreten Realisierung so eines möglichen Datenfahrtes ab. Ich habe ja ganz am Anfang dieses Kapitels bzw. dieser Überlegung jetzt,
dass es hier zu einem Befehl setzen,
also schon mal so grob so ein bisschen vorgestellt,
das, was dann eigentlich ja die Kunst ist,
dass man sagt, ich habe bestimmte Hardware-Einheiten,
die sind so und so miteinander irgendwie verschaltet und die Abarbeitung eines Befehls heißt dann immer,
dass ich also quasi entsprechende Datenfahre schalte,
um jetzt von da,
vom Register irgendwie zu Alu zu kommen und das Ergebnis wieder
in Register zu bringen oder um das Ergebnis irgendwie jetzt
in den Hauptspeicher zu bringen oder was auch immer
Also hier ist dann mal so ein typische Datenfahrt,
wo man jetzt sagt, okay, ich habe hier einen Register,
ich habe hier noch irgendwie sowas wie Eingaberegister für die Alu,
ich habe meine Alu, gegebenenfalls noch irgendwie sowas wie ein Ausgaberegister,
was in dem Fall dann aber eben auch schon mal heißt,
also eine Art Operation braucht logischerweise vier Tage,
bevor sie also wirklich fertig ist,
weil ich eben sagen kann, okay,
im ersten Tag werden A und B also quasi hintransferiert,
im zweiten Tag habe ich hier das Ergebnis,
so jetzt habe ich Unsinn erzählt,
also sie braucht ihn zufolge drei Takte und im dritten Tag
habe ich das Ergebnis wieder in die Registerbank zurückgeschrieben
Ist dann eben letztendlich immer das, was,
also diese Hardware-Struktur dann zulässt. Und je nachdem,
wie die jetzt eben konkret aufgebaut sind, also sprich,
wie viele Register gibt es hier noch irgendwie dazwischen auf diesen Wegen,
dauert das dann eben entweder länger oder kürzer.
Um das jetzt also hier nochmal an so einem anderen Beispiel kurz zu zeigen,
also hier, das wäre jetzt also ja nochmal,
also diese MIPS-Architektur, also hier würde es jetzt aber vier Tage benötigen,
Registerbank mit dem ersten Tag landen, quasi die Werte hier drin.
Das sind dann also quasi die Äquivalente zu diesen einzelnen Registern A und B.
Mit dem nächsten Tag habe ich das Ergebnis hier drin.
Dann brauche ich noch einen Tag, der Speicher interessiert nicht,
aber das Ganze wandert hier erst nur nur eine Stufe weiter.
Und dann nochmal einen weiter, da habe ich das Ergebnis dann erstmal letztendlich hier drin.
Gut, das ist aber dann eben wirklich immer davon abhängig,
wie sehen diese Hardware-Einheiten konkret aus?
Wie sind die entsprechend miteinander verschaltet? Wo man dann eben sagt,
okay, also wenn ich jetzt die X86-Architektur nehme,
Intel oder AMD oder wer auch immer,
dann also entsprechend das so und Suda dort aufgebaut für seinen Prozessor.
Nicht, wenn ich das also hier irgendwie auf so einen Mips-Rechner nehme,
sieht das also so aus, wenn ich das so auf irgendeinen Arm-Chor abbilde,
sieht das dann also wieder entsprechend anders aus, in demzufolge muss man immer zusehen,
wie dann quasi diese Pfade hier quasi geschaltet werden können.
Gut, aber jetzt waren wir hier bei den Evasierungsarten,
das heißt, da geht es jetzt erst mal darum zu fragen,
woher kommen diese Operanten?
Wenn ich jetzt also im Prinzip für ein Alugefehl drei Operanten habe,
nach diesem Frau Neumann, Paradigma, liegen eigentlich erstmal alle Daten im Hauptspeicher.
Das heißt aber, von drei Adressbefehlen,
ich habe zwar Operanten und ein Resultat,
ich muss also quasi im Speicher, also hier irgendwie drei Zugriffe drauf machen.
Kann man technisch gesehen also auch so realisieren,
dass ich einen Speicher habe,
wo ich also quasi mehrere lese und Schreibports habe und das dort mache,
ist aber eben vergleichsweise sehr teuer aufwendig und von der Geschwindigkeit
her auch alles andere als optimal
Also auch das führt dann eben dazu, dass man sagt,
okay, in der Regel nutze ich eben diese Register im Prozessor,
um das Ganze zwischenzuspeichern.
Ich habe dann eben hier einfach nur diesen Speicherzugriff,
das ist eben genau das, was dann Load und Store machen.
Nicht, dass ich also quasi hier von irgendeiner Adresse aus
dem Speicher lese und das in Register transferiere,
beziehungsweise genau umgekehrt,
dass ich dann eben den Inhalt eines Registers auf eine Speicheradresse
irgendwo schreiben kann
Gut, und bei den unterschiedlichen Befehlssatzarchitekturen kann man eben sagen,
okay, also bei Restrechnern ist es eben so,
dass die Rechenbefehle explizit nur Metrigistern arbeiten, also Speicher,
Zugriff geht eben nur über Load ins Store.
Bei Cis-Gerechnern ist es eben so, dass es eigentlich gemischt ist,
also genau das, was ich jetzt gesagt hatte, hier als Beispiel X86-Architektur.
Es kann eben eigentlich auch direkt mit jedem Befehl entsprechend auf den Speicher zugegriffen werden,
ist aber eben vergleichsweise langsam und inzwischen arbeiten eigentlich alle Compiler so,
das von den Befehlen beziehungsweise von den Aggressierungsarten,
die also beim X86-B4-Satz möglich sind,
eigentlich die auch nur so benutzt werden,
dass man dort eigentlich nur eben auch über Load and Store und Register-Adressierung arbeitet.
Gut, das würden wir dann also auch noch so ein bisschen in den Beispielen im nächsten Kapitel dann sehen. Gut,
jetzt hatte ich ja schon gesagt, also mit den Adressen,
welche Varianten gibt es da, wenn wir jetzt also sagen Alu-Befehl,
maximal drei Adressen möchte ich eigentlich ansprechen, kann ich natürlich sagen,
okay, Der optimale Fall wäre eben wirklich sowas wie ein Drei-Adressformat,
was also entsprechend flexibel ist, vergleichsweise einfach zu programmieren.
Nachteil ist aber, in dem Befehlswort muss ich dann auch erstmal alle drei Adressen unterbringen können.
Beim Zwei-Adressformat arbeitet man dann in der Regel so,
dass quasi der eine Operant dann eben auch wieder mit überschrieben wird.
Das heißt also, die eine Adresse wird dann eben doppelt verwendet.
Beim Ein-Adressformat habe ich eigentlich nur ein einziges Register und Allo-Operationen arbeiten dann mit einem expliziten Adressen Kumulatorregister,
wo man dann eben sagen muss, okay,
also ich kann, habe dann eben noch zusätzliche Befehle,
mit denen ich quasi dieses Akkumulatorregister erst mal laden kann.
Ich kann dann eben rechnen, Akkumulatorregister plus irgendein Operant
und habe dann eben noch mal einen zusätzlichen Befehl,
mit dem ich das Kumulatorregister ein anderes Register übertragen kann.
Nicht, aber ich brauche dann in den Befehlen eben wirklich
nur noch eine einzige Adresse.
Warum macht man jetzt überhaupt solche Überlegungen mit dieser Anzahl der Adressen
Aus genau diesem Grund war ich also hier diese Adressen irgendwo in meinem Befehlswort unterbringen muss.
Und hier also bei dem Deko-Befehlssatz sieht man eben zum Beispiel schon mal,
also hier bei diesen Alu-Befehlen, das ist eben eine Zwei-Adressenmaschine.
Das heißt also, das eine Register wird dann eben sowohl
als Quelle als auch als Ziel genutzt. Ich war das Problem,
ist natürlich, wenn ich hier so eine eingeschränkte Wortbreite habe,
kann ich jetzt also nicht, wenn ich,
also beispielsweise sage ich,
hätte 32 Register,
dann bräuchte ich also hier für die Kodierung eben schon irgendwie
fünf Bit,
um die zu unterscheiden
Nicht, wenn ich eine 3-Adressenmaschine hatte,
bräuchte ich also 3 mal 5 Bit,
um dann also quasi jetzt meine beiden Quellen und das Ziel mitzubeschreiben.
Ja, damit sind es schon mal so 15 Bit aus meinem Befehlswort
letztendlich weg. Und wenn das Befehlswort entsprechend klein ist,
beziehungsweise wenn ich jetzt also noch größere Registersätze habe, sieht man,
dass man da dann eigentlich so einen Zielkonflikt dann immer hat.
Deswegen kann es eben sinnvoll sein, dass man sagt,
okay, ich mache jetzt eben nicht diesen allgemeinen Fall,
sondern mache das eben kürzer.
Und als Extremfall gibt es dann eben
noch null Adressformat
Das ist dann eben klassisch Stack-Maschieb-Maschine. Das heißt,
also ich habe eigentlich gar keine Register,
sondern ich habe im Kellerspeicher ein Stack,
kann mit Push und Pop-Befehlen explizit Werte auf den Stack schreiben,
beziehungsweise davon lesen.
Und alle Alu-Befehle machen also nichts anderes,
als dass sie die obersten beiden Elemente vom Stack nehmen.
Entsprechend die arathmetische Operation ausführen und das Ergebnis wieder auf den
Stack schreiben. Das ist dann also quasi dieses Ziel,
dass ich es dann eben sage,
top auf Stack plus next.
Und das Ergebnis landet dann eben wieder als Tor
Vorteil dabei ist dann eben, sowas wie Adressverwaltungen fehlt.
Also ich muss mich jetzt nicht darum kümmern,
wie viele Register ich habe, sondern ich habe immer nur einen Stack,
in den ich was reinschiebe. Ich bloß,
man muss da natürlich bayeratmetischen Ausdrücken jetzt wieder dafür sorgen,
dass alles dann irgendwie passend sortiert wird,
damit ich das dann eben entsprechend richtig mache.
Gut, im Compilerbau ist sowas dann also auch mal ganz beliebt.
Gut, was sind so die Unterschiede jetzt von diesen verschiedenen N-Adress-Befehlsätzen?
Sieht man an diesem Beispiel nochmal, beziehungsweise dazu wird es
dann im nächsten Jahr auch nochmal eine Übungsaufgabe geben.
Wo ich dann eben sage, okay,
also ich habe hier einen arathmetischen Ausdruck, und zwar A-B,
geteilt durch C plus D-MALE.
Es gelten also die üblichen arathmetischen Regeln, das heißt,
das ist hier Punktrechnung vor Strichrechnung.
Ansonsten wird hier also dann auch irgendwie noch barf geklammert.
In einer Drei-Adressenmaschine könnte ich dann eben sagen,
okay,
Subtraktion Z ist gleich
Damit habe ich diesen Teil hier abgearbeitet,
dann rechne ich,
also jetzt für ihr mit einem Hilfsregister Tee und sage D
mal E und addiere C hinzu und teile Z dann eben durch den Weg dieses Hilfsregisters,
das Ergebnis landet im Set und damit habe ich dann also hier quasi diesen Ausdruck mit vier Befehlen bereichnet.
Bei einer Zwei-Adressmaschine würde ich eben sagen, okay,
im ersten Schritt lade ich A mit einem Move-Befehl in das Set rein,
subtragiere dann B,
das Ergebnis landet dann eben wieder ins Set
Lade die nach Tee, multipliziere das mit E,
agiere C und teile dann Z durch dieses Hilfsregister und habe das hier. So,
und was man hier dann eben auch schon sehr gut sieht,
also bei, je mehr Adressen ich unterscheiden kann,
umso kürzer wird mein Programmcode, umso kompakter,
also das heißt, hier brauche ich vier Befehle,
hier brauche ich schon sechs Befehle bei einer Ein-Adressmaschine,
wo ich dann also quasi jetzt mit diesem Akkumulator arbeite,
der sich hier mit D geladen wird
Mal eh, nicht das Ergebnis liegt ja in einem Akkumulator,
dann wird C hinzuaddiert, das wird dann erstmal ein Set gespeichert,
aber in den Akkumulator geladen, Besub tragiert.
Und dann Z oder dieses Ergebnis durch Z geteilt und das Ganze
nach Z gespeichert und nicht, da sieht man also,
hier baue ich dich dann eben schon acht Befehle und bei einer Nulladressmaschine,
wenn das Ganze auf dem Stack stattfindet,
brauche ich eben
noch mehr Befehle
Wie das Ganze funktioniert ist, also hier nochmal ein bisschen genauer aufgeschrieben,
also mit dem ersten Befehl Pursche würde quasi eh in den Steck hineingeschoben,
anschließend d hineingeschoben, entsprechend wandert das Ergebnis dann hier,
also oder ist eh hier dann ein weiter,
dann multipliziert das Ergebnis landet dann quasi unter Top-off-Stack,
also die obendrauf,
mit dem Push wird also das C reingeschoben, addiert,
dann habe ich also quasi unter Top-off-Stack diesen Ausdruck schon fertig ausgerechnet,
mit dem entsprechenden Puschbefehl wird,
B. Und später dann A. in das Deck geschoben,
die beiden voneinander subtrahiert, da anschließend die Division gemacht.
Ich habe dann also quasi hier jetzt mal komplett ausgerechnet.
Ausdruck und mit dem entsprechenden Popbefehl. Das heißt,
ich nehme Top of Stack und speichere das dann eben ins Set.
Habe ich dann eben hier diese letzte Zuweisung noch gemacht.
Gut, das zu der Anzahl der Adressen. Welche Adressierungsarten
gibt es? Sind hier jetzt also nochmal grob aufgelistet,
wobei das also auch nicht vollständig ist,
weil also je nach Befehlssatz kann man sich dann noch beliebig viele unterschiedliche Varianten ausdenken.
Ich habe aber so die allgemein bekannten bzw. bekanntesten sind immediate.
Das heißt, dort steht also wirklich in den Befehlsworte auf
der Hand direkt selber drin. Nachteil bei dem Ganzen,
bei der begrenzten Wortbreite habe ich dann natürlich nicht mehr
den kompletten Platz in meinem Befehlswort,
um jetzt also irgendwelche Zahlen darzustellen
Beziehungsweise um irgendwelche Adressen dort reinzuschreiben. Es sei denn,
man benutzt dann wieder mehr Wortbefehle, wird gleich auf der übernächsten Folie,
glaube ich, dann folgen. Dann gibt es direkte Adressierung,
da steht dann eben die Adresse des Operanten im Befehl.
Dieses ist dann in diesem Fall dann eine Speicheradresse, also Hauptspeicher.
Das heißt also, damit mache ich dann einen Speicherzugriff.
Auch da habe ich natürlich jetzt entweder die Notwendigkeit, mehr Wortbefehle zu benutzen.
Das heißt also, ich brauche dann eben für einen Befehl mehrere Maschinenworte.
Oder ich habe eben nun einen entsprechend kleineren Adressbereich.
Indirekt ist das Ganze dann noch mal über einen zweiten Hauptspeicherzugriff.
Das heißt also, man hat, man adressiert einen Wert
oder eine Adresse im Hauptspeicher unter dieser Adresse im Hauptspeicher steht dann,
von welcher anderen Hauptspeicheradresse dann eigentlich das Ganze gelesen werden soll.
Das sind also Gerade so diese indirekte Adressierung, das ist etwas,
was dann eben auch noch so aus der SISC-Ära von früher kam,
was also heutzutage eigentlich auch so gut wie gar nicht mehr benutzt wird. Gut,
dann, hier ging es jetzt darum,
dass ich also erstmal nur hier über den Hauptspalt,
also entweder im Mediat oder eben hier über den Hauptspeicher gehe,
kann man das Ganze aber natürlich dann also auch mit Registern machen,
das heißt also Registeradressierung, das heißt,
ich habe dann eben den Adresse meines Registers entsprechend im Befehl,
und das ist eben das, wo man dann sagen kann,
okay, damit kriege ich ihn einen Befehl,
wenn ich jetzt nicht zu große Registersätze habe,
dann eben auch schon zwei oder drei Adressformate mit rein kodiert.
Register indirekt ist dann quasi das Äquivalent zu dem Indirekt,
das heißt, in dem Register steht dann eben die Hauptspeicheradresse,
auf die dann eben entsprechend zugegriffen wird. Und häufig,
was man dann eben auch noch hat,
sind also irgendwelche Indizierten, Adressierungen,
wo es dann eben letztendlich darum geht, dass man sagt,
also ich habe einen Register und ich habe noch irgendwie
ein zweites Register oder einen Emediate-Wert und Darüber wird dann also quasi arathmetisch erst mal die Summe gebildet,
also eine Addition gemacht und dann eben entsprechend also auf irgendwie den Speicher rauf zugegriffen.
Das ist insofern immer ganz elegant,
weil man damit eigentlich ganz gut immer so was wie Array-Zugriffe machen kann,
indem ich dann eben sage, okay, also der,
wenn ich eine Array in einem,
in einem Hauptspeicher dann gespeichert habe, in aufeinanderfolgenden Adressen,
dann habe ich eben quasi für das erste Array-Element quasi die Basisadresse irgendwo in dem Register gespeichert und kann dann eben sagen,
das nächste Element ist dann eben Basisadresse plus 4, plus 8,
plus 12, je nachdem wie groß die Datenstrukturen da sind, wie viele beide ich dann eben quasi jeweils dann auch als Offset mit benutzen muss.
Gut, hier ist das Ganze,
was ich jetzt eben so lang und breit versucht habe,
einigermaßen verständlich darzustellen, nochmal ein bisschen grafisch dargestellt.
Ich denke, dann wird das vielleicht also auch noch klarer,
falls das jetzt bei einigen noch nicht angekommen ist. Also wie gesagt,
Emediate, wenn ich jetzt sowas wie nun ein Wortbefehl habe,
habe ich eben hier einfach direkt jetzt mal eine Zahl drin,
habe aber natürlich, da ich teile dieses,
meines Befehlswortes natürlich dann also für den Op-Code und vielleicht also irgendwie für eine Registeradresse oder für irgendwas anderes benötige,
also hier nur die Möglichkeit,
also entsprechend kleine Zahlen darzustellen.
Es sei denn, man hat dann eben mehr Wortbefehle,
wo ich dann eben auch in 32 mit dem Mediatern eben quasi ein zweites Wort tun kann.
Gut, je nach Architektur kann man dann eben sagen,
okay, bei X86 ist im Mediat dann also immer
zwei Wortbefehl
Bei anderen Architekturen gibt es dann immer irgendwie noch irgendwelche anderen Tricks herein,
wo man dann eben mit diesen kurzen Worten lebt und sagt,
okay,
das ist dann eben entweder die oberen oder die unteren 16-Bit
von irgendeinem Emediate-Wert oder was also gerade beim Arm-Prozessor dann immer ganz gut funktioniert,
dann nimmt man also quasi immer die unteren rechts im Bild und hat dann noch irgendwie so eine Art Versatzmodus,
wo ich das Ganze noch also schieben kann.
Das heißt also, ich kann dann nachher nicht alle Zahlen darstellen,
aber immer nochmal mal zwei Mal vier, mal acht und so weiter,
indem ich das dann also nochmal durch so ein Shifter durchschiebe.
Direkte Adressierung, wie gesagt, die Adresse des Operanten steht im Befehl.
Das heißt, ich hätte dann hier in meinem Operanten direkt
die Adresse. Hier jetzt eben als 32 Bit,
demzufolge dann, also in zwei Wortbefehl, nicht,
das ist dann eben die Adresse dieser Speicherzelle und die
soll dann eben beispielsweise hier in eurer Register geladen werden,
was dann also hier mit drin steht.
Indirekt Wie gesagt, dann nochmal mit dem zweiten Speicherzugriff.
Das heißt, hier habe ich jetzt einfach nur den Verweis auf
die Hauptspeicheradresse und dieses ist letztendlich sowas wie ein Pointer.
Das heißt, die Adresse der Speicherzelle, von der ich
dann wirklich lese, die dann dort laden wird.
Liest sich jetzt eigentlich erstmal vergleichsweise kompliziert. Wie gesagt,
also das ist hier eben als typische Cis-Adressierungsart eigentlich das,
wie also früher die Rechner entsprechend gearbeitet haben.
Hupercisk rechnet heutzutage, kommt sowas also überhaupt nicht mehr vor
Nicht sowas wie diese indizierten Modi, hatte ich ja gesagt.
Da hat man dann also quasi entweder sowas wie zwei Register,
die dann also quasi hadiert werden.
Und das ist dann eben letztendlich so was wie eine Speicheradresse.
Nicht damit kann man dann eben relativ einfach sagen,
okay, das ist also hier so eine Startadresse vom Array
und in dem zweiten Register rechne ich dann mit irgendwelchen Offsets.
Oder ich habe dann eben irgendwelche anderen Mechanismen,
die da also irgendwie arbeiten. Also zum Beispiel hier etwas,
was ich hier jetzt mal so Update-Adressierung nenne,
wo man dann eben sagt, okay,
ich habe dann sowas wie eine Basisadresse und eine zusätzliche Adresse.
Und dann gibt es noch den Mechanismus, dass nach dem
Speicherzugriff dann also hier quasi diese Adresse automatisch inkrementiert wird.
Also auch damit kann man dann zum Beispiel sehr einfach dann sowas machen,
wie eben so vor E-Life 1 oder E-Life 0 zu irgendwas
und dann eben quasi durch so ein ganzes Speichererrät durchlaufen,
was dann immer automatisch die Adressen mit draufzählt.
Also dafür ist sowas dann eben auch ganz nett.
Aber wie gesagt, das ist immer von der Prozessor-Architektur genau abhängig.
Was sie ja eigentlich auch immer schon sehen,
also diese Begriffe, ja was ist denn jetzt, sorry,
indizierte Adressierung sind da auch immer nicht so ganz einheitlich.
Deswegen, also jetzt keine Angst,
also wenn in Übungsaufgaben oder irgendwo sowas abgefragt oder benutzt werden sollte,
folgen nochmal entsprechende hämonische Beispiele, also sprich irgendwie sowas,
was eigentlich dann deutlich machen soll, was sich hinter diesem Adressierungsmodus verbirgt.
Das heißt, also müssen jetzt nicht zwingend wissen,
was jetzt also irgendwie sowas wie so eine Update-Adressierung ist
Gut, hier sieht man jetzt also für diese Maps-Architektur,
was ist dort so ein anderes Jungsarten gibt, also wie gesagt,
die Mediate, daran aber eben nur mit kleinen Werten,
Register-Adressierung, sowas für so eine Basisadressierung,
das heißt nur Register und ein kleiner Emediate-Wert.
Nicht dann gibt es sowas wie Adressierungsmodi, die relativ zum Programmzähler sind.
Gut, bislang hatten wir hier immer nur allgemeine Register.
Hier geht es jetzt auf einmal irgendwie um irgendwelche Sachen,
wo was mit dem Programmzähler passiert, wofür ist das sinnvoll? Ja, für Sprünge logischerweise. Wenn ich jetzt also sage,
ich implementiere jetzt also irgendwo eine Schleife in meinem Programm,
im SM-Blacode, heißt es ja dann eben auch,
dass ich also irgendwo die einzelnen Adressen durchlaufe,
irgendwann an eine Abfrage habe und dann abhängig von einem Ergebnis diese Abfrage dann eben sage,
okay,
jetzt soll entweder als Programmzähler die nächste Adresse genommen werden oder soll
eben hier oben hin zurückgesprungen werden und dann eben quasi der Schleifenrumpf nochmal durchlaufen werden nicht und dafür sind dann eben solche Adressierungsmodi ganz sinnvoll,
wo man dann eben relativ zum Programmzähler sagen kann,
okay,
jetzt springen wir irgendwie um 100 Befehle zurück und für die
dann eben nochmal aus
Gut, ja, das zu den, generell erstmal zu den Adressierungsdaten.
Ja, was ist da jetzt eigentlich alles so an Kombinationen möglich?
Also einmal die Anzahl der Adressen und dann auch noch bei den Adressierungsdaten,
also irgendwie ein relativ praktes Spektrum. Man muss ja eben sagen,
okay, je nachdem, bei welcher Rechnerarchitektur ich bin,
beziehungsweise bei welchem Konzept wird da eben eine entsprechende Kombination irgendwo ausgewählt,
nicht also bei sowas wie so eine Stackmaschine,
wie gesagt, teilweise vom Heilabbau beziehungsweise bei einigen speziellen Sprachen,
Bios-mäßig wird sowas also auch ganz gerne benutzt.
Java, die virtuelle Maschine ist im Prinzip,
also auch eine Steckmaschine, nicht bei kleinen Mikrocontrollern,
weil ich da so wenig Adressen habe,
arbeitet man dann ganz gerne mit solchen Akkumulatormaschinen, also einen Adressmaschinen.
Wenn ich entsprechend kleine Wortbreiten habe,
typischerweise dann eben noch irgendwie zwei Adressmaschinen,
wenn man jetzt 32 wird oder eben jetzt aktuell bei Prozessoren
64-Bit zur Verfügung hat,
dann sind das in der Regel
drei Adressmaschinen
Ja, und hier sieht man eben auch schon,
also X86-Befehlssatz umfasst dann also von einem Adress bis zwei und drei Adressenmaschinen.
Alles mögliche. Bei den Adressierungsarten ist es eben auch so,
Siskrechner, Darwin, also wirklich meistens sehr viele sehr unterschiedliche Adressierungsarten erlaubt.
Bei Riskrechnern arbeitet man also in der Regel wirklich nur mit diesem Grundprinzip Load and Store.
Ich habe ganz kleine Emediate-Werte und eben Adressierung eigentlich nur indiziert.
Das heißt also, Ich habe in einem Register die Adresse
aus dem Hauptspeicher, auf die dann zugegriffen wird.
Und man kann dann eben vielleicht noch mit irgendwie einem kleinen
Emidia dann eben als Offset da zu arbeiten. Gut,
bei der X-86-Architektur, die uns dann ja im nächsten Kapitel,
beziehungsweise jetzt ja also auch schon über Weihnachten in der einen Übungsaufgabe,
so ein bisschen begegnen wird,
wovon wird es also wirklich nur eine sehr kleine Ausschnitt,
also auch überhaupt benutzen werden.
Ich muss jetzt immer sagen, okay, was ist das?
Nicht, weil ich spreche immer munter davon, dass also
erst mal so grob gesagt die übliche Bezeichnung für Prozesse, Familie.
Also ich sage jetzt mal intel, weil die das also erfunden haben.
Ansonsten X86, kompatible Prozessoren, gibt es natürlich also auch
von AMD und das gibt es also auch noch von diversen anderen Firmen.
Also gerade so für eingebetteten Bereich gibt es da eben auch
noch andere Firmen, die also dort entsprechend kompatible Prozessoren machen.
Gut, ansonsten Intel-Prozessoren kann man eben sagen, okay,
also angefangen hat das dann eben in den Ende der 70er Jahre
mit den ersten 80, 86 Prozessoren.
Ich zog sich dann eben über so eine ganze Familie hin.
Zurzeit ist man also bei irgendwas. Rechnern,
zumindest wenn wir jetzt so sind, im Desktop-Bereich letztendlich bleibt.
Früher lief das Ganze dann eben auch noch unter dem Stichwort IAA 32,
Intel Architecture 32 Bit. Auch das ist inzwischen obsolet.
Also eigentlich alle aktuellen Prozessoren arbeiten mit 64 Bit.
64 Bit heißt in diesem Fall eigentlich erstmal nur Unterscheidung hinsichtlich des Adressraums.
Weil bei den Datentypen muss man eben sagen,
also auch IA 32 kann mit 64-Bit-Daten rechnen.
Also die Alu kann auch damit rechnen,
aber der Adressraum ist eben erstmal nur auf kleinere Sorgleiten beschränkt gewesen.
Gut, bei aktuellen Prozessoren mehr als vier Gibi-Byte-Speicher verlässt man
dann eben genau diesen Bereich, sind wir dann eigentlich da. Gut,
die Architektur oder das Ganze hatte ich also auch schon mehrfach gesagt,
das ist eigentlich CISC-Komplex, Astruction-Set-Computer, wie gesagt,
mit vielen historisch gewachsenen, ich muss immer sagen,
Altlasten, dann über die Jahre hinzugekommen, sehr,
sehr vielen Erweiterungen und wie gesagt, das Ganze dann auch immer so aufgebaut,
dass es dann eben letztendlich abwärtskompatibel ist.
Ab dem 80-3, 86 war es dann auch eben so,
dass das von den Befehlssätzen her bzw. von der Art und Weise
dann eben auch wie eine 8-Registermaschine verwendet werden konnte.
Mich also ursprünglich eben nur Zugriff auf den Hauptspeicher. Inzwischen Ja,
inzwischen ist gut, also der 80,
also 386 war also auch irgendwie Anfang der 90er Jahre,
nee, vorher,
muss man Mitte der 80er Jahre aktuell gewesen sein,
ist es eben so,
dass sie eben auch Prozessoregister gibt und dass man dann eben auch angefangen hat,
also den quasi mehr wie so eine Load-Store-Architektur zu benutzen.
Das heißt, Datentransfer vom Hauptspeicher in die Register, Alu-Befehler arbeiten auf den Registern,
Ergebnis aus dem Register wird wieder in der Hauptspeiche zurückgeschrieben.
Gut, sich hier nur nochmal der Hinweis,
also wenn ich jetzt hier irgendwas vorstelle, irgendwelche Beispiele,
irgendwelcher Assembler-Code, also von daher keine Angst,
also in der Klausur wird es jetzt nicht heißen,
also schreibe sie mal ein Programm in X-86 Assembler,
das das und das macht.
Also das wird bestimmt nicht vorkommen. Deswegen, da,
das wird auch nicht so weiter. Gut,
hier auch noch mal grob aufgelistet,
nicht so ein bisschen Entwicklung, wie gesagt,
erste Prozessor, wissen wir,
8.008 waren eigentlich der erste dann so richtig in Serie
gegangen ist
8 mit Mikroprozessor, 1972, nicht mit 100,
8 Kilohertz getaktet, 3500 Transisturen groß, 16 Kiwi-White-Speicher,
welchen dann zog sich das Ganze dann eben entsprechend
hin bis zu den aktuellen Prozessoren, wie gesagt,
die erste Generation Core-I-Prozessoren,
also 2008 rausgekommen,
die dann eben
schon entsprechend groß war
Deutlich schneller getaktet und, und, und, nicht,
wo man dann eben sieht,
was dann hier immer noch so ein bisschen als Zusatz hinzugekommen ist,
nicht, also 16 Bit, 32 Bit,
nicht dann mehr Pipelines Befehl setze oder Befehlssatzerweiterung,
also MMX-Fehler von Marty Media Extension,
ich dann ein paar Pension 4 wurden, also die Cyber-Threading-Konzept eingeführt,
auch das ist dann ja etwas, wo ich dann,
also vielleicht mal bei dem Pipelining dann nochmal drauf eingehen werde,
grob gesagt, kann man sagen, also wenn ich so ein Hardper-Streiting-Prozessor habe,
stellt er sich immer dar, als ob es,
also mehr Prozessoren sind, also beispielsweise, also bei den aktuellen,
nehmen wir jetzt irgendwie Corey7 oder sowas,
also habe ich jetzt beispielsweise dann eben vier Prozessoren,
wenn ich mir das anzeigen lasse, sagt er aber,
es sind acht, das heißt jeder Prozessor eigentlich mal zwei,
was dadurch erreicht wird,
dass in den Pipelining-Faden quasi teilweise die Register doppelt vorhanden sind
Und es gibt eben zwei Programm-Counter, es gibt zwei Instruktionsregister und es gibt also
bestimmte andere Sachen, also gerade die ganzen Pipeline-Registern immer doppelt.
Es gibt aber eigentlich dann so rumgesehen nur eine Alu oder eine Recheneinheit.
Was aber passiert, ist, dass quasi dadurch,
dass diese Registersätze doppelt sind, also sehr schnell im Kontextwechsel passieren kann.
Weil das, was ja in der Regel passiert,
wenn ich jetzt aus irgendeinem Programm,
irgendein Thread auf dem Prozessor am Laufen habe, ist,
dass durch Interrab externes Ereignis ein oder ich möchte also irgendwo was vom Hauptspeicher oder irgendwie aus dem Speicher lesen oder schreiben,
war es jetzt aber nicht direkt im Cash zugreifbar ist.
Ja, dann brauche ich auch mal wieder tausend Takte.
Dann kann der Prozessor natürlich anhalten, das,
was aber eigentlich rein praktisch macht,
dass er dann sagt, na gut,
also während dieser tausend Takte oder wie viele ist er auch da noch immer sein mögen,
lasse ich einen anderen rechenwilligen Prozess weiterlaufen.
So, und was man sonst ja machen müsste,
ist also wirklich sämtliche Register,
Programmzähler und alles
irgendwo sichern
Für den anderen Thread, der arbeiten soll, das Ganze quasi in den Prozessor reinladen, dann kann der weiterlaufen.
Das ist also etwas, was das Betriebssystem ja nun logischerweise für einen macht. Und bei diesem Hyper-Streading,
beziehungsweise man das allgemein nimmt, nennt sich das Simultaneyous Marty-Sreading,
was dort passiert, ist,
dass dann eben quasi jetzt nicht mehr umständlich dieses Wegspeichern,
zurück und so weiter passieren muss,
sondern dadurch,
dass man einen zweiten Registersatz hat,
wird einfach nur irgendwann an einer Stelle ein Bit gekippt und ich arbeite da nicht mehr mit dem Registersatz,
sondern mit dem anderen und habe dann quasi innerhalb eines Taktes
im Kontextwechsel gemacht
Und nach außen hin für das Betriebssystem stellt sich das dann eben so dar,
als ob der Prozessor quasi, oder nein,
also als ob ich eben doppelt so viele Rechenkerne habe,
rein praktisch ist es aber eben nicht, sondern so,
das ist eben das, was Hype Buff Redding ist,
erstmal grob und schnell erklärt.
Wie gesagt, wenn wir dann am Ende noch dahin kommen, dann zu Pipelining,
werde ich das dann also auch nochmal kurz mit irgendwelchen Grafiken belegen können.
Gut, ja, dann ab 2007, 64 Bit-Architekturen und so weiter und so fort.
Wie gesagt, da kamen also relativ fiel immer noch zusätzlich hinzu. Also wie gesagt,
inzwischen sind wir dann also auch schon hiervon entfernt, sondern also über halb,
also irgendwie so bei 4,2 Gigahertz und zumindest jetzt bei den Desktop-Prozessoren,
also inzwischen auch schon bei sechs oder acht physikalischen Kern und bei den Server-Prozessoren sind das also eh nochmal deutlich mehr.
Gut, hier auch nur so ein paar Daten zusammengetragen,
das ist also i7,960, das ist also die erste CoI-Generation,
wo man dann eben auch hier nur irgendwie so ein paar technische Daten hat,
also das heißt Dark-Frequenz, vier Kerne eben mit diesem Haber-Stridding quasi mal zwei genommen.
Dann habe ich eben so eine typisch, so eine Speicher-Jerarchie,
dass man eben sagt, ich habe First-Level-Cash, der ist dann auch immer oder talt in Instruktions- und Daten. Cash,
das sind also dann quasi immer diese schnellen Speicher, das,
womit der Prozess jetzt wirklich aktuell nur rechnet und dort dann eben auch anders als jetzt beim Allgemeinen von Neumann-Konzept unterteilt nach Instruktion und nach da,
das ist eben auch das,
was Da nämlich also auch so ein bisschen, also hier darauf abzielt,
hier habe ich also auch immer diese Trennung, Instruktionsspeicher und Datenspeicher,
weil ohne diese Trennung kann ein Pipelining zum Beispiel auch wieder gar nicht funktionieren.
Gut, dann das dann eben getrennt für jeden Einzelnen. Rechenkern. Nicht,
dann habe ich ein Second Level Cash,
das ist dann eben ein gemeinsamer Cash für Instruktion und Daten,
aber auch noch getrennt für jeden Rechenkern und dann eben noch
einen gemeinsamen Cash für alle Rechenkerne und dann geht es erst
irgendwie hier raus
Richtung Hauptspeicher. Gut, Speichermodell beim X-86-Prozessor, Little-Indien-Maschine, Little-Indien hatte ich ja nun gerade eingeführt und erklärt nicht.
Also da geht es eben darum, dass das Least Significant Barth die kleinste Adresse hat. Das heißt,
wenn ich jetzt also hier so ein IA32 als Beispiel nehme,
ich also 32 Bit, Speichermodell, dann habe ich eben mit der Adresse null,
ich bald null ein, mit der Adresse 1,
bald 1, bei 2, bei 3,
dann irgendwie von so einem 32-Bit-Wort, mich in das nächste Wort,
wie er dann eben spare ich Adresse 4, 8, 12 und so weiter.
Der Speicher ist voll weit adressierbar, das heißt,
also hier, da sollen jetzt 0,1, 2,
3 und so weiter, Haar steht dann immer für Hex,
sollen dann immer jeweils wirklich Speicheradressen sein, und hier mit diesem Bereich,
beziehungsweise mit den entsprechenden Strings, dazu würde man dann eben sehen,
was man quasi ausliest, wenn ich jetzt sage,
also an Adresse sechs möchte ich ein Quadwort lesen,
nicht dann lese ich eben in diesem Fall an den niederwärtigsten Bars 0b,
dann zwei, drei, A, 4,
1,
F und so weiter und dem sofort gebaut sich dann,
also hier dieses ganze Wort dann daraus zusammen.
Gut, dieses Ganze hier ist als Beispiel dann also auch
direkt aus dem Intel 64 und IA 32 Architecture Software Developers Manual
Was man sich also dann direkt bei Intel also herunterladen kann,
beziehungsweise ich weiß gar nicht,
also früher hatte ich das auch immer noch mal als Lokale Kopie.
Also bei uns auf dem Server ansonsten, die aktuelle Version ist dort.
Also wenn man wirklich mal in die Verlegenheit kommt und zwangsweise,
auch wenn man das vielleicht nicht möchte, Assembler programmieren muss,
das wäre dann also immer die entsprechende Referenz,
ist dann also irgendwie so 1.200 Seiten PDF.
Und da kann man dann wirklich alles nachlesen,
wie welcher Befehl im X86-Befehlsatz wirkt,
was sich dort alles tut und,
und,
Wie gesagt, das wäre dann Referenz, also gut,
also ob man das mal freiwillig macht, na gut.
Ja, dann hier Register, das,
was wir jetzt also auch gerade in den Beispielen dann immer kennenlernen werden,
ist also quasi IA32, also es stimmt nicht mehr so ganz.
Das heißt also, da arbeiten wir jetzt eigentlich mit,
zumindest in diesen Beispielen jetzt,
also aus dem Brian und du Hellerinnen immer mit diesen Registern,
also EAX,
ECX,
EDX,
EBX und so weiter
Das sind also dann quasi immer die von Intel offiziell vergebenen Namen für diese Register,
in diesem Fall dann also 32 Bit-Register,
was man hier dann auch noch sieht, ist,
es gibt dann also Quirmasi immer noch sowas wie A,
X, C, X, D, X,
das E steht dann nämlich für Extended,
das ist dann quasi die Abwärtskompatibilität von dem Ganzen,
wo es dann nämlich also nur um 16 Bit-Breiteregister geht und wenn sie also jetzt die Übungsaufgabe 10,
3 quasi nicht ich mit dem 32-Spitz,
sondern mit dem 64-Bit-Compiler übersetzen und dann in den Sambler-Code reingucken,
dann wird dort also nichts mit EAX oder also hier den entsprechenden,
ja, so der Stackpoint, da wird dann,
glaube ich,
nur explizit mit benutzt.
ESP auftauchen, sondern wird das RSP heißen, das ist
dann nämlich das entsprechende 64-Bit-Register nicht. Das heißt also,
bei dem aktuellen ist das Ganze dann eben nochmal hier quasi
erweitert und davon gibt es dann eben entsprechend die 64-Bit-Varianten.
Gut, andererseits, da wird jetzt also hier nicht Hochgradig
performanten Assembler-Code machen oder lernen wollen. Wie gesagt,
bleibe ich also hier im Ankehe ich immer bei dem,
was in diesem Buch dann also entsprechend mit diesen etwas eingeschränkten Beispielen hier drin ist.
Gut, also das heißt, hier habe ich jetzt also meine Intel-Universal-Register,
wie man die benutzt, was man damit macht,
wird dann eigentlich nur genauer im nächsten Kapitel dann eben entsprechend folgen.
Nicht dann gibt es den EIP, steht dafür Instruction-Pointer,
das ist der Programmzähler und es gibt dann eben noch einen expliziten Flag-Register,
da stehen dann eben Statusinformationen mit drin.
Und das, was wir jetzt ja sich hier alles weiter nicht benutzen werden,
dann also 80 Bittbreite, Fließkomma-Register für die Fließkomma-Befehle.
Guten Fleckregister, ist im Prinzip dann auch ein 32 oder
dann nehmen wir jetzt April 64 Bittbreites Register,
wo dann quasi einfach jetzt nur in einzelnen Bits irgendwelche Statusinformationen drin stehen.
Also was hier für uns vielleicht noch interessant sein kann,
ist jetzt, wenn ich sowas wie arathmetische Operationen mache,
Science Zero Perity, Carrie, also einfach um jetzt
Zum Beispiel sowas wie bei Vergleichen, das Ergebnis abzufragen,
würde dann eben quasi im Vergleich dadurch passieren,
dass ich dann eben sage, statt A,
größer B wird dann eben quasi gerechnet, A,
minus B und wenn das Ergebnis negativ ist,
dann ist eben in diesem Fall dann also A,
kleiner Gleich B, beziehungsweise ein negutes Beispiel,
müsst ihr jetzt also einmal größer Gleich fragen,
wenn das Ergebnis negativ ist,
dann wäre es kleiner.
Nicht ansonsten bräuchte ich dann eben auch noch das Zero-Flag und das einschläg.
Und damit kann man dann eben diese ganzen Vergleichsoperatoren
dann eben letztendlich dann immer hier auf sowas mit abbilden.
Nicht, dass es also der hat das, wofür man das noch braucht.
Gut, ansonsten steht halt eben noch so diverse Sachen,
die dann also beispielsweise eben vom Betriebssystem jetzt für Prozesskontrolle benutzt werden.
Also Privilege-Level und, und, und. Also verschiedene andere Sachen, die da dann eben eigentlich auch noch überall so mit reinspielen. Gut,
X86 Datentypen, also auch hier sieht man beliebig breit gefächert von einem einzelnen Byte über ein Word,
was dann 16-Bit sind, Double Word, 32-Bit,
Quadwörtern eben, 64-Bit, ne, Enteger, irgendwelche Aufzählungstypen,
dann gibt es ja so einen eigenen BCD-Typ,
wenn ich dann also irgendwie was wie Pointer-Typen, 32-Bit,
beziehungsweise dann zu größerer Adressbereiche dann erweitert,
dann auf 48-Bit,
mich dann also hier irgendwelche Bitstrinks und eben noch diverse Fließkomma-Formate
All das, was es da so gibt, wie gesagt,
Referenz, dieses IA-32 beziehungsweise IA-64, Reference Venue,
da ist es dann wirklich im Einzelnen genau beschrieben,
brauchen wir jetzt hier irgendwie nicht weiter.
Gut, als Befehlssatz, wie gesagt,
also das ist jetzt hier bei weitem nicht der vollständige Befehlssatz,
sondern nur so ein ganz kleiner Ausschnitt nicht.
Also ich habe im Prinzip sowas wie so einen einfachen Datenzugriff,
also Move,
wo ich dann also einfach quasi jetzt zwei den Inhalt eines
Vergissers in ein anderes Register schieben kann oder Exchange,
wo ich also
quasi zwei Registerinhalte vertauschen kann
Nicht, ja, aber irgendwie war es, um also
auf dem Stack zu arbeiten, also Push und Pop.
Nicht, dann gibt es was, um Typen zu konvertieren, dann die arathmetischen Befehle. Nicht,
dann gibt es noch spezielle Befehle für so eine BCD-Arathmetik,
die war allerdings auch nicht benutzen,
was also auch eher historisch gewachsen ist, dann logische Operationen,
Sprünge, Befehle für String, nicht und dann eben noch so Sachen,
um dann eben noch Prozesskontrolle und, und, und zu machen.
Also auch das ist nur ein ganz,
ganz kleiner Ausschnitt, nicht also aktueller Befehlsatz,
ich weiß Hätte ich mal reingucken müssen in das besagte Manual,
wie viele Befehle es da insgesamt gibt,
aber ich hätte jetzt also gesagt, also irgendwie so geschätzt müssen,
dass das irgendwie deutlich über 100 sein und hinzu kommen dann auch diverse Varianten an Adressierungsarten.
Gut, wie gesagt, das Ganze ursprünglich sogenannter Assisk-Befehlssatz. Orstert sich dann eben dadurch,
dass ich also jetzt nicht nur sowas wie Obcode habe,
sondern eben noch irgendwelche Modifier, mit denen man sagen kann,
arbeite ich jetzt auf irgendwelchen Standardregistern oder arbeite ich also da noch auf einem anderen Register.
Also da kann man teilweise dort also auch noch irgendwelche Sachen mit aus umschalten. Nicht,
dann hat man teilweise noch irgendwie so die Möglichkeit,
dann eben zu sagen, ich möchte jetzt eben noch mit so einer Offset-Adressierung arbeiten,
dann gibt es ein explizites Basisregister und, und, und.
Letztendlich kann man sagen, in der Kombination,
also aus Befehlen und den Adressierungsarten kommt man dann insgesamt also auf
Ich denke mal so deutlich über 400, 500 Befehle,
die da also letztendlich möglich sind. Wovon, wie gesagt,
also inzwischen eigentlich nur noch ein ganz, ganz kleiner Teil benutzt wird.
Führt aber eben dazu, also das ist eben das,
weswegen es dann heißt komplexes Struction Set Computer,
eben dieses vergleichsweise komplexe Befehlsformat und in der Abarbeitung.
Das heißt, wenn ich sage, okay, Befehl,
holen, Befehl interpretieren,
ist das dann eben
auch dort vergleichsweise aufwendig
Gut, hier nochmal Beispiele eben für solche Modifier.
Wie gesagt, also wollen wir jetzt die Ansicht gar nicht
weiter darauf eingehen. Hier Beispiele für einige einfache Befehle,
also hier aus dem Buffon Peterson und Tennessee,
also so ein Jump Equal, also ein bedingter Sprung,
wo ich also abhängig von einer Bedingung irgendwo hinspringe und dann irgendwie also auch noch irgendwelche Offsets mit angeben kann,
unter Programmaufruf, Schiebebefehl,
wo ich Push quasi den Inhalt eines Registers auf den Stack schreibe.
Und wie gesagt, hier sieht man eben auch,
also dieses ist ein Befehl, Einbeit lang.
Hier habe ich jetzt also auch beim Call,
also ein Befehl, der ist dann also irgendwie 40-Bit breit.
Also da sieht man eben, das ist wirklich sehr komplexes
und ein sehr irreguläres Befehlsformat ist. Also von Einbad,
zwei, byte, hier dann also irgendwie fünf Bad,
also 40-Bit matisch,
fünf Bad groß und so weiter und so fort
Gut, hier, ja gut, das sind eigentlich auch nochmal
so diese Befehle dann eben in so einer mämonischen Schreibweise.
Auch das wird uns dann im nächsten Kapitel dann nochmal ein bisschen genauer begegnen.
Also gerade so in Bezug auf irgendwelche Speicherzugriffe verwendet man dann immer ganz gerne.
Also im Prinzip so eine Notation. Also zum Beispiel,
wenn ich hier sowas wie diesen Unterprogrammaufruf nehme, also das Call,
dann ist also quasi hier als Funktion mit dieser mämonischen Streitweise notiert,
was sich dahinter eigentlich verbirgt,
nämlich dass dieser Befehl Zum einen den Stackpointer verändert.
Das heißt, er wird um vier dekrementiert.
Das heißt eigentlich genau eine Speicheradresse weitergerechnet. Nicht dann wird
in den Speicher der Wert des Instruktionszählers plus 5 an die
Adresse von dem Stackpointer gespeichert.
Wofür braucht man das?
Das ist letztendlich genau die Rücksprungadresse
Nämlich, dass ich einfach sage, okay,
also wenn ich ein Unterprogramm aufrufe,
möchte ich nach Beendigung des Unterprogramms ja in meinem Hauptprogramm ab dieser Adresse,
wo ich also quasi den Call gemacht habe,
beziehungsweise genau einen weiter, eigentlich weiterarbeiten.
Und das ist eben genau dieser Teil hier,
wo quasi aus dem Instruction Pointer plus 5 eben das nächste Byte adressiert wird.
Das wird dann eben hier in den Speicher geschrieben, anstelle des Stackpointers.
Und beim Rücksprung, also bei dem Return,
würde da nämlich entsprechend der EIP der Instruction Pointer dann also
quasi mit diesem gesicherten Wert hier dann wieder geladen werden.
Und in dem Fall beim Call passiert dann eben,
dass der Instruction-Pointer dann eben jetzt, also mit diesem Wert
geladen wird und ich dann eben quasi jetzt genau in dieses Unterprogramm reinspringe.
Gut, also auch dazu werden wir dann im nächsten Kapitel
dann noch einiges an Beispielen sehen.
Ich habe das nur so als, mal kurzes Beispiel,
was sich dann eigentlich hinter dieser komischen hämonischen Schreibweise verwirbt. Gut,
ansonsten X86 Assembler, wenn man sich dann sowas anguckt,
also irgendwie so ein Code, also hier dann das allgemein beliebte Beispiel HelloWorld,
beziehungsweise in diesem Fall Hello X86, nicht einmal übersetzt und dann disassembliert.
Dann habe ich hier eigentlich mein Hauptprogramm,
also das wäre jetzt quasi das Äquivalent zum CQL-Code,
wo ich dann also sage, main,
ich habe hier irgendwo meinen Print und einen Return,
nicht in das, was sich dann hier tut,
ist, dass ich hier im Prinzip dann,
also ein Unterprogrammaufruf mache und dort dann eben quasi diese Printfunktion anstoße und die macht dann eben nichts anderes,
als dass sie dann also hier quasi in so einer Schleife dann die einzelnen Zeichen vorausgibt.
Und wie gesagt, das wäre dann also der entsprechende SM-Lakot dazu.
Keine Angst, auch das wird jetzt nicht kommen,
dass wir hier also jetzt hier irgendwo welche Formen C-Compiler
generierten Assembler Listings also irgendwo hinlege und dann gefragt wird,
was macht das denn genau?
Zumindest also jetzt nicht in dieser Form, wie das hier steht.
Es wird zwar sowas in der Richtung kommen,
aber wie gesagt,
da werden wir vorher dann deutlich mehr an Beispielen nehmen und
es würde dann also auch wirklich nur ein sehr kleiner,
sehr eingeschränkter Befehlssatz sein
Und zumindest das,
was dann also dort vielleicht noch irgendwo jetzt von ihnen irgendwie an Transferleistung erwartet wird,
ist dann also, sagen wir jetzt nicht mehr so,
dass man jetzt sagen muss, okay,
also ich habe jetzt also irgendeinen beliebig komplizierten Algorithmus,
dann hast du dann eigentlich immer sehr kleine,
ganz minimale Beispiele, wo man dann einfach nur mal für,
also hier irgendwie so einen Code von irgendwie 20 Zahlen eigentlich sieht oder sagen soll,
was tut sich da.
Aber wie gesagt,
das wird im nächsten Kapitel eigentlich genauer beleuchtet,
dann also auch mit
entsprechend vielen Beispielen
nochmal hinterlegt
Wenn ich das heißt, also zumindest sowas von dieser Komplexität, wird nicht zwingend abgefragt werden.
Gut, ja, das schon mal so als kleiner Vorgeschmack,
Androhung oder wie auch immer man das verstehen mag, zumindest X86-Befehlsatz.
Ja, was ist jetzt Instruction-Set Architecture-Bewertung? Hier nun mal aufgelistet,
das, was wir sagen wir mal so als Kriterien nehmen kann,
um zu sagen, was ist ein vernünftiger Befehlssatz, vollständig,
vollständig in dem Sinne, dass man eben sagt, okay,
also alles, was ich für ein Programm Programm brauche,
gibt es als Befehle, orthogonal.
Damit ist er einfach nur gemeint, dass ich jetzt also nicht irgendwie fünf Befehle habe,
die alle genau das Gleiche machen oder rechnen,
auch wenn das teilweise im X86-Befehlsatz so der Fall ist.
Dann hat man teilweise sowas wie Symmetrie, also das ist eben Addition,
aber eben auch eine Subvertraktion gibt, adäquat, das ist eben dieser technische Aufwand entsprechend zum Nutzen.
Also das zieht so ein bisschen auf das,
was ich also in dem Kapitel zu den Schaltnetzen oder Schaltnetzen zum Beispiel schon mal angedeutet habe,
dass ich gesagt habe,
okay, also theoretisch kann ich um die Quadratwurzel auszurechnen,
also mir irgendwie so eine Hardware-Einheit überlegen,
also jetzt aus irgendwelchen Cary safe,
Adieran und das Ganze so und so angeordnet,
die also wirklich jetzt die Quadrathostel ausreichen.
In Hardware. Frage ist jetzt eben, wie oft brauche ich das,
wenn ich jetzt also irgendeinen x-beliebigen Algorithmus schreibe? Ja,
und wenn das jetzt nicht irgendein Spezialprozessor ist, der,
was weiß ich, jetzt im Wechselstromnetz, also irgendwie Leistungsmessung macht,
wo man das vielleicht zufällig mal brauchen sollte,
lohnt es sich nicht dafür.
Also im Prinzip so und so viel Prozent,
aber Prozessorfläche zu opfern, um da, also diese Hardware-Einheit zu bauen.
Das ist also das, was dieses Addäquat eigentlich heißt,
also der technische Aufwand entsprechen dem Nutzen
Gut, Effizienz ist das, was wir haben wollen. So,
wenn man sich jetzt die Befehlsätze ansieht und dort quasi sich einfach mal so ein Standard-Benchmark nimmt,
also da gibt es ja so verschiedene,
die dann also von irgendwelchen automatischen Operationen,
Simulationen bis zu irgendwie so Benchmarks,
wo man jetzt also mit klassischen Büroanwendungen oder irgendwelchen Anwendungsszenarien arbeitet und dann mal guckt,
welcher, welche Befehle werden davon jetzt eigentlich benutzt?
Nicht, dass man da dann eben sieht,
beim X86-Prozessor 22 Prozent sind Lotbefehle, 20 Prozent bedingte Sprünge,
16 Prozent Vergleiche, Store ist dann quasi das Gegenteil vom Lot,
nämlich Register, in Speicher zurückschreiben, Addition,
Bedvises und Subtraktion schieben zwischen Registern, Call- und Return.
So, das heißt,
die zehn häufigsten Befehle von diesem geschätzt jetzt irgendwie über 100,
wenn ich jetzt erstmal die Adressierungsart noch weglasse.
Damit kann ich
96 Prozent des Codes ausdrücken
So, und dann habe ich noch irgendwie 90 Befehle,
die dann also vielleicht die anderen vier Prozent abdecken.
Und das zeigt eigentlich das, was also hier jetzt dahinter steht,
Dominanz da einfach eine Instruktion,
weil das sind eigentlich wirklich sehr einfach, ganz primitive Befehle.
Und das ist auch das, was wir jetzt also im
nächsten Kapitel dann mit der Sambler-Programmierung dann überhaupt noch kennenlernen werden.
Also da werde ich jetzt also keine Multimedia-Vektorbefehle vorstellen,
sondern wirklich nur diese ganz einfachen, primitiven Sachen.
So, hier ist das Ganze jetzt also nochmal ausführlich,
nämlich wirklich für so eine Spec, Indiger Suite,
also im Prinzip ein klassischer Benchmark, gemacht, welche Befehle,
wie oft wo benutzt wurden.
Das sind dann also hier die einzelnen Benchmarkteile davon und also auch da,
also das, was ich hier eben quasi aufliegen hatte,
war dann nämlich also quasi dieses Ergebnis,
also diese Hit-Everage von den Szenen häufigsten.
Nicht, wo man dann eben wirklich sieht,
welche Befehle hier wie oft benutzt wurden. Gut,
in diesem Fall war das ein Indiger-Benchmark,
es werden also hier die bei den Fließkomma-Befehlen taucht da überall eine Null auf,
nicht, das Ganze gibt es natürlich dann also auch nochmal irgendwie für Fließkomma-Sachen.
Ja, man sieht eben wirklich,
also nur wenige
ganz einfache Befehle
So, das Ganze kann ich auch für andere Prozessorarchitekturen machen.
Also hier ist es jetzt mal nicht für X86,
sondern für einen Mips-Prozessor gemacht. Und auch da sieht man,
dass ich Ladebefehl, 37 Prozent der Befehle,
so bittweise Verknüpfung von Registern, 16 Prozent, Addition,
Subtraktion, bedingte Sprünge und so weiter.
Und gut, auch dieses hier, also diese farbigen Darstellungen,
das sind eben Teile, jetzt irgendwie dieses Benchmarks.
Also auch da sieht man, dass man im Prinzip für
die allermeisten Berechnungen in diesem Programm, beziehungsweise in diesem Benchmark,
da wirklich nur sehr, sehr wenige Instruktionen braucht.
Das, was man auch typischerweise an Instruktionen braucht,
sind eben nur Load und Store und irgendwie so ganz einfache All-Operationen.
Genau das ist dann eigentlich die Motivation,
die dann also dazu geführt hat, dass man sagt,
naja gut, ich habe zwar irgendwie diese Riesenbefehlsätze in den althergebrachten X86 Architekturen mit drin,
aber eigentlich brauche ich davon gar nichts.
Also warum baue ich nicht Rechner mit kleineren, kompakteren Befehlsätzen,
die eben nur genau diese Konzepte auch realisieren.
Und das ist eben das, woraus dann also letztendlich in
den 90er Jahren dieser Risk die Risk rechnet, beziehungsweise Reskarchitekturen herausgekommen sind.
Gut, jetzt hier nur noch mal zur Veranschaulichung.
Also jetzt oder für Fortstellung, wie es also von den Begrifflichkeiten,
also wie gesagt, Sisk, Komplex Instruction, Set Computer.
Ja gut, das sind eben also alle mit vergleichsweise irregulären
Komplexen Befehlssatz. Das, wo oder die Art und Weise
, wie eigentlich jetzt Rechner ursprünglich mal entwickelt worden sind.
Nicht, das heißt also in den 50er, 60er Jahren,
als die rechten Architekturen aufkamen, das waren eigentlich alles Cisk-Rechner.
Warum? Ja, und da muss man jetzt eben sagen,
okay, also damals gab es keine Hochsprachen.
Da gab es auch noch nicht diese hochelaborierten Compiler, die wir heute kennen und benutzen, sondern da hat man also im Prinzip so einen Rechner mit Assembler programmiert. So,
und wenn ich da einen Assembler-Befehl habe, wo ich sagen kann,
greif von dieser Adresse mit dem Offset darauf zu und hin addiere das zu irgendwas anderem und hinterher inkrementiere noch irgendwie diese Basisadresse und da kann man damit zwei befehlen,
also quasi jetzt irgendwie sowas wie eine Matrix-Multiplikation machen,
wo ich also ganze Race durchlaufe, ist das natürlich ganz,
ganz schön, dass man da dann eben entsprechend komplexe Assembler-Befehle hat,
führt aber eben auch dazu,
dass sich diese unglaubliche Variation an Adressierungsmechanismen habe mit Autoinkrement und nicht Autoinkrement und Basis und was was es da alles gab,
was eben letztendlich dadurch motiviert ist, dass man da eben wirklich, also auch für Sembler-Ebene programmiert hat und dann eben quasi die Komplexität des Programms eben entsprechend damit erschlagen konnte.
Typisch dem zuvor, also ich habe sehr viele Befehle,
völlig irreguläre Instruktionsformate, nicht von einem Wort bis zu fünf,
sechs, sieben, acht Wortbefehlen, mit entsprechend komplexen Befehlsdekodierungen,
mit vielen Schreib und Lese zu treffen, Ruhrbefehl und eben auch mit sehr unterschiedlichen Datentypen.
Gut, Adressierungsarten hatte ich gesagt, nicht,
dann gab es irgendwie so Unterprogrammaufrufe, die erfolgten dann typischerweise über ein Stack,
dass wir dann also beim X86 Assembler dann eben auch noch kennenlernen,
wie das Ganze funktioniert,
dass Unterprogramme dort dann eben quasi über so einen Stack-Mechanismus funktionieren,
also hier in diesem Call-Beispiel war das ja auch schon grob angedeutet,
dass da also irgendwie sowas wie so ein Stack-Pointer gibt.
Und es gibt dann eben typischerweise sowas wie Zustandscodes,
das,
was also
dieses Flag-Register ist
Ja, was sind Vor- und Nachteile?
Vorteil ist mit diesen sehr komplexen Befehlen, kann ich natürlich,
also auch entsprechend schon direkt mein Programm kodieren. Das heißt,
ich bin eigentlich direkt nah an der Programmiersprache und man hat
vergleichsweise kompakten Code, weil ich brauche eigentlich nur wenig Befehle,
um irgendwie was zu machen.
Nachteil, der Compiler muss also quasi jetzt,
also diese ganze Variation von Befehlen irgendwo wie abfrühstücken können
Kann jetzt hinsichtlich der Ausführungszeit nicht sagen,
also ich kriege einen Befehl in ein oder zwei oder sonst so viele Takten abgearbeitet,
sondern es gibt eben Befehle, die brauchen nur wenige Takte,
es gibt Befehle, die brauchen, also was weiß ich,
mehrere hunderte Maschinen takte, bevor dieser eine einzige Befehl abgearbeitet ist.
Dadurch, dass ich dann mehr Wortbefehle habe, ist natürlich,
hat man eigentlich so eine Art variables Instruktionsformat,
das heißt also ich lese immer erst das erste Wort,
meines Befehls und muss dann eben entscheiden,
ist das ein zwei oder drei oder vier Wortbefehl und dann
eben nochmal wieder teile in weitere Instruktionsregister nachholen.
Also das heißt, das ist dann eben auch entsprechend aufwendig.
Führt dann in der Handhabung dann auch dazu,
dass also im Prinzip sowas wie Pipelining dort eigentlich gar nicht richtig funktioniert.
Beziehungsweise gut, so wie es jetzt funktioniert,
in X86 muss man jetzt eben sagen, geht das eigentlich auch nur deshalb,
weil das Im Prinzip wie Prozessor-Architektur, und okay, das fehlt,
also im Zweifelsfall dann also hier auf dieser Folien noch mit drauf.
Dass die Prozessor-Architektur hier im Prinzip da nämlich also auch so umgebaut wurde,
dass intern dann im Prinzip dann also was wie Pipelining hinzugekommen ist,
was aber eigentlich dazu führt,
dass eigentlich ein ursprünglicher X-86-Befehl intern übersetzt wird,
eigentlich in kleinere,
riskartige Befehle,
und dann über so eine Art Mikroprogrammierung
abgearbeitet wird
Gut, wie gesagt, Beispiele X86,
68.000 Deckbacks und eben die ganzen Rechner von Arnold Lunemas,
also von deutlich vor 2000, da hat man sowas.
Gut, typischerweise, habe ich auch schon gesagt,
also dann Mikro programmiert, das heißt also einen Befehl braucht
jetzt eben nicht nur einen, sondern mehrere Takte.
In der Regel wird das eben entweder durch so eine Art
endliche Automaten abgearbeitet oder wie das dann ja also beispielsweise im Rechnerstrukturenpraktikum
bei dem Dekor ist, dann also über so ein Mikroprogrammierung.
Und da kann man dann eben unterscheiden, in dem allgemeinen Fall,
also das, was ich dann also auch in dem B-Code drin ist, nennt sich dann horizontale Mikroprogrammierung, sieht so aus. Ich habe hier eben quasi einen Mikroquarm-Zähler,
habe dann also hier als Teil dieses Speichers,
also da würde dann also quasi immer die Adresse dekodiert,
hätte dann also hier quasi von diesem Mikroprogramm-Wort dann eben ein Teil,
der mir sagt, welche Folgeadresse wird genommen,
um dann eben quasi jetzt über mehrere Aufeinanderfolgende Takte erstmal diesen einen Befehl abzuarbeiten und hätte dann also hier hinten dann quasi so einen relativ langen Kontrollvektor,
wo dann eben jetzt wirklich für jede Steuerleitung,
also sprich jeden Multiplexer Eingang und für die Alu,
wo dann also meinetwegen drei oder vier oder fünf Bit benötigt werden,
um da Alu zu sagen,
was dort passiert werden soll,
dann eben letztendlich aber jetzt für jede Einheit in der Hardware
dann eben hier drin die entsprechenden Steuerleitungen drinstehen
Führt natürlich dazu, dass diese Speicher relativ groß sind.
Deswegen gibt es da dann eben noch die Idee,
das Ganze als vertikale Mikroprogrammierung zu machen,
wo dann also im Prinzip nur geguckt wird,
wie viele unterschiedliche Belegungen oder Kombinationen habe ich und dann hat
man dann letztendlich sowas wie so eine mehrstufige Dekodierung.
Das ist also die Art und Weise, war es dann
also im Prinzip bei dieser Mikroprogrammierung dort letztendlich passiert.
Gut, bei den CIS-Befehlsätzen kann man dann eben letztendlich
diese Komplexität dieser Befehle dann eben in das Mikroprogramm auslagern
Nicht, wenn ich sage, ich habe einen Befehl,
der soll das und das und das und das alles machen.
Dann wird er eben letztendlich in 30,
40 Mikroprogrammschritten abgearbeitet, die dann eben der Reihe nachmachen.
Ich inkrementiere den Zähler, ich mache das, ich mache das.
Und erst wenn das alles fertig ist, dann kann der
nächste Befehl angefangen werden. Gut, das zur Historie,
das, was wir heute typischerweise haben, sind eigentlich risk.
Reduced Instruction, Set Computerrechner. Wie gesagt, ursprünglich in
den Anfang der 80er Jahre von den Herrn Hennessey und Patterson mitentwickelt worden.
Und wie gesagt, da ist eben die Idee,
ich benutze wirklich nur diese ganz einfachen Befehle,
habe klassisch so eine Load-Store-Architektur, das heißt,
Datentransfer zum Hauptspeicher geht nur auf Register und zurück und alles,
was Alu oder eben sonstige Berechnung ist,
passiert eben nur auf Registern.
Und gut, wie kriegt man damit dann komplexe Programme hin?
Ja, ich habe Hochsprachen,
ich habe einen vernünftigen optimierenden Compiler und der übersetzt mir
das dann eben einfach eine lange Folge von Assembler befehlen
Und in der Regel möchte ich mir den Assembler-Code,
der also irgendwo aus dem Compiler rausguckt, sowieso nicht ansehen.
Nicht, dass es dann eben letztendlich das, was,
sag mal, inzwischen heutzutage auch die Art und Weise ist,
wie wir ja dann eigentlich auch programmieren oder mit den Rechnern letztendlich arbeiten.
Wie gesagt, die ersten Rechner,
wo das aufgekommen ist, also im Prinzip diese MIPS-Architekturen,
Spark-Rechner und gerade im eingebetteten Bereich inzwischen eigentlich der de facto Standard,
Arm-Architekturen, wobei es da dann auch inzwischen ist,
also auch schon ganze Vielzahl gibt.
Merkmale von dem Ganzen, wie gesagt,
ich habe Nur weniger einfache Instruktionen, also maximal 128.
Also ich hätte jetzt gesagt, das sind eigentlich eher noch weniger.
Führt natürlich dazu, dass ich für ein Programm deutlich mehr Instruktion brauche. Andererseits,
damit der Compiler das also aus meinem C-Code oder was war dann eigentlich als Programmiersprache genommen habe,
heraus erzeugt, so mich das so nicht weiter interessieren.
Und diese Instruktionen werden dann eben entsprechend effizient mit kleiner,
schnell getakteter Hardware ausgeführt,
sodass also die Gesamtlaufzeit des Programmes verglichen mit irgendwelchen hochkomplexen Cis-Befehlen eben auch nicht größer ist,
sondern es soll es da sogar eher nochmal ein ganzes Stück kleiner,
weil ich bei diesen Rissprozessen,
mit dieser regulären Struktur dann nämlich entsprechend gut Pipeline realisieren kann.
Also beispielsweise sage ich dann eben wie,
also da Maps vorgestellt, eine Wortbreite für Befehle für Adressen,
für irgendwelche Datenstrukturen im einfachen Fall erstmal nicht.
Also das heißt, man kommt damit also sehr einfach hin.
Alle Befehle brauchen genau die gleiche Zeit.
Damit kann man dann sehr einfach dieses ganze Pipelining mit realisieren,
spare ich halt Zugriff, wie gesagt, nur Load and Store.
Das ganze Register adressiert. Man hat dann eben vergleichsweise viele
Register, teilweise werden dann auch noch so Tricks angewandt.
Das ist dann nämlich auch das Interessante.
Also Unterprogrammaufrufe passieren dann auch über Register,
dass die Prozessoren dann eben auch deutlich mehr Register haben
als das,
was der Programmierer sieht
Und sowas wie so ein Kontextwechsel vom Prozessor dann eben dadurch passiert,
dass einfach nur der Registersatz umgeschaltet wird. Das heißt,
ein Task, der irgendwie auf dem Prozessor läuft, sieht,
sage ich beispielsweise jetzt mal eben irgendwie seine 32 Register,
dann kommt irgendein Interrap, dieser Task muss unterbrochen werden und das,
was dann eben passiert, ist,
dass jetzt nicht alle Register gesichert werden,
ein anderer dahin geschrieben wird,
sondern es wird irgendwo nur so ein Pointer umgesetzt,
die nächsten 32 Register werden jetzt von anderen Tasks genommen und
wenn der erste Unterbrochene dann quasi weiterläuft,
wird das
wieder zurückgeswitcht
Ist also auch vergleichsweise extrem effizient. Also auch da kann man
relativ viel machen. Und bei Unterprogrammaufrufen kann man jetzt eben damit arbeiten,
dass Teile dieser Register quasi von dem aufrufenden Programm und von dem Unterprogramm gemeinsam genutzt werden.
Dadurch kann ich natürlich sehr einfach Parameterübergabe machen. Nicht,
weil ich dann einfach weiß,
in den und den Registern sind eben entsprechend die Parameter beziehungsweise
das Ergebnis und das aufrufende Programm schreibt hier rein,
das Unterprogramm startet,
hinterlässt dort sein Ergebnissen,
das Ganze kann dann dementsprechend
einfach weiterarbeiten
Ist dann auch deutlich effizienter als ein Stack-basierter Mechanismus,
wie wir ihn dann also beim X-86 noch kennenlernen werden.
Gut, Flex gibt es dann häufig auch nicht,
sondern gibt es dann eben spezielle Anweisungen, die das Ganze letztendlich machen.
Gut, Vor- und Nachteile. Kein Mikroprogramm, sehr einfache Instruktionen,
Pipe-Lining, in der Regel dann eben quasi in einem Tag
wird eine Instruktion quasi angefangen, beziehungsweise abgearbeitet.
Nachteil, dadurch, dass ich jetzt natürlich in der Instruktion
nicht ganz so mächtig bin, längerer Maschinencode,
und ich brauche eben die entsprechend vielen Register
Gut, würde ich heute nicht allzu lange überziehen, aber ich glaube,
das müsste jetzt auch die letzte oder vorletzte Folie sein.
Wie sieht das jetzt aktuell aus? Cisco vs. Risk,
also wie gesagt, ursprünglich gab es da mal also irgendwie relativ viel Diskussion,
was effektiver ist und, und, und.
Inzwischen muss man sagen, die Grenzen verwischen. Also X86
ist eigentlich ein Cisk-Maschine, wird heutzutage aber eher wie ein Risscomputer verwendet. Klassische, einfache Rissarchitekturen. Gut,
Maps ist noch so geblieben, aber bei den Armarchitekturen,
ist es halt dazu gekommen, dass man gesagt hat,
na gut,
also wenn ich meinen Armprozess jetzt also irgendwie für Multimedia-Verarbeitung in meinem Smartphone brauche,
weil ich da jetzt irgendwie mein Streaming befehle habe,
dann bin ich mit den ganz einfachen Befehlen vielleicht nicht so gut aufgestellt und möchte vielleicht noch Befehle haben,
die dort also ein paar andere Sachen haben.
Demzufolge hat sich das dann eben irgendwie immer so ein bisschen vermischt.
Deswegen muss man eigentlich sagen, also inzwischen lebt man eigentlich
immer ganz gut mit dem Besten von beiden Welten in der Regel,
wie gesagt, bei eingebetteten Prozessen eindeutig so eine Risk-Orientierung,
aber ansonsten ist man da dann eigentlich entsprechend fest.
Gut, ja, das sind dann eigentlich schon entsprechend Easer Design heute.
Wie gesagt, also wir werden dann hier nachher so ein bisschen davon noch was kennenlernen.
Und damit bin ich jetzt endlich mit diesem Kapitel fertig.
Und danke für die Geduld nach Weihnachten wird es dann,
wie angedroht, mit X86, Assembler und so kleinen Beispielen
dann so ein bisschen weitergehen.
Bis dahin, zwei Wochen lang frohes Fest, guten Rutsch,
gutes Feiern, was man sich sonst noch so wünscht.
Und das war es dann