WEBVTT

00:10.100 --> 00:13.820
Wir hatten letztes Mal angefangen, uns die Befehlsarchitektur, die

00:13.820 --> 00:16.500
ISA, Instruction Set Architecture, anzuschauen.

00:16.980 --> 00:19.280
Wir hatten uns angeschaut, was es da für verschiedene

00:19.280 --> 00:20.660
Ausführungsmodelle gibt.

00:21.180 --> 00:22.520
Die wiederholen wir jetzt noch mal kurz.

00:22.640 --> 00:24.560
Zwei, drei Folien von dem, was wir schon gesehen hatten.

00:25.140 --> 00:27.480
Und danach machen wir weiter mit dem, was zur ISA gehört.

00:27.660 --> 00:30.580
Wir werden verschiedene Befehlsformate sehen, verschiedene

00:30.580 --> 00:31.660
Adressierungsarten sehen.

00:31.760 --> 00:32.700
Mal gucken, wie weit wir damit kommen.

00:35.000 --> 00:37.040
Als Erinnerung, die Folie haben wir schon ein paar Mal gesehen.

00:37.180 --> 00:39.780
Wir wissen inzwischen, dass die ISA quasi die Schnittstelle ist,

00:39.860 --> 00:43.000
zwischen der Programmiersprache, der Assemblersprache und unten der

00:43.000 --> 00:43.940
Hardware -Architektur.

00:44.460 --> 00:46.120
Und die muss einfach irgendwie mal definiert werden.

00:46.360 --> 00:49.300
Die obere Hälfte, der Compiler, die Programmierer, können sich darauf

00:49.300 --> 00:49.700
verlassen.

00:49.820 --> 00:52.320
Die wissen, wie sie mit der Hardware reden müssen.

00:52.920 --> 00:55.560
Und unten drunter kann die Hardware irgendwas implementieren.

00:55.640 --> 00:58.900
Völlig egal, was Hauptsache ist, realisiert die angebotene ISA.

00:59.000 --> 01:02.100
Wie es das genau tut, ist dann den Hardware-Entwicklern überlassen.

01:02.240 --> 01:03.640
Deswegen saubere Schnittstelle.

01:06.000 --> 01:06.920
Was gehört dazu?

01:07.100 --> 01:11.580
Es gehört dazu, wie Daten repräsentiert, dargestellt werden, wo die

01:11.580 --> 01:14.740
Daten gespeichert werden, wo die Operationen sind, wie man angeben

01:14.740 --> 01:18.940
kann, wo die Operatoren, also die Eingabedaten für Operationen sind,

01:20.600 --> 01:23.060
wie Befehle kodiert werden, wie man auf Daten zugreifen kann.

01:23.480 --> 01:24.180
All das ist ISA.

01:25.160 --> 01:27.000
Ein paar haben wir schon gesehen, restliche sehen wir jetzt.

01:27.120 --> 01:29.980
Was wir schon gesehen hatten war, wo stehen denn diese Operanten

01:29.980 --> 01:30.420
eigentlich?

01:30.560 --> 01:33.420
Also entweder die Eingaboperanten oder die Ausgaboperanten, das

01:33.420 --> 01:34.520
Ergebnis von der Berechnung.

01:34.920 --> 01:37.580
Die können an verschiedenen Stellen stehen, hatten wir gesehen,

01:37.720 --> 01:38.580
schauen wir gleich nochmal an.

01:38.880 --> 01:40.820
Hier nochmal die Wörter würde ich gerne wiederholen.

01:41.140 --> 01:43.280
Explizite und implizite Adressierung.

01:43.820 --> 01:47.540
Einfach was das heißt ist, entweder die Adresse, also wo ist der

01:47.540 --> 01:52.200
Operant, wo soll nachgeschaut werden, ist explizit angegeben, sprich

01:52.200 --> 01:55.980
im Befehl steht dann sowas wie, tue Berechnung, Addition, keine

01:55.980 --> 01:58.660
Ahnung, und tue sie auf folgenden Operanten.

01:59.060 --> 02:03.800
Und dann wirklich wird hingeschrieben, welche Operanten das sein

02:03.800 --> 02:04.160
sollen.

02:04.520 --> 02:08.480
Wohingegen die implizite Adressierung halt eben nicht hinschreibt, wo

02:08.480 --> 02:11.800
die Operanten zu finden sind, sondern es ist immer die gleiche Stelle

02:11.800 --> 02:14.460
und die ist implizit im Befehl schon angegeben.

02:14.860 --> 02:17.640
Zum Beispiel gibt es halt Befehle, die auf einem Akkumulator

02:17.640 --> 02:21.720
operieren, da ist der Befehl, der heißt dann schon Adds-Akkumulator,

02:21.900 --> 02:23.360
zum Beispiel, wie auch immer.

02:23.720 --> 02:25.980
Und da muss halt eben kein Operant angegeben werden, weil klar ist,

02:26.000 --> 02:28.140
der Befehl wird immer auf dem Akkumulator arbeiten.

02:28.480 --> 02:30.580
Oder wir hatten diese Kellermaschinen gesehen, sehen wir gleich

02:30.580 --> 02:35.240
nochmal, auf Kellermaschinen muss die Stelle der Operanten nicht

02:35.240 --> 02:36.820
angegeben werden, das ist immer der Keller.

02:38.260 --> 02:40.480
So, explizite, implizite Adressierung.

02:40.840 --> 02:45.540
Dann haben wir noch die überdeckte Adressierung, das heißt, es sind

02:45.540 --> 02:50.260
zwar Operanten, mindestens einer, explizit angegeben, aber es sind

02:50.260 --> 02:54.960
nicht alle angegeben, im Sinne von, es sind nicht alle unterscheidbar,

02:55.060 --> 02:56.320
sauber getrennt, unterscheidbar.

02:56.740 --> 02:59.140
Zum Beispiel, wenn ich eine Addition habe, brauche ich eigentlich drei

02:59.140 --> 03:00.640
Operanten, Quelle, Quelle, Ziel.

03:01.180 --> 03:05.940
Und da könnte man zum Beispiel sagen, ja, der Zieloperant, wo soll das

03:05.940 --> 03:07.000
Ziel hingeschrieben werden?

03:07.660 --> 03:09.700
Ja, da, wo die erste Quelle hergenommen wurde.

03:10.140 --> 03:14.360
Also, ich gebe nur zwei Adressen, zwei Stellen, zwei Operanten

03:14.360 --> 03:19.300
explizit an und Überdeckung heißt, eine von den beiden ist auch das

03:19.300 --> 03:21.040
Ziel, soll überschrieben werden.

03:21.140 --> 03:24.340
Das wäre ein Beispiel, wo sich Quelle und Ziel überdecken.

03:26.380 --> 03:29.480
Befehlsformat kann verschieden lang sein, Befehle können 32-Bit lang

03:29.480 --> 03:32.420
sein, 8-Bit lang sein, 64-Bit, irgendwelche groben Zahlen.

03:32.860 --> 03:34.500
Also, hatten wir auch gesehen.

03:34.740 --> 03:36.880
Okay, jetzt gucken wir uns die verschiedenen Ausführungsmodelle mal

03:36.880 --> 03:37.080
an.

03:37.420 --> 03:40.240
Nicht alle einzeln nochmal, sondern einfach nur, das war so ein

03:40.240 --> 03:42.540
Übersichtsbild, daran kann man eigentlich die Unterschiede auch sehr

03:42.540 --> 03:43.080
schön erkennen.

03:43.640 --> 03:46.940
Wir hatten die Kellerarchitektur gesehen, wo halt eben die Operanten

03:46.940 --> 03:50.860
nicht explizit angegeben werden müssen, sondern sie stehen implizit

03:50.860 --> 03:54.240
auf dem Keller und da überdeckt sich nichts, es wird quasi ein Operant

03:54.240 --> 03:55.160
nach dem anderen genommen.

03:56.380 --> 04:00.540
Wir haben also so einen Keller, wir haben einen Zeiger, der uns sagt,

04:00.760 --> 04:02.340
wo auf dem Keller arbeiten wir gerade.

04:02.460 --> 04:06.560
Das ist der Top-of-Stack, Stack für Keller, der zeigt quasi auf ein

04:06.560 --> 04:10.300
Element und wenn ein Befehl kommt, Addition, heißt das so viel wie,

04:10.560 --> 04:13.180
erster Operant ist das oberste Element auf dem Stack.

04:13.460 --> 04:14.000
Welches ist das?

04:14.180 --> 04:15.720
Das, worauf Top-of-Stack zeigt.

04:16.200 --> 04:19.220
Das wird dann quasi als Input in die ALU reingeholt.

04:19.700 --> 04:23.780
Danach zeigt ja Top-of-Stack auf den neuen Top, also das Element ist

04:23.780 --> 04:25.960
ja weggenommen worden, jetzt haben wir einen neuen Top-of-Stack.

04:26.440 --> 04:27.880
Zweiter Operant, wo kommt der her?

04:28.020 --> 04:29.180
Naja, auch wieder Top-of-Stack.

04:29.560 --> 04:32.460
Ist inzwischen halt eben dieses Element hier, wird das hier als Input

04:32.460 --> 04:33.060
reingeholt.

04:33.720 --> 04:38.680
Dann ist Top-of-Stack sozusagen, naja, würde ein unten drunter zeigen,

04:38.760 --> 04:39.480
ist hier nichts hingemalt.

04:39.600 --> 04:41.660
Also in dem Fall könnte man auch sagen, der ist leer, weil kein

04:41.660 --> 04:42.720
anderes Element hingemalt ist.

04:43.340 --> 04:44.840
Wo kommt das Ergebnis der Berechnung hin?

04:45.120 --> 04:49.780
Das ist das neue Top-of-Stack-Element, wird also quasi ein über den

04:49.780 --> 04:52.200
Stack geschrieben und damit ist es der neue Top-of-Stack.

04:54.120 --> 04:58.340
Akkumulator heißt einfach, es gibt ein spezielles Register, das wäre

04:58.340 --> 05:02.940
der Akkumulator zum Beispiel und fast alle oder quasi alle Befehle

05:02.940 --> 05:04.180
arbeiten auf dem Akkumulator.

05:04.580 --> 05:07.300
Ein Operant ist dann immer der, also ein Quelloperant ist der

05:07.300 --> 05:11.100
Akkumulator und das Ziel kommt auch wieder in den Akkumulator zurück

05:11.100 --> 05:13.840
und dann gibt es halt noch ein paar spezielle Befehle, die halt eben

05:13.840 --> 05:17.880
entweder aus dem Speicher den Akkumulator füllen oder wieder heraus

05:17.880 --> 05:21.700
retten können oder es gibt auch häufig Architekturen, die haben sowohl

05:21.700 --> 05:24.600
ein Register-File als auch einen Akkumulator.

05:25.040 --> 05:28.400
Dann kann man halt eben auch aus dem Register-File Daten in den

05:28.400 --> 05:30.340
Akkumulator rüber kopieren oder eben wieder zurück.

05:31.660 --> 05:33.780
Naja, man kann es hier sehen, ein Operant ist halt einfach immer im

05:33.780 --> 05:36.480
Akkumulator, Ergebnis geht zurück in den Akkumulator, ja.

05:37.560 --> 05:41.360
Register-Speicher, ein Operant kommt aus dem Register-File, ein

05:41.360 --> 05:44.800
Operant kommt aus dem Speicher, Ergebnis wieder zurück ins Register

05:44.800 --> 05:45.120
-File.

05:45.620 --> 05:51.340
Die üblichste Architektur ist die Register-Register-Architektur, wo

05:51.340 --> 05:54.980
einfach beide Operanten aus dem Register-File kommen und wo es dann

05:54.980 --> 05:58.080
spezielle Ladespeicherbefehle gibt, mit denen man quasi zwischen

05:58.080 --> 06:01.920
Hauptspeicher und Register-File-Daten hin und her schaufeln kann.

06:03.120 --> 06:03.120
Okay,

06:06.180 --> 06:09.320
ja vielleicht noch, das hier war quasi das Nulladress, also Keller war

06:09.320 --> 06:11.880
quasi Nulladressformat, man muss gar keinen Operanten dann geben.

06:11.880 --> 06:15.420
Akkumulator muss sich mindestens einen angeben, nämlich wo die zweite

06:15.420 --> 06:18.080
Quelle herkommen soll, eigentlich eigentlich genau einen, wo die

06:18.080 --> 06:19.320
zweite Quelle herkommen soll.

06:20.060 --> 06:23.680
Andere Quelle und Ziel ist implizit und überdeckt sich.

06:24.140 --> 06:26.080
Register-Speicher, ja gibt es Varianten.

06:26.260 --> 06:30.020
Ich könnte entweder sagen, also ich muss mindestens angeben, aus

06:30.020 --> 06:32.540
welchem Register-File die Quelle herkommen soll und ich muss angeben,

06:32.620 --> 06:34.660
wo es mit Speicher der Input herkommen soll.

06:35.000 --> 06:37.660
Der Output könnte sich jetzt wieder überdecken, also könnte ich zwei

06:37.660 --> 06:40.800
Adressen vielleicht nur angeben oder ich würde sogar sagen, auch der

06:40.800 --> 06:42.860
Output kann an eine andere Stelle geschrieben werden, wo sich halt

06:42.860 --> 06:44.160
eben drei Adressen angeben können.

06:45.180 --> 06:47.220
Bei Register-Register habe ich auch alle möglichen Varianten, könnte

06:47.220 --> 06:49.100
ich zwei oder drei Operanten angeben.

06:52.440 --> 06:55.820
Und als letzte Wiederholungsfolie hatten wir hier den Vergleich noch

06:55.820 --> 06:59.220
mal gesehen, wie es aussieht, wenn man es mal versucht zu

06:59.220 --> 07:01.940
programmieren, als Assembler quasi Beispiel, versucht zu

07:01.940 --> 07:02.420
programmieren.

07:02.980 --> 07:04.120
Ich gehe da auch mal kurz durch.

07:05.760 --> 07:12.760
Wir hatten hier offenbar irgendein Register, das soll eine

07:12.760 --> 07:15.180
Speicheradresse sein, mit irgendeinem Wert gefüllt wird.

07:15.300 --> 07:16.520
Also das hier soll eine Adresse sein.

07:16.800 --> 07:19.820
Wir werden ein paar Folien später noch sehen, wie man diese Adressen

07:19.820 --> 07:20.660
eigentlich angeben kann.

07:20.760 --> 07:23.320
Da gibt es gefühlt tausend verschiedene Arten, wie man die angeben

07:23.320 --> 07:23.520
kann.

07:23.760 --> 07:24.780
Ein paar davon sehen wir.

07:25.860 --> 07:28.340
Hier irgendeine andere Speicheradresse, soll ein anderes Register

07:28.340 --> 07:29.200
reingeladen werden.

07:29.360 --> 07:31.220
Das sind halt gerade die Operationen, die werden ausgeführt.

07:31.440 --> 07:33.880
A und B sollen ladiert werden, Speicheradresse C geschrieben werden.

07:34.380 --> 07:37.060
Danach kommt dann die eigentliche Addition auf den Register.

07:37.260 --> 07:38.980
Wir sind in der Register-Register-Architektur.

07:39.560 --> 07:42.700
Danach wird das Ergebnis, hier das Ergebnis aus der Berechnung

07:42.700 --> 07:46.580
Register 1 plus Register 2, das Ergebnis wird dann quasi genommen und

07:46.580 --> 07:48.960
in den Hauptspeicheradresse C zurückgeschrieben.

07:50.680 --> 07:54.100
Hier wird C und B geladen, weil das die nächste Operation, die gemacht

07:54.100 --> 07:55.180
werden soll.

07:55.600 --> 07:59.920
Und dann wird hier C, Register 1, B, Register 2 gerechnet.

08:00.020 --> 08:00.960
Ergebnis nach Register 3.

08:01.060 --> 08:03.720
Register 3 wird in Hauptspeicheradresse D geschrieben.

08:04.560 --> 08:07.940
In der Speicheradress-Architektur spare ich mir ein paar der

08:07.940 --> 08:08.760
Ladeoperationen.

08:09.940 --> 08:11.180
A lade ich immer noch.

08:11.560 --> 08:14.780
Aber der Additionsbefehl erlaubt jetzt halt eben, Register, Speicher,

08:15.120 --> 08:18.320
ein Register als Quelle anzugeben und eine Speicheradresse als Quelle

08:18.320 --> 08:18.940
anzugeben.

08:19.400 --> 08:22.620
Also heißt das hier Inhalt von Register 1 plus Inhalt von

08:22.620 --> 08:23.780
Speicheradresse B.

08:24.420 --> 08:26.700
Ergebnis wird abgelegt in Register 1.

08:27.560 --> 08:30.180
So gesehen kann ich Register 1 dann wieder speichern nach C.

08:30.280 --> 08:30.920
Sollte ich ja machen.

08:31.260 --> 08:37.260
Kann es gleich wieder laden, kann B davon abziehen und das Ergebnis

08:37.260 --> 08:38.760
dann speichern nach D.

08:39.140 --> 08:42.900
Also ich habe ein paar Befehle gespart, weil ich die Speicheradressen

08:42.900 --> 08:44.300
direkt bei der Addition angeben kann.

08:44.460 --> 08:46.300
So gesehen kompakterer Code.

08:46.400 --> 08:49.160
Das war einer der Vorteile, wenn ich speichern als Operanten mit

08:49.160 --> 08:49.840
angeben kann.

08:50.320 --> 08:52.740
Der Nachteil war halt eben auch die Latenz.

08:52.780 --> 08:55.980
Die Operation ist unterschiedlich lang, was manchmal zu Problemen

08:55.980 --> 08:56.460
führen kann.

08:57.240 --> 09:00.040
Hier weiß ich bei einer Addition, die wird immer einen Takt dauern,

09:00.620 --> 09:03.980
hier hängt die Addition davon ab, es gibt dann quasi auch ein

09:03.980 --> 09:06.680
Additionsbefehl, der Register-Register oder Register-Akkumulator ist

09:06.680 --> 09:10.220
und es gibt ein Additionsbefehl, der Register-Speicher ist, der wird

09:10.220 --> 09:10.800
länger dauern.

09:10.860 --> 09:13.100
Wie lange der dauert, naja, hängt irgendwie davon ab.

09:13.180 --> 09:15.940
Hängt davon ab, wie lange der Speicher braucht zum Antworten.

09:15.980 --> 09:17.960
Das ist ja variabel lang im Allgemeinen.

09:20.140 --> 09:20.580
Akkumulator.

09:20.860 --> 09:22.340
Ich habe implizit einen Akkumulator.

09:22.640 --> 09:27.940
Load A heißt sozusagen, der Wert, der an Adresse A steht, soll in den

09:27.940 --> 09:29.780
Akkumulator reingeschrieben werden.

09:30.180 --> 09:31.920
Dass es der Akkumulator ist, muss ich nicht angeben.

09:32.040 --> 09:34.100
Also implizite Adressierung des Akkumulators.

09:35.400 --> 09:39.260
B soll geladen und auf den Inhalt vom Akkumulator aufaddiert werden.

09:39.460 --> 09:42.500
Ja, also Akkumulator gleich Akkumulator plus Inhalt von B.

09:43.060 --> 09:45.460
Was jetzt im Akkumulator steht, soll nach C geschrieben werden.

09:45.840 --> 09:48.300
Man kann einfach schon sehen, das ist irgendwie lesbarer.

09:49.060 --> 09:53.740
Weniger Buchstaben, weniger Krimskrams als hier drüben, weil überall

09:53.740 --> 09:56.000
implizit der Akkumulator als Adresse angegeben ist.

09:56.080 --> 09:59.320
Also Lesbarkeit, aber Lesbarkeit ist nicht so der ultimative Vorteil,

09:59.680 --> 10:01.660
weil der Code ja meistens automatisch generiert wird.

10:03.260 --> 10:04.700
Keller gab es noch einen Unterschied.

10:05.440 --> 10:09.360
Keller muss ich erstmal aus dem Speicher Daten heranschaffen.

10:09.580 --> 10:11.960
In dem Fall fange ich mit B an, fange ich mit A an, ist egal.

10:14.420 --> 10:15.340
Fangen wir mit B an.

10:16.680 --> 10:20.720
Aus dem Speicher heranschaffen und dann auf den Keller oben drauf

10:20.720 --> 10:22.080
drücken, pushen.

10:23.260 --> 10:26.240
Danach A aus dem Speicher holen, oben auf dem Keller drauf.

10:26.960 --> 10:31.340
Addition heißt jetzt, erstes Element vom, also top of stack, oberstes

10:31.340 --> 10:33.000
Element vom Keller wieder runter holen.

10:33.080 --> 10:36.700
Das oberste Element ist das zuletzt draufgepuschte, also A, weil wir

10:36.700 --> 10:39.840
wollen ja A plus B rechnen, deswegen haben wir A als oberstes drauf

10:39.840 --> 10:44.100
gepusht, plus das jetzt neue oberste Element vom Stack.

10:44.340 --> 10:46.920
Naja, in dem Fall dann B, das vorletzt drauf gepushte.

10:47.840 --> 10:53.360
Ergebnis wird hier automatisch wieder auf den Stack oben drauf

10:53.360 --> 10:53.580
gemacht.

10:53.740 --> 10:57.840
Also der Stack momentan, das enthält als top of stack, als oberstes

10:57.840 --> 11:01.480
Element das Ergebnis der Addition und was auch immer vorher drauf war,

11:01.480 --> 11:02.380
interessiert uns hier nicht.

11:04.900 --> 11:09.100
Jetzt muss ich das oberste Element runter holen und es irgendwie in

11:09.100 --> 11:10.320
Speicher C retten.

11:11.640 --> 11:14.500
Zum einen muss ich das sowieso machen, weil das war ja die Aufgabe,

11:14.640 --> 11:15.740
ich soll es in Speicher retten.

11:16.280 --> 11:20.220
Zum anderen muss ich es aber auch deswegen machen, weil hier die

11:20.220 --> 11:21.360
Reihenfolge ist relevant.

11:21.700 --> 11:25.260
Ich kann mir hier bei der Subtraktion, da kommt es ja auf die

11:25.260 --> 11:28.140
Reihenfolge der beiden Operandinnen, kann ich jetzt nicht mehr sagen,

11:28.240 --> 11:31.360
ob das eine minus andere oder umgekehrt, habe ich keine

11:31.360 --> 11:32.320
Ausdrucksmöglichkeit.

11:32.720 --> 11:36.760
Es wird top of stack minus das nächste top of stack gerechnet, in

11:36.760 --> 11:38.000
genau dieser Reihenfolge.

11:38.540 --> 11:41.240
Bei den Registern, zum Beispiel hier, kann ich bei Subtraktionen, wenn

11:41.240 --> 11:43.520
ich irgendwie will, dass es umgekehrt ist, kann ich hier Register

11:43.520 --> 11:46.120
zwei, hier Register einzeln schreiben, dann habe ich gerade die andere

11:46.120 --> 11:47.020
Subtraktion gemacht.

11:47.560 --> 11:49.900
Beim Keller habe ich diese Wahl nicht.

11:50.620 --> 11:53.040
Das heißt, ich muss hier in der richtigen Reihenfolge die Elemente

11:53.040 --> 11:56.540
drauf machen, der Subtraktionsbefehl holt dann das top of stack

11:56.540 --> 12:00.700
runter, das ist das zuletzt drauf gepushte, c minus, das nächste top

12:00.700 --> 12:02.740
of stack wäre dann b, c minus b.

12:02.980 --> 12:05.600
Ergebnis kommt auf den Stack drauf und wird hier runtergeholt und in

12:05.600 --> 12:06.300
den Hauptspeicher geschrieben.

12:07.100 --> 12:10.680
Das sind die verschiedenen Architekturen in einer Kurzzusammenfassung.

12:13.060 --> 12:17.360
Gut, bis dahin waren wir gekommen und jetzt machen wir weiter, was es

12:17.360 --> 12:19.720
bei der ISA sonst noch alles gibt.

12:21.080 --> 12:24.120
Einmal gibt es Datentypen, einiges davon haben wir schon gesehen, zwei

12:24.120 --> 12:26.060
Komplimenten und so, schnick, schnack, gucken uns nicht mehr im Detail

12:26.060 --> 12:28.700
an, aber einfach mal gibt es hier natürlich auch.

12:28.960 --> 12:33.500
Man muss in der ISA ganz konkret spezifizieren, wie die Datentypen,

12:33.560 --> 12:36.920
welche es gibt, welche von der Hardware, in welcher Form auch immer,

12:37.100 --> 12:41.480
supported werden sollen und wie die im Speicher abzulegen sind oder

12:41.480 --> 12:43.920
abgelegt werden, wie die im Registerfile abgelegt sind.

12:44.440 --> 12:47.660
Da gibt es ein paar ärgerliche Details, sehen wir eins gleich noch.

12:49.320 --> 12:54.180
Der Wertebereich muss angegeben werden, also wie groß ist die, also

12:54.180 --> 12:56.880
von bis ist er, kann gespeichert werden.

13:03.400 --> 13:07.160
Ja gut, also Datentypen, mit Datentypen sind immer die gemeint, die in

13:07.160 --> 13:08.900
Hardware wirklich direkt supported werden.

13:08.960 --> 13:11.300
Es kann natürlich andere Datentypen geben, die nicht supported werden,

13:11.380 --> 13:14.640
zum Beispiel viele Microcontroller supporten keinen Floating Point.

13:15.060 --> 13:17.400
Natürlich gibt es trotzdem Floating Point, es wird nur nicht in

13:17.400 --> 13:18.020
Hardware supported.

13:18.320 --> 13:20.440
Wenn dann der Programmierer irgendwas in Floating Point rechnen

13:20.440 --> 13:23.740
möchte, dann muss der Compiler einspringen oder meistens in der

13:23.740 --> 13:27.100
Bibliothek und dann wird quasi in Software die Floating Point

13:27.100 --> 13:27.960
emuliert.

13:28.140 --> 13:29.860
Das ist schrecklich langsam, aber es funktioniert.

13:30.000 --> 13:32.740
Also für den Programmierer, der kann eventuell trotzdem Floating Point

13:32.740 --> 13:35.140
benutzen, nur die Hardware supportet es halt eben nicht.

13:35.220 --> 13:37.420
Also hier ist mit Datentypen immer nur das gemeint, was die Hardware

13:37.420 --> 13:39.960
nativ selber anbieten kann, unterstützen kann.

13:42.800 --> 13:44.120
Mal sehen, was haben wir noch?

13:47.040 --> 13:49.580
Ja, also ganz normal für einen Datentyp.

13:50.700 --> 13:55.280
Also wenn ich eine Addition habe, dann wird die eine Integer-Addition

13:55.280 --> 13:57.020
machen, nicht eine Floating Point-Addition.

13:57.220 --> 13:58.680
Da gibt es dann quasi verschiedene Befehle.

13:58.800 --> 13:59.900
Befehle gucken wir uns nachher noch an.

14:00.420 --> 14:01.600
Würde eben verschiedene Befehle geben.

14:01.680 --> 14:04.620
Einmal für Integer-Addition, einmal für Floating Point-Addition.

14:05.000 --> 14:07.520
Es gibt teilweise auch ein paar bizarre Unterschiede.

14:07.600 --> 14:10.080
Es gibt zum Beispiel Additionen für Unsigned und Additionen für

14:10.080 --> 14:13.860
Signed, wo man sich fragt, zweier Kompliment muss sich doch nicht

14:13.860 --> 14:17.840
unterscheiden, ob das jetzt ein Vorzeichen hat, die Zahl oder nicht.

14:17.940 --> 14:19.900
Warum gibt es dann zwei Assembler-Befehle dafür?

14:20.400 --> 14:22.200
Also das sehen wir nachher noch.

14:22.500 --> 14:24.700
Da gibt es einen Grund für, der Grund hat sich nur in einer sehr

14:24.700 --> 14:27.700
schlechten Namensgebung der beiden Assembler-Befehle ausgedrückt

14:27.700 --> 14:28.160
letztendlich.

14:28.620 --> 14:29.400
Gut, wie gesagt, sehen wir noch.

14:31.400 --> 14:35.000
Eine Alternative, einfach nur der Vollständigkeit halber.

14:35.260 --> 14:37.900
Man kann sich sowas vorstellen, ist aber nicht unbedingt üblich.

14:39.140 --> 14:44.120
Man kann sich vorstellen, dass die Datentypen, also dass die Daten,

14:44.240 --> 14:48.520
wenn sie gespeichert werden, ihren Typ wissen und den auch mit sich

14:48.520 --> 14:49.020
mitführen.

14:49.220 --> 14:53.060
Also da steht quasi nicht nur 32 Bit Einsen und Nullen, da stehen noch

14:53.060 --> 14:56.660
ein paar mehr Bits und die paar mehr Bits sagen, diese 32 Bits sind

14:56.660 --> 15:00.320
eine Floating Point-Zahl oder diese 32 Bits sind eine Unsigned Integer

15:00.320 --> 15:00.700
-Zahl.

15:01.220 --> 15:05.180
Und je nachdem, was dieser Typ, der wirklich explizit mitgeführt wird,

15:05.260 --> 15:09.100
sagt, wird dann der Assembler-Befehl Addition entweder die Integer

15:09.100 --> 15:12.200
-Einheit drauf losrennen oder die Floating Point-Einheit.

15:12.740 --> 15:18.320
Oder im Fall von einem Shift-Befehl, also Shift rechts, wo man quasi

15:18.320 --> 15:21.080
überlegen muss, will ich logisch shiften, immer Nullen reinschieben

15:21.080 --> 15:23.980
oder will ich arithmetisch shiften, will ich das Vorzeichen erhalten,

15:24.120 --> 15:26.980
eventuell Einsen reinshiften, falls das MSB vorher schon eins war.

15:27.560 --> 15:30.900
Da würde quasi als Datentyp stehen, ist das eine Integer-Zahl, ist es

15:30.900 --> 15:33.700
eine Signed Integer-Zahl oder ist es eine Unsigned Integer-Zahl oder

15:33.700 --> 15:34.620
ist es ein Bit-Vektor.

15:35.060 --> 15:38.040
Und je nachdem würde der Shift-Rechts-Befehl dann quasi das MSB

15:38.040 --> 15:40.980
erhalten oder einfach pauschalen Nullen reinschieben.

15:41.740 --> 15:44.880
Also quasi die Datentypen, die Daten wissen ihren Typ und die

15:44.880 --> 15:46.920
Operationen lesen den einfach aus.

15:48.920 --> 15:51.780
Hat sich nicht, also gibt es nicht, aber könnte man sich so

15:51.780 --> 15:52.180
vorstellen.

15:52.280 --> 15:54.400
Wurde mal so, naja, aber macht irgendwie nicht so den ultimativen

15:54.400 --> 15:54.600
Sinn.

15:55.260 --> 15:57.500
Soll sich der Compiler drum kümmern, muss ich weniger Bits speichern.

15:58.140 --> 15:59.360
Man kann es aber auch so machen.

16:01.380 --> 16:07.100
Gut, ja, wie schon gesagt, es können nicht notwendigerweise alle

16:07.100 --> 16:11.020
Hardware -Architekturen, alle Datentypen, die werden dann auch

16:11.020 --> 16:13.880
explizit in der ISA halt eben als nicht supported oder einfach, die

16:13.880 --> 16:14.580
stehen einfach nicht drin.

16:14.920 --> 16:16.300
Somit ist klar, dass sie nicht supported sind.

16:17.360 --> 16:20.880
Und wenn sie nicht supported sind, dann müssen sie in Software

16:20.880 --> 16:21.580
emuliert werden.

16:22.360 --> 16:24.480
Jetzt gibt es da noch einen Kompromiss.

16:25.840 --> 16:29.280
Ein Kompromiss im Sinne von, also konkretes Beispiel für Floating

16:29.280 --> 16:29.800
Point einfach.

16:30.800 --> 16:34.540
Es gibt einige Architekturen, die keine Floating Point-Hardware

16:34.540 --> 16:38.480
anbieten, weil Floating Point-Multiplikation, Floating Point-Division

16:38.480 --> 16:39.700
eine ganz schreckliche Sache ist.

16:39.780 --> 16:42.120
Schrecklich im Sinne von, braucht viel Fläche.

16:42.440 --> 16:44.740
Die ALU, die das rechnet, braucht viel Fläche.

16:45.260 --> 16:48.020
Wenn ich jetzt irgendwie eine winzig kleine CPU habe, die für sehr,

16:48.140 --> 16:51.720
sehr, sehr kleine Geräte, weiß nicht, hier diesen Presenter, wofür

16:51.720 --> 16:52.460
braucht der Floating Point?

16:52.540 --> 16:55.160
Da wird eine sehr kleine CPU drin sein, da ist eine CPU drin, muss ja

16:55.160 --> 16:57.360
irgendwie, aber die wird halt sehr klein sein, die braucht jetzt nicht

16:57.360 --> 16:58.420
unbedingt Floating Point-Hardware.

16:58.900 --> 17:05.940
Was dann die Emulation machen muss, ist sowas wie die Daten aus dem

17:05.940 --> 17:09.060
Speicher, 32-Bit, könnte eine Single-Precision-Float sein, in

17:09.060 --> 17:11.840
irgendein General-Purpose-Register reinladen und eine Funktion

17:11.840 --> 17:13.160
aufrufen, die damit rechnet.

17:13.440 --> 17:14.480
Das ist irgendwie okay.

17:15.340 --> 17:17.840
Jetzt gab es bei Floating Point aber nicht nur Single-Precision, es

17:17.840 --> 17:20.100
gab auch Double-Precision, sind schon 64-Bit.

17:20.500 --> 17:24.440
Also müsste ich da schon quasi zwei Werte aus dem Speicher in zwei

17:24.440 --> 17:26.720
verschiedene Register laden, dann irgendwie mit den beiden

17:26.720 --> 17:31.040
verschiedenen Registern als eine 64-Bit-Zahl interpretieren und damit

17:31.040 --> 17:31.800
rumwurschteln.

17:32.220 --> 17:36.180
Also wir haben uns überlegt, in dem Floating Point gab es ja diese

17:36.180 --> 17:40.860
Aufteilung in das Vorzeichenbild und Mantisse und Exponent, also

17:40.860 --> 17:41.580
Exponent.

17:42.860 --> 17:46.840
Und wenn ich jetzt zwei 32-Bit-Zahlen habe, dann habe ich irgendwo an

17:46.840 --> 17:50.840
einer beliebigen Stelle, wo dürfte das sein, vielleicht hier, habe ich

17:50.840 --> 17:53.140
die eine 32-Bit-Zahl und die andere 32-Bit-Zahl.

17:53.480 --> 17:56.860
Das heißt, ich muss irgendwie immer mit so einer Mischung aus den 32

17:56.860 --> 18:00.600
-Bit hier und noch ein paar Bit von dort rechnen, um meine Mantisse

18:00.600 --> 18:01.400
zusammenzukriegen.

18:01.700 --> 18:04.100
Das ist ein ziemliches Gewurschteln, wenn man das in Software

18:04.100 --> 18:04.820
ausrechnen will.

18:05.680 --> 18:08.300
Noch schlimmer wird es, wenn ich, es gab ja noch Floating Point

18:08.300 --> 18:11.740
Extended, 80-Bit-Format, das passt ja nicht mal mehr in zwei Register

18:11.740 --> 18:14.040
rein, da brauche ich drei Register und das dritte ist nur halb voll

18:14.040 --> 18:14.980
und alles sehr hässlich.

18:15.360 --> 18:17.540
Was manche Architekturen machen, ist das folgende.

18:18.100 --> 18:22.020
Sie bieten ein spezielles Register-File an für Floating Point-Zahlen,

18:22.740 --> 18:27.100
wo dann 44-Bit als einen Block oder sogar 80-Bit als einen Register

18:27.100 --> 18:28.640
quasi angeboten werden.

18:28.760 --> 18:28.920
Warum?

18:29.500 --> 18:35.340
Naja, ein Register-File ist nicht umsonst, aber nicht so groß.

18:35.540 --> 18:38.400
So ein paar 80-Bit-Register, das kann man schon machen.

18:39.160 --> 18:43.360
Und die bieten halt nicht die Alus an, die Rechen-Alus, die dann

18:43.360 --> 18:45.800
wirklich die Operationen durchführen, aber einfach dadurch, dass sie

18:45.800 --> 18:49.080
das Register-File anbieten, machen sie die Software-Emulation, die das

18:49.080 --> 18:51.660
Ganze dann in Software emulieren muss, deutlich einfacher.

18:52.100 --> 18:56.960
Das ist dieser Kompromiss dazwischen, der hier im unteren Teil erwähnt

18:56.960 --> 18:57.160
wird.

18:57.720 --> 19:02.860
Also Spezial-Register-File, hier haben wir es glaube ich auch, aber

19:02.860 --> 19:04.960
trotzdem die Rechnung muss in Software durchgeführt werden.

19:08.900 --> 19:14.360
Ja, was gibt es typischerweise für Größen 8, 16, 62, das sind so die

19:14.360 --> 19:17.640
üblichen Bits, die üblichen Formate, auf denen gearbeitet wird.

19:18.280 --> 19:24.640
Eine eventuell ärgerliche Sache, dieses Wort namens Wort, was hier als

19:24.640 --> 19:29.980
32 -Bit steht, ist alles hersteller-architektur-spezifisch.

19:30.500 --> 19:36.680
Zum Beispiel benutzt Intel seit Menschengedenken, meinen die 16-Bit,

19:36.740 --> 19:37.800
wenn die von Wort reden.

19:38.220 --> 19:42.680
Das war bei denen einfach beim 80, fragt mich nicht, 86 so und das

19:42.680 --> 19:43.760
haben die halt so beibehalten.

19:44.080 --> 19:47.220
Die haben halt irgendwann, klar, haben die auch 32-Bit-Architekturen

19:47.220 --> 19:51.100
angeboten, die irgendwann auch 64-Bit-Architekturen, aber aus Legacy

19:51.100 --> 19:54.540
-Gründen haben die einfach gesagt, Wort war damals 16-Bit und das

19:54.540 --> 19:55.940
bleibt auch so, period.

19:56.760 --> 20:03.160
Wohingegen die meisten anderen mit Wort die Größe meinen, auf der die

20:03.160 --> 20:04.160
CPU arbeitet.

20:04.680 --> 20:08.840
Also wenn ich 32-Bit-Register habe und eine 32-Bit-ALU habe, dann

20:08.840 --> 20:11.200
meine ich mit Wort auch meistens 32-Bit.

20:11.660 --> 20:15.420
Dann habe ich auch meistens einen 32-Bit-Datenbus also quasi alles auf

20:15.420 --> 20:16.420
32 -Bit ausgelegt.

20:17.060 --> 20:20.620
Und kleinere CPUs, die wirklich nur 16-Bit haben, die meinen dann mit

20:20.620 --> 20:21.660
Wort auch wirklich 16-Bit.

20:21.760 --> 20:23.140
Also ist unterschiedlich.

20:23.780 --> 20:27.080
Mit diesem Begriff muss man aufpassen, man muss schauen, wie ist es

20:27.080 --> 20:27.600
definiert.

20:28.000 --> 20:29.060
Es gibt keine Alternative.

20:29.480 --> 20:33.140
Meistens 32-Bit gemeint, ab und zu auch was anderes, bei Intel immer

20:33.140 --> 20:33.480
16.

20:36.700 --> 20:39.420
Wie sieht es aus im IA32 von Intel?

20:39.720 --> 20:40.700
Keine Überraschungen.

20:42.080 --> 20:44.920
Byte, Wort, also zwei Byte bei denen.

20:45.520 --> 20:48.060
Double Word wären dann quasi die normalen 32-Bit.

20:48.420 --> 20:49.980
Quad Word wären dann 64-Bit.

20:50.560 --> 20:53.480
Setzt sich einfach immer durch Verdopplungen aus dem anderen zusammen.

20:53.700 --> 20:56.760
Ist keine echte Überraschung drin, nur einfach mal muss angegeben

20:56.760 --> 20:57.020
sein.

20:57.160 --> 21:00.220
Könnte ja völlig wild und willkürlich anders definiert werden.

21:00.660 --> 21:03.360
Also zur ISA-Spezifikation gehört es dazu.

21:03.880 --> 21:06.340
Nur in dem Fall sind keine Überraschungen drin, müssen wir jetzt nicht

21:06.340 --> 21:07.080
großartig angucken.

21:08.900 --> 21:09.600
Was gibt es noch?

21:10.100 --> 21:10.360
Bit.

21:12.220 --> 21:16.660
Es gibt wirklich Architekturen, wo man Assemblerbefehle hat, mit denen

21:16.660 --> 21:20.460
man ganz gezielt einzelne Bits von dem Register manipulieren kann.

21:20.540 --> 21:22.240
Da muss man eben auch sagen, welches Bit das ist.

21:22.980 --> 21:26.480
Also kann man einfach irgendeins, das ITE-Bit, wie auch immer, in dem

21:26.480 --> 21:30.020
Register setzen, clearen oder testen, vergleichen.

21:31.140 --> 21:36.420
Bitvektoren, also zusammenhängende Bereiche von Bits, gibt es auch

21:36.420 --> 21:37.960
einige Architekturen, die es unterstützen.

21:38.340 --> 21:43.940
Da wird dann immer angegeben, der Offset vom 0.10-Bit, also könnte

21:43.940 --> 21:46.700
jetzt sein im Register oder im Speicher, sagen wir mal im Register für

21:46.700 --> 21:50.640
den Moment, der Offset vom, also welches Register, dann haben wir hier

21:50.640 --> 21:55.520
zum Beispiel 32-Bit-Register, der Offset vom 0.10-Bit und die Länge.

21:55.960 --> 21:59.120
Damit ist dann dieser Bitvektor definiert und man kann damit irgendwas

21:59.120 --> 21:59.780
gemacht werden.

22:00.600 --> 22:03.760
Zum Beispiel könnte man da quasi eine Und-Operation machen im gleichen

22:03.760 --> 22:06.940
Register zwischen dem Bitvektor von hier bis hier und dem Bitvektor

22:06.940 --> 22:07.840
von hier bis hier.

22:08.000 --> 22:10.260
Sollten bitte die gleiche Länge haben, dann kann man die quasi

22:10.260 --> 22:10.960
zusammentödeln.

22:15.910 --> 22:20.550
Ganz normale Zahlen, vorzeichnose Dualzahlen, Zweier-Komplement

22:20.550 --> 22:21.350
-Dualzahlen.

22:21.910 --> 22:25.470
Ganz wie man es gewohnt ist, keine Überraschung, aber wie gesagt, muss

22:25.470 --> 22:29.050
in ISA spezifiziert werden, brauchen wir jetzt hier nicht weiter

22:29.050 --> 22:32.830
anschauen, weil es wirklich genau so spezifiziert ist in dem Beispiel,

22:32.930 --> 22:34.750
was wir hier haben, wie man es erwarten würde.

22:38.590 --> 22:40.910
BCD-Ziffern hatten wir auch schon gesehen, da gibt es jetzt nochmal

22:40.910 --> 22:41.950
zwei verschiedene Varianten.

22:42.090 --> 22:43.850
Es gibt die gepackte und die ungepackte Form.

22:43.930 --> 22:47.290
Hat erstmal auf die BCD-Ziffer selber überhaupt keine Auswirkung,

22:47.530 --> 22:50.490
sondern es geht hier ausschließlich darum, wie die BCD-Ziffern

22:50.490 --> 22:53.930
gespeichert werden, im Register und auch im Hauptspeicher nachher.

22:55.910 --> 23:00.790
Gepackte Form heißt einfach, ihr erinnert euch, eine BCD-Ziffer war

23:00.790 --> 23:02.310
ein 4-Bit-Wert.

23:02.430 --> 23:08.270
Mit dem 4-Bit-Wert kann man die Zahlen 0 bis F angeben und man nutzt

23:08.270 --> 23:14.150
aber nur den Bereich 0 bis 9, also quasi eine Dezimalziffer, da

23:14.150 --> 23:15.010
brauche ich 4-Bit für.

23:15.590 --> 23:18.610
Da die meisten Architekturen auf Bytes arbeiten, könnte man sich

23:18.610 --> 23:23.510
überlegen, bietet sich doch an, zwei BCD-Ziffern in ein Byte zusammen

23:23.510 --> 23:24.690
zu packen.

23:25.090 --> 23:27.070
Und das ist die gepackte Form.

23:27.250 --> 23:30.550
Zwei BCD-Zahlen in einem Byte zusammengefasst.

23:31.950 --> 23:32.870
Mehr steht hier auch nicht.

23:33.270 --> 23:37.730
Das ist BCD-Zahl 0, das ist BCD-Zahl 1 und so passen die in einen 8

23:37.730 --> 23:38.990
-Bit -Byte rein.

23:39.490 --> 23:43.070
Wenn ich ein 16-Bit habe, dann hätte ich halt eben BCD-Zahl 0, 1, 2

23:43.070 --> 23:48.470
und 3, also vier BCD-Zahlen in einem 16-Bit-Dingsy, würde Wort sagen.

23:49.010 --> 23:53.250
Und hier halt eben acht BCD-Zahlen in einem 32-Bit-Dingsy.

23:55.870 --> 23:57.970
Achso hier, ich hätte ja schon mal gesagt, warum man BCD-Zahlen

23:57.970 --> 24:00.670
braucht, diese 0,1, mal versucht irgendwie hinzuschreiben.

24:00.990 --> 24:02.750
Rechnet mal weiter, das Ding terminiert nicht.

24:03.990 --> 24:07.330
Okay, wenn es die gepackte Form gibt, muss es logischerweise auch die

24:07.330 --> 24:08.650
ungepackte Form geben.

24:09.890 --> 24:14.310
Die sieht einfach so aus, weil Byte halt nun mal die elementare Größe

24:14.310 --> 24:15.930
ist, auf denen die meisten Sachen arbeiten.

24:16.270 --> 24:21.230
Einfach gesagt, ich nehme eine BCD-Ziffer in einem Byte, verschwendet

24:21.230 --> 24:22.530
offensichtlich Speicherplatz.

24:22.650 --> 24:24.190
Ich hätte ja zwei reinpassen können.

24:24.650 --> 24:25.670
Warum mache ich das?

24:26.230 --> 24:27.070
Ganz einfach.

24:27.370 --> 24:31.230
Es gibt CPU-Architekturen, die haben dedizierte Hardwarebefehle, um

24:31.230 --> 24:35.350
auf BCD-Zahlen zu arbeiten und die können dann für gewöhnlich auch gut

24:35.350 --> 24:36.790
mit den gepackten Sachen umgehen.

24:37.510 --> 24:42.450
Wenn es das nicht gibt, wenn ich quasi nur Integer, also Zweier

24:42.450 --> 24:45.890
-Komplement -Arithmetik habe, Addition, Subtraktion und so weiter,

24:46.590 --> 24:51.690
dann will ich nicht erst die BCD-Zahlen auseinander, ich muss ja quasi

24:51.690 --> 24:55.250
jede BCD-Zahl einzeln berechnen, weil ich ja quasi eine Zweier

24:55.250 --> 24:57.530
-Komplement -Berechnung mache und danach muss ich sie wieder

24:57.530 --> 24:59.790
konvertieren, damit es wieder eine BCD-Zahl wird.

25:00.490 --> 25:03.370
Und dafür muss ich das erst auseinanderwurschteln.

25:03.690 --> 25:10.570
Wenn ich quasi hier eine BCD-Zahl und hier eine BCD-Zahl habe und das

25:10.570 --> 25:18.650
jetzt einfach mal addiere mit irgendeiner anderen BCD-Zahl, dann würde

25:18.650 --> 25:24.590
ja quasi die MSB von dieser BCD-Zahl bei der Addition gegebenenfalls

25:24.590 --> 25:28.850
überlaufen hier rüber, was aber eine Zweier-Komplement-Überlauf wäre,

25:29.030 --> 25:32.250
weil es eine Zweier-Komplement-Arithmetik war und es ist danach

25:32.250 --> 25:33.670
einfach keine BCD-Zahl mehr.

25:33.890 --> 25:37.070
Rechnet doch einfach mal irgendwelche zwei BCD-Zahlen zusammen, so

25:37.070 --> 25:42.430
dass halt eben bei dem vierten Bit halt eben ein Überlauf passiert.

25:42.830 --> 25:45.310
Also keine Ahnung, neun plus neun als einfaches Beispiel.

25:46.010 --> 25:48.230
Das würde halt eben überlaufen und hier irgendwas wurschteln.

25:48.550 --> 25:51.970
Also das wäre am Ende keine korrekte BCD-Darstellung mehr, die muss

25:51.970 --> 25:53.090
wiederhergestellt werden.

25:53.610 --> 25:56.490
Und das mache ich, indem ich es gar nicht zu einem Überlauf kommen

25:56.490 --> 26:00.890
lasse, sondern indem ich vorher die BCD-Zahlen trenne und einzeln, auf

26:00.890 --> 26:02.290
jeder Ziffer einzeln rechne.

26:02.770 --> 26:04.330
Und dieses Trennen ist halt extra Arbeit.

26:04.490 --> 26:07.990
Ich muss die quasi mit Und- und Shift-Operationen ausmaskieren.

26:08.570 --> 26:12.150
Und dann kann es sich lohnen, wenn ich vielleicht Zeit sparen will und

26:12.150 --> 26:15.890
genug Speicher habe, also mir diese schlechtere Speicherauslastung von

26:15.890 --> 26:20.170
der ungepackten Form egal ist, dass ich gleich sage, ich speicher ein

26:20.170 --> 26:21.830
BCD -Ziffer pro Byte.

26:22.010 --> 26:24.870
Dann muss ich das auseinander dividierende Ziffer nicht machen, das

26:24.870 --> 26:25.510
spare ich mir dann.

26:26.110 --> 26:27.990
Also hat dafür durchaus auch Vorteile.

26:29.530 --> 26:31.530
Aufwand gegen Speicherplatz.

26:34.610 --> 26:37.610
Gleitkommazahlen hatten wir schon gesehen, da ist auch keine

26:37.610 --> 26:38.710
Überraschung drin.

26:38.970 --> 26:41.650
Ganz normal, wie IEEE sagt, einfache doppelte Genauigkeit.

26:44.490 --> 26:52.730
Bitvektoren kann man auch im Speicher angeben und wenn die im

26:52.730 --> 26:55.850
Hauptspeicher sind, dann dürfen die auch gerne länger als vier Bytes

26:55.850 --> 26:56.050
sein.

26:56.190 --> 26:59.350
Die können auch, naja, nicht beliebig, aber sehr viel länger sein.

27:02.390 --> 27:03.710
Wie werden die angegeben?

27:04.110 --> 27:06.750
Die werden immer angegeben im Hauptspeicher.

27:07.550 --> 27:10.430
Der Hauptspeicher ist meistens byte-adressierbar, das sehen wir gleich

27:10.430 --> 27:13.570
noch, aber was das heißt, ist einfach, jedes Byte des Hauptspeichers

27:13.570 --> 27:14.830
hat eine eindeutige Adresse.

27:15.450 --> 27:20.590
Und so wird dann angegeben, welches Byte im Hauptspeicher zum ersten

27:20.590 --> 27:25.850
Mal Teile von dem Bitvektor enthält, also die Byte-Adresse, wo es

27:25.850 --> 27:26.290
anfängt.

27:26.990 --> 27:31.310
Und dann innerhalb von dem Byte, das Bitfeld, was ich angeben muss,

27:31.390 --> 27:34.030
fängt nicht notwendigerweise mit Bit Null an, es könnte verschoben

27:34.030 --> 27:34.350
sein.

27:34.750 --> 27:37.030
Also es ist die Frage, wie viel ist es verschoben?

27:37.150 --> 27:39.450
Offset, der Betrag des Verschiebens.

27:40.890 --> 27:46.610
Also wie weit innerhalb des ersten Bytes fängt das Bitfeld an und dann

27:46.610 --> 27:48.150
einfach, wie lang ist es?

27:50.010 --> 27:52.950
Da können teilweise ziemlich lange, wir sehen gleich noch bei IR32,

27:53.190 --> 27:56.150
können da sehr lange Bitfelder spezifiziert werden.

27:59.810 --> 28:00.370
String.

28:01.690 --> 28:05.430
Viele Architekturen haben keine speziale Hardware für String, da muss

28:05.430 --> 28:06.610
sich die Software selber darum kümmern.

28:06.670 --> 28:09.930
Einfach Arrays von Chars und dann mach halt was damit.

28:10.510 --> 28:14.730
Es gibt aber tatsächlich auch Architekturen, die spezielle Assembler

28:14.730 --> 28:18.070
-Befehle haben, um mit Strings zu arbeiten, um Strings zu vergleichen,

28:18.190 --> 28:19.910
zu konkretisieren und sonst irgendwie sowas.

28:21.610 --> 28:25.550
Und die stellen natürlich die Strings auch ganz normal aus einzelnen

28:25.550 --> 28:30.870
ASCII -Zeichen dar, merken sich dann aber für gewöhnlich, wie viel

28:30.870 --> 28:32.530
Zeichen der String lang ist.

28:36.420 --> 28:39.400
Gut, ein Beispiel von IR32.

28:39.920 --> 28:42.600
Eigentlich ist alles so, wie wir gerade gesehen haben.

28:42.720 --> 28:47.060
Es gibt ganz normale Unsigned Integers, das werden auch Ordinalzahlen

28:47.060 --> 28:47.440
genannt.

28:49.060 --> 28:53.300
Es gibt Zweierkomplement, also Unsigned oder Signed Integers.

28:53.640 --> 28:56.380
Es gibt diese Packed-BCDs, Unpacked-BCDs.

28:57.460 --> 28:59.160
Jetzt gibt es noch ein paar andere Sachen.

28:59.940 --> 29:02.440
Es gibt Near-Pointer und Farb-Pointer.

29:03.140 --> 29:07.740
Pointer im Sinne von Zeiger, also auch eine Zahl letztendlich, aber

29:07.740 --> 29:12.260
die quasi als Hinweis, als Adresse, als Offset einer Adresse zu

29:12.260 --> 29:12.820
verstehen ist.

29:13.000 --> 29:18.060
Also eine Zahl, die irgendwo in den Speicher zeigt.

29:18.540 --> 29:22.540
Und zwar entweder in der näheren Umgebung, mir, oder wo ganz woanders

29:22.540 --> 29:23.620
im Speicher fahr.

29:24.800 --> 29:27.120
Und was heißt jetzt nähere Umgebung, das lässt sich jetzt wirklich

29:27.120 --> 29:27.720
schwer erklären.

29:28.100 --> 29:33.200
Aber bei X86 ist der Speicherzugriff über unangenehm viele Stufen

29:33.200 --> 29:34.260
hierarchisch aufgebaut.

29:34.400 --> 29:36.120
Und einer der ersten Stufen ist das Segment.

29:36.780 --> 29:39.580
Also es gibt zum Beispiel das Code-Segment, das Daten-Segment, das

29:39.580 --> 29:43.840
Stack -Segment und man muss erst mal sagen bei jedem Zugriff, welches

29:43.840 --> 29:45.480
von diesen Segmenten meinst du eigentlich.

29:46.000 --> 29:48.520
Das Segment ist so gesehen wirklich nur eine reine gedachte

29:48.520 --> 29:49.800
Abstraktionsebene.

29:50.300 --> 29:53.260
Es gibt am Ende, am Ende gibt es ganz normal den Speicher, irgendeinen

29:53.260 --> 29:57.180
DRAM -Riegel mit, keine Ahnung, 2x32-1 bytes oder sowas.

29:57.720 --> 30:00.980
Und da gibt es eben eine logische Unterteilung, wo gesagt wird, ein

30:00.980 --> 30:03.860
gewisser Bereich davon benutzen wir als Programm-Segment, gewissen

30:03.860 --> 30:05.800
anderen Bereich benutzen wir als Daten-Segment.

30:06.160 --> 30:10.580
Und dann wissen wir ja, das Programm-Segment sollte nicht schreibbar

30:10.580 --> 30:10.780
sein.

30:10.900 --> 30:13.980
Ich meine, ich lade einmal das Programm da rein und danach sollte es

30:13.980 --> 30:14.920
nicht mehr schreibbar sein.

30:15.280 --> 30:18.000
Wenn jemand versucht, das Programm-Segment zu schreiben, dann ist das

30:18.000 --> 30:21.960
eventuell was Unangebrachtes und man sollte mal dem Anwender darauf

30:21.960 --> 30:24.440
hinweisen, dass da eventuell jemand versucht, Lützen zu tun.

30:24.440 --> 30:28.880
Genauso das Daten-Segment, das wird für gewöhnlich nicht ausgeführt.

30:29.160 --> 30:33.420
Es gibt gewisse Schutzmechanismen, bei denen man sagen kann, das Daten

30:33.420 --> 30:35.340
-Segment ist nicht ausführbar, das Programm-Segment ist nicht

30:35.340 --> 30:35.980
überschreibbar.

30:36.280 --> 30:39.320
Wenn jemand versucht, das Daten-Segment auszuführen, dann geht hier

30:39.320 --> 30:41.820
was gehöre schief, Programm beenden, fertig.

30:42.220 --> 30:43.860
Dann kann zumindest kein Schaden gemacht werden.

30:44.740 --> 30:46.640
Dafür haben die mal diese Segmente eingeführt gehabt.

30:48.680 --> 30:55.300
Und mit dem Near-Point ist einfach gemeint, innerhalb eines

30:55.300 --> 30:59.140
angegebenen Segments ein Offset darin, also eine Adresse innerhalb von

30:59.140 --> 31:00.060
einem angegebenen Segment.

31:01.160 --> 31:06.760
Wohingegen der Farb-Pointer setzt sich aus mehreren Sachen zusammen.

31:07.100 --> 31:10.340
Nämlich einmal ein Segment-Selektor, mit dem gesagt werden kann,

31:10.460 --> 31:11.980
welches Segment soll es denn sein.

31:12.120 --> 31:13.980
Ist also gar nicht hard-codiert, sondern wiederum eine

31:13.980 --> 31:15.340
Indirektionsstufe sozusagen.

31:16.040 --> 31:21.020
Ein Register, in dem drinsteht, welches Segment es sein soll.

31:21.480 --> 31:24.040
Könnte also bei dem gleichen Assembler-Befehl mal dieses Segment, mal

31:24.040 --> 31:27.080
jenes Segment gemeint sein, je nachdem, was in dem Segment-Selektor

31:27.080 --> 31:27.660
drinsteht.

31:28.160 --> 31:30.380
Und dann ein Offset innerhalb des Segmentes.

31:31.620 --> 31:35.080
Gut, aber das ist wirklich sehr x86-spezifisch.

31:35.280 --> 31:38.720
Bit-Felder hatten wir schon gesehen, Strings hatten wir schon gesehen.

31:42.660 --> 31:49.380
Die Länge von so Strings, mal sehen, eines Bit-Strings, so Strings im

31:49.380 --> 31:52.140
Sinne von ASCII-Zeichen oder auch Bit-Strings, Bit-Vektoren.

31:52.660 --> 31:55.820
Ein Bit-String kann irgendwo in einem Byte beginnen, also hat ein

31:55.820 --> 32:01.940
Offset innerhalb des Bytes und der kann 32-1 Bits lang sein, also sehr

32:01.940 --> 32:02.460
lang.

32:02.560 --> 32:04.420
Also ich kann sehr lange Bit-Vektoren angeben.

32:05.860 --> 32:07.960
Bei einem String kann ich quasi den ganzen Speicher füllen.

32:08.580 --> 32:11.000
Und dann gibt es ganz normal Floating-Points, Single-Position, Double

32:11.000 --> 32:11.460
-Position und so weiter.

32:11.780 --> 32:13.300
Also da ist nichts Ungewöhnliches mehr.

32:15.880 --> 32:20.600
Bei der MIPS-Architektur ist es in vielerlei Hinsicht einfacher, da

32:20.600 --> 32:21.140
gibt es weniger.

32:21.860 --> 32:28.180
Da gibt es ganz normal 64-Bit-Zweierkomplement-Integer-Zahlen und es

32:28.180 --> 32:31.160
gibt Single- und Double-Position-Gleitkommer-Zahlen.

32:34.240 --> 32:38.920
Und wenn man die Laden speichern will, also zwischen Hauptspeicher und

32:38.920 --> 32:42.560
Registersatz transferieren will, dann werden die automatisch gefüllt

32:42.560 --> 32:46.760
im Sinne von, ich könnte sagen, ein Assemblerbefehl, der heißt Lade

32:46.760 --> 32:51.000
-Byte, aber mein Register ist vielleicht 74-Bit groß, also deswegen

32:51.000 --> 32:51.800
MIPS -64.

32:52.140 --> 32:56.080
Mein Register ist 64-Bit groß, ich sage Lade-Byte, dann heißt das

32:56.080 --> 33:00.960
einfach, in mein 64-Bit großes Register soll das Byte vom Speicher

33:00.960 --> 33:04.960
geladen werden und der Rest, was auch immer vorher drin stand, wird

33:04.960 --> 33:08.100
auf Null zurückgesetzt, damit ich quasi einen definierten Wert darin

33:08.100 --> 33:08.360
habe.

33:08.760 --> 33:11.640
Genauso, wenn ich sage Lade-Wort, dann werden 32-Bit aus dem Speicher

33:11.640 --> 33:15.000
geholt für gewöhnlich einen Datenbus-Transfer und der Rest halt eben

33:15.000 --> 33:15.720
auf Null gesetzt.

33:17.680 --> 33:21.540
Beziehungsweise nicht notwendigerweise auf Null, sondern wenn es ein

33:21.540 --> 33:25.300
Signed -Befehl ist, gibt es wirklich verschiedene Assemblerbefehle,

33:25.380 --> 33:27.660
die halt eben Unsigned oder Signed arbeiten.

33:28.100 --> 33:31.460
Wenn es ein Load-Bind-Unsigned ist, wird es auf Null gesetzt, wenn es

33:31.460 --> 33:33.820
ein Load-Bind-Signed ist, wird das Vorzeichen erhalten.

33:34.360 --> 33:35.580
Gucken wir uns das Byte hier nochmal an.

33:35.980 --> 33:39.600
Wenn quasi von dem Byte das höchste Bit eine Eins war, das würde ja

33:39.600 --> 33:42.020
beim Zweikomplement darauf schließen lassen, dass es eine negative

33:42.020 --> 33:46.060
Zahl ist, dann werden hier halt überall Einsen reingeschrieben, um das

33:46.060 --> 33:47.580
Vorzeichen zu erhalten.

33:48.420 --> 33:48.860
Gut.

33:50.380 --> 33:51.340
Ja, Datentypen.

33:51.820 --> 33:55.160
Okay, jetzt haben wir noch an ein paar Stellen schon die

33:55.160 --> 33:56.980
Speicheradressierung angeschaut.

33:57.360 --> 34:01.900
Also, es gibt offensichtlich einen Hauptspeicher und den kann man

34:01.900 --> 34:02.460
zugreifen.

34:02.680 --> 34:03.680
Sehr praktische Sache, das.

34:04.120 --> 34:07.380
Und da muss man jetzt auch spezifizieren, wie man den zugreifen darf.

34:10.340 --> 34:13.760
Ja, normal, Standard, ich wüsste jetzt keine Gegenbeispiele, aber

34:13.760 --> 34:15.000
vielleicht gibt es alles mögliche.

34:15.060 --> 34:19.780
Aber normal ist, ein Byte-adressierbarer Speicher soll heißen, jedes

34:19.780 --> 34:21.900
Byte im Speicher hat eine eigene Adresse.

34:22.620 --> 34:25.160
Auch wenn ich normalerweise vielleicht immer wortweise auf einen

34:25.160 --> 34:27.940
Speicher zugreifen würde, also das erste Wort, das zweite Wort, das

34:27.940 --> 34:31.820
dritte Wort, hat trotzdem jedes Byte eine eigene Adresse.

34:32.260 --> 34:37.320
Das heißt, wenn ich das 0-te Wort in dem Speicher laden wollen würde,

34:37.460 --> 34:43.020
das sei ein 32-Bit-breites Wort, indem ich den Namen 0-tes Wort gebe,

34:44.140 --> 34:49.800
dann würde der quasi bei Byte 0 anfangen, Byte 0, 1, 2, 3, das wäre

34:49.800 --> 34:50.700
dann dieses 0-te Wort.

34:51.060 --> 34:55.380
Und das nächste Wort, das ich dem Namen Wort 1 geben möchte, würde bei

34:55.380 --> 34:56.960
Adresse 4 anfangen.

34:57.280 --> 35:02.140
Also, sie sind Bytes 0, 1, 2, 3, Bytes 4, 5, 6, 7.

35:03.540 --> 35:07.640
Also, auch wenn ich auf Worten arbeite, die Adresse hier ist immer in

35:07.640 --> 35:07.980
Byte.

35:08.020 --> 35:11.140
Und bei Byte wäre die Adresse halt eben dann 0 und 4.

35:14.300 --> 35:18.540
Nochmal, das ist adressierbar, Byte-adressierbar.

35:18.940 --> 35:22.180
Und hier steht nicht adressierbar, hier steht organisiert.

35:22.800 --> 35:27.900
Und organisiert heißt einfach, wie ist der Speicher an den Bus

35:27.900 --> 35:28.660
angebunden.

35:29.180 --> 35:33.440
Und ich muss ja irgendwie vom Speicher über den Bus in mein Register

35:33.440 --> 35:35.280
-File die Daten reinbekommen.

35:36.200 --> 35:40.560
Und normalerweise wird das so gemacht, dass der Bus erst mal genauso

35:40.560 --> 35:43.120
breit ist wie die Register-Files.

35:43.280 --> 35:46.740
Also, wenn ich ein Register-File habe, wo das Register 34-Bit groß

35:46.740 --> 35:49.020
ist, dann habe ich im Idealfall auch einen Speicherbus, der 34-Bit

35:49.020 --> 35:49.460
groß ist.

35:49.800 --> 35:53.340
Genauso halt eben 32-Bit-Register, da habe ich meistens auch 32-Bit

35:53.340 --> 35:53.880
-Datenbus.

35:54.100 --> 35:55.380
Muss nicht, habe ich aber häufig.

36:00.300 --> 36:05.040
Es gibt manche CPUs, wo man die Busbreite, manche Systeme, wo man die

36:05.040 --> 36:06.340
Busbreite einstellen kann.

36:06.580 --> 36:11.900
Im Sinne von, die haben vielleicht einen 34-Bit-breiten Bus, aber man

36:11.900 --> 36:14.980
kann sagen, ich will die oberen 34-Bit nicht nutzen, mache die mal

36:14.980 --> 36:15.600
immer aus.

36:16.060 --> 36:19.660
Also, verwende die einfach nicht, die dürfen nicht Daten übertragen.

36:20.160 --> 36:20.940
Warum mache ich das?

36:21.060 --> 36:22.100
Macht ja eigentlich keinen Sinn.

36:22.500 --> 36:24.140
Das mache ich aus Stromspargründen.

36:24.540 --> 36:28.100
Wenn ich quasi weiß, dass ich immer nur auf 32-Bit arbeite, weil

36:28.100 --> 36:31.280
vielleicht ich nur mit 32-Bit-Integern oder 32-Bit-Single-Position

36:31.280 --> 36:34.960
-Zahlen arbeite und ich quasi niemals 64-Bit vom Speicher haben

36:34.960 --> 36:38.360
möchte, dann kann ich im Speicher auch gleich sagen, hier benutzt mal

36:38.360 --> 36:40.980
die oberen 32-Bit nicht.

36:41.940 --> 36:45.000
Dann sind die immer quasi konstant null, werden nicht umgeladen,

36:45.200 --> 36:47.960
toggeln nicht, also wechseln nicht zwischen 0 und 1 hin und her und

36:47.960 --> 36:49.240
brauchen somit weniger Speicher.

36:49.720 --> 36:52.200
Und genauso kann ich eben auch teilweise sagen, ich will nur 16, ich

36:52.200 --> 36:56.220
will nur 8-Bit von dem Speicherbus benutzen.

36:59.990 --> 37:03.770
Die Ausrichtung von den Daten im Speicher ist relativ wichtig.

37:03.770 --> 37:10.290
Data Alignment, das heißt im Wesentlichen, wo im Speicher dürfen die

37:10.290 --> 37:10.910
Daten liegen.

37:11.470 --> 37:14.210
Also der Speicher ist für gewöhnlich byte-adressierbar.

37:14.790 --> 37:16.910
Sagen wir mal, ein Wort ist 32-Bit lang.

37:17.530 --> 37:20.690
Darf jetzt dieses Wort überall im Speicher liegen oder darf es nur an

37:20.690 --> 37:22.130
gewissen Stellen im Speicher liegen?

37:22.550 --> 37:24.770
Und die Antwort ist, es darf nur an gewissen Stellen im Speicher

37:24.770 --> 37:25.010
liegen.

37:26.310 --> 37:28.210
Einfach kann man sich das am Beispiel so vorstellen.

37:28.590 --> 37:29.850
Ich habe hier wieder meinen Speicher.

37:31.370 --> 37:35.950
Ich habe hier ein Wort, beginnt an byte-adresse 0, ein Wort beginnt an

37:35.950 --> 37:37.390
byte -adresse 4 und so weiter.

37:39.290 --> 37:44.690
Und was ich jetzt nicht machen darf, ist zum Beispiel zu sagen, ich

37:44.690 --> 37:49.610
hätte gerne so ein Ladebefehl, Load aus dem Hauptspeicher, bei byte

37:49.610 --> 37:51.210
-adresse 2.

37:52.290 --> 37:57.490
Ein Wortladen an byte-adresse 2 würde heißen, ich würde die beiden

37:57.490 --> 37:58.430
Bytes hier haben.

37:59.610 --> 38:02.990
Und ich wollte ja ein Wort, also 32-Bit, die beiden Bytes.

38:04.370 --> 38:08.570
Das würde quasi heißen, wenn ich sage, Load Words, weiß ich nicht,

38:08.670 --> 38:11.610
nach Register 1, beginnt bei Adresse 2.

38:13.650 --> 38:16.230
Und das ist bei sehr vielen Architekturen verboten.

38:16.430 --> 38:18.530
Quasi bei allen Architekturen.

38:19.850 --> 38:23.270
MIPS hatte das immer erlaubt und hat es irgendwie geschafft, ein

38:23.270 --> 38:26.290
Patent darauf zu kriegen und deswegen durften das die anderen nicht

38:26.290 --> 38:26.550
machen.

38:26.970 --> 38:28.490
Aber man will es auch nicht machen.

38:28.570 --> 38:29.630
So gesehen macht es auch keinen Sinn.

38:29.750 --> 38:31.590
Das Patent ist auch inzwischen ausgelaufen, aber es interessiert

38:31.590 --> 38:32.070
einfach keinen.

38:32.150 --> 38:33.510
Also es macht keinen Sinn, das zu machen.

38:34.090 --> 38:35.710
Warum macht es keinen Sinn, das zu machen?

38:36.190 --> 38:41.510
Naja, das liegt an der vorherigen Folie, die Wortorganisation.

38:42.270 --> 38:46.710
Also hiermit ist nicht nur gemeint, der Bus ist 32-Bit groß, sondern

38:46.710 --> 38:51.410
damit ist auch gemeint, ich kann nicht beliebige 32-Bit-Blöcke aus dem

38:51.410 --> 38:52.150
Speicher laden.

38:52.630 --> 38:55.270
Zum einen müssen die 32-Bit-Blöcke immer zusammenhängend sein.

38:55.410 --> 38:58.370
Ich kann nicht sagen, ein Byte von hier, ein Byte von da, in der Summe

38:58.370 --> 39:03.270
32 -Bit, sondern es müssen einfach zusammenhängende Bereiche sein und

39:03.270 --> 39:07.710
die Bereiche müssen halt eben genau so sein wie hier.

39:07.850 --> 39:11.650
Sprich, diese vier Byte, die darf ich am Stück laden.

39:12.270 --> 39:15.350
Was der Rote hier will, das sind auch vier Byte, die sind auch

39:15.350 --> 39:19.610
zusammenhängend, beide Adressen 2, 3, 4 und 5, zusammenhängend, aber

39:19.610 --> 39:22.470
die würden sich über zwei verschiedene Worte erstrecken.

39:22.850 --> 39:26.830
Und der Speicher ist einfach so angebunden, dass man dem Speicher

39:26.830 --> 39:30.090
quasi einfach nur die Wortadresse mitteilt und er gibt einem dann das

39:30.090 --> 39:31.950
Wort und das wird übertragen.

39:32.410 --> 39:35.790
Und das sind hier zwei verschiedene Wörter, nämlich die Wortadresse 0

39:35.790 --> 39:37.210
und die Wortadresse 1.

39:37.570 --> 39:41.430
Und damit müsste man zwei Speicherzugriffe machen, um die rot

39:41.430 --> 39:43.350
gewünschten 32-Bit-Blöcke anzukarren.

39:43.450 --> 39:46.110
Also dauert das effektiv doppelt so lange, weil doppelt so viele

39:46.110 --> 39:47.310
Speicherzugriffe notwendig sind.

39:47.710 --> 39:50.110
Das wäre ein Beispiel für nicht-aligned.

39:50.690 --> 39:53.590
Aligned heißt also immer, hier ist die Definition, ich habe

39:53.590 --> 39:58.370
irgendeinen Datentyp, der benötigt S-Byte, beim Wort wären es zum

39:58.370 --> 40:05.350
Beispiel 4-Byte und der ist dann ausgerichtet, wenn die Adresse, die

40:05.350 --> 40:08.950
Startadresse, die Bars-Adresse modulo seine Größe 0 ergibt.

40:08.950 --> 40:10.410
Was hatten wir hier?

40:10.810 --> 40:13.430
Wir hatten hier die Startadresse, Adresse 2.

40:14.370 --> 40:17.010
Okay, egal Modulo was, das ergibt nicht 0, kann man nicht ändern.

40:17.630 --> 40:22.890
Aber Startadresse 2 modulo die 4-Byte, die ein Wortnummer groß ist,

40:22.950 --> 40:24.430
gibt halt eben 2, ergibt nicht 0.

40:24.630 --> 40:25.530
Was würde 0 ergeben?

40:26.090 --> 40:28.290
Adresse 0 modulo 4 würde 0 ergeben.

40:28.650 --> 40:33.510
Adresse 4 modulo 0 würde 0... Adresse 4 modulo 4 würde 0 ergeben.

40:33.930 --> 40:35.770
Adresse 8 modulo 4 würde 0 ergeben.

40:36.110 --> 40:40.590
Alle, die an diesen Wortgrenzen ausgerichtet sind, das wären wort

40:40.590 --> 40:41.550
-aligned -Zugriffe.

40:42.170 --> 40:45.610
Genauso gibt es ja typischerweise auch 16-Bit-Zahlen, gültig

40:45.610 --> 40:46.010
ausgerichtet.

40:46.130 --> 40:50.650
16 -Bit-Zahlen wären zum Beispiel dieses hier oder dieses hier und

40:50.650 --> 40:53.890
nicht gültig ausgerichtet wäre halt eben eins, was über diese Grenze

40:53.890 --> 40:54.450
hinausgeht.

40:54.650 --> 41:01.990
Also das, was quasi hier bei Adresse was auch immer und was wäre keine

41:01.990 --> 41:03.030
gültige Ausrichtung.

41:07.190 --> 41:11.430
Noch eine Eigenschaft von alignment, von gültiger Ausrichtung, wenn

41:11.430 --> 41:15.210
man sich die Adresse als Bitzahl anguckt, dann sind immer die letzten

41:15.210 --> 41:16.490
so und so viel Bit 0.

41:17.170 --> 41:21.430
Also bei Byte-Zugriffen trifft das nicht wirklich zu und Byte kann ja

41:21.430 --> 41:23.110
jetzt wirklich jedes Byte hier drin sein.

41:23.390 --> 41:25.050
Aber gucken wir mal zu Wortzugriffe zu.

41:25.650 --> 41:26.770
Einfach, was ist hier hingeschrieben?

41:27.170 --> 41:35.130
Hier steht, wenn ich zum Beispiel 4 Byte hätte, dann wäre das also 2

41:35.130 --> 41:39.810
hoch 2, dann steht hier, die letzten zwei Bit von der Byte-Adresse

41:39.810 --> 41:40.330
sind 0.

41:42.610 --> 41:43.890
Was hatten wir hier?

41:43.950 --> 41:49.630
Wir hatten hier in rot den bösen Zugriff, machen wir wieder rot, der

41:49.630 --> 41:53.090
böse Zugriff, der nicht erlaubte Zugriff, der nicht aligned-Zugriff.

41:54.330 --> 42:00.850
Startadresse ist Binär 2, wäre also Bunch of Zeros und irgendwo am

42:00.850 --> 42:02.330
Ende steht mal eine 2.

42:04.010 --> 42:07.490
Und hier kann man jetzt sehen, es sind nicht die letzten zwei Bit 0.

42:07.910 --> 42:09.710
Wann sind die letzten zwei Bit 0?

42:09.890 --> 42:18.130
Naja, bei allen Zahlen, die irgendwie die Form haben 0 Dezimal oder

42:18.130 --> 42:20.050
halt eben hier müssen Nullen stehen bleiben.

42:20.170 --> 42:21.890
Hier kann von mir aus nur eins, das wäre die nächstgrößere.

42:21.990 --> 42:27.290
Die nächstgrößere wäre quasi genau die hier, wo die Nullen bleiben und

42:27.290 --> 42:28.690
der hier zählt langsam hoch.

42:29.990 --> 42:33.090
Das wäre die nächstgrößere word-aligned-Adresse.

42:33.570 --> 42:34.790
Das wäre halt gerade die 4.

42:34.970 --> 42:36.590
Gerade die 4, die wir da auch hatten.

42:38.270 --> 42:40.610
Die nächstgrößere wäre dann irgendwie sowas.

42:41.870 --> 42:42.730
Das ist gerade die 8.

42:43.390 --> 42:44.710
Danach kommt die 12 und so weiter.

42:45.070 --> 42:48.230
Also die letzten zwei Bit sind 0, wenn es word-aligned ist.

42:48.370 --> 42:52.870
Das letzte Bit ist 0, wenn es 16 Bit, wenn es short-aligned ist.

42:53.510 --> 42:57.510
Und bei einer wirklich byte-aligned Adresse, naja jede Adresse bei

42:57.510 --> 43:00.110
einem byte-adressierbaren Speicher ist byte-aligned.

43:02.570 --> 43:06.750
Ja, also wie gesagt, nicht ausgerichtete Daten, also misalignment hört

43:06.750 --> 43:10.390
sich schon an, da sollte man es lieber nicht machen, ist halt manchmal

43:10.390 --> 43:10.890
erlaubt.

43:10.970 --> 43:14.430
Und wenn es erlaubt ist, dann kann man es quasi an beliebigen byte

43:14.430 --> 43:16.110
-Adressen starten lassen.

43:16.690 --> 43:19.050
Auch eine 32-Bit-Zahl, ich hatte jetzt hier glaube ich noch ein

43:19.050 --> 43:19.470
Beispiel.

43:20.110 --> 43:23.150
Die 32-Bit-Zahl, die kann nicht nur da beginnen, eine 32-Bit-Zahl

43:23.150 --> 43:27.030
könnte auch zum Beispiel hier beginnen und hier reingehen.

43:27.190 --> 43:30.050
Ja, das wäre auch ein misaligned 32-Bit.

43:30.790 --> 43:32.030
Und natürlich andere Extreme auch.

43:33.910 --> 43:39.130
Das ist nicht bei allen Prozessoren erlaubt, es ist bei sehr wenigen

43:39.130 --> 43:41.090
Prozessoren erlaubt.

43:41.530 --> 43:44.550
Was passiert eigentlich, wenn man es trotzdem hinschreibt?

43:45.090 --> 43:49.050
Ich meine, nicht erlaubt heißt ja, nicht supported, hätte man nicht

43:49.050 --> 43:49.710
machen sollen.

43:50.170 --> 43:52.290
Aber es kann mich ja keiner daran hindern, es hinzuschreiben.

43:52.630 --> 43:55.390
Ich muss ja hinschreiben, Assemblerbefehl, load word und dann

43:55.390 --> 43:58.330
irgendeine Zahl und die Zahl ist eine byte-Adresse.

43:58.510 --> 44:00.030
Also ich kann das hinschreiben.

44:00.450 --> 44:02.210
Was passiert denn, wenn es nicht erlaubt ist?

44:02.710 --> 44:05.450
Wir hatten ganz am Anfang gesehen, dass es bei CPUs noch irgendwas

44:05.450 --> 44:07.230
gibt, was sich Unterbrechung nennt.

44:07.610 --> 44:08.570
Das wäre so ein Fall.

44:08.710 --> 44:11.010
Ja, ich habe irgendwas gemacht, was ich nicht hätte machen sollen.

44:11.350 --> 44:15.310
Dann wird die Programmausführung unterbrochen und es wird ein Händler,

44:15.550 --> 44:21.570
ein Bearbeiter aufgerufen, Intrap-Händler.

44:22.130 --> 44:25.210
Und der Intrap-Händler, der wird mitgeteilt, pass mal auf, du wurdest

44:25.210 --> 44:31.670
aufgerufen, weil das Programm an eine nicht-alignete Adresse einen

44:31.670 --> 44:32.950
Word -Zugriff gemacht hat.

44:33.190 --> 44:34.090
Mach mal was.

44:35.330 --> 44:38.690
Jetzt könnte der Intrap-Händler sagen, ja, nee, keine Lust, Programm

44:38.690 --> 44:39.610
terminieren, fertig.

44:39.950 --> 44:43.690
Oder er könnte sagen, was soll's, dann kümmere ich mich drum, dann

44:43.690 --> 44:47.490
mache ich zwei aufeinanderfolgende Word-Zugriffe an word-aligneten

44:47.490 --> 44:49.670
Adressen und wurstel die zusammen.

44:49.970 --> 44:51.070
Das ist aber ziemlich lästig.

44:51.710 --> 44:52.830
Mal sehen, passt das hier noch mit drauf.

44:53.490 --> 44:58.010
Also wenn zum Beispiel der Zugriff, wie hier oben hin, abgeschickt

44:58.010 --> 45:01.950
wird, dann sagt die CPU, das war nicht aligned, Unterbrechung.

45:02.430 --> 45:05.950
Und der Intrap-Händler sagt, gut, ich kümmere mich drum, macht zwei

45:05.950 --> 45:12.370
Load -Word-Befehle, nämlich er lädt einmal dieses Wort, das ist

45:12.370 --> 45:15.930
aligned, das darf er, und er lädt einmal dieses Wort, das ist aligned,

45:16.150 --> 45:16.730
das darf er.

45:17.230 --> 45:19.870
Das heißt, er hat diese beiden Wörter in zwei verschiedenen Registern,

45:20.150 --> 45:21.410
jetzt muss er die zusammen wursteln.

45:21.590 --> 45:27.030
Zusammen wursteln heißt, sagen wir mal, das hier ist R2 und das hier

45:27.030 --> 45:27.970
ist in R1.

45:28.550 --> 45:33.050
Dann muss er quasi R1 nehmen und ein bisschen nach links schieben,

45:33.490 --> 45:35.990
halt eben gerade so viel nach links, dass die Lücke hier frei wird.

45:36.550 --> 45:41.990
Und dann muss er aus R2 den oberen Teil, den er geladen hat, auf null

45:41.990 --> 45:42.390
setzen.

45:45.810 --> 45:50.330
Nochmal, den oberen Teil, den er von R2 geladen hat, muss er auf null

45:50.330 --> 45:50.690
setzen.

45:51.050 --> 45:51.630
Wie macht er das?

45:51.730 --> 45:54.390
Na gut, er macht sich eine Maske, wo der obere Teil auf null ist und

45:54.390 --> 45:56.070
der untere auf 1 und dann veruntert die beiden.

45:56.250 --> 45:59.810
Damit ist der obere Teil hier weg, also auf null gesetzt und der

45:59.810 --> 46:00.790
untere Teil bleibt erhalten.

46:01.190 --> 46:03.270
Und danach kann er R1 und R2 zusammen odern.

46:03.630 --> 46:07.530
Dann hat er seinen... Das hat ziemlich einen Unsinn gemacht.

46:08.050 --> 46:09.910
Ja, es hätte ein bisschen anders sein sollen.

46:10.930 --> 46:11.810
Was hätte er dann gemacht?

46:11.910 --> 46:13.310
Dann hätte er quasi die Reihenfolgen vertauscht.

46:13.310 --> 46:14.750
Vielleicht malen wir es nochmal ran.

46:15.930 --> 46:19.350
Das wäre ja eigentlich das least significant Byte.

46:19.470 --> 46:22.290
Das nächste Byte, das nächste Byte, das nächste Byte.

46:22.730 --> 46:25.470
Und so wie ich es gerade beschrieben habe, hätten wir danach was

46:25.470 --> 46:26.990
bekommen, was so ausgesehen hätte.

46:27.370 --> 46:28.910
Aber das wollten wir ja eigentlich gar nicht.

46:29.230 --> 46:30.690
Also sprich, er hätte es ein bisschen anders machen müssen.

46:30.790 --> 46:33.010
Er hätte das anders schieben müssen.

46:33.490 --> 46:39.350
Er hätte R1 nach rechts schieben müssen, damit das nullte Byte auch an

46:39.350 --> 46:40.750
der nullten Stelle angekommen ist.

46:40.850 --> 46:43.130
Das am besten mit einem arithmetischen Schiebebefehl, dass links

46:43.130 --> 46:44.370
Nullen reingeschoben kommen.

46:45.030 --> 46:48.810
Dann hätte er von R2, hätte er nach links schieben müssen, um das an

46:48.810 --> 46:49.970
die dritte Stelle zu kriegen.

46:50.250 --> 46:52.710
Und danach kann er es zusammen odern, um das Ergebnis zu erhalten.

46:53.070 --> 46:57.970
Also man sieht zumindest, der arme Händler muss einiges machen, um so

46:57.970 --> 46:59.890
einen missalignten Zugriff hinzukriegen.

47:00.430 --> 47:04.130
Also selbst wenn es so einen Händler gibt, der dann tut, was man

47:04.130 --> 47:07.730
eigentlich haben wollte, schneller wird es dadurch nicht.

47:08.310 --> 47:12.690
Aber was ist der Vorteil wenn man solche missalignten Wortzugriffe

47:12.690 --> 47:13.170
erlaubt?

47:13.430 --> 47:16.850
Der Vorteil ist, man kann die Daten möglicherweise enger im Speicher

47:16.850 --> 47:17.630
packen.

47:17.690 --> 47:21.130
Wenn ich nur ein Array habe aus ganz vielen Wörtern, dann ist das

47:21.130 --> 47:21.410
egal.

47:21.530 --> 47:22.690
Dann kann ich die auch alignt machen.

47:23.170 --> 47:25.970
Wenn ich aber ein Array habe oder eine Datenstruktur habe, die eine

47:25.970 --> 47:31.010
Mischung ist aus 16-Bit, 32-Bit und 8-Bit-Typen durcheinander

47:31.010 --> 47:37.030
gewürfelt, dann will ich nicht quasi nach jedem 8-Bit-Datentyp die

47:37.030 --> 47:40.890
nächsten 3-Bit-Lücke lassen, weil danach ein 32-Bit-Datentyp kommt.

47:41.150 --> 47:43.910
Und dann will ich sie vielleicht eng an eng zusammenpacken, spare ich

47:43.910 --> 47:46.490
Speicher, habe dann aber Zugriffsschwierigkeiten.

47:46.630 --> 47:48.550
Hängt dann wieder darauf ab, was mir wichtiger ist.

47:48.850 --> 47:51.910
Weniger Speicherbrauch oder weniger Laufzeit, dann kann ich immer

47:51.910 --> 47:52.490
beides haben.

47:54.490 --> 47:58.430
Hier steht es auch, also lückenlose Nutzung des Speichers, wo ist denn

47:58.430 --> 47:58.950
die Maus hin?

47:59.330 --> 48:03.170
Na, egal, ist damit der Vorteil.

48:03.410 --> 48:05.830
Nachteil, zusätzliche Speicherzugriffe.

48:08.210 --> 48:10.930
Mal sehen, der Pentium Pro,

48:14.860 --> 48:20.540
der erlaubt Teile davon, nicht wirklich alles, aber doch, er erlaubt

48:20.540 --> 48:20.780
alles.

48:22.060 --> 48:25.380
Pentium ist ja Intel, also sprich, wenn die von Word reden, meinen sie

48:25.380 --> 48:25.880
16 -Bit.

48:26.320 --> 48:29.860
Also quasi, da darf man über die Grenzen hinweg was ablegen, aber

48:29.860 --> 48:33.360
genau wie gesagt, braucht zusätzliche Speicherzugriffe, lässt sich

48:33.360 --> 48:34.000
nicht vermeiden.

48:36.340 --> 48:39.400
Und hier haben wir noch einen Spezialfall, hier.

48:40.200 --> 48:45.500
Und zwar wird da auch folgendes als aligned toleriert.

48:46.040 --> 48:48.860
Nämlich, angenommen, ich habe hier meinen Speicher, ich habe hier

48:48.860 --> 48:53.840
wieder die Worte, also Adresse 0, 4, soweit Adresse 0, 4, 8 und so

48:53.840 --> 48:54.080
weiter.

48:55.600 --> 48:58.820
Und angenommen, ich habe jetzt einen 16-Bit-Datentyp, dann ist nach

48:58.820 --> 49:02.460
der Definition gerade eben, wäre folgendes aligned, also das hier wäre

49:02.460 --> 49:04.320
ein 16-Bit-Datentyp, der aligned ist.

49:04.700 --> 49:06.820
Und das hier wäre ein 16-Bit-Datentyp, der aligned ist.

49:07.380 --> 49:11.360
Und laut der Definition hier von Pentium Pro, wäre auch das hier ein

49:11.360 --> 49:12.960
16 -Bit-Datentyp, der aligned ist.

49:13.360 --> 49:16.300
Die Idee ist da einfach, der ist zwar irgendwie komisch in der Mitte,

49:16.700 --> 49:21.040
aber ich kann ihn trotzdem mit einem 32-Bit-Ladebefehl aus dem

49:21.040 --> 49:21.700
Speicher holen.

49:21.840 --> 49:24.660
Also ich muss zwar irgendwie was in der Gegend rumschieben, um ihn da

49:24.660 --> 49:27.520
hinzukriegen, wie ich ihn haben wollte, aber ich brauche nur einen

49:27.520 --> 49:31.680
Speicherzugriff, das ist so irgendwie aligned.

49:34.340 --> 49:37.900
MIPS gibt es auch Varianten, die das Ausrichten streng vorschreiben,

49:38.020 --> 49:40.420
aber wie gesagt, MIPS waren diejenigen, die das mal patentiert hatten.

49:40.940 --> 49:42.780
Also gibt es unterschiedliche Beispiele dafür.

49:46.740 --> 49:51.900
Okay, das ist jetzt nicht mehr die Ausrichtung des Wortes im Speicher,

49:52.340 --> 49:57.060
sondern die Endianess ist die Interpretation, wie die Daten zu

49:57.060 --> 49:57.820
interpretieren sind.

49:58.420 --> 50:02.900
Und da gibt es zwei Formate, Little Endian und Big Endian.

50:03.580 --> 50:07.440
Und es gab für ziemlich viele Jahre, ich sag mal, religionsartige

50:07.440 --> 50:10.260
Glaubenskriege, dass das eine gut ist und das andere schlecht.

50:11.060 --> 50:13.340
Letztendlich ist es Schnuppe.

50:13.480 --> 50:16.840
Also der eine hat den Vorteil, der andere hat den Vorteil.

50:17.380 --> 50:20.880
Beide Vorteile sind sehr, sehr schwach aus heutiger Sicht.

50:21.200 --> 50:24.580
Also keine guten Argumente, sondern eher so, ja, wenn du willst, warum

50:24.580 --> 50:24.840
nicht.

50:25.800 --> 50:28.040
Aber leider widersprechen sich die beiden Endianess.

50:28.480 --> 50:31.340
Und das kann sehr unangenehme Schwierigkeiten machen.

50:33.260 --> 50:36.020
Na gut, gucken wir uns erst mal an, wie die beiden eigentlich

50:36.020 --> 50:36.700
definiert sind.

50:38.600 --> 50:42.980
Wenn das hier wieder der Speicher ist, und zwar zu interpretieren,

50:50.660 --> 50:58.420
also zu interpretieren als, das hier ist die Wortadresse, da steht

50:58.420 --> 50:59.780
hier vorne irgendwas, mir egal.

51:00.400 --> 51:03.100
Und hier hinten, sorry, die Byte-Adresse.

51:04.200 --> 51:09.860
Hier ist ein Wort, das hier ist die Byte-Adresse, das ist die Byte

51:09.860 --> 51:13.180
-Adresse von dem ersten Byte davon, das wäre Byte-Adresse Null, das

51:13.180 --> 51:14.740
wäre halt eben die nächste Vier.

51:15.800 --> 51:21.100
Und dann, wenn ich eine 64-Bit-Zahl speichern möchte, dann heißt

51:21.100 --> 51:26.980
Little Endian, das least significant Byte kommt hier hin, das next

51:26.980 --> 51:30.560
significant Byte hierhin, das nächste hier, das nächste hier, das

51:30.560 --> 51:32.740
nächste hier, das nächste hier, das nächste hier, das nächste hier.

51:33.000 --> 51:37.060
Sieht ziemlich naheliegend aus, sieht sehr naheliegend aus.

51:38.200 --> 51:42.860
Einfach nur das least significant kommt als erstes und danach immer

51:42.860 --> 51:45.280
eins mehr, eins mehr, eins mehr und so weiter.

51:46.020 --> 51:47.260
So liegt die Zahl dann im Speicher.

51:49.200 --> 51:52.040
Ich bringe einfach mal kurz, das wird Intel Format genannt, weil Intel

51:52.040 --> 51:54.240
das irgendwann mal einfach mal so für sich definiert hat, wir machen

51:54.240 --> 51:55.280
dieses Format fertig.

51:56.340 --> 51:59.440
Das andere Format, einfach mal kurz rüberspringen, wird Motorola

51:59.440 --> 52:02.160
Format auch genannt, Big Endian, weil Motorola einfach irgendwann

52:02.160 --> 52:04.280
gesagt hat, ja, wir machen das so fertig, es war egal.

52:05.600 --> 52:06.700
Hier sieht's anders aus.

52:07.520 --> 52:11.080
Hier sieht es so aus, hier wiederum der Speicher, da ist jetzt quasi

52:11.080 --> 52:14.800
das most significant Byte steht im Speicher und dann immer ein

52:14.800 --> 52:20.740
weniger, 6, 5, 4, 3, 2, 1 und das least significant Byte würde hier

52:20.740 --> 52:21.080
stehen.

52:22.660 --> 52:24.980
Warum, warum haben die das so gemacht?

52:25.660 --> 52:28.480
Kann man sich eigentlich ziemlich gut vorstellen.

52:29.000 --> 52:33.440
Ich meine, wenn ich den Speicher anschaue, dann gucke ich mir

52:33.440 --> 52:38.460
normalerweise, ich gucke mir Adresse 1 an, danach gucke ich mir

52:38.460 --> 52:39.720
Adresse 2 an und so weiter.

52:39.880 --> 52:43.420
Ich gucke mir den Speicher normalerweise in aufsteigenden Adressen an.

52:44.240 --> 52:46.200
Ich weiß ja nicht, was die größte ist, da wo es zu Ende ist.

52:46.240 --> 52:48.080
Ich fange mit der kleinsten Adresse an, gucke mir die nächsten Adresse

52:48.080 --> 52:48.300
an.

52:48.660 --> 52:50.240
Wie schreibe ich Zahlen auf?

52:50.760 --> 52:54.180
Ich schreibe Zahlen auf, indem ich erst die, keine Ahnung,

52:54.380 --> 53:01.480
höchstwertige Dezimal stelle, danach die, also 8.750 und 2.

53:01.960 --> 53:03.320
Im Deutschen wird es ein bisschen komischer gesprochen.

53:04.420 --> 53:10.120
Also, ich schreibe Zahlen auf, indem ich die höchstwertige Stelle als

53:10.120 --> 53:12.380
erstes hinschreibe und als erstes spreche.

53:12.680 --> 53:13.600
Das haben die auch gemacht.

53:13.680 --> 53:16.320
Die haben die höchstwertige Stelle als erstes in den Speicher

53:16.320 --> 53:18.800
geschrieben, also an die kleinste Adresse, weil ich den Speicher eben

53:18.800 --> 53:20.540
von kleinen Adressen zu großen Adressen mache.

53:20.900 --> 53:25.540
Also, so gesehen versucht es so zu machen, wie man es spricht und wie

53:25.540 --> 53:26.420
man es schreibt vor allem.

53:26.520 --> 53:29.140
Sprechend tut es jeder anders, aber wie man es schreibt vor allem.

53:29.960 --> 53:31.620
Also, kann man nachvollziehen.

53:33.120 --> 53:37.000
Warum hat Intel und auch einige andere es so gemacht?

53:38.600 --> 53:41.740
Gut, also einmal, wenn man es anguckt, in dieser Schreibweise anguckt,

53:41.840 --> 53:43.020
sieht es natürlicher aus.

53:43.600 --> 53:46.020
Und es gab damals wirklich einen realen Grund dafür.

53:46.480 --> 53:50.480
Und der reale Grund war eigentlich, die Speicher waren jetzt nicht

53:50.480 --> 53:53.820
unbedingt mit 64 Bit angebunden, soll heißen, ich konnte jetzt nicht

53:53.820 --> 53:57.740
pro Speicherzugriff 64 Bit bekommen, sondern ich habe pro

53:57.740 --> 53:59.660
Speicherzugriff nur ein Byte bekommen.

54:00.240 --> 54:02.440
Und danach das nächste Byte und dann das nächste Byte und dann das

54:02.440 --> 54:03.060
nächste Byte.

54:03.460 --> 54:06.980
Und wenn ich quasi Byte für Byte lesen musste, dann war es bei vielen

54:06.980 --> 54:10.420
Operationen, die irgendwie einen Carry hatten, konnte ich mit dem

54:10.420 --> 54:12.480
höchsten Byte nichts anfangen.

54:12.920 --> 54:16.300
Ich musste mit dem kleinstwertigen Byte anfangen, das hat quasi, wenn

54:16.300 --> 54:19.620
ich zum Beispiel Zahlen addiere, fange ich mit dem kleinstwertigen

54:19.620 --> 54:22.720
Byte an, das will ich zuerst haben, das addiere ich mit irgendwas,

54:23.060 --> 54:26.380
dann kommt ein Carry als Überlauf dabei heraus und den Carry brauche

54:26.380 --> 54:29.800
ich dann, wenn ich das nächstwertige Byte addiere, was ja dann danach

54:29.800 --> 54:30.740
aus dem Speicher kommt.

54:31.060 --> 54:34.240
Also die haben die Reihenfolge ursprünglich mal deswegen so gemacht,

54:34.600 --> 54:37.460
weil sie dann quasi Byte für Byte aus dem Speicher lesen konnten und

54:37.460 --> 54:40.280
wenn das Byte kam, konnten sie damit die Operation, die Addition

54:40.280 --> 54:42.720
gleich einen Schritt weiter machen und wenn das nächste Byte kam,

54:42.820 --> 54:43.980
konnten sie den nächsten Teil addieren.

54:45.220 --> 54:47.020
Den Grund gibt es heute halt eben nicht mehr.

54:47.120 --> 54:49.680
Heute gibt es keine Speicher mehr, wo ich Byte für Byte drauf

54:49.680 --> 54:50.140
zugreife.

54:50.240 --> 54:52.900
Deswegen ist der Grund heute hinfällig, aber ursprünglich gab es den

54:52.900 --> 54:53.700
halt irgendwie mal so.

54:54.500 --> 54:56.000
Und heute ist es halt völlig egal.

54:56.400 --> 54:58.220
Heute kümmert sich auch keiner mehr wirklich drum.

54:58.820 --> 55:00.460
Also der Religionskrieg ist zu Ende.

55:00.580 --> 55:02.300
Es gibt beide und fertig.

55:02.700 --> 55:05.820
Es gibt viele Architekturen, die auch einfach beide unterstützen, wo

55:05.820 --> 55:07.920
man quasi umschalten kann zwischen den beiden.

55:08.960 --> 55:10.760
Vielleicht noch, warum ist das eigentlich schwierig?

55:10.960 --> 55:13.780
Warum ist das ein Problem, die Verwechslung zwischen den beiden?

55:14.640 --> 55:20.480
Angenommen, ich habe ein Programm, C-Code und das macht nur ganz

55:20.480 --> 55:21.360
harmlose Sachen.

55:21.540 --> 55:24.900
Das funktioniert das Beispiel, macht nur ganz harmlose Sachen wie Load

55:24.900 --> 55:28.060
Word, ein bisschen rechnen, Store Word, alles kein Problem.

55:29.260 --> 55:33.360
Load 16-Bit Half-Words, damit ein bisschen rechnen, Store Half-Words,

55:33.700 --> 55:34.440
alles kein Problem.

55:35.640 --> 55:39.160
Aber angenommen, der Programmierer kommt auf die Idee, er könnte da

55:39.160 --> 55:39.960
was optimieren.

55:40.680 --> 55:43.300
Angenommen, der Programmierer kommt auf die Idee, ich habe hier doch

55:43.300 --> 55:48.360
ein Array von Bytes in meinem C-Programmiersprache deklariert, Array

55:48.360 --> 55:52.080
von Bytes, aber ich will jetzt nicht Load Byte, Rechnung, Load Byte,

55:52.160 --> 55:55.780
Rechnung, Load Byte, Rechnung machen, sondern ich mache ein Load Word,

55:56.180 --> 55:59.740
da bekomme ich ja gleich vier Bytes auf einmal und mache dann

55:59.740 --> 56:00.960
irgendwelche Rechnungen damit.

56:01.400 --> 56:04.860
Und dann hat er in seinem Kopf nämlich eine Vorstellung, wo in seinem

56:04.860 --> 56:07.340
Register sind die Bytes eigentlich.

56:07.840 --> 56:10.680
Und diese Vorstellung passt eventuell nicht, weil da muss er sich

56:10.680 --> 56:13.880
überlegen, stelle ich es mir als Big-Endian oder als Little-Endian

56:13.880 --> 56:14.160
vor.

56:14.680 --> 56:17.340
Und das ist die Stelle, wo dann einfach Fehler passieren können.

56:17.920 --> 56:22.760
Sprich unoptimierte Programme, die für Little-Endian gedacht

56:22.760 --> 56:25.440
geschrieben worden sind, wenn ich die auf eine Big-Endian-Maschine

56:25.440 --> 56:26.880
ausführe, das klappt meistens.

56:27.360 --> 56:30.380
Und je mehr die Programmierer versuchen, da irgendwie clever was zu

56:30.380 --> 56:33.140
optimieren, desto höher wird die Chance, dass das Programm einfach

56:33.140 --> 56:34.440
ganz schrecklich falsch rechnet.

56:35.000 --> 56:37.680
Und deswegen ist dann die Portabilität zwischen Big- und Little-Endian

56:37.680 --> 56:40.860
nicht mehr gegeben und kann einem ganz fürchterliche Kopfschmerzen

56:40.860 --> 56:41.100
machen.

56:41.680 --> 56:45.280
Zum Beispiel kann das auch wunderbar passieren, wenn ich entwickle,

56:45.480 --> 56:48.760
also ein Programm schreibe, programmiere, für irgendeinen kleinen

56:48.760 --> 56:52.480
Controller, der Big-Endian benutzt, aber dann habe ich meine

56:53.240 --> 56:56.680
Entwicklungsumgebung, die läuft ja auf meinem PC, das ist ein x86, das

56:56.680 --> 56:57.560
ist ein Little-Endian.

56:58.060 --> 57:01.860
Also ich lasse mein C-Programm zum Test 10 auf dem Intel Little-Endian

57:01.860 --> 57:05.280
laufen, klappt alles wunderbar und danach lasse ich es auf dem

57:05.280 --> 57:08.340
Controller Big-Endian laufen und wundere mich, dass es nicht mehr

57:08.340 --> 57:08.600
geht.

57:08.740 --> 57:11.660
Und ich brauche Stunden, um rauszufinden, was ich vermasselt habe.

57:12.100 --> 57:15.020
Also an der Stelle kann es einem wirklich Kopfschmerzen machen, da

57:15.020 --> 57:15.780
muss man aufpassen.

57:16.480 --> 57:19.020
Wenn man quasi nur an einer Architektur arbeitet und immer an der

57:19.020 --> 57:21.620
gleichen, dann kann man es gerade ignorieren.

57:21.840 --> 57:25.520
Wenn man mehrere hat, für die man als Ziel im Kopf haben muss, dann

57:25.520 --> 57:26.700
muss man wirklich darauf aufpassen.

57:30.300 --> 57:34.220
Genau, IA-64 zum Beispiel kann umgeschalten werden.

57:35.900 --> 57:36.500
Warum?

57:36.800 --> 57:40.260
Damit man halt eben diese Portierungen von Programmen einfacher machen

57:40.260 --> 57:40.500
kann.

57:41.020 --> 57:46.640
Was ist zum Beispiel ein hoch optimiertes, sehr low-level C-Programm?

57:46.760 --> 57:47.680
Das Betriebssystem.

57:48.420 --> 57:52.420
Und das zu portieren von einer Architektur auf eine andere, das kann

57:52.420 --> 57:54.120
halt an sich schon Kopfschmerzen machen.

57:54.460 --> 57:56.900
Wenn man dann noch im Endianist sich rumwurschteln muss, dann kann das

57:56.900 --> 57:57.720
sehr unangenehm werden.

57:58.040 --> 57:59.960
Deswegen haben die irgendwann gesagt, gut, wir supporten beides.

58:00.380 --> 58:02.900
Defaultmäßig lassen wir das, was Intel immer hatte, aber man kann auch

58:02.900 --> 58:05.360
auf das andere umschalten, wenn einem das das Leben irgendwie

58:05.360 --> 58:05.940
einfacher macht.

58:06.700 --> 58:07.780
Dafür haben die das angeboten.

58:09.620 --> 58:10.860
Wo kommt der Name her?

58:11.300 --> 58:13.680
Also das kann man jetzt alles lesen, ist eine Menge Text.

58:15.400 --> 58:18.180
Aber es scheint wirklich zu stimmen, also es scheint mehr zu sein als

58:18.180 --> 58:18.680
nur ein Gerücht.

58:19.760 --> 58:22.900
Das ist ein Buch, Gulliver's Reisen, hat man vielleicht mal irgendwo

58:22.900 --> 58:24.020
gehört, diesen Namen.

58:24.100 --> 58:27.100
Das war irgendwie so ein Typ, der ist in der Gegend rumgetingelt und

58:27.100 --> 58:30.680
der ist übers Meer, keine Ahnung, irgendeine Insel und da waren ganz

58:30.680 --> 58:35.280
viele sehr, sehr kleine, ja nicht Menschen, sondern Lilliputaner.

58:36.280 --> 58:38.480
Und für die war der halt der Große, keine Ahnung was.

58:39.100 --> 58:42.880
Und er hat eigentlich nichts mit dem Buch zu tun, aber im Rahmen von

58:42.880 --> 58:47.880
diesem Buch hat er halt irgendwie, der Autor, beschrieben, dass es

58:47.880 --> 58:52.740
zwei verschiedene Gruppen, Rassen, whatever, von diesen Lilliputanern

58:52.740 --> 58:56.480
gab und die einen wollten halt unbedingt, also das Beispiel ist nicht

58:56.480 --> 58:58.780
sinnvoll, ja, aber das steht halt nun mal so in dem Buch drin.

58:59.160 --> 59:01.680
Und die einen wollten unbedingt, dass man, ich glaube es waren Eier,

59:01.820 --> 59:05.480
genau, es waren Eier, die sind ja nicht achsensymmetrisch nach oben,

59:05.580 --> 59:08.720
unten, sondern es gibt eine dünne Seite und eine dicke Seite, ja, dass

59:08.720 --> 59:11.940
man Eier oben aufschlägt und die anderen sagt, nein, nein, nein, man

59:11.940 --> 59:14.420
muss die unten aufschlagen, also nicht unten, sondern an der dicken

59:14.420 --> 59:15.180
Seite aufschlagen.

59:15.800 --> 59:19.340
Also das Konzept ist das Gleiche.

59:19.600 --> 59:22.660
Es gibt zwei Möglichkeiten, an welcher Seite man Eier aufschlagen kann

59:22.660 --> 59:25.140
und es ist völlig schnuppe, welche man nimmt.

59:25.480 --> 59:28.200
Aber die einen sind ganz fest überzeugt, dass man es da machen sollte

59:28.200 --> 59:30.480
und die anderen sind ganz fest überzeugt, dass man es da machen

59:30.480 --> 59:30.760
sollte.

59:31.320 --> 59:34.700
Und ich glaube, das ist irgendwie, woher dann der Name, wieso der Name

59:34.700 --> 59:35.700
übernommen wurde.

59:36.080 --> 59:40.240
Also in diesem Buch hießen die beiden Lilliputaner-Rassen halt die Big

59:40.240 --> 59:42.140
Andeans und die Little Andeans.

59:42.240 --> 59:42.860
Haben wir es irgendwo noch?

59:42.960 --> 59:43.040
Nein.

59:43.640 --> 59:47.300
Also die hießen in dem Buch einfach so, warum auch immer der die in

59:47.300 --> 59:48.120
dem Buch so genannt hat.

59:48.560 --> 59:51.260
Dann hat irgendjemand gesagt, hey, euer Religionskrieg mit den

59:51.260 --> 59:54.140
Darstellungen von Zahlen ist im Wesentlichen so relevant, wie das Eier

59:54.140 --> 59:55.420
aufschlägt von diesen Lilliputanern.

59:55.740 --> 59:57.140
Also nennen wir es doch Big und Little Andean.

59:58.880 --> 01:00:03.480
Laut Wikipedia hat das Buch wiederum Bezug genommen.

01:00:03.640 --> 01:00:07.980
Also warum hat das Buch damals, 1700, was weiß ich, 26, warum hat das

01:00:07.980 --> 01:00:09.040
Buch so einen Blödsinn erzählt?

01:00:09.160 --> 01:00:10.540
Ich meine, darum ging es ja nicht in dem Buch.

01:00:11.080 --> 01:00:13.600
Laut Wikipedia war das wiederum eine Anspielung.

01:00:14.140 --> 01:00:18.160
Es gab nämlich damals zwei, und diesmal waren es sogar wirklich

01:00:18.160 --> 01:00:21.340
Religionen, die sich gesplittet hatten, nämlich die evangelische und

01:00:21.340 --> 01:00:22.420
die katholische Religion.

01:00:22.620 --> 01:00:24.520
Die einen sagten, das ist so und die anderen sagten, das ist so und

01:00:24.520 --> 01:00:25.300
danach haben sie Krieg geführt.

01:00:25.700 --> 01:00:27.380
Und das ging halt eben im Wesentlichen um nichts.

01:00:28.460 --> 01:00:32.000
Und das soll angeblich, das müsste man den Autor fragen, aber der ist

01:00:32.000 --> 01:00:35.460
schon tot irgendwie, das soll angeblich eine Satire sein auf die

01:00:35.460 --> 01:00:36.300
Religionskriege.

01:00:37.180 --> 01:00:39.800
Was auch immer, zumindest die Big- und Little-Andean-Zahlen

01:00:39.800 --> 01:00:42.780
-Darstellung ist wohl definitiv eine Satire oder eine Entlehnung von

01:00:42.780 --> 01:00:43.700
diesem Buch.

01:00:49.130 --> 01:00:49.410
Gut.

01:00:51.990 --> 01:00:54.890
Ja, und dann gibt es Adressierungsarten.

01:00:55.930 --> 01:01:00.430
Adressierungsarten, da geht es jetzt darum, also Speicheradressierung,

01:01:00.910 --> 01:01:02.250
meistens jetzt von Speicher.

01:01:02.490 --> 01:01:03.690
Die Maus ist schon wieder weg.

01:01:04.190 --> 01:01:04.910
Von Speicher die Rede.

01:01:06.570 --> 01:01:10.810
Also im Sinne von, ich habe Speicher, ich habe Assemblerbefehle und

01:01:10.810 --> 01:01:13.910
ich will denen irgendwie den Zugriff auf Speicher ermöglichen.

01:01:14.070 --> 01:01:18.530
Dafür ist klar, ich muss dem Assemblerbefehl mitteilen, an welcher

01:01:18.530 --> 01:01:20.910
Adresse er den Speicher zugreifen soll.

01:01:21.750 --> 01:01:22.390
Das muss ich machen.

01:01:22.950 --> 01:01:26.030
Und da gibt es jetzt ziemlich viele verschiedene Arten, wie man dem

01:01:26.030 --> 01:01:30.290
Befehl mitteilen kann, an welcher Adresse im Speicher irgendwas zu

01:01:30.290 --> 01:01:32.410
suchen und zu finden ist.

01:01:35.930 --> 01:01:36.070
Mal sehen.

01:01:36.490 --> 01:01:39.710
Also ich kann die Adresse, ich mache mal wieder kurz weg, damit ich

01:01:39.710 --> 01:01:40.530
die Maus wieder kriege.

01:01:42.590 --> 01:01:46.950
Da kann man halt eben zum einen direkt die Speicheradresse im

01:01:46.950 --> 01:01:50.390
Befehlswort angeben, also quasi ein Additionsbefehl, also da steht

01:01:50.390 --> 01:01:53.610
dann Addiere, Register und dann lange Adresse, vielleicht sogar eine

01:01:53.610 --> 01:01:54.390
32 -Bit-Adresse.

01:01:54.970 --> 01:01:58.770
Oder man kann es etwas komplizierter machen, wo dann die Adresse

01:01:58.770 --> 01:02:03.370
berechnet werden muss, weil irgendwie ein paar Teile davon angegeben

01:02:03.370 --> 01:02:06.030
sind im Befehl und daraus muss dann erst was gerechnet werden, um die

01:02:06.030 --> 01:02:07.990
eigentliche Adresse rauszufinden.

01:02:11.430 --> 01:02:14.590
Da müssen wir erstmal unterscheiden zwischen drei verschiedenen Arten

01:02:14.590 --> 01:02:15.130
von Adressen.

01:02:15.190 --> 01:02:17.450
Der Programmadresse, Prozess- und Maschinenadresse.

01:02:18.310 --> 01:02:24.510
Die Programmadresse damit ist das gemeint, was im Assembler-Programm

01:02:24.510 --> 01:02:25.250
drinsteht.

01:02:25.370 --> 01:02:29.190
Also ich habe ein paar Assembler-Befehle und da steht, irgendwo stehen

01:02:29.190 --> 01:02:32.150
da mal ein paar Zahlen drin, wirklich so hart codierte Zahlen.

01:02:32.270 --> 01:02:33.710
Das ist gemeint mit Programmadresse.

01:02:36.150 --> 01:02:40.790
Das muss nicht notwendigerweise heißen, dass das die Adresse ist, auf

01:02:40.790 --> 01:02:43.190
die im Speicher zugegriffen werden soll, sondern das könnte zum

01:02:43.190 --> 01:02:45.690
Beispiel, wir werden gleich noch deutlich mehr Beispiele sehen, das

01:02:45.690 --> 01:02:48.530
könnte zum Beispiel heißen, die Zahl, die ich hier im Programm

01:02:48.530 --> 01:02:54.550
hingeschrieben habe, ist zu interpretieren als die Zahl plus irgendein

01:02:56.130 --> 01:02:56.610
Registerinhalt.

01:02:56.610 --> 01:02:58.890
Wir hatten vorhin die Segmente vom x86.

01:02:59.490 --> 01:03:02.410
Also das Segment der Daten plus diese Zahl.

01:03:02.830 --> 01:03:05.510
Das ist die Adresse, auf die im Speicher zugegriffen werden soll.

01:03:05.810 --> 01:03:09.910
Also die Zahl, die im Programmtext steht, muss nicht die Zahl sein,

01:03:10.030 --> 01:03:11.770
auf der im Speicher zugegriffen wird, sondern es ist nur eine

01:03:11.770 --> 01:03:13.630
Konstante, die bei der Berechnung mitbenutzt wird.

01:03:15.470 --> 01:03:22.550
Die Prozessadresse oder auch typischer das Wort effektive Adresse, das

01:03:22.550 --> 01:03:24.010
ist dann das Berechnungsergebnis.

01:03:24.510 --> 01:03:28.070
Also quasi die Zahlen, die im Programmtext drinstehen, da wird eine

01:03:28.070 --> 01:03:29.090
Adressberechnung gemacht.

01:03:29.250 --> 01:03:31.310
Wir sehen gleich noch, welches da alles gibt, ziemlich viele.

01:03:31.970 --> 01:03:35.890
Und das, was aus der Adressberechnung rauskommt, das ist die effektive

01:03:35.890 --> 01:03:36.330
Adresse.

01:03:38.150 --> 01:03:40.010
Und die effektive Adresse,

01:03:43.110 --> 01:03:46.630
das ist das, was der Prozessor wirklich verwenden möchte.

01:03:47.330 --> 01:03:50.470
Also das ist die Adresse, auf der die CPU dann zugreifen möchte.

01:03:51.510 --> 01:03:56.510
Jetzt gibt es aber zwischen CPU und Hauptspeicher noch ein paar andere

01:03:56.510 --> 01:04:01.430
Sachen, die das nochmal verändern.

01:04:03.670 --> 01:04:09.730
Zum Beispiel, ich hätte eine normale CPU und da laufen mehrere

01:04:09.730 --> 01:04:10.810
Anwendungen drauf.

01:04:11.650 --> 01:04:15.330
Und wenn beide Anwendungen jetzt der Meinung sind, auf Adresse 0x

01:04:15.330 --> 01:04:20.710
.datbeef zuzugreifen, dann meinen die nicht die gleiche Adresse.

01:04:20.910 --> 01:04:22.090
Es sind ja zwei verschiedene Programme.

01:04:22.270 --> 01:04:25.890
Die meinen, jedes Programm denkt ja, ich habe meinen eigenen Speicher.

01:04:26.010 --> 01:04:28.570
Das andere Programm denkt auch, ich habe meinen eigenen Speicher.

01:04:29.130 --> 01:04:32.030
In Wirklichkeit laufen die aber auf der gleichen Maschine und teilen

01:04:32.030 --> 01:04:32.890
sich einen Speicher.

01:04:32.890 --> 01:04:35.770
Die sollen aber nicht in der Lage sein, den Speicher der jeweils

01:04:35.770 --> 01:04:38.130
anderen Anwendungen lesen oder schreiben zu können.

01:04:38.410 --> 01:04:39.370
Also muss man die trennen.

01:04:39.650 --> 01:04:40.150
Speicherschutz.

01:04:42.150 --> 01:04:47.230
Und da macht man das so, dass man quasi den Gesamtspeicher hat und

01:04:47.230 --> 01:04:51.390
dann sagt man jetzt zum Beispiel sehr vereinfacht, der eine, das eine

01:04:51.390 --> 01:04:56.470
Programm, Task A von mir aus, das bekommt den Bereich des Speichers

01:04:56.470 --> 01:05:01.810
und Task A hat einen Assemblerbefehl, der heißt irgendwie LoadWord,

01:05:03.050 --> 01:05:08.390
irgendwie Zahl, Hexzahl 5000, was weiß ich.

01:05:08.830 --> 01:05:12.850
Und da steht vielleicht noch irgendwie ein Register mit dabei und

01:05:12.850 --> 01:05:17.350
daraus wird die Adresse berechnet, nämlich Inhalt von dem Register

01:05:17.350 --> 01:05:18.370
plus die Zahl.

01:05:18.790 --> 01:05:22.290
Das Ergebnis soll dann in Register 2 gespeichert werden oder sowas.

01:05:22.870 --> 01:05:27.010
Das hier wäre jetzt die Programmadresse, die hart codiert im Programm

01:05:27.010 --> 01:05:27.590
drin steht.

01:05:28.070 --> 01:05:32.010
Das Ergebnis der Adressberechnung wäre die effektive Adresse.

01:05:33.230 --> 01:05:36.730
Und jetzt ist es aber so, dass an den Speicher, der ja hier ist, wird

01:05:36.730 --> 01:05:39.110
nicht die effektive Adresse gegeben, sondern da ist nochmal dieser

01:05:39.110 --> 01:05:43.990
Speicherschutz -Dingsy zwischen, also Memory Management Unit, MMU

01:05:43.990 --> 01:05:48.610
heißt so ein Teil, und der weiß halt eben, die effektive Adresse ist

01:05:48.610 --> 01:05:55.030
zwar die, auf die das Programm zugreifen möchte, aber Task A beginnt

01:05:55.030 --> 01:05:58.350
seinen Speicher nämlich hier mit Adresse 0 und dann irgendwie mehr und

01:05:58.350 --> 01:06:00.550
dann ist halt eben irgendwo hier der Teil, der zugreifen wird,

01:06:00.970 --> 01:06:05.970
wohingegen, wenn Task B die gleiche Adresse gefragt hätte, Task B

01:06:05.970 --> 01:06:09.410
liegt vielleicht hier im Speicher, der hat seine Adresse 0 hier und

01:06:09.410 --> 01:06:11.390
dann wäre hier eben plus der Offset.

01:06:11.730 --> 01:06:14.750
Also da wird dann quasi nochmal ausgerechnet, mit welcher Adresse der

01:06:14.750 --> 01:06:17.070
Speicher wirklich belästigt wird, also welche Adresse am Speicher

01:06:17.070 --> 01:06:18.430
wirklich abgefragt wird.

01:06:19.650 --> 01:06:22.850
Und in Wirklichkeit ist es halt nicht ganz so einfach, dass diese

01:06:22.850 --> 01:06:26.750
Blöcke von Task A und Task B so schöne zusammenhängende Bereiche sind,

01:06:27.170 --> 01:06:30.470
in Wirklichkeit ist es dann eher so, dass Task A vielleicht hier einen

01:06:30.470 --> 01:06:34.190
kleinen Teil hat, hier nochmal was da, irgendwas, und Task B hat dann

01:06:34.190 --> 01:06:35.810
vielleicht irgendwo anders ein paar Teile.

01:06:35.890 --> 01:06:38.730
Also die müssen nicht notwendigerweise zusammenhängt sein, die können

01:06:38.730 --> 01:06:39.670
ganz schön verstreut sein.

01:06:40.930 --> 01:06:43.990
Und darum kümmert sich dann die Memory Management Unit, die wandelt

01:06:43.990 --> 01:06:47.210
die effektive Adresse in die Maschinenadresse rum und das ist dann

01:06:47.210 --> 01:06:50.910
das, was wirklich an den Speicher wirklich physikalisch dahingesteckt

01:06:50.910 --> 01:06:51.130
wird.

01:06:56.050 --> 01:06:59.030
Bevor wir uns die ganzen verschiedenen Adressierungsarten angucken,

01:06:59.110 --> 01:07:01.770
mit denen man halt eben genau diese Adressberechnungen hier auf

01:07:01.770 --> 01:07:04.530
unglaublich viele verschiedene Arten und Weisen ausdrücken kann,

01:07:05.110 --> 01:07:07.650
gucken wir uns erstmal an, wie sehen die ganzen Befehle,

01:07:07.730 --> 01:07:10.830
Assemblerbefehle oder was auch immer, die es so gibt oder die man

01:07:10.830 --> 01:07:12.930
spezifizieren könnte, wie sehen die eigentlich aus.

01:07:13.610 --> 01:07:16.390
Klassischerweise gibt es da oder kann man die Befehle, die es gibt in

01:07:16.390 --> 01:07:20.030
verschiedene, was macht die Mausbus, dass sie immer verschwindet?

01:07:20.770 --> 01:07:23.890
Naja, kann man die halt in verschiedene Arten von Befehlen verteilen.

01:07:23.990 --> 01:07:27.510
Es gibt Transportbefehle, das heißt einfach nur von einem Register ins

01:07:27.510 --> 01:07:31.450
andere, Daten bewegen oder vom Speicher ins Register oder zurück oder

01:07:31.450 --> 01:07:34.050
von Akkumulator nach Register oder Speicher nach Akkumulator.

01:07:34.210 --> 01:07:36.390
Also Transport, einfach hin und her schubsen von Daten.

01:07:37.090 --> 01:07:38.850
Arithmetisch, logisch, ist selbsterklärend.

01:07:39.990 --> 01:07:42.960
Schieberotations, also auf Registerinhalten, die in der Gegend

01:07:42.960 --> 01:07:45.200
rumschieben, also die Werte, die drinstehen, schieben.

01:07:46.920 --> 01:07:51.340
Multimediabefehle, das ist in der Erweiterung, die vor vielen

01:07:51.340 --> 01:07:54.540
Jahrzehnten aufgekommen ist, dass man quasi nicht mehr nur noch

01:07:54.540 --> 01:07:58.440
normale, general purpose, elementare Befehle, Addition, Laden,

01:07:58.600 --> 01:08:02.840
Multiplizieren anbietet, sondern dass man auch noch Befehle anbietet,

01:08:03.120 --> 01:08:07.300
die jetzt speziell geeignet sind, um Multimediaanwendungen

01:08:07.300 --> 01:08:07.980
auszuführen.

01:08:08.460 --> 01:08:13.080
Also das sind dann meistens Vektorbefehle im Sinne von, mach mal eine

01:08:13.080 --> 01:08:17.940
Addition, aber nicht auf zwei Zahlen, sondern auf zwei Arrays, die im

01:08:17.940 --> 01:08:19.160
Speichern liegen, Vektoren.

01:08:19.680 --> 01:08:22.100
Und der eine Vektor beginnt da, der andere Vektor beginnt da, der

01:08:22.100 --> 01:08:25.440
dritte, also das Ziel soll dort sein, addier das mal.

01:08:26.000 --> 01:08:29.280
Dann gibt es auch meistens noch spezielle Vektorregistersätze, also wo

01:08:29.280 --> 01:08:32.700
halt eben nicht ein Register drin ist, sondern wo 100 oder mehr

01:08:32.700 --> 01:08:35.800
Register drin sind, um so einen Vektor im Speicher, also in der CPU

01:08:35.800 --> 01:08:38.220
innen drin, nicht im Hauptspeicher, halten zu können.

01:08:38.940 --> 01:08:41.540
Und dann wäre es halt nicht nur, addiere die Vektoren mal, sondern

01:08:41.540 --> 01:08:45.320
berechne einen Filter oder ein Bild, berechne eine Kantenglättung,

01:08:45.760 --> 01:08:49.660
berechne eine FFT-Transformation, also wirklich ziemlich komplexe

01:08:49.660 --> 01:08:53.480
Befehle, die dann für Multimediaerweiterungen sehr speziell da sind.

01:08:54.160 --> 01:08:55.140
Warum hat man das gemacht?

01:08:55.600 --> 01:08:58.200
Hatte ich am Anfang, also ganz am Anfang, erste Vorlesung, nullte

01:08:58.200 --> 01:09:03.100
Vorlesung, schon mal erwähnt, man kann laut Moore's Law immer mehr und

01:09:03.100 --> 01:09:07.340
mehr Transistoren herstellen und die Frage war, was macht man damit?

01:09:07.820 --> 01:09:10.280
Und diese Multimedia-Befehle sind eine von den Antworten.

01:09:10.920 --> 01:09:14.800
Es ist einem nichts eigengefallen, was irgendwie allgemein alle

01:09:14.800 --> 01:09:18.180
Anwendungen schneller machen würde, aber man sieht ganz deutlich, die

01:09:18.180 --> 01:09:21.780
Multimedia -Anwendungen sind die, die am langsamsten sind und es sind

01:09:21.780 --> 01:09:23.700
die, wo alle jammern, dass sie langsam sind.

01:09:24.120 --> 01:09:27.140
Also macht man doch vielleicht Befehle, die speziell dafür gedacht

01:09:27.140 --> 01:09:30.680
sind, lass irgendeine andere Anwendung laufen und der Teil der

01:09:30.680 --> 01:09:33.320
Hardware liegt brach, der wird nicht genutzt, der liegt nur in der

01:09:33.320 --> 01:09:34.380
Gegend rum und frisst Strom.

01:09:34.380 --> 01:09:38.320
Aber wenn eine Anwendung läuft, die die Befehle brauchen kann, dann

01:09:38.320 --> 01:09:39.340
profitiert sie gewaltig.

01:09:39.720 --> 01:09:42.960
Das waren früher halt die MMX-Befehle, heute die SSE-Befehle, also

01:09:42.960 --> 01:09:46.400
gibt es viele Befehlsetzerweiterungen, speziell für Multimedia-Sachen.

01:09:47.460 --> 01:09:50.480
Natürlich Gleitkommabefehle, ist klar, so Floating Point, Single

01:09:50.480 --> 01:09:51.520
Precision oder was auch immer.

01:09:53.000 --> 01:09:54.320
Programmsteuerbefehle, einfach nur Sprünge.

01:09:57.340 --> 01:09:58.680
Systemsteuerbefehle, was ist das denn?

01:09:59.100 --> 01:10:03.940
Also mit Systemsteuerung ist jetzt irgendwie immer gemeint, es geht

01:10:03.940 --> 01:10:07.640
nicht mehr darum, den Algorithmus, den ich in C programmiert habe, mit

01:10:07.640 --> 01:10:11.500
Assemblerbefehlen auszudrücken, sondern irgendwie was anderes zu

01:10:11.500 --> 01:10:14.560
machen, was halt irgendwie nicht dem Algorithmus der Anwendung hilft,

01:10:14.640 --> 01:10:17.560
sondern was dem umgebenden Betriebssystem hilft.

01:10:18.280 --> 01:10:21.840
Zum Beispiel die MMU, die wir gerade gesehen haben, die muss ja

01:10:21.840 --> 01:10:24.320
irgendwie mal initialisiert werden, dafür könnte es spezielle

01:10:24.320 --> 01:10:28.660
Zugriffsmöglichkeiten geben oder die Unterbrechungen, die wir schon

01:10:28.660 --> 01:10:32.520
als Beispiel hatten bei dem Annalyze Speicherzugriffen, die

01:10:32.520 --> 01:10:34.300
Unterbrechungen muss man irgendwie angeben können.

01:10:34.580 --> 01:10:36.920
Man kann auch von Hand, also durch ein Assemblerbefehl, so eine

01:10:36.920 --> 01:10:39.200
Unterbrechung aufrufen, Trap heißt das für gewöhnlich.

01:10:41.940 --> 01:10:44.360
Ja, Unterbrechung gucken wir später nochmal an, aber nicht jetzt.

01:10:45.020 --> 01:10:47.620
Und dann gibt es noch als letztes Synchronisationsbefehle.

01:10:48.800 --> 01:10:53.560
Synchronisationsbefehle ist einfach auch wiederum... Okay, ich kann

01:10:53.560 --> 01:10:55.580
jetzt leider nicht mehr sagen, dass es nicht für den Algorithmus ist.

01:10:55.940 --> 01:10:59.520
Es geht hier darum, wenn ich einen Algorithmus habe und den

01:10:59.520 --> 01:11:03.780
parallelisiere, also dass ich halt eben nicht nur ein

01:11:03.780 --> 01:11:07.240
Assemblerprogramm laufen lasse, was den Algorithmus ausreicht, sondern

01:11:07.240 --> 01:11:10.440
wenn ich mehrere Threads habe, wenn ich mehrere Assemblerprogramme

01:11:10.440 --> 01:11:14.480
habe, die parallel laufen und dann müssen die sich irgendwann mal

01:11:14.480 --> 01:11:15.480
wieder synchronisieren.

01:11:15.960 --> 01:11:18.000
Es gibt da verschiedene Konzepte, wie man sich synchronisieren kann,

01:11:18.080 --> 01:11:21.280
es gibt Barrieren oder es gibt halt eben atomare Befehle.

01:11:22.480 --> 01:11:26.880
Also ich habe zum Beispiel eine Anwendung, die beginnt irgendwie, die

01:11:26.880 --> 01:11:29.160
macht irgendwas, das soll ein Thread sein, ein Faden.

01:11:29.800 --> 01:11:32.920
Irgendwann parallelisiert der sich, sprich da kommen dann mehrere

01:11:32.920 --> 01:11:36.420
Threads, die parallel laufen und das also wirklich parallel im Sinne

01:11:36.420 --> 01:11:40.240
von, ich habe ja meistens zwei oder vier Kerne in meiner CPU drin,

01:11:40.620 --> 01:11:42.880
also kann ich auch vier Threads parallel laufen lassen.

01:11:43.540 --> 01:11:45.880
Und irgendwann, wenn alle fertig sind, sollen die wieder

01:11:45.880 --> 01:11:49.860
zusammenkommen und sollen sagen, jetzt hat jeder seinen Teil erledigt

01:11:49.860 --> 01:11:54.360
und jetzt soll auf dem berechneten, von allen vier Threads

01:11:54.360 --> 01:11:55.860
berechneten, soll weitergemacht werden.

01:11:56.400 --> 01:11:58.560
Dafür müssen die sich synchronisieren.

01:11:58.880 --> 01:12:02.460
Es könnte ja sein, dass der hier vielleicht viel schneller war als die

01:12:02.460 --> 01:12:02.820
anderen.

01:12:03.220 --> 01:12:05.020
Also muss der jetzt warten auf die anderen.

01:12:05.260 --> 01:12:07.600
Dafür sind diese Barrieren zuständig.

01:12:08.060 --> 01:12:11.800
Und diese Barrieren, das kann man nur sehr schwer mit normalen

01:12:11.800 --> 01:12:13.860
Additions - und Shift-Operationen ausdrücken.

01:12:14.260 --> 01:12:18.960
Dafür gibt es spezielle Befehle im Befehlssatz der CPU, die einem

01:12:18.960 --> 01:12:19.560
dabei helfen.

01:12:22.320 --> 01:12:23.620
Zum Beispiel...

01:12:27.120 --> 01:12:30.040
zum Beispiel Compare-and-Swap.

01:12:30.540 --> 01:12:35.200
Compare-and-Swap wäre so ein Assemblerbefehl, der zum Synchronisieren

01:12:35.200 --> 01:12:36.040
benutzt werden kann.

01:12:36.600 --> 01:12:37.380
Was bedeutet der?

01:12:38.060 --> 01:12:40.720
Der sagt einfach, es gibt einen Speicher,

01:12:45.760 --> 01:12:46.320
also, nein Gott, da ist ein Speicher angegeben.

01:12:47.680 --> 01:12:49.120
Und da ist ein Register angegeben.

01:12:51.020 --> 01:12:59.300
Und der sagt, an dieser Adresse im Speicher, guck da mal nach, also

01:12:59.300 --> 01:13:05.300
lies den Inhalt von der Adresse im Speicher, vergleiche ihn, ich

01:13:05.300 --> 01:13:08.200
brauche zwei Register, machen wir hier noch eins hin, vergleiche den

01:13:08.200 --> 01:13:11.040
Inhalt, also die Zahl, die du hier rausgelesen hast, die Zahl, die du

01:13:11.040 --> 01:13:13.480
hier rausgelesen hast, der Wert, den du hier rausgelesen hast,

01:13:14.180 --> 01:13:19.220
vergleiche den, gleich, Fragezeichen, mit dem Inhalt von, keine

01:13:19.220 --> 01:13:24.600
Ahnung, nehmen wir Register 1, und falls der gleich ist, und nur dann,

01:13:25.200 --> 01:13:29.120
nimm das, was in Register 2 drin steht und schreib es an diesen

01:13:29.120 --> 01:13:29.620
Speicher.

01:13:30.520 --> 01:13:31.760
Das macht der Befehl.

01:13:32.220 --> 01:13:36.060
Und zwar macht er es so, dass er ununterbrechbar ist, also atomar.

01:13:36.320 --> 01:13:41.160
Soll heißen, der hat ja einen Laden, einen Vergleich unten, speichern,

01:13:41.260 --> 01:13:43.520
konditionelles Speichern, möglicherweise speichern.

01:13:45.180 --> 01:13:48.320
Und es ist so definiert oder so implementiert der Befehl, dass

01:13:48.320 --> 01:13:52.480
zwischen dem Laden und dem Speichern garantiert wird, dass kein

01:13:52.480 --> 01:13:54.200
anderer Buszugriff passieren darf.

01:13:54.640 --> 01:13:59.740
Also es kann nicht sowas passieren wie, dass er erst den Wert lädt und

01:13:59.740 --> 01:14:02.620
danach kommt auf einmal einer, zum Beispiel dieser Thread hier, den

01:14:02.620 --> 01:14:05.960
Wert lädt und danach kommt einer von den anderen Threads und

01:14:05.960 --> 01:14:07.340
modifiziert den Wert hier.

01:14:07.340 --> 01:14:09.860
Das ist garantiert, dass es nicht passieren kann.

01:14:09.960 --> 01:14:12.420
Das garantiert die Implementierung von dem Assemblerbefehl.

01:14:12.840 --> 01:14:16.880
Und mit diesem atomaren Befehl, also wo mehrere Sachen ununterbrechbar

01:14:16.880 --> 01:14:19.740
direkt nacheinander gemacht werden, Laden, Vergleichen, Speichern,

01:14:20.180 --> 01:14:22.960
kann man den Befehl benutzen, um sich zu synchronisieren.

01:14:23.640 --> 01:14:26.920
Zum Beispiel benutzt man das dann so, aber dazu kommen die Details in

01:14:26.920 --> 01:14:28.120
der Betriebssystemvorlesung dran.

01:14:28.800 --> 01:14:33.340
Aber um es grob zu Ende zu bringen, benutzt man das, dass man quasi in

01:14:33.340 --> 01:14:38.400
die Speicheradresse zum Beispiel seine eigene Prozess-ID reinschreibt

01:14:38.400 --> 01:14:43.220
und wenn das Reinschreiben geklappt hat, dann weiß man, die gehört

01:14:43.220 --> 01:14:43.480
einem.

01:14:43.720 --> 01:14:45.040
Noch ein bisschen konkreter.

01:14:45.780 --> 01:14:51.060
Es gibt irgendein Stück Code, von dem sicherzustellen ist, dass nur

01:14:51.060 --> 01:14:56.200
einer von diesen vier Threads, T1, T2, T3, T4, ihn gleichzeitig

01:14:56.200 --> 01:14:56.660
ausführt.

01:14:56.760 --> 01:14:59.580
Also es dürfen nicht alle vier oder mehr als einer gleichzeitig den

01:14:59.580 --> 01:15:00.260
Code ausführen.

01:15:00.660 --> 01:15:03.320
Und dann gibt es eine Variable, so eine Art Lock, so eine Art

01:15:03.320 --> 01:15:07.540
Sicherheitsschloss, Eingangskontrolle, wie auch immer, in dem drin

01:15:07.540 --> 01:15:11.400
steht zum Beispiel der Wert 0 heißt frei, wird gerade nicht benutzt,

01:15:11.520 --> 01:15:15.420
keiner ist in diesem kritischen Abschnitt, keiner ist in diesem nur

01:15:15.420 --> 01:15:17.560
einmal auszuführenden Teil des Codes drin.

01:15:18.760 --> 01:15:22.480
Und dann kann jede CPU, jeder Thread sowas machen wie den CAS-Befehl

01:15:22.480 --> 01:15:27.160
benutzen, vergleichen, steht da die Null drin, wenn ja heißt das, der

01:15:27.160 --> 01:15:31.140
ist frei, ich kann ihn haben und dann schreibt irgendwas anderes rein,

01:15:31.220 --> 01:15:33.260
was nicht Null ist, zum Beispiel die eigene Prozess-ID.

01:15:33.820 --> 01:15:37.060
Und wenn ein anderer Thread kommt und auch diesen Abschnitt betreten

01:15:37.060 --> 01:15:40.100
möchte, auch das machen will, wird er diesen Wert lesen und sagen, oh,

01:15:40.180 --> 01:15:43.180
ist nicht Null, ich darf da jetzt gerade nicht rein, ich darf das

01:15:43.180 --> 01:15:44.460
jetzt nicht machen, ich muss warten.

01:15:44.820 --> 01:15:48.360
Ich mache kurze Pause und mache danach diesen CAS-Befehl nochmal.

01:15:48.980 --> 01:15:51.520
Und irgendwann ist der erste Thread, der drin war, fertig, dann sagt

01:15:51.520 --> 01:15:53.860
er, ich bin fertig, da kann jetzt wieder eine Null reingeschrieben

01:15:53.860 --> 01:15:55.580
werden, damit gibt er das wieder frei.

01:15:56.140 --> 01:15:59.680
Also das ist eine, das war jetzt keine gute, aber das ist eine Art,

01:15:59.760 --> 01:16:00.960
wie man sich synchronisieren kann.

01:16:01.400 --> 01:16:04.860
Und da gibt es eben noch zig andere Arten für und die werden alle in

01:16:04.860 --> 01:16:08.540
der Crip-System-Vorlesung erklärt und viele davon benötigen atomare

01:16:08.540 --> 01:16:11.540
Befehle und das sind genau diese Synchronisationsbefehle.

01:16:11.620 --> 01:16:13.980
Und da gibt es halt ziemlich viele verschiedene Arten, was die CPU da

01:16:13.980 --> 01:16:15.340
an Support anbieten kann.

01:16:15.420 --> 01:16:17.440
Und CAS ist halt eben einer, gibt noch viele andere.

01:16:21.740 --> 01:16:22.740
Gut, Befehlsformat.

01:16:22.900 --> 01:16:26.360
Jetzt für alle von diesen Befehlen, wie sehen die aus?

01:16:26.780 --> 01:16:29.320
Befehlsformat sagt, wie sieht der Befehl aus?

01:16:30.480 --> 01:16:33.680
Da gibt es verschiedene Felder normalerweise im Befehlsformat.

01:16:33.920 --> 01:16:38.940
Eins von den Feldern, der Opcode sagt halt eben, was für ein Befehl

01:16:38.940 --> 01:16:39.760
ist das eigentlich?

01:16:40.240 --> 01:16:44.000
Und dann müssten natürlich logischerweise noch irgendwie Operanten

01:16:44.000 --> 01:16:48.580
kommen und der Opcode sagt halt eben auch gleich, wie viele Operanten

01:16:48.580 --> 01:16:49.160
kommen müssen.

01:16:49.560 --> 01:16:54.720
Also wenn der Opcode zum Beispiel ein Befehl ist, von dem klar, von

01:16:54.720 --> 01:16:57.860
dem bekannt ist, dass er drei Adressen braucht, dann ist klar, dass in

01:16:57.860 --> 01:17:01.460
dem Befehl hinten dran irgendwie noch drei weitere Adressen kommen

01:17:01.460 --> 01:17:01.740
müssen.

01:17:01.960 --> 01:17:04.640
Okay, bei zwei Adressen, ein Adress, null Adressen halt jeweils

01:17:04.640 --> 01:17:05.840
weniger viele.

01:17:06.540 --> 01:17:09.880
Also der Opcode sagt, wie viele Parameter noch zu erwarten sind.

01:17:10.280 --> 01:17:12.080
Gucken wir uns ganz einfach mal ein Beispiel an.

01:17:12.680 --> 01:17:15.240
Und wie gesagt, x86 ist ein ziemlich unangenehmes Biest.

01:17:15.680 --> 01:17:16.800
MIPS ist da viel angenehmer.

01:17:17.140 --> 01:17:19.120
Gucken wir uns die MIPS Befehle mal an.

01:17:20.080 --> 01:17:24.420
Es gibt in MIPS, ich sage mal, drei verschiedene Befehlsformate.

01:17:24.520 --> 01:17:26.260
Es gibt noch ein paar mehr für Floating Point, ignorieren wir

01:17:26.260 --> 01:17:26.580
geschickt.

01:17:27.080 --> 01:17:29.540
Und ansonsten gibt es halt drei verschiedene Befehlsformate.

01:17:30.280 --> 01:17:36.820
Und in dem Format Typ R, der ist halt quasi gedacht für alle Register

01:17:36.820 --> 01:17:37.700
-Register -Befehle.

01:17:38.180 --> 01:17:42.360
Im Sinne von, da ist jetzt hier Platz gelassen, um drei verschiedene

01:17:42.360 --> 01:17:44.600
Register zu adressieren, jeweils mit fünf Bit.

01:17:44.680 --> 01:17:48.100
Also gibt es offenbar ein Register-File, wo 32 Register drin sind.

01:17:48.540 --> 01:17:53.320
Und ich kann für diese 32 Register zwei Quellen und ein Zielregister

01:17:53.320 --> 01:17:54.620
angeben.

01:17:55.380 --> 01:18:00.360
Der Opcode hier vorne, der sagt jetzt erst mal nur, es ist der Typ R

01:18:00.360 --> 01:18:01.300
Befehl.

01:18:01.420 --> 01:18:04.240
Also es ist ein Typ R Befehl.

01:18:04.640 --> 01:18:07.140
Er sagt aber noch nicht unbedingt, was der Befehl eigentlich tut.

01:18:07.760 --> 01:18:11.100
Er sagt erst mal nur, es ist ein Typ R Befehl und damit ist klar, die

01:18:11.100 --> 01:18:15.720
weiteren Bits hier hinten sind zu interpretieren als Quellregister,

01:18:15.880 --> 01:18:17.220
Zielregister und so weiter.

01:18:19.140 --> 01:18:22.200
Beim MIPS sind übrigens alle Befehle 32 Bit groß.

01:18:22.680 --> 01:18:24.460
Deswegen muss man gucken, wie man die da reingequetscht kriegt, aber

01:18:24.460 --> 01:18:25.120
hier hat es gut gepasst.

01:18:25.740 --> 01:18:28.520
Wenn ich jetzt ein Typ R Befehl habe, gibt es noch zwei weitere Felder

01:18:28.520 --> 01:18:29.000
hier hinten.

01:18:29.460 --> 01:18:31.400
Da gibt es einmal die Funktion.

01:18:32.800 --> 01:18:35.300
Und die sagt jetzt, was eigentlich gerechnet werden soll.

01:18:35.640 --> 01:18:37.880
Die sagt jetzt, soll eine Addition gemacht werden oder eine

01:18:37.880 --> 01:18:41.200
Multiplikation oder eine Shift-Arithmetik oder irgendwie sowas halt

01:18:41.200 --> 01:18:41.380
eben.

01:18:41.520 --> 01:18:42.880
Also da steht die Funktion drin.

01:18:43.420 --> 01:18:47.180
Das Angenehme an der Sache ist, die sechs Bits, die da hinten sind,

01:18:47.660 --> 01:18:52.000
die sind so definiert worden, dass man die quasi direkt an die ALU

01:18:52.000 --> 01:18:52.860
weitergeben kann.

01:18:53.280 --> 01:18:56.300
Also man kann quasi direkt die sechs Bits ohne jegliche Änderung

01:18:56.300 --> 01:18:57.460
einfach an die ALU geben.

01:18:59.760 --> 01:19:04.220
Also wenn ich hier quasi eine ALU habe, die kann alles Mögliche.

01:19:04.420 --> 01:19:08.380
Die kann addieren, die kann subtrahieren, die kann multiplizieren, die

01:19:08.380 --> 01:19:09.020
kann alles Mögliche.

01:19:09.580 --> 01:19:10.960
Die hat quasi ein Steuersignal.

01:19:11.360 --> 01:19:14.500
Und an das Steuersignal kann ich direkt die Funktion anschließen.

01:19:16.180 --> 01:19:20.040
Und die hat halt eben noch, die braucht Inputs offensichtlich und die

01:19:20.040 --> 01:19:26.340
Inputs kann ich direkt sagen, die Zahlen, die hier stehen, die

01:19:26.340 --> 01:19:28.980
Register muss ich aus dem Register-File rausladen.

01:19:29.580 --> 01:19:34.280
Also wenn hier irgendwo das Register-File ist, dann hat das vielleicht

01:19:34.280 --> 01:19:35.600
zwei Adresseingänge.

01:19:35.760 --> 01:19:39.120
Also ist auch nur ein Speicher mit mehreren 32-Bit-Registern.

01:19:39.760 --> 01:19:41.080
Da kommen zwei Adressen rein.

01:19:41.240 --> 01:19:44.680
Die beiden Adressen sind halt gerade genau der und der.

01:19:45.560 --> 01:19:48.380
Und das, was hier rauskommt, kann ich direkt an die ALU geben.

01:19:49.160 --> 01:19:53.080
Das Ergebnis, was hier rauskommt, jetzt wird's echt hässlich, das wird

01:19:53.080 --> 01:19:56.560
ja irgendwie wieder ins Register, vielleicht hier, reingeschrieben.

01:19:56.900 --> 01:20:00.560
Und wohin es geschrieben werden soll, dritte Adresse zum Beispiel

01:20:00.560 --> 01:20:00.820
hier.

01:20:01.260 --> 01:20:04.720
Also ich kann quasi die Bitfelder, die in dem Befehl sind, ziemlich

01:20:04.720 --> 01:20:08.340
direkt an die Hardware anschließen, damit der Befehl ausgeführt wird.

01:20:08.420 --> 01:20:10.860
Das ist das, sag mal, elegante an diesem Befehlsformat.

01:20:12.580 --> 01:20:14.060
Ja, an diesem Teil des Befehls.

01:20:15.040 --> 01:20:17.680
Okay, und dann gibt's noch irgendeinen seltsamen Klingen des

01:20:17.680 --> 01:20:18.660
Feldnamens.

01:20:19.000 --> 01:20:19.900
Ja, wie spricht man das?

01:20:19.960 --> 01:20:20.420
Keine Ahnung.

01:20:20.840 --> 01:20:22.960
Was gemeint ist, ist Shift Amount.

01:20:23.400 --> 01:20:28.060
Also falls es ein Schiebebefehl ist, dann wird darin angegeben, wie

01:20:28.060 --> 01:20:31.520
weit, wie viele Stellen soll der Wert denn geschoben werden.

01:20:31.640 --> 01:20:35.200
Das ist mit Shift Amount, whatever, gemeint.

01:20:36.040 --> 01:20:38.180
Es gibt, wie gesagt, drei Befehlsformate.

01:20:38.900 --> 01:20:42.500
Der zweite wäre für Immediate Registerbefehle.

01:20:43.080 --> 01:20:48.120
Immediate heißt immer, einer der Operanden, der Eingaben ist nicht in

01:20:48.120 --> 01:20:51.100
dem Register hinterlegt, sondern wird als Konstante angegeben.

01:20:51.580 --> 01:20:56.640
Und die Konstante, hier eine 16-Bit-Zahl, steht direkt im 32-Bit

01:20:56.640 --> 01:20:57.760
-Befehlswort mit drin.

01:20:58.340 --> 01:21:02.440
Da könnte man zum Beispiel sowas sagen wie Add Immediate, würde

01:21:02.440 --> 01:21:07.640
heißen, ich glaube, Register T ist in dem Fall die Quelle.

01:21:08.280 --> 01:21:13.040
Register T plus die Zahl, die hier steht, also das soll addiert

01:21:13.040 --> 01:21:13.440
werden.

01:21:14.340 --> 01:21:18.720
Und das Ergebnis davon schreiben nach, das wäre dann das Destination

01:21:18.720 --> 01:21:19.600
-Register, glaube ich.

01:21:19.680 --> 01:21:20.780
Oder umgekehrt, bin mir nicht sicher.

01:21:21.280 --> 01:21:23.320
Zumindest da gibt eins die Quelle, anderes Destination.

01:21:24.500 --> 01:21:28.440
Und hier das Immediate wäre halt eben eine Zahl, die dann je nachdem,

01:21:28.640 --> 01:21:32.640
ob es ein Signed oder Unsigned-Add-Befehl ist, eventuell noch

01:21:32.640 --> 01:21:33.560
erweitert werden muss.

01:21:33.720 --> 01:21:37.380
Also hier steht eine 16-Bit-Zahl im Immediate und wenn das jetzt ein

01:21:37.380 --> 01:21:40.420
Vorzeichen auf der 16-Bit-Zahl ist, die muss ja erst auf 32-Bit

01:21:40.420 --> 01:21:43.760
erweitert werden, würde ich quasi das Vorzeichen auf 32-Bit erweitern,

01:21:43.880 --> 01:21:46.540
danach kann ich es addieren und danach kann ich es abspeichern.

01:21:47.600 --> 01:21:52.040
Wird auch benutzt, der gleiche Befehlstyp für Ladespeicherbefehle.

01:21:53.740 --> 01:21:56.820
Da wird das Immediate benutzt, um einen Teil der Adresse anzugeben.

01:21:57.220 --> 01:22:01.440
Da ist es für gewöhnlich so zu sehen, dass das Quellregister, also ich

01:22:01.440 --> 01:22:06.060
bin mir nicht sicher, sagen wir mal RT ist das Quellregister, das, was

01:22:06.060 --> 01:22:12.920
da drinsteht plus der Wert vom Immediate und die Adresse, die soll

01:22:12.920 --> 01:22:15.980
dann zum Beispiel hier geladen oder mit Storework gespeichert werden.

01:22:16.840 --> 01:22:20.440
Bei Laden würde es heißen, also Inhalt hiervon plus das hier, das ist

01:22:20.440 --> 01:22:24.340
die Adresse, die Adresse aus dem Speicherladen, Ergebnis davon hier

01:22:24.340 --> 01:22:24.840
reinschreiben.

01:22:26.240 --> 01:22:29.100
Und es wird noch benutzt für Sprungbefehle.

01:22:30.340 --> 01:22:35.700
Der hier würde heißen Branch if Equal, im Sinne von, der hat kein

01:22:35.700 --> 01:22:39.420
Quellregister, sondern der ändert den Programme-Counter, also wo die

01:22:39.420 --> 01:22:40.620
Programmausführung weitergeht.

01:22:41.080 --> 01:22:45.160
Der würde quasi einfach sagen, also Equal, die beiden Register hier,

01:22:45.280 --> 01:22:50.040
RS und RT, werden gelesen, die werden verglichen, ob der Inhalt gleich

01:22:50.040 --> 01:22:53.620
ist und wenn der Inhalt gleich ist, Equal, dann wird gesprungen.

01:22:53.900 --> 01:22:55.020
Gesprungen wohin?

01:22:55.420 --> 01:22:58.480
An irgendwas, was abhängig vom Immediate ist.

01:22:58.640 --> 01:23:02.340
Und zwar ist das meistens sowas wie eine PC-relative Adressierung,

01:23:02.520 --> 01:23:06.960
soll heißen, das Sprungziel ist, da wo der Programme-Counter jetzt

01:23:06.960 --> 01:23:10.880
gerade ist, plus die Zahl, die im Immediate drinsteht.

01:23:11.220 --> 01:23:14.160
Also kann man da quasi, wenn der Programme-Counter im Instruction

01:23:14.160 --> 01:23:18.040
Memory gerade, ich gebe ein Register, das heißt Programme-Counter,

01:23:18.180 --> 01:23:22.180
dieses Register zeigt irgendwo auf den Speicher und das Immediate

01:23:22.180 --> 01:23:24.960
könnte dann quasi, das ist der Befehl, der gerade ausgeführt wird, das

01:23:24.960 --> 01:23:28.040
Immediate könnte dann quasi in einer gewissen Range um diesen

01:23:28.040 --> 01:23:30.660
Programme -Counter drumherum, plus, minus, könnte es die

01:23:31.300 --> 01:23:32.100
Programmausführung versetzen.

01:23:32.240 --> 01:23:34.980
Also könnte hier weitermachen oder hier weitermachen, irgendwo in

01:23:34.980 --> 01:23:37.440
einem 16-Bit-Bereich um den Programme-Counter drumherum.

01:23:37.520 --> 01:23:39.020
Also man kann ein bisschen nach oben, ein bisschen nach unten

01:23:39.020 --> 01:23:40.280
springen.

01:23:40.360 --> 01:23:42.680
Wobei ein bisschen, also 16-Bit sind schon eine ganze Menge.

01:23:45.100 --> 01:23:50.220
Und dann gibt es noch das Jump-Befehlsformat, offensichtlich auch für

01:23:50.220 --> 01:23:50.880
Sprünge gedacht.

01:23:52.220 --> 01:23:57.560
Und hier gibt es jetzt auch nur ein Immediate, nur ein sehr großes,

01:23:57.760 --> 01:23:58.920
ein 26-Bit-Immediate.

01:23:59.400 --> 01:24:02.520
Damit kann man offenbar noch weiter weg springen, wenn irgendwas wo

01:24:02.520 --> 01:24:04.120
ganz woanders im Speicher ist.

01:24:04.580 --> 01:24:07.320
Dafür ist dann aber auch hier vorne kein Platz mehr gewesen für

01:24:07.320 --> 01:24:07.740
Register.

01:24:08.180 --> 01:24:09.740
Man muss ja irgendwo das Immediate mal unterbekommen.

01:24:10.600 --> 01:24:13.320
Das ist halt der Nachteil, wenn man quasi die Befehle sagt, alle sind

01:24:13.320 --> 01:24:15.540
200 -Bit lang, dann geht dann irgendwann der Platz aus.

01:24:17.240 --> 01:24:21.160
Das wäre also quasi hier Jump, ein unbedingter Sprung, also ein immer

01:24:21.160 --> 01:24:24.620
durchzuführender Sprung, keinen Vergleich, keine Kondition, immer tun.

01:24:25.320 --> 01:24:29.200
Und dann wäre auch wieder Programme-Counter plus 26-Bit großes

01:24:29.200 --> 01:24:29.700
Immediate.

01:24:31.120 --> 01:24:34.960
Vielleicht, damit man es mal gesehen hat, das wäre sozusagen der

01:24:34.960 --> 01:24:37.920
Assembler -Befehl, den man nutzt, um eine Funktion aufzurufen.

01:24:38.080 --> 01:24:42.080
Also so ein Funktionsaufruf hat ja die Eigenschaft, dass ich

01:24:42.080 --> 01:24:47.260
irgendwann, wenn die Funktion fertig ist, dahin zurückkommen muss, von

01:24:47.260 --> 01:24:48.720
wo die Funktion aufgerufen wurde.

01:24:49.340 --> 01:24:52.240
Also ich habe quasi, ja können wir hier nehmen, ich habe hier den

01:24:52.240 --> 01:24:57.580
Programme -Counter, der zeigt irgendwohin und da wird dieser JAL, Jump

01:24:57.580 --> 01:25:02.360
and Link Befehl, aufgerufen und dem wird gesagt, ändere mal den

01:25:02.360 --> 01:25:06.300
Programme -Counter, damit er hierhin zeigt, weil hier ist jetzt

01:25:06.300 --> 01:25:09.260
irgendeine Funktion, die ausgeführt werden soll, eine Unterfunktion,

01:25:09.820 --> 01:25:12.760
dann führt er die aus, aber irgendwann ist er ja fertig und dann muss

01:25:12.760 --> 01:25:13.180
er zurück.

01:25:13.800 --> 01:25:17.080
Und für das Zurück ist dieses JAL, End Link.

01:25:17.360 --> 01:25:21.560
End Link heißt, es gibt noch ein anderes Register in Hardware, das

01:25:21.560 --> 01:25:26.720
Link -Register und bei dem Jump and Link-Register wird zum einen der

01:25:26.720 --> 01:25:30.580
Programme -Counter auf das Sprung-Ziel geschrieben und zum anderen

01:25:30.580 --> 01:25:35.140
wird in das Link-Register, was hier oben ist, wird reingeschrieben, wo

01:25:35.140 --> 01:25:36.100
es weitergehen soll.

01:25:36.220 --> 01:25:38.880
Also wo es weitergehen soll, wäre quasi da, wo der Programme-Counter

01:25:38.880 --> 01:25:42.740
gerade hingezeigt hat und dann ein weiter, den nächsten Befehl.

01:25:43.400 --> 01:25:46.060
In das Link-Register wird der Wert reingeschrieben von dem nächsten

01:25:46.060 --> 01:25:46.540
Befehl.

01:25:46.920 --> 01:25:50.180
Wenn hier oben irgendwann die Funktion fertig ist, dann kann man

01:25:50.180 --> 01:25:54.460
sagen, return, was im Wesentlichen heißt, springen dahin, was im Link

01:25:54.460 --> 01:25:55.420
-Register drin steht.

01:25:55.760 --> 01:25:58.180
Das wäre dann das Zurückkehren, zu wo die Funktion ursprünglich

01:25:58.180 --> 01:25:58.860
hergekommen ist.

01:26:01.280 --> 01:26:06.740
Das sind die verschiedenen Befehlsformate, nochmal die Variablen

01:26:06.740 --> 01:26:07.900
erklärt.

01:26:08.520 --> 01:26:10.180
Das Beispiel gucken wir uns noch kurz an.

01:26:10.660 --> 01:26:14.120
Hier hätten wir jetzt quasi einfach nur ein Additionsbefehl, der wäre

01:26:14.120 --> 01:26:17.380
vom R-Type und R-Type heißt in dem Fall, dass hier eine 0 drinstehen

01:26:17.380 --> 01:26:17.660
soll.

01:26:18.260 --> 01:26:24.800
Hier sind die Register von Quelle und Ziel, Shiften tunwendig, also

01:26:24.800 --> 01:26:29.340
egal, was hier drin steht, könnte auch 7 sein und der Befehl für

01:26:29.340 --> 01:26:31.140
Addition heißt dann offenbar HEX 20.

01:26:33.400 --> 01:26:37.500
Add-Unsigned ist quasi dasselbe, nur dass hier 21 drinsteht.

01:26:38.180 --> 01:26:41.820
Add-Immediate wäre jetzt ein anderes Befehlsformat.

01:26:41.820 --> 01:26:45.420
Hier wird der E-Type genommen, wo wir dann hier das Immediate haben.

01:26:47.720 --> 01:26:50.420
Jetzt wurde ich noch vorhin schon angekündigt, zwischen Add und Add

01:26:50.420 --> 01:26:54.760
-Unsigned, diese Namensgebungen sind ein bisschen verwirrend, weil wir

01:26:54.760 --> 01:26:57.100
hatten uns ja beim Zweier-Komplement überlegt, dass man nicht

01:26:57.100 --> 01:26:59.880
unterscheiden muss zwischen, ob es Signed oder Unsigned ist.

01:26:59.960 --> 01:27:01.320
Man kann das einfach addieren, fertig.

01:27:01.820 --> 01:27:06.460
Also die Additions-Hardware, die ALU macht keinen Unterschied zwischen

01:27:06.460 --> 01:27:07.260
den beiden Befehlen.

01:27:07.260 --> 01:27:12.260
Der Unterschied ist wie folgt, wenn bei der Berechnung ein Überlauf

01:27:12.260 --> 01:27:15.940
passiert, also quasi das Ergebnis nicht mehr in 32 Bit reinpasst,

01:27:15.960 --> 01:27:16.740
sondern ein 33.

01:27:17.020 --> 01:27:22.280
Bit benötigt würde, dann kann man die CPU wieder unterbrechen.

01:27:22.480 --> 01:27:23.720
Wieder so eine Unterbrechung.

01:27:24.020 --> 01:27:27.040
Und die Unterbrechung kann dann sagen, Überlauf, muss irgendwas

01:27:27.040 --> 01:27:27.440
machen.

01:27:27.820 --> 01:27:28.800
Keine Ahnung, was ich machen muss.

01:27:29.100 --> 01:27:30.460
Das kann der Programmierer sich überlegen.

01:27:30.960 --> 01:27:35.400
Und einer von den beiden Befehlen, nämlich der normale 50-50.

01:27:35.860 --> 01:27:36.600
Ich weiß es nicht.

01:27:36.880 --> 01:27:40.540
Einer von den beiden Befehlen führt einen Überlauf aus, führt eine

01:27:40.540 --> 01:27:43.900
Unterbrechung aus, wenn ein Überlauf passiert und der andere ignoriert

01:27:43.900 --> 01:27:44.800
den Überlauf einfach.

01:27:45.040 --> 01:27:46.860
Das ist der Unterschied zwischen den beiden Befehlen.

01:27:47.240 --> 01:27:49.800
Wieso der eine jetzt Unsigned heißt, boah, was weiß ich.

01:27:49.860 --> 01:27:52.620
Wahrscheinlich auch wieder Legacy, irgendwer mal irgendwann definiert.

01:27:54.180 --> 01:27:57.000
Gut, damit hätten wir eigentlich so das Befehlsformat und so weiter

01:27:57.000 --> 01:27:57.260
durch.

01:27:57.320 --> 01:27:59.720
Wir haben schon gesehen, man muss in der Lage sein, Adressen angeben

01:27:59.720 --> 01:28:02.760
zu können und verschiedene Arten der Adressberechnung gucken wir uns

01:28:02.760 --> 01:28:05.500
dann nächstes Mal, also am Mittwoch an.

01:28:05.640 --> 01:28:06.120
Bis Mittwoch.

01:28:06.200 --> 01:28:06.540
Dankeschön.

