Gut, ja, im Prinzip jetzt vor Weihnachten hatte ich
ja aufgehört mit dieser Beschreibung jetzt zur Instruction Set Architecture, wo im Prinzip jetzt eigentlich nochmal zwei Aspekte wichtig waren. Ich denke mal,
das werden sie inzwischen ja wissen,
weil das jetzt also auch in der einen Übungsaufgabe schon dran gewesen ist,
beziehungsweise dann, also sprechen wir heute abzugeben ist,
nämlich einmal diese Sache mit den Adressierungssaten,
wo es ihm letztendlich darum geht,
jetzt also so eine En-Adressmaschine zu haben, eben von einer reinen Steckmaschine,
also sprich Null-Adressformat bis zu einer Maschine eben beispielsweise im Drei-Adressformat,
nicht wo dann eben hier als Beispiel mit drauf war,
wie man jetzt also bei unterschiedlichen Maschinenbefehlssätzen so einen arathmetischen Ausdruck ausrechnet.
Und sag mal, sowas ist dann immer noch ganz interessant zu wissen,
war in Übungsaufgaben drin, konnte gerne auch meinen ähnlichen Formen dann in der Klausur mit ran.
Ja, also wie gesagt, das als der eine Hinweis. Dann zu den Adressierungsarten,
die hatte ich letztes Mal ja dann quasi als Letztes noch so kurz vorgestellt,
wobei ich dann auch immer sagen muss,
es ist dann immer so ein bisschen vom Maschinenbefehlssatz,
beziehungsweise wenn man in der Literatur nachsieht,
dann auch vom Auto abhängig, wie die Adressierungsarten genau genannt werden.
Wenn sowas jetzt immer irgendwie nicht so ganz eindeutig ist,
dann, denke ich mal, wird das eigentlich auch immer mit irgendwelchen Beispielen belegt.
Also auch dazu war ja in der einen Übungsaufgabe schon was gefragt worden,
wo dann eben auch im Prinzip dann also nochmal mit Beispielen belegt war,
was sich hinter diesen Adressierungsarten verbirgt.
Grob Oder ganz allgemein unterteilt, kann man eben sagen,
Emediate, da habe ich dann eben wirklich einen Zahlenwert,
mit dem ich rechne oder den ich als Adresse benutze, direkt im Befehl drinstehen.
Bei Direkt, ababrassiert habe ich in dem Befehl eine Speicheradresse drinstehen. Das heißt,
das, was dort ist,
ist dann eben wirklich eine Speicheradresse, wirkt dann eben,
ähnlich wie hier, wo ich dann eben sage,
ich habe hier jetzt also in dem Befehl beispielsweise noch mit einem zweiten Befehl Wort eine 32-Bett-Adresse,
mit der grafisch auf den Speicher drauf zu.
Und wenn das dann zum Beispiel sowas wie ein Ladebefehl ist,
wird das Ergebnis dann eben hier in einem Register geladen.
Indirekt ist das Ganze jetzt quasi noch einmal weiter verpointert.
Das heißt, ich habe in dem Befehl eine Speicheradresse stehen.
Diese Speicheradresse ist aber im Prinzip sowas wie ein Pointer,
ein Zeiger, rein technisch gesehen also wieder.
Einfach nur eine weitere Adresse und mit diesem zweiten Zugriff so
wie das dann hier dargestellt ist,
verweise ich hiermit quasi indirekt auf diese oder erstmal also auf diese Speicheradresse.
Das ist dann aber eben auch nur wieder ein Verweis auf
diese Adresse und damit passiert dann was,
dass wir dann eben ein Beispiel für indirekte Adressierung.
Und quasi Äquivalent dazu, bei Direkt und indirekt habe ich
in dem Befehl jetzt immer Speicheradressen drin stehen,
kann ich das Ganze dann eben quasi auch über Register machen.
Das heißt, ich habe in meinem, bei einer Registeradressierung
in meinem Befehl jetzt eben direkt irgendwie die Adresse eines Registers drinstehen.
Und damit wird irgendwie was gemacht, Register indirekt,
wäre dann quasi das Äquivalent zu indirekt. Das heißt,
ich habe in dem Befehl eine Registeradresse in dem Register steht dann die Speicheradresse,
auf die ich dann drauf zugreife. Indiziert ist dann quasi sowas,
wo ich dann auch wieder auf Register drauf zugreife,
dort dann aber im Prinzip noch entweder mit einem zweiten Register oder mit einem Offset rechne,
das Ganze dann eben auch immer so ein bisschen vom Befehlsatz abhängig.
Also hier sind es zum Beispiel dann so mal die Beispiele,
wie man sie also typischerweise bei Riskmaschinen hat,
wo ich dann also im Prinzip sowas wie so eine Index-Adressierung habe.
Das heißt, ich habe hier zwei Register und die Inhalte
dieser Registervierenden beispielsweise dann eben addiert und verweisen auf den Speicher.
Oder ich habe eben einen Register und dann eben noch so einen kleinen Emediate-Wert,
der dann als Offset noch dazu gerechnet wird.
Und auch das verweist dann wieder auf den Speicher. Sowas mit solchen indizierten Abressen,
solche Basisadressen benutzt, sondern eigentlich immer ganz gerne,
wenn man jetzt also irgendwie auf Datenstrukturen,
wie zum Beispiel Air Race drauf rumrechnet,
weil man dann also sehr einfach sagen kann,
ich habe Hier eben so was wie die Basisadresse,
also spricht das nur der Element von dem Array und kann
dann eben plus 1 plus 2 plus 3 dann eben entsprechend die weiteren Elemente dann über solche Offsets mit berechnen.
Gut, das jetzt eben nochmal so weit zu den Adressierungsarten.
Das war dann ja eigentlich auch das,
womit ich jetzt im Dezember eben vor Weihnachten aufgehört habe.
So, jetzt bis zum Ende des Kapitels
Wird noch ein bisschen was kurz vorgestellt zur Intel X 86 Architektur.
Dazu jetzt also auch erstmal nur als Anmerkung. Also das,
was jetzt hier auf den folgenden Folien ist, ist dann teilweise schon ein bisschen spezieller.
Hier geht es eigentlich erstmal nur irgendwie am Grundverständnis.
Hier kommen jetzt also auch ein paar Beispiele. Es geht jetzt aber nicht darum,
dass wenn die jetzt also hier gleich irgendwelche Befehle aufgezählt werden,
dass sowas, also sei es als Übungsaufgabe,
sei es als Klausuraufgabe irgendwo abgefragt wird,
das verlangt wirklich niemand,
nicht deswegen also hier jetzt nicht erschrecken lassen,
wenn es hier gleich also irgendwie so ein paar Folien kommen,
die ich dann also auch nur relativ oberflächlich noch
irgendwie inhaltlich erkläre
Andererseits, weswegen dieser Teil jetzt hier immer noch in den Vorlesungsunterlagen mit drin ist.
Im nächsten Kapitel werden wir uns so ein bisschen mit Assembler-Programmierung,
wenn auch sehr einfach gestrickt beschäftigen und für so ein bisschen Grundverständnis,
wollte ich jetzt eben vorneweg hier erst mal soweit die X86-Architektur grob skizzieren.
Nicht typischerweise das, was man damit, also ja,
beschreibt, sind dann also quasi die, alles,
was sich dann so evolutionär dann also aus seinem ersten Intel-Mikrofon Prozessoren heraus entwickelt hat.
Nicht vom allerersten 80, 86 Prozessor bis zu den aktuellen Core-I irgendwas,
beziehungsweise in etwas größeren Xeon-Prozessoren, die man dort hat.
Die Prozessoren selber sind sogenannte CIS-Prozessoren,
Komplex Instruction Set Computer, was sich dahinter verbirgt,
im Gegensatz zu Risk,
die beiden oder diese Wortpaare sind ja also auch schon
ein paar Mal in der Vorlesung gefallen,
werde ich also am Ende dieses Kapitels dann also
auch noch ganz kurz ein bisschen was zu sagen
Die Befehle, die man jetzt eigentlich typischerweise auf Intel-Prozessoren bzw.
intelartigen Prozessoren muss sich dann eigentlich richtigerweise sagen,
weil also die ganzen AMD-Prozessoren behören, beherrschen logischerweise auch diesen Befehlssatz,
was man darauf kann, muss man eben sagen,
sind eigentlich Befehle, die in vielerlei Hinsicht historisch gewachsen sind.
Deswegen hier diese Anmerkung nicht,
also es ist vom Arm sehr einfachen Befehlssatz,
den also die ersten Prozessoren Ende der 70er Jahre hatten
Bis zu dem Befehlssatz sind die Prozessoren jetzt teilweise haben, wo dann also viele Erweiterungen hinzugekommen sind. Gerade diese ganzen Multimedia-Befehle,
SSE steht dann für Single, SEMD, Extensions,
war es dann im Prinzip eine Möglichkeit ist,
die in den Prozessoren vorhandenen Rechenwerke,
weil ich also inzwischen ja nicht nur sagen kann,
Ich habe auf einer CPU mehrere Kerne,
also vier Kerner, acht Kerner,
wie viel auch immer,
sondern in jedem CBU-Kern habe ich ja eben nicht nur eine einzige Allum mit drin,
sondern inzwischen typischerweise dann eben auch schon vier Entagerreichen Werke und ein,
mindestens ein Fließkomma-Rechenwerk und es gibt dann eben auch entsprechende Befehle,
mit denen man dann also quasi mit einem Befehl dann eben
gleich mehrere Aktionen auf allen Rechenwerken dann letztendlich mit anstoßen kann
Gut, das,
was diese Prozession dann eben auch immer noch auszeichnet,
ist, also die Abwärtskompatibilität, das heißt,
das ist eigentlich etwas,
was das Ganze dann also auch vergleichsweise so erfolgreich gemacht hat,
dass im Prinzip immer Befehle hinzugekommen sind,
aber die alten Befehle oder Artenbefehl setzt du eigentlich auch immer noch gültig waren,
führte eben dazu,
dass man also quasi einmal gekaufte Software
auch auf der nächsten Prozessegeneration immer noch laufen lassen konnte
Gut, das, was wir dann hier benutzen werden,
ist im Prinzip Code, der eigentlich so, ja,
sagen wir mal so, in Anfang der 80er-Jahre angesiedelt wird,
also x, 3, 86 oder 80, 3,
86, wo man das dann also als wie so eine einfache Registermaschine verwendet.
Wie gesagt, Beispiele dazu folgen dann gleich noch. Hier nochmal so ein bisschen aufgezählt,
was sich da, sagen wir mal, eigentlich jetzt getan hat,
beziehungsweise hier dann eben wirklich so die ganzen Ersten,
die die ganzen entsprechenden Erweiterungen, also sind im Prinzip die erste Ursprünglich IBM-PC,
Ende der 70er Jahre rausgekommen.
Nicht 29.000 Transistoren, konnte ein Megabyte-Speicher dann adressieren,
wobei das dann auch noch mit ein bisschen Trickserei letztendlich ging.
Das Ganze dann als 16-Bit-Computer,
nicht inzwischen ist man also bei entsprechend etwas größeren Systemen mit 64,
GB Speicheradressierung, Jahren 100 Millionen Transistoren und eben relativ viel an zusätzlichen Erweiterungen,
Tricksereien, mehr Kern und so weiter. Gut,
dass hier auch eine Folie, wo man sagt,
technisch inzwischen auch schon drei Jahre, vier Jahre alt,
also zweite Generation, i7-Prozessoren, wo man dann sagt,
okay, also inzwischen Taktfrequenz, die aktuellen sind also bis zu vier Gigahertz,
hier mit vier Kern Das hier ist dann im Prinzip der Datendurchsatz in Giga Tee, steht für Transfer.
Das heißt,
wie sich also die CPU dann quasi mit der Schnittstelle und mit dem Speicher mit unterhalten kann,
dann eben hier entsprechend die Wortbreitwiese, Interfaces,
auf dem Ship dann eben schon drei Ebenen Cash-Speicher,
hatte ich ja auch im Prinzip so Mitte Dezember schon kurz erwähnt.
Cash-Speicher was das ist, spare ich der Hierarchie,
folge dann also ganz am Ende der Vorlesung dann nochmal als eigenes Kapitel,
wo ich das zumindest dann nochmal ganz kurz vorstellen werde.
Gut, das, was wir jetzt eigentlich noch wissen sollten,
bei den X86-Prozessoren als Speichermodell, haben wir ein Little-Indien-System.
Was war das noch? Auch das hatte ich ja kurz
vor Weihnachten noch soweit angesprochen. Man kann immer unterscheiden zwischen
B-Carmy und Little-Indien-System. Da geht es letztendlich darum,
dass man sagt, Von der Adressierung her,
adressiere ich beidweise, wenn ich jetzt in meinem Speicher,
aber quasi zum Beispiel Speicherworte von 32 Bit ablege, das heißt 4 Byte.
Und beispielsweise jetzt eine Zahl ablege,
die dann also auch 32 Bit breit ist, also 4 Byte belegt,
wo landen Moos significant Byte, das heißt die höchstwertigsten,
8 Bit und wo landen die niederwärtigsten 8 Bit.
Und bei Big Indian-System würden die höchstwertigsten Bits als erste auftauchen,
als erste gelesen werden.
Das heißt, bei den kleinsten Adressen stehen.
Also wenn ich das hier nochmal entsprechend zurückscrolle.
Da haben wir also da die entsprechende Folie. Das heißt,
da habe ich im Prinzip jetzt als Muss significant Bit bei
den kleinsten Adressen
Das wäre dann also hier entsprechend dargestellt, dass wenn ich hier in 32 Bitford habe,
die höchstwertigsten Bits dann immer eben Anfang weiterer Cenol, dann bei Adresse 1,
die nächsten 8 Bar Bit und so weiter und so fort.
Bei X86 haben wir quasi ein Little-Andien-System, das ist genau umgekehrt,
das heißt, da fange ich mit den, da haben wir es,
da fange ich eben mit den niederwärtigsten Bit an,
das heißt also unter der kleinsten Adresse habe ich eben das niederwärtigste Byte meines Speicherworts,
dann entsprechend wird dann immer jeweils so raufgezählt.
Hier ist das Ganze dann also auch nochmal beschrieben,
das ist also jetzt eine Grafik,
die im Prinzip aus dem Intel Programmers Manual kommt,
in der aktuellen Auflage, wo man dann eben wirklich sieht,
wie jetzt also hier irgendwelche Speicheradressen, 0, 1,
2, 3, jeweils quasi Hex hier irgendwie durchgezählt daran jetzt also irgendwelche Speicherworte abgebildet werden.
Und wenn ich ihm beispielsweise ein Wort,
das heißt also ein 16-Bit-Wert, umfasst zwei Byte an Adresse 1,
lese, das ist also diese Adresse,
hätte ich quasi als Wort CB 31 und die höchstwertigsten Bit sind dann eben entsprechend auf unter der zweiten Adresse abgelegt.
Das heißt eben, das ist eben genau dieses Little Andien Adressierungsschema,
das ich eben mit den niederwärtigsten Bits anfange
Hier ist es dann eben mit verschiedenen Beispielen, verschiedenen Wortbreiten aufgezeigt,
was man hier auch sieht, was sich ja auch quasi als letztes noch so mit vorgestellt habe.
Die X86-Architekturen können eben auch sogenannte Miss Alliance-Zugriffe machen. Das heißt also,
ich kann jetzt eben beliebig, meinetwegen jetzt hier innerhalb meiner Speicherworte,
Adressen wählen und dort was rauslesen,
hat aber dann eben immer den Nachteil, dass ich dann natürlich gegebenenfalls,
wenn ich jetzt also ein Wort lesen möchte oder einen Wert lesen möchte,
der sich dann aber über zwei Speicherworte erstreckt,
dann eben dem zuvor auch zwei Speicherzugriffe brauche und das Ganze dann dementsprechend langsamer wird.
Deswegen machen in der Regel die Compiler das dann auch so,
dass sie dann also über entsprechende Anweisungen und Flex dafür sorgen,
dass eben normalerweise der Code dann quasi erlined wird,
das heißt also dann lieber bisschen Wasser und Platz im Speicherbereich verschenkt wird,
ich dafür dann aber eben immer die Kotworte wirklich jetzt an solchen Speicherwortgrößen ausgerichtet habe.
Dann X86 Register, das ist also auch etwas,
was wir dann also im nächsten Kapitel dann auch noch reichlich sehen würden,
beziehungsweise war es dann also auch in der nächsten Übungsaufgabe mit auch Taufen tauchen wird.
Hier auch nur kurz angedeutet, eben nochmal,
sagen wir mal so, der Vergleich von den ersten Intel-Prozessoren,
80, 86, was im Prinzip ein 16-Bit-Rechner war.
Da hatte dann eben nur 16-Bit-Breiteregister, alles,
was jetzt hier, also die dunkelgrau gezeichnet ist.
Ab dem 386er-Prozessoren, die dann also quasi die ersten 32-Bit-Rechner waren,
wurden dann eben entsprechend die Register dann also auch noch hier breiter.
Und zusätzlich kam dann eben auch noch Fließkomma-Register, 80-Bit-Breit hinzu.
Und bei den Indiger-Registern, kann man jetzt eben sagen, gut,
es gibt hier als ausgewählte Register einmal den Programm-Counter, PC,
Programmzähler, beziehungsweise in der NTL Architektur heißt es dann immer IP,
beziehungsweise E-EP steht dann für Instruction Pointer.
Dann habe ich hier acht Register,
mit denen letztendlich vom Benutzer her im Assembler-Code aus agiert werden kann,
wovon ich jetzt also im Prinzip dann allerdings zwei,
nämlich Diese Space Adress in den Stackpointer auch noch ein bisschen anders behandeln.
Aber das werden wir dann also im nächsten Kapitel beziehungsweise bei
denen dort auftretenden Beispielen dann also auch wirklich genau sehen,
was sich dahinter verbirgt.
Ich habe im Prinzip erst mal acht Register, die man benutzen kann.
Der Programmzähler, wie gesagt, den ich in der Regel
natürlich jetzt nicht direkt beeinflusse, außer durch irgendwelche Sprungbefehle.
Und zusätzlich gibt es dann eben noch ein explizites Flag-Register.
Da dann eben auch als 32-Bit-Braters-Register, in dem jetzt also verschiedene Das heißt,
es werden einfach einzelne Bits in diesem Register jetzt durch irgendwelche Ereignisse gesetzt oder gelöscht.
Und ich kann dann eben lesen, nach arathmetischen Operationen beispielsweise,
Science Flag, Zero Flag, Parity, Carrie.
Nicht, also ob jetzt eine bestimmte arathmetische Operation ein Ergebnis gebracht hat,
was also beispielsweise gleichen Olle ist. Das heißt,
da muss, kann ich das dann eben direkt,
also aus diesem Zahlenfleck auslesen,
beziehungsweise gibt es dann entsprechende Befehle,
mit denen man dieses Register dann abfragen kann
Datentypen bei den X86-Architekturen, wie gesagt,
wären dann sowas von einem einzelnen Bite,
jetzt über verschiedene lange Inager-Typen, bis zu 64 Bittern eben,
nicht dann im Prinzip BCD-Typen, irgendwelche Zeiger,
32 Bitbreit bzw. dann eben in Erweiterung 48-Bit bei den X386-Prozessoren inzwischen,
wie gesagt, gilt das auch nicht so ganz,
aber das, was wir jetzt hier der Einfachheit halber betrachten,
das ist erstmal so ein vergleichsweise einfacher Befehlssatz,
beziehungsweise dann nur Ausschnitte davon und dann eben noch verschiedene Fließkomma-Typen,
Bitstrings, Spidestrings und so weiter.
Aber wie gesagt,
also das ist ja eigentlich auch eine Vollständigkeit halber mit drauf,
weil, also auch dazu kommt dann beim nächsten Mal oder im Surfsal nachher noch die Anmerkung,
Was wir hier natürlich machen oder was ich jetzt hier vorstelle,
ist jetzt also auch nicht irgendwie ein Kurs-Assembler-Programmierung.
Das will man nicht, das will ich auch nicht.
Das möchte auch keiner von Ihnen machen, sondern wirklich nur so
ein bisschen Grundverständnis wie Assembler-Befehle im Prinzip ablaufen, was sich dahinter verbirgt.
Aber jetzt nicht sämtliche Datentypen, sämtliche Arten von Befehlen,
die es dann also hier beispielsweise geben mag,
das wird nicht weder gelehrt noch abgefragt
Gut, wenn man sich hier den Befehlssatz selber ansieht,
habe ich dann eben, sagen wir mal,
von einfachen Daten zugriffen, spezielle Befehle für den Stack.
Hier irgendwie sowas wie Befehle, mit denen ich hier Typumwandlung machen kann,
dann natürlich irgendwelche arathmetischen Befehle in der Regel arbeiten wir natürlich
dann also quasi in mit der Binär-Arathmetik. Das heißt,
also ich interpretiere meine Zahlen eben entweder als Unsight oder als Science-Saal,
habe dort dann eben entsprechende Befehle, dann gibt es noch,
was aber auch eher historisch gesehen ist, noch solche BCD-Arathmetik-Befehle,
wenn ich dann diverse logischen Operationen sprünge, bedingte Sprünge,
unbedingte Sprünge, unter Programmaufrufe, also Korn und Return.
Ich bin da noch diverse Operationen für String-Befehle,
beziehungsweise dann eben noch Sachen, die man jetzt also für
den Programmablauf seins des Betriebssystems dann eben auch noch bochte.
Gut, auch das hier ist nun ein sehr kleiner Ausschnitt. Wie gesagt,
grundsätzlich kann man sagen, X686-Befehlssatz ist eben ein Cisk,
komplexer Befehlssatz mit verschiedenen Ausnahmen, Spezialfällen. Die Befehle können von einem Byte,
das heißt also ein Befehl der 8 Bit umfasst,
bis zu 36 Byte lang sein,
enthalten im Prinzip alle irgendwie sowas wie ein Obco,
dann aber noch diverse optionale Komponenten,
mit denen dann also beispielsweise jetzt irgendwelche Varianten hinsichtlich des Adressierungsmodus unterschieden werden,
wo dann noch irgendwelche automatischen Offsets mit reingerechnet werden können und so weiter und so fort.
Da gibt es also relativ viel an Zusatzoptionen,
was dann natürlich dazu heißt,
dass wenn der Computer oder der Rechner dann also bei der Phase Befehl abarbeiten ist,
das heißt, der Befehl wurde geholt,
liegt jetzt also irgendwo in meinem Befehlsregister drin,
soll dann irgendwo interpretiert werden,
dass dann demzufolge natürlich das Ganze dann auch entsprechend aufwendig ist
und abläuft
Gut, hier auch nur noch mal Beispiele über solche möglichen Ergänzungen in den Befehlen,
nicht wo man dann also im Prinzip,
also innerhalb der Befehle dann also irgendwie umschalten kann.
Also auch das nur als Beispiel für diese Sache,
wie man dann also sich so eine Code-Kompatibilität erreicht
zwischen 16 und 32 Bit-Adresslängen beziehungsweise inzwischen hat man dann eben
auch noch eine Umschaltung auf 64 Bit mit drin,
nicht wo dann im Prinzip in den Befehlen entsprechend diese Wortlängen mit umgeschaltet werden können.
Hier auf dieser bzw. der nächsten Folie dann eben
auch noch mal kurze Beispiele dazu.
Also hier habe ich eben
einen Befehl
Wirklich minimal groß, ein Weit, Push, das heißt,
da wird also im Prinzip jetzt also der Inhalt eines Registers auf den Stack gepackt.
Nicht dann, also hier sowas wie so ein Move-Befehl,
der ist dann hier also auf einmal drei Weit groß,
unter Programmaufruf. Dann hier mit fünf Byte,
also 40 Bit, und da sieht man eben wirklich,
wie irregulär da dann eben auch teilweise die Befehlsformate sind.
Also auch hier als Beispiel, wie eben der Obcode,
Ist also hier mal vier Bit groß, mal fünf Bit,
mal sechs Bit, mal sieben Bit, mal acht Bit.
Dann gibt es also den bestimmt auch noch, also irgendwelche Befehle,
die dann also auch noch entsprechend andere größere kurz haben.
Gut, hier auch noch mal kurz aufgezählt für einige von diesen Befehlen,
was ich dann dahinter als Funktion verbirgt. Wie gesagt,
da das jetzt also hier nicht weiter relevant ist,
werde ich diese Folie jetzt,
also hier noch mal kurz überspringen und dann eigentlich nur mal
kurz auch dann eben auf so eine, so ein Assembler-Beispiel übergehen.
Wie gesagt, das kommt ja dann im nächsten Kapitel bzw. dann
eben auch in den Übungsaufgaben nochmal so ein bisschen mit dran.
Das ist ja dann also auch was, sehr jetzt,
also schon über die Feiertage in der einen Übungsaufgabe mit drin hatten,
dass man also zumindest mal einen C-Code einmal übersetzen lässt.
Und dazu dann ist ein Assembler-Code hart nicht. Und also
wenn man sich das dann eben ansieht oder als disassemblierten Code dann hat,
Das wäre dann also hier quasi jetzt die jeweiligen entsprechenden Befehle.
Hier da in Assembler schreibweise, das heißt in so einer Mememonic-Schreibweise,
wo ich das also letztendlich lesen kann.
Und hier dann unter entsprechenden Adressen, also die erste Spalte,
hier wäre dann immer die jeweilige Adresse und hier dahinter dann
in der zweiten Spalte dann der wirkliche Binärcode,
der dann eben quasi diesem Befehl hier entspricht
Und das war es dann, also hier hinterher noch als
Kommentar mit drin ist, ist dann letztendlich das,
was dann also irgendwo ursprünglich mal aus dem Quellcode gekommen ist.
Also in diesem Fall geht es darum, also dann beispielsweise
war es wie so ein String auszudrucken mit dem Print. Das heißt,
ich habe ihren Zeiger auf dem Charakter Array nicht und solange ich nicht als Zeichen ein Zeichen mit lauter Nullen lese,
was ich also im Prinzip dann hier mit so einem Komperbefehl letztendlich hier an dieser Stelle, nämlich mit Abfrage,
beziehungsweise hier wird dann immer ein einzelnes Zeichen quasi kopiert und dann mit dem IO-Befehl dann ausgegeben.
Gut, und wenn ich sowas dann habe,
also wie gesagt, das wäre jetzt ein Ausschnitt,
also quasi so eine einzelne Printroutine,
nicht wenn ich sowas dann habe und dann also jetzt wirklich als Beispiel sowas wie mein Hello World nehme und das dann entsprechend zusammenbaue,
da würde dann eben genau dieser Teil hier mit dem Print dann eben Sorry,
so, das war zu eins so.
Eins soweit. Gut, hier haben wir das Print,
da dann eben auch noch mit auftauchen.
Das ist dann nämlich hier in dieser innere Teil.
Ich finde hier außen habe ich dann eben letztendlich dann
den Aufruf von diesem Print und hier dann den Teil.
Nicht. Also das jetzt nur als so ein kurzes Beispiel,
wie dann also letztendlich so ein Assembler-Code aussieht.
Wie gesagt, also ich will das jetzt hier nicht Befehl für Befehl durchgehen.
Das ist in diesem Fall jetzt hier auch überhaupt nicht gefragt.
Nicht aber einfach nur als Beispiel dann eben für so einen
Ist ja im Black Code, wie gesagt, genaueres dazu
folgt dann auch gut. Gut, ja, abschließend,
Instruction Set Architecture, was ist jetzt eigentlich ein vernünftiger,
ein guter Befehlssatz, denn da kann man sagen,
jetzt grob folgende Kriterien sagen, also Vollständigkeit, alle notwendigen Instruktionen verfügbar.
Jetzt ist natürlich die Frage, was ist notwendig? Notwendig stellt man hinterher fest,
wenn man also jetzt irgendwie seinen Algorithmus oder sein Programm hat
und versucht das also in Assembler Code zu schreiben auf einmal feststellt, Mensch,
also diesen einen Befehl, den ich jetzt eigentlich bräuchte, den habe ich nicht.
Da hat man ihn leider im Befehlssatz vergessen und er ist nicht vollständig.
In der Regel arbeiten wir jetzt anders, dass wir sagen,
okay, ich habe jetzt also entweder X86 Architektur oder ich habe einen Armprozessor,
also sprich irgendwie einen fertigen Prozessor und ich muss mir jetzt keine Gedanken darum machen,
mir jetzt selber einen Befehlssatz für irgendeine neue Architektur zu überlegen.
Aber wenn das der Fall sein sollte, müsste man dann eben gucken,
was brauche ich an Befehlen und versuche da die dann eben entsprechend jeweils mit reinzunehmen.
Typischerweise, also auch das war ja dann schon einige Folien vorher,
da letztendlich mit drauf, brauche ich dann, ich weiß gar nicht mehr wo, Genau.
Hier habe ich, so war es nicht,
also im Prinzip so folgende Befehlsklaas, dass man eben sagt,
okay, ich brauche im Prinzip jetzt erstmal irgendwie sowas,
um zu rechnen, arethmetische Operationen, logische Operationen,
Schiebebefehle, nicht also das sind,
das sind irgendwo Sachen, wo man sagen kann,
ist vergleichsweise sinnvoll und kann man im Algorithmus irgendwo auch mal Ganze nicht benutzen.
Vergleiche muss ich machen können, irgendwelche Datentransfer,
sei das vom Register zum Speicher,
innerhalb vom Speicher oder innerhalb von Registern,
wie auch immer, nicht aber im Prinzip irgendwie sowas,
ich muss dann die Möglichkeiten haben, dass den Programmablauf zu steuern,
das heißt also irgendwelche Sprünge, bedingte Sprünge,
vielleicht sowas wie Unterprogrammaufrufe, irgendwelche Verzweigungen.
Und ich brauche eben noch so ein bisschen was,
um dann also jetzt quasi sagen, es ist Betriebssystems zu sagen,
okay, das Programm ist fertig, abgelaufen.
Und jetzt kannst du irgendwie was anderes nehmen oder wenn zwischendurch,
also irgendwo ein Interabkommt, wie wird der dann irgendwie entsprechend abgearbeitet?
Das heißt, da kann man jetzt so unterschiedliche Befehlsklassen sagen. Und wenn man sagt,
okay, ich habe hier von allem mehr oder minder was mit drauf,
ich hatte zum Beispiel, kann man sagen,
gut, die Vision,
Multiplikation brauche ich nicht zwingend,
das kann ich
auch über Unterprogramme machen
Man kann dafür aber eben auch eigene Befehle vorsehen. Gut,
Vollständigkeit, Orthogonalität, einfach gesagt,
dass ich also natürlich dann jetzt nicht unnötig viele Befehle habe,
sondern eben auch nur so wenig wie möglich.
Das heißt, daraus ergibt sich dann zwingendermaßen,
dass ich also nicht für gleiche Operationen
fünf verschiedene Befehle habe
Nicht, dann hat man häufig sowas wie Symmetrie mit drin.
Das heißt, wenn ich einen Befehl für die Addition habe,
ist es sinnvoll, einen für die Subtraktion zu haben,
dann adäquat, effizient.
Da geht es dann eben letztendlich darum, brauche ich einen Spezialbefehl,
der mir die fünfte Wurzel von einem Integer berechnet?
Oder brauche ich das nicht? Da diese Aufgabenstellung vergleichsweise selten auftaucht,
kann man sagen, dafür brauche ich nicht einen speziellen Maschinenbefehl,
sondern wenn das auftaucht,
dann wird das eben quasi jetzt über entsprechende
arathmetische Operationen und Unterprogrammen gemacht
Beziehungsweise dann eben als Folge von Befehlen direkt geschrieben. Das ist also quasi adäquatheit,
Effizienz, dann eben sowas, dass man darauf achtet,
dass die Befehle eben auch entsprechend gut auf der Hardware ausgeführt werden können. So,
wichtig ist sowas jetzt eben eigentlich in dem Zusammenhang,
dass ich sage,
wenn ich mir jetzt angucke, beispielsweise jetzt beim X86-Prozessor,
wo ich nur deutlich über 300 verschiedene Befehle habe und ich mir jetzt also mal so eine typische Workload hernehme und beispielsweise jetzt also irgendwie sowas wie ein Benchmark laufen lasse,
beziehungsweise das übersetze durch einen Compiler, dann eben gucke,
welche Befehle werden dort verwendet, dass sie dann eben sagen kann,
wenn ich die zehn häufigsten Befehle nehme von diesen 300 möglichen,
habe ich schon 96 Prozent des Codes eigentlich abgedeckt.
Das heißt, diese 300 Befehle sind dann eigentlich schon in Anführungszeichen Overkill,
weil ich also wirklich nur vergleichsweise wenig brauche,
weil ich eben beispielsweise mit Lade- und Speicherbefehlen, das heißt Datentransfer,
vom Speicher in ein Register oder vom Register in den Speicher,
sowas relativ häufig habe, bedingte Sprünge,
Vergleichsoperationen und dann eben Additionen,
Bitvises und Subtraktionen und dann eben hier Datentransfer,
Zwischenregister und eben unter Programmaufrufe.
Hier ist dann also quasi nochmal die gesamte Statistik dazu aus dem Buch von Hennessee und Patterson,
wo man jetzt hier zum Beispiel für die Backend,
Suite, das heißt also ein Benchmark für Festkommaberechnung,
deswegen tauchen die hier bei den ganzen Floating-Point-Befehlen auch jeweils null Prozent mit auf.
Nicht, aber wo man dann eben für diesen Benchmark dann eben mal wirklich guckt,
wie häufig werden welche Befehle ausgeführt und die häufigsten zehn sind dann eben letztendlich diejenigen, die dann da aufgetaucht sind.
Gleiches gilt nicht nur für X86 Architektur mit Cisc-Befehls, Ja,
mit Sisk-Befehlssatz, sondern es gilt eben auch für Risk-Prozessoren,
also hier als Beispiel jetzt für den Mips-Prozessor,
wo dann eben auch einmal hier der Speckind-Benchmark,
beziehungsweise als Festkommaratmetik, beziehungsweise hier für Fließkomma-Arratmetik geguckt wurde,
wo ich dann eben auch mit,
in diesem Fall jetzt hier sieben Befehlen,
96 Prozent des Codes eigentlich schon abgedeckt habe,
hier und hier,
also im Prinzip dann auch mit zehn Befehlen,
auch schon 97 Prozent des Codes
Ne,
und das ist dann eben eigentlich auch eine der Motivationen,
weswegen in den 80er Jahren Riskbefehlsätze entsprechend populär geworden sind,
weil man dann eben einfach gesagt hat, okay,
ich habe zwar theoretisch auf meinen Prozessoren 300 mögliche Befehle,
aber ich benutze davon vielleicht 20, 30 Stück,
nicht warum muss ich das überhaupt so umständlich machen,
sondern packe das nicht entsprechend kleiner zusammen und man hat eben wirklich nur sehr einfache Operationen,
wie dieses Beispiel hier dann ja zeigt,
die dann eben vergleichsweise häufig genutzt werden und kann damit eigentlich auch schon alles berechnen.
Deswegen eben die Motivation für solche Riskbefehlsätze. Ja,
hier nur noch mal kurz aufgezeigt im Prinzip der Vergleich,
was verbirgt sich hinter diesen Begriffen,
Cisk,
also für Komplex Instruction Set Computer
Hatte ja auch schon mehrfach gesagt, ist eigentlich noch ein Relikt aus der Zeit der Großrechner,
wo es nämlich noch gar keine Hochsprachen gab.
Also sagen wir mal so Konzepte wie Objektorientierung sind ja jetzt
überhaupt erst in den letzten knapp 20 Jahren aufgekommen.
Und auch andere komplexere Sachen jetzt also aus Hochsprachen oder andere das,
was wir jetzt eigentlich im Prinzip immer in den Programmiersprachen benutzen, gab es auch nichts.
Und ganz am Anfang war es eben wirklich so,
programmiersprachen, waren auf den allerersten Maschinen überhaupt nicht zu fahren,
sondern es gab also wirklich nur einen Maschinenbefehlsatz,
die Maschine wurde dann eben direkt auf Assembler-Ebene programmiert,
um dort aber die Möglichkeit zu haben, also jetzt vergleichsweise einfach sowas wie zum Beispiel irgendwelche Schleifen zu machen.
Also das heißt, ich habe einen Array,
ich möchte jetzt die Elemente eines Race,
der eine Reihe nach adressieren, irgendwie durchlaufen, um beispielsweise,
wenn das jetzt also sowas wie ein zwar dimensionales Array ist,
wo ich jetzt also für irgendwelche Matrix-Berechnungen das also jetzt elementweise durchlaufen muss,
nicht da gab es dann eben entsprechende Befehle,
wo ich dann eben quasi mit einem einzigen Befehl sagen konnte,
nimm Alle Elemente dieses RAs beginnen bei der Adresse,
machen Autoinkrement,
um jeweils so und so viel und addiere das Ganze.
Und dafür gab es dann eben wirklich einen speziellen Befehl,
der dann aber im Prinzip das machen konnte, was wir heutzutage,
also quasi einfach komplett schon in eine kleine Vorschleife schreiben würden.
Nicht deswegen diese Ausdruck, dass eben quasi die Komplexität in
der Programmierung dann eben durch solche vergleichsweise sehr mächtigen Befehle
umgangen wurde
Gut, das Ganze führt dann eben dazu,
dass man natürlich also in den Instruktionen setzen sehr viele Befehle hat,
mit auch sehr komplizierten Instruktionsformaten,
wie eben an dem Beispiel X86 ja gezeigt, von einem Befehl,
der dann eben nur ein Wort umfasst, dann eben bis zu Befehlen,
die dann also quasi jetzt auch über mehrere Speicherworte gehen,
mit den ganzen Nachteilen, komplexe Befehlsdekodierung, dieser Tal,
was also quasi im Fonds-Neumann-Konzept immer dieses Befehl holen ist,
dann auch so aussieht, dass ich jetzt eben nicht sagen kann,
Befehle holen.
Das heißt, von der Speicheradresse lese ich jetzt meinen Befehl,
sondern es kann sein, dass der Buffet ja jetzt über mehrere Speicheradressen geht.
Das heißt, man liest dann eben nur den ersten Teil des Befehls,
stellt dann quasi aufgrund des Briefixes fest. Aha,
das ist ein Befehl, der dann also aus zwei,
drei, vier, fünf Speicherworten besteht und muss dann eben
quasi eben auch in diesem Teil Befehl holen,
das Ganze dann in mehrere Teilschritte wieder unterteilen.
Nicht dank roten diese ganzen Befehle dann auch noch auf entsprechend vielen
unterschiedlichen Datentypen rechnen. Nicht also auch das Wie gesagt,
als, oder vor dieser Grundidee,
dass man mit den Befehlen sehr viel gleichzeitig machen konnte.
Was dann eigen ist, dass sich dann eben für
die Befehle auch sehr viele Adressierungsarten oder Kombinationen habe.
Das heißt eigentlich so gut wie alle Befehle können direkt auf den Speicher zugreifen.
Die können teilweise aber auch auf Register drauf zugreifen.
Und ich habe dann Wie das also zum Beispiel jetzt
also an diesem Beispiel indirekte Adressierung der Fall ist,
dann eben auch irgendwelche komplexeren Adressberechnungen. Das heißt also,
ich habe in meinem Befehl sowas wie eine Speicheradresse,
die Speicheradresse ist, aber eben auch nur ein Pointer und
verweist wieder auf eine andere Speicheradresse und so weiter und so fort.
Dann haben Ciskrechner typischerweise unter Programmaufrufe auch so gestaltet,
dass das Ganze über ein Stack passiert.
Das würden wir dann im nächsten Kapitel bzw. dann also auch noch in der letzten Übungsaufgabe zwölf,
die dann quasi nächste Woche rauskommt, dann auch noch mitsehen.
Und Stack von der Idee her ist dann eben auch nur
ein bestimmter Bereich des Hauptspeichers, wo man sagt,
der wird dann eben quasi jetzt speziell genommen vom Betriebssystem,
um dort jetzt also für Unterprogrammaufrufe irgendwelche Argumente zu hinterlegen
Und ich habe dann eben explizite Anweisungen, Push und Pop,
um dann eben Elemente auf den Stack zu packen, beziehungsweise von dort wieder runterzulesen.
Nicht dann, das hatte ich ja auch schon kurz angedeutet,
es gibt dann eben sowas für so ein explizites Fleckregister,
wo dann also im Prinzip von irgendwelchen aratmetischen Operationen einzelne Bits gesetzt werden,
ist das Ergebnis gleich null, ist es größer null,
ist ein Überlauf aufgetreten und was auch immer.
Gut, was sind Vor- und Nachteile von CISK-Befehlsätzen? Ich hatte schon gesagt,
also man kann mit diesen komplexen Befehlen natürlich dann also relativ viel ausdrücken,
hat dann eigentlich eine Vergleichsweise einfachen Assembler, hat insgesamt auch einen etwas kompakteren Code,
weil ich eben teilweise mit diesen Befehlen auch sehr viel mehr machen kann.
Das heißt, um jetzt also ein Algorithmus oder ein Programm zu schreiben,
brauche ich eben erstmal vergleichsweise wenig Befehle. Andererseits ist es so,
wenn ich jetzt sage, ich baue eben wirklich einen Compiler,
Um jetzt also aus einer Hochsprache das in den entsprechenden Assembler-Befehlsatz zu übersetzen, ist das,
oder würde es jetzt mal vergleichsweise schwierig, diesen gesamten Befehlsatz vom Compiler überhaupt sinnvoll auszunutzen.
Und was dann eben auch noch ganz entscheidend ist, ist,
dass dann eigentlich die Abarbeitungszeit für einen Befehl eben auch nicht konstant ist,
sondern abhängig von der Art des Befehls,
wie sieht das mit der Adressierung aus,
greift das noch jetzt über mehrere Pointer auf den Speicher drauf zu,
kann es dann eben sein,
dass ein Befehl dann Beispielsweise noch irgendwie drei oder vier Speicherzugriffe nach sich zieht,
was dann eben auch wieder heißt,
dass dann eben die Abarbeitung dieses Befehls entsprechend mehrere Takte auf einmal dauert,
nicht und das Ganze dann ihm wirklich davon abhängig ist.
Gut, Variables Instruktionsformat hatte ich eben schon kurz angedeutet,
was damit gemeint ist,
dann eben diese verschiedenen Adressmodi machen natürlich dann also im Prinzip sowas wie eine Speicherhierarchie,
das heißt also alles, was wir heutzutage eigentlich mit Cashspeicher haben,
dann auch entsprechend sehr umständlich handhabbar und sowas wie Pipelining in dem Sinne geht,
eigentlich auch erstmal nicht oder man muss sich da dann auch wieder einige Tricks einfahren lassen.
Dazu würde dann auch relativ am Ende der Vorlesung nochmal so
im eigenen Kapitel ein bisschen was dazu kommen. Wie gesagt,
prominentestes Beispiel eigentlich jetzt für das, was jetzt eben noch Cisk-Rechner sind,
Intel X86 Architekturen, alles, was dahinter steht,
Motorola 68.000 oder Deck Wax-Rechner sind Rechner,
wo man dann sagen kann, gut,
also die haben wir bei uns in der Informatik noch irgendwo im Computermuseum rumstehen.
Ansonsten sind das aber alles Sachen, die dann eigentlich
auch schon seit den Anfang der 80er Jahre ausgestorben sind.
Um jetzt diese komplexen Befehle, wie sie in einem ziskrechner Auftreten abzuarbeiten,
braucht man dann eben etwas,
was sich ja auch quasi ganz am Anfang dieses Abschnitts hier dann nochmal kurz vorgestellt habe.
Man braucht nämlich Mikroprogrammierung, weil Ein Befehl jetzt nicht nur einen Takt,
sondern eben entsprechend mehrere Takte braucht und um diesen Ablauf zu steuern.
Das heißt, um jetzt also irgendwie so ein etwas komplexeren Befehl,
der jetzt also beispielsweise bei einer indirekten Adressierung mehrere Speicherzugriffe nach sich zieht,
abzuarbeiten, bräuchte ich eben eigentlich sowas, wo sich dann hinter jedem Befehl ein endlich Automat versteckt.
Und das Ganze macht man dann eben typischerweise dann eben als Mikroprogramm,
wo dann jeder Befehl dann eben über mehrere einzelne Stufen die dann Mikroinstruktionen genannt werden, implementiert wird.
Das passiert dann eben typischerweise mikroprogrammiert, so über sowas,
wie das hier aussieht, dass sich dann eben letztendlich sage,
ich habe hier ein Speicher,
in dem ich quasi jetzt mit meinem Befehl hinein adressiere,
dass wir dann quasi dann die Startadresse beispielsweise dieses Befehls,
das, was ich dort rauslese, da habe ich dann jetzt eben
Zum einen sowas wie eine Folgeadresse, wo dann eben die einzelnen Schritte,
die der Befehl in der Abarbeitung macht,
dann eben der Reihe nach irgendwo abgelegt sind und dann eben ein mehr oder minder langen Kontrollvektor,
mit dem ich dann also quasi jetzt für meine sämtlichen arathmetischen Einheiten für meine Multiplexer,
für was auch immer, in der Hardware letztendlich sage,
was dort gemacht werden soll
Nicht und der Befehl wird dann eben dadurch abgearbeitet,
dass ich eben hier irgendwo anfange,
den dann über mehrere Schritte durchlaufen lasse,
wo ich dann eben sage, um die ersten Operanten zu holen,
soll erst das passieren, im nächsten Schritt soll,
der da in Fahrt geschaltet werden,
im nächsten Schritt soll dann die eigentliche Addition stattfinden und nicht und je,
weil es abhängig davon, was sich dann dort tut,
wird dann eben hier das entsprechende Wort aus Nullen und Einsen hinterlegt sein.
Nicht, dass wir eigentlich so die direkte Implementation,
wie man das Ganze dann über so ein Mikroprogramm steuert.
Von der Idee her, ganz früher war das Ganze eigentlich
immer nur als ROM implementiert. Inzwischen ist es so,
dass man meistens, zumindest bei den X-Hacks und 80er-Architekturen,
dort dann also auch noch einen Speicher hat. Das ist also etwas,
was, wenn man sich das also irgendwo auf den Systemen mal ansehen kann,
was eigentlich beim Buten passiert, dass also irgendwo auch relativ am Anfang dann sowas wie ein Mikroprogramm geladen wird.
Und auch das haben die Ship-Hersteller dann teilweise schon ausgenutzt,
als es bei X-86-Prozessoren in den 90er Jahren mal diesen einen Fehler gab,
wo im Prinzip bei der Fließkomma Division unter bestimmten Bedingungen was schiefgelaufen ist,
gab es dann quasi ein Patch in dem Sinne,
dass man also quasi jetzt den Mikrocode für den Prozess einfach so verändert hat,
dass man die Abläufe so umgebaut hat,
dass dieser Fehler oder dieser Zustand,
der dafür zugeführt hat,
dass dieser Fehler aufgetreten ist,
eben nicht erreicht wird
Nee, nein, also das ist ja eben immer die Sache,
also wenn ich das jetzt als ROM habe,
dann ist es ja wirklich, also die Frage war,
wie hat man das jetzt also wirklich technisch gemacht mit dem Patch?
Also wenn ich das als ROM habe,
dann habe ich das natürlich fest eingebrannt,
da kann ich nichts ändern, da kann ich nur sagen,
okay, Prozessor kommt irgendwann in neuere Vision raus, nicht,
deswegen hat man typischerweise bei den Prozessoren dann ja auch immer Unterscheidungen nach irgendwelchen Steppings,
die dann also im Laufe der Zeit,
das ist zwar immer noch der gleiche Prozessor,
aber dann vielleicht auch schon mal ein bisschen überarbeitet,
wo dort
was geändert wurde
Bei dem, was man da aber inzwischen eigentlich hat,
ist, dass man Arsenal als Rom, Teil auch als Ramm hat.
Und der Inhalt dieses Rahmens liegt dann letztendlich im Bayos irgendwo mit drin. Das heißt,
das ist dann etwas, wo man sagen kann, okay,
das kann ich dann noch irgendwo über Bayos oder Betriebssystem-Sachen,
also irgendwo so hinterlegen,
dass es quasi von diesem allerersten Teil Rom ganz zu Beginn des Butens irgendwo reingeladen wird.
Und ich dann also diesen Mikrocode geladen habe und dann Prozesse sammle,
also die in Anführungszeichen komplexeren Befehle macht und für alles andere
vorher habe ich dann eben wirklich noch irgendwelche Befehle,
die so trivial sind, dass man sagt,
okay,
da gibt es
keinen Fehler
Nicht, aber das ist dann eigentlich immer so ein bisschen von Prozess für Architektur und den ganzen Sachen abhängig. Gut,
das, was ich jetzt hier vorgestellt habe,
ist dann eben etwas, was ich eigentlich horizontale Mikroprogrammierung nennt,
wo ich dann eben wirklich sage,
ich habe deswegen horizontal ein sehr langes Mikroprogrammwort,
wo ich wirklich für jeden Multiplexer und Freedes,
was ich irgendwo an Steuerleitung habe,
hier eine einzelne Spalte in meinem Steuerwerk habe, führt natürlich dazu,
dass wenn ich jetzt also im Prozessor also irgendwie 70,
80 Steuerleitungen habe, hier dieses, dieser Speicher entsprechend groß werden muss,
was man machen kann, ist dann etwas,
was ja ein Vertikalem oder mehrstufige Mikroprogrammierung nennt,
wo ich dann nämlich wirklich quasi hier gucke,
weil häufig sind bestimmte Kombinationen einfach allen oder in vielen Mikroprogrammwörtern identisch,
dass man so eine mehrstufige Kodierung macht und guckt,
wie viele unterschiedliche Belegungen an Steuerworten habe ich hier wirklich nur
Wenn ich also hier beispielsweise jetzt wirklich nur 50 unterschiedliche Belegungen habe,
dann kann ich sagen, ja gut,
also diese 50 unterschiedlichen Belegungen könnte ich dann mit Logarithmus,
Duales von 50 quasi Bit kodieren.
Also in diesem Fall jetzt also hier mit sechs Bit.
Nicht damit kann ich dann also entsprechend jetzt, also 64 unterschiedliche Teile kodieren.
Habe dann eben hier quasi mein Mikroprogramm speicher,
wo ich dann also hier 512 Wörter mit sechs Stellen
habe und in der zweiten Dekodierung mache ich dann quasi jetzt
also aus diesen sechs den entsprechend breiten Vektor
erst raus
Nachteil von dem Ganzen ist da natürlich auch immer,
dass man dann wieder nochmal eine mehrstufige Dekodierung hat,
das Ganze dann noch letztendlich auch wieder nochmal wieder deutlich mehr Zeit kostet.
Also all das muss man sagen,
diese Überlegung hier mit Mikroprogrammierung und wie spare ich da was ein,
sind eben eigentlich auch noch irgendwo so aus der früheren Zeit
der Großrechner inzwischen bei modernen Prozessoren sieht das eben in
der Regel entsprechend anders aus
Risk, im Gegensatz dazu,
reduced Instruction Set Computer,
beziehungsweise man findet gelegentlich dann auch Regular Instruction Set Computer,
ist dann eben aufbauend auf dieser Grundidee,
dass ich eigentlich nur vergleichsweise wenig Befehle überhaupt nur brauche,
wurde eben Anfang der 80er Jahre von den Herren Hennessey und Patterson von Stanford,
beziehungsweise Berkley quasi entwickelt,
war dann also ein internes Projekt bei IBM
Die Idee war eben, dass ich sage, gut,
inzwischen gab es vernünftige Hochsprachen.
Es gab vernünftige Compiler. Man hat eigentlich festgestellt,
also ich brauche von meinem Riesenbefehlsatz nur vergleichsweise wenig Befehle.
Verlager die Komplexität eigentlich in den Compiler. Das heißt,
die Scheibe, also in der Hochsprache in meinem Programm,
habe einen Compiler, der mir das also hoffentlich vernünftig in diese Riskbefehle übersetzt.
Und ich brauche eben nicht mehr diese unendlich komplexen Befehle mit
irgendwelchen Adressierungsarten, wo ich mit einem Befehl,
also eine gesamte Matrix-Multiplikation beschreiben kann
Nicht demzufolge sagt man dann eben, ich verlage das letztendlich
dann eben in den Compiler. Führt dann eben auch dazu,
dass quasi diese CIS-Programmierung, das heißt also auch diese ganze Mikroprogrammierung,
die ich da jetzt also vorher hatte,
dann eigentlich auch nicht mehr nur drin ist, sondern das,
was dann eben auch wirklich das Entscheidende für Riskmaschinen ist,
ist, dass ich dann eben sage,
ich habe deutlich weniger Instruktion, sehr einfache Instruktionen,
die aber Entsprechend schnell und effizient auf der Hardware ausgeführt werden können.
Das Ganze dann in entsprechend regulären Strukturen stattfindet.
Das heißt, ich habe beispielsweise jetzt bei uns 32-Bit-Rechnern,
also 32-Bit-Wortbreite und eben auch 32-Bit-breite Befehle nur.
Das heißt, mit einem Wort hole ich dann auch immer genau einen Befehl.
Alle Befehle sind in gleichartiger Zeit ausführbar, egal ob das
jetzt Ein Sprung ist, ein Ladebefehl, ein arathmetischer Befehl.
Wir brauchen eben alle gleich lang. Und ich kann das Ganze
dann eben auch in Form einer Pipeline-Verarbeitung starten. Das heißt,
dass mehrere Befehle dann eben auch überlappend abgearbeitet werden und ich jetzt
wirklich mit jedem Takt des Prozessors genau einen neuen Befehl anfangen kann.
Ist dann eben entsprechend sehr effizient, Pipelining,
was sich dahinter verbirgt, welche Vorteile das hat.
Allerdings noch welche
Teilweise Nachteile, wird dann also auch noch im übernächsten Kapitel
ein bisschen weiter mit angesprochen, was das dann ist.
Gut, jetzt habe ich hier. Genau, hier unten und nur noch entsprechend übersprungen. Referenzen,
Beispiele für Riss Computer, wie gesagt,
ursprünglich mal sowas wie Mips, inzwischen die Prozessoren,
gibt es Mips Nachfolger, gibt es noch einige,
Spark und Deck Alpha sind inzwischen auch ausgestorben,
Armprozessoren sind auch eigentlich jetzt klassische Risk-Prozessoren und eigentlich auch die,
die sicherlich am meisten verbreitet sind.
Die meisten von ihnen werden davon also diverse haben. Nicht,
weil, also sagen wir mal,
sämtliche eingebettelten Geräte sei das Smartphones,
sei das irgendwas in der Autoelektronik sah,
dass also irgendwas, was den Fernseher,
den DVD-Spieler oder Sonstiges steuert,
läuft in der Regel
mit irgendwelchen Armkernen
Gut, was dann eben noch typisch für risscomputer ist,
sämtlichen Speicherzugriff geht eben nur mit Load- und Store-Anweisungen.
Das heißt, es gibt dann eben ausgewählte Anweisungen,
wo ich dann also quasi aus dem Hauptspeicher in Register übertragen kann oder aus dem Register in den Hauptspeicher.
Was ich aber nicht machen kann, ist eben das,
was im Prinzip bei CIS geht,
was dann also auch in den nächsten Kapiteln in den Übrisaufgaben auch noch mit auftauchen wird,
ist, dass ich ihm quasi mit irgendwelchen arathmetischen Operationen auch sagen kann,
ich nehme den Inhalt von dem und dem Register und addiere beispielsweise dazu den Inhalt,
der sich unter der Speicheraddresse im Hauptspeicher befindet.
Das heißt, eben solche Operationen,
wo ich dann eben mit einem oder mehreren Hauptspeicheradressen arbeite,
zum Beispiel bei arathmetischen Operationen, bei logischen Operationen,
nicht all sowas,
geht dann eben bei Riskrechner nicht,
sondern es gibt eben einen Speicherzugriff nur mit diesen beiden Elementaren Anweisungen und alles,
was arathmetisch logische Berechnungen sind,
darf eben nur auf Registern passieren
Demzufolge habe ich dann eben auch ein deutlich registerorientierteren Befehlssatz.
Man hat eben sehr viele und sehr viel mehr Register.
Also vorhin hatte ich ja bei den x686 Prozessoren kurz gezeigt,
typischerweise als Integer-Register, acht Stück.
Das ist zumindest das, was wir dann also im gesamten
nächsten Kapitel dann also auch noch sehen werden.
Nicht bei Riss gerechnet habe ich eigentlich mindestens 32 Register,
meistens sogar noch deutlich mehr
Damit kann man dann teilweise sehr effizient so ein paar Tricks implementieren,
dass man eben sagt, wenn ich jetzt mehrere Tasks habe,
die gleichzeitig rechnen sollen. Das,
was ich also auf dem X680-Rechner im Prinzip machen muss,
wenn ein Prozess irgendwie unterbrochen wird,
ein anderer Prozess auf die CPU kommt, dort laufen soll,
dass also die Register von dem ersten Prozess gesichert werden im Speicherbereich,
die aus dem zweiten reingeladen werden.
Der fängt an zu rechnen, wenn er wieder verdrängt wird,
wird das Ganze wieder quasi zurückgeswitcht. Nicht all sowas Borschbarisk im Zweifelsfall nicht,
weil man da dann also auch sagen kann,
wenn ich jetzt beispielsweise 2056 Register habe, ein Prozess sieht logisch nur 32 Register.
Und wenn ich eben irgendwie so einen Task-Switch habe und auf einen anderen Prozess übergehe,
sage ich einfach nur, okay,
ich nehme jetzt nicht mehr die Register 0 bis 31,
sondern eben meinetwegen 32 bis 63 und rechne damit weiter und
muss dann nicht irgendwelche Sachen wieder umständlich in den Speicher schreiben bzw. entsprechend zurücksichern.
Nicht damit lassen sich dann eben sehr einfach solche Task-Switches realisieren und
der Programmaufrufe gehen dann eben auch direkt über solche Register.
Adressierungsmöglichkeiten und so weiter
Dann habe ich bei Riss-Rechnern auch nicht explizit jetzt sowas wie Flex oder ein Fleck-Register,
sondern es gibt dann eben spezielle Anweisungen,
mit denen man prüfen kann,
ob ein Ergebnis meinetwegen jetzt gleich null ist,
ob irgendeine arathmetische Operation größer null ist und das Ergebnis landet dann eben auch wieder nur in einem Register,
was dann eben entsprechend weiter behandelt wird.
Gut, vor und Nachteile. Ich habe eben keine Mikroprogrammierung mehr,
sondern die Befehle sind eben so einfach und
so effizient in der Hardware umgesetzt,
dass es letztendlich dann fest verdrahtet ist
Man hat eben sehr einfache Erderinstruktionen mit entsprechend wenig Adressierungsarten.
Pipelining basiert eigentlich darauf, dass ich sage,
ich muss eigentlich ein Riskgefehlssatz haben, sonst funktioniert das nicht.
Was man dort dann eben auch hat, ist,
dass man dann eben sagt, Cycles purnstruction gleich 1,
das heißt eben mit jedem Takt fange ich jeweils einen neuen Befehl an.
Wie das Ganze funktioniert,
wie dann auch das Pipelining funktioniert, wie gesagt,
dann im übernächsten Kapitel noch
ein bisschen mehr dazu
Nachteile von Risk-Sachen ist natürlich, dass ich sage,
ich habe jetzt sehr wenig sehr einfache Befehle,
wenn ich jetzt also irgendwie ein etwas komplexeres Programm habe,
ein bisschen größere Sachen.
Habe ich natürlich von der Anzahl der Befehle erstmal im Maschinencode
entsprechend mehr Befehle, die ich brauche, um das auszudrücken.
Das heißt, im Maschinencode ist länger. Andererseits,
da wir bei Risk-Maschinen nicht mehr anfangen, irgendwie Assembler selber zu schreiben.
Soll uns das egal sein, sondern es ist ein Problem
des Compilers, der den Maschinencode letztendlich erzeugt.
Und das Ganze funktioniert eben nur, wenn man auch entsprechend viele Register hat.
Nicht dem zuvorge, muss quasi gegeben sein,
dass ich überhaupt erstmal sowas wie einen vernünftigen Compiler da habe.
Auf der anderen Seite ist eben das,
was dann also teilweise auch an Compiler-Optimierung wirkt,
dann eigentlich auch erst durch solche Befehlsätze möglich
Gut, und eben diese gesamte Speicherhierarchie,
das heißt alles, was dieses Cashing ist,
funktioniert dann eben dann auch mit Rissgerechnen bzw. andersrum,
muss man auch sagen, damit ein Rissgerechne überhaupt funktionieren kann,
brauche ich dann eigentlich so was wie eine Speicherhierarchie,
weil das geht eben nur,
wenn ich getrennten Daten- und Instruktionsspeicher habe,
nicht nur vom normalen von Neumann-Modell her,
habe ich das nicht
Auftreten tut das Ganze dann, aber eben, wenn ich sage,
okay, wie sind Prozessoren aufgebaut und ich habe ja inzwischen nicht mehr den Hauptspeicher direkt an der CPU hängen,
sondern ich habe da Third-Level, Second-Level,
First-Level-Cash und beim First-Level-Cash, also das heißt,
bei dem spart er das direkt an der CPU hängt,
da habe ich eben genau die Unterscheidung schon zwischen Instruktionsspeicher und Datenspeicher,
die sind eben getrennt.
Gut, das ist dann eben dieser Teil Speicherhierarchie,
folgt dann quasi als letztes Kapitel dann hier noch in
der Vorlesung mit drauf
Gut, Sisk, versus Risk, wie sieht das aus,
als es aufkam, gab es also relativ viel Diskussion,
was ist denn jetzt das bessere Konzept,
wo man dann also für jeden Teil, sowohl Siskrechner,
Assourist-Rechner, also irgendwie ein entsprechende Vorteile tinten konnte,
nicht und sagen konnte, okay,
wenn ich einen normalen Compiler habe und jetzt keine Optimierung,
dann ist Sisk erst mal einfacher und Der Code wird prinzipiell erstmal ein bisschen kleiner.
Andererseits mit optimierenden Compilern, das kann ich eigentlich einen Riss deutlich besser machen,
weil ich dann nämlich genau immer weiß,
für welchen Befehl oder welcher Befehl, wie lange in der Abarbeitung braucht.
Und dadurch, dass sie dann eben entsprechend einfache Hardware habe,
ist das Ganze dann entsprechend schneller.
Inzwischen muss man sagen, die Grenzen sind da eigentlich relativ stark verwischt.
Das, was also ursprünglich Riss, Prozessoren waren,
also eben beispielsweise was wie so ein Mips, Müps-Maschine. Also auch die sind inzwischen deutlich komplexer geworden. Und auf der anderen Seite sieht es so aus,
dass wenn ich jetzt also einen aktuellen, eine aktuelle CPU nehme,
wie sie jetzt eben hier beispielsweise Laptop, Desktop-Bereich sind,
dann sind das zwar vom Befehlssatz her eigentlich riskrechner, Blödsinn-Cisk-Rechner,
aber vom internen Aufbau sind aktuelle Prozessoren eigentlich auch eher was,
wo man sagt,
die haben innen drin von der Abarbeitung eigentlich sowas wie ein Risskern und alles,
was sich also dann noch als Assembler-Befehl sehe,
wird intern schon in mehrere kleine Teile automatisch unterteilt,
ohne dass sich jetzt sowas wie eine Mikroprogrammierung dahinter stehen habe.
Nicht dadurch verwischen, da dann eben auch die Grenzen.
Und das hat eigentlich auch dazu übergeführt,
dass sowas wie X86-Architektur inzwischen oder eigentlich bis jetzt eben auch immer noch überlebt hat,
obwohl das eigentlich jetzt vom Effizienzkonzept her sich dann eigentlich Riskmaschinen sonst geschlagen geben müsste.
Gut, wie gesagt, bei Desktop-Prozessoren, da ist das Ganze sowieso vorgegeben.
Das Problem Code-Kompatibilität hatte ich ja auch schon kurz angesprochen. Gut,
und die andere Sache ist, was man da dann eigentlich
auch mal noch so ein bisschen mit reinnimmt, ist natürlich,
wofür oder für welche Art von Anwendung wird jetzt irgendwas genommen?
Und da kann ich jetzt immer sagen, gut,
wenn ich jetzt nicht explizit darauf angewiesen bin, also besonders günstigen,
kleinen, billigen Prozesse und mit wenig Leistungsaufnahme zu bauen,
sondern sagen kann, na gut,
der hängt hier also am entsprechend großen 68 Millie-Ampere-Stunden-Akku,
beziehungsweise das Ding hängt also eh irgendwie am ein paar hundert Wattnetzteil,
dann kann ich ja einfach entsprechend viel Hardware spendieren und dann wird das eben so oder so mit entsprechender Hardware schnell ausgeführt.
Nicht bei eingebetteten Prozessoren, da sieht das eben so aus,
das sind in der Regel eigentlich alles Risk-Architekturen, weil die eben wirklich inhärent,
gerade wenn man jetzt sagt, also ich habe eben irgendwo Sachen,
wo es also um energieeffizienz geht, dort deutlich mehr Einsparpotenzial,
um das Ganze dann eben entsprechend effizient umzusetzen.
Gut, ja, hier auch noch mal kurz zusammengefasst, Isar Design heute, also Restriktion wurde durch Hardware abgeschwächt. Code-Kompatibilität hatte ich auch schon gesagt,
was da dann auch noch so ein bisschen mit reinkommt,
ist dann eben noch sowas wie Emulation oder Virtualisierung als Stichwort,
dass man eben auch sagen kann,
also es gab ja auch schon mal so diverse Konzepte,
wo man beispielsweise gesagt hat,
ich habe eigentlich sowas wie einen Cisk, nein, einen Riskrechner,
der aber eigentlich einen Cisk-Satz befehlt oder einen Cisk-Befehlssatz abarbeiten kann,
das Ganze dann aber quasi dann über so eine Art Virtualisierungsschicht,
die dann eigentlich auf dem eigentlichen Prozessor arbeitet und als Benutzer kriegt man eigentlich gar nichts davon mit.
Also auch sowas ist dann als Konzept im Prinzip
noch möglich
Ich. Gut, das,
wo wir jetzt inzwischen sind, so im klassischen Desktop-Bereich,
ist dann eben Intel Architektur 64, also 64 Bild Wortgröße.
Entsprechend komplizierte Befehlsätze, wo dann eben aber auch gerade so diese Effizienz
hinsichtlich mehrere Recheneineinheiten dann eben auch noch mit ausgenutzt werden kann.
Und wo man dann eben mit entsprechenden Compilern dann also auch wirklich sehr effizient arbeitet. Gut,
dann werde ich jetzt noch mal kurz das Thema Assembler-Programmierung,
was uns dann also Ende dieser Woche beziehungsweise an die nächste Woche begleiten würden,
noch mal motivieren. Hier noch mal zwei Folien,
die also relativ am Anfang der Vorlesung aufgetaucht sind,
wo es also im Prinzip dann noch mal um diese unterschiedlichen Ebenen ging,
Kompilierungssystem, Abstraktionsebenen, nicht, wo man sagt,
also ich habe im Prinzip für ein Programm unterschiedliche Repräsentationen.
Ich programmiere das jetzt in meiner Programmierspare, Hochspare beispielsweise jetzt,
wie dann eigentlich hier jetzt also auch immer in den Beispielen dann zum Beispiel in C sind darks.
Habe dann meinen Pre-Prozessor, meinen Comparler,
der da draußen dann also letztendlich sowas wie Assembler-Code generieren kann,
habe dann eben einen Assembler, der quasi jetzt also aus
diesem Memornic-Code von dem Assembler dann eben wirklich Maschinencode macht.
Das heißt also dann eben wirklich irgendwie ein Nährprogrammen,
das heißt also ein Code-Block aus irgendwelchen Nullen und Einsen.
Dann habe ich als nächsten Mechanismus einen Linker, wo ich dann eben sagen kann,
für irgendwelche Bibliotheksfunktionen, wenn ich also beispielsweise aus Standard-IIO,
also jetzt irgendwie aus dem Print F benutze und das Ganze jetzt zum Beispiel statisch-linke,
wird das dann eben noch aus einer entsprechenden Bibliotheks,
aus einer entsprechenden Bibliothek herausgelesen und dann eben in meinen Beinerie zusätzlich noch mit eingebaut.
Nicht, dass man da dann eben entsprechend mehrere Schritte hat,
nicht an anschließend, habe ich dann eben mein ausführbares Programm,
was dann in einer nächsten Stufe, die jetzt hier nicht drauf ist,
vor der Ausführung dann also quasi über einen Loader,
eben wirklich in den Hauptspeicher gebracht wird, dort dann eben angefangen wird,
abzuarbeiten und das dann eben logischerweise dann eben diesen klassischen von Neumann-Prinzip gehorcht.
Gut, hier, das Ganze dann eben nochmal mit so ein bisschen Code-Beispiel hinterlegt,
beziehungsweise dann also hier nochmal eine etwas andere Folie jetzt,
also Forst im Buch von Patterson und Tennessee,
wo ich dann eben im Prinzip auch genau die gleichen Sachen eben auch nochmal sehe,
als Vergleich eben jetzt hier in C sind DAX,
also mein High-Level-Programm, hier dann also irgendwie sowas wie so ein Assembler-Code,
in diesem Fall ist das jetzt hier natürlich jetzt wieder ein Mips-Code,
nicht dann also irgendwie nach dem Assembler dann also sowas wie Binär-Code,
also irgendwelche wilden Folgen von 0 und 1. Das wird dann
eben quasi von der Hardware entsprechend interpretiert als ein Befehl,
wo dann eben beispielsweise hier entsprechend irgendwas
mit gerechnet wird
Gut, jetzt natürlich die Frage,
warum werde ich sie jetzt, also die nächsten anderthalb Wochen,
somit sowas wie Assembler-Programmierung quälen, langweilen, wie auch immer.
Nicht, warum braucht man das noch? Weil typischerweise muss man jetzt mal sagen,
gut, also wir als Informatiker möchten natürlich nicht direkt in Assembler programmieren,
würde ich auch nicht wollen,
sondern in der Regel arbeitet man natürlich in irgendwelchen Hochsprachen.
A, weil es also deutlich produktiver ist,
weniger fehleranfällig ist. Inzwischen die Compiler bzw. die Werkzeuge,
die man da zur Verfügung hat,
also wirklich so gut sind
Das ist das, was dort also in der Regel rauskommt, also auch deutlich besser ist als irgendwelche handoptimierter Assembler-Code.
Nichtsdestotrotz, und deswegen kommt das jetzt eben hier noch auf den nächsten 150 Folien, ist im Prinzip so ein Grundwissen über Abarbeitung von Assembler-Code. Insofern wichtig,
dass man natürlich also wissen sollte,
wie zum einen jetzt also auf Maschinenebene wirklich das Programm abgearbeitet werden kann,
beziehungsweise, dass man da dann eben auch Synchronverständnis dafür bekommt,
also was kann passieren bei Fehlern im Programm,
wenn das Ganze jetzt nicht richtig funktioniert,
nicht worauf basieren diese Fehler vielleicht nicht und vieles davon erklärt sich eben letztendlich dann auch nur über Assembler-Code,
die ich und spätestens,
wenn ich also in meinem laufenden Programm das De-Barging jetzt nicht dadurch mache,
dass ich also in jeder dritten Zeile,
also mit irgendeinem Print-Effen ja also irgendwelche Variablen ausgeben lasse,
sondern das also wirklich mit einem De-Barg einzeln ansehe und dort also vielleicht irgendwo durchlaufen lasse.
Nicht also spätestens dann ist man also teilweise in dem Bereich,
wo man so ein bisschen Verständnis für den Assembler-Code aufbringen muss.
Dann ist das Ganze natürlich noch wichtig,
wenn man jetzt sagt, okay, bei der Programmabarbeitung,
warum laufen bestimmte Programme effizient oder ineffizient ab?
Nicht, dass man da dann einfach so ein bisschen sieht,
was kann dort
alles schieflaufen
Wie baue ich vielleicht meine Datenstrukturen auf,
dass die vom Compiler dann eben auch effizient auf die Hardware abgebildet werden,
beziehungsweise im Gegenbeispiel, nicht wenn irgendwie ein Code,
sich mal schneller läuft als irgendwie ein anderer Code,
was ist dort schiefgelaufen,
was habe ich dort also quasi ineffizient gemacht.
Und logischerweise brauche ich natürlich Grundwissen, um Assemblerprogrammierung,
wenn ich also irgendwann mal anfange,
wirklich ein Compiler zu programmieren
Das heißt also im Compiler-Bau, wo ich dann eben letztendlich
den Maschinencode erzeugen soll, muss ich das natürlich entsprechend wissen.
Beziehungsweise auf den untersten Ebenen hinsichtlich jetzt eines Betriebssystems, das heißt,
wenn man jetzt also wirklich mal in die Verlegenheit kommen sollte,
einen Gerätetreiber zu schreiben oder hinsichtlich der Systemprogrammierung jetzt vom Betriebssystem.
Dort irgendwie was macht. Also auch dort ist es dann
teilweise noch üblich, sowas wie Assembler zu machen.
Deswegen jetzt hier als in Anführungszeichen. Lernziele. Also,
was soll so ein bisschen hängen bleiben? Grundverständnis von
der Programmausführung. Sprich, wir werden arathmetisch-logische Operationen umgesetzt.
Wie kriegt man Crontrollstrukturen hin? Also sprich, Verzweigung,
Sprünge, bedingte Sprünge, Schleifen. Wie sehen Datenstrukturen aus?
Einfache Datenstrukturen oder eben sowas wie irgendwelche Race oder Records.
Wie funktionieren Funktionsaufrufe
Beim X86 dann quasi über den Stack. Wie übergebe ich Parameter?
Nicht in der Programmierung haben wir Call by Value Call by Reference.
Was passiert eigentlich dahinter jetzt quasi in der Hardware?
Was muss passieren, um zu sagen, ich muss übergebe
ein Parameter bei Value oder ich übergebe den bei Reference.
Wie kriegt man rekursive Funktionen beschrieben? Und darf man dann eigentlich
noch ein bisschen abgeladet dann eben solche Sachen, wie sieht die Speicherverwaltung aus?
Wie kriegt man vielleicht dann sowas wie objektorientierte Konzepte mit hin.
Gut, dazu zwei Minuten noch für die nächste Folie.
Wie gesagt, Speicherverwaltung, Speicher ist dann aufgeteilt eigentlich in mehrere Regionen.
Auch das hatte ich schon mal kurz vorgestellt,
was das dann im Einzelnen ist,
folgt dann also auch am Ende dieses Kapitels dann nochmal im kurzen Beispiel.
Nicht, dass ich dann eben sage,
ich habe typische Bereiche mit Code, mit Funktionsbibliotheken,
dann eben das, was wir als Deck benutzen und so weiter.
Und dann eben Gerade hinsichtlich der Speicherverwaltung kommt dann, Asio,
das ist jetzt also auch seit dem letzten Jahr Rechnerstrukturen so ein bisschen neu dazugekommen,
nochmal so ein bisschen ein Kapitel, wo es dann eigentlich um Programmierfehlungen und Sicherheitslücken geht nicht.
Das heißt also, wie kann es eben passieren,
dass jetzt irgendwelche Schadprogramme, irgendwelche Viren letztendlich wirksam werden,
nicht weil dort eben beispielsweise hinsichtlich der Speicherverwaltung irgendwelche Effekte ausgenutzt werden,
dass die dann also auf einmal irgendwelche Speicherbereiche überspringen.
Also auch dazu folgen dann noch einige kleine Beispiele, was ich esse.
Erst mal prinzipiell dahinter verbirgt am Ende dieses Kapitels.
Gut, letztendlich werden wir uns jetzt hier bei Rechnerstrukturen dann
aber eben wirklich nur außer auf sehr einfache Konzepte beschränken.
Also wie gesagt, der Assembler in der etwas älteren Version,
32 Bitcode, mit auch nur einem einzigen Datentyp,
nämlich einem Long-Intager von 32 Bit und eben auch bei den Befehlen,
werden jetzt hier vielleicht also irgendwie zehn Befehle maximal,
na oder größenordnungsmäßig benutzt.
Das heißt also eigentlich eben auch nur ein
entsprechend sehr kleiner Subset von den eigentlichen Befehlen.
Damit werden dann aber eben die Grundkonzepte soweit kurz vorgestellt.
Ansonsten gibt es hier natürlich noch unendlich viele Themen,
die dann einfach jetzt hier also aufgrund der Zartbeschränkungen jetzt nicht
weiter behandelt werden können
Gut, damit dann Schluss für heute.
Danke für die Geduld und am Freitag dann entsprechend weiter
an dieser Stelle