Gut, angefangen hatte ich ja am Mittwoch im Prinzip noch mit der Idee der Speicherorganisation,
wobei man jetzt eben sagen kann,
generell auf den meisten Systemen ist es so,
dass also der Speicher eigentlich beidweise adressierbar ist.
Man hat aber jetzt innerhalb eines Speicherworts,
was natürlich also deutlich größer ist als ein Bike,
ja dann irgendwo seine Daten untergebracht.
Und die Frage ist jetzt eben, wie die einzelnen Bytes
jetzt quasi innerhalb dieses Speicherworts wieder angeordnet werden.
Das heißt, unter welchen Adressen greife ich dann eben auf
welche Bytes zu
Und da gibt es grundsätzlich erstmal zwei Möglichkeiten, die da Big Indien oder Little Indien heißen.
Das ist im Prinzip aus Gullivers Reisen übernommen. Die Begriffsbildung. Im Prinzip,
also kann man einfach immer nur sagen, okay,
also Big Indien heißt eben, dass das höchstwertigste Byte,
also das heißt, das most signifikant worden,
also das heißt, das,
wenn ich jetzt eine Zahl habe,
mit den höchstwertigsten Stellen an der kleinsten Adresse im Speicher
liegt und auf den folgenden Speicheradressen dann eben die immer kleiner werdenden Byte und das niederwertigste Byte hat dann eben letztendlich die höchste Speicheradresse.
Das wäre Big Indien, das heißt, man liest die großen Werte zuerst.
Little Indien ist genau das Umgekehrte. Da hat eben das
Most Significant Barry höchste, das liest Significant Barty niedrigste Adresse.
Letztendlich findet man beides auf aktuellen Computersystemen, X-86-Architektur,
hat man typischerweise Little Indien, auf Workstations.
Gut, die es inzwischen eigentlich kaum noch gibt.
Aber im Prinzip ist es dann, hat man Big Indien,
MacHard, eigentlich auch noch traditionell Big Indien.
Darstellung. Hier ist das Ganze jetzt also auch nochmal so
ein bisschen grafisch gezeigt. Also ich habe jetzt hier meine Speicheradressen.
0,1, 2, 3, 4 und so weiter.
Habe hier 1,32 Bit Word. Und beim Big Indien-System
würde ich eben halten mit deren kleinsten Adresse,
die von der Stellenwertigkeit her Höchsten war jetzt eben zuerst lesen
Und dann eben hier so rumgehen beim Little Indian System ist das quasi dann eben genau umgekehrt.
Das heißt, die einzelnen Bytes sind dann entsprechend die Wahls vertauscht.
Beide Varianten kann man eigentlich begründen. Wenn ich jetzt sage,
ich habe sowas wie ein String,
wo ich dann eben wirklich von die einzelnen Zeichen der Reihe nachlese,
dann entspricht das eigentlich eher so ein Big Indian System.
Wenn ich sage, Ich gucke mir das an,
wie da eigentlich jetzt die Wertigkeit innerhalb der Zahlen ist, dann ist eigentlich Little Indian, das in Anführungszeichen logischere. In der Regel kann man immer sagen,
kriegen wir davon also jetzt nicht so sonderlich viel mit,
weil da gibt es dann eben entsprechende Umrechnungen beziehungsweise Konvertierungsfunktionen,
werde ich gleich auch noch mal kurz vorstellen,
weil das sind dann nämlich genau die Probleme, die einem begegnen,
wenn man jetzt also wirklich mal so ein bisschen weiter unten auf der Systemebene guckt,
wie also irgendwie Daten von einem System zum anderen übertragen werden,
weil da kann es dann nämlich schon relativ häufig vorkommen,
dass man also auf einmal irgendwie so eine Byte Weise Vertauschung zwischen Big- und Little-Andien-System vornehmen muss,
beziehungsweise man empfängt da irgendwelche Wert und wundert sich,
dass da so irgendwie komische Zahlen drin stehen,
was da nicht passen kann.
Dann ist dann meistens sowas immer der Fall. Hier auch nur nochmal Beispiel,
wenn man sich das also vorstellen muss,
jetzt also hier für die Zahl 15.213. Binär kodiert,
quasi als dieser 16 Bits dringen,
Hexer detzimal dann also 3 B-6D und auf einem Little Indien-System würde das dann quasi so im Speicher stehen,
auf einem Big Indien-System würde das dann so im Speicher stehen.
Nicht also jeweils die Zahlen in ihren Hexwerten unter den jeweiligen Bartadressen.
Hier auch nochmal angedeutet, wie das gilt dann im Prinzip
genauso dann eben auch für negative Zahlen. Das heißt,
wenn ich jetzt also hier Zweierkompliment zahlen, jetzt hiervon bilden würde,
und das hier reinschreiben würde, hätte ich genau das gleiche Ergebnis.
Und was hiermit dann eigentlich nur noch angedeutet ist,
ist dann eben die Tatsache, okay, hier ist die X686-Architektur,
also das, was jetzt hier mit Linux bezeichnet ist,
das gilt aber genauso für Windows-Systeme, im Prinzip noch jetzt,
also für 32 BitWord-Breite so aufgeschrieben,
nicht also bei 64 Bild,
also was dann zum Beispiel bei den Alpha-Prozessoren der Fall ist,
habe ich dann eben
die entsprechende Erweiterung dann noch um die höchstwertigsten weit
Wo man sowas dann auch immer rein praktisch sehen kann,
ist, also und wirklich, wenn ich mir jetzt also irgendwie Datenstrukturen ansehe,
dann ansehe, was macht der Compiler draus und dann sehe,
wie ist das Ganze dann wirklich irgendwo im Code abgelegt.
Hier ein Beispiel, wo ich also beispielsweise jetzt in C,
also so ein Breakout definiere für irgendwie ein Datenbank von Mitarbeitern,
was weiß ich auch immer nicht,
also es gibt einen Angestellten,
der hat eben irgendwie ein Alter,
ein Gehalt und eben irgendwie einen Namen mit zwölf Stellen,
was das auch nur bedingt realistisch ist,
aber okay
Und da gibt es jetzt eben beispielsweise von diesem Angestellten,
jetzt also hier ein Jimmy mit folgendem Rekord,
der ist eben 23 Jahre alt, verdient also irgendwie 50.000, was auch immer.
Und heißt eben Jim. Smith. Und gut,
interessant sind es eben hier die Zahlen, also sprich die 23,
beziehungsweise die 50.000 wären dann eben hexadezimal geschrieben, 0017,
beziehungsweise mit entsprechenden 4-0,
0 noch davor und die 50.000 dann C,
3-5-0,
dann müssten aber natürlich dann logischerweise auch noch Nullen davor
So, und wenn man sich das jetzt wirklich mal ansieht,
rein praktisch, was also ein C-Compiler da draus macht,
hier für eine i386-Architektur,
und dann also quasi mit einem Object-Damens nicht mehr ansieht,
wie die entsprechende Datenstruktur dann also dort abgelegt ist,
dann sieht man also hier die 17,
also sprich das Alter 320, also sprich 16 plus 7,
und hier eben die 15.000 die 50.000 Little-Andien-Format, das gleiche Programm übersetzt,
auf einer Spark 32-Bit-Architektur, sieht man dann eben,
dass also hier die Zahlen nämlich genau andersrum kodiert sind,
das heißt also auch hier bei den 50.000 ist,
habe ich dann eben quasi jetzt immer hier die Bite dann eben wirklich vertauscht,
das heißt,
das niederwertigste Bite ist dann also hier beim Big-Andien-Format eben wirklich am weitesten rechts stehen,
da dann das zweite und das,
was dann eben hier zu einem höherwertigeren Bites geht,
ich hier unter den kleineren Adressen, bei dem anderen System.
So, wo sowas dann eben auch noch mit auftaucht,
ist dann eben bei der sogenannten Networkbite-Order, nämlich,
wenn ich jetzt wirklich Daten zwischen Rechnern auch übertrage,
prominentestes Beispiel dafür ist, also das Internetprotokoll,
wo Big-Anien-Formaten nämlich angewendet wird.
Jetzt habe ich gesagt, bei X86-Rechnern, also sprich,
wenn ich jetzt hier irgendwie Kot von dem Ding hier nehmen würde,
sind sämtliche Zahlen oder alles, was da kodiert wird,
eben erstmal Little-Andien-Format intern vorhanden.
Das heißt, beide Übertragung,
wenn ich eben hier mit Daten irgendwie auf die Netzwerkschnittstelle gehe,
muss das Ganze entsprechend irgendwo konvertiert werden.
Und Beispiel dazu finden sich dann eben hier zum Beispiel,
also auf Linux-System in Makros, net A, net-i-n.h.
Dahinter findet man nämlich quasi einen folgenden Code.
Den habe ich ganz am Anfang schon mal, als es
um die logischen Operationen ging, kurz vorgestellt. Nicht,
wo man an mich genau so eine Funktion namens beispielsweise bei Swap 32 hat,
wo ich also im Prinzip jetzt, also hier ein Argument X nehme,
dann jetzt hier immer irgendwelche Bytes ausmaskiere.
Das ist dann also quasi X. Und eben dieser entsprechende Ausdruck
. Und das dann eben mit Schiebeoperationen um 24 Bittern
eben an die entsprechende Stelle in meinem Ausgabestring schiebe.
Das mache ich dann also quasi jetzt mit allen vier Bytes in einem 32 Bit Ward.
Und hier habe ich dann eben mit einem mitweisen oder als Verknüpfung
dann wieder das Zusammenbauen der entsprechend vertauschten Teile zu dem richtigen Wort.
Und so war es wohl dann eben beispielsweise dort als Funktion
mit eingesetzt und irgendwo in den Systemeinstellungen gibt es dann eben so was,
wo dann also quasi im Compiler dann per Pre-Prozesse dann einfach abgefragt wird.
Also wenn Bite Order Little Andien ist,
dann wird als Konvertierungsfunktion Network to Host Long Byteswap 32 aufgerufen,
wenn das nicht der Fall ist, dann ist Network to Host Long quasi leer.
Das heißt, oder ist das eben so definiert,
dass dann eben wirklich nur X zurückgegeben wird, dann passiert eben genau diese Konvertierung nicht.
Und deswegen kriegt man das, also sagen wir mal wieder,
als Benutzer nichts weit davon mit, weil automatisch,
wenn ich ihm das jetzt nehme und das,
was ich dann also, gut,
das ist jetzt natürlich jetzt hier nicht Network,
aber was ich hier gemacht habe,
eben wirklich den gleichen Code nehme und eben einmal auf dem Little-Andien und einmal auf dem Big-Andien-System übersetze,
dass dann eben solche Mechanismen beispielsweise greifen würden,
die mir das automatisch umsetzen
Gut, ja, noch mal kurz geschrieben.
Also wie gesagt, wie das funktioniert, könnte man sich
dann also beispielsweise aus dem Buch von Brian und Helleron raussuchen.
Nicht, dass dann also ein Programm, was dann also wirklich datenbeidweise ausgibt.
Nicht, wenn man das dann also nimmt,
übersetzt und dann quasi jetzt irgendwelche Daten dort übergibt und dann ausgeben lässt,
würde man dann eben auch sehen, wie das also entsprechend im Speicher abgelegt ist.
Nicht, weil das dann also hier dann auch quasi jetzt
wirklich über Fortlaufende Weite Adressen rausgelesen wird. So,
eine andere Problematik, die dann eigentlich auch noch bei der
Zuordnung von irgendwelchen Werten oder Zahlen auf Speicherworte vorkommen kann,
ist das Problem des Alignments. Da geht es,
grob gesagt, also einfach erstmal darum, dass ich sage,
ich habe jetzt hier also meinetwegen mein Speicherwort, von,
in diesem Fall jetzt nicht vier,
sondern acht Byte und habe dort dann eben auch ein acht Bytewort speichern, also ein 64-Bit-Wert.
Wenn das Ganze aligned wäre,
was man sich eigentlich erst mal intuitiv hier erstmal wünschen würde,
sieht das eben so aus,
dass also quasi ich mit einer Speicheradresse eben auch genau dieses eine Wort so eins zu eins aus dem Speicher auslese.
Aber auch da kann man sagen,
das ist letztendlich erstmal nicht per Definition Gott gegeben,
sondern ich kann mir eben auch vorstellen,
dass also quasi jetzt mal 64-Bit-Wort nicht bei der Speicheradresse 8 beginnt,
sondern bei der Speicheradresse 12,
demzufolge dann eben bis zur Adresse 19 geht und physikalisch im Speicher dann eigentlich unter zwei Adressen ist,
nämlich ein Teil, sprich hier die ein, vier Byte,
dann ab Adresse 16 zu finden sind und die anderen vier Byte dann eben ab,
aber dann eben von 15 bis 12 zu finden sind.
Und sagen wir mal, auch solche non-aligned Speicherzugriffe sind im Prinzip möglich.
Auch da ist es letztendlich wieder von der Hardware oder der Hardware-Architektur und dem Betriebssystem abhängig,
ob sowas jetzt erlaubt ist oder nicht erlaubt ist.
Nicht bei X86 Architekturen, ist das im Prinzip erlaubt.
Bei anderen Architekturen, also zum Beispiel bei Spark-Rechnern eben nicht,
da würde es dann also quasi zu irgendwelchen Ausnahmebehandlungen und Programmabbruch kommen. Nichtsdestotrotz,
was man sich natürlich dann also auch erst mal vorstellen oder logischerweise natürlich denken kann,
man möchte sowas nicht haben,
weil das natürlich einfach von der Performance schon mal schlecht ist,
weil ich möchte eigentlich nur ein Wort lesen,
Man muss aber mehrere Speicherzugriffe machen und dieses Wort wird dann
eben aus diesen Speicherzugriffen dann auch noch über Maskierung und irgendwelche Schieberoperationen dann erst letztendlich wieder zusammengesetzt.
Aber das ist dann eben auch so ein Tretoff, wo man sagen kann,
worauf optimiere ich mein Programm oder was sehe ich als wichtiger an?
Wenn ich sage, ich habe jetzt hier zum Beispiel jetzt
irgendwie mehrere kleine Werte und spare ich jetzt beispielsweise jetzt nicht ein ganzes 64-Bitwort,
sondern also nur 32 Bit-Entager ab,
spendiere ich da für jeden eine eigene Speicheradresse,
die dann aber vielleicht nur zur Hälfte gefüllt ist und verschwende Platz.
Oder sage ich eben, okay,
also ich packe das hier rein und wenn da noch was frei ist,
kommt dann eben der nächste Speicherwert dann gegebenenfalls dort rein,
was dann eben dann zu solchen Miss Aligned Sachen führen kann.
Und Geschwindigkeits- oder Performance-mäßig ist eben so ein Allined-Zugriff eigentlich günstiger,
nicht wenn ich das Programm jetzt möglichst klein halten will oder möglichst kompakt im Speicher ablesen will,
dann kann es eben sinnvoller sein, auch sowas zuzulassen.
Gut, dann auch nur noch abschließend Anmerkungen zum Speicher,
Memory Map, also da wiederholen sich jetzt eigentlich relativ viele Sachen,
die ich also alle schon mal angesprochen hatte,
grundsätzlich von der Idee her kann man immer sagen,
die CPU sieht halt jetzt wirklich oder kann jetzt adressieren bei 32 Bild-Adresse 4GB Hauptspeicher bei aktuell 64 Bildadresse deutlich mehr.
So viel Speicher wird niemand also irgendwo in seinem Rechner haben.
Das heißt also, in der Regel hat man eigentlich sowieso keinen voll ausgebauten Speicher. Und das,
was sie unter Memory Map dann also versteckt,
ist dann also eben die Zuordnung,
wie jetzt wirklich benutzte Adressen dann auch,
oder ja,
also der benutzte Hauptspeicher Adressraum dann eben auch auf den
physikalischen Hauptspeicher oder gegebenenfalls dann eben auf virtuellen Speicher abgelegt wird
Ein virtueller Speicher, das ist etwas,
was dann also am Ende der Vorlesung auch noch mit folgen wird,
wenn es dann also um Cash-Speicher und Speicherhierarchie,
unter anderem dann eben mitgeht,
ist dann eben auch die Idee, dass man sagt,
ich habe gegebenenfalls jetzt durch die Programme mehr Bedarf anspare ich als das,
was ich physikalisch an Hauptspeicher habe und habe dann eben einen Mechanismus,
dass Teile des wirklich von den aktuell benutzt,
von den Programmen benutzten Hauptspeichers
dann eben auf die Festplatte ausgelagert werden
Inzwischen bei Hauptspeichergrößen von 4GB minimal und typischerweise,
also beim Einsteiger- oder Standardsystem, so 8GW oder sowas,
betrifft uns das eigentlich nicht mehr,
weil wenn man jetzt nicht gerade,
also irgendwie Videoschnitt macht oder also irgendwie sehr aufwendige Fotobearbeitung,
dann werden meistens die Programme also deutlich weniger Speicher benutzen als das,
was wirklich physikalisch verbaut ist.
In der Frühzeit der Computertechnik als Speicher,
sagen wir mal, entsprechend teuer war,
war es eben
wirklich gang und gäbe
Dass man ihm deutlich weniger Speicher hatte und eben relativ viel oder
relativ häufig erst vorkam, dass eben irgendwie Speicher ausgelagert wurde.
Nicht, dass es dann eben auch etwas,
was da dann noch mit rein passt, hier auch nochmal als Anmerkung,
weil ich habe das also hier nämlich auch immer laufen,
dann kann man das also dann auch nochmal sehen.
Also hier ist zum Beispiel zurzeit von dem Rechner benutzt
2,5 Gigabyte von den verbauten 8 speichern und das,
was ich jetzt zur Zeit an wirklich von den Programmen her belege,
ist also dieser Use-Teil, der also dann auch nochmal deutlich weniger ist.
Den Rest genehmigt sich dann also schon das Betriebssystem nochmal so als Zwischenspeicher,
um jetzt also irgendwelche einen Ausgabeoperationen zu puffern oder irgendwas zu machen.
Und gut, wenn ich jetzt also hier nicht anfange,
also wirklich aufwendigere Sachen zu machen oder eine virtuelle Maschine zu starten,
dann tut sich hier also erstmal noch nicht so sonderlich viel.
Nicht gut, und je nach Art des Betriebssystems kann man
sich das dann eben Anzeigen lassen, beziehungsweise da entsprechend untersuchen.
Gut, ja, also Memory Map, eben nochmal wirklich jetzt
diese Zuordnung zwischen Adressraum des Prozesses zum physikalisch vorhandenen Speicher.
Ich meine, praktisch sieht das dann eben so aus,
wie also auf diesen Folien von dem Armprozessor.
Das hatte ich also im letzten Kapitel ja schon kurz mit drin. Nicht,
wo man dann also im Prinzip jetzt über beispielsweise Aufteilung von Adressleitungen sagen kann, okay,
also ich habe in meinem Adressierbaren Raum nicht nur Rahmenspeicher,
sondern ich habe vielleicht auch Rahmenspeicher
Und ich habe eben vielleicht auch ein Ausgabegeräte, also dieses Memory Mapd IO, das hatte ich ja auch schon kurz angesprochen.
Nicht also auch das wird in der Regel für Schnittstellen entsprechend oft und entsprechend häufig benutzt. Wie gesagt,
diejenigen, die jetzt schon das Praktikum machen,
also da wird das dann eben auch zum Beispiel benutzt,
um jetzt in so einem kleinen 16-Bit-System,
wo man dann im Prinzip dann 64 Grad Adressen zur Verfügung hat,
eben nicht nur Hauptspeicher hat,
sondern eben auch noch irgendwelche Teile für Betrieb System und eben auch irgendwelche einen Ausgabebereich, jetzt für irgendwelche dort vorhandenen Schnittstellen.
Nicht aber eben auch sonst liegt, ist so ein Memory-Map-Mechanismus, also durchaus gang und gäbe. Hier noch in deren,
sag mal, etwas eltbächeren Anwendung, wenn ich jetzt also irgendwelche Windows 95,
98 oder sowas nehme, nicht wo man jetzt sagt,
okay, also ich habe jetzt,
also hier mit einem 32-Bit-System ein 4GB-Adressraum und hier im unteren Bereich,
also von den Adressen 0 bis 0010 FFF 0,
also sprich innerhalb der ersten ein Megabyte, liegen dann auch Sachen,
also ganz historisch irgendwie so aus der Discorporating-System-DOS-Zeit,
wo also im Prinzip also Coach zum Booten vorhanden ist,
beziehungsweise für ein paar einfache ein Ausgabegeräte.
Nicht dann hat man typischerweise ungefähr zwei Gigabyte für Anwendungen und
hier oben dann im in unserem oberen Bereich,
dann also 1 Gigabyte, Adressraum,
eigentlich jetzt für den Betriebssystemkern irgendwelche Treiber und dann eben nochmal 1 Gigabyte-Adressraum,
der dann eben auch noch für gemeinsam genutzte Anwendungen benutzt wird.
Ja, also die Frage war jetzt,
Im Prinzip bei Windows-Systemen, also 32-Bit-Systemen,
dass man da eben jetzt wirklich von der Adressierung dann eben auch
keine 4 Gigabyte adressieren konnte, sondern in der Regel auch weniger.
Und gut, da muss ich zugeben,
habe ich jetzt also irgendwie selber jetzt keine Programme geschrieben,
weiß jetzt nicht, warum es da jetzt eine Assis von seinen der Programmierung jetzt,
also für den Programmierer da schon irgendwo eine Einschränkung gibt,
weil eigentlich dieses hier ist ja die Art und Weise, wie das Betriebssystem das verwaltet.
Ich habe bei mir als Programmierer,
kann das eigentlich jetzt erstmal weil ich habe ja sowieso noch einen Abbildungsmechanismus,
den mir jetzt quasi meine Programmadressen irgendwo hier rein abbildet und letztendlich jetzt für meinen von mir geschriebenes Programm habe ich sowieso noch knapp zwei GB,
also zumindest laut dieser Folie, dann zur Verfügung,
warum da jetzt also nicht der gesamte Adressraum jetzt also für den Programmierer dann schon zur Verfügung stand, weiß ich jetzt auch nicht.
Ich vermute, dass da dann aber im Prinzip auch noch irgendwelche Mechanismen mitgriffen,
dass dann also auch im Zweifelsfall irgendwo so aus der Frühzeit der Computertechnik,
also bestimmte Adressbereiche sowieso ausgeblendet waren
Ich habe einen Arbeitsmeister, den man betrachten kann,
dann auch mit mir, wenn alles freut.
Ah ja, gut, wieder was dazu gelernt.
Also hier an Merkungen beziehungsweise Ergänzung war dann eben auch noch,
also der Speicherbereich der Grafikkarte oder des Grafiksystems,
wo da dann eben entsprechend auch noch mit eingeblendet und
das kann dann natürlich dann da auch nochmal irgendwie unter anderem
eine Rolle mitgespielt haben
Gut, hier dann eben auch nochmal,
wenn man sich das also quasi jetzt irgendwie unter Windows angeguckt hat,
nicht,
also von einem Systemmanager konnte man sich dann ja eben ansehen lassen,
wie es also hier die Adressbereiche dann aussehen,
wo man dann im Prinzip solche langen Scrollbahnlisten bekam,
wo dann also quasi jetzt hier entsprechende Adressen drin waren und
wo dann eben hier also auch schon mit abgebildet war,
also jetzt in die Adressen,
wie dann Asitava dann die Systemkomponenten darüber dann eben
auch noch mit adressiert werden konnten
Gut, hier dann beispielsweise dann eben noch irgendwelche einen Ausgabesspeicherbereiche.
Also auch da ist es dann eben so, dass die dann in den vorhandenen Adressraum mit eingeblendet werden.
Letztendlich sind das aber natürlich immer Sachen oder Mechanismen,
wo man dann sagen kann, okay,
das ist dann eben wirklich vom Betriebssystem von der Programmierumgebung,
die ja dann also quasi diese Mechanismen dann also bereitstellt, letztendlich mit abhängig.
Und inzwischen kann ich ja immer glücklicherweise sagen, gut,
da habe ich dann eben irgendwo meine Dadurch,
dass ich eigentlich in der Regel,
wenn ich jetzt nicht gerade irgendeinen Treiber für irgendwas schreibe,
habe ich da eigentlich sowieso keinen direkten Zugriff,
also auf solche Adressbereiche,
wo jetzt also quasi irgendwelche Systeme reingemappt werden,
sondern da ist der Zugriff auf irgendwelche Geräte in der Regel
dann ja eben nur über irgendwelche Systemaufrufe möglich,
die das dann also quasi dann wieder über den Betriebssystemkern
eigentlich kanalisieren
Gut, hier auch nochmal kurz angedeutet, später dazu mehr, Speicherhierarchie. Nicht,
dass es eben nur nochmal zusammengefasst eben, wo du jetzt diese Grundidee,
dass man eben sagt, gut, also das,
was sie insgesamt jetzt eigentlich als Speicher zur Verfügung habe und da teilt sich dann eben von sehr wenig Speicher,
den ich dann aber sehr schnell zugreifen kann, nämlich das,
was jetzt wirklich an Registern im Prozessor dran sitzt,
über irgendwelche S-Rahmenspeicher,
die dann also quasi als Cashes auf dem System verwendet werden.
Ich hatte ja in der letzten Woche dann eben auch schon kurz vorgestellt,
also Unterschiede zwischen statischem Rahmen und dynamischen Rahmen,
dann eben dem eigentlichen Hauptspeicher, das, was der Rahmen ist.
Und dann eben das,
was als virtueller Speicher gilt und quasi und sowas wie Local Disks,
beziehungsweise inzwischen kann man dann eben auch noch sagen,
gut,
eigentlich sowas wie irgendwelche Netzwerkspeicher oder verteilten Speichersysteme
Teilweise irgendwelche Webservices kann man da dann
eben zumindest rein logisch dann eben auch noch mit dazunehmen.
Ansonsten, also die Folie hier ist quasi auch aus dem
aktuellen oder der aktuellen Auflage von Brian und Ohhala Roron.
Nichtsdestotrotz ist die Folie natürlich dann doch schon ein paar Jährchen älter.
Ich war also inzwischen hat man also mindestens drei,
also in zumindest aktuellen Prozessoren, also mindestens drei Ebenen,
Cashes und also auch Level 2 ist dann eigentlich auch schon Onship und
Da kommt dann eben nochmal was dazwischen. Aber wie gesagt, dazu, vor später noch so ein bisschen mehr. Gut,
wie gesagt, die Grundidee von diesem Cash-Speicher,
hier auch noch mal ganz grob skizziert, ist eben,
dass ich sage, wenn ich jetzt hier mein Chip oder meine CPU habe,
dass ich jetzt eben mit irgendwelchen Rechenoperationen bzw. mit dem Zugriff auf das eigentlich laufende Programm,
wo ich ja immer sage, ich muss Befehl holen,
Befehl dekodieren, nach dem von Neumann-Konzept liegt das ja auch alles immer,
Hauptspeicher, dass ich das eben alles eben nicht dadurch mache,
dass sie ja sie hier auf den wirklich dära am Hauptspeicher zugreife,
sondern eben sowohl die,
das aktuell abzuarbeitende Programm mit seinen Befehlen als auch das,
womit ich da also jetzt an Daten rechne,
eben hier lokal in irgendwelchen Cashes habe,
beziehungsweise bevorzug dann also im direkten Zugriff und mit voller Prozessorgeschwindigkeit dann eigentlich auf irgendwelchen Registern rumrechne.
Und nur wenn ich ihm jetzt Daten anfrage bzw. im Programm jetzt also irgendwelche Teile habe,
die jetzt eben nicht in den Cashes liegen,
was dann eben auch Über mehrere Ebenen quasi geht, dann findet wirklich ein Hauptspeicherzugriff statt.
Eben die Kunst bei dem Ganzen ist dann eben letztendlich zu entscheiden,
nach welchen Kriterien sortiere ich die Daten in den Cash,
beziehungsweise wenn der Cash voll ist und ich habe jetzt irgendwie was anderes an Daten, was schmeiße ich raus.
Und da wird dann eben auch relativ viel an Aufwand betrieben beziehungsweise also auch an Protokollen dann mit umgesetzt,
um dafür eben zu sorgen, dass Aus Sicht der CPU quasi immer alles,
was sich an Daten benötige und auch alles,
was ich an Instruktionen benötige, um jetzt mein aktuelles Programm abzuarbeiten,
eben immer und ständig hier im Cash liegt.
Und man da dann eigentlich direkt drauf arbeitet. Gut,
bei der Art und Weise, wie der Cash dann quasi organisiert ist,
das heißt, wieder angesprochen wird,
wie das überhaupt erst mal funktioniert mit dieser Idee, dieses schnellen Zugriffs,
das wird dann eben später noch ein bisschen genauer beschrieben.
Also hier erst mal nur von der Idee her,
dass man einfach erst mal weiß, okay,
letztendlich ist das für die CPU oder für den Programmierer quasi transparent.
Das heißt, in meinem Programm sage ich jetzt einfach nur,
ich habe jetzt irgendwelche Werte oder ich hole mir jetzt eben Sachen aus dem Speicher und schreibe dahin.
Aber ob dieser Speicher dann nachher physikalisch hier ist oder ob das First oder Second Level Cash sind,
Das macht eigentlich für die Programmierung keinen Unterschied,
sondern das sind dann eben nur Sachen,
wo sich dann eben entsprechende Hardware-Einheiten darum kümmern,
beziehungsweise wo sich dann eben das Betriebssystem darum kümmert,
wie das Ganze jetzt eben zueinander eingeblendet wird.
Gut, ja, das habe ich hier eigentlich soweit schon jeweils groß skizziert.
Wie gesagt, genaueres Volk dann eigentlich in dem Kapitel 17. Gut,
dann kommen wir jetzt eigentlich wieder zu diesem zentralen Eigenschaften, Instruction-Set-Architecture.
Das heißt, ich habe ja einen Prozessor,
der soll irgendwo ja seine Befehle abarbeiten und bei der Art und Weise,
wie diese Befehle aufgebaut sind, wie sie organisiert sind, womit diese Befehle rechnen können.
Das sind eigentlich die Unterscheidungsmerkmale hinsichtlich derer Mann jetzt,
also solche Befehlsätze, also Instruction-Set-Architectures für unterschiedliche Prozessoren dann eben unterscheiden kann.
Ausgangspunkt von dem Ganzen oder Grundidee ist ja eigentlich immer Dieser Befehlszyklus,
jetzt im Sinne so eines von Neuma,
so einer von Neumann-Maschine, wo ich dann eben sage,
ich habe Daten und Befehle in einem gemeinsamen Hauptspeicher,
also wie gesagt, alles, was jetzt eben vorher gesagt habe,
mit Cash oder sonstigen, nehmen wir erstmal außen vor.
Es gibt einfach erstmal nur einen Hauptspeicher, auf dem ich arbeite.
Und bei der Abarbeitung der Programme habe ich dann eben letztendlich
sowas wie so eine Endlosschleife, wo ich sage,
ich habe also irgendwo mein ausgewähltes Register Programmzähler, Program-Counter,
da drin steht eben die Adresse des Befehls,
die jetzt gerade abgearbeitet werden soll.
Das heißt, damit adressiere ich in den Speicherbereich,
wo jetzt also wirklich meine Befehle abgelegt sind.
Der dort gelesene Wert kommt dann eben in ein ausgewähltes Befehlsregister.
Hier dann immer als ER für Instruction-Register abgekürzt.
Anschließend kann ich dann dort den Befehl dekodieren, gucken,
was ist das für ein Befehl dann letztendlich ausführen.
Und wenn ich damit fertig bin,
wird dann eben der nächste Befehl genommen und genauso abgearbeitet.
Das ist also wirklich erstmal so dieses Grundschema, Programmabarbeitung nach dem von Neumann-Prinzip.
Wie wenn man sich das jetzt nochmal einzeln anguckt,
diese einzelnen Schritte, dann habe ich eben einmal dies Instruction Fetch,
also Befehl holen.
Im Programm zählt ja die Adresse,
Lesezugriff auf den Speicher und das Ergebnis wird dann eben in
das Befehlsregister geladen
Und gegebenenfalls, das ist immer so ein bisschen Architekturabhängig,
kann man dann eben sagen, wird,
also der Programmzieler dann eben auch schon inkrementiert.
Wenn ich jetzt beispielsweise in 32 Betrissrechner habe, mit 32 mit Befehlen,
wäre das dann in so einer Memonic-Schreibweise dann eben Instruction Register,
bekommt den Inhalt, Mem soll jetzt hier der Hauptspeicher sein,
an der Adresse PC und anschließend wird PC gleich PC plus 4 gerechnet.
Warum plus 4
Genau, nicht, dass es eben genau die Sache mit dieser Speicheradressierung,
also die ist plus 4, ist dann eben letztendlich das nächste Speicherwort,
wo ich dann ja eben genau den nächsten Befehl hole,
der dann hier von der Idee her jetzt eben auch 32
Bitbreite ist und genau mit dem Offset 4 bin ich dann eben ein Speicherwort weiter.
Nicht deswegen, da eben diese plus 4. Gut,
Bei den Rechnerarchitekturen wird dann eben noch unterschieden nach unterschiedlichen Befehlssatzarchitekturen.
Risk und Sisk werde ich nachher gleich noch kurz vorstellen.
Bei Sisk-Maschinen sieht es dann eben so aus,
dass ich dann gegebenenfalls eben nicht nur 1,32-Wittbefehl habe,
sondern dass die Befehle auch deutlich länger sein können mit größeren Befehlsmördern,
die dann eben entsprechend auch noch jetzt über mehrere Speicheradressen geholt werden können.
Das heißt, gegebenenfalls kann es da dann eben vorkommen,
dass sich quasi hier dann also auch letztendlich sowas wie mehrere Instruktionsregister habe,
wo ich dann eben den ersten Teil des Befehlswortes reinlade,
feststelle, das ist ein Befehl,
der jetzt noch länger ist und noch ein zweites oder ein drittes oder ein viertes Wort benötigt.
Und die werden dann eben gegebenenfalls auch noch nachgeladen.
Gut, anschließend wird dann eben Instruktion dekodiert, Instruction
Decode, Voraussetzung ist jetzt also, der Befehl steht im Befehlsregister
Es wird jetzt eben geguckt, quasi über den,
das, was man immer Opcode nennt,
also Operationscode, also sprich irgendein binär kodierten String,
das soll die und die Art von Befehl sein.
Und irgendwo in meinem Befehl steht dann eben auch noch ein Verweis da drauf,
mit welchen Operanten beispielsweise jetzt gerechnet werden soll,
wenn das also sowas wie ein Alu-Befehl ist.
Und außerdem werden dann also an die Funktionseinheiten,
also beispielsweise eben bei diesem Alu-Befehl dann eben auch schon die entsprechenden Steuersignale generiert,
um jetzt eben quasi der Alu zu sagen, mache meine Addition.
Gut, was dann dort teilweise noch mit reinspielt,
ist dann, also ich weiß, hier Instruction
Decode ist dann etwas, was sich dann eben Operan Fatch nennt.
Das heißt also, ich muss ja,
wenn ich also bei dem Beispiel arathmetisch logischer Befehl bleibe,
ja auch irgendwo die Operanten herholen.
Und typischerweise kann man, wird das ja eigentlich immer so gemacht,
dass also bei Risk-Rechnern dieses holende Operanten quasi in dieses Instruction,
die Code mit reingezählt wird. Bei Cis-Rechner,
weil da das holende Operanten noch deutlich aufwendiger werden kann,
weil die Operanen dann gegebenenfalls nämlich über noch mehrere Schritte erst irgendwo aus dem Hauptspeicher geladen werden müssen,
wird dann eben dieser Teil operan fetch,
also das hohen Operanten dann eben noch in die Ausführung des Befehls mit reingerechnet.
Gut, Ausführung des Befehls ist es eben,
wie gesagt, Befehl steht in IR drin.
Der Decoda hat das Ganze entschlüsselt, an den Funktionseinheiten liegen
die Steuereinheiten und letztendlich wird dann eben beispielsweise die Addition ausgeführt.
Und sofern das nicht hier vorne schon passiert ist,
der Programmzähler neu gesetzt oder inkrementiert. Funktional oder von der Idee
her wird das Setzen des Programmzählers dann also auch gerne in diese Execute-Phase mit reingenommen,
weil wenn ich als Befehl so was wie einen bedingten Sprung habe. Das heißt,
bei Sprungbefehlen habe ich jetzt ja nicht der Befehlszähler einfach nur inkrementiert wird,
um jetzt also auch von der nächsten Speicheradresse jetzt eben das nächste,
den nächsten Befehl zu lesen, sondern Sprungbefehl heißt ja dann,
also ich muss jetzt eben auch eine ganz andere Speicheradresse gehen,
um dann zu sagen, von dort soll jetzt der nächste Befehl gelesen werden.
Und bei bedingten Sprüngen sieht es dann häufig so aus, dass also quasi die Bedingungen sich aus irgendeiner Alu-Operation ergibt. Das heißt,
es wird eben beispielsweise abgefragt, zwei Operanten,
ist der eine, oder sind die operanten beispielsweise jetzt gleich,
nicht dem zuvor, wird dann also die Alu einfach rechnen,
operant a-operantb, wenn das Ergebnis null ist,
sind sie gleich und das,
oder dieses Ergebnis steht dann natürlich erst nach der Ausführung durch
dieser Subtraktion dann entsprechend fest,
nicht deswegen wird dann also häufig also die Berechnung des Programmszählers
dann eben auch noch in diese Execute-Phase
mit reingenommen
Gut, letztendlich ist das dann aber eben wirklich von der Art und Weise des Befehls abhängig,
also auch für die Ausführungszeit gilt das Ganze. Und gut,
typischerweise ist diese Ausführung dann eben durch entsprechende Hardware-Einheiten quasi festverdrahtet vorgegeben,
nämlich genau das,
was ich eigentlich oder was wir eigentlich so in den ganzen letzten Kapiteln,
also beispielsweise, wo es jetzt also um die Alos ging,
gesehen haben.
Gut, wenn man sich jetzt so typische Befehle selbst ansieht,
was braucht man eigentlich an Befehlen
Und hier jetzt nun mal so grob von der Idee her
beschrieben befehlst lassen. Ich habe sowas wie arrathmetische Operationen,
Additionen, Supraktionen, Inkrement, Dekrement, Multiplikationen,
die Visionen sind dann eher schon vergleichsweise seltener,
beziehungsweise die sind dann häufig eben auch nur,
wenn ich jetzt nicht spezielle Hardware-Einheiten habe, sowas wie irgendwelche Unterprogramme.
Dann habe ich logische Operationen, die ganzen Schieberoperationen,
dann typischerweise irgendwelche Vergleichsoperationen, also comper Equip, comper Greater,
comper Lestern, wie auch immer sie jetzt heißen mögen,
um jetzt quasi zwei Zahlenwerte miteinander zu vergleichen.
Dann hat man typischerweise Befehle jetzt, um Datentransfers einzuleiten.
Das heißt also, Außenspeicher erinnern, Register laden,
aus dem Register in den Spalter wieder zurückschreiben.
Oder um jetzt eben einen Speicherbereich auf einen anderen Speicherbereich zu kopieren,
sowas wie ein Ausgabeoperation. Und dann gibt es eben noch spezielle Befehle,
die ich dann eben brauche jetzt, Kontrollschluss zu machen.
Das heißt, Sprungbefehle, bedingte Sprünge,
also beispielsweise Jump Equal und irgendwas um Unterprogrammaufrufe zu machen.
Beispiele dafür, wie das dann eigentlich genauer noch aussieht. Wir werden jetzt hier im Folgenden eigentlich nur ganz kurz skizziert,
im vorigenden Kapitel dann zur Assembler-Programmierung,
also an einem einfachen Beispiel des X86 Assemblers,
werden uns da dann also noch so ein paar von diesen Sachen dann eben auch nochmal wieder begegnen,
aber jetzt eben auch nicht in aller Tiefe und auch nicht so genau,
dass man jetzt sagt, also okay, wenn man das jetzt hier gemacht hat, dann kann man also hier irgendwie jetzt Assembler-Code wirklich genau schreiben.
Gut, zu den Befehlssätzen oder Computerarchitekturen, also diese Unterscheidung,
Komplex Instruction, Set Computer, Reduced Instruction Set Computer,
ich habe das hier also andauernd ja auch schon irgendwie erwähnt,
werde ich nachher nochmal genauer vorstellen,
was sich dahinter Von der Grundidee her verbirgt,
beziehungsweise wie da so die Unterschiede sind,
weil inzwischen muss man eben auch sagen,
dass da eigentlich vieles jetzt auch nicht mehr so ganz klar definiert ist,
sondern also die Grenzen verschwimmen eigentlich so ein bisschen,
also das was, womit wir eigentlich überwiegend oder häufig arbeiten,
also sprich jetzt hier irgendwelche x, 86 Architekturen,
also sprich so,
PCs oder Laptops eben die Standardprozessoren sind von der Idee her eigentlich.
Risk-Architekturen war es dann eben eigentlich aus der Historie kommt,
also sprich irgendwie aus den 80er Jahren.
Heutzutage werden die dann aber häufig, also auch gerade seins,
es ist Helmblastern, also eher wie Cisk-Architekturen behandelt.
Gut, wie sieht jetzt die Dekodierung von Befehlen aus.
Ich hatte ja immer gesagt, also im Befehlsregister habe ich
jetzt eben irgendwie einen aktuellen Befehl.
Das kann jetzt also einfach irgendwie ein 32-Bit-Wert,
also das heißt hier irgendwie so ein abstruser Bit-String sein.
Die Frage ist jetzt eben, wie interpretiert die Hardware diesen Wert?
Da kann ich jetzt natürlich sagen, gut,
das ist einfach die Adresse in einem Speicher, also ein Mikro-Code-Rom beispielsweise.
Und ohne diese Adresse gucke ich nach und dann weiß ich,
was also aus diesem Befehl rauskommt.
Macht man natürlich nicht, weil dann müsste dieser Speicher
32 Einträge haben
Und 4 Gigabyte werden dafür dann also bestimmt nicht spendiert. Sondern das,
was man typischerweise macht, ist,
dass man dann dieses Befehlswort dann eben jetzt in einzelne Gruppen unterteilt,
hinter denen sich dann also zum einen sowas wie der eigentliche Befehl,
also der Obcode verbergen, dann aber eben auch schon Angaben auf die Operan.
Das heißt, wo kommen die Operanten her? Oder teilweise dann eben operan,
eben auch wirklich als Zahlen-Bit-Strinks quasi dort,
Teil dieses Befehls sind und ihm hier direkt drin stehen,
also was dann bei Emediate-Werten der Fall ist.
Die eigentliche Dekodierung passiert dann eben demzufolge dann nur noch jetzt
für den Ob-Code oder eben für Teile,
wo jetzt also hier auf die Operanten zugegriffen sind,
dann eben überansprechend mehrere kleine Tabellen.
Gut, das führt dazu, dass man dann eben diese
Befehle dann eben in unterschiedliche Klassen, sogenannte Befehlsformate unterteilen kann.
Am einfachsten sieht man das jetzt eben bei typischen Riskbefehlen,
wo jetzt nämlich alle Befehle, also auch die gleiche Wortlänge haben.
Also hier auch nun mal so grob angedeutet, wenn ich sage,
ich nehme dieses 32 Bitwort und sage, okay,
hier die ersten 8 Bit oder die U-Bahn 8 Bit sind dann eben der Obcode.
Das heißt also, die Art und Weise, was das eigentlich für ein Befehl ist.
Nicht da habe ich dann eben beispielsweise jetzt mal eine Alu-Operation mit beschrieben.
Nicht dann habe ich hier irgendwo im Rest des Befehls irgendwelche Verweise auf Register,
mit denen gerechnet wird, sei es für die Operanten,
sei es für das Resultat einer Operation.
Und ich habe ihm für weise auf Speicheradressen oder eben auch so was wie Emediate-Werte stehen.
Das heißt, wo also wirklich jetzt Werte direkt in dem Befehl dran stehen. Und beispielsweise könnte das,
was jetzt hier oben drin ist, dann eben so was sein,
wie hiermit kodiert eben ein Befehl,
speichere einen Wert in ein bestimmtes Register und dieser Wert sind dann eben hier quasi diese unteren 16-Bit die hier letztendlich drin stehen,
müssten dann natürlich noch jetzt also entsprechend irgendeine Konvention,
also beispielsweise interpretiert werden,
als Zweierkomplimentzahl dann eben natürlich zu einem 32-Bit-Wert ergänzt werden.
Nicht, wenn ich das hier als Zweierkomplimentzahl lese,
diesen Immediate-Wert, dann wäre das also hier quasi eine Acht,
die oberen 4enden 16-Bits wären dann eben auch alle entsprechend hier mit dem Most Significant-Bit ergänzt,
mit Nullen.
Und hier habe ich dann eben beispielsweise jetzt mit 5-Bit als Zielregister
das Register 30 adressiert
Gut, je nach Art des Befehlsatzes sind diese Zuordnung da natürlich also entsprechend willkürlich,
das, was sie jetzt hier im Folgenden live vorstellen werde,
ist also erstmal, um so einen einfachen Eindruck zu bekommen,
also den MIPS-Befehlsformat, also klassische Risk-Architektur, 32 Bitbreit,
da gibt es dann nämlich also auch nur drei Befehlsformate,
was dann also, sag mal,
sehr wirklich sehr schön, sehr übersichtlich ist,
diese kleine Beispielarchitektur Decore wird dann eben ab 16 Bit-Architektur am Praktikum verwendet und das,
womit was ich also hier Ende dieses Kapitels dann noch mit vorstellen werde,
beziehungsweise das, was uns das also im nächsten Kapitel mit beschäftigt,
ist dann also x86 Architektur, was dann eigentlich eine CISC-Maschine ist,
wo ich dann eben letztendlich eine sehr irreguläre Struktur habe mit vielen unterschiedlichen Formaten,
weil eben vieles davon also auch wirklich historisch über 35 Jahre quasi gewachsen ist,
wo es dann teilweise auch zu einzelnen Befehlen mehrere unterschiedliche Kodierungen geben kann,
und die Befehle eben von extrem kurzen Befehlen,
die nur einen weit groß sind,
bis zu irgendwelchen Befehlen der Größe 36 bald gehen können,
wo ich dann aber im Prinzip dann also auch wirklich schon in meinem Superskalan-Rechner,
also wirklich sämtliche Recheneinheiten parallel ansprechen kann,
was dann also im Prinzip jetzt mit diesen ganzen späteren Erweiterungen im X86-Befehlsatz,
also diese MMX, also Multimedia Extension bzw. SSE,
SSE2-Erweiterungen hinzugekommen ist,
wo dann eben spezielle Befehle mit drin sind,
wo ich eben mit einem einzigen Befehl Dann eben letztendlich schon 5 und Integralus gleichzeitig ansprechen und mit Daten versorgen kann.
Gut, aber fangen wir jetzt erstmal mit einem einfachen und
übersichtlichen Beispiel an und nicht mit irgendwelchen abstrusen Befehlsformaten.
Da bietet sich dann immer MIPS an, ist eben
ein festes Format. Alle Befehle sind 32 Bit lang.
Das Obcode-Fell ist auch immer genau sechs Bit breit
Dort werden auch unterschiedliche Adressierungsmodi gleich mit kodiert. Das heißt,
insgesamt habe ich demzufolge dort natürlich dann eben auch nur zwar auch sechs unterschiedliche Befehle zur Verfügung.
Es gibt dann genau drei Befehlsformate. Einmal das Registerformat,
wo ich dann im Prinzip Alu-Operationen mitbeschreibe, die alle immer nur mit Registern rechnen.
Dann habe ich für Emediate-Werte ein E-Format,
wo ich dann im Prinzip mit Register und einem Emediate-Wert rechnen kann und für Sprungbefehle,
Drums gibt es dann eben noch ein Format,
wo dann eben auch unter anderem, oder jetzt statt des Emidiate-Werdes,
dann eben entsprechende Speicheradressen angesprochen werden können.
Gut, diese MIPS-Architektur ist Mitte der 80er Jahre,
also im Prinzip in Stanford, entwickelt worden,
steht als Akronym für Microprocessor without interlocked Pipeline Stages,
in dem Kapitel zum Pipelining werde ich da dann also auch nochmal so ein bisschen drauf eingehen,
beziehungsweise jetzt hier in der, ich glaube,
übernächsten Folie ist, also nochmal so grob diese Architektur skizziert,
wie das aussieht.
Von der Die Idee her kann man eben sagen,
das ist also eigentlich die klassische 32-Petrisk-Architektur.
Das heißt, alle Worte, alle Datenformate, 32-Bit-Wortbreite,
32-Bit, Speicherwortbreite, 32-Bit-Befehle.
Es gibt 32 Register. R1 bis R31. Universalregister,
die man quasi lesen und schreiben kann.
Rnull ist ein Register, das kann ich nur lesen und
das liefert mir immer
den Wert null
Das heißt, rein physikalisch wird das eigentlich gar nicht als Register auftauchen,
sondern ist dann nur irgendwo hart verdrahtet mit drin.
Ist bei einigen, folgt auch gleich als Beispiel,
ist also, sagen wir mal, bei einigen Tricksereien,
um damit zu rechnen, also ganz nett,
dass mal so eine Möglichkeit hat,
sich also relativ schnell und relativ einfach eine Null zu generieren.
Gut, das ist dann eben nahe,
also Risk dann eben eine einfache Load-Store-Architektur, das heißt also,
ich habe nur zwei Befehle, nämlich Laden Außenspeicher,
Load und schreiben in den Speicher-Store, um auf den Hauptspeicher zuzugreifen.
Und alles, was Arithmetische, Operationen sind, funktioniert eben nur auf Register.
Gut, damit hat man dann eben einen vergleichsweise einfachen Befehlssatz. Alles,
was dann alles ist wirklich an komplexeren Operationen möglich sein soll,
wird jetzt also nicht direkt durch die Hardware unterstützt,
sondern muss dann eben wirklich in Form von irgendwelchen Makros oder
irgendwelchen Unterprogrammen
implementiert werden
Das geht dann sogar so weit, dass also Effekte,
die sich dann quasi durch das Pipelining ergeben, sogenannte Pipeline-Hazards,
da dann eben auch schon direkt quasi durch den Assembler-Code mit beseitigt werden müssen.
Beispiele dazu folgen dann also auch später noch bei dem Pipelining.
Gut, dieses Register-Modell, 32 Register, jeweils 32 Bett bereit.
Wofür braucht man diese konstante Null? Wenn ich also zum Beispiel sagen möchte, ich möchte einen Wert in einem Register Negieren kann ich eben einfach rechnen,
Sub, R5, R0 R5, was heißt, Subtraktion,
hier der erste Operant ist jeweils das Zielregister und das heißt,
ich würde rechnen, R5 ist gleich eher null minus R5,
womit ich dann also quasi genau diese Invetierung gemacht habe,
wenn ich ein Register quasi mit null vorbesetzen möchte,
kann ich das eben durch so einen einfachen Additionsbefehl machen,
wenn ich ein Register auf einen zumindest kleinen Wert setzen möchte,
kleinen Wert setzen möchte, kann ich das dann eben über so ein Emediate-Befehl machen,
in dem ich dann eben einfach eine Addition mache mit R0 und eben diesem Wert.
Und dadurch kann ich dann eben direkt in das Register R3
oder dem Register R3, die 17 zuweisen,
ohne dass ich da jetzt also irgendwie einen speziellen Befehl brauche, der das macht.
Also der Trick mit diesem R0-Register ist halt eben,
dass man sich dadurch dann wieder ein paar Befehle sparen kann.
Dadurch da das für die Befehle ja auch wieder nur sechs Bits zur Kodierung zur Verfügung stehen, wird das dann eben ganz gerne gemacht. Gut,
dann Mips-Prozessor hat dann eben quasi jetzt keine besonderen Flex,
um jetzt also quasi aus arathmetischen Operationen im Ergebnis her zu bekommen,
positiv, negativ, Überlauf, Unterlauf,
nicht all solche Sachen gibt es eben nicht,
sondern wenn ich also jetzt irgendwelche bedingten Sprünge machen möchte oder irgendwas,
dann muss ich dann also quasi erstmal Register mit vertreten Gleichoperationen setzen,
was dann eben dazu führt,
dass sich eben sowas mache wie Set Leston,
nee,
wo ich dann also quasi das Register R1 entweder auf null oder auf eins setze und letztendlich dort dann also quasi als Ergebnis landet,
ob er zwar kleiner als R3 ist und dann kann ich abhängig von dem Inhalt von A1 wieder einen Sprung ausführen oder nicht.
Wie gesagt, genaueres findet sich dann in der Literatur eigentlich
bei Dem Buch von Patterson und Hennessey Computer Organization and Design,
Dartress Software Interface, ist also auch in größerer Anzahl,
naja, großer, aber zumindest in einigermaßen Anzahl,
also auch hier bei uns in der Fachbereichsbibliothek vorhanden.
Nicht dort wird dann eben auch wirklich genau die Hardware-Struktur
dann nochmal erläutert und die Art und Weise,
wie dann eben die einzelnen Befehle dort abgearbeitet werden können.
Gut, diese Rechner haben dann eben die klassische Befehls-Pipeline,
also sprich diese einzelnen Stufen, die ich vorhin schon so grob vorgestellt habe,
Instruktionen holen, dekodieren, ausführen, gegebenenfalls dann eben noch Speicherzugriff.
Und wenn ich jetzt also ein Load-Befehle mache,
wo ich also quasi jetzt aus dem Speicher was in die Registerbank schreibe,
passiert das dann eben in dieser letzten Stufe, schreibt,
right back, beziehungsweise wenn ich hier irgendwas berechnet habe und das zurückgeschrieben wird,
passiert das dann eben auch hier.
Je nach Art des Befehls werden nicht immer alle Stufen dieser Befehls-Pipeline durchlaufen,
sondern ihm immer nur einige,
nicht aber der Trick ist eben dadurch,
dass ich quasi in dieser Pipeline auch immer fünf Befehle gleichzeitig in Bearbeitung habe,
ist der Durchsatz eben entsprechend groß.
Wie gesagt, genaueres, dazu folgt dann eh in dem
entsprechenden Kapitel hier dann noch, wo man dann eben sieht,
wie eigentlich dieses Pipeline im Prinzip funktioniert. Gut,
die Hardware-Struktur sieht dann ungefähr so aus oder kann man sich erstmal so vorstellen,
dass ich sage, ich habe hier quasi mein Program-Counter habe hier also einen Speicher,
letztendlich den Instruction Cash, aus dem ich meinen Befehl lese.
Ich lande dann eben hier in einem Register,
wo dann also quasi dann letztendlich das Befehlsregister jetzt für diesen Befehl drin ist.
Dort wird er dann dementsprechend dekodiert. Ich habe dann hier
in Zugriff dann also auf die Yosterbank die Ergebnisse,
wenn ich jetzt zum Beispiel eine Alu-Operation mache,
landen dann eben hier als Operant A und Operant
B für die Alu
Dann eben hier wieder in entsprechenden Registern, dann hätte ich hier sowas wie eine Allo-Operation,
das Ergebnis landet, hier in einem Register.
Hier hätte ich dann Lese oder schreibe Zugriff auf den Datenspeicher und das,
was dann eben dieses Ride Back ist, passiert dann eben in dieser letzten Stufe,
wo dann eben Werte wieder in die Registerbahn zurückgeschrieben werden.
Und das, was dann,
was man sich dann eben immer erst mal vorstellen muss,
von der Idee her ist,
dass ich dann jetzt eben nicht einen Befehl habe,
der quasi sequenziell hier alles durchläuft und dann kommt der nächste Befehl,
sondern jedem Tag wandert die Befehlsabarbeitung dann eben jeweils eine Stufe weiter.
Das ist also grob gesagt, also dann schon mal so das,
was dann eigentlich dieses Pipeline im Prinzip ist. Gut,
Befehlsformate bei dieser MIPS-Architektur dann eben quasi hier als Beispiel,
die ist R-Format, also Registerzugriffe,
wenn ich also jetzt hier diesen String habe,
die obersten sechs Bit sind ja Operationscode,
dann habe ich quasi 32 Register,
die ich hier adressieren kann.
Dafür brauche ich also
fünf Bit
In diesem Befehlsformat kann ich dann drei Adressbefehle bearbeiten. Das heißt,
ich kann eben berechnen, Inhalt des einen Registers plus Inhalt
eines zweiten Registers wird in ein drittes Register geschrieben.
Demzufolge muss ich hier drei Register adressieren.
Nämlich den ersten Operanen, den zweiten Operanten und Zielregister.
Und dann habe ich eben hier noch über eine Funktion Unterscheidung,
welche Rechenoperationen beispielsweise stattfindet. Gut,
und gleichzeitig gibt es hier dann eben noch so als so eine Besonderheit,
dann noch so ein optionales Shiften,
womit ich dann also teilweise dann die Werte auch noch skalieren kann
Und beispielsweise würde dann also ein Befehl wie Subprahiere,
Register 23 oder rechne Register 23 minus Register 30 und schreibe das Ergebnis in Register 3,
also sprich diese Operation, dann eben hier entsprechend so kodiert sein,
nicht dass ich dann eben sage,
ob Kot 0 ist Alu-Operation, hier mit diesem String,
sage ich dann eben,
es soll die Aktion durchgeführt werden.
Es wird keine Skalierung gemacht
Das Zielregister ist Register 3. Das ist genau dieser String. Und Register 23 minus Register 30
sind dann dieses Strings. Das heißt also durch so ein Ausblenden der entsprechenden Teile in diesem Befehlswort, kann ich dann immer sagen, was welchem Typ oder welcher Sache entspricht. Gut,
bei diesen Emediate-Werten sieht das dann immer so aus,
dass ich dann also beispielsweise sowas sage,
wie also hier als Beispiel Load World R5 bekommt den Inhal oder soll aus dem Speicher lesen,
von der Adresse, die in Register 8 steht und es gibt dann noch zusätzlich einen Adressen-Offset von 6. Das ist dann eben hier in den Beispielern so beschrieben,
dass dann eben quasi Load Word dieser String hier ist,
die Speicheradresse oder der Off-Saper-Adresse D6,
ist dann eben hier in diesen unteren 16 Bett beschrieben, wohin gespeichert wird,
also dass R5 Hier habe ich die 5 drin stehen und die Basisadresse,
die dann quasi mit diesem Offset noch zusammen berechnet wird,
sind dann also hier diese 5 Bit.
Letztendlich habe ich dann in Memonic Beschreibungen,
also was wie R5 ist,
gleich Memory von R8 plus 6. Gut,
hier auf den nächsten Folien wäre dann also nochmal kurze Beispiel dann also noch auch so andere 32 Bitteres,
Garchitektur der M-Core, beziehungsweise davon abgeleitet der Deko,
dass also was dann hier als 16-Bit-Architektur im Praktikum verwendet wird,
was man dann also hier dann auch mal entsprechend sich mit Hades ansehen und simulieren könnte,
wird dort also entsprechend gemacht mit Registermodell-Befehlssatz, Befehlsformaten.
Darauf werde ich jetzt aber hier aus Zeitgrund nicht weiter eingehen,
weil das bringt es dann meistens also auch sowieso nichts anderes,
sondern weder nur ein anderes Beispiel,
wie jetzt also hier irgendwie so
eine Befehlsformate aussehen
Gut, was dann aber natürlich noch interessant ist,
ist die Art und Weise Adressierungsarten, woher kommen die Operanten? Das,
was ich bislang ja hier vorgestellt hatte,
jetzt also an dem Beispiel Mips, ist,
dass ich sage, gut,
also die Operanten stehen entweder in Registern drin oder sind Teile des Befehlswortes,
wobei ich natürlich jetzt beim 32 mit Befehlswort,
wo dann also hier schon irgendwelche Bits für irgendwas anderes vergeben werden.
Natürlich dann also hiermit nur eingeschränkt mit Zeilen oder mit Teilworten arbeiten kann.
Nicht da kann man jetzt eben unterscheiden in den Befehlen oder in den Befehlsklassen zwischen jetzt den Art und Weisen,
wie letztendlich adressiert werden kann, wie Diese Bits drinks,
die jetzt in dem Befehl drin stehen,
dann eben entsprechend zu interpretieren sind und eben auch hinsichtlich der Befehlsklassen,
wie viele mögliche operant nicht in einem Befehl drin stehen habe.
Also jetzt hier bei diesem Beispiel Mips-Architektur, da war es eben so das,
was man typischerweise als Rechenbefehler, als Alu-Befehler hat.
Das sind immer drei Adressbefehle, wo ich dann eben sagen kann,
ein Register als Ziel und zwei Register,
mit denen ich jetzt irgendwie was rechne.
Ich kann aber eben auch sagen, gut,
ich habe jetzt nicht nur drei Adressbefehle,
sondern ich habe 2-1 oder sogar Null Adressbefehle
Was ist das? Vorgleich. Dann muss ich eben noch unterscheiden,
wie ich jetzt in einem Befehlshorman die Operanten adressiere, im Mediat-Wert.
Das heißt also als Zahlenwert direkt in dem Befehl.
Greife ich über Register darauf zu, greife ich über Speicheradressen
darauf zu und so weiter und so fort.
Da gibt es also relativ viele unterschiedliche Möglichkeiten.
Gerade bei Cisk-Rechnern kann man dann also hinsichtlich der Aggressierungsmodi,
dann nochmal ungefähr so 30 Typen unterscheiden.
Deswegen, wie gesagt, also bei Risk ist das vergleichsweise einfach.
Da gibt es also eigentlich nur letztendlich nur zwei, drei unterschiedliche Typen,
was das Ganze dann also entsprechend einsichtig dann auch macht.
Und das sind dann eben letztendlich die Unterscheidungsmerkmale der unterschiedlichen Architekturen.
Hier auch nur nochmal als Anmerkung. Ich habe ja immer schon
das letzte Mal ja auch gesagt, hier ist auch nochmal wieder Speicherhierarchie eingeführt.
Also das, was man natürlich sagen kann,
Wenn ich auf den Hauptspeicher zugreife, kostet mich das erstmal relativ viel Zeit.
Demzufolge ist es natürlich ganz schön, wenn man jetzt sagen kann,
also ich habe relativ viele Universalregister, kann irgendwie mit den Registern rechnen.
Und das läuft dann eben entsprechend schnell, deswegen möglichst registerstark Couchspeicher verwenden. Und das ist dann eben eine der Motivationen,
die dann also auch dazu geführt hat,
dass eben solche Loadstore-Architekturen entsprechend prominent geworden sind,
beziehungsweise ich also inzwischen, sagen wir mal,
soweit durchgesetzt haben, dass man also selbst bei X86 Rechnern,
die von der Grundidee her eben sagen können,
ich kann eine Operation wie X ist gleich Y plus Z und alles,
drei sind irgendwie Werte, die also direkt in den Speicher gehen,
dass das eigentlich inzwischen nicht mehr so umgesetzt wird,
sondern die meisten Compiler daraus,
also was erzeugen,
was dann eigentlich auch eher so ein Load-Store-Prinzip entspricht
Gut, also zu den Adressierungsarten, also angenommen,
ich habe jetzt eben so eine typische arathmetische Operation mit drei Variablen,
was also dann also beispielsweise bei einem Risk-Rechner dann eben sowas ist wie Adiere,
den Inhalt von Register 4 und Register 5 und schreibe das in R2 und eben die Unterscheidung,
ob ich jetzt hier Mit Registern,
arbeite oder mit Speicheradressen oder eben mit direkten Werten.
Damit habe ich dann eben die Klassifikation hinsichtlich dieser Architekturen. Gut. Hier auch nur nochmal grob skizziert,
also das, womit ich ja dann meistens rechne,
also quasi ein typischer Datenfahrt,
dass ich dann eben sage, ich habe eine Registerbank,
wo ich jetzt also mit mehreren,
mehrere Operanten oder meinem Operanten letztendlich drin stehen habe,
kann das dann über irgendwelche gegebenenfalls vorhandenen Hilfsregister,
dann eben auf die Alu machen,
habe dann eben mein Ergebnis und kann das eben vielleicht also auch noch entsprechend wieder hier zwischen speichern.
Und je nachdem, was also hiervon vorhanden ist,
hat man dann eben entsprechend unterschiedliche Architekturen. Gut,
zu den Operanten, wenn ich jetzt erstmal sage,
ich denke mir jetzt wieder alles weg und gehe erstmal nur auf
dieses ursprüngliche von Neumann-Prinzip und ich habe eben einen 3-Adress-Befehl.
Das heißt also, ich habe zwei Operanten und ein Resultat
Dann heißt, dass das all diese drei Daten im Hauptspeicher
liegen und ich muss eigentlich für diese eine Berechnung dann drei Hauptspeicherzugriffe machen.
Ist natürlich entsprechend aufwendig,
der Speicher kann theoretisch zwar also über so einen Multiport-Speicher,
also auch realisiert werden, wo ich dann also quasi in einem Speicherbaustein von mehreren Adressen gleichzeitig lesen kann und auch noch irgendwo anders hinschreiben kann.
Das ist aber also extrem aufwendig und extrem teuer und das macht also niemand,
sondern das womit inzwischen eigentlich typischerweise gerechnet wird, ist,
dass man eben genau sagt, also aus diesen Geschwindigkeitsgründen auch,
ich arbeite eigentlich mit Registern und gegebenenfalls, wenn es notwendig ist,
wird dann eben nochmal ein Datentransfer zwischen Register und dem Hauptspeicher gemacht,
was dann eben über diese Lot und Store Operationen geht.
Und das ist dann eben diese Operation, diese Motivation,
wo man dann eben sagt, okay,
also bei Risk Rechnern habe ich dieses Prinzip jetzt also wirklich stringent im Befehlssatz mit drin und alle Befehle arbeiten nur mit Registern,
während bei Sisk-Maschinen kann ich eben adressieren,
dass also die Operanen sowohl in Registern stehen,
als auch im Speicher, was also beispielsweise beim X86-Befehlsatz möglich ist,
wo ich aber ja schon sagte, das wird in der Regel so auch nicht mehr gemacht oder eingesetzt.
Gut, dann muss sie jetzt eben auch noch unterscheiden,
Oder hat er ja schon gesagt,
kann man immer noch unterscheiden hinsichtlich der Anzahl der Adressen,
die jetzt also in einem Befehlssatz stehen.
Wie gesagt, dass der Allgemeine vom Fall sind eben drei Adressformate,
wo ich dann eben wirklich sagen kann,
also ich habe hier drei unterschiedliche Register XY und Z. Bin damit natürlich optimal flexibel,
muss aber in meinem Befehlswort natürlich alle drei Adressen unterbringen.
So, wenn diese Adressen,
da kann man sich jetzt eben auch schon mal denken,
wo dann also letztendlich die Koks von dem Ganzen stickt,
steckt, wenn diese Adressen letztendlich Hauptspeicheradressen sind,
dann muss ich also in meinem Befehlswort dann eben auch wirklich drei Hauptspeicheradressen unterbringen.
Das heißt, das wird dann eben auch entsprechend lang.
Gut, dann zwei Adressformate sind in der Regel so,
dass quasi eine Adresse doppelt verwendet wird.
Das heißt eben, sowohl Quäle als auch Ziel der Operation.
Beim einen Adressformat habe ich dann eben für meine arathmetischen Operationen
noch ausgewählte ist da quasi als Akkumulator, also einen ausgewählten Zwischenspeicher.
Das ist also im Prinzip sowas wie dieses hier,
wenn ich das also jetzt hier in so eine Hardware-Struktur übersetze,
wo ich dann eben sage, okay,
ich kann in der Berechnung quasi immer diesen Akkumulatorregister und eben
ein anderes Register hinzufügen und damit rechnen
Ist eigentlich, sagen wir mal, im Prinzip etwas,
was also bei älteren Systemen noch der Fall gewesen ist.
Also inzwischen hat man das eigentlich nicht mehr. So,
und das, was sie unter null Adressformat eigentlich verbirgt,
ist sowas wie eine Stack-basierte Maschine. Das heißt also,
damit arbeite ich eben wirklich nur mit dem Kellerspeicher,
kann eben sagen, ich schiebe meine Operanten auf den Stack drauf.
Und wenn ich eine arathmetische Operation mache,
werden eben die obersten beiden Operanten genommen,
miteinander verknüpft und das Ergebnis landet wieder auf dem Steak.
Nicht das ist das, was man also früher bei Taschenrechnern,
also HP, dann quasi immer mit der UPN-Angabe hatte.
Wo ich da also jetzt nicht eingetippt habe,
A plus Speed Live, sondern da habe ich gesagt,
A, Enter, B, Enter und dann nur Plus gedrückt habe.
Und dann hatte ich also mein Ergebnis. Vorteil,
also sowas wie, man denkt sich ja,
null Adresse und Stack, das ist ja ein bisschen abstrus,
hat aber eben auch Vorteile.
Also zum einen muss ich mich da jetzt also
nicht um irgendwelche Adressen kümmern
War es natürlich dann entsprechend Vorteile, also jetzt für die Befehlsformate hat.
Und gerade im Compiler-Baus, so war es sehr beliebt,
weil man da dann eben auch entsprechend oft effizient drauf arbeiten kann,
um irgendwelche Sachen zu beschreiben.
Gut, wie sieht so was rein praktisch aus?
Jetzt kommen wir nämlich endlich mal zu den Übungsaufgaben,
die ja noch über Weihnachten anstehen.
Angenommen, wir haben jetzt diesen Ausdruck zu berechnen.
A-B geteilt durch
C plus D mal E. Hier soll also entsprechend deren allgemein arathmetischen Regeln,
also die Multiplikation, die Präferenz vor dieser Addition haben.
Dann kann ich mir das also quasi auf so einer Drei-Adresse-Maschine so vorstellen,
dass ich sage, subtrahiere A minus B, schreibe das Ergebnis in Set.
Das ist dieser erste Befehl. Multipliziere D mal E,
schreibe das Ergebnis in eine Hilfsregister Tee. Adiere C plus
Tee schreibe das Ergebnis wieder in Tee und dann habe ich noch eine Division,
Z durch Tee und das Ergebnis landet im Set und dann bin ich also mit diesen vier Befehlen fertig.
Als zwei Adressenmaschine könnte man das Ganze dann eben beispielsweise so realisieren,
dass ich sage, den Wert von A möchte ich erstmal in Z verschoben haben,
dann mache ich eine Subtraktion Set und B.
Das heißt also, Z ist leicht set-b.
Damit habe ich also quasi diesen Teil des Ausdrucks hier berechnet.
Dann mache ich hier genau das Gleiche letztendlich für diesen Therm hier hinten. Das heißt also,
die wird erstmal in Tee geladen, anschließend eh hinzuaddiert,
anschließend Blödsinn ansprüchen, mal mit E multipliziert,
anschließend C hinzuaddiert und dann muss ich dann eben noch Z
durch Tee teilen und habe dann ins Set wieder mein entsprechendes Ergebnis.
Als ein Abressmaschine wäre das dann eben Die Laden mit E multiplizieren.
C-Adian. Ich habe immer implizit quasi ein Register, mit dem also gerechnet wird.
Was also hier eben mit diesem Lotbefehl dann quasi damit geladen wird.
Dann wird das Ganze nach Z gespeichert. Nicht, dann
berechne ich hier den zweiten Teil Ausdruck. Teile hier und speichere das Ergebnis wieder. Gut,
und bei der Nullabressmaschine hält hätte ich dann also letztendlich diese Folge beziehungsweise,
dass es dann nicht auf dieser Folie ein bisschen übersichtlicher nochmal dargestellt,
weil da sieht man nämlich,
also was sich da dann also auf dem Stack tut.
Also ich habe hier quasi als oberste beiden Teile,
also der Stack würde jetzt immer hier reinwachsen, Top of Stack,
Next of Stack, das ist also hier mit Thos und Noss, erstmal nur so angedeutet.
Push e würde also quasi eh hier obendrauf schreiben,
Push e würde dann eben dafür sorgen,
dass das hier einen weitergeschoben wird und ich de hier oben liegen habe,
die Multiplikation nimmt dann eben die obersten beiden Elemente,
berechnet die Verknüpfung und schiebt das Ganze dann wieder in den Stack rein.
Damit habe ich dieses hier,
pusht se at fördern zu diesem Teilausdruck führen und anschließend muss ich dann,
also jetzt um hier diesen Therm dann zu berechnen,
dann also wieder mit zwei Push-Operationen das Ganze in den Stack tun,
die Subtraktionen machen und mit diesem letzten Divisionsbefehl hätte ich dann unter Top of Stack dann,
also wirklich meinen arathmetischen Ausdruck berechnet und mit einem Pop-Befehl würde das dann eben entsprechend in das Register Z geladen werden können.
Nicht also das nur so grob zur Veranschaulichung, was sich hinter 3,
2, 1 und 0 Adressmaschine verbirgt und wie man sieht,
also das ist letztendlich immer, sagen wir jetzt Geschmackssache vom Befehlssatz,
nicht also bei nur Adressmaschinen Maschinen brauche ich eben entsprechend viele Befehle. Andererseits sind die natürlich extrem einfach gehalten und ich habe in den Befehlen jetzt also nicht noch irgendwie den Zwang,
dass ich dort jetzt viel an Adressen kodieren muss,
nicht sondern es wird implizit eben immer nur auf dem Stake gerechnet,
nicht im Gegensatz dann eben zwar sehr wenig Befehle,
ich muss aber eben in Markt auch noch entsprechend drei Adressen mit unterbringen und solange ich sich also,
wie jetzt also beispielsweise beim MIPS-Befehlsatz, um Registeradressen handelt,
ist das natürlich also kein Problem,
wenn es sich da jetzt aber um Hauptspeicheradressen handelt oder wenn da jetzt also noch irgendwelche Emediate-Werte mit drin sind,
mit entsprechend großen Wortbreiten, dann heißt das eben auch,
dass also so ein Maschinenbefehl dann eben nicht mehr ein Speicherwort ist,
sondern es auf einmal mehrere Speicherworte und dadurch dann also quasi wieder die Gesamtanzahl Verbrauch dabei für das Programm eben wieder größer wird.
Gut, damit sind wir dann nämlich eigentlich auch schon bei den Adressierungsarten angekommen.
Hatte ich ja bislang, also auch nur an dem Mipps Beispiel,
im Mediat-Adressierung kurz vorgestellt. Da geht es eben darum,
dass man sagt, also da steht der Operant direkt im Befehl,
wird einfach also quasi aus dem Instruktionsregister als ein Teil heraus interpretiert.
In der Regel habe ich dann aber natürlich problemhinsichtlich der Länge des Operanten Direkt adressiert heißt,
dass in dem Befehl die Adresse des Operanten drinsteht,
führt aber dann natürlich dazu,
dass sich dann also einen zusätzlichen Speicherzugriff machen muss und dass dadurch,
dass ich in meinem Befehl natürlich jetzt nur einen Teil des Befehls noch für diese Adresse zur Verfügung habe,
der Adressbereich eben auch beschränkt ist,
bei indirekter Adressierung habe ich dann eben Die Adresse eines Pointers im Befehl.
Wobei ich dann eben in einem ersten Speicherzugriff,
also quasi jetzt diesen Pointer lese,
anschließend dann eben diesen gelesenen Wert dann eben nehme,
um dann eben auch von dieser Speicheradresse meinen Operanen zu holen.
Damit ist man dann zwar sehr flexibel hinsichtlich dessen,
was man da alles adressieren kann,
hat aber letztendlich dann eben auch zwei Speicherzugriffe, was gegebenenfalls sehr langsam sein kann.
Gute Registeradressierung ist im Prinzip genau das Gleiche wie Direkte Adressierung,
nur dass ich jetzt eben hier nicht also einen Zugriff auf den Hauptspeicher habe,
sondern einen Zugriff auf das Register.
Gut, und Register indirekt ist dann im Prinzip genau das Äquivalent zu dem hier,
dass ich eben in meinem Befehl ein Register auswähle und in dem Register steht dann eben die Speicheradresse.
Gut. Dann gibt es noch sowas wie indizierte Sachen,
wo ich dann eben jetzt also aus mehreren Zugriffen auf Register und vielleicht irgendwelche Speicheradressen mir dann also meine eigentlichen Adressen zusammenrechne,
ähnlich zu dem, wie das also bei dem MIPS-Beispiel da der Fall war,
wo ich also quasi das, was ich meinte,
ist dieses hier, wo ich dann eben sage,
ich habe hier quasi das Register R8,
da steht eben die Speicheraddresse drin.
Zusätzlich kann ich eben noch optional mit diesem Feld dann eben
noch so ein Offset mit beschreiben
Ja, und damit ist man an ihm vergleichsweise auch sehr flexibel,
das wäre dann eben so eine indizierte Adression.
Gut, was verbirgt sich dahinter? Ja, bitte ich jetzt zu entschuldigen,
da muss ich jetzt nochmal zwei, drei Minuten,
also hier überziehen, aber war das also dann auch für die Übungsaufgabe noch der Fall ist.
Also bei, war ja auch nur relativ kurz, wird das dann also gegebenenfalls nochmal wiederholen. Wie gesagt,
Emediate kann man sich eben immer so vorstellen,
ich habe in meinem Befehlswort den eigentlichen Wert drin stehen,
habe aber natürlich dadurch, dass es eine Zahl ist,
die dann also beim einen Wortbefehl mit einer relativ kurzen Wortbreite nur noch,
oder muss zwingend dann quasi ein zweites Speicherwort mit dazu nehmen,
um dann eben wirklich ein 32-Bit-Wert,
also dort in dem Wort drin stehen zu haben.
Direkte Adressierung wäre dann eben sowas, dass ich sage,
ich habe entweder An dieser Stelle oder in einem zweiten Wort an sowas wie eine Speicheradresse drinstehen.
Das heißt, dort wird dann eben wirklich in den Hauptspeicher adressiert,
um dann eben zum Beispiel einen Ladebefehl,
wo ich dann eben hier aus dem Speicher etwas in einen Register lade,
zu sagen, von welcher Adresse gelesen werden soll.
Das heißt, direkte Adressierung ist immer Zugriff auf den Hauptspeicher und der Wert des Hauptspeicher oder die Adresse,
von der irgendwas jetzt gelesen werden soll oder auf die geschrieben werden soll,
steht dann eben in dem Wort direkt mit drin.
Indirekt wäre dann nochmal mit diesem zusätzlichen Pointer-Zugriff, das heißt,
ich habe hier eine Adresse, von der lese ich was,
das interpretiere ich aber auch nur als Pointer, das heißt,
der Inhalt ist dann wieder eine Adresse für einen zweiten Speicherzugriff und dieser Wert wird dann eben beispielsweise gelesen Wir haben mit diesen Indizierten Adressierungsmodi ist dann halt eben etwas gemeint,
wo ich dann eben letztendlich sage, ich habe hier eine Adresse drinstehen,
mit der ich ein Register adressiere und die Adresse oder das,
was in dem Register dransteht, ist dann eben eine Speicheradresse,
auf die ich darauf zugreife.
Da gibt es dann eben noch zusätzliche Adressionsmodi,
wo ich dann eben beispielsweise jetzt, wie hier angedeutet,
entweder noch zwei Register miteinander verknüpfen kann für einen Speicherzugriff oder wo ich dann eben sowas wie,
also in diesem Beispiel von dem Mips eBay-Fehlsatz,
dann eben sowas habe.
Ich habe einen Register und ich habe hier dann noch irgendeinen Offset,
der dann eben noch miteinander berechnet wird,
um sowas dann eben entsprechend zu überrechnen.
Gut, damit jetzt im Schnelldurchgang nur mal grob skizziert diese Adressierungsarten.
Ich hoffe, es ist von der Grundidee her klar geworden,
was sich dahinter verbirgt. Ich werde mir im Zweifelsfall dann
also nochmal genau die Übungsaufgabe ansehen, in der Hoffnung,
dass zumindest einigermaßen klar ist, was damit gemeint ist.
Ansonsten werde ich mich nochmal an die Übungsgruppenleiter wenden und sagen,
zumindest über diese einen Aufgabe dann also nicht ganz so streng
zu bewerten
Dass es da dann also keine Probleme gibt und es dann also heißt,
naja gut, da wird jetzt aber was in der Übung abgefragt,
was in der Vorlesung nicht dran gekommen ist.
Gut, jetzt mit fünf Minuten über der Zeit,
peinlich peinlich, frohe Weihnachten,
guten Rutsch,
frohes Feiern und dann bis Januar