DASM32 ist ein 32Bit-Makroassembler, der aus Assemblerquelltexten Objektmodule zur Weitergabe an den Linker erzeugt. Er besitzt folgende Eigenschaften :
Zur Steuerung der Arbeitsweise erwartet DASM32 bestimmte Optionen, die
nachfolgend beschrieben werden.
4.1 Optionen
Der Aufruf von DASM32 geschieht auf folgende Weise :
Um Ihnen die Arbeitsweise von DASM32 zu verdeutlichen, wollen wir ein kleines Beispielprogramm betrachten:
.586p .model flat extrn dosopen:near extrn dosclose:near extrn xmalloc:near extrn memfree:near extrn dosputstr:near extrn crlf:near extrn _decstrl:near extrn _cpu:dword public start .code start: mov eax,offset meldung call near ptr dosputstr push dword ptr _cpu call _decstrl add esp,4 call dosputstr call crlf mov eax,100000h call xmalloc mov memzeiger,eax mov edi,eax add edi,100000h label1: mov byte ptr [eax],0 inc eax cmp eax,edi jb short label1 mov byte ptr -100[edi],10 mov eax,memzeiger call memfree retf .data meldung db 'Installierter CPU-Typ : ',0 .data? memzeiger dd ? end startProzessor-Direktiven :
Die erste Anweisung im Programm ist die Prozessor-Direktive .586p. Diese teilt dem Assembler mit, daß alle Prozessor-Befehle bis einschließlich Pentium zur Verfügung stehen sollen. Diese Direktiven dienen nur der Kompatibilität zu anderen Assemblern, denn DASM32 ignoriert grundsätzlich jede Prozessor-Direktive, d.h. es stehen zu jeder Zeit alle Prozessor-Befehle zur Verfügung. Es liegt in der Verantwortung des Programmierers dafür zu sorgen, daß Programme, die z.B. Pentium Pro- oder MMX-Befehle nutzen, auch nur unter den entsprechenden CPUs ausgeführt werden dürfen (das gleiche gilt für Coprozessor-Befehle), weshalb eine solche Prüfung durch den Assembler irrelevant ist.
Speichermodell :
Die Anweisung .model flat in der zweiten Zeile teilt dem Assembler mit, daß das Programm im Flat-Speichermodell ablaufen wird. Da DASM32 ausschließlich Code für dieses Speichermodell erzeugt, wird auch die .model-Direktive ignoriert (sie dient ebenfalls nur für Kompatibilitätszwecke). DASM32 impliziert daher auch folgende Anweisungen :
DGROUP group _DATA, _BSS assume cs:_TEXT, ds:DGROUP, es:DGROUP, ss:DGROUP_TEXT steht für das 32Bit-Codesegment, _DATA für das initialisierte 32Bit-Datensegment und _BSS für das uninitialisierte 32Bit-Datensegment. Sollten im Quelltext andere ASSUME- und GROUP-Direktiven auftreten, so werden diese ignoriert.
Externe Symbole :
Die Zeilen 4 bis 11 deklarieren die erwähnten Symbole mit der EXTRN-Direktive
als externe Referenzen, d.h. diese Symbole wurden in anderen Modulen definiert
und dort mit der PUBLIC-Direktive veröffentlicht.
Veröffentlichte Symbole :
In Zeile 13 wird das Symbol "start" durch die PUBLIC-Direktive anderen Modulen zur Verfügung gestellt, die ihrerseits über eine EXTRN-Direktive auf dieses Symbol zugreifen können.
Codesegment :
Die Anweisung .code in Zeile 15 ist das Kennzeichen für den Assembler, daß der folgende Programmtext für das Codesegment bestimmt ist. Diese Anweisung ist eine Alternative für diese Anweisung :
_TEXT SEGMENTIn der ersten Zeile des Codesegments wird das Symbol "start" definiert. Generell können Symbole auf fünf Arten definiert werden :
1. Label: [ Befehl ]
label1: mov byte ptr [eax],0 jmp label1
mov eax,label1
ACHTUNG : Durch das Flat-Speichermodell sind alle Label grundsätzlich 32Bit-Direktwerte. Die Anweisung
mov ax,label1
dwordvar label dword bytevar1 db 0,0,0,0 ;ansprechbar als bytevar oder dwordvar tbytevar label tbyte bytevar2 db 0,0,0,0,0,0,0,0,0,0 ;ansprechbar als bytevar oder tbytevar
Hier ein Beispiel :
WERT = 100*2+3 ;WERT=203 mov eax,WERT*2 ;EAX = 406 WERT = 400+1 ;WERT=401 (Redefinition von WERT) mov edx,WERT/2 ;EDX=200
mov eax,offset meldung
Das Register EAX bekommt die Adresse der Variablen meldung als
Direktwert zugewiesen.
call near ptr dosputstr
Die Prozedur dosputstr wird angesprungen. Durch die near
ptr-Angabe wird dosputstr in ein Near-Label konvertiert, was
in diesem Fall aber überflüssig ist, da dies bereits vorher
durch die EXTRN-Direktive festgelegt wurde. Die near ptr-Angabe
ist bei DASM32 generell überflüssig, da es im Flat-Modell keine
Far-Label bzw. Far-Prozeduren gibt.
push dword ptr _cpu
Der Inhalt der Variablen _cpu wird als DWORD auf den Stack geschoben.
Da _cpu in einer vorherigen EXTRN-Direktive bereits als DWORD-Typ
deklariert wurde, kann die dword ptr-Angabe in diesem Fall auch
weggelassen werden.
call _decstrl
Die Near-Prozedur _decstrl wird angesprungen, da _decstrl
durch eine vorherige EXTRN-Direktive als Near-Label deklariert wurde.
add esp,4
Der Inhalt des Registers ESP wird um 4 erhöht.
call @dosputstr
Die Near-Prozedur dosputstr wird angesprungen, da dosputstr
durch eine vorherige EXTRN-Direktive als Near-Label deklariert wurde.
call @crlf
Die Near-Prozedur crlf wird angesprungen, da crlf durch
eine vorherige EXTRN-Direktive als Near-Label deklariert wurde.
mov eax,100000h
Das Register EAX bekommt den Wert 100000h als hexadezimalen Direktwert
zugewiesen.
call xmalloc
Die Near-Prozedur xmalloc wird angesprungen, da xmalloc
durch eine vorherige EXTRN-Direktive als Near-Label deklariert wurde.
mov memzeiger,eax
Der Inhalt des Registers EAX wird in die DWORD-Variable memzeiger
kopiert. Diese Variable wird weiter unten im _BSS-Segment als DWORD definiert.
mov edi,eax
Der Inhalt des Registers EAX wird in EDI kopiert.
add edi,100000h
Der Inhalt des Registers EDI wird um den hexadezimalen Direktwert 100000h
erhöht.
label1: mov byte ptr [eax],0
Das Label label1 wird definiert und markiert von nun an die
nachfolgende Anweisung als mögliches Sprungziel. Die Anweisung kopiert
den Direktwert 0 in das Byte im Speicher, dessen Adresse im Register EAX
vermerkt ist.
inc eax
Der Inhalt des Registers EAX wird um 1 erhöht.
cmp eax,edi
Der Inhalt der Register EAX und EDI wird verglichen.
jb short label1
Falls EAX < EDI (jump-if-below) wird zu label1 gesprungen.
Durch den short-Zusatz wird ein 8Bit-Offset erzwungen, d.h. das Sprungziel
muß zwischen -128 und +127 Bytes von dieser Anweisung entfernt sein.
mov byte ptr -100[edi],10
Das Speicherbyte, dessen Adresse sich aus dem Inhalt des Registers
EDI - 100 ergibt, bekommt den Dezimalwert 10 zugewiesen.
mov eax,memzeiger
Der Inhalt der DWORD-Variable memzeiger wird in das Register
EAX kopiert.
call memfree
Die Near-Prozedur memfree wird angesprungen, da memfree
durch eine vorherige EXTRN-Direktive als Near-Label deklariert wurde.
retf
Es wird ein Far-Return ausgeführt, wodurch CS und EIP vom Stack
zurückgeholt werden.
Datensegment (initialisiert) :
Die Anweisung .data in Zeile 43 ist das Kennzeichen für den Assembler, daß der folgende Programmtext für das initialisierte Datensegment bestimmt ist. Diese Anweisung ist eine Alternative für die Anweisung
_DATA SEGMENTIn der Zeile 44 steht die einzige Definition für das _DATA-Segment :
meldung db 'Installierter CPU-Typ : ',0
Hier wird die Byte-Variable meldung definiert, die den nullterminierten
String "Installierter CPU-Typ : " adressiert.
Datensegment (uninitialisiert) :
Die Anweisung .data? in Zeile 46 ist das Kennzeichen für den Assembler, daß der folgende Programmtext für das uninitialisierte Datensegment bestimmt ist. Diese Anweisung ist eine Alternative für :
_BSS SEGMENTIn der Zeile 47 steht die einzige Definition für das _BSS-Segment :
memzeiger dd ?
Hier wird die Dword-Variable memzeiger mit unbestimmtem Inhalt
definiert.
Quelltextende
Die END-Direktive in Zeile 49 zeigt dem Assembler das Ende des
Quelltextes an. Alle nachfolgenden Zeilen im Quelltext werden ignoriert.
Die Angabe des Labels "start" hinter der END-Direktive definiert
dieses Label als Einstiegspunkt für die Programmausführung.
4.3 Ausdrücke und Symbole
4.3.1 Konstanten
Ausdrücke und Symbole sind grundlegende Elemente eines Assembler-Programms.
Ausdrücke werden zur Berechnung von Werten und Speicheradressen benutzt,
während Symbole verschiedenartige Werte repräsentieren können.
Konstanten sind Zahlen oder Strings, die als konstante Werte interpretiert werden.
Radix | erlaubte Zeichen |
Binär | 0 1 |
Oktal | 0 1 2 3 4 5 6 7 |
Dezimal | 0 1 2 3 4 5 6 7 8 9 |
Hexadezimal | 0 1 2 3 4 5 6 7 8 9 A B C D E F (bzw. a b c d e f) |
Für hexadezimale Konstanten können sowohl Groß- als
auch Kleinbuchstaben verwendet werden. Der Radix wird immer durch Auswertung
des letzten Zeichens einer Konstante bestimmt :
Zeichen | Radix |
B oder b | Binär |
O oder o | Oktal |
D oder d | Dezimal |
H oder h | Hexadezimal |
10 | = 10 dezimal |
0ffh | = FF hexadezimal |
ach | = ungültige numerische Konstante : erstes Zeichen ist keine Ziffer ! |
1273d | = 1273 dezimal |
37242O | = 37242 oktal |
4.3.2 Symbole
Jedem Symbol ist ein Typ zugeordnet, der Auskunft über die Eigenschaften und gespeicherten Informationen gibt. Der Typ wird durch die Art der Definition eines Symbols festgelegt.
Folgende Typen sind erlaubt :
Typ | Bedeutung |
Num | Symbol steht für einen numerischen Wert |
Makro | Symbol steht für ein definiertes Makro |
Label | Symbol steht für eine Adresse im Programmtext |
Byte | Symbol adressiert ein Byte |
Word | Symbol adressiert ein Word (2 Bytes) |
Dword | Symbol adressiert ein Dword (4 Bytes) |
Fword | Symbol adressiert ein Fword (6 Bytes) |
Qword | Symbol adressiert ein Qword (8 Bytes) |
Tbyte | Symbol adressiert ein Tbyte (10 Bytes) |
Xmmword | Symbol adressiert ein XmmWord (16 Bytes) |
Es gibt zwei vordefinierte Symbole : "$" und "?". Das "$"-Symbol repräsentiert
den aktuellen Programmzähler im aktuellen Segment des Programmoduls,
während das "?"-Symbol ein Synonym für den Wert 0 (Null) darstellt.
4.3.3 Ausdrücke
Ausdrücke werden vom Assembler zum Zeitpunkt der Assemblierung ausgewertet und können die Lesbarkeit und Modularisierung des Codes erhöhen, indem z.B. bestimmte Werte durch Symbole dargestellt werden. Ausdrücke werden grundsätzlich auf 32Bit-Integer-Basis ausgewertet. In jedem Ausdruck können Konstanten benutzt werden :
test ecx,16*2 ;'16' und '2' sind dezimale KonstantenAusdrücke können sich auch aus Symbolen und Konstanten zusammensetzen :
.data tabelle db 1024 .code MASKE = 511 test byte ptr tabelle[eax+6],MASKE AND 127
Operator | Bedeutung |
OFFSET <Symbol> | Adresse einer Speichervariable bestimmen |
( <Ausdruck> ) | Klammerung von Ausdrücken |
+ <Ausdruck> | Ausdruck ist positiv |
- <Ausdruck> | Negation des Ausdrucks |
<Ausdruck1> * <Ausdruck2> | Multiplikation |
<Ausdruck1> / <Ausdruck2> | Division |
<Ausdruck1> MOD <Ausdruck2> | Divisionsrest |
<Ausdruck1> SHL <Ausdruck2> | Links-Shift |
<Ausdruck1> SHR <Ausdruck2> | Rechts-Shift |
<Ausdruck1> + <Ausdruck2> | Addition |
<Ausdruck1> - <Ausdruck2> | Subtraktion |
NOT <Ausdruck1> | bitweises NOT |
<Ausdruck1> AND <Ausdruck2> | bitweise UND-Verknüpfung |
<Ausdruck1> OR <Ausdruck2> | bitweise ODER-Verknüpfung |
<Ausdruck1> XOR <Ausdruck2> | bitweise EXKLUSIV-ODER-Verknüpfung |
Operator | Bedeutung |
<Typ> PTR <Ausdruck> | Der Ausdruck wird in den angegebenen Typ konvertiert |
[ <Ausdruck> ] | Der Ausdruck wird als Speicheroperand betrachtet |
Hier ein paar Beispiele dazu :
mov eax,dword ptr [ebx] ;Dword an Adresse EBX wird in EAX kopiert mov eax,[ebx] ;Dword an Adresse EBX wird in EAX kopiert mov eax,[_dwordvar] ;Inhalt von _dwordvar wird in EAX kopiert lea edi,5[ecx] ;EDI bekommt den Inhalt von ECX+5 zugewiesen push word ptr _bytevar[3];_bytevar+3 wird auf den Stack geschobenIn den Beispielen wurde gezeigt, daß die Benutzung von [ ] in Ausdrücken mit Speicheroperanden eine implizite Addition zur Folge hat. Der [ ]-Operator ist überflüssig bei Ausdrücken, die bereits als Speicheroperanden interpretiert werden aber unerläßlich bei der Benutzung von Registern als Bestandteile von Speicheroperanden. Der [ ]-Operator ist ebenfalls überflüssig bei der Konvertierung von Ausdrücken, die bereits den gewünschten Typ besitzen. Hierzu einige Beispiele :
.data _dwordvar dd 12345678h .code mov eax,[_dwordvar] ;[] überflüssig, da bereits Speichervariable mov eax,dword ptr _dwordvar ;Dword-Konvertierung ist überflüssig mov eax,dword ptr [_dwordvar] ;Dword-Konvertierung und [] sind überflüssig mov eax,_dwordvar ;hat gleiche Wirkung wie alle drei Zeilen zuvor mov ax,word ptr _dwordvar+2 ;AX = 1234h mov al,byte ptr _dwordvar ;AL = 78h mov ebx,offset _dwordvar+1 mov ax,[ebx] ;AX=3456h
Die folgende Tabelle zeigt die Reihenfolge bei der Operator-Auswertung.
Die Priorität der Operatoren nimmt dabei mit jeder Zeile ab.
Priorität | Operatoren |
PTR | |
OFFSET | |
( ), [ ] | |
*, /, MOD, SHL, SHR | |
+, - | |
NOT | |
AND | |
OR, XOR |
DASM32 ist ein reiner 32Bit-Assembler, was bedeutet, daß er ausschließlich
32Bit-Code erzeugt, der mit 32Bit-Linkern weiterverarbeitet werden kann.
Grundlage dessen ist das Flat-Speichermodell, bei dem alle Segmente eine
Größe von 4G besitzen. Da hierdurch praktisch keine Segmentierung
mehr notwendig ist (denn alle Adressen können mit einem "Near-Pointer"
erreicht werden) werden für Programme auch nur zwei Segmente benötigt :
EIN Code- und EIN Datensegment. Das Codesegment beinhaltet alle ausführbaren
Anweisungen des Programms und das Datensegment alle Daten, auf die das
Programm zugreift. Diesem Prinzip trägt auch DASM32 Rechnung, indem
es alle Anweisungen und alle Daten in eines dieser beiden Segmente schreibt.
Folgende Segmentbezeichnungen werden dazu eingesetzt :
Segmentname | Bedeutung |
_TEXT | Codesegment |
_DATA | Datensegment mit initialisierten Daten |
_BSS | Datensegment mit uninitialisierten Daten |
DGROUP | gesamtes Datensegment bestehend aus _DATA und _BSS |
Auffällig ist, daß das Datensegment (DGROUP) aus zwei Bestandteilen zusammengesetzt wird : _DATA und _BSS. Das _DATA-Segment nimmt alle Daten auf, die einen festen bzw. vordefinierten Wert haben müssen, z.B. Tabellen mit definiertem Inhalt, Zeichenketten etc. Alle Daten hingegen, die im _BSS-Segment abgelegt werden, benötigen keine explizite Initialisierung. Hier ein Beispiel dazu :
.code mov eax,offset filename xor edx,edx call @dosopen mov handle, eax ... .data filename db 'TEST.TXT',0 .data? handle dd ?Die Variable filename muß im _DATA-Segment stehen, da sie einen konstanten Text adressiert (in diesem Fall ein Dateiname). Die Variable handle dagegen kann im _BSS-Segment stehen, da sie keinen vordefinierten Inhalt besitzen muß, denn sie wird vom Programm selbst gefüllt. Alle Daten im _BSS besitzen in Wirklichkeit keinen undefinierten Inhalt, sondern das komplette _BSS-Segment wird beim Programmstart mit Null initialisiert, d.h. die Variable handle besitzt beim Programmstart automatisch den Inhalt 0 (als Dword).
Die Benennung der Segmente mit _TEXT, _DATA, _BSS und DGROUP ist kompatibel
mit TASM und MASM, d.h. Programme sind in Bezug auf die Segmentierung leicht
auf einen anderen Assembler zu übertragen.
4.4.1 _TEXT-Segment
Das _TEXT-Segment dient zur Aufnahme aller Anweisungen eines Programms. Dem Assembler kann die Verwendung des _TEXT-Segments auf zwei Weisen angezeigt werden :
_TEXT SEGMENToder
.codeDie allgemeine Syntax für die Markierung eines Segmentstarts ist diese :
Ausrichtung | Ausrichtung auf... |
byte | Bytegrenze (=> keine Ausrichtung) |
word | Wortgrenze (gerade Adresse) |
dword | Doppelwortgrenze (ganzzahlig durch 4 teilbare Adresse) |
para | Paragraphengrenze (ganzzahlig durch 16 teilbare Adresse) |
page | Seitengrenze (ganzzahlig durch 4096 teilbare Adresse) |
Wird die Ausrichtung weggelassen, wird automatisch DWORD benutzt.
Die Kombination gibt dem Linker an, wie er einzelne Segmente dieses Namens kombinieren soll. Zur Zeit wird nur die Angabe PUBLIC unterstützt. Die Größe zeigt die Segmentgröße an. Im Flat-Speichermodell ist dies immer USE32 (= 32Bit-Segment). Die Klasse zeigt dem Linker an, welche Segmente er aus einzelnen Modulen im Speicher gruppieren muß. Auf diese Weise lassen sich das Code-, das initialisierte und das uninitialisierte Datensegment zu einem lauffähigen Programm zusammenfassen. Diese Angaben sind erlaubt :
'CODE' : Klasse für das Codesegment (_TEXT)
'DATA' : Klasse für das initialisierte Datensegment (_DATA)
'BSS' : Klasse für das uninitialisierte Datensegment (_BSS)
Innerhalb eines Quelltextmoduls können die verschiedenen Segmente aus beliebig vielen Teilen bestehen, sie werden vom Assembler automatisch aneinander gehängt. Das Attribut Ausrichtung eines Segments muß nur bei der ersten Verwendung eines Segments in einem Quellmodul angegeben werden, alle späteren Verwendungen dieses Segments benutzen dann dieselbe Ausrichtung. Die Assemblierung von _TEXT-Anweisungen geschieht solange, bis die ENDS-Direktive auftritt, ein anderes Segment begonnen wird oder der Quelltext endet. Die ENDS-Direktive besitzt diese Syntax :
4.4.2 _DATA-Segment
Das _DATA-Segment dient zur Aufnahme aller initialisierten Daten eines Programms. Die Assemblierung von Prozessorbefehlen ins _DATA-Segment ist nicht möglich. Dem Assembler kann die Verwendung des _DATA-Segment wieder auf zwei Arten angezeigt werden :
_DATA SEGMENToder
.dataDas Segment endet ebenfalls entweder beim Antreffen der ENDS-Direktive, dem Start eines anderen Segments oder dem Ende des Quelltextes.
4.4.3 _BSS-Segment
Das _BSS-Segment dient zur Aufnahme aller Daten, die keinen vordefinierten Inhalt besitzen müssen (oder mit Null initialisiert sein sollen). Auch hier können keine Anweisungen ins Segment assembliert werden.
_BSS SEGMENToder
.data?Das Segment endet auf die gleiche Weise wie das _DATA-Segment.
4.4.4 Stacksegment
Das Anlegen eines Stacks für DiceRTE-Programme ist nicht Aufgabe
des Assemblers, sondern des Linkers. Dies hängt mit der Verwendung
des Portable-Executable-Formats für ausführbare Programme zusammen.
4.4.5 ASSUME-Direktive
Die ASSUME-Direktive ist im Flat-Speichermodell nicht nötig, da es nur zwei Segmente gibt. Genauer gesagt gilt grundsätzlich immer diese ASSUME-Direktive :
assume cs:_TEXT, ds:DGROUP, es:DGROUP, ss:DGROUPDiese Zuweisungen können nicht verändert werden, d.h. jede anderslautende ASSUME-Direktive im Quelltext wird ignoriert.
4.4.6 GROUP-Direktive
Im Flat-Speichermodell wird grundsätzlich nur eine Gruppe definiert :
DGROUP. Diese besteht aus den beiden Segmenten _DATA und _BSS. Jede anderslautende
Gruppendefinition wird ignoriert.
4.4.7 ALIGN-Direktive
Die ALIGN-Direktive wird benutzt, um den folgenden Prozessorbefehl, die folgende Datendefinition oder das folgende Symbol auf eine Adresse zu justieren, die ein Vielfaches von 2 darstellt. Die Syntax lautet wie folgt :
ALIGN 16 ;Justierung auf Paragraph (nächste 16 Byte-Grenze) ALIGN 32 ;Justierung auf nächste 32 Byte-Grenze ALIGN 256 ;Justierung auf nächste 256 Byte-GrenzeBefindet sich der aktuelle Programmzähler bereits auf einer entsprechend justierten Grenze, wird die ALIGN-Direktive ignoriert.
4.4.8 EVEN-Direktive
Durch EVEN wird der aktuelle Programmzähler auf die nächste gerade Adresse ausgerichtet. Es tritt damit der gleiche Effekt wie bei
ALIGN 2ein.
4.4.9 EVENDATA-Direktive
Durch EVENDATA wird der Programmzähler des aktuellen Datensegments auf die nächste gerade Adresse ausgerichtet. Es tritt der gleiche Effekt wie bei Verwendung von
ALIGN 2in einem der beiden Datensegmente ein.
4.4.10 Segmentreihenfolge
Die Segmentreihenfolge im Quelltext gibt die Reihenfolge vor, in der
der Linker die einzelnen Segmente im Programm aneinanderreiht. DASM32 sortiert
die Segmentreihenfolge automatisch so, daß _TEXT immer vor _DATA
und _BSS im Objektmodul erscheint.
4.5 Datendefinition
Datendefinitions-Direktiven werden benutzt, um Speicher in Segmenten zu belegen, wobei zwischen initialisierten und uninitialisierten Daten unterschieden wird. Initialisierte Daten werden bei der Definition mit einem bestimmten Inhalt definiert, während unitialisierte Daten mit "?" definiert werden sollten. Mit Hilfe der DUP-Direktive können außerdem ganze Blöcke mit Daten belegt werden. Die allgemeine Syntax für Datendefinitionen sieht so aus :
bytevar db 5 dup (1,2,3) wordvar dw 3 dup (?)
bytevar db 1,2,3,1,2,3,1,2,3,1,2,3,1,2,3 wordvar dw ?,?,?
Direktive | Bedeutung |
DB | 1 Byte Datenfeld belegen |
DW | 2 Byte Datenfeld (Word) belegen |
DD | 4 Byte Datenfeld (Dword) belegen |
DQ | 8 Byte Datenfeld (Qword) belegen |
DT | 10 Byte Datenfeld (Tbyte) belegen |
Für jede Direktive sind unterschiedliche Definitionen erlaubt :
DB (Byte)
1.0 -3.2 2.0E-4 0.001Fließkommazahlen werden grundsätzlich am Dezimalpunkt in der ersten Zahl erkannt.
4.6 Makros
Mit Makros bekommt man die Möglichkeit, häufig benutzte Zeichenketten
oder Codefolgen mit einem symbolischen Namen anzusprechen, um so die Tipparbeit
zu verringern.
4.6.1 Einfache Textmakros
Ein einfaches Textmakro ist ein Symbol, das für eine feste Zeichenkette steht. Bei der Assemblierung wird jedes Auftreten dieses Symbols im Quelltext durch die zugewiesene Zeichenkette ersetzt. Hier ein Beispiel :
Zeichenkette equ "Dies ist ein String" Meldung db ZeichenketteDiese Definition ist identisch mit :
Meldung db "Dies ist ein String"Durch die oben benutzte EQU-Direktive werden einfache Textmakros definiert :
Zeichenkette equ <"Dies ist eine Botschaft !">4.6.2 Mehrzeilige Makros
Mehrzeilige Makros bezeichnen (wie der Name schon sagt) Makros, die über mehrere Zeilen verteilt sind. Diesen Makros können Argumente zugewiesen werden, die beim Aufruf durch übergebene Parameter ersetzt werden. DASM32 setzt diese Makros an jeder Stelle im Quelltext ein, wo der Makroname benutzt wird :
Ausgabe MACRO push eax mov eax,offset Meldung call @dosputstr pop eax ENDMDie (optionale) Parameterliste ist eine Liste von Symbolen, die als Argumente für das Makro benutzt werden können. Die einzelnen Argumente werden durch Kommata voneinander getrennt. Hier ein Beispiel :
Fuellen MACRO a1, a2 mov eax,a1 mov edx,a2 ENDMDie Argumente a1 und a2 sind Platzhalter für echte Parameter, die beim Aufruf des Makros übergeben und innerhalb des Makros ersetzt werden :
Fuellen 1, 2erzeugt diese Befehlsfolgen :
mov eax,1 mov edx,2Die einzelnen Parameter für den Aufruf des Makros werden durch Kommata oder Leerzeichen voneinander getrennt. Um bei einem Makroaufruf andere (einfache) Makros als Parameter zu übergeben, wird der %-Operator benutzt :
msg equ <"Dies ist ein Text"> MSGOUT %mDem Makro MSGOUT wird hier der String "Dies ist ein Text" übergeben und nicht das Zeichen 'm'. Sollen einem Makro konstante Zeichenketten mit Leerzeichen oder Kommata übergeben werden, so müssen diese in spitze Klammern eingeschlossen werden :
MSGOUT <"Dies ist ein Text">Innerhalb eines Makros können auch weitere Makros aufgerufen werden :
Addieren MACRO wert1,wert2 mov eax,wert1 add eax,wert2 ENDM Berechnen MACRO a1,a2,a3 Addieren a1,a2 sub eax,a3 ENDM Berechnen 10*3, 4+5, (5-3)*2erzeugt diese Befehle :
mov eax,10*3 add eax,4+5 sub eax,(5-3)*2Der '&'-Operator hat innerhalb eines Makrotextes eine spezielle Bedeutung : er dient zur Kennzeichnung eines Makroparameters, der von DASM32 normalerweise nicht gefunden wird (z.B. weil er Bestandteil eines Wortes ist). Hier ein Beispiel :
DEFINE MACRO sym, def, wert @&sym&1 label byte _&sym def wert ENDMDer Aufruf
DEFINE nichts db "Dies ist ein Test !"erzeugt diese Befehlsfolge :
@nichts1 label byte _nichts db "Dies ist ein Test !"Durch den &-Operator "sieht" DASM32 das Argument sym und kann es somit durch den übergebenen Parameter ersetzen.
4.6.3 PURGE-Direktive
Die PURGE-Direktive löscht Makrodefinitionen. Die gelöschten Makros sind danach nicht mehr ansprechbar :
4.7 Bedingte Assemblierung
Mit Hilfe der bedingten Assemblierung läßt sich die Assemblierung von bestimmten Befehlssequenzen im Quelltext steuern :
Makroname : | Name eines einfachen Textmakros |
Operator : | einer der Operatoren =, <, >, <=, >=, EQ(gleich), GT(größer), LT(kleiner), NE(ungleich), GE(größer/gleich), LE(kleiner/gleich) |
Zeichenkette : | Die Zeichenkette, die mit dem Makro verglichen werden soll |
Hier ein Beispiel :
IFDEF DEBUG OUTPUT <"Zeile 1000"> ENDIFDie Anweisung OUTPUT <"Zeile 1000"> wird hier nur assembliert, wenn vorher das Makro DEBUG definiert wurde.
IF STUFE_1 > 1 TU_DIES IF STUFE_2 <= 2 TU_DAS ELSE TU_IRGENDWAS ELSE IF STUFE_2 > 2 ODER_TU_NICHTS ENDIF ENDIFBei den Vergleichsausdrücken wird immer ein Zeichenvergleich durchgeführt, d.h. das Makro wird zeichenweise mit der Zeichenkette verglichen (es findet keine Umrechnung in numerische Werte statt !).
4.8 Linkerschnittstellen
In der Regel bestehen komplette Programme aus mehreren Modulen, die
alle in mehr oder weniger komplexer Weise miteinander verbunden sind. Compiler
bzw. Assembler behandeln diese Module getrennt voneinander, während
der Linker diese Module zu einem ausführbaren Programm zusammenfügt.
Damit dies funktioniert, müssen die einzelnen Module dem Linker mitteilen,
welche Daten und Funktionen sie aus anderen Modulen benötigen und
welche eigenen Daten und Funktionen sie anderen Modulen zur Verfügung
stellen. Dazu gibt es zwei Direktiven : EXTRN und PUBLIC.
4.8.1 EXTRN-Direktive
Die EXTRN-Direktive teilt Assembler und Linker mit, daß bestimmte Symbole in anderen Modulen definiert sind und in diesem Modul benutzt werden. Die Syntax lautet wie folgt :
Typ | Bedeutung |
near | Label (z.B. Einstiegspunkt einer Prozedur) |
byte | Variable vom Typ Byte (8 Bit) |
word | Variable vom Typ Word (16 Bit) |
dword | Variable vom Typ Dword (32 Bit) |
fword | Variable vom Typ Fword (48 Bit) |
qword | Variable vom Typ Qword (64 Bit) |
tbyte | Variable vom Typ Tbyte (80 Bit) |
xmmword | Variable vom Typ XmmWord (128 Bit) |
Die Typ-Angabe muß mit dem Originaltyp des Symbols bei der Definition
übereinstimmen. Wird die Typ-Angabe weggelassen, benutzt DASM32 das
deklarierte Symbol als externes DWORD. Die EXTRN-Direktive kann
an jeder beliebigen Stelle im Quelltext auftreten, auch nachdem das Symbol
bereits benutzt wurde.
4.8.2 PUBLIC-Direktive
Durch die PUBLIC-Direktive werden in diesem Modul definierte Symbole anderen Modulen zur Verfügung gestellt.
4.9 Sprachreferenz
4.9.1 Schlüsselwörter :
align | assume | byte | db | dd | dq | dt |
dup | dw | dword | else | end | endif | endm |
endp | ends | equ | extrn | fword | group | if |
ifdef | ifndef | include | label | macro | near | offset |
org | page | para | proc | ptr | public | purge |
qword | segment | short | size | tbyte | type | use32 |
word | xmmword | .code | .data | .data? | .model |
4.9.2 Register :
al | ax | eax | cs | st(0)/st | cr0 | dr0 | tr0 | mm0 | xmm0 |
bl | bx | ebx | ds | st(1) | cr1 | dr1 | tr1 | mm1 | xmm1 |
cl | cx | ecx | es | st(2) | cr2 | dr2 | tr2 | mm2 | xmm2 |
dl | dx | edx | fs | st(3) | cr3 | dr3 | tr3 | mm3 | xmm3 |
ah | sp | esp | gs | st(4) | cr4 | dr4 | tr4 | mm4 | xmm4 |
bh | bp | ebp | ss | st(5) | cr5 | dr5 | tr5 | mm5 | xmm5 |
ch | si | esi | st(6) | cr6 | dr6 | tr6 | mm6 | xmm6 | |
dh | di | edi | st(7) | cr7 | dr7 | tr7 | mm7 | xmm7 |
4.9.3 Vordefinierte Symbole :
_TEXT | _DATA | _BSS | DGROUP | $ | ? |
4.9.4 Unterstützte Mnemonics :
aaa | aad | aam | aas | adc | add | addps | addss |
and | andnps | andps | arpl | bound | bsf | bsr | bswap |
bt | btc | btr | bts | call | cbw | cdq | clc |
cld | cli | clts | cmc | cmova | cmovae | cmovb | cmovbe |
cmovc | cmove | cmovg | cmovge | cmovl | cmovle | cmovna | cmovna |
cmovnb | cmovnbe | cmovnc | cmovne | cmovng | cmovnge | cmovnl | cmovnle |
cmovno | cmovnp | cmovns | cmovnz | cmovo | cmovp | cmovpe | cmovpo |
cmovs | cmovz | cmp | cmpps | cmps | cmpss | cmpsb | cmpsd |
cmpsw | cmpxchg | cmpxchg8b | comiss | cpuid | cvtpi2ps | cvtps2pi | cvtsi2ss |
cvtss2si | cvttps2pi | cvttss2si | cwd | cwde | daa | das | dec |
div | divps | divss | emms | enter | f2xm1 | fabs | fadd |
faddp | fbld | fbstp | fchs | fclex | fcmovb | fcmovbe | fcmove |
fcmovnb | fcmovnbe | fcmovne | fcmovnu | fcmovu | fcom | fcomi | fcomip |
fcomp | fcompp | fcos | fdecstp | fdisi | fdiv | fdivp | fdivr |
fdivrp | femms | feni | ffree | fiadd | ficom | ficomp | fidiv |
fidivr | fild | fimul | fincstp | finit | fist | fistp | fisub |
fisubr | fld | fld1 | fldcw | fldenv | fldl2e | fldl2t | fldlg2 |
fldln2 | fldpi | fldz | fmul | fmulp | fnclex | fndisi | fneni |
fninit | fnop | fnsave | fnstcw | fnstenv | fnstsw | fpatan | fprem |
fprem1 | fptan | frndint | frstor | fsave | fscale | fsetpm | fsin |
fsincos | fsqrt | fst | fstcw | fstenv | fstp | fstsw | fsub |
fsubp | fsubr | fsubrp | ftst | fucom | fucomi | fucomip | fucomp |
fucompp | fwait | fxam | fxch | fxrstor | fxsave | fxtract | fyl2x |
fyl2xp1 | hlt | idiv | imul | in | inc | ins | insb |
insd | insw | int | into | invd | invlpg | iret | iretd |
iretw | ja | jae | jb | jbe | jc | jcxz | je |
jecxz | jg | jge | jl | jle | jmp | jna | jnae |
jnb | jnbe | jnc | jne | jng | jnge | jnl | jnle |
jno | jnp | jns | jnz | jo | jp | jpe | jpo |
js | jz | lahf | lar | ldmxscr | lea | leave | lgdt |
lidt | lds | les | lfs | lgs | lss | lldt | lmsw |
lock | lods | lodsb | lodsd | lodsw | loop | loope | loopne |
loopnz | loopz | lsl | ltr | maskmovq | maxps | maxss | minps |
minss | mov | movaps | movd | movhps | movhlps | movlps | movlhps |
movmskps | movntq | movntps | movq | movs | movsb | movsd | movss |
movsw | movsx | movups | movzx | mul | mulps | mulss | neg |
nop | not | or | orps | out | outs | outsb | outsd |
outsw | packssdw | packsswb | packuswb | paddb | paddd | paddsb | paddsw |
paddusb | paddusw | paddw | pand | pandn | pavgb | pavgusb | pavgw |
pcmpeqb | pcmpeqd | pcmpeqw | pcmpgtb | pcmpgtd | pcmpgtw | pextrw | pf2id |
pfacc | pfadd | pfcmpeq | pfcmpge | pfcmpgt | pfmax | pfmin | pfmul |
pfrcp | pfrcpit1 | pfrcpit2 | pfrsqit1 | pfrsqrt | pfsub | pfsubr | pi2fd |
pinsrw | pmaddw | pmaxsw | pmaxub | pminsw | pminub | pmovmskb | pmulhrw |
pmulhuw | pmulhw | pmullw | pop | popa | popad | popaw | popf |
popfd | popfw | por | prefetch | prefetchnta | prefetcht0 | prefetcht1 | prefetcht2 |
prefetchw | psadbw | pshufw | pslld | psllq | psllw | psrad | psraw |
psrld | psrlq | psrlw | psubb | psubd | psubsb | psubsw | psubusb |
psubusw | psubw | punpckhbw | punpckhdq | punpckhwd | punpcklbw | punpckldq | punpcklwd |
push | pusha | pushad | pushaw | pushf | pushfd | pushfw | pxor |
rcl | rcpps | rcpss | rcr | rdmsr | rdpmc | rdtsc | rsm |
rep | repe | repne | repnz | repz | ret | retf | rol |
ror | rsqrtps | rsqrtss | sahf | sal | sar | shl | shr |
sbb | scas | scasb | scasd | scasw | seta | setae | setb |
setbe | setc | sete | setg | setge | setl | setle | setna |
setnae | setnb | setnbe | setnc | setne | setng | setnge | setnl |
setnle | setno | setnp | setns | setnz | seto | setp | setpe |
setpo | sets | setz | sfence | sgdt | shufps | sidt | shld |
shrd | sldt | smsw | sqrtps | sqrtss | stc | std | sti |
stmxcsr | stos | stosb | stosd | stosw | str | sub | subps |
subss | syscall | sysret | test | ucomiss | ud2 | unpckhps | unpcklps |
verr | verw | wait | wbinvd | wrsmr | xadd | xchg | xlat |
xlatb | xor | xorps |
Bei Schlüsselwörtern, Registern und Mnemonics findet keine Unterscheidung zwischen Groß- und Kleinschreibung statt. Die Erkennung der vordefinierten Symbole dagegen ist abhängig von der Kommandozeilen-Option -ml. Sie unterliegen damit den gleichen Regeln wie selbstdefinierte Symbole.