Gut, heute werde ich jetzt zusammen,
also mit dem ersten Teil zur Assembler-Programmierung anfangen, beziehungsweise dem Teil,
was wir jetzt also quasi von Assembler letztendlich sehen werden.
Ich hätte ja vorgestern schon gesagt,
also hier geht es das jetzt grundsätzlich nicht darum,
also wirklich Assembler-Programmierung zu lehren oder zu vermitteln,
sondern nur ein gewisses Grundverständnis dafür zu bekommen,
was sich eigentlich dann eben wirklich so auf dieser Schnittstelle zwischen Software und der Hardware letztendlich tut,
wie man das beispielsweise dann eben jetzt hier an dem Beispiel des X86er Samblas letztendlich umsetzen kann.
Und um jetzt eben nachher dann also auch gerade einige Effekte
in Bezug auf Speicherverwaltung, Sicherheitsmechanismen, also das,
was dann so die klassischen Pufferüberläufe sind und so weiter zu erklären.
Hier dann also nochmal so ein paar Sachen.
Sambla Programmierung. Aber wie gesagt, das ist dann
natürlich also erst nur sehr eingeschränkt
Ja, aus Sicht des Assembler-Programmierers, kann man sagen,
ist der Rechner, so wie wir ihn jetzt hier auch sehen,
also im Prinzip nichts anderes als CPU mit einem Registersatz,
in diesem Fall, also quasi vom 32-Bit X-86 Assembler mit 8 Registern,
dem Flag-Register, das,
was also hier als Condition-Codes bezeichnet ist und dem Programmzähler,
EIP, Instruction-Pointer, immente Terminologie,
abgekürzt und dann eben eben im Hauptspeicher,
wobei dann in den Hauptspeicherbereich oder in den Hauptspeicher dann quasi eingeblendet,
der Stack ist, dass dann also auch etwas,
was wir jetzt hier heute beziehungsweise nächsten Mittwoch dann also noch so ein bisschen kennenlernen werden als Mechanismus,
da als Methode um Unterprogramme aufzurufen beziehungsweise da dann eben Parameter zu übergeben.
Ja, aus Sicht des Programmiers, wie gesagt, sehe ich den Programmzähler.
Dort steht dann eben in der Regel die Adresse der nächsten Anweisungen,
die aus dem Hauptspeicher geholt werden soll,
dann eben die Registerbank mit theoretisch Achter gestern,
diesem E-Fleck-Register, wo dann also die Statusinformationen drin sind,
was für bedingte Sprünge beispielsweise genutzt werden kann und was über mehrere Möglichkeiten gesetzt wird.
Auch das werde ich gleich noch kurz vorstellen.
Im Prinzip implizit über die letzte rentmedische Operation,
ansonsten gibt es aber eben auch spezielle Anweisungen,
mit denen man solche Abfragen in diesem Flag-Registern
eben auch
explizit machen kann
Der Speicher ist dann eben aufgebaut als ein Aree,
was baldweise adressiert wird, wo dann eben letztendlich der Programmcode,
die Nutzerdaten und eben teilweise noch Informationen des Betriebssystems drin sind.
Gut, und dort eben entsprechend eingebettet dann der Stack. Umsetzung von Hochsprachencode,
also hier im Beispiel wird jetzt immer C genommen,
in den ausführbaren Objektcode, mehrstufig, also was ähnliches,
gab es ja immer schon auf diversen anderen Folien,
also ich habe im Prinzip meine programmierte Textdatei, das,
was sie mit irgendeinem Textadditor eingegeben habe, hat man eben den Compiler,
was dann beispielsweise bei der Nude-Tool-Tain als GCC-S aufgerufen wird,
der erzeugt aber auch schon eben den Assembler-Code.
Das war ja dann also auch schon, jetzt erstmal nur
vom Aufruf her, so Teil dieser einen kleinen Übungsaufgabe.
Nicht dann kommt ihm der eigentliche Assembler,
der daraus dann eben Binär-Code generiert und hinterher um draußen ausführbares Programm zu erzeugen,
dann eben ein Lauf mit dem Linker,
wo dann eben noch statisch benutzte Bibliotheken mit hinzugenommen werden.
Im Gegensatz dazu gibt es, das wird nachher also auch
nochmal kurz erläutert, dann eben auch noch dynamische Bibliotheken.
Das, was also beispielsweise bei Windows dann diese DLLs sind,
dann eine McLink-Libraries auf Unix-artigen Systemen, SO-Bibliotheken, also Shared-Object-Files.
Gut, das war's, der,
es nennen wir jetzt eben Macht oder der Compiler macht,
wenn ich jetzt das Assembler-Code erzeuge, hier als Hochsprachencode,
Aufruf einer Funktion,
die mir also im Prinzip die Summe von zwei Entager-Werten berechnet
Wird dann beispielsweise so umgesetzt,
eben eine Folge von verschiedenen Anweisungen im Ascenalar-Code, was sich dahinter verbirgt,
wird also vielleicht noch ein bisschen genauer vorgestellt, die Grundidee.
Und wie gesagt, das Ganze passiert dann eben durch Aufruf von GCC-S.
Und dann kann man noch diverse Parameter damit angeben,
die dann also die Art der Code-Erzeugung beeinflussen,
also minus Groß-O ist dann im Prinzip die einfachste Stufe
der Optimierung
Da kam man dann aber auch noch diverse andere Parameter angeben,
beziehungsweise Default-mäßig ist, meine ich,
also auf den meisten Linux-Systemen sowieso O2, glaube ich, mit eingestellt.
Und je nachdem, was man dort angegeben hat,
mit welcher Compiler-Version man dann auch arbeitet, ist dann also auch der Erzeugte Assembler-Code jedes Mal etwas anders.
Deswegen ist es jetzt eben auch nicht so,
dass man jetzt sagen kann, okay,
beispielsweise den Übungsaufgaben, ich nehme jetzt also mein C-Programm,
schiebe das durch den Assembler und genau das ist das richtige Ergebnis,
sondern das ist ja wirklich von der Compiler-Version und von irgendwelchen Parametern,
die man damit übergeben hat, abhängig,
wie dann hinterher der Sambler-Code genau aussieht.
Gut, wenn man sich jetzt ansieht,
wie sieht so ein Assembler-Code, also diese,
das war es also mit Punkt S dann aus dem Compiler rauskommt, aus.
Man ist dort natürlich dann entsprechend hardwarenah,
das heißt also wirklich mit diesen einfachen Befehlen jetzt aus dem
normalen Befehlssatz des Rechners,
das was also im vorigen Kapitel dann ja immer als
Instruction Set bezeichnet wurde
Ich habe dann jeweils pro Zeile einen Befehl, meistens in irgendeiner mähmonischen Schreibweise,
irgendwelche Konstanten oder die sind dann eben entweder extradezimal oder dezimal mit angegeben.
Beispiele folgen dann auch gleich. Nicht,
dann gibt es dann also irgendwelche Bezeichner für die vorgehenden Register,
das mit den allengängigen Namen, naja gut,
da kann man sich drüber streiten,
ob das irgendwie sonderlich eingängig ist,
aber zumindest bei den acht Registern,
die wir jetzt also hier sehen,
ist es noch
vergleichsweise überschaubar
Gibt es für die Adressierungsarten mehrere verschiedene Modi,
die werden dann also im Folgenden auch gleich noch ein bisschen genauer vorgestellt.
Und war es dann also auch immer noch abhängig von dem Compiler
oder von der Entspannung Compiler Toolchain dann, unterschiedlich ist.
Also wie der SM-Lakot dann eigentlich genau notiert ist,
also bei der Gnu-Toolchain sieht das dann eben so aus,
dass bei irgendwelchen Operationen das Ziel letztendlich recht drinsteht
Gut, Sprünge, Sprungadressen sind dann also quasi über symbolisches Label mit angegeben.
Die werden dann einfach mit Punkt L 1 bis irgendwas durchnummeriert,
beziehungsweise für irgendwelche Unterprogrammaufrufe gibt es dann eben sowas wie globale Label,
die dann genommen werden, um also von vielen verschiedenen Stellen auf ein Programm reinzuspringen.
Beispiele, wie gesagt, vor Augen gleich noch ein bisschen.
Als Datentypen werden dort eigentlich nur die vergleichsweise einfachen primitiven Datentypen benutzt,
das heißt also irgendwie sowas wie Zusammengesetzte Typen, Arrays,
Records oder sowas, sind dann auf der Assembler-Ebene letztendlich schon komplett aufgelöst.
Das wird dann also auch ein bisschen später in der nächsten Woche,
demzufolge dann also auch nochmal kurz skizziert, wie sowas also umgesetzt wird.
Da sieht man dann nämlich also auch,
wie man oder weswegen man also beispielsweise jetzt gerade so bei
der C-Programmierung mit irgendwelchen RAs dann also schnell mal die berühmten Pufferüberläufe bekommt,
wo man sich da dann eben irgendwie mit den Indizes verrechnet und dann also auf einmal in irgendwelchen Speicherbereichen ist,
also die dem Programm eigentlich nicht mehr gehören und wo dann also hoffentlich das Betriebssystem feststellt,
dass dort was schief läuft.
Wie gesagt, das heißt,
da hat man dann eben nur entsprechend sehr einfache Datentypen,
was dann also typischerweise die üblichen Indiger-Zahlentypen sind, entweder signed,
unsigned, dann mit entsprechenden Längen, Charakters und eben Fließkomma-Datentypen,
die werden wir jetzt aber hier auch weiter nicht betrachten.
Gut, daneben gibt es dann eben noch Adressen,
beziehungsweise die entsprechenden Werte können dann eben auch jeweils an Speicheradressen interpretiert werden.
Damit werden dann programmiertechnisch sowas wie Pointer letztendlich implementiert.
Gut an, befehlen hat man dann also,
wie gesagt, von den sehr vielen sehr unterschiedlich.
Ich habe ja so irgendwie vorgestern gesagt,
so grob über 300 Befehlen beziehungsweise Varianten,
die es also im Prinzip so auf der Intel-Architektur gibt
Natürlich sehr viele. Wir wären jetzt hier nur einen sehr kleinen Ausschnitt und
also auch nur entsprechend sehr einfache Befehle sehen. Entsprechend dieser Klassifikation,
wie ich sie bei der Instruction Set Architecture vorgestellt hatte,
kann man dann eben auch sagen, gut,
ich habe einmal so die üblichen Arrenmeter, logischen Befehle,
wo ich dann eben mit Registern oder mit Speicheradressen arbeiten kann.
Ich habe dann also irgendwelche Befehle, mit denen ich also
quasi Datentransfer machen kann.
Das heißt also irgendwie, welche Zugriff auf einen Speicher
in Register
Und es gibt dann eben noch diverse Befehle, um letztendlich Kontrolltransfer zu machen.
Das heißt also, wie werden Sprünge implementiert,
wie wir ein Unterprogrammaufrufe umgesetzt und wie kriegt man dann,
wobei das würde ich dann also hier jetzt auch nicht mehr genauer vorstellen,
wie kriegt man da sowas wie Interraps, Ausnahmebehandlung oder Systemeaufrufe letztendlich hin.
Seitens der Bibliotheken gibt es dann eben auch noch Unterprogramme dort vorgegeben,
dann eigentlich letztendlich sowas wie Makros,
was dann im Prinzip auch erstmal nichts anderes sind,
wenn ich jetzt also irgendeine Bibliotheksfunktion benutze, beispielsweise in C,
jetzt also aus Standard-IEO, dann also irgendwie sowas wie ein Print,
die sind dort dann eben auch nur definiert dann als Folge von Assembler-Befehlen.
Gut, das, was der Assembler jetzt ja also aus
dem kompilierten Assembler-Code macht, ist dann eben wirklich Objektcode,
was beispielsweise jetzt also für diesen Assembler-Code Summe dann irgendwie so aussehen könnte.
Das heißt also einfach nur bei irgendwie beginnt einer bestimmten Startadresse.
Eine Folge von irgendwelchen Hexadezimalen Werten,
die dann entsprechend dem Programmcode zu interpretieren sind.
Und bei dem X86-Prozesse war es ja so,
dass die Befehle unterschiedliche Längen hatten.
Das heißt, es gibt eben Befehle,
die dann also hier noch ein einziges Byte umfassen.
Es gibt aber eben auch welche,
die dann zwei oder drei
Bytes umfassen
Und um das zu sehen oder zu interpretieren,
würde man dann eben wieder mit einem Disassembler folgt,
dann gleich auf der nächsten Folie nach dieser hier,
das Ganze sich ansehen und würde dann quasi aus diesem Code
oder dieser Interpretation dann im Prinzip wieder sowas rauslesen können.
Gut, nochmal Unterschied Assembler und Linker. Das,
was man als Assembler bezeichnet, ist eben der Teil,
der eigentlich die Übersetzung von dem Assembler-Code, also dem lesbaren,
hämonischen Code, in so eine Objektdatei macht.
Letztendlich einfach nur eine 1-zu-1-Ersetzung, wo dann eben quasi jetzt für irgendwelche Anweisungen passende,
binäre Kodierung gewählt werden und was dann eben noch passiert,
ist, dass für die Label, die in einem Assembler-Code drin sind,
dann eben schon irgendwelche willkürlichen Adressen oder Adresseersetzungen stattfinden.
Dann gibt es als weiteren Schritt dann eben noch den Linker,
beziehungsweise aus der Historie dann auch in Deutschland immer noch oft als Binder bezeichnet.
Dass es dann eben quasi nochmal die nächste nachfolgende Stufe,
die dann, wenn ich jetzt meinen Programmcode aus mehreren Quelldateien erstellt habe,
das Ganze dann eben wirklich zu einem ausführbaren Programm zusammenlinkt,
wo dann eben entsprechende Querverweise, Referenzen aufgelöst werden.
Und wenn das Programm statisch gelingt wird, das heißt,
aus den irgendwelchen Systembibliotheken dann eben Programmaufrufe wirklich in den Code direkt noch eingebaut werden,
wird das dann eben entsprechend dort auch noch gemacht.
Meistens macht man das aber aus Effizienzgründen, spreche um Platz zu sparen. Natürlich so, dass die meisten Programme dynamisch gelingt sind. Das heißt,
in der Regel sind die meisten Standardfunktionen bzw. irgendwelche Bibliotheken,
die benötigt werden, um jetzt also irgendwelche mathematischen Sachen zu machen,
um grafisch über irgendeinen Grafik-Subsystem,
Ein- und Ausgaben zu machen,
Zeichenroutinen und so weiter,
was man also alles nicht selber von Skretsch von Hand neu programmieren und erfinden möchte,
nimmt man ja typischerweise dann eben aus irgendwelchen Bibliotheken und in der Regel ist das eigentlich dynamisch gelingt,
weil diese Teile sowieso schon von irgendwelchen anderen Programmen,
sei es des Betriebssystems oder irgendwelchen anderen Applikationen mit benötigt werden und man die deswegen dann eigentlich nur insgesamt ein einziges Mal im Speicher laden muss und nicht in jedes ausführbare Executable quasi diese Programmteilaufrufe mit reinbringen muss.
Gut, in dem Beispiel, wie sieht das jetzt also aus bei dieser Summenfunktion?
Da war ja im Prinzip hier sowas wie der C-Code,
wo jetzt also zwei Ganzzahlen addiert werden. Im Assembler-Code entspricht diese eigentliche Addition dann so einem Aufruf at long,
dass L ist dann immer quasi für den Datentyp, weil wie gesagt,
da gibt es dann also noch die abhängig von den Datentypen diverse unterschiedliche Additions- Subtraktions- und was auch immer Operationen,
das, womit wir jetzt hier arbeiten, beziehungsweise das,
was sich jetzt auf den Folien vorstellen werde,
sind dann immer quasi auf Long-Daten-Worten, also sprich 32 Bit-Wortbreite.
Das, was dort dann letztendlich hier passiert,
ist, dass ich hier berechne,
quasi jetzt das Äquivalent zu diesen operanten in dem Register EAX,
was mit diesem Prozent EAX gekennzeichnet ist,
dort muss dann eben quasi vorher über jetzt irgendwelche anderen Befehle der eine Operant herangebracht werden.
Damit wird gerechnet, dann wird ein, für den zweiten
Operanten aus dieser Formel dann also hier ein Speicherzugriff gemacht.
Das ist dieses acht, Klammer auf Prozent EBP.
Klammer zu, was sich dahinter verbirgt,
wie das zu interpretieren ist
Kommt gleich noch, also von daher jetzt nicht gleich sagen,
um Gottes Willen, was ist das für eine Krude,
Syntax, dann muss ich das jetzt verstehen.
Wird gleich noch ein bisschen genauer erklärt, was sich dahinter verbirgt.
Nicht nur, also jetzt hier,
um schon mal so grob diesen Befehl oder die Idee jetzt
von dieser Folie jetzt zu erklären. Also das heißt,
da habe ich also hier den einen Operanten,
hier den anderen Operanten, das Ergebnis wird dann wieder in dem Register IAX gespeichert.
Nicht das heißt,
dadurch ergibt sich dann also hier diese Zuordnung und insgesamt
habe ich dann also auch das Ergebnis dieser Summenoperation
dann in diesem Register,
wo das dann typischerweise übergeben wird
Nicht im X86-Befehlsatz, wäre dann,
wenn ich jetzt also diesen Assembler-Befehl nehme und das dann also jetzt irgendwie dann wirklich jetzt über ein Assembler beziehungsweise linker dann nachher umsetze,
dann eben irgendwas,
das also an einer bestimmten Speicheradresse dann also diese Dreibyte beispielsweise stehen,
was, wenn man jetzt hier zurückscrollt, dann nämlich genau,
wenn ich das jetzt hier richtig zu blöd, das zu bedienen.
So war es dann nämlich im Prinzip genau dann.
Also hier der Fall ist, also 03 4 5 08,
wenn ich das jetzt richtig hatte. Nicht, das ist
dann also genau dieser Befehl, der dann aus dem SM-Lakot rauskommt.
Gut, um sich jetzt also wirklich diesen Objektcode anzusehen.
Also auch dazu gab es ja schon diese eine kleine Aufgabe,
wo man da zumindest mal das Programm mal startet.
Nicht dafür gibt es dann eben den Befehl Objectdarm,
wo ich mir dann mit Objectdarm minus D und dann also hier quasi einen Teil des Programms,
dann also aus Dem Code, der dann eigentlich untersucht wird,
dann wieder irgendwie was Lesbares mit so einer hämonischen Schreibweise rauskriege,
wo ich dann eben auch genau diese Zuordnung habe zwischen dem X86-Assembler-Befehl und dem entsprechenden Binärcode.
Damit kann man sich das Ganze dann entsprechend ansehen,
hier unten eben nur als Anmerkung,
das gilt also sowohl für die eigentlichen ausführbaren Programme,
also das, was dann also wirklich jetzt irgendwo als Executable startet,
gilt aber eben auch noch für die Programmteile,
bevor sie vom Linker, also jetzt wirklich zusammengebaut werden.
Das heißt also das, was jetzt, wenn ich mehrere C-Quell-Programme habe,
die jetzt also mit einem Compiler in Assembler-Code übersetze,
die dann jeweils mit einem Assembler in entsprechende.o-Object-Dateien übersetze,
könnte ich also auch jede dieser Teildateien schon entsprechend untersuchen und dann eben sehen,
was dort letztendlich dann vorhanden ist.
Alternativ kann man das auch mit dem D-Bug-Tool machen,
GDB ist der Gnou D-Bugger, so in nackter Form,
wenn man das jetzt so aufruft, dann also auch erst mal in irgendeiner,
sagen wir mal, unfreundlich zu bedienenden Kommandozahlen-Oberfläche.
Es gibt dafür dann aber auch noch entsprechende Frontends,
wo man das dann eben auch dann entsprechend grafisch aufbereitet hat,
beziehungsweise das, was wir, sagen wir,
eigentlich dann so gewohnt sind,
sind dann also irgendwelche entsprechend etwas größeren,
komfortableren Entwicklungsumgebungen,
also Market Clips oder so,
wo sowas dann eben letztendlich mit eingebettet ist
Wo ich mir dann eben beispielsweise da dann eben auch so Code mit ansehen könnte.
Und auch da gibt es dann eben die Möglichkeit,
diese Objektdatei dann eben mit, nach dem Staat dann mit Disressemble,
Sam dann also quasi hier in so eine Ausgabezüge führen,
wo ich dann eben letztendlich auch genau dieses Ergebnis sehe.
Gut, was kann alles disressembliert werden, habe ich ja eben schon gesagt,
also letztendlich alles, was irgendwie ausführbarer Code sein kann,
egal ob er jetzt schon zusammengelingt ist, oder noch nicht. Und gut,
der Disressembler versucht das dann eben so weit wie möglich,
gibt dann noch so ein bisschen Informationen,
also wie es dann genau das Dateiformat aus,
was sind dann die eigentlichen Befehlsteile,
was gibt es noch an irgendwelchen statischen Teilen,
die also teilweise in dem Code noch mit drin enthalten sind,
wo dann also letztendlich sowas wie irgendwelche Konstanten, Definitionen beispielsweise hinterlegt sind.
Gut, von dem, was jetzt eigentlich so in die Assembler-Programmierung selber reingeht,
das, was ich jetzt hier so ein bisschen vorstellen werde,
sind also erstmal so ganz einfach diese Adressierungsarten,
also spreche ich das mit diesem Prozentregister, Klammer auf,
Regist, Prozentregister und sowas.
Was sich da dahinter verbirgt, dann eben einiges zu den arathmetischen Operationen, Handhabung des Statusdrückers. Das heißt,
wie man jetzt eben sowas wie diese Sprünge hinkriegt und dann so eine grob Vorstellung,
wie eigentlich jetzt irgendwelche Programmstrukturen, also sprich das,
was wir in der Hochsprache jetzt typischerweise haben,
wenn wir jetzt also irgendwelche Verzweigungen machen,
wenn wir irgendwelche Schleifen implementieren,
wie sowas,
zumindest von der Idee her,
erstmal eine Samblercode
umgesetzt werden kann
Wie gesagt, das Ganze mit der Einschränkung, dass wir jetzt hier also nur die 32-Bittlung-Datentypen nutzen.
Das heißt, der X86-Prozessor, der dem zugrunde liegt,
wird dann eigentlich eher so ein bisschen wie so eine rissartige Maschine benutzt.
Und alles, was eigentlich sonst mit dem Befehlssatz dort möglich ist,
haben wir jetzt also hier nicht weiter betrachtet. Die Syntax,
wie gesagt, ist dann eben jetzt die von der Gnu-Tool-Chain,
also das, was sich eingangs ja auch schon gesagt hatte,
als ich jetzt die nächsten oder dieses Kapitel angekündigt hatte. Nicht also das,
was sie jetzt entweder in Form einer virtuellen Maschine oder eben eine entsprechende Installation,
also auf einem eigenen Rechner oder Nutzung in irgendwelchen Pool-Rechnern des Fachbereichs benutzen können,
wobei da muss ich auch gleich einschränken,
weil das habe ich jetzt also auch gerade noch von irgendwelchen Studenten gehört,
also auf den Pool-Rechnern im Rechenzentrum ist zurzeit,
da muss man nochmal dafür sorgen,
dass dafür sonst nochmal was nach installiert wird,
die 32-Bit-Entwicklungsumgebung quasi nicht installiert, beziehungsweise die entsprechenden Teile fehlen.
Das heißt, wenn ich da so irgendwie so ein GCC minus M32 starte,
kriege ich also da einfach eine Fehlermeldung. Gut,
aber da eigentlich jetzt also nicht zwingend dann noch weiter Aufgaben in der Richtung zu lösen sind,
in den Übungsaufgaben, in der nächsten Übungsaufgabe,
war das also der Lionstock auch noch nicht so groß,
dass ich da jetzt also die Kollegen vom Rechenzentrum noch angespitzt habe,
da jetzt noch, also schnell was nachzuinstallieren.
Gut, Auch nochmal Hinweis, also alles,
was ich hier immer habe, also diese ganzen bunten Assembler-Folien,
sind eigentlich aus dem Foliensatz,
der zu dem Buch von Bryant und Ohelleran gehört,
Computer Assystem und Programmers Perspective, rausgenommen,
weil da habe ich jetzt also im Prinzip dann immer auf die entsprechenden Querverweise bei den Quellen,
nicht weil in der Regel habe ich sonst immer hier irgendwie so eine Quellenangabe jeweils mit drin,
verzichteten.
Das heißt, das gilt jetzt eigentlich hier für alles an Grafiken.
Gut, Assembler-Befehle. Allereinfachster Befehl ist im Prinzip erstmal irgendwie
so was wie Datentransfer. Das heißt also irgendwie Speicher,
Register oder Register, Register, irgendwas hin und her schieben.
Das, womit wir jetzt hier arbeiten, von Registersatz,
jetzt beim 32-Bitch, x,
86 Prozesse sind dann im Prinzip diese acht Register,
wobei die beiden Grün gekennzeichneten, ESP und EBP,
für was anderes reserviert sind.
Damit wird dann nämlich im Prinzip dann
der Stack gehandhabt
Das wird dann also auch gleich noch hier auf den Folien entsprechend kommen.
Und diese Register sind im Prinzip frei verfügbar.
Eigentlich sind also diese ESI und EDI eigentlich auch noch so
ein bisschen für irgendwelche Rehzugriffe gedacht.
Aber die werden jetzt hier eigentlich auch erstmal beliebig frei verwendet.
Gut, das heißt, wir haben jetzt also im Prinzip
einen Prozessor mit acht oder in Anführungszeichen oder eingeschränkten eigentlich sechs Registern,
die man dann letztendlich irgendwie benutzen kann.
Und um Daten zu transferieren, kann man dann eben sagen,
okay, einfachster Befehl,
move long
Quelle, Komma, Ziel, ich finde das, was hier steht, wird da reinkopiert.
Nicht, das ist also eine der entsprechend sehr häufigsten Instruktionen,
die, wenn man jetzt also Kot analysieren würde, dort auftritt. So,
und das, was dann also hier für Quelle beziehungsweise Ziel angegeben werden kann,
an Operanten, können entweder Emediate-Werte sein,
das heißt also ganz zahlige positive, negative Zahlen, die kann ich,
die werden eben geschrieben, wie in C, als Konstante,
aber dann eben jetzt den Assembler-Code oder in der Assembler sind das dann also mit dem Brefix-Dollar davor.
Das heißt, also ich kann dann eben entweder in Hexadezimal,
also Dollar, 0X und dann 4-0-0,
entsprechend Hex werden oder wenn ich jetzt einfach nur hätte Dollar und dahinter eine Zahl,
wäre das dann eben die entsprechende Integer-Zahl und gut in diesem Fall jetzt,
also bei einer negativen Zahl, also dann Dollar,
minus 533.
So, und je nachdem, wie groß die Zahl ist,
würde das dann eben vom Assembler entsprechend lang kodiert werden
Dann kann Ziel, beziehungsweise auch Quelle ein Register sein,
eben eins von diesen acht Ganszahlregistern. Register sind immer dadurch gekennzeichnet,
dass sich also quasi jetzt hier einen von diesen Bezeichnern
habe und dann eben die entsprechend dann dies Prozent davor.
Das ist jetzt einfach erstmal, sagen wir mal,
so die Namenskonvention oder als drittes kann der Operan dann im Prinzip einen Speicherzugriff sein.
Das heißt also, ich kann eben genauso gut auch irgendwelche Werte
direkt in den Speicher schreiben, beziehungsweise direkt aus dem Speicher lesen.
In diesem Fall der Slong dann eben auf vier aufeinanderfolgende Speicherarbeit.
Die Art und Weise, wie die Adressen dort gehandhabt werden,
folgt dann gleich. Da gibt es dann eben auch noch
unterschiedliche Beschreibungen. Gut, einfachste Methode. Jetzt für die
Smooth L Hier unterteilt, abhängig jetzt von der Quelle,
beziehungsweise der Art und Weise, wo jetzt hingeschrieben wird.
Wäre, wenn ich jetzt also ein Emediate-Wert in ein Register schreiben möchte,
also Move L, Dollar, dann in Emediate-Wert,
dann kommen wir dann entsprechend das Register.
Würde dann eben den Hexadezimalwert 4 in dieses Register EAX speichern,
Mechasie hin,
wäre dann also quasi immer noch so eine Analogie zur C
sind,
da so
ein bisschen aufgezeigt
Speicherzugriff, also den Emidiate-Wert jetzt also irgendwie auf den Speicher,
in den Speicher schreiben, wäre dann eben die entsprechende Emidiate-Wert.
Und wenn ich dieses Register dann in Klarmann setze,
ist das dann eben eine, das,
was also genau jetzt letzte Woche oder die,
beziehungsweise dann über Weihnachten, die indirekte Adressierung war,
dass ich dann eben, oder eine Registeradressierung ist,
dass ich da dann eben quasi den Inhalt des Registers als Speicheradresse interpretiere und eben auf diese Adresse,
die in dem Register steht,
den Wert speicher.
Das heißt, wenn ich das jetzt also quasi als Sie sind DAX interpretiere,
steht in dem Register dann quasi die Speicheradresse.
Das heißt, das Register ist dann sowas wie ein Point da.
Deswegen dieses Stern-P. Gut, ich kann da natürlich jetzt also
auch irgendwie als Quelle ein Registerwert haben.
Demzufolge dann eben hier entsprechend direkt das Register. Ich kann
aber eben auch als Quelle, also irgendwie ein Speicherzugriff machen.
In diesem Fall wäre das jetzt hier quasi ein Äquivalent,
jetzt für irgendeine Art Load-Operation,
wo ich jetzt eben den Speicher Auslese an der Adresse,
die in dem Register IAX steht und das Ganze in das Register EDX abspeicher.
Nicht, das sind erstmal so die einfachsten Kombinationen,
wie dieses Move Long funktioniert. Hier nochmal diese ersten drei Fälle,
die ich jetzt gerade hier in den Beispielen schon hatte,
soweit vorgestellt. Ich hatte, wie gesagt, Emediate-Wert,
wird eben einfach entsprechend direkt interpretiert.
Register ist dann eben quasi dadurch,
dass sich dies Prozent und dann den Bezeichnis Registers
dastehen habe
Hier, das ist dann also immer diese Memonic-Schreibweise,
wo dann also quasi dann mit der Inhalt des Registers gemeint ist.
Und der normale Zugriff oder das,
was sie als normal bezeichnet wird,
ist dann also quasi mal der Speicherzugriff,
wo ich dann eben den Inhalt des Registers nehme und das also als speicher Adresse interpretiere und damit arbeite.
Gut, dann gibt es dabei noch eine Erweiterung,
dass ich dann also vor diese Klammer auch noch ein Offset schreiben kann,
also quasi einfach ein ganz normale Indiger-Zahl,
die jetzt aber eben nicht durch einen Dollar gekennzeichnet wird,
nicht also so viel zur eingängigen Syntax
Wo man dann beispielsweise, wie in diesem Befehl,
die Adresse, von der gelesen wird,
aus dem Register EBP liest,
uns zu dieser Adresse noch als Offset 8 hinzu addiert.
Das heißt also, die Adresse ist demzufolge dann 8 Byte größer.
Nein, also nicht, ja, also 8 Byte dazu addiert.
Also 8 dazu addiert,
also entsprechend dann also 8 Byte weiter im Speicher
Nicht, dass es dann eben so ein zusätzlicher Offset.
Gut, hier ist jetzt einmal so das erste Mal so
ein kleines Beispiel für eine Funktion oder Funktionsaufrufe,
wie das Ganze dann im Assembler aussieht.
Generell kann man immer erst mal sagen, also hier als Funktion,
Swap Asson, also quasi zwei Zeige auf Inditorwerte genommen werden,
werden hier quasi jetzt oder werden übergeben.
Nicht, die sollen dem,
wie der Name dann schon andeutet, quasi vertauscht werden,
also sprich X und Y oder Inhalt von X-Pointer und Y-Pointer
wird dann eben demzufolge dann also vertauscht
Ich weiß, man dann also beispielsweise ein C so kodieren könnte. Und der Assembler-Code würde dann so aussehen,
will ich jetzt noch gar nicht im Einzelnen vorstellen,
weil da kommen jetzt eben noch gerade diese Sachen zur Handhabung von Unterprogrammen mit rein.
Generell kann man nur erst mal sagen, das,
was eigentlich immer typisch ist für jetzt irgendwelche Funktions- oder Prozeduraufrufe,
ist, dass man das Ganze eigentlich immer unterteilen kann in drei Teile.
Einmal irgendwie etwas, was also hier dann im Englischen immer als Setup bezeichnet ist. Also letztendlich sowas wie so eine Art Initialisierung,
die quasi zu Beginn des Aufrufs stattfindet, wo es darum geht,
entsprechende Konvention, wie Funktionen aufgerufen werden,
bestimmte Registerinhalte zu sichern bzw. entsprechend sowas wie diese beiden speziellen Register,
nämlich den Basepointer und den Stackpointer entsprechend umzusetzen,
was dort passiert, wird gleich im nächsten Teil entsprechend klarer,
wenn ich das dann also noch wirklich genauer jetzt die Handhabung des Stacks vorstelle,
weil da geht es also darum,
dass hier quasi Initialisierung für den Stack passiert,
dann kommt die eigentliche Berechnung,
das heißt dieser Tal hier drin entspricht jetzt eigentlich dem,
was jetzt also hier an Programmcode mit drin ist,
wo im Prinzip jetzt also hier eine der Referenzierung jetzt dieser Inhalte stattfindet,
das sind also hier diese beiden Befehle,
anschließend werden dann also hier die Werte von ECX und EDX ECX und EDX als Adressen genommen.
Das ist dann ja quasi, war das hier hier Pointer sind,
die Inhalte entsprechend in EAX und EBX gelesen und hier das Ganze dann quasi jetzt wieder vertauscht zurückgeschrieben.
Das heißt, also dieser Teil hier drin entspricht dann eigentlich dem hier.
Und dann gibt es eben,
bevor die Funktion oder dieser Aufruf dann quasi mit dem Return verlassen wird,
dann eben so etwas, was letztendlich in umgekehrter Reihenfolge dem hier entspricht,
wo dann eben diese Handhabung von dem Stack dann quasi wieder zurückgespielt wird.
Ja, wie gesagt, die genaue Handhabung von dem Stack,
also die ist hier vorne folgen etwas später hier wirklich noch im Detail.
Also deswegen wollte ich da jetzt also nicht lange drauf rumreißen.
Allgemein noch mal zu der Adressierung oder zu den Adressierungsmodi.
Das, was die Syntax jetzt vom X-86 Coach zulässt,
ist eigentlich erst mal so eine ganz allgemeine Form.
Also das, was sie jetzt hier eben schon vorgestellt hatte,
weil es also Inhalt von Register, nicht also dadurch bekennzeichnet,
dass sich das also jetzt als Sparchadresse interpretiere,
wenn das in Klammern ist, ein Displacement davor.
Also es geht eigentlich noch ein bisschen allgemeiner,
indem ich dann also hier optional dann eigentlich noch drei Teile haben kann.
Zum einen eben dieses Displacement, also hier ist es dann
als Emediate eigentlich als Offset bezeichnet.
Dann eben ein Basisregister, also eins von den acht Entager-Registern
Das, was bislang vorgestellt wurde,
die anderen Teile sind dann eben auch hier entsprechend erst mal optional,
wurden weggelassen, wäre dann eben jeweils so was wie hier,
wo ich dann eben quasi jetzt hier den Inhalt von dem Register nehme und dann zwölf oder hier entsprechend mit acht als Offset noch irgendwie was rechne.
Dann gibt es aber eben noch die Möglichkeit, so was wie so eine richtige Basisadressierung zu machen. Das heißt,
wo ich also irgendwo so was wie eine Startabreise von einem Array beispielsweise hinterlege und dann mit einem Indexregister,
wo ich dann also quasi einen Zähler habe,
der dann also jetzt quasi für die einzelnen Rehe-Elemente durchlaufen wird,
mit Offsets von dieser Basisadresse rechne,
was sich dann dadurch hier kennzeichnet,
dass ich dann sage,
ich habe also hier die Basisadresse,
addiere dann immer noch entsprechend ein Offset von diesem Index mit hinzu,
dann gibt es noch als
auch noch optionales Argument sowas wie ein Skalierungsfaktor
Der ist eigentlich genau dann notwendig, wenn ich jetzt also
im Prinzip als Datentyp irgendwas habe, was mehrere Bytes umfasst.
Wenn ich jetzt also im Prinzip jeweils 32 Bitworte in meiner Adressierung weiterspringen möchte,
müsste ich dann ja immer quasi als Adressenbasisadresse plus null Basisadresse Plus 4,
Basisadresse plus 8, dass sich da dann eben immer entsprechend jetzt in der Adressierung diesen Offset mit berücksichtige.
Demzufolge gibt es da dann eben immer noch diesen Skalierungsfaktor,
der dann eben typischerweise der Anzahl der Byte entspricht.
Und gut, zusätzlich gibt es dann eben noch diesen Emediate als zusätzlichen Offset.
Das heißt also, in einer ganz allgemeinen Form wäre dann
so ein Ausdruck umzusetzen als ein Speicher, Zugriff unter einer Adresse,
die sich dann eben aus diesem hier errechnet.
Das heißt also, ich nehme eben den Inhalt des Registers
plus den Inhalt dieses Registers mit einem Skalierungsfaktor, plus diesem Emmediate.
Gut, die, wie gesagt,
gebraucherlichsten Fälle sind dann also hier nochmal so aufgezielt,
aber im Prinzip kann man also aus dieser Form erst mal
mehr oder minder sinnige oder unsinnige Adressierungsmodi sich zurechtrechnen.
So, wie sieht so eine Adressberechnung jetzt beispielsweise aus?
Also das ist jetzt auch eine der aktuellen Übungsaufgaben, angenommen,
ich habe im Register EDX Den Wert, Hexer dezimal F000 stehen,
im Register ECX 100. Ich dann würde die Adresse,
die sich jetzt also quasi aus diesem Ausdruck ergibt,
0X8, Klammer auf, Register EDX, Klammer zu,
dann eben errechnet werden,
als der Inhalt von EDX plus of Set 8,
also F-008. Nicht,
wenn ich hier drin stehen hätte,
jetzt eben EDX,
Koma E-CX,
wäre das dann eben entsprechend
die Addition von den beiden
Wenn ich hier dann eben das Ganze dann nochmal mit diesen Skalierungsfaktor,
das heißt, dann nehme ich eben EDX plus ECX mal 4,
war es dann eben zu diesem Ergebnis führen würde.
Und man kann das eben aber auch nutzen in einer Art und Weise,
wo ich dann, also hier zum Beispiel jetzt,
also dieses erste Argument weggelassen habe,
EDX mal 2 plus Offset wäre dann zweimal F-0-0-0,
also 1 E 0-0 plus 8-0, eben entsprechend dieser Ausdruck.
Gut, das ist, ich sag mal erst mal etwas,
wo man so sagen kann, okay,
also muss sie also irgendwie diese Krude Sind Darks jetzt,
also hier von dieser Form letztendlich nur merken, was sich dahinter verbirgt,
dann ist es aber eigentlich relativ straightforward und also wirklich simpel auszurechnen.
Nicht ansonsten muss man immer sagen, also auch jetzt hier in den Beispielen,
ich war, da wurde eigentlich dann nachher nochmal so ein bisschen motiviert,
wofür diese Adressierungsschema dann also auch sinnig sein können.
In der Regel ist es ja so, dass wir ja
nun nicht irgendwie auf Assembler-Ebene selber kodieren, glücklicherweise nicht. Also ich kann das nämlich auch nicht,
sondern also in der Regel wird das also eher höchst nochmal so sein,
dass man sagt, okay, ich habe jetzt meinen Code,
ich übersetze den, gucke mir dann eben raus,
was kommt als Assembler-Code raus und das,
was die Compiler letztendlich machen, ist dann eben irgendwas,
wo dann eben diese ganzen Adressierungsmodi letztendlich benutzt werden,
um mehr oder minder elegant irgendwelche Adressberechnungen oder irgendwelche Berechnungen durchzuführen.
Gut, dann an Operationen oder Operatoren, die dann möglich sind.
Erstmal ganz normal, so die üblichen, binären Berechnungen,
wo ich dann eben sage, Addition,
Subtraktion,
Enteger Multiplikation,
Shift Left
Was dann also entweder SHL oder SAL, also Shift,
Arrithmetic Left oder einfach nur Shift Left,
weil sie quasi bezeichnet werden, nicht beim Linksschieben.
Gut, da gibt es sowieso keine Unterscheidung zwischen Arrithmetisch und logischem Schieben.
Beim Rechtsschieben gibt es dann eben einmal das Arrithmetische und einmal das logische Schieben,
dann sowas wie XOR und oder und noch diverse andere.
Daneben geht bis dann natürlich noch irgendwelche unnäheren Operatoren,
Inkrement, Dekrement,
Negation,
Nicht Unterschied zwischen den beiden sollte, hoffentlich klar sein.
Also ein Negation, ein Minus ist jetzt wirklich die arathmetische Negation.
Das heißt, irgendwas, was hier als Ausdruck genommen wird,
was negiert wird, wird als Zweierkomplimentsal interpretiert.
Welche Negation oder Negative wertvoller Zweierkomplimentszahl wäre,
wenn ich das schon ein bisschen länger her, Kapitel 4,
wäre dann im Prinzip Bidvises invertieren und Addition plus 1 am niederwärtigsten Bett.
Das ist also quasi die einfachste Bildung, um direkt jetzt das Zweier-Kompliment zu bilden.
Not ist wirklich nur Mitweises invertieren. Das heißt also,
alle Bits werden 0 und 1 hier jeweils gekippt. Gut,
ein Befehl, der dann auch häufig noch genutzt wird,
der eigentlich von der Intel Isar-Terminologie als LEA bezeichnet wird,
Load Effective Adress,
der aber eigentlich für alles mögliche als Ladebefehl bzw. als Rechenbefehl missbraucht wird,
zumindest also von den gängigen Compilern.
Deswegen ist er hier also auch nochmal explizit aufgeführt.
Der benutzt nämlich im Prinzip also die für die Adressberechnung genau diese Syntax,
wo ich dann eben im Prinzip, also Basisregister,
Indexregister und ein Skalierungsfaktor habe und dann eben noch zusätzlich dann noch als zusätzlichen Offset diesen Emediate wert,
um damit dann also irgendwie was zu rechnen und das Ganze dann in einem Register abzuspeichern.
Wie gesagt, in den folgenden Beispielen sieht man an,
wie das also wirklich benutzt wird,
um damit also letztendlich irgendwie was zu berechnen
Gut, mit diesen arrothmetischen Operatoren kann man dann letztendlich irgendwelche Sachen,
die ich mir jetzt also aus irgendwelchem Code-NC jetzt überlegt habe,
compiler-mäßig, ja, Assembler-mäßig dann also umsetzen,
beziehungsweise Compiler würde dann also beispielsweise aus diesem C-Code dann also hier sowas machen,
nicht nur da,
also auch hier erstmal nur diese Unterteilung von Initialisierung beziehungsweise diesen Endprozeduren,
die dann eigentlich erstmal nur dafür da sind,
um Den Stack zur Hand haben und innen drin jetzt also hier irgendwelche Sachen,
die dann eigentlich diesen ganzen Formeln entsprechen.
Was verbirgt sich dort im Einzelnen? Das ist jetzt nämlich
im Prinzip hier nochmal mit den entsprechenden Kommentaren jetzt in Bezug,
also auf den C-Code hinterlegt. Also dies ist jetzt also genau das,
was also hier innen drin in der Folie vorher da als Body bezeichnet wurde.
Und zwar beim Aufruf dieses Unterprogramms wird entsprechend dieser Aufrufkonvention bei X86-Prozessoren,
also der Stack so aussehen, dass es in diesem Register EBP-Basepointer ein Verweis ist auf eine bestimmte Speicheradresse.
Und die entsprechend nächst höheren Speicheradressen, das heißt mit Offset 4,
wäre jetzt die Return-Adresse von dem Unterprogrammaufruf und alles,
was da noch davor darüber ist,
hat das aufrufende Programm auf dem Stack, dann nehme ich die Parameter hinterlegt.
Das heißt, die sind dort an entsprechend in umgekehrter Reihenfolge,
dann eben hier auf dem Stack drauf. Deswegen wird jetzt,
wenn jetzt also dieses ARED entsprechend aufgerufen ist und hier dieser Teil mit dem Bardi jetzt in die Abarbeitung kommen,
wird als erstes jetzt quasi vom Stack entsprechend runtergelesen in das Register EAX,
das Argument X, was sich dann eben errechnet,
aus über so einen Move Long, Basepointer mit Offset 8.
Das ist dann nämlich genau hier entsprechend diese Adresse,
das wird in EAX gelesen, das heißt EAX hat dann in Wert
X. EDX hätte dann entsprechend das zweite Argument. Nicht dann dieser Teilausdruck T1,
also sprich X plus Y wird dann mit diesem Load-Effective Adress letztendlich berechnet,
also jetzt nicht durch ein Additionsbefehl,
sondern also in diesem Fall,
also hier über diesen LEA-Befehl
Wo dann im Prinzip EDX plus EIX addiert wird und das Ergebnis in ECX reingeschrieben wird. Anschließend wird dann also hier gerechnet,
was also hier gleich noch benutzt wird als Trickserei,
um Y mal 48 zu rechnen, ist,
dass sich also hier ein Y,
was ja so in einem Register EDX ist,
mit dem diesem Load Effective Adress DDX, EDX,
Skalierungsfaktor 2 wird im Prinzip berechnet in EDX,
wo vor Y drin war, jetzt dreimal Y,
nämlich Y mal zwei plus Y. Sprich,
damit habe ich dann also hier dreimal Y und dieses 48 mal Y wird dann dadurch erzeugt,
dass ich also jetzt quasi dieses dreimal Y nehme und das jetzt also noch entsprechend um vier Oder um den Offset 4 dann nach links verschiebe.
Womit ich dann also im Prinzip 4 nach links verschieben wäre,
mal 2, mal 4, mal 8, mal 16.
Dadurch, dass ich das vorher schon mal drei multipliziert habe,
habe ich dadurch dreimal 16.
Und das ist dann nämlich genau dieses Y mal 48,
was dann also anschließend nach diesem Befehl hier landet.
Also das jetzt nur so als Beispiel, wie man also
mit diesen Bitch-Sheeberreihen und mit diesen LEAL-Befehlen dann also irgendwie so ein bisschen dreckige Arithmetik macht.
Wie gesagt, das muss man jetzt nicht lösen,
möglicherweise in der Regel nicht selber programmieren,
sondern das macht einen also dann ein hoffentlich schlauer Compiler-Verein,
nicht aber nur mal um so ein bisschen zu veranschaulichen,
wie das aussieht, beziehungsweise das, was dann eigentlich da, also bei solchem Compiler-Code bei rauskommt.
Gut, entsprechend wird dann also hier Weiterverfahren jetzt,
also für die anderen Ausdrücke,
nicht also um jetzt Z plus T1 zu berechnen,
nicht T1 war ja ein ECX gespeichert, Z habe ich noch gar nicht geholt,
das wird dann also quasi auch wieder über diesen Base-Pointer mit Offset 16 geholt,
habe ich dann eben hier EBP, Aufsatz 16 plus ECX,
das Ganze wird dann wieder in ECX gespeichert,
dem zuvor gegilt anschließend.
Dieses hier nicht und um dann, also hier die Berechnung,
dann jeweils für TV 5 beziehungsweise nachher für diesen Return-Web abzuschließen,
würden dann also hier diese Befehle entsprechende auch noch eingesetzt werden.
Gut, also das als Beispiel, wie letztendlich jetzt mit
diesen atmetischen Operationen gerechnet wird, bei logischen Operationen Im Prinzip Ähnliches, wie gesagt,
also hier auch jetzt irgendwie nur eine willkürlich gewählte Funktion,
mit der jetzt also irgendwelche logischen Operationen gemacht werden,
wo ich jetzt also hier beispielsweise X exklusiv oder Y rächen,
also Bitweise ist exklusiv oder nicht anschließend wird dieses Ergebnis genommen, um 17 Bit nach rechts geschoben
Dann wird eben noch eine Maske generiert,
wo ich dann also eine Eins um 13 bin,
nach links schiebe, minus sieben und anschließend dann eben dieses Zwischenergebnis mit dieser Maske maskiert, also sprechen Bitvises.
Und das ist dann mein Ergebniswert. Und das,
was also dort jetzt ein irgendwie schlau programmierter,
optimierender Compiler dann rausmacht, sind dann also letztendlich diese vier Befehle,
wo dann im Prinzip auch hier jetzt über entsprechende Offsets von diesem Basepointer, dann also hier die Argumente geholt werden.
Also hier sprich wird X gelesen, hier wird dann also
X exklusiv oder Y gerechnet. Bei dieser Maskierung hat der Compiler quasi im,
beziehungsweise dieser Befehl hier macht jetzt also erstmal jetzt hier nur diesen zweiten Teil,
also das ist Berechnung von T2, dass sich da dann eben TA1 nehme, um 17 Bündn nach rechts schiebe.
Gut, und hier dann eben hier bei diesem letzten Teil,
also der Erstellung der Maske und dieser Maskierung,
da hat der Compiler dann also quasi diesen konstanten arrethmetischen Ausdruck schon entsprechend ausgewertet.
Nicht 2 hoch 13, minus 7, dann als 8185
letztendlich in den Code direkt rein kompiliert.
Und dann ist also quasi diese,
sind diese beiden Zeile dann also hier nichts anderes als dieses
Wittweise und von der konstanten und dementsprechenden Wert
Gut, also das erst mal so zum hohem Ablauf oder zum Grundverständnis,
so der einfachen, arathmetischen, logischen Befehle bzw. zur Art und Weise,
wie jetzt also im Prinzip mit diesen Adressierungsmodi gearbeitet werden kann,
wie werden jetzt Programmstrukturen umgesetzt,
sprich wie kriege ich jetzt also irgendwie meine Verzweigung hin,
wie kriege ich meine Schleifen hin,
nicht bzw. wie kann man jetzt eben sowas abfragen,
um dann also quasi in Verzweigung letztendlich zu sagen,
abhängig von irgendeiner Bedingung,
also meinetwegen x-größer Y soll es eben irgendwie das eine oder das andere passieren.
Gut, dafür werden eben die Inhalte dieses E-Flex-Register benutzt,
beziehungsweise von diesen bestehenden Flex, also jetzt hier auch nicht alle,
beziehungsweise das, was ich hier vorstelle, ist,
wie gesagt, immer nur ein sehr kleiner Ausschnitt.
In dem Fall sind jetzt also hier interessant eigentlich erstmal nur diese vier Flex,
Carry, Sign, Zero und Overflow,
die jetzt im Prinzip auf drei Arten und Weisen
gesetzt werden können
Beziehungsweise Teile davon. Zum einen habe ich bei arathmetischen Operationen
im Prinzip immer eine implizite Aktualisierung dieser vier Flags.
Das heißt, wenn ich also sowas rechne wie Adlong,
Source, Destination und das Ergebnis langsam auch immer in Destination,
wird quasi dann für das Ergebnis bestimmt, wird dieses Carry Flag gesetzt.
Das heißt, habe ich Bade-Addition, ein Carry Out,
was dann bei der Unsigned-Interpretation dann quasi als Überlauf gilt,
dann habe ich Zero, wenn das Ergebnis gleich null ist,
sein, wenn das Ergebnis kleiner null ist,
bei Zweier-Kompliment vergleichsweise trivial, das höchstwertigste Bit, meines Ergebnisses,
und dann gibt es eben noch eine Bedingung für Overflow beim Zweier-Kompliment,
also auch das hatte ich in diesem Tal zur Rechnerathmetik schon mal kurz angedeutet
, Kann man eigentlich mal dadurch feststellen, dass ich sage,
beispielsweise, jetzt hier bei so einem Befehl,
also man kann sich das immer in diesem Zahlenkreis veranschaulichen,
nicht beispielsweise, wenn ich jetzt sage, also beide Argumente sind positiv,
also a größer 0 und b größer 0 und das Ergebnis ist negativ,
dann habe ich entsprechend einen Überlauf in die eine Richtung gehabt oder ich kann natürlich also auch entsprechend einen Überlauf andersrum haben,
nämlich, dass ich sage,
also beide Ergebnisse oder beide Argumente sind negativ,
das,
was aber aus der radmetischen Berechnung rauskommt,
ist dann auf einmal
ein positives Ergebnis
Gut, neben diesem impliziten Sätzen durch die arathmetischen Befehle gibt es dann eben noch einen ganzen Satz von Vergleichsoperationen,
wo dann im Prinzip diese Flex explizit gesetzt werden können,
beispielsweise jetzt über so einen Compare-Befehl, beziehungsweise dann also korrekt ausgedrutscht,
also auch Compare Long, deswegen CMPL,
Compare würde dann quasi jetzt im Prinzip von den beiden Argumenten als Ausdruck berechnen,
Source 1 minus Source 2,
also eigentlich genau das Gleiche wie Sublong.
Nur bei, wenn bei aronmetischen Befehlen, also bei der Subtraktion,
wird das Ergebnis dann also auch hier als Source 1 gespeichert beim Compere,
werden die Werte hier entsprechend nicht verändert.
Und gut, also auch da gilt dann eben entsprechend der jeweilige Arrithmetik,
dass dann also entsprechend diese Flex gesetzt werden.
Also wie gesagt, hier ist das jetzt also für dieses Beispiel der Subtraktion beschrieben.
Also beispielsweise jetzt hier mit den Überläufen Nicht bei der Subtraktion,
wenn das eine Argument größer null ist, es andere kleiner null.
Und das Ergebnis ist dann also auf einmal entsprechend falsch oder hier entsprechend umgekehrt.
Gut, daneben gibt es dann noch Testanweisungen. Das ist
dann im Prinzip so was wie eine, also quasi ein Affirland zu dem hier, also dieses Vergleichsoperationen oder diese Vergleichsoperationen sind Im Prinzip jetzt hier immer irgendwelche arathmetischen Operationen,
während die Testoperationen sind dann immer irgendwelche logischen Operationen,
das heißt also X-R-End oder All,
wo ich dann also im Prinzip dann mit diesem Testanweisungen dann quasi als Äquivalent wie so ein Bidvises End rechne,
nicht nur dass da dann eben auch quasi wie beim bei diesen Compersachen da dann,
also auch das Ergebnis nicht abgespeichert wird und okay, also die,
da ich hier jetzt natürlich keine Adertometik habe und dort nicht rechne,
werden dann also quasi jetzt die Overflow und das Carrie Flag nicht gesetzt,
aber Zero und Scient werden dann eben entsprechend für das Ergebnis gesetzt.
Gut und von diesen Befehlen gibt es dann relativ viele,
die man benutzen kann und zwar einmal in der Form Von dem Set-Befehlen,
wo ich dann im Prinzip jetzt ein Byte in einem Register setzen kann,
explizit und dann eben mit unterschiedlichen Bedingungen,
nämlich gleich null und gleich null, negativ,
nicht negativ, größer, größer, leicht, kleiner,
kleiner, leicht, barf und Billo.
Der Unterschied bei zwischen größer bzw. kleiner ist hier dann eben entsprechend die Interpretation,
dass das eine letztendlich auf Scient-Arrithmetik aufbaut, das andere auf Ansign Arrithmetik.
Deswegen, wenn hier dem zufolge also einmal nur die Carryflex betrachtet und einmal hier diese Designflag.
Gut ein Beispiel dafür wäre dann im Prinzip sowas,
wo ich dann also hier Als Funktion zum Beispiel sowas hätte,
es soll ein Intagela-Wert berechnet werden oder zurückgegeben werden,
wenn für die Bahnargumente, wenn entsprechend x größer Y ist.
Und das würde dann in Assembler Code dann quasi in
diesem Anweisungsteil so ausgeführt werden, dass man sagt,
im Prinzip über diesen Compare-Befehl wird Das entsprechende Ergebnis beit.
Oder der entsprechende Vergleich gemacht. Dann mit diesem Setbefehl wird
das Ergebnis dann quasi gesetzt. Da diese Befehle immer nur
ein einzelnes Bite in den Registern setzen. Dazu muss man jetzt immer sagen,
hier sieht man jetzt für diese Register immer noch,
wie die dann teilweise auch noch beidweise adressiert werden können.
Das ist dann also auch, sagen wir jetzt eher aus dieser Historie zu erklären.
Also die ersten 8008 beziehungsweise 80,
80-prozessoren waren also erstmal 8-Bit-Prozessoren mit dem 80, 86,
88, 88, hatte man dann im Prinzip 16-Bit-Prozessoren und erst intel 80,
3,
86 kam dann im Prinzip die 32-Bit-Prozessoren und demzufolge wurden dann eigentlich auch innerhalb der Architektur die Register immer nur namentlich eigentlich irgendwie breiter gemacht.
Das heißt, mit dem Register EAX spreche ich hier in 32-Bit-Register an
Es gibt aber eben auch noch A. High und A. Lo,
wo ich dann also wirklich die und von den unteren 16 Bit
dann eben entweder das Slow oder das Highbite explizit ansprechen kann.
Und diese Setbefehle arbeiten oder setzen jetzt eben erstmal nur ein einzelnes Bite.
Deswegen gibt es hier dann eben so ein Set, die für Hallo,
was dann also im Prinzip dann genau hier dieses eine Bad erstmal nur setzt,
dann braucht man eben noch so einen Befehl,
der dann quasi hier draus Oder aus diesem Wert dann eben wirklich ein gültiges Rückgabewert in das Register EAX-Sets.
Das ist dann eben dieses Move. Move, äh,
was Zero extent bart to long, was dann eben quasi hier raus,
dann also wirklich einen Wert macht, wo dann also hier die oberen Bits letztendlich nur gelöscht werden.
Ich, was dann dieses hier setzt, eine andere Möglichkeit wäre,
dass jetzt also zum Beispiel auch mit einer Bit-Maske zu machen,
aber wie gesagt, das also nur als zum Beispiel,
wie das dann also letztendlich so Compiler machen würde.
Gut, so kann man dann im Prinzip sowas machen,
wie jetzt abfragen und das dann als Integer-Wert quasi explizit setzen, zurückgeben. Was man natürlich eigentlich meistens hat,
beziehungsweise was dann im folgenden, also gerade jetzt bei Verzeigung,
Programmaufrufen dann ja auch eigentlich eher auftaucht,
ist dann natürlich, also irgendwie sowas wie Sprünge,
demzufolge gibt es dann auch analog zu diesen Setbefehlen,
wo ich jetzt also so ein einzelnes Bite in so einem Universalregister setze,
Dann eben auch entsprechende Jump-Befehle,
die dann eben auch mit den gleichen Bedingungen gleich und leicht,
negativ, nicht negativ größer, größer, leicht,
kleiner, kleiner,
leicht.
Entsprechend Sprünge ausführe, beziehungsweise es gibt dann eben auch noch
einen unbedingten Sprung, um jetzt also wirklich explizit irgendwo hinzuspringen.
Gut, beim Springen, wie wird das gehandhabt?
Dazu kann man eben in dem SM-Lacode dann eben
auch beliebige Label mitverwenden
In der Regel sieht es ja erst mal so aus,
der Assembler-Code liegt also irgendwo beidweise im Speicher,
wird dann eben quasi Befehl für Befehl abgearbeitet,
um jetzt Sprünge zu machen,
baut einem der Compiler oder der Assembler-Programmierer,
wenn man sowas wirklich mal selber machen muss,
dann eben entsprechende Label mit ein,
was dann eben irgendwelche symbolischen Namen erst mal sind,
die dann nachher vom Assembler,
der dann quasi jetzt also aus diesem hämonischen Code dann also wirklich irgendwie beidfolgen generiert,
dann entsprechende Adressen mit rein,
nicht aber hier arbeitet man also erstmal mit irgendwelchen symbolischen Bezeichnern.
In der Regel sind das eben, wie gesagt,
also irgendwas mit L irgendwas durchnummerierte Adressen.
Es gibt dann aber eben auch noch sowas wie globale Label,
die dann also sich dadurch dann immer auszeichnen,
dass sie ja so mit dem Unterstrich begehen.
Nicht, dass es dann eben etwas um ist,
also zwischen mehreren Teilen oder um jetzt Unterprogrammaufrufe
Zu realisieren, wo dann also im Prinzip dieses Label dann
mich dem bezeichnet, des Unterprogramms in der Regel entsprechen.
Gut, wie werden jetzt bedingte Sprünge realisiert? Ja,
also jetzt haben wir hier ein Programm,
war es jetzt also nicht für zwei Argumente überprüft,
ist das eine größer als das andere und es wird als Ergebnis 0 oder 1 zurückgegeben,
sondern ich habe hier eine Funktion,
Max,
die mir im Prinzip für die beiden Argumente X und Y das Maximum zurückgibt,
was man also in C wäre noch ein bisschen umständlich dann
also so schreiben könnte
Der Assembler-Code, den der Compiler draus macht,
sieht dann eben auch so aus, dass man sagt,
also ich habe hier einmal als globales Label dieses Max,
nämlich den Bezeichner der Funktion und ich habe dann eben hier noch irgendwo zwischendurch irgendwo in meinem koten Sprunglebel,
was ich eben brauche,
um jetzt hier quasi diese Verzweigung zu realisieren.
Nicht und ansonsten hat das also auch wie irgendwelche Unterprogrammaufrufe diesen Code,
der benötigt wird,
um das Ganze zu initialisieren bzw. den Unterprogrammaufruf zu beenden oder
dann eben diesem Tal mit dem Buddy innen drin
Und wie wird das Ganze jetzt rein technisch implementiert?
Das heißt, wie funktioniert dieser Code? Und dazu muss man eben sagen,
das, was dieser Code hier ist, ist eigentlich jetzt nichts,
was äquivalent oder direkt äquivalent zu dieser Art C-Programmierung ist,
sondern das entspricht eigentlich eher so einem C-Code, der also quasi mit so einem sehr unschönen Go-To arbeitet. Das heißt,
wenn ich also diese Funktion Max in C jetzt also mal so umschreibe,
dass ich sage, initialisiere,
quasi mein Returnwert jetzt willkürlich mit einem von den beiden Argumenten,
mache dann hier meine Abfrage, abhängig von der Abfrage,
bin ich entweder, habe ich hier in meinem Returnwert schon das richtige Argument,
dann kann ich dann eben direkt entsprechend hierhin springen,
wenn nicht, hätte ich den anders initialisieren müssen,
also setze ich, er will jetzt nicht auf Y,
sondern auf X und lande dann eben hier bei dem Return Airwell
Nicht, dass wir also jetzt quasi in C das Ganze
mehr oder minder unschön mit Goto und irgendwelchen Sprüngen umgeschrieben.
Und genau das ist das, was eigentlich der Assemble oder
der Blödsinn der Compiler aus so einem C-Code machen würde,
dass er eigentlich also intern sowas macht, was er dem hier entspricht.
Nämlich hier werden die beiden Argumente X und Y jetzt also
auch erstmal über Offsets von diesem Base-Pointer in EDX und ERX gelesen.
Nicht dann werden die beiden miteinander Verglichen, in EAX,
was ich dann also benutze, um auch mein Ergebnis wieder zurückzugeben,
habe ich ja schon einen der Werte drin stehen.
Das ist nämlich das, was ich also hier als
quasi Y am Anfang da mal reingelesen habe.
Und nach diesem Compare wird jetzt eben gefragt,
wenn das Ergebnis kleiner Null ist,
Dann habe ich hier also schon den größeren Wert reingespeichert.
Dann kann ich also leicht zum Ende springen. Und nur wenn das Ergebnis
nicht kleiner Null ist, wird hier der nächste Befehl genommen.
Es wird dann eben in EAX der andere Wert,
also sprich X, reingespeichert. Das ist dann eben noch
dieser Move-Long-Befehl. Und anschließend lande ich dann eben auch hier
bei dem Label und irgendwann am Ende dann,
also bei irgendwas, was dann eben dies entsprechend diesem Return arbeitet.
Nicht, dass das dann die Art und Weise, wie im Prinzip jetzt bedingte Sprünge Also sprich einfache E-Verzwagung umgesetzt werden. Gut,
wie sieht das jetzt mit Schleifen aus,
wenn ich jetzt als Code hier sowas wie so eine,
du Wildschleife habe, beispielsweise jetzt hier um die Fakultät auszurechnen,
wo ich dann da so sage,
das Ergebnis wird mit Arzt initialisiert und ansonsten,
und dann wird das Ergebnis jeweils mit X multipliziert,
X dekrementiert und das Ganze solange das Ergebnis größer eins ist und das,
was sich dann eben in diesem Enteger Result dran habe,
wird dann entsprechend zurückgegeben.
Auch das wird dann vom Compiler in so eine Art Go-To-Variante umgesetzt. Das heißt,
also das, was dann also Assassembler-Code rauskommt, entspricht,
also nicht sowas, sondern also eher dieser Version hier,
wo ich dann eben hier Initialisierung habe,
schleife hier hinten an eben quasi den Vergleich.
Und wenn ich jetzt eben noch nicht meine Abbruchbedingungen habe,
entsprechend den Rücksprung in die nächste Iteration,
ansonsten habe ich hier das richtige Ergebnis mit Brennen
Und genau das ist dann eben auch das, was letztendlich
hier in dem Assembler-Code dann aus dem Compiler rausfallen würde.
Ja, also hier auch erst mal, also im Prinzip diese Initialisierung.
Nicht dann wird also im Prinzip ERX mit 1 initialisiert in EDX.
Das Argument X reingelesen. Dann habe ich hier das Label,
dann eben die entsprechenden Befehle, um Multiplikation zu machen.
Dann eben den Compare-Befehl und dann eben einen Jump-Greater.
Also sprich, ist das Ergebnis größer. Eins,
wenn ja, bin ich mit meiner Iteration noch nicht fertig.
Das heißt, dann springe ich wieder entsprechend auf dieses Label zurück.
Wenn nicht, habe ich in EAX dann eben ein Ergebnis und dann
kommt also hier quasi wieder dieser Code um diesen Unterprogrammaufruf quasi zu beenden.
Wenn ich das also die Art und Weise,
wie dann also eine Du, Wildschleife übersetzt wird,
beziehungsweise ganz allgemein, kann man das eben immer so sagen,
dass ich sage, ich habe du,
Wildbedingungen, und das wird eben quasi implementiert als Schleifenlabel,
hier die Anweisung, dann eben, wenn die Bedingungen,
war es entsprechender Rücksprung, wenn nicht, bin ich also hier,
mit dem richtigen Ergebnis fertig.
Gut, wenn ich jetzt eine Waldschleife andersrum habe,
das heißt, die Abfrage nicht am Ende,
sondern am Anfang der Schleife Also auch das kann man sich
dann in so einen Code mit entsprechenden Gotus übersetzt vorstellen.
Und zwar wird da der Code einfach ansprechend so umgesetzt,
dass man daraus erstmal so eine Dualschleife macht.
Das heißt also, dass ich sage,
wenn ich jetzt nicht sowieso diese Bedingungen oder andersrum,
wenn diese Bedingungen sowieso jetzt erstmal falls wird,
dann springe ich ganz ans Ende.
Und ansonsten habe ich also quasi jetzt hier wieder
diesen Teil mit du Schleifenkörper
War es dann eben auch entsprechend diesem vorherigen Schema dann wieder
eins zu eins umgesetzt wird. Nicht das heißt,
also eine Wild-Schleife mit Abfrage am Anfang der Schleife,
das heißt das, was man eigentlich so typischerweise als Wahlschleife hat,
würde dann letztendlich durch so ein Konstrukt vom Campala her
logisch umgesetzt werden und daraus wird dann eben der entsprechende Assembler-Code gemacht werden.
Gut, damit haben wir Schleifen,
Abfrage am Ende, Abfrage am Anfang,
jetzt fiel nur noch
die Vorschleife
Und auch das ist dann eigentlich nichts anderes,
als wir so eine mehr oder minder mehrstufige Umsetzung mit irgendwelchen Sprüngen,
dann in so eine, so einen unschönen Code NC,
der dann also mit irgendwelchen Gotus arbeitet. Also das heißt,
wenn ich also hier so eine Vorschleife habe,
würde das also letztendlich übersetzt werden jetzt,
also in so eine Wahlschleife,
wo ich dann also quasi die Initialisierung hier affe erstmal noch rausgezogen habe und eben die Aktualisierung,
das heißt also meinetwegen ist das irgendwie Umsetzung vom Laufindex dann eben entsprechend,
also hier an das Ende gepackt habe.
Nicht in das kann man dann eben auch wieder eins zu eins dann,
also in so eine Dual-Version und das dann eben wieder eins zu arzt,
so eine Go-To-Version umsetzen. So, das heißt,
damit können dann im Prinzip alle Arten von Verzweigung,
von Schleifenkonstrukten im Assembler-Code dann eben über solche Sprünge letztendlich realisiert werden.
Gut, abschließend, wie sieht das aus mit Mehrfachverzweigung?
Das heißt also, wenn ich jetzt also jetzt nicht If habe,
sondern also um jetzt ein Zeh sind, sie sind tags zu bleiben,
so ein Switch, also sprich so eine Case-Anweisung.
Man kann das natürlich also erstmal relativ stumpf jetzt,
also irgendwie in so eine If-Than-Ls-Folge abwandeln,
wo ich dann also im Prinzip jetzt immer irgendwie weiter geschachtelte Ifs habe,
ist natürlich, wenn ich jetzt also entsprechend viele Fälle habe,
ineffizient, weil ich dann also im Prinzip jedes Mal quasi meine Abfragen und meine Sprünge habe.
Das heißt, das lohnt sich dann eigentlich nur, wenn man entsprechend wenig Alternativen hat. Das,
was die Compiler dann eigentlich typischerweise machen, ist,
wenn ich jetzt also im Prinzip was Größeres habe,
ist das dann eine sogenannte Sprungtabelle angelegt wird,
wo dann im Prinzip diese einzelnen Abfragen komplett rausgenommen wird und
wo man dann also im Prinzip mit einer einzigen Testanweisung und dann nochmal so einer indirekten Referenzierung über diese Sprungtabelle sofort bei dem Sprungziel,
beziehungsweise bei dem Code, der dann quasi hier drin ist,
letztendlich landet.
Wie würde das Ganze funktionieren oder wie muss man sich das vorstellen?
Also hier als Beispiel, im Prinzip so eine Funktion heißt es hier Unpaar Simble.
Ich habe jetzt hier also im Prinzip einen Aufzählungstyp at mult-stiff-mod und Bad,
was dann also im Prinzip jetzt hier für irgendwelche Operationen irgendwie
eines Parsas stehen soll und die Funktion kriegt dann eben das
entsprechende Argument und sollen wir dann also quasi hier so einen entsprechenden Charakter dazu zurückliefern.
Gut, das ist jetzt also hier in C, als Switch-Anweisung geschrieben.
Wie würde das dann in so einer Sprungtabelle aussehen? Das,
was der Compiler daraus macht, ist,
dass er also aus dieser Verzweigung für die einzelnen Anweisungsteile,
also das, was hier diese Blöcke sind,
die entsprechend jeweils als Teile irgendwie übersetzt und ablegt.
Und dann wird eben noch eine Sprungtabelle benötigt. Das heißt,
entsprechend diesen Case-Bedingungen gehe ich jetzt, oder werden hier irgendwelche Indizes berechnet,
unter denen in dieser Tabelle dann eben die Startadressen dieser jeweiligen Codeblöcke hinterlegt sind.
Das ist eigentlich die Grundidee jetzt von so einer Sprungtabelle. Das heißt also,
grobe Idee ist halt eben,
dass sich dann also hier vom Compiler statisch diese Tabelle erzeugt wird und damit dann abhängig von diesem Wert,
der dann also letztendlich als Index hier dient, ich die direkt,
also im Ansprung Ziel habe und dann mit so einem Go-To dann eben hier in den entsprechenden Teil reingehen kann.
Nicht Vorteil von dem Ganzen ist eben auch für
beliebig große Mehrfachverzweigung, der Aufwand ist quasi linear groß.
Echt, das ist also das,
was dieses O von 1 Operationen andeutet
Oder er ist konstant groß, so rum. Gut,
wie würde das jetzt hier im Prinzip aussehen von der Idee her,
also angenommen, ich habe jetzt also hier so einen Aufzählungstyp,
das, was dort ja erstmal ein C letztendlich passiert,
ist, dass diese Aufzählungstyp dann eigentlich auch erstmal in irgendwelche aufgezählten Entager übersetzt wird,
also meinetwegen dann also jetzt für die sechs Fälle dann,
also von 0 bis 5 und Das, was dann dort passiert,
ist, dass dann eben im Prinzip hier einfach das Argument genommen wird,
mit so einer Compare-Anweisung, dann eben geguckt wird, welcher,
erstmal ist es überhaupt ein gültiger Wert, das heißt,
also hier gibt es so einen Compare-Operant mit fünf,
wenn das Ergebnis größer als fünf ist, das heißt,
wenn ich hier so einen ungültigen Wert habe,
dann soll eben direkt zum Ende gesprungen werden.
Nicht und ansonsten wird mit diesem Ausdruck dann quasi in diese Sprungtabelle rein referenziert,
wo dann quasi die entsprechenden Co-Teile hinterlegt sind.
Wenn ich also hier nochmal die Idee von dem Ganzen skizziert,
das hatte ich ja eben hier schon gesagt,
dass dann also hier die entsprechenden Label erzeugt werden und Adressen hinterlegt werden.
Das heißt also für ihn möglichen Fall wird ein Label erzeugt. Tabellen,
hier die Sprünge auf die den Inhalt von dem Register mit dem Offset 4,
damit gehe ich dann eben entsprechend jetzt also auf,
jeweils aufeinander folgende Tabellen zahlen,
mit dem Offset von dieser Basisadresse von der Tabelle
wird das Ganze dann eben entsprechend referenziert und rein praktisch würde das dann eben so aussehen,
dass man dort dann eben wirklich einmal in dieser Read-Only-Section quasi
diese Tabelle hinterlegt hat mit den Labeln und den entsprechenden oder diesen Labeln dann eben hier den eigentlichen Code und man dann eben über diese Sprünge dann mit null quasi hier rein adressiert,
damit dann also quasi, dass die Referenz hier zu dem Label 51 und damit eben an diesem Teil landet.
Das Ganze wird entsprechend ausgeführt, anschließend sprengt man dann eben wirklich zum Ende,
wo dann also quasi der Returnfall stattfindet,
welchen das passiert dann eben hier für die entsprechenden Teile.
Beim letzten wird dann also hier quasi auf dieses hier verzichtet,
weil dann bin ich dann sowieso hier bei dem Return,
das ist das,
was hier dieser Kommentar ist
Gut, hier nur noch kurz als Beispiel,
wie das dann also eigentlich aussieht, also in dem Objektcoach,
also auch da gibt es dann,
also solche Sprungtabellen ist dann etwas, was,
also der Compiler in dieser Read-Only-Data-Sektion von dem Code erzeugt und hinterlegt,
der hängt hier in einem disassassiblierten Teil,
sind dann eben die entsprechenden Adressen hinterlegt,
das Ganze ist dann Wasser hier unten eigentlich,
das man nur angedeutet ist,
also wenn man sich jetzt also wirklich die Mühe machen würde als Hacker,
also irgendwelchen Code von Hans zu disesemblieren und da also irgendwie was größer machen zu wollen,
um zu gucken, also wo liegt was im Speicher,
wo kann ich da irgendwo hin zurückspringen.
Das ist dann also auch erstmal nicht so ganz einfach zu lesen,
weil das Ganze dann eben immer mit umgekehrter Breitanordnung hier in diesen Tabellen mit drin ist,
das ist eigentlich nur das,
dass also hier miteinander so im Prinzip die Adresse gemeint ist,
aber letztendlich das,
was also hier diese Grafik eigentlich nur sagen soll,
ist,
dass dann also hier unter dieser Adresse Dann also im Prinzip sechs Werte lang,
dann also hier die jeweiligen Sprungziele aus diesem Beispiel mit drin sind.
Gut, das war jetzt eigentlich auch schon dieser erste Teil
zum Assembler, kurz zusammengefasst. Das heißt also,
welche einfachen Operationen gibt es, wie wir in Kontrollstrukturen umgesetzt.
Und nächste Woche werde ich dann eben noch was zum Unterprogrammaufruf
und zu dieser Handhabung von dem magischen Stack sagen.
Und damit soll es dann aber auch genug an Eigenheiten zu dem Assembler-Code sein.
Gut, damit dann mit schwer überzogen Schluss für heute,
schönes Wochenende