Tutorial 1: Die Grundlagen
Dieses Tutorial geht davon aus, dass der Leser mit MASM umzugehen weiß. Wenn Sie noch nicht mit MASM vertraut sind, laden Sie die Datei win32asm.exe herunter und arbeiten Sie die Texte in dem Package durch, bevor Sie mit diesem Tutorial weitermachen. Gut. Sie sind nun bereit. Auf geht's!
Theorie:
Win32-Programme laufen im Protected-Mode, welcher ab dem 80286 verfügbar ist. Aber der 80286 ist heute Geschichte. So müssen wir uns nur mit dem 80386 und seinen Nachfolgern beschäftigen. Windows lässt jedes Win32-Programm in einem eigenen virtuellen Adressraum laufen. Das bedeutet, dass jedes Win32-Programm seinen eigenen 4 GB großen Adressraum hat. Wie auch immer, das bedeutet nicht, dass jedem Win32-Programm 4 GB physikalischen Speicher zur Verfügung stehen, sondern nur, dass das Programm jede Adresse in diesem Bereich adressieren kann. Windows wird alles nötige unternehmen, damit die Speicherreferenzen Ihres Programmes gültig sind. Natürlich muss sich Ihr Programm an die Regel von Windows halten, ansonsten wird eine Allgemeine Schutzverletzung erzeugt. Jedes Programm ist alleine in seinem Adressraum. Das ist der Unterschied zu Win16. Alle Win16 Programme können sich *sehen*. Nicht so unter Win32. Dieses Merkmal hilft dabei, die Chance zu reduzieren, dass ein Programm den Code/die Daten eines anderen Programmes überschreibt.
Die Speichermodelle differieren ebenso dramatisch von der guten alten 16-Bit Welt. Unter Win32 müssen wir uns nicht mehr für Speichermodelle oder Segmente interessieren! Es gibt nur ein Speichermodell: das Flat-Speichermodell. Es gibt keine 64K Segmente mehr. Der Speicher ist ein großer, fortlaufender Bereich von 4 GB. Das bedeutet ebenfalls, dass Sie nicht mehr mit den Segmentregistern spielen müssen. Sie können jedes Segmentregister verwenden, um jegliche Adresse im Speicher zu adressieren. Das ist eine GROSSE Hilfe für Programmierer. Das ist es, was Win32-Assembler-Programmierung so einfach macht, wie die Programmierung mit C.
Wenn Sie unter Win32 programmieren, müssen Sie einige wichtige Regeln kennen. Eine dieser Regeln ist, dass Windows intern ESI, EDI, EBP und EBX benutzt und nicht erwartet, dass die Werte in diesen Register geändert werden. So merken Sie sich diese erste Regel: wenn Sie eins der vier Register in ihrer Callback-Funktion verwenden, vergessen Sie niemals, die ursprünglichen Werte wieder in die Register zu schreiben, bevor Sie die Kontrolle wieder an Windows übergeben. Eine Callback-Funktion ist eine Ihrer eigenen Funktionen, die von Windows aufgerufen wird. Das offensichtlichste Beispiel ist die Fenster-Prozedur. Das bedeutet nicht, dass Sie diese vier Register nicht benutzen können, Sie können. Seien Sie sich nur sicher, dass Sie sie wieder herstellen, bevor die Kontrolle wieder an Windows übergeben wird.
Inhalt:
Hier ist das Grundgerüst. Wenn Sie etwas von dem Code nicht verstehen, keine Panik. Ich werde später alles genau erklären.
.386
.MODEL Flat, STDCALL
.DATA
......
.DATA?
......
.CONST
......
.CODE
.....
end
Das ist alles! Lassen Sie uns nun das Grundgerüst analysieren.
.386
Das ist eine Assemblerdirektive, die dem Assembler mitteilt, dass der 80386-Befehlssatz benutzt werden soll. Sie können genauso .486, .586 benutzen, aber am sichersten ist es, bei .386 zu bleiben. Außerdem gibt es zwei fast identische Formen für das CPU-Modell. .386/.386p, .486/.486p. Diese "p"-Versionen sind nur nötig, wenn Ihr Programm privilegierte Befehle benutzt. Privilegierte Befehle sind für die CPU respektive für das Betriebssystem reservierte Befehle, wenn man sich im Protected-Mode befindet. Sie können nur von privilegierten Code verwendet werden, wie von "Virtual Device Drivers". Die meiste Zeit wird ihr Programm im nicht privilegierten Modus arbeiten, so dass es besser ist , nicht die "p"-Versionen zu benutzen.
.MODEL FLAT, STDCALL
.MODEL ist eine Assemblerdirektive die das Speichermodell Ihres Programms spezifiziert. Unter Win32 gibt es nur ein Modell, das FLAT Modell.
STDCALL teilt MASM mit, wie die Parameter übergeben werden, ob von links-nach-rechts oder von rechts-nach-links und ebenso, wer den Stackrahmen nach einem Funktionsaufruf wieder anpaßt.
Unter Win16 gibt es zwei Arten von Aufruf-Konventionen, C und PASCAL
Die C Aufruf-Konvention übergibt die Parameter von rechts nach links, das heißt, das der letzte Parameter als erstes gepusht wird. Der Aufrufer ist verantwortlich für das anpassen des Stackrahmens nach dem Aufruf. Die Funktion foo(int erster_param, int zweiter_param, int dritter_param) zum Beispiel wird in der C Aufruf-Konvention so als Asm Code aufgerufen:
push [dritter_param] ; den dritten Parameter pushen
push [zweiter_param] ; gefolgt vom zweiten
push [erster_param] ; und dem ersten
call foo
add sp, 12 ; Der Aufrufer passt den Stackrahmen an
Die PASCAL Aufruf-Konvention ist das Gegenteil zur C Aufruf-Konvention. Diese übergibt die Parameter von links nach rechts und der Aufgerufene ist für das anpassen des Stackrahmens nach dem Aufruf verantwortlich.
Win16 unterstützt die PASCAL Aufruf-Konvention weil sie kleineren Code erzeugt. Die C Konvention ist dann nützlich, wenn Sie nicht wissen, wie viele Parameter der Funktion übergeben werden, wie zum Beispiel im Falle von wsprintf(). Beim Beispiel wsprintf() hat die Funktion keine Möglichkeit vorher zu bestimmen, wie viele Parameter auf den Stack gepusht werden, so dass sie den Stackrahmen nach dem Aufruf auch nicht angleichen kann.
STDCALL ist ein Hybride aus C und PASCAL. Die Parameter werden von rechts nach links übergeben, aber der Aufgerufene ist für die Anpassung des Stackrahmens nach dem Aufruf verantwortlich. Die Win32 Plattform benutzt ausschließlich STDCALL. Außer in einem Fall: wsprintf(). Sie müssen für wsprintf() die C Aufruf-Konvention benutzen.
.DATA
.DATA?
.CONST
.CODE
Alle vier Direktiven sind sogenannte Sektionen. Es gibt ja keine Segment mehr unter Win32, erinnern Sie sich? Aber Sie können ihren kompletten Adressraum in vier logische Sektionen unterteilen. Der Anfang einer Sektion kennzeichnet gleichzeitig das Ende der vorangegangenen Sektion. Es gibt zwei Gruppen von Sektionen: Daten und Code. Daten-Sektionen sind in 3 Kategorien unterteilt:
-
.DATA Diese Sektion enthält die initialisierten Daten ihres Programms.
-
.DATA? Diese Sektion enthält die nicht initialisierten Daten ihres Programms. Manchmal möchten Sie einfach nur schon einmal Speicherplatz reservieren, diesen aber noch nicht initialisieren. Diese Sektion ist hierfür gedacht. Der Vorteil von nicht initialisierten Daten ist folgender: sie benötigen keinen Speicherplatz in der ausführbaren Datei. Wenn Sie zum Beispiel 10.000 Bytes in ihrer .DATA? Sektion alloziieren, wird ihre ausführbare Datei nicht um 10.000 Bytes aufgebläht. Sie behält ihre Grösse. Man teilt dem Assembler nur mit, wieviel Platz Sie brauchen, wenn Ihr Programm in den Speicher geladen wird. Das ist alles.
-
.CONST Diese Sektion enthält Deklarationen von Konstanten, die von ihrem Programm benutzt werden. Konstanten in dieser Sektion können niemals in ihrem Programm verändert werden. Sie sind halt einfach *kontant*.
Sie müssen nicht alle drei Sektionen in ihrem Programm benutzen. Deklarieren sie nur die Sektion(en) die Sie benutzen wollen.
Es gibt nur eine Sektion für Code: .CODE. In diese Sektion kommt ihr Code hin.
<label>
end <label>
wobei <label> irgend ein willkürlich gewähltes Label sein kann, welches ihren Code umschließt. Beide Labels müssen identisch sein. Ihr gesamter Code muss sich zwischen <label> und end <label> befinden.
Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage