Sie sind auf Seite 1von 78

Assembler - Einführung 1.

Teil
Vorstellung der wichtigsten Grundbegriffe und Grundkenntnisse sowie der
wichtigsten Hardwarekomponenten
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1. Einleitung

Wenn man durchs Internet surft kann man den Eindruck gewinnen, dass es keine deutschsprachige
Internetseite gibt, die es fertig bringt eine Einführung in Assembler ins Netz zu stellen.

Aus diesem Grund hab’ ich mich mit den letzten drei Büchern, die in der Buchhandlung noch zu haben
waren bewaffnet und hoffe mit dieser kleinen Einführung und meinen bescheidenen Kenntnissen
diesem Misstand ein Ende zu bereiten ;-)

Naja, viel Bestärkung habe ich allerdings bei meinen Recherchen nicht erhalten. Mal hörte ich „Was
willste denn mit dem alten Quatsch. Mach lieber Java, das läuft immer und auf allem“ oder
„Äh Assembler ?. Das ist doch nur Tipperei !“.

Wahr ist aber auch:

ü Man hat endlich mal Gelegenheit zu verstehen wie ein Computer eigentlich funktioniert.
ü Assembler gibt einem die volle Kontrolle über den Computer (einschließlich der Kontrolle über
das Ticken der Systemuhr und vieles mehr... ).
à Daher ist der Assembler bei Systemprogrammieren immer noch geachtet.

ü Assembler ermöglicht das Schreiben von sehr kompakten und extrem schnellen Programmen
(..schneller als Programme, welche mit Hochsprachen erstellt wurden ) .
à Hacker lieben Assembler für diese Eigenschaften. Denn wer lässt sich
schon einen Virus andrehen, der die Größe von Office 2000 hat und so
lange geladen werden muss wie Windows 98 ??? .. ja ok, leicht
übertrieben..;-)...und außerdem „ Allein der Gedanke ist verwerflich“ ;-)

ü Funktionen, welche man bei problemorientierten Hochsprachen schmerzlich vermisst sind in


Assembler leicht zu realisieren.
ü Assemblerprogramme lassen sich durchaus in Hochsprachenprogramme einbinden.

peschko@aol.com 2
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

2. Die Maschinensprache - Grundlagen

Ja, welche Sprache versteht denn der Prozessor ( CPU ) des Computers?
Nun, es ist eigentlich keine Sprache sondern Zahlen. Wollen wir direkt der CPU des Computers einen
Befehl erteilen, müssen wir dies mittels einer Zahl tun. Die Gesamtheit der Befehle die ein
Prozessortyp kennt nennt man seinen Befehlssatz.
Die Dezimalzahl 10 bedeutet: addiere zwei Zahlen. Die Zahl 5 kann die CPU zum Beispiel anweisen
den Inhalt eines Speicherplatzes an eine andere Speicherstelle zu verschieben usw.. Der 8086 , der
Urahne unseres Pentium III, kennt 90 Grundbefehle. Jeder Befehl hat zu seiner Identifikation eine
eigene Zahl. Diese Befehle versteht unser Pentium III auch, da er ja ein Nachkomme des 8086 ist.
Der Pentium III hat jedoch noch zusätzliche Befehle bekommen, die jedoch dem 8086 überhaupt
nichts sagen. Wir können also alle Befehle des 8086 durchaus auch bei nachfolgenden
Prozessorgenerationen einsetzen.

Es ist völlig egal, welche Programmiersprache wir einsetzen. Am Ende übersetzt der
jeweilige Sprachencompiler die Additionsanweisung des Quellcodes in die Zahl 10. Diese kann dann
an die CPU geschickt werden. Nun, aber warum gibt es dann mehr Programmiersprachen als Sand
am Meer ? Ganz einfach: Der Arbeitsschritt eines einzelnen Grundbefehls ist sehr klein. Es ist daher
sehr verführerisch mehrere Grundbefehle zu einer Anweisung zusammenzufassen.
Jede Hochsprache ( C, C++, Cobol, Pascal..) macht dies mit einem anderen Hintergedanken.
Cobol zum Beispiel fasst Grundbefehle so zusammen, dass Anweisungen entstehen, welche
besonders gut kaufmännische Probleme lösen helfen. Will man aber wissen wie viel Speicherplatz auf
der Festplatte noch vorhanden ist tut man sich mit Cobol etwas schwer.

Das menschliche Gehirn hat aber mit Zahlen in aller Regel Probleme. Vor allem, wenn es gleich 90
sind und es sich auch noch zu jeder Zahl den dazugehörigen Grundbefehl merken muss. Deshalb hat
man zu einem Trick gegriffen. Und der heißt Assembler. Statt der Dezimalzahl 10 merkt man sich
einfach den Mnemonic ADD ( was natürlich für ADDition steht). Entsprechend wählt man für die Zahl 5
den Mnemonic MOV ( was, wer hätt’s gedacht, für MOVe steht ). Nein falsch, Mnemonic ist kein
Tippfehler, sondern ein Kunstwort aus memory und name (ob Keanu Reeves das weiß ? Ich mein
„...und wie es funktioniert ?“). Wir schreiben in unserem Quellcode einfach ADD und MOV. Der
Assembler macht daraus die Zahlen 10 sowie 5 und die CPU addiert und bewegt. Was mit wem
addiert und von wo nach wohin bewegt wird kommt später.

Aber auch die Befehlssätze der einzelnen Prozessortypen unterscheiden sich voneinander.
Ein Prozessor für eine Spielekonsole hat mehr Befehle, welche die Erzeugung von Bildern, Tönen und
Bewegungen unterstützen. Für einen Prozessor in einer Steuerung für Produktionsabläufe wären sie
vermutlich überflüssig.

Man sieht also recht deutlich, dass nicht jeder Maschinencode auf jedem Prozessor
läuft.

peschko@aol.com 3
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

3. Die Central Processing Unit ( CPU ) oder einfach: Prozessor des PC’s

Nähern wir uns nun vorsichtig dem eigentlichen Objekt der Begierde.

Eine CPU besitzt erwartungsgemäß ein Rechenwerk, denn sie soll ja addieren können. Das
Rechenwerk kann aber auch Subtrahieren, Multiplizieren und Dividieren. Das Rechenwerk kann sogar
zusätzlich noch logische Bedingungen erstellen und prüfen.

Was aber dekodiert die Grundbefehle die wir nacheinander in die CPU schieben und entscheidet was
zu tun ist? Hierfür ist das Steuerwerk in der CPU zuständig.

Nun muss es aber auch noch Speicherplätze in der CPU geben, wo wir unsere Grundbefehle und
Daten parken können bis die CPU Zeit hat sie von dort einzulesen. Zusätzlich sind auch
Speicherplätze auf der CPU notwendig an denen die CPU ihre Arbeitsergebnisse für uns zur
Abholung bereithält. Diese Speicherplätze nennt man Register.

Als besonderen Service der CPU gibt es noch Speicherplätze auf der CPU, deren Inhalt uns über den
Erfolg oder Misserfolg eines Arbeitsschrittes informieren. Außerdem können an diesen
Speicherplätzen noch generelle Anweisungen gegeben werden die dann für die gesamte Zeit der
Ausführung der Grundbefehle gelten.
All diese Speicherplätze werden unter dem Begriff Statusflags zusammengefasst.

Schauen wir uns mal die Register zu Beginn etwas genauer an. Mit ihnen haben wir am meisten zu
tun. Im Mittelpunkt stehen die 4 Allzweckregister.

Sie haben folgenden Aufbau:

Beim 8086 Prozessor galt:

Ein Register ist eine Speicherort bestehend aus 16 Bits. Ein Bit ist ein nicht mehr weiter zu
unterteilender Speicherplatz. Der Inhalt eines Bits kann sozusagen nur eine 1 oder eine 0 sein.
Ein „leeres“ Bit kann es nicht geben, denn entweder ist das Bit gesetzt oder es ist nicht gesetzt.

Ein Register:
Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0

Low – Teil
High – Teil

Ein Register lässt sich in zwei 8- Bit- Register unterteilen. Der hohe und der tiefe Teil lassen sich
getrennt ansprechen. Man kann aber auch alle 16 Bits auf einmal auslesen oder beschreiben.

Jedes der 4 Allzweckregister hat einen eigenen Namen und einen eigenen Kennbuchstaben, sowie
einen hauptsächlichen Verwendungszweck.

Das erste Register trägt den Namen Akkumulator und trägt den Kennbuchstaben A.
Will man zum Ausdruck bringen, dass man alle 16 Bit des Registers meint, so hängt man noch ein X
an. Die Kennung lautet dann AX. Meint man nur Bit 0 bis einschließlich Bit 7, so schreibt man AL
(Akkumulator- Low). Für Zugriffe auf Bit 8 bis Bit 15 schreibt man AH (Akkumulator – High). Das
Register AX wird hauptsächlich im Zusammenhang mit arithmetischen Operationen und logischen
Vergleichen verwendet.

Das zweite Register trägt den Namen Basisregister mit Kennbuchstaben B.


Für den niedrigen Teil schreibt man BL, für den hohen BH und für das Register als Ganzes BX.
Verwendung findet diese Register vor allem für Zugriffe auf den Arbeitsspeicher des PC’s.

peschko@aol.com 4
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Das dritte Register trägt den Namen Countregister mit Kennbuchstaben C.


Für den niedrigen Teil schreibt man CL, für den hohen Teil CH und für das Register als Ganzes CX.
Verwendung findet diese Register vor allem wenn es sich ums Zählen dreht. Das ist vor allem bei
Schleifen der Fall.

Das vierte Register trägt den Namen Datenregister mit Kennbuchstaben D.


Für den niedrigen Teil schreibt man DL, für den hohen Teil DH und für das Register als Ganzes DX.
Verwendung findet diese Register vor allem mit dem Register AX zusammen, wenn Multiplikations-
und Divisionsaufgaben anstehen.

Bevor wir uns nun der Statusflags bemächtigen können müssen wir uns leider mit einem besonderen
Zahlensystem ( Binäres Zahlensystem ) beschäftigen.

4. Das binäre Zahlensystem

Eine Dezimalzahl darf nur die Ziffern 0 bis 9 beinhalten.

Zahlen die nur aus den Ziffern 1 und 0 bestehen haben einen besonderen Namen.
Es handelt sich dabei um „Dualzahlen“. Jedes Zahlensystem hat eine sogenannte Basis. Diese Basis
entspricht der Anzahl der im Zahlensystem zur Verfügung stehenden Ziffern. Im binären
Zahlensystem ist die Basis = 2.

Es stellt sich nun die Frage : „Wie rechnet man eine Dualzahl gebildet aus auf-
einander folgenden Bitnummern um in
eine Dezimalzahl ?“
Ganz einfach:
Betrachten wir doch einmal die Dualzahl in AL im Beispiel weiter oben.

3 2 1 0
1 * 2 + 0 * 2 + 1 * 2 + 0 * 2 = 10

Das erste Bit einer Dualzahl von rechts ist das


niedrige Bit. Hier beginnt man immer mit
der Bitnummer 0.

Kann man die Dezimalzahl 255 noch in das Register AH schreiben ?

255 : 2 = 127 Rest 1 niedriges Bit ( 1. Ziffer von rechts )


127 : 2 = 63 Rest 1
63 : 2 = 31 Rest 1
31 : 2 = 15 Rest 1
15 : 2 = 7 Rest 1
7:2= 3 Rest 1
3:2= 1 Rest 1
1:2= 0 Rest 1 hochwertiges Bit

Antwort: Ja, denn sie benötigt nur 8 Bits. Die Dezimalzahl 256 könnte man
nicht mehr nach AH schreiben.

peschko@aol.com 5
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

4.1 Addition und Subtraktion im binären Zahlensystem

Die Addition und Subtraktion im binären Zahlensystem ist etwas eigenwillig und gewöhnungsbedürftig.

4.1.1 Addition

Erst mal das, was uns noch vertraut ist:

0 0 1 jetzt aber: 1 Die 1 ist der Übertrag !


+ 0 + 1 + 0 +11

0 1 1 10

4.1.2 Subtraktion

0 1 1 1 0
- 0 - 1 - 0 - 1

0 0 1 1

5. Das hexadezimale Zahlensystem

Im hexadezimalen Zahlensystem gibt es die Ziffern 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 sowie


die „Ziffern“ A, B, C, D, E, F.

Die hexadezimale Zahl A steht für die dezimale Zahl 10.


Die hexadezimale Zahl B steht für die dezimale Zahl 11.
Die hexadezimale Zahl C steht für die dezimale Zahl 12.
Die hexadezimale Zahl D steht für die dezimale Zahl 13.
Die hexadezimale Zahl E steht für die dezimale Zahl 14.
Die hexadezimale Zahl F steht für die dezimale Zahl 15.

Welche Dezimalzahl entspricht der Hexadezimalzahl AFFE ?


3 2 1 0
A * 16 + F * 16 + F * 16 + E * 16 = 45054

Das erste Bit einer Hexadezimalzahl von rechts ist das


niedrige Bit. Hier beginnt man immer mit
der Bitnummer 0.

Welche hexadezimale Zahl passt gerade noch in das Register AH ?

255 : 16 = 15 Rest F niedriges Bit ( 1. Ziffer von rechts )


15 : 16 = 0 Rest F
hochwertiges Bit

Antwort: Es ist die hexadezimale Zahl FF

Wie man Hexadezimalzahlen addiert und subtrahiert soll hier nicht gezeigt werden, da es für die
Assemblerprogrammierung keine große Bedeutung hat.

So, nun sind wir gerüstet um uns mit den Statusflags auseinandersetzen zu können.

peschko@aol.com 6
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

6. Die Statusflags

Ja, auch das Statusregister ist beim 8086er ein 16 Bit- Register. Von diesen 16 Bit
werden jedoch nur 9 Bits benötigt. 6 Bits sind sogenannte Statusflags. Sie werden von der CPU
gesetzt oder rückgesetzt. Die restlichen 3 Bits sind Kontrollbits. Sie werden vom Programmierer
gesetzt oder gelöscht.

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

0 0 0 0 1 1 1 1 1 1 0 1 0 1 0 1

OF DF IF TF SF ZF AF PF CF

6.1 Statusflags

Bit Nr. 0 nennt man Carry Flag (Übertragflag)

Es zeigt an, wenn nach einer Addition oder Subtraktion der Wertebereich in einem Register
überschritten worden ist, wenn die Zahl des Ergebnisses zu viele Stellen hat. In diesem Fall steht in
Bit 0 eine 1.
Mit anderen Worten: Das Carry Flag zeigt einen Übertrag aus dem höchstwertigen Bit an.

Beispiel: Im Register AH steht die Dualzahl 1111 0111. Es soll nun


zu dieser Zahl die Dualzahl 1000 0000 addiert werden.

1111 0111

+ 1000 0000
1
1 0111 0111

Um das Ergebnis speichern zu können wären 9 Bits notwendig. Wir haben jedoch nur 8 Bits ( 1 Byte).
Das Ergebnis kann nicht mehr in AH gespeichert werden.

Daraus folgt CF = 1. Dieses Flag kann jedoch durch den Befehl CLC gelöscht
(CF = 0 ) und durch STC gesetzt werden ( CF = 1 ).

Bit Nr. 2 Das Parity Flag

dient der Fehlerprüfung bei Datenübertragungen über sie serielle Schnittstelle. Dieses Flag wird auf 1
gesetzt, wenn das Ergebnis einer Operation in den niederwertigen 8 Bits ( Low – Byte ) eine gerade
Anzahl an 1 en aufweist. Ansonsten ist PF = 0.

In unserem Beispiel: à PF = 0

1111 0111

+ 0001 0000
1 1 1 1
1 0000 0111

peschko@aol.com 7
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Bit Nr. 4 Das Auxiliary Flag / ( Hilfsübertragflag )

wird gesetzt (AF = 1 ), wenn ein Übertrag von Bit 3 nach Bit 4 erfolgt. Es wird hauptsächlich im
Zusammenhang mit dem BCD Code verwendet.

1100 1111

+ 0000 1000
1
1101 0111

Bit Nr. 6 Das Zero Flag ( Nullflag )

Wenn das Ergebnis einer Vergleichs- oder arithmetischen Operation 0 ergab, wird dieses Bit gesetzt
( ZF = 1).

1111 0111

- 1111 0111

0000 0000

Bit Nr. 7 Das Sign Flag ( Vorzeichenflag )

Auch Dualzahlen können Vorzeichen haben. Zur Darstellung von vorzeichenbehafteten Dualzahlen
wird aber kein + oder – verwendet, sondern das höchstwertige Bit. Hierbei entspricht
der Ziffer 1 das – und der Ziffer 0 das +.

Wenn wir die Dualzahl 0101 1110 als vorzeichenbehaftet betrachten erhalten wir:

1010 0010
als Dezimalzahl – 94.
0101 1110
als Dezimalzahl + 94.

( Zweierkomplement)

peschko@aol.com 8
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Bit Nr. 11 Das Overflow Flag ( Überlaufflag )

Dieses Bit wird gesetzt ( OF = 1 ), wenn ein Übertrag ins höchstwertige Bit erfolgt.
( Nicht zu verwechseln mit CF à Übertrag aus dem höchstwertigen Bit).

0111 0111

+ 0100 0000
1
1011 0111

6.2 Kontrollflags

Bit Nr. 8 Trap Flag ( Einzelschrittflag )

Durch setzten dieses Flags ( TF = 1 ) schaltet der Prozessor in den Einzelschrittmodus. Dadurch kann
man sich den Inhalt der Register nach jedem Grundbefehl anschauen und analysieren. Sehr
vorteilhaft bei der Fehlersuche im Programmcode !

Bit Nr. 9 Das Interrupt Enable Flag ( Unterbrechungsflag )

Gesetzt wird dieses Bit durch den Befehl STI. Rückgesetzt kann das IF mit dem Befehl CLI werden.
Es kommt vor, dass ein Programm seinen vorgesehen Ablauf nicht einhalten kann. Durch ( IF = 0 )
kann zum Beispiel verhindert werden, dass ein Programm durch das Drücken von STRG + C
abgebrochen wird .

Bit Nr. 10 Das Direction Flag ( Richtungsflag )

Wird eine Zeichenkette (String) im Speicher abgelegt, so muss jeder Speicherplatz eine eigene
Adresse haben, damit man weiß an welchem Ort ein Zeichen abgelegt wurde. Soll eine solche
Zeichenkette verarbeitet werden, müssen die einzelnen Adressen der Reihe nach aufgerufen werden.

DF = 1 Stringverarbeitung nach aufsteigenden Adressen.


DF = 0 Stringverarbeitung nach absteigenden Adressen.

7. Arbeitsspeicher

Unter dem Arbeitsspeicher kann man sich so etwas wie einen Notizblock des Prozessors vorstellen.
Er kann in den Arbeitsspeicher schnell Daten auslagern, die in seinen Registern im Moment keinen
Platz mehr haben. Andererseits kann er sie im Bedarfsfall auch schnell wieder zurückholen.

Natürlich muss man wissen an welchen Ort man die Daten geschrieben hat. Und da kommen die
Adressen ins Spiel.

peschko@aol.com 9
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

7.1 Adressbildung

Der Prozessor kann die Adressen von Speicherplätzen nur an Registern ausgeben. Die Register sind
beim 8086er aber auf eine 16 Bit Architektur ausgerichtet. Mit 16 Bit können wir die Dezimalzahlen 0
bis 65 535 darstellen. Wir könnten also mit einem Register 65 535 Byte durchnummerieren. Jedes
Byte hätte damit eine eigene Zahl und somit eine eigene Adresse.
Jetzt waren 65 535 Byte aber auch für damalige Verhältnisse keine berauschende Speicherkapazität.
Schon für einen 1MB großen Arbeitsspeicher hätte man ein 20 Bit Register benötigt, denn man hätte
1 048 576 Byte adressieren können. Das war aber beim 8086er so eine Sache. Also musste man sich
anders helfen.
Bei Intel hat sich deshalb ein schlauer Kopf damals etwas einfallen lassen.

Obwohl der 8086er nur 16 Bit Register hatte, war er an einen 20 Bit Adressbus angeschlossen.
Das bedeutet der 8086er hätte eine 20 Bit Adresse abschicken können, was aber nicht ging, da er
eben eigentlich nur mit 16 Bit breiten Adressen umgehen konnte.
Besagter schlauer Kopf hat sich nun folgendes überlegt: Wir nehmen die 1 048 576 Bytes und teilen
diese Zahl durch 16. Was das bringt ? Na ja, das ergibt 65 535. Das bedeutet mit einem 16 Bit
Register könnten wir jedem sechzehnten (16.) Byte eine Adresse geben. Diese Byte heißen
Paragraphen. Die Paragraphen müssen also durch 16 teilbar sein.

Es entstand folgendes Konzept:

Das Bild soll einen Ausschnitt des Arbeitsspeicher darstellen. Die Fächer sind die einzelnen
Speicherplätze.

Hier sei zum Beispiel das 12. Paragraphen- Byte


Seine 12_PB- Adresse somit: (12*16 = ) 192 Dual: 1100 0000

.
.

Hier sei zum Beispiel das 11. Paragraphen- Byte


Seine 11_PB- Adresse somit: (11*16 = ) 176 Dual: 1011 0000 .
.
.
Segment .

Byte 1

Byte 2
Unsere Byte 1 habe nun z. B. den Abstand 76 Adressen (dezimal) vom 11. Paragraphen- Byte
Abstand 1 als Offset-Adresse 1 = 0100 1100 Byte 3

.
.
Unsere Byte 3 hat daher den Abstand 78 Adressen (dezimal) vom 11. Paragraphen- Byte .
Abstand 3 als Offset-Adresse 3= 0100 1110 .

peschko@aol.com 10
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Der blaue Pfeil bezeichnet einen Bereich im Arbeitsspeicher. So ein Bereich hat einen besonderen
Namen: Segment. Das Segment beginnt mit einem Paragraphenbyte und umfasst maximal 64 KByte.

Mit der Adresse eines Paragraphenbyte kann man den Anfang eines Segmentes definieren. Diese
Anfangsadresse nennt man daher Segment- Adresse.

Die Offset- Adresse eines Speicherplatzes ist der Abstand (gerechnet in Speicherplätzen ) von
diesem Paragraphenbyte im Arbeitsspeicher.

Die vollständige Adresse eines Speicherplatzes:


Segment- Adresse : Offset- Adresse

„Byte 1“ wird daher in unserem Beispiel in folgender Form angegeben:


1011 0000 : 0100 1100

Übrigens:
Wenn in Windows 95 eine Anwendung an die Wand gefahren wird, kommt die allseits bekannte
Meldung „Allgemeine Schutzverletzung“. Nach anklicken von „Details“ kommen dann einige Adressen
in obiger Form ( allerdings hexadezimal ! ) mit ihrem jeweiligen Inhalt ( Hä ?).

Wie macht man jetzt eine 20- Bit- Adresse daraus ?

Es wird die Segment- Adresse mit 16 multipliziert . Im binären Zahlensystem bedeutet dies einfach
das Anhängen von 4 Nullen. Dann addiert man einfach die so erhaltene Segment- Adresse und die
Offset- Adresse und erhält eine 20- Bit- Adresse.

Segment- Adresse mal 16 :


+

0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0

Offset- Adresse:

0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0
=

20- Bit- Adresse:

0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 1 1 0 0

Die so erhaltene 20- Bit- Adresse hat aber einen Schönheitsfehler. Und zwar kann es jetzt sein,
dass ein und die selbe Speicherplatzstelle mehrere Adressen hat.

Sagen wir zum Beispiel : Das Segment beginnt am Paragraphenbyte 12. Unser „Byte1“ hat zu
dieser Segmentadresse einen Abstand von 92 Adressen.

peschko@aol.com 11
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Damit erhalten wir für die gleiche Speicherstelle „Byte 1“ die 20- Bit- Adresse:

0000000011000000 0000

0000000000000101 1100
+

0000000011000101 1100

Zum Abschluss der Einführung kommen nun noch 4 Segmentregister sowie 5 Index- und
Zeigerregister.

7.2 Die Zeiger- und Indexregister

Codesegment- Register (CS) à 16 Bit :

In diesem Register befindet sich eine Paragraphenbyte- Adresse mit der ein Segment beginnt in dem
Befehle gespeichert werden.

Nun gibt es ein weiteres Register, den Befehlszeiger (IP) à 16 Bit.


Dieses Register beinhaltet die Offset- Adresse eines Befehls innerhalb dieses Codesegments.

Ein Speicherplatz an dem im Codesegment ein Befehl sitzt bekommt also die vollständige Adresse:
CS : IP

Datensegment- Register (DS) à16 Bit :

In diesem Register befindet sich eine Paragraphenbyte- Adresse mit der ein Segment beginnt in dem
Daten gespeichert werden.

Extrasegment- Register (ES) à 16 Bit :

In diesem Register befindet sich eine Paragraphenbyte- Adresse mit der ein Segment beginnt in dem
ebenfalls Daten gespeichert werden.

Reicht der Platz im Datensegment nicht aus, so kann das ES als zusätzlicher Datenspeicher für
Variablen verwendet werden. Sehr wichtig ist dieses Register für das Kopieren, Verschieben und
Vergleichen von Zeichenketten.

Nun gibt es zwei weitere Register,


das Source- Index (SI) und das Destination- Index (DI) Register à jeweils 16 Bit.

Das SI und das DI Register unterstützen unter anderem das Datensegment- und
Extrasegmentregister als Offset- Register bei der Adressierung der Speicherstellen.

Auch das Register BX kann mit DS verwendet werden.

DS : SI
DS : DI
DS : BX

ES : SI
ES : DI
ES : BX

peschko@aol.com 12
Assembler - Einführung 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

8. Der Stack

Der Stack ist ein Bereich des Arbeitsspeichers in dem Daten abgelegt werden, wie Schriftstücke auf
einem Aktenstapel. Das neueste Schriftstück wird oben auf den Stapel gelegt. Das erste Schriftstück
liegt somit ganz unten im Stapel. Im Stacksegment- Register (SS) steht wieder die Adresse eines
Paragraphenbyte als Startadresse des Segments. Auch der Stack „wächst“ von der höheren Adresse
zur niedrigeren Speicheradresse!!! . Das Stackpointer- Register (SP) beinhaltet immer den Offset
der niederwertigsten Speicherplatzstelle im Stacksegment, in die gerade Daten eingelesen wurden.
Möchte man auf eine beliebige Speicherplatzstelle dazwischen zugreifen, muss zur Adressierung des
Offsets das Basepoint (BP)- Register eingesetzt werden.

SS : SB
SS : BP

Im Stack sollen nur Daten nach dem LIFO ( Last In , First Out ) Prinzip abgelegt werden. Zuletzt
eingelesene Daten sollen auch zuerst wieder ausgelesen werden.

SS

BP

SP

9. Zusammenfassung
Allzweckregister

AX

BX

Segmentregister
CX

DX
CS

DS

ES
Statusregister CPU
SS

Zeiger- und Indexregister


IP SI BP
DI SP
peschko@aol.com 13
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Assembler - Einführung 2. Teil


Assembler – Tools und Umgebung

1
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

1. Programmaufbau

Wie im ersten Teil schon erwähnt wird ein Assemblerprogramm in verschiedene Segmente aufgeteilt.
Ein Assemblerprogramm enthält in der Regel mindestens drei Segmente :

• STACK
• Datensegment
• Codesegment

Wir haben nun bei der direkten Segmentierung die Möglichkeit diese Segmente persönlich anzulegen
und nötigenfalls die Segment- Startadressen in die entsprechenden Zeiger- und Indexregister
(DS, CS) zu schreiben.

Als Alternative steht bei neueren Assemblern noch die indirekte Segmentierung über Speichermodelle
zur Verfügung. Dies ist interessant, wenn die 64- KB- Barriere der Segmente überwunden werden
muss. Die einzelnen Speichermodelle können hierzu ein Programm teilweise in beliebig viele
Segmente aufteilen. Es stehen insgesamt 6 Speichermodelle zur Verfügung.

1. Direkte Segmentierung

Ein Segment wird im Quellcode in folgender Form dargestellt:

Name segment [Ausrichtung] [Komb] [Klasse] ; Beginn des Segmentes.


; Diese
; Zeilen
; sind der Inhalt des Segmentes.
[Name] ends ; Ende des Segmentes.

Bei den rot geschriebenen Wörtern handelt es sich um Assemblerbefehle. Diese müssen in genau
dieser Form eingesetzt werden. Bei den grün geschriebenen Wörtern handelt es sich um einen
notwendigen Zusatz, der das Segment von anderen Segmenten im Programm unterscheidbar macht.
Diese sind aber wahlfrei. Man kann sich etwas „schönes“ ausdenken J. Es ist immer empfehlenswert
Kommentare im Programmcode zu platzieren. Vor diesen Kommentaren muss jedoch ein „;“ stehen.

2. Das erste Programm

Nun währen wir in der Lage ein erstes Programm zu schreiben.


Es soll – das ist so Tradition – "Hallo Welt ! " ausgeben.

Zu diesem Zweck öffnen wir unter Windows ein DOS- Fenster. Hierzu klickt man „START“ à
„PROGRAMME“ à „MS- DOS- EINGABEAUFFORDERUNG“.

Es erscheint ein DOS Fenster mit dem Inhalt:


C:\Windows> _

wir geben ein : CD.. und drücken die RETURN – Taste. Es erscheint folgende Ausgabe:
C:\> _

wir geben ein : MD ASM und drücken die RETURN – Taste. Es erscheint folgende Ausgabe:
C:\> _

Jetzt haben wir ein Verzeichnis mit dem Namen ASM angelegt. In dieses Verzeichnis wollen wir nun
wechseln. Dazu geben wir ein: CD ASM und drücken die RETURN – Taste.
Es erscheint folgende Ausgabe:
C:\asm> _

2
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Jetzt geben wir ein : EDIT HALLO.ASM und drücken die RETURN – Taste.

Es öffnet sich als blaues Fenster der DOS- Editor. Hier schreiben wir nun unseren Programmcode in
die Datei „HALLO.ASM“. Die Dateiendung „ASM“ ist wichtig !

Andere Schreibprogramme sind problematisch, da sie in der Regel nicht nur den Text an sich in der
Datei abspeichern, sondern noch zusätzlich Steuerungszeichen für die Darstellungsweise des Textes.

Diese Steuerungszeichen drehen dann beim Assemblieren des Textes den Assembler durch den
Wind. Die so verursachten Fehlermeldungen sind geeignet einen an den Rand des
Nervenzusammenbruchs zu bringen, da man im Quellcode einfach keinen Fehler finden kann.

Wir geben ein:

; ********************************************
;*Ausgabe: „Hallo Welt!“ auf Bildschirm*
;*********************************************

DATEN SEGMENT

MELDUNG DB "Hallo Welt !“, "$“

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP (?)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX
MOV DX, OFFSET MELDUNG

MOV AH, 9H
INT 21H

MOV AH, 4CH


INT 21H

CODE ENDS

END START

3
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Nun muss unser Quellcode noch gespeichert werden. Hierzu fahren wir mit der Maus im DOS Fenster
auf "DATEI" à "SPEICHERN" à "BEENDEN".

Wir sehen wieder auf dem Bildschirm im DOS Fenster den Eingabe Prompt:
C:\asm> _

Jetzt müssen zwei Programme TASM.EXE ( der Assembler) UND TLINK.EXE ( der Linker) in das
Verzeichnis ASM kopiert werden. Das sind die Programmiertools von BORLAND. Von Microsoft gibt
es diese Programme auch. Dort haben sie die Bezeichnungen MASM.EXE und LINK.EXE. Bei der
Erstellung von anspruchsvolleren Programmen sollte man immer darauf achten, dass man die
aktuellste Version dieser Programme hat. Auch bei der Syntax der jeweiligen Assembler gibt es kleine
Unterschiede zwischen BORLAND und Microsoft. Die hier vorgestellten Programme wurden mit
TASM und TLINK erstellt.

Jetzt geben wir ein : TASM HALLO.ASM und drücken die RETURN – Taste.

Wenn Du keine Tippfehler in Deinem Quellcode hast und auch keine ";" vergessen hast, müsste nun
folgende Meldung erscheinen:

TURBO ASSEMBLER VERSION .......

ASSEMBLER FILE : HALLO.ASM

ERROR MESSAGES : none


WARNING MESSAGES : none
…..
C:\asm> _

Jetzt geben wir ein : TLINK HALLO.OBJ und drücken die RETURN – Taste.

folgende Meldung erscheinen:


TUROBO LINK VERSION.....
C:\asm> _

Jetzt geben wir ein: HALLO.EXE und drücken die RETURN – Taste.

folgende Meldung erscheinen:


HALLO WELT !
C:\asm> _

Nun haben wir endlich das erste Programm zum laufen gebracht ! Mit der Hochsprache C++ hätte
man einfach COUT << "Hallo Welt !" eingegeben.

Nun wollen wir uns einmal anschauen wie unser Programm vom Assembler übersetzt wird. Dazu
geben wir nun ein: TASM / ZI HALLO,, und drücken die RETURN – Taste.
Es wurde dadurch eine Datei HALLO.LST erstellt. Diese Datei wird nun im DOS Editor durch die
Eingabe von : EDIT HALLO.LST und drücken der RETURN – Taste geöffnet.

4
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Es erscheint folgende Ausgabe:

1 ;**********************************************
2 ;*Ausgabe : "Hallo Welt !" auf Bildschirm*
3 ;**********************************************
4
5
6 0000 DATEN SEGMENT
7
8
9 0000 48 61 6C 6C 6F 20 57 + MELDUNG DB "Hallo Welt !","$"
10 65 6C 74 20 21 24
11
12 000D DATEN ENDS
13
14
15 0000 STAPEL SEGMENT BYTE STACK
16
17 0000 80*(????) DW 128 DUP (?)
18
19 0100 STAPEL ENDS
20
21
22
23 0000 CODE SEGMENT
24
25 ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAP
L
26
27 0000 B8 0000s START: MOV AX, DATEN
28 0003 8E D8 MOV DS, AX
29 0005 BA 0000r MOV DX, OFFSET MELDUNG
30
31 0008 B4 09 MOV AH, 9H
32 000A CD 21 INT 21H
33
34 000C B4 4C MOV AH, 4CH
35 000E CD 21 INT 21H
36
37 0010 CODE ENDS
38
39 END START

Symbol Name Typ Value

??DATE Text "25/03/00"


??FILENAME Text "HALLO "
??TIME Text "21:31:19"
??VERSION Number 0101
@CPU Text 0101H
@CURSEG Text CODE
@FILENAME Text HALLO
@WORDSIZE Text 2
MELDUNG Byte DATEN: 0000
START Near CODE: 0000

Groups & Segments Bit Size Align Combine Class

CODE 16 0010 Para none


DATEN 16 000D Para none
STAPEL 16 0100 Byte Stack

5
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Im oberen Ausdruck können wir schön erkennen, wie sich der Assembler die Anordnung unseres
Programms im Arbeitsspeicher vorgestellt hat.

In der ersten Spalte werden Zeilennummern angegeben. Von diesen macht der Assembler Gebrauch,
wenn er uns Fehlermeldungen um die Ohren haut. Hinter einer Fehlermeldung befindet sich immer die
Zeilennummer in der sich der Fehler befindet. Diese Zeilennummern sind nicht Bestandteil unseres
Programms im Arbeitsspeicher.

Zeilen 1,2,3 sind Kommentarzeilen. Der Text hinter dem Semikolon befindet sich nur zur Information
eines nachfolgenden Programmierers im Quellcode. Auch ein nachfolgender Programmierer soll
wissen worum es in diesem Programm geht. Dieser Text ist nicht Bestandteil unseres Programms im
Arbeitsspeicher.

Zeilen 4,5 sind Leerzeilen.

In Zeile 6 teilen wir dem Assembler mit, dass wir nun ein Segment (Bereich im Arbeitsspeicher)
beginnen wollen. Der Assembler antwortet uns mit dem Hinweis, dass er ab dem nächsten
verfügbaren Paragraphenbyte dieses Segment mit Namen DATEN beginnt und die erste
Speicherstelle in dem Segment DATEN mit der Offset- Adresse 0000 adressiert.

In Zeile 9 teilen wir dem Assembler mit, dass eine Zeichenkette Hallo Welt ! im Segment DATEN des
Arbeitsspeichers abgelegt werden soll. Der Rechner kann aber keine Buchstaben sondern nur Zahlen
im Arbeitsspeicher ablegen. Also wandelt er gemäß dem ASCII Code die Buchstabe in Zahlen um.
Man kann zum Beispiel gut erkennen, dass die hexadezimale Zahl 20 für ein Leerzeichen steht.
Da 13 Zeichen inklusive Leer- und $-zeichen abgelegt werden sollen und jedes Zeichen 1 Byte
(1 Speicherplatz ) belegt, befinden wir uns am Ende der Zeichenkette an
Speicherplatz 000D (= 0013 dezimal ). Außerdem teilen wir mit MELDUNG dem Assembler mit, dass
wir der Speicherstelle 0000 den Namen MELDUNG zuordnen, und dass wir an anderer Stelle des
Programms durch Verwendung des Namens MELDUNG auf diese Speicherstelle zugreifen wollen. Mit
DB (DEFINE Byte) geben wir zu verstehen, dass Speicherplätze nur byteweise belegt werden sollen

In Zeile 12 Teilen wir dem Assembler mit dem ENDS Befehl mit, dass hier dieses Segment endet.
Der Assembler bestätigt dies mit dem Hinweis, dass für ihn damit an der Offset- Adresse 000D dieses
Segment beendet ist.

Für Zeile 15 gilt entsprechend das Gleiche wie für Zeile 6. Der Zusatz BYTE bedeutet, dass das
Segment an der nächsten Adresse beginnen soll. Der Zusatz STACK zeigt an, dass wir nur mit
SS:SP auf diesen Bereich zugreifen wollen. Ehrlich gesagt brauchen wir den STACK in diesem
Programm gar nicht. Aber 1. kann man daran erkennen wie man einen STACK anlegt und 2. ist es
sehr einfach das Reservieren von Speicherplatz zu demonstrieren.

In Zeile 17 hätten wir nun der Startadresse wieder einen Namen, etwa MAKE_PROG_LOOK_BIG,
zuordnen können. Da wir aber eh nicht auf diesen Speicherplatz zugreifen wollen, wäre dies
überflüssig. Der Ausdruck DW steht für DEFINE Word. Mit dieser Direktive geben wir an, dass wir
Speicherplatz immer nur als Vielfaches von 2 Byte (=1 Word) belegen wollen. Die Zahl 128 bewirkt,
dass 128 solcher Einheiten ( eine Einheit besteht aus je 2 Byte ) im Arbeitsspeicher reserviert werden.
Insgesamt werden also 256 Byte im Arbeitsspeicher reserviert. Damit wird klar warum das Segment
STAPEL an Adresse 0100 (hexadezimal) in Zeile 19 endet. Die hexadezimale Zahl 0100 entspricht
der Dezimalzahl 256. Das "?" mit der DUP ( ) Direktive bewirkt, dass die einzelnen Speicherplätze mit
keinem Standartwert vorbelegt werden. Ihr Inhalt bleibt so, wie wir ihn vorgefunden haben. Die
hexadezimale Zahl 80 entspricht der Dezimalzahl 128.

In Zeile 23 beginnt das Segment CODE.

In Zeile 25 müssen wir nun dem Prozessor mitteilen, welche Segmentadressen nun den einzelnen
Allzweckregistern zugeordnet werden. Im CS- Register landet die Segmentadresse vom
Codesegment, weil dort die Befehle abgelegt sind usw.

In Zeile 27 kopieren (bewegen) wir die Segmentadresse des Datensegmentes in das CPU
Register AX.

6
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

In Zeile 27 kopieren (bewegen) wir die Segmentadresse des Datensegmentes nun von AX in das
Datensegmentregister DS.
Eine Vereinfachung zu MOV DS, DATEN ist nicht möglich, da der MOV Befehl dies nicht zulässt.

In Zeile 29 bewirkt der Ausdruck OFFSET MELDUNG, dass die Speicherstelle mit dem Namen
Meldung in das Register DX kopiert wird. Zusätzlich kommt zum Ausdruck, dass diese Speicherstelle
nur den Beginn eines Speicherbereiches darstellt. Damit kann der Prozessor nun unsere
Zeichenkette HALLO WELT ! finden. Im DX Register steht die Adresse an der, der String beginnt. Da
es aber viele Speicherstellen mit der Adresse 0000 im Programm gibt, teilen wir ihm zusätzlich im DS
Register mit in welchem Segment der Prozessor die Adresse suchen soll.

Die Zeilen 27 und 28 sind immer notwendig, wenn wir auf Adressen im Datensegment zugreifen
wollen!!!

Warum verschieben wir die Startadresse unseres Strings gerade in das DX Register ?
Die Antwort auf diese Frage steht in Zeile 32.

Dort wird mit dem Befehl INT unser Programm unterbrochen um ein Unterprogramm des
Betriebssystems mit der "Nummer" 21 zu starten. Dieses Unterprogramm ist Bestandteil des
Betriebssystems und wurde mit ihm geliefert.

Dieses Unterprogramm schaut immer zuerst in das Register AH und sieht nun dort die hexadezimale
Zahl 9H ( Das H steht für hexadezimal). Das Unterprogramm weiß dadurch, dass es eine
Zeichenkette auf dem Bildschirm ausgeben soll. Das Unterprogramm verlangt, dass die Adresse der
ersten Speicherstelle dieser Zeichenkette im Register DX steht. Das Unterprogramm nimmt allerdings
automatisch an, dass diese Speicherstelle sich in dem Segment befindet, dessen Adresse in DS
angegeben ist. Jetzt gibt das Unterprogramm nacheinander den Inhalt der Speicherstellen auf dem
Bildschirm aus. Wenn das Unterprogramm jedoch auf das $-zeichen trifft, signalisiert dies dem
Unterprogramm, dass die Zeichenkette zu Ende ist und es seine Arbeit beenden kann. Nun gibt das
Unterprogramm die Kontrolle an unser Programm zurück.

In Zeile 35 rufen wir schon wieder dieses Unterprogramm auf. Mit der Zahl 4CH im Register AH teilen
wir diesmal dem Unterprogramm mit, dass unser Programm zu Ende ist. Das Unterprogramm gibt jetzt
die Kontrolle nicht mehr an unser Programm sondern ans Betriebssystem zurück. Wir sehen den
DOS- Prompt auf dem Bildschirm.

In Zeile 39 Befindet sich der Ausdruck END START. Das Label hinter END signalisiert dem Assembler
an welcher Stelle der erste Befehl in unserem Programm zu finden ist (bei uns in Zeile 27 ). Jetzt weiß
der Prozessor welche Startadresse er in sein Register IP laden muss (nämlich die Offset- Adresse
von START= 0000).

In Zeile 27 steht in der 3. Spalte 0000s. Das "s" bedeutet, dass dies eine Segmentadresse sein soll. In
Zeile 29 steht in der 3. Spalte 0000r. Das "r" steht hier für Relativadresse ( Offset- Adresse). Wie
kommt der Assembler auf die Idee dem Datensegment die Adresse 0000 zugeben ? Was ist, wenn zu
Beginn des Arbeitsspeicher (am ersten Paragraphenbyte) schon ein anderes Programm seine Daten
geparkt hat ? Die Antwort: Der Assembler hat gar keinen Einfluss darauf, wo unser Programm im
Arbeitsspeicher abgespeichert wird. Die "Parkplatzeinweisung" macht der Lader des Betriebssystems.
Er kennt die Adresse des letzten noch freien Speicherplatzes. Sollte zum Beispiel die letzte freie
Speicherstelle die Adresse 0400H haben, so addiert der Lader einfach zu jeder Segmentadresse in
unsrem Programm 0400H dazu und legt das Programm an dieser Stelle im Arbeitsspeicher ab.

Übrigens: Dies ist auch der Grund warum man Adressen nicht addieren darf !

7
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

3. DEBUG

Eines der wichtigsten Hackertools ist mit Sicherheit das Programm DEBUG. Es ermöglicht einen Blick
hinter die Kulissen. Außerdem ist es das einzige Programm mit dem man an jedem Rechner mit MS-
DOS und in allen Lebenslagen ein Programm schreiben kann
(Vorausgesetzt man versteht etwas von Assembler) . Dies ist möglich, weil DEBUG mit MS- DOS
geliefert wird. Die Programme haben jedoch die Einschränkung, dass sie nur aus einem Segment
bestehen können. Da wir aber mit DEBUG an fremden Rechnern keine Programme schreiben wollen,
für die Mann- Jahre notwendig sind, ist dies meistens auch kein Problem.

3.1 DEBUG als Testhilfe

Ja, dann nehmen wir mal unser Programm Hallo.exe auseinander.

Dazu öffnen wir wieder ein DOS- Fenster und wechseln in das Verzeichnis ASM.
Wir sehen:
C:\asm> _

Wir geben ein:


DEBUG HALLO.EXE und drücken die RETURN – Taste.

Wir sehen:
C:\asm>debug hallo.exe
-_

Wir geben ein: t und drücken die RETURN – Taste.

Wir sehen:
C:\asm>debug hallo.exe
-t
AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000
DS=1B95 ES=1B95 SS=1BA5 CS=1BB6 IP=0003 NV UP EI PL NZ NA PO NC
1BB6:0003 8ED8 MOV DS,AX
-
DEBUG zeigt uns in den ersten beiden Zeilen die Inhalte der Register unserer CPU nach Ausführung
der Zeile 27 in obigem Ausdruck. In der dritten Zeile sehen wir die nächste zur Ausführung
vorgesehene Programmzeile. Außerdem sehen wir nun in welchen Bereich des Arbeitsspeichers der
Lader das Programm geladen hat. Die Adressen der jeweiligen Segmente sind hier blau unterlegt.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=0005 NV UP EI PL NZ NA PO NC
1BB6:0005 BA0000 MOV DX,0000
-

Es ist klar, dass bei jedem Rechner andere Segmentadressen als in diesem Beispiel auftreten
werden. Der Arbeitsspeicher ist je nach Rechner unterschiedlich groß und es werden vorher
unterschiedliche Programme in den Speicher geladen.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=0008 NV UP EI PL NZ NA PO NC
1BB6:0008 B409 MOV AH,09
-

8
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

DEBUG arbeitet im Einzelschritt Modus unser Programm ab. Man sieht deutlich, dass im Register IP
schon immer die Adresse des nächsten Befehls steht.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=09A5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=000A NV UP EI PL NZ NA PO NC
1BB6:000A CD21 INT 21
-

Wie man sieht steht nun tatsächlich im hohen Teil von AX eine 09, die wir ein Rechenschritt zuvor
dorthin "gemoved" haben.
Wir sind nun wieder an die Stelle gekommen, an der das Unterprogramm "Nr.21H"des
Betriebssystems aufgerufen wird. Schauen wir uns doch mal an wohin die Reise im Arbeitsspeicher
geht.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=09A5 BX=0000 CX=0120 DX=0000 SP=0107 BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1194 IP=0445 NV UP DI PL NZ NA PO NC
1194:0445 EAA004B407 JMP 07B4:04A0
-

Hä? Was ist das ? Wir sind an der Speicherstelle 1194:0445 gelandet ! Dies ist aber noch nicht unser
Unterprogramm. Wir müssen einen Sprung (JMP) zur Adresse 07B4:04A0 ausführen, um zum
Unterprogramm zu gelangen. Wir sind in einer Tabelle gelandet. Jede Zahl bei einem INT Befehl führt
zu einer Speicherstelle an der lediglich die Adresse des eigentlichen Unterprogramms steht.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=09A5 BX=0000 CX=0120 DX=0000 SP=0107 BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=07B4 IP=04A0 NV UP DI PL NZ NA PO NC
07B4:04A0 80FC72 CMP AH,72
-
Hier sehen wir die erste Zeile des Unterprogramms. Nach unserem INT Befehl haben wir unser
Programm verlassen und haben sozusagen das Hoheitsgebiet von Microsoft betreten. Wir könnten
uns auf diese Weise den Quellcode des ganzen Unterprogramms zugänglich machen und die
geistigen Ergüsse der Microsoft Programmierer begutachten. In der Fachsprache nennt man das
zugänglich machen des Quellcodes eines Programms Disassemblierung. Das Unterprogramm ist lang
und langweilig. Bevor wir unseren Rechner abschießen

geben wir diesmal ein: q und drücken die RETURN – Taste.

Dadurch wurde DEBUG verlassen und wir sehen den DOS Prompt. Wir fangen wieder von
vorne ( 3.1 DEBUG ) an bis wir wieder an die Stelle kommen :

AX=09A5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=000A NV UP EI PL NZ NA PO NC
1BB6:000A CD21 INT 21
-
Wir geben diesmal an dieser Stelle ein p ein und drücken die RETURN – Taste.

9
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Nun wird das gesamte Unterprogramm am Stück durchgeführt. Daher sehen wir nun:

Hallo Welt !
AX=0924 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000
DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=000C NV UP EI PL NZ NA PO NC
1BB6:000C B44C MOV AH,4C
-
und landen wieder in unserem Programm.

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=4C24 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=000E NV UP EI PL NZ NA PO NC
1BB6:000E CD21 INT 21
-
Wir geben ein: p und drücken die RETURN – Taste.
Wir sehen:

Programm wurde normal beendet


-
Wir geben ein: q und drücken die RETURN – Taste.
Wir sehen:

C:\asm>_

3.2 Editieren des Arbeitsspeichers mit DEBUG

Jetzt haben wir bis auf unseren String HALLO WELT ! alles von unserem Programm im
Arbeitsspeicher gesehen. Und den String werden wir uns nun auch noch anschauen.

Zu diesem Zweck müssen wir erst einmal herausfinden wo sich der String im Arbeitsspeicher befindet.

1. Schritt:

Wir öffnen wieder ein DOS- Fenster und wechseln in das Verzeichnis ASM.
Wir sehen:
C:\asm> _

Wir geben ein:


DEBUG HALLO.EXE und drücken die RETURN – Taste.

Wir sehen:
C:\asm>debug hallo.exe
-_

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

C:\asm>debug hallo.exe
-t

AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1B95 ES=1B95 SS=1BA5 CS=1BB6 IP=0003 NV UP EI PL NZ NA PO NC
1BB6:0003 8ED8 MOV DS,AX
-

10
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=0005 NV UP EI PL NZ NA PO NC
1BB6:0005 BA0000 MOV DX,0000
-

Wir geben ein: t und drücken die RETURN – Taste.


Wir sehen:

AX=1BA5 BX=0000 CX=0120 DX=0000 SP=010D BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA5 CS=1BB6 IP=0008 NV UP EI PL NZ NA PO NC
1BB6:0008 B409 MOV AH,09
-

Nun haben wir den String HALLO WELT ! in den Arbeitspeicher geladen. Ohne diesen Schritt wäre es
Zufall, was wir an den entsprechenden Speicherstellen antreffen.

2. Schritt:

Der String HALLO WELT ! steht bei mir an der Adresse 1BA5: 0000 Bei Dir ergibt sich hier mit
Sicherheit für den vorderen Teil eine andere Adresse. Der String HALLO WELT ! benötigt
mit Leerzeichen und $-zeichen 13 Speicherstellen. Das Ende des String befindet sich dann an der
Adresse1BA5 : 000D.
.
Wir geben ein: E 1BA5:0000 und drücken die RETURN – Taste.
Wir sehen:

-E 1BA5:0000
1BA5:0000 48._

Mit der hexadezimalen Zahl 48 sehen wir hier das "H" von HALLO. Wir haben nun 3 Möglichkeiten:

1) Durch drücken der Leerzeichen- Taste ohne Veränderung des Wertes zum nächsten Zeichen
vorzurücken.
2) Einen neuen Wert einzugeben und dann mit Drücken der Leerzeichen- Taste zum nächsten
Wert vorzurücken.
3) Durch drücken der RETURN Taste den Editiermodus zu verlassen ohne etwas zu verändern.

Ich habe mich gerade dafür entschieden aus unserm Programm die englischsprachige Version
HELLO WORLD! zumachen.

Da das H noch stimmt drücken wir nun die Leerzeichentaste.


Wir sehen:

-E 1BA5:0000
1BA5:0000 48. 61._

Das "e" ist im ASCII Code ist einfach 4 Buchstabe weiter. Das bedeutet wir ersetzten die Zahl 61
durch die Zahl 65.

Wir geben ein: 65 und drücken die LEERZEICHEN – Taste.


Wir sehen:

-E 1BA5:0000
1BA5:0000 48. 61.65 6C._

11
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Wir drücken so oft die LEERZEICHEN- Taste bis wir an diesen Punkt kommen:

-E 1BA5:0000
1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65._

Hier steht nun das "e" von Welt. Wir ersetzten es durch ein "o" à Zahl: 6F

wir tippen ein: 6F und drücken die LEERZEICHEN – Taste.


Wir sehen:

1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65.6F


1BA5:0008 6C._

Hier steht nun das "l" von Welt. Wir ersetzten es durch ein "r" à Zahl: 72

wir tippen ein: 72 und drücken die LEERZEICHEN – Taste.


Wir sehen:

1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65.6F


1BA5:0008 6C.72 74._

Hier steht nun das "t" von Welt. Wir ersetzten es durch ein "l" à Zahl: 6C

wir tippen ein: 6C und drücken die LEERZEICHEN – Taste.


Wir sehen:

1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65.6F


1BA5:0008 6C.72 74.6C 20._

Hier steht nun das " " hinter dem Wort Welt. Wir ersetzten es durch ein "d" à Zahl: 64

wir tippen ein: 64 und drücken die LEERZEICHEN – Taste.


Wir sehen:

1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65.6F


1BA5:0008 6C.72 74.6C 20.64 21._

Wir sehen nun den ASCII Code des Ausrufezeichens. Der bleibt unverändert.

wir drücken die LEERZEICHEN – Taste.


Wir sehen:

1BA5:0000 48. 61.65 6C. 6C. 6F. 20. 57. 65.6F


1BA5:0008 6C.72 74.6C 20.64 21. 24._

Wir sehen nun den ASCII Code des $-zeichens. Der bleibt unverändert.

wir drücken die RETURN – Taste.


Wir sehen:
-
Wir geben ein: g und drücken die RETURN – Taste.
Wir sehen:

Hello World!
Programm wurde normal beendet
-
Wir geben ein: q und drücken die RETURN – Taste.
Wir sehen:
C:\asm>_
Wenn wir nun hallo eingeben und RETURN drücken kommt wieder die Meldung Hallo WELT !.
Dies liegt daran, dass das Programm mit den alten Werten von der Festplatte neu geladen wird.

12
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

3.3 Erstellen von ausführbaren Programmen mit DEBUG

Wir schreiben nun direkt ein Programm in den Arbeitsspeicher und lassen es anschließend mit
DEBUG assemblieren und ausführen. Diese ausführbaren Dateien können ( wegen DEBUG) nur aus
einen Segment bestehen. Dies bedeutet, dass wir die Daten und den Programmcode in das gleiche
Segment schreiben müssen. Daher müssen wir dafür Sorge tragen, dass der Prozessor nicht auf
unsren String "Hallo Welt !" trifft und denkt das seien Befehle die er jetzt ausführen muss.
DEBUG setzt nämlich alle Segmentregister auf den gleichen Wert. Dieses Problem lässt sich dadurch
entschärfen, indem man die Daten im richtigen Moment einfach überspringt.

Dazu öffnen wir wieder ein DOS- Fenster und wechseln in das Verzeichnis ASM.
Wir sehen:
C:\asm> _

Wir geben ein: DEBUG und drücken die RETURN – Taste.

Danach wird ein a eingegeben und die RETURN – Taste gedrückt.

Wir sehen ( Die Segmentadresse ist wahrscheinlich eine andere):

1B72:0100 _

Da wir als erstes den String "Hallo Welt !" abspeichern wollen müssen wir zuerst einen Sprung
eingeben:

Jetzt brauchen wir ein wenig Kopfrechnen. Der Sprungbefehl mit Adresse benötigt 2Byte im Speicher.
Überspringen wollen wir den String "Hallo Welt !","$". Dafür brauchen wir noch einmal 13 Byte. Der
Befehl MOV AH, 09H könnte erst im 14 Byte abgespeichert werden. Da müssen wir mit dem JMP
Befehl hinspringen.
Das bedeutet: Speicherplatz 0100 für JMP
0101 für Sprungadresse
0102 H
.
.
010E $
010F MOV AH, 09H
.
.
Wir springen also zu 010F

Wir geben ein: JMP 010F und drücken die RETURN – Taste.
Wir sehen:

-a
1B72:0100 JMP 010F
1B72:0102_

Wir geben ein: DB "Hallo Welt !","$" und drücken die RETURN – Taste.
Wir sehen:
-a
1B72:0100 JMP 010F
1B72:0102 DB "Hallo Welt !","$"
1B72:010F_

Tatsächlich landen wir am richtigen Platz!

13
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Wir geben ein: MOV AH, 9 und drücken die RETURN – Taste.
Wir sehen:

-a
1B72:0100 JMP 010F
1B72:0102 DB "Hallo Welt !","$"
1B72:010F MOV AH,9
1B72:0111_

Das Unterprogramm möchte bekanntlich im DX Register die Offset- Adresse des Strings haben.
Der String beginnt in Speicherstelle 0102. Diese Speicherstelle laden wir nun nach DX.

Wir geben ein: MOV DX, 102 und drücken die RETURN – Taste.
Wir sehen:
C:\asm>debug
-a
1B72:0100 JMP 010F
1B72:0102 DB "Hallo Welt !","$"
1B72:010F MOV AH,9
1B72:0111 MOV DX, 102
1B72:0114_

Wir geben nun den restlichen Text ein, bis wir diese Stelle erreichen:
-a
1B72:0100 JMP 010F
1B72:0102 DB "Hallo Welt !","$"
1B72:010F MOV AH,9
1B72:0111 MOV DX, 102
1B72:0114 INT 21
1B72:0116 MOV AH, 4C
1B72:0118 INT 21
1B72:011A_

Nun wird nichts mehr eingegeben sondern einfach nur die RETURN – Taste gedrückt.
Wir sehen:
-a
1B72:0100 JMP 010F
1B72:0102 DB "Hallo Welt !","$"
1B72:010F MOV AH,9
1B72:0111 MOV DX, 102
1B72:0114 INT 21
1B72:0116 MOV AH, 4C
1B72:0118 INT 21
1B72:011A
-_

Wir geben ein: n WELT.COM und drücken die RETURN – Taste.


Wir geben ein: RCX und drücken die RETURN – Taste.
Wir sehen:

-n WELT.COM
-RCX
CX 0000
:_

DEBUG möchte jetzt, dass wir die Anzahl der Bytes eingeben sie unser Programm belegt.
Bei uns sind dies 25 Bytes.

Wir geben ein: 25 und drücken die RETURN – Taste.

14
Assembler - Einführung 2. Teil (Beta 2)
Copyright 2000 Thomas Peschko

Wir geben ein: w und drücken die RETURN – Taste.

Wir sehen:
CX 0000
:25
-w
00025 Bytes werden geschrieben
-_

Wir geben ein: g und drücken die RETURN – Taste.


Wir sehen:

Hallo Welt !
C:\asm>_

Wir haben auf diese Weise eine COM Datei geschrieben. Der Unterschied zur EXE Datei besteht
darin, dass die COM Datei nur 64 KB groß sein kann und aus einem Segment besteht.

3.4 Disassemblierung mit DEBUG

Mit dem Parameter u kann man sich das Programm Hallo.exe im Arbeitsspeicher anzeigen lassen.
DEBUG erkennt aber nicht wo unser Programm zu Ende ist.

Microsoft(R) Windows xx
(C)Copyright Microsoft Corp 19xx-19xx.

C:\WINDOWS>cd..

C:\>cd asm

C:\asm>debug hallo.exe
-u
1BB6:0000 B8A51B MOV AX,1BA5
1BB6:0003 8ED8 MOV DS,AX
1BB6:0005 BA0000 MOV DX,0000
1BB6:0008 B409 MOV AH,09
1BB6:000A CD21 INT 21
1BB6:000C B44C MOV AH,4C
1BB6:000E CD21 INT 21
1BB6:0010 3C2A CMP AL,2A
1BB6:0012 7503 JNZ 0017
1BB6:0014 83CA02 OR DX,+02
1BB6:0017 3C3F CMP AL,3F
1BB6:0019 7503 JNZ 001E
1BB6:001B 83CA04 OR DX,+04
1BB6:001E 0AC0 OR AL,AL
-

Es gibt natürlich noch andere Programme zum Disassemblieren ( CODEVIEW) die wesentlich
komfortabler sind als DEBUG. Diese Einführung erhebt keinen Anspruch auf vollständige Darstellung
aller Möglichkeiten und Sachverhalte. Sie soll lediglich einen Eindruck vom grundsätzlichen Vorgehen
vermitteln.

15
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Assembler I - Grundlagen 1. Teil


Assembler – Unterprogramme und Makros

peschko@aol.com 1
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1. Unterprogramme

In den bisher gezeigten Programmen wurde schon deutlich, dass manche Programmteile sich öfters
wiederholen oder mittels Schleifen mehrmals durchlaufen werden.

Bei größeren Programmen bedeutet dies zusätzliche Schreibarbeit. Außerdem werden solche
Programme schnell unübersichtlich, wenn nicht mehr klar ist in welcher Reihenfolge und in welchen
Fällen welche Sprünge ausgeführt werden. Selbst der ursprüngliche Programmierer tut sich
irgendwann schwer da noch durchzublicken. Es kommt der Zeitpunkt an dem der Programmier im
Programmcode nur noch planlos umherhüpft und sich in den endlosen Weiten des Programmcodes
verliert.

Man nennt diesen Programmierstiel dann "Spaghetti – Code".

1.1 Aufbau und Struktur von Unterprogrammen

Aus diesem Grund ist man auf die Idee gekommen, dass man die Programmteile welche öfters
durchlaufen werden einfach in ein eigenes Programm packt.

Dieses Unterprogramm wird dann einfach im Hauptprogramm an der benötigten Stelle aufgerufen.
Das Hauptprogramm wartet dann solange, bis das Unterprogramm seine Aufgabe durchgeführt hat
und fährt danach mit der Ausführung des restlichen Programmcodes fort.

Wie startet man nun ein solches Unterprogramm aus dem Hauptprogramm heraus ?

Die geschieht mit dem Befehl: CALL ( à rufen)

Nun muss man natürlich auch noch angeben welches Unterprogramm (Namen) man aufruft. Den
Namen kann man sich selbst auswählen. Der Befehl CALL darf nicht verändert werden.

Der Aufruf im Hauptprogramm sieht daher so aus: CALL PROGRAMMNAME

Dieses Unterprogramm befinden sich meist am Ende des Hauptprogramms. Der Assembler muss nun
aber auch erkennen können an welcher Stelle ein bestimmtes Unterprogramm beginnt und an welcher
Stelle es endet.

Ein Unterprogramm hat folgendes Grundgerüst:

PROGRAMMNAME PROC [NEAR][FAR] ← Anfang des Unterprogramms

.
.
Anweisungen
.
.
RET
PROGRAMMNAME ENDP ← Ende des Unterprogramms

Die Ausdrücke NEAR und FAR sind grün, weil wir uns für einen Ausdruck von beiden entscheiden
müssen. (Die eckigen Klammern werden nicht geschrieben !)

Es wird der Ausdruck NEAR gewählt, wenn sich das Unterprogramm mit dem Namen
PROGRAMMNAME im gleichen Segment wie sein Aufruf (CALL PROGRAMMNAME ) befindet.

Die RET – Anweisung zeigt dem Assembler an, an welcher Stelle wieder ins Hauptprogramm zurück
gesprungen wird.

Die PROGRAMMNAME ENDP – Anweisung markiert das Ende des Unterprogramms.

peschko@aol.com 2
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1.1.1 NEAR - Unterprogramme

Das Programm HALLO.EXE soll nun mittels eines Unterprogramms erstellt werden.
Wir schreiben den unteren Programmcode in eine Datei UPROG1.ASM

DATEN SEGMENT

MELDUNG DB "HALLO WELT !","$"

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX

CALL PRINT

MOV AH, 4CH


INT 21H

PRINT PROC NEAR

MOV AH, 09H


MOV DX, OFFSET MELDUNG
INT 21H

RET
PRINT ENDP

CODE ENDS
END START

Ja, ok…. Dieses Programm würde niemand mittels eines Unterprogramms schreiben. Es eignet sich
aber besonders um die Verwendung eines Unterprogramms zu zeigen.

Nachdem dieses Programm assembliert und gelinkt ist werden wir uns den Maschinencode mittels
DEBUG anschauen.

Damit ist es möglich uns ein Bild von der Wirkung der einzelnen Befehle zu machen.

peschko@aol.com 3
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Wir geben am DOS – Prompt ein:

C:>\ASM>DEBUG UPROG1.EXE
-u

…und wir sehen unser Programm im Maschinencode...

1EB8:0000 B8A71E MOV AX,1EA7 1. START


1EB8:0003 8ED8 MOV DS,AX 2.
1EB8:0005 E80400 CALL 000C 3.
1EB8:0008 B44C MOV AH,4C 8.
1EB8:000A CD21 INT 21 9. ENDE
1EB8:000C B409 MOV AH,09 4.
1EB8:000E BA0000 MOV DX,0000 5.
1EB8:0011 CD21 INT 21 6.
1EB8:0013 C3 RET 7.
1EB8:0014 83CA02 OR DX,+02 10.
1EB8:0017 3C3F CMP AL,3F 11.
1EB8:0019 7503 JNZ 001E .
1EB8:001B 83CA04 OR DX,+04 .
1EB8:001E 0AC0 OR AL,AL
-

Auch hier erkennt DEBUG nicht an welcher Stelle unser Programm endet. Der rote Teil ist nicht mehr
Bestandteil unseres Programms. Es ist zu erkennen, dass das Unterprogramm im selben Segment
steht wie das restliche Programm. Es ist also wirklich ein NEAR Sprung.

Am rechten Rand steht die Reihenfolge in der die einzelnen Programmzeilen ausgeführt werden. Es
ist zu erkennen, dass der CALL Befehl eigentlich ein Sprungbefehl ist. Die OFFSET – Adresse hinter
dem Sprungbefehl gibt das Sprungziel an. Die OFFSET – Adresse 000C hat bei uns den "Namen"
PRINT.

Durch den RET – Befehl wird wieder zurück gesprungen.

1.1.2 FAR – Unterprogramme

Es kommt relativ oft vor, dass Routinen mehrmals in einem Programm verwendet werden. So zum
Beispiel Routinen für das Ausgeben von Text, das Öffnen von Dateien, das Beenden von
Programmen usw.....

Das Hauptprogramm und die Standartroutinen werden in getrennte Dateien geschrieben. Auf diese
Weise kann man die einmal geschriebenen Standartroutinen immer wieder in jedes neue Programm
einbinden ( spart viel Tipperei J ).

Hinweis: Die Dateien DATEI_2.ASM und SUB_PROG.ASM müssen nicht vollständig neu abgetippt
werden. Wenn man DATEI_1.ASM hat kann man durch kleine Abänderungen und durch
das Abspeichern unter einem anderen Namen die oben genannten Dateien leicht erstellen.

peschko@aol.com 4
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Ein Beispiel:

Wir schreiben zunächst eine Datei DATEI_1.ASM

EXTRN PRINT:FAR
EXTRN MSDOS:FAR

DATEN SEGMENT

MELDUNG DB "Meldung aus Datei 1!","$"

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX
MOV DX, OFFSET MELDUNG

CALL PRINT

CALL MSDOS

CODE ENDS
END START

Danach wird eine Datei DATEI_2.ASM erstellt.

EXTRN PRINT:FAR
EXTRN MSDOS:FAR

DATEN SEGMENT

MELDUNG DB "Meldung aus Datei 2!","$"

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX
MOV DX, OFFSET MELDUNG

CALL PRINT

CALL MSDOS

CODE ENDS
END START

peschko@aol.com 5
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Zum Schluss eine Datei SUB_PROG.ASM

PUBLIC PRINT
PUBLIC MSDOS

DATEN SEGMENT

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: PRINT PROC FAR

MOV AH, 09H


INT 21H

RET
PRINT ENDP

MSDOS PROC FAR

MOV AH, 4CH


INT 21H

RET
MSDOS ENDP

CODE ENDS
END START

Nun wird jede Datei einzeln mit TASM assembliert:

C:\asm>TASM DATEI_1.ASM
Turbo Assembler Version 1.01 Copyright (c) 1988, 1989 Borland International

Assembling file: DATEI_1.ASM


Error messages: None
Warning messages: None
Remaining memory: 411k

C:\asm>TASM DATEI_2.ASM
Turbo Assembler Version 1.01 Copyright (c) 1988, 1989 Borland International

Assembling file: DATEI_2.ASM


Error messages: None
Warning messages: None
Remaining memory: 411k

C:\asm>TASM SUB_PROG.ASM
Turbo Assembler Version 1.01 Copyright (c) 1988, 1989 Borland International

Assembling file: SUB_PROG.ASM


Error messages: None
Warning messages: None
Remaining memory: 41
peschko@aol.com 6
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

C:\asm>_
Dann können die einzelnen Hauptprogramme DATEI_1.OBJ und DATEI_2.OBJ jeweils mit der
Unterprogrammdatei SUB_PROG.OBJ zusammengebunden werden.

Dies geschieht mittels:

C:\asm>TLINK DATEI_1.OBJ SUB_PROG.OBJ


Turbo Link Version 2.0 Copyright (c) 1987, 1988 Borland International

C:\asm>TLINK DATEI_2.OBJ SUB_PROG.OBJ


Turbo Link Version 2.0 Copyright (c) 1987, 1988 Borland International

C:\asm>_

Durch Eingabe von:


C:\asm>Datei_1

Sehen wir:
Meldung aus Datei 1!

Durch Eingabe von:


C:\asm>Datei_2

Sehen wir:
Meldung aus Datei 2!

Es tauchen nun in den Dateien DATEI_1.ASM und DATEI_2.ASM die Ausdrücke EXTRN und PUBLIC
auf.

In der Datei DATEI_1.ASM werden die Unterprogramme PRINT und MSDOS aufgerufen. Der
Assembler kann aber diesmal diese Unterprogramme in der Datei DATEI_1.ASM nicht finden ! Wenn
nun nicht die Hinweise EXTRN PRINT:FAR und EXTRN MSDOS:FAR in der Datei wären, würde der
Assembler eine Fehlermeldung ausgeben. Mit diesen Hinweisen wird dem Assembler mitgeteilt, dass diese
Unterprogramme mit den Namen PRINT und MSDOS eben außerhalb (extern) und im wahrsten Sinne weit weg
(far) von unserer Datei DATEI_1.ASM (nämlich in der Datei SUB_PROG.ASM ) existieren.

Der Assembler lässt also das Programm DATEI_1.OBJ lediglich zwei "Ösen" auswerfen, an denen dann der
Linker (TLINK) die Unterprogramme einklinken kann. Dazu muss der Assembler aber die Datei SUB_PROG.ASM
zwei "Haken" auslegen lassen. Damit der Assembler dies veranlasst braucht es die Ausdrücke PUBLIC PRINT
und PUBLIC MSDOS.

Das mit den Haken und Ösen wollen wir nun etwas genauer anschauen. Wir geben ein:

C:\asm>DEBUG DATEI_1.EXE
-u
1EC9:0000 B8A71E MOV AX,1EA7
1EC9:0003 8ED8 MOV DS,AX
1EC9:0005 BA0000 MOV DX,0000
1EC9:0008 9A0000CB1E CALL 1ECB:0000
1EC9:000D 9A0500CB1E CALL 1ECB:0005
1EC9:0012 0000 ADD [BX+SI],AL
1EC9:0014 0000 ADD [BX+SI],AL
1EC9:0016 0000 ADD [BX+SI],AL
1EC9:0018 0000 ADD [BX+SI],AL
1EC9:001A 0000 ADD [BX+SI],AL
1EC9:001C 0000 ADD [BX+SI],AL
1EC9:001E 0000 ADD [BX+SI],AL

Zunächst erkennen wir hier ( bei anderen Rechnern werden sich andere Segmentadressen ergeben ),
dass in den ersten zwei Zeilen die Adresse des Datensegments ( 1EA7 ) ins DS Register geschoben
wird. Für DX ist in der dritten Zeile die Offset – Adresse 0000 zu entnehmen. Dies bedeutet, dass
unsere Zeichenkette an Adresse 1EA7 : 0000 beginnt.
peschko@aol.com 7
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Wir suchen nun den String "Meldung aus Datei 1!" im Arbeitsspeicher auf. Dazu geben wir ein:

-E 1EA7:0000

und sehen zunächst das M von Meldung. Durch wiederholtes Drücken der Leerzeichentaste schauen
wir uns die ganze Zeichenkette an.

-E 1EA7:0000
1EA7:0000 4D. 65. 6C. 64. 75. 6E. 67. 20.
1EA7:0008 61. 75. 73. 20. 44. 61. 74. 65.
1EA7:0010 69. 20. 31. 21. 24.

Die 24 ist wieder das $ - Zeichen, welches das Ende des Strings kennzeichnet. Als nächstes wollen
wir uns das Unterprogramm anschauen, welches den String ausgibt. Dazu geben wir ein:

-u 1ECB:0000

Wir sehen:

1ECB:0000 B409 MOV AH,09


1ECB:0002 CD21 INT 21
1ECB:0004 CB RETF
1ECB:0005 B44C MOV AH,4C
1ECB:0007 CD21 INT 21
1ECB:0009 CB RETF

Wir sehen nun auch an Adresse 1ECB:0005 den Beginn des Unterprogramms, welches nach Ablauf
unseres Gesamtprogramms die Kontrolle an das Betriebssystem zurück gibt. Ebenfalls sehen wir nun
auch, dass wir ein Unterprogramm vom Typ FAR benötigen. Der CALL sitzt im Segment 1EC9. Das
aufgerufene Unterprogramm sitzt im Segment 1ECB. Beide Segmente sind unterschiedlich !

2. Makros

Makros haben einen ähnlichen Aufbau wie Unterprogramme. Die Wirkung im Quellcode ist jedoch
eine völlig andere. Ein Unterprogramm wird nur einmal in den Arbeitsspeicher geschrieben. Dann wird
von verschiedenen Stellen des Hauptprogramms zu diesem Unterprogramm gesprungen.

Der Assembler ersetzt beim Assemblieren den Aufruf des Makros einfach durch den Inhalt des
Makro ! Dies bedeutet, dass durch einen Makroaufruf kein Sprung entsteht. Wenn wir zum Beispiel ein
Makro dreimal hintereinander aufrufen wird der Inhalt dieses Makro im Quellcode lediglich dreimal
hintereinander gehängt.

Ein Mako hat folgenden Aufbau:

MAKRONAME MACRO VARIABLE1_DES _QUELLCODES, …

.
.
Befehle Makrokörper
.
.
ENDM

peschko@aol.com 8
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Wie veranlasst man nun den Assembler den Makrokörper in den Quellcode einzufügen ?

Dies gelingt mit dem Ausdruck:

MAKRONAME VARIABLE1_DES_MAKRO, …

Neu sind die Zusätze "VARIABLE1_DES_MAKRO, …" und " VARIABLE1_DES _QUELLCODES,..." .
Mit diesem Zusatz erfährt der Assembler, dass die beiden unterschiedlichen Ausdrücke das gleiche
meinen und austauschbar sind.

Wie der Name Variable schon vermuten lässt, werden über diese Ausdrücke Zahlenwerte
ausgetauscht. Es können mehrere Variablen hintereinander geschrieben werden.

Ein Beispiel:

Wir erstellen zuerst eine Makro – Bibliothek. Zu diesem Zweck erstellen wir mit dem DOS- Editor die
Datei MACRO.BIB und schreiben folgende Makros :

PRINT MACRO STRING

MOV AH, 09H


MOV DX, OFFSET STRING
INT 21H

ENDM

MSDOS MACRO

MOV AH, 4CH


INT 21H

ENDM

Der Assembler muss nun diese Datei nach den Makros durchsuchen, die im Hauptprogramm
verwendet werden. Damit er das tut, muss im Hauptprogramm auf diese Datei hingewiesen werden.
Dies geschieht mit dem Ausdruck INCLUDE.

peschko@aol.com 9
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Jetzt wird das Hauptprogramm DATEI_3.ASM erstellt.

INCLUDE MACRO.BIB

DATEN SEGMENT

MELDUNG DB "Meldung aus Datei 3!","$"

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX

PRINT MELDUNG

MSDOS

CODE ENDS
END START

Der Assembler setzt hier die beiden Ausdrücke MELDUNG und STRING gleich. Es wird die
Offset – Adresse der Zeichenkette MELDUNG übergeben.

Zum Assemblieren geben wir ein:

C:\asm>TASM DATEI_3.ASM

Wir sehen:
Turbo Assembler Version 1.01 Copyright (c) 1988, 1989 Borland International

Assembling file: DATEI_3.ASM


Error messages: None
Warning messages: None
Remaining memory: 411k

Dann wird eingegeben:

C:\asm>TLINK DATEI_3.OBJ

Wir sehen:
Turbo Link Version 2.0 Copyright (c) 1987, 1988 Borland International
C:\asm>_

peschko@aol.com 10
Assembler - Grundlagen 1. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Wir schauen uns das Programm mit DEBUG an.

Wir geben ein:

C:\asm>DEBUG DATEI_3.EXE
-u

Wir sehen:

1EB9:0000 B8A71E MOV AX,1EA7


1EB9:0003 8ED8 MOV DS,AX
1EB9:0005 B409 MOV AH,09
1EB9:0007 BA0000 MOV DX,0000
1EB9:000A CD21 INT 21
1EB9:000C B44C MOV AH,4C
1EB9:000E CD21 INT 21

Man kann hier nicht erkennen, dass dieses Programm mittels Makros erstellt wurde.

Fazit: Programme werden mittels Unterprogrammen geschrieben, wenn sie möglichst klein sein
sollen.
Für den Fall, dass jedoch die Geschwindigkeit der Programme im Vordergrund steht, bedient
man sich lieber Makros.

peschko@aol.com 11
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Assembler I - Grundlagen 2. Teil


Assembler – Adressierung und Speichermodelle

peschko@aol.com 1
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1. Adressierung

Im letzten Teil dieses Kapitels werden wir uns mit der direkten Adressierung und der indirekten
Adressierung beschäftigen.

Ich denke ein anschauliches Beispiel verdeutlicht auch hier den Unterschied. Jeder hat diese Situation
schon erlebt. Man hat es eilig und jemand quatscht einen auf der Strasse an: " Äh, wissen Sie wo das
Kino ist ?".
Jetzt gibt es die Möglichkeit, dass man es weiß und dem Ratsuchenden sagt: " Ja, da gehen Sie jetzt
da hoch und dann oben rechts. Dann stehen Sie genau davor." Das wäre die direkte Adressierung.

Wenn man es nicht weiß könnte man sagen: "Hm, ich weis nicht wo das Kino ist. Aber drei Straßen
weiter auf der linken Seite ist ein Taxistand. Der kann Ihnen sagen wo Sie das Kino finden." Das wäre
die indirekte Adressierung. Man hat einen Ort angegeben an dem man die eigentliche Adresse findet.

1.1 Direkte Adressierung

Bei diesem Speicherzugriff folgt dem Operationsteil die effektive Adresse.

Operation Adresse

Speicherstelle

Zunächst ist ein generelles Problem zu beachten:

Die Konventionen zur Adressierung werden zum Teil lasch gehandhabt. Das hat zur Folge, dass es in
manchen Fällen zu mehrdeutigen oder missverständlichen Ausdrücken kommt.

Beispiel:

Es wird eine Variable VAR1 definiert und mit dem Wert 0 belegt.

VAR1 DW 0

Danach wird folgende Operation durchgeführt:

MOV AX, VAR1

Diese Anweisungen führen bei manchen Assemblern zu einer Warnmeldung. Es ist nicht ganz klar
was gemeint ist. Man kann der Ansicht sein, dass die Zahl 0 in das Register AX zu kopieren ist. Jetzt
wissen wir aber auch, dass der Ausdruck VAR1 genauso als Bezeichner für die Speicherstelle
verwendet werden kann. Nehmen wir einmal an, dass besagte Speicherstelle im Segment die Offset –
Adresse 50H hat. Dann könnte man auch der Ansicht sein, dass VAR1 ein Bezeichner ist der für die
Zahl 50H steht.

Im ersten Fall würde in AX eine 0 stehen. Im zweiten Fall würden wir in AX die Zahl 50H vorfinden.
Wie schon gesagt wollen uns manche Assembler diese Entscheidung nicht abnehmen und verlangen
Eindeutigkeit.

peschko@aol.com 2
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1. Fall: Es soll die 0 nach AX kopiert werden:

MOV AX, [VAR1]

Hier steht VAR1 für die Adresse der Speicherstelle . Die eckigen Klammern geben nun an, dass der
INHALT von dieser Speicherstelle nach AX zu kopieren ist.

2. Fall: Es soll die Offset – Adresse der Speicherstelle nach AX kopiert werden:

MOV AX, OFFSET VAR1

Hier wurde das AX Register direkt angesprochen. Es ist auch möglich den Inhalt einer bestimmten
Speicherstelle direkt auszulesen. Zum Beispiel mit

MOV AX, [0040:0923]

Übrigens:
Man kann auch einen CALL direkt adressieren:
Einen NEAR CALL mit: CALL NEAR PTR SEGMENT:OFFSET
Einen FAR CALL mit: CALL FAR PTR SEGMENT:OFFSET

peschko@aol.com 3
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Beispiel:

DATEN1 SEGMENT

MELDUNG1 DB "HALLO WELT 1!",10,13,"$"

DATEN1 ENDS

DATEN2 SEGMENT

MELDUNG2 DB "HALLO WELT 2!",10,13,"$"

DATEN2 ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

CODE2 SEGMENT

ASSUME CS:CODE2

DRUCK PROC FAR

MOV AH, 09H


INT 21H

RETF
DRUCK ENDP

CODE2 ENDS

CODE1 SEGMENT

ASSUME CS:CODE1,SS:STAPEL,ES:NOTHING

START: MOV DX, OFFSET DATEN1:MELDUNG1


MOV AX, DATEN1
MOV DS, AX
CALL FAR PTR CODE2:DRUCK

MOV DX, OFFSET DATEN2:MELDUNG2


MOV AX, DATEN2
MOV DS, AX
CALL FAR PTR CODE2:DRUCK

MOV AH, 4CH


INT 21H

CODE1 ENDS
END START

Es ist nun (nach dem Assemblieren und Linken ) möglich mit

C:\asm>DEBUG DIREKT.EXE

und anschließender Eingabe des Parameters t durch das Programm klicken.. äh ENTERn ;-)
( Wie schon gesagt, bei einem INT 21H - Befehl sollte mit einem p weiter geschaltet werden...)

peschko@aol.com 4
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Auf meinem Rechner ergibt sich unter anderem...

C:\asm>debug direkt.exe
-t
AX=0000 BX=0000 CX=014E DX=0000 SP=0100 BP=0000 SI=0000 DI=0000
DS=1B95 ES=1B95 SS=1BA7 CS=1BB8 IP=0003 NV UP EI PL NZ NA PO NC
1BB8:0003 B8A51B MOV AX,1BA5
-t
AX=1BA5 BX=0000 CX=014E DX=0000 SP=0100 BP=0000 SI=0000 DI=0000
DS=1B95 ES=1B95 SS=1BA7 CS=1BB8 IP=0006 NV UP EI PL NZ NA PO NC
1BB8:0006 8ED8 MOV DS,AX
-t
AX=1BA5 BX=0000 CX=014E DX=0000 SP=0100 BP=0000 SI=0000 DI=0000
DS=1BA5 ES=1B95 SS=1BA7 CS=1BB8 IP=0008 NV UP EI PL NZ NA PO NC
1BB8:0008 9A0000B71B CALL 1BB7:0000
-t
AX=1BA5 BX=0000 CX=014E DX=0000 SP=00FC BP=0000 SI=0000 DI=0000
DS=1BA5 ES=1B95 SS=1BA7 CS=1BB7 IP=0000 NV UP EI PL NZ NA PO NC
1BB7:0000 B409 MOV AH,09
-t

Es ist zu erkennen, dass bei mir ( bei Dir mit Sicherheit nicht ) der Bezeichner CODE1 für die
Segment – Adresse 1BB8 steht. Im Instruction Pointer steht immer schon die Offset – Adresse des
nächsten Befehls.
Zunächst das Übliche: Die Adresse des Strings "HALLO WELT 1!" wird geladen. Der CALL – Befehl
kommt uns auch nicht mehr ganz neu vor. Wir sehen, dass die Sprungadresse DIREKT angegeben
ist. Was aber dem ungeübten Auge nicht auffällt ist, dass der Stack Pointer ( Pfeile ) um 4 Bytes
( Speicherplätze ) gewachsen ist ( ja, gewachsen. Wie gesagt: Der Stack wächst von oben nach
unten. ).

Nun, da wir einen FAR – CALL haben springen wir zu einem Speicherplatz in ein anderes Segment
mit einem anderen Offset. Dies bedeutet: Wir müssen zwei Adressen speichern. Denn der Prozessor
will ja beim RETF – Befehl wissen wohin er zurückspringen soll !
Jede Adresse benötigt 2 Byte.

Mit der Segmentadresse 1BB7 befinden wir uns im Segment CODE2 . Hier ist das Unterprogramm,
welches mit der Anweisung MOV AH, 09H beginnt.

AX=09A5 BX=0000 CX=014E DX=0000 SP=00FC BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA7 CS=1BB7 IP=0002 NV UP EI PL NZ NA PO NC
1BB7:0002 CD21 INT 21
-p
HALLO WELT 1!

AX=0924 BX=0000 CX=014E DX=0000 SP=00FC BP=0000 SI=0000 DI=0000


DS=1BA5 ES=1B95 SS=1BA7 CS=1BB7 IP=0004 NV UP EI PL NZ NA PO NC
1BB7:0004 CB RETF
-t
AX=0924 BX=0000 CX=014E DX=0000 SP=0100 BP=0000 SI=0000 DI=0000
DS=1BA5 ES=1B95 SS=1BA7 CS=1BB8 IP=000D NV UP EI PL NZ NA PO NC
1BB8:000D BA0000 MOV DX,0000
-t

Mit dem RETF endet das Unterprogramm. Es ist zu sehen, dass in das Segment CODE1 gesprungen
wird. Hierzu wird die Rücksprungadresse benötigt. Die wird vom Stack genommen. Dadurch
schrumpft der Stack wieder um 4 Bytes.

peschko@aol.com 5
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Wir haben hier als Beispiel den Prozessor ohne "Taxistand" direkt zum Unterprogramm geschickt.

DATEN1:0000

DATEN2:0000

STAPEL:0000

CODE2:0000

CODE1:0000

1.2 Indirekte Adressierung

Bei dieser Art der Adressierung kann man im wahrsten Sinne des Wortes alle Register ziehen. Auch
hier gibt es zunächst ein paar kleine Gemeinheiten.

Es gibt viele "Orte" an denen man im PC Speicheradressen verstauen kann. Beim 8086 gab es bereits
17 Möglichkeiten der Adressierung.

1.2.1 Register – Adressierung

Hier wird mittels eines Registers adressiert. Dies bedeutet: Wir schauen in ein Register um dort eine
Adressangabe zu finden. Danach suchen wir den Speicherplatz, der die angegebenen Adresse hat
auf. In diesem Speicherplatz ist die eigentliche Information die wir suchen. Wir können hier aber auch
Daten ablegen.

Operation Name des Registers

Speicherstelle
Inhalt: Adresse der
Inhalt: Info
Speicherstelle

Register

peschko@aol.com 6
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Beispiel:

MOV [BX], DX

Dies bedeutet: Schreibe den Inhalt des Registers DX an die Adresse im Arbeitsspeicher, die in BX
angegeben ist.

MOV AX, [BX]

Dies bedeutet: Lies den Inhalt der Adresse, die in BX angegeben ist. Schiebe diesen Inhalt in das
Register AX.

Jetzt die Fallen:

1. Falle:

Wir wollen die Zahl 10 an eine Speicherstelle schreiben deren Offset - Adresse ( Segmentadresse
liegt, sofern nicht durch ASSUME geändert, in DS vor ) in BX angegeben ist.

Also: MOV [BX], 10

Der Assembler macht jetzt "Hä ??" und gibt eine Fehlermeldung aus. Warum ? Die Zahl 10
ist binär 1010. Uns ist klar: 1010 braucht 4 Bit. Da eine Speicherstelle 8 Bit hat, passt unsere Zahl 10
wunderbar in eine Speicherstelle rein. Manchen Assemblern ist das aber nicht klar ! Denn es könnte
sich ja auch um den Ausdruck 0000 0000 0000 1010 ( Word ) handeln.

Wir müssen darauf hinweisen, dass wir 0000 1010 meinen:

MOV [BYTE PTR BX], 10

Manche werden sich nun fragen: Warum funktioniert dann MOV [BX], DX . Ja, hier haben wir es nur
mit Registern zu tun. Und dass diese beiden Register jeweils eine Breite von 16 Bit haben rafft der
Assembler dann doch.

2. Falle:

Hier tappen viele Anfänger rein:

MOV DL, BX

Wir schreiben Daten mit einer Länge von 16 Bit an einen Ort, der nur 8 Bit groß ist. Oh, Oh.....

peschko@aol.com 7
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1.2.2 Basis-relative – Adressierung

Hier wird zu einer in einem Register gespeicherten Adresse eine Zahl addiert.
Die Summe aus Adresse und Konstante ergibt die Adresse der gesuchten Speicherstelle.

Operation Name des Registers

Inhalt: Adresse Speicherstelle


+ Konstante = Inhalt: Info

Register

MOV [BX + 10H], DX

Die Adresse der Speicherstelle an der der Inhalt des Registers DX gespeichert wird ist wie folgt
bestimmt: Man nimmt den Inhalt des Registers BX als Adresse und addiert die hexadezimale
Zahl 10H zu dieser Adresse hinzu. Damit erhält man die Adresse der gesuchten Speicherstelle.

Dieser Ausdruck kann auch auf andere Weise dargestellt werden:

MOV [BX]10H, DX

Diese Befehlsform ist gleichbedeutend wie die obere Schreibweise.

1.2.3 Basis-indizierte Adressierung

Hier muss der Inhalt von Register1 und Register2 addiert werden um die Adresse der gesuchten
Speicherstelle zu erhalten .

Operation Name des Registers 1 Name des Register 2

Inhalt des Register 1 + Inhalt des Register 2 Speicherstelle


Inhalt: Info

MOV AL, [BX+SI]

Beim 8086 sind folgende Kombinationen erlaubt:

BX+SI
BX+DI
BP+SI
BP+DI

Ab dem 80386 sind auch alle Kombinationen der 32 – Bit Register ( außer ESP ) erlaubt.

peschko@aol.com 8
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

1.2.4 Basis-indizierte Adressierung mit Displacement

Die Adressbildung ist hier folgende:

Es wird der Inhalt des ersten Registers zum Inhalt des zweiten Registers und dazu noch eine
Konstante addiert. Das Ergebnis ergibt die Adresse der gewünschten Speicherstelle.

Operation Name des Registers 1 Name des Register 2

Inhalt des Register 1 + Inhalt des Register 2 + Konstante Speicherstelle


Inhalt: Info

MOV [BX+SI+80H], DX

MOV LISTE[BX][DI], AL Liste ist eine Variable

1.3 Zusammenfassung zur Adressierung

Man erkennt, dass alle bisher behandelten Adressierungsarten Spezialfälle der basis-indizierten
Adressierung sind.

Da unsere Programme auch auf jeder alten Krücke laufen sollen, werde ich hier nur die 8086
Konventionen darstellen:

Mit folgendem Schema kann man sich eine Eselsbrücke bauen:

[Basisregister] [Indexregister] Konstante

BX SI

oder + oder + Konstante

BP DI

Somit ergeben sich folgende gültigen Kombinationen:

[BX] [BP]
[SI] [DI]
[BX+SI] [BP+SI]
[BX+DI] [BP+DI]
[BX+Konstante] [BP+Konstante]
[BX+SI+Konstante] [BP+SI+Konstante]
[BX+DI+Konstante] [BP+DI+Konstante]
[SI+Konstante] [DI+Konstante]
[Konstante]

peschko@aol.com 9
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

2. Speichermodelle

Alle Programme bisher wurden mit Hilfe der direkten Segmentierung erstellt. Jedes Segment wurde
mit der Anweisung:

NAME SEMGMENT

NAME ENDS

erzeugt.

Darüber hinaus mussten von uns auch immer mittels der Zeile

ASSUME CS:CODE, DS:DATEN,ES:NOTHING,SS:STAPEL usw. …..

die Segmentregister geladen werden.

Zusätzlich war noch das Daten-Segment zu initialisieren :

MOV AX, DATEN


MOV DS, AX

Nicht zu vergessen ist auch die Startmarke und Endmarke

START:

END START

Wichtig war auch, dass am Ende des Programms immer das Betriebssystem die Kontrolle erhält:

MOV AH, 4CH


INT 21H

Diese Einzelschritte können "automatisiert" werden, da sie bei jedem Programm auftreten.

Zuerst wenden wir uns der Segmentierung durch den Assembler zu:
Mit der direkten Segmentierung konnten wir die Abfolge der Segmente, die Anzahl der Segmente
sowie ihre Größe beeinflussen.
Dies kann auch der Assembler für uns machen. Die Auswahl ist dabei aber auf einige Typen
beschränkt. Diese Typen nennt man Modelle.

peschko@aol.com 10
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Im Angebot sind die Typen :

TINY Programm-Code und Daten müssen in ein 64 KByte – Segment passen ! Code und
Daten sind NEAR.

SMALL Programm-Code und Daten müssen in je ein 64 KByte – Segment passen ! Code und
Daten sind NEAR.

MEDIUM Programm-Code kann größer als 64 KByte sein; Daten müssen in ein 64 KByte-
Segment passen. Code ist FAR, Daten sind NEAR.

COMPACT Programm-Code muss in ein 64 KByte-Segment passen; Daten können größer als
64 KByte sein, aber kein Datenbereich darf für sich allein größer als 64 KByte sein.
Daten und Code sind FAR.

LARGE Programm-Code und Daten können beide größer als 64 KByte sein, aber kein
Datenbereich darf für sich allein größer als 64 KByte sein. Daten und Code sind FAR.

HUGE Programm-Code und Daten können beide größer als 64 KByte sein, auch einzelne
Datenbereiche dürfen größer als 64 KByte sein. Daten und Code sind FAR.

Ein solches "Grundgerüst" wird durch dir Direktive

.MODEL Typ

ausgewählt. Bei soviel Auswahl hat man die Qual der Wahl. Auch hier gibt es eine einfache
Faustregel:
Verwende ein möglichst einfaches ( weit oben stehendes ) Modell.

Der entstehende Maschinencode ist somit schneller und auch einfacher zu analysieren.

Trotzdem müssen wir nicht jegliche Möglichkeit zur Einflussnahme aus der Hand geben. Wir können
dem Assembler auch bei Modellen sagen, welcher Programmteil in welchen Segmenten abgelegt
werden soll. Es gibt hier die Direktiven:

.STACK Zahl

Mit dieser Direktive wird der STACK angelegt. Diese Direktive ersetzt sozusagen den Ausdruck:

STAPEL SEGMENT BYTE STACK

DW 128 DUP(0)

STAPEL ENDS

Mit Zahl kann man den zu reservierenden Speicher festlegen.

.DATA

Damit wird das Daten - Segment angelegt.

.CODE [Name]

Damit wird das Code – Segment angelegt. Bei den Modellen MEDIUM, LARGE und HUGE können in
einem Assembler – Programm mehrere Code – Segmente vorkommen. Um diese unterscheiden zu
können, müssen sie mit einem Namen versehen werden.

peschko@aol.com 11
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Damit wirklich kein Zweifel über den Programmanfang besteht, wird nach der .CODE Anweisung noch
die Anweisung

.STARTUP

gesetzt.

Diese kleine Anweisung ersetzt :

ASSUME CS:CODE,DS:DATEN,ES:NOTHING, SS:STAPEL

MOV DX, DATEN


MOV DS, DX

START:

Wichtig ist natürlich auch der Programmabschluss.

Mit

.EXIT

werden die Befehle

MOV AH, 4CH


INT 21H

ersetzt. Zum Schluss nicht das

END

vergessen. Dies entspricht dem

END START

Damit würde unser Programm HALLO.ASM nun so aussehen:

.MODEL SMALL

.DATA

MELDUNG DB "Hallo Welt!","$"

.STACK

.CODE

.STARTUP

MOV DX, OFFSET MELDUNG


MOV AH, 09H
INT 21H

.EXIT

END

peschko@aol.com 12
Assembler I - Grundlagen 2. Teil (Beta 1)
Copyright 2000 Thomas Peschko

Achtung: Die Segmentierung über Modelle ist immer eine Quelle von endlosen Problemen und viel
Ärger ! Die Programmierung über Modelle wird nicht von allen Versionsnummern
von TASM unterstützt. Auch sind die Konventionen bei MASM (von Microsoft ) anders.
Das Beispielprogramm oben wurde mit

Turbo Assembler Version 4.0 Copyright (c) 1988, 1993 Borland International

assembliert.

Dies hier soll lediglich eine Einführung sein, die erste Gehversuche ermöglichen soll.

Noch ein Tipp: Es gibt den Borland Turbo – Assembler 4.0 auch auf CD. Inbegriffen ist da ein
Online – Handbuch und Anschauungsprogramme. Zu beziehen über den Buchhandel.
Verlag: Franzis

peschko@aol.com 13
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Assembler II - DOS
ASSEMBLER – Arbeiten mit Dateien und Daten

peschko@aol.com 1
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Wer nun den Eindruck hat, dass unsere Programme hauptsächlich nur Unterprogramme vor ihren
Karren spannen und sich darauf beschränken den "Laden" zu managen, liegt damit nicht ganz falsch.

Die Schwierigkeit beim Programmieren besteht hauptsächlich darin zu wissen, welche INTERRUPT-
Routine was macht und in welchen Registern sie Eingabewerte erwartet und in welchen Registern sie
Werte zurück gibt (auch hier gilt: "Wissen ist Macht !") .

1. Dateien

Da wir uns nicht unnötig Schreibarbeit aufhalsen wollen, öffnen wir einfach wieder unser altes
HALLO.ASM. Zunächst ändern wir es wie folgt ab. Danach speichern wir es einfach unter dem neuen
Namen DATEI.ASM.

; ********************************************
;* Programm 2 *
;*********************************************

DATEN SEGMENT

MELDUNG DB "Es wird eine Datei erstellt !“, "$“


DATEINAME DB "NEU_DAT.DAT",0
DATEIINHALT DB "Dies wird in die Datei geschrieben.","$"

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP (?)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX

MOV DX, OFFSET MELDUNG


MOV AH, 9H
INT 21H

MOV AH, 3CH ; Datei erstellen und öffnen.


MOV DX, OFFSET DATEINAME
MOV CX, 00H ; Dateiart
INT 21H

PUSH AX ; Dateinummer auf STACK auslagern.


MOV AH, 40H
MOV DX, OFFSET DATEIINHALT
MOV CX, 23H ; Anzahl der geschriebene Bytes
POP BX ; Dateinummer zurückholen
INT 21H

MOV AH, 3EH ; Datei schließen


INT 21H

MOV AH, 4CH ; MS-DOS


INT 21H

CODE ENDS

END START

peschko@aol.com 2
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Es kommt wieder das INTERRUPT Unterprogramm "Nr. 21" zum Einsatz. Wieder wird zunächst ein
String ausgegeben. Im nächsten Block wird die Funktionsnummer 3CH in das Register AH geladen.
Dadurch wird das Unterprogramm veranlasst eine Datei zu erstellen und diese zu öffnen.
Zur Erstellung der Datei benötigt das Unterprogramm den Namen der Datei als Zeichenkette.
Das Unterprogramm des Betriebssystems erwartet nun im DX Register die Adresse des ersten
Speicherplatzes der Zeichenkette als Offset- Adresse. Im Register CX muss eine Zahl angegeben
werden, an der das Unterprogramm die Art der gewünschten Datei erkennt. Nach dem alle Register
mit den vom Unterprogramm verlangten Informationen geladen sind wird das Unterprogramm mit INT
21H aufgerufen.

Da mehrere Dateien gleichzeitig geöffnet sein können, lädt das Unterprogramm nach dem Erstellen
und Öffnen der Datei eine Identifikationsnummer in das Register AH. Damit kann die Datei auch von
anderen Programmen angesprochen werden. Der alte Wert 3CH im Register AH wird dadurch
überschrieben.

Als nächster Schritt muss unbedingt diese Identifikationsnummer gerettet werden. Denn der Befehl
MOV AH, 40H würde nun wieder diese Identifikationsnummer durch Überschreiben vernichten. Jetzt
kommt zum Erstenmal der STACK ins Spiel. Mit dem Befehl PUSH AX wird nun der Inhalt des
Registers AX in dem reservierten Speicherbereich STACK abgelegt. Es wird immer die letzte freie
Speicherstelle im STACK von einem PUSH Befehl hierfür eingesetzt.

Nun kann in das Register AH der Wert 40H geladen werden. Damit erfährt das Unterprogramm, dass
in eine Datei geschrieben werden soll. Zum Schreiben in eine Datei benötigt das Unterprogramm den
Text als Zeichenkette. Das Unterprogramm des Betriebssystems erwartet nun im DX Register die
Adresse des ersten Speicherplatzes der Zeichenkette als Offset- Adresse.
Im Register CX muss eine Zahl angegeben werden, an der das Unterprogramm die Anzahl der zu
schreibenden Bytes erkennt. Hierbei ist zu beachten, dass ein Zeichen (Buchstabe) 1 Byte benötigt.
Im BX Register verlangt das Unterprogramm nun die Identifikationsnummer der Datei, in die
geschrieben werden soll. Zum Glück haben wir diese ja auf dem STACK gespeichert. Mit dem Befehl
POP holen wir den Inhalt des letzten belegten Speicherplatzes auf dem STACK in das Register BX
zurück. Da zwischenzeitlich keine weiteren Werte auf diesen STACK geladen wurden, lädt der Befehl
POP die Identifikationsnummer der Datei in das Register BX. Jetzt kann wieder das Unterprogramm
"Nr. 21H" aufgerufen werden, das die Zeichenkette von DATEIINHALT in diese Datei schreibt.

Im vorletzten Block wird nun die Datei wieder geschlossen. Dies ist wichtig, da sonst die Datei nicht
von anderen Programmen bearbeitet werden kann. Hierzu wird in das Register AH der Wert 3EH
geladen. Im Register BX wartet das Unterprogramm wieder die Identifikationsnummer der zu
schließenden Datei. Diese Nummer befindet sich bereits im Register BX, da wir sie schon dorthin
geladen und zwischenzeitlich nicht überschrieben haben. Nach dem alle Register mit den vom
Unterprogramm verlangten Informationen geladen sind wird das Unterprogramm mit INT 21H
aufgerufen.

Im letzten Block wird nun wieder die Kontrolle an das Betriebssystem zurückgegeben.

Nun müssen wir den Quellcode assemblieren und linken. Nach einem Doppelklick auf das Programm
DATEI.EXE sollte nun die Meldung „Es wird eine Datei erstellt !“ auf dem Bildschirm erscheinen.
Zusätzlich sollte auch eine Datei mit dem Namen NEU_DAT.DAT erstellt worden sein. Wenn man
diese Datei mit dem EDIT Befehl öffnet, sollte man den Text „ Dies wird in die Datei geschrieben“
lesen können.

Dieses Programm hat primär dazu gedient die elementaren Funktionen der Dateihandhabung zu
demonstrieren.

peschko@aol.com 3
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Als nächstes soll nun ein Programm 3 erstellt werden, das den Inhalt einer Textdatei mit dem Namen
QUELLE.TXT verschlüsselt und in einer zweiten Datei mit dem Namen ENCRYPT.TXT verschlüsselt
abspeichert. Zu diesem Zweck wird zu jedem ASCII Code eines Buchstabens bzw. eines Zeichens die
Zahl 2 addiert. Dies bedeutet zum Beispiel, dass für ein a im Text ein c dargestellt wird. Da ein a den
ASCII Code 61 hat wird daraus ein c (ASCII Code 63), wenn wir zum ASCII Code des Buchstaben a
die Zahl 2 addieren.

Diese Art der Verschlüsselung ist natürlich primitiv, aber man kann den Algorithmus auch beliebig
kompliziert gestalten. Auf jeden Fall kann man auf diese Weise das Programmieren üben. Da wir uns
abermals nicht unnötig Schreibarbeit aufhalsen wollen, öffnen wir nochmals unser altes HALLO.ASM.
Zunächst ändern wir es wie folgt ab. Danach speichern wir es einfach unter dem neuen
Namen VERSCHL.ASM ab.

;****************************************
;*Programm 3 *
;****************************************

DATEN SEGMENT

MELDUNG DB "Dieses Programm verschlüsselt eine Datei","$"


DATEINAME_QUELLE DB "QUELLE.TXT",0
DATEINAME_ENCRYPT DB "ENCRYPT.TXT",0
PUFFER DW 1 DUP (0)
HANDLE_OF_QUELLE DW 1 DUP (0)
HANDLE_OF_ENCRYPT DW 1 DUP (0)

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP (?)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX

MOV DX, OFFSET MELDUNG


MOV AH, 9H
INT 21H

MOV AH, 3CH


MOV CX, 00H
MOV DX, OFFSET DATEINAME_ENCRYPT
INT 21H
MOV HANDLE_OF_ENCRYPT, AX

MOV AH, 3DH


MOV AL, 0H
MOV DX, OFFSET DATEINAME_QUELLE
INT 21H
MOV HANDLE_OF_QUELLE, AX

M1: MOV AH, 3FH


MOV CX, 01H
MOV DX, OFFSET PUFFER
MOV BX, HANDLE_OF_QUELLE
INT 21H

CMP AX, 0H
JE ENDE

peschko@aol.com 4
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

ADD [PUFFER], 2H

MOV AH, 40H


MOV CX, 2H
MOV BX, HANDLE_OF_ENCRYPT
MOV DX, OFFSET PUFFER
INT 21H

JMP M1

ENDE: MOV AH, 3EH


MOV BX, HANDLE_OF_QUELLE
INT 21H

MOV AH, 3EH


MOV BX, HANDLE_OF_ENCRYPT
INT 21H

MOV AH, 4CH


INT 21H

CODE ENDS

END START

Jetzt wird der Quellcode assembliert und gelinkt. Fertig ist das Programm !

"Und ? Wie es funktioniert?..... " ;-)

Ganz einfach ! Wir erstellen zuerst eine Datei QUELLE.TXT in dem selben Ordner in dem sich unser
Programm VERSCHL.EXE befindet.
In diese Datei schreiben wir mittels des DOS – Editor nun etwas beliebiges rein.
Zum Beispiel: Das ist streng geheim ! ;-)

Dann speichern wir den Text ab und schließen die Datei QUELLE.TXT

Als nächstes doppelklicken wir auf unser Programm VERSCHL.EXE , welches wir ja schon
geschrieben und assembliert haben.

Jetzt befindet sich eine neue Datei mit dem Namen ENCRYPT.TXT im Ordner.

Wenn wir nun diese Datei mit dem DOS – Editor öffnen sehen wir Smilies, Herzchen, Karos usw....
Tja, das ist der Text, aber verschlüsselt...

Was ist passiert?

Das Programm sollte bis zur Marke M1: klar sein !


Danach wird es interessant. Mit

MOV AH, 3FH

ordnen wir nun an, dass aus der Datei dessen Identifikationsnummer in BX mittels

MOV BX, HANDLE_OF_QUELLE

geparkt wird genau

MOV CX, 01H

1 Byte ( 01H [exadezimal] = 1 dezimal!) gelesen werden soll.


Dieses Byte wird im Datensegment ab der Speicherstelle mit dem "Namen" PUFFER abgelegt.

MOV DX, OFFSET PUFFER

peschko@aol.com 5
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Hmm… 1 Byte sind 8 Bit ! Wir belegen also eigentlich nur 8 Bit im Arbeitsspeicher.

Warum haben wir dann mit der Anweisung

PUFFER DW 1 DUP (0)

genau 1 Wort = 2 Byte = 16 Bit reserviert und diese mit Nullen vorbelegt ???

Schauen wir uns einmal an wie es weiter geht.

Im Register AX wird die Anzahl der gelesen Byte zurückgegeben. Wenn wir nun die Zahl 0 mit der
Zahl in AX vergleichen und es ergibt sich Gleichheit, dann wissen wir, dass in AX die Zahl 0 steht !

Dies geschieht mittels

CMP AX, 0H

CMP = CoMPare ( vergleiche)

Der folgende Befehl bezieht sich nun hierauf. Wenn in AX eine 0 steht, dann wird alles bis zur Marke
ENDE übersprungen und die Programmausführung hinter der Marke ENDE : fortgesetzt.

Dies geschieht mittels

JE ENDE

JE = Jump if Equal ( springe wenn gleich)

à Das bedeutet es wird erst nach ENDE : gesprungen, wenn es nichts mehr zu lesen gab !

Zu schwierig ?....

Wenn es aber etwas zu lesen gab, dann steht das Zeichen ( besser gesagt sein ASCII Code ) jetzt in
der Speicherstelle mit dem "Namen" PUFFER.

Nehmen wir einmal an, dass wir das Zeichen mit dem höchsten ASCII Code eingelesen hätten.
Dieses Zeichen hat die Codezahl 255.

Das ist binär... Na, wissen wir es ????

genau ! à 1 1 1 1 1 1 1 1 Diese binäre Zahl braucht ganz genau 8 Bit also 1 Byte !

Jetzt addieren wir aber zu dem Inhalt des Puffers einfach die Zahl 2 mittels der Anweisung

ADD [PUFFER], 2H

Was geschieht dabei ??? (Jetzt kommt der Hard – Part)

1111 1111
+ 10

10000 0001

peschko@aol.com 6
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Oh, Oh … winke, winke …

Die grüne 1 ginge im Puffer verloren, wenn wir nur 1 Byte reserviert hätten ! J
War also doch ganz gut mit dem

PUFFER DW 1 DUP (0)

Damit steht nun im Puffer:

0000 0001 0000 0001

Wenn wir dadurch nun den ASCII Code des Zeichens im Puffer völlig entstellt... äh, ich meine
verschlüsselt haben, können wir nun den neuen Inhalt des Puffers in unsere Datei ENCRYP.TXT
übertragen.
Dies geschieht durch die Befehlsfolgen...

MOV AH, 40H


MOV CX, 2H
MOV BX, HANDLE_OF_ENCRYPT
MOV DX, OFFSET PUFFER
INT 21H

Danach springen wir auf jeden Fall wieder nach oben zur Marke M1:......

JMP M1

JMP = JuMP (springe)

...und das nächste Zeichen ist an der Reihe....bis kein Zeichen mehr da ist.

Dann werden mit den Befehlzeilen...

ENDE: MOV AH, 3EH


MOV BX, HANDLE_OF_QUELLE
INT 21H

MOV AH, 3EH


MOV BX, HANDLE_OF_ENCRYPT
INT 21H

MOV AH, 4CH


INT 21H

...alle Dateien geschlossen und das Programm beendet.

Hinweis: Es besteht ein wichtiger Unterschied zwischen dem Ausdruck


ADD [PUFFER], 2H
und dem Ausdruck
ADD PUFFER, 2H

Im ersten Fall wird zum INHALT der Speicherstelle PUFFER die Zahl 2 addiert. Im zweiten
Fall wird zur OFFSET – ADRESSE der Speicherstelle mit dem "Namen" PUFFER die
Zahl 2 addiert !

peschko@aol.com 7
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

Das nachfolgende Programm 4 liest den verschlüsselten Text aus der Datei ENCRYPT.TXT wieder
ein und wandelt es um in unseren ursprünglichen Text um ihn dann in die Datei DECRYPT.TXT zu
schreiben.

;****************************************
;*Programm 4 *
;****************************************

DATEN SEGMENT

MELDUNG DB "Dieses Programm entschlüsselt eine Datei","$"


DATEINAME_ENCRYPT DB "ENCRYPT.TXT",0
DATEINAME_DECRYPT DB "DECRYPT.TXT",0
PUFFER DW 1 DUP (0)
HANDLE_OF_ENCRYPT DW 1 DUP (0)
HANDLE_OF_DECRYPT DW 1 DUP (0)

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP (?)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX

MOV DX, OFFSET MELDUNG


MOV AH, 9H
INT 21H

MOV AH, 3CH


MOV CX, 00H
MOV DX, OFFSET DATEINAME_ENCRYPT
INT 21H
MOV HANDLE_OF_DECRYPT, AX

MOV AH, 3DH


MOV AL, 0H
MOV DX, OFFSET DATEINAME_DECRYPT
INT 21H
MOV HANDLE_OF_ENCRYPT, AX

M1: MOV AH, 3FH


MOV CX, 02H
MOV DX, OFFSET PUFFER
MOV BX, HANDLE_OF_ENCRYPT
INT 21H

CMP AX, 0H
JE ENDE

SUB [PUFFER], 2H

MOV AH, 40H


MOV CX, 1H
MOV BX, HANDLE_OF_DECRYPT
MOV DX, OFFSET PUFFER
INT 21H

JMP M1

ENDE: MOV AH, 3EH

peschko@aol.com 8
Assembler – DOS (Beta 1)
Copyright 2000 Thomas Peschko

MOV BX, HANDLE_OF_DECRYPT


INT 21H

MOV AH, 3EH


MOV BX, HANDLE_OF_ENCRYPT
INT 21H

MOV AH, 4CH


INT 21H

CODE ENDS

END START

Zum Abschluss dieses Kapitels soll noch gezeigt werden wie ein Programm "Selbstmord" begeht. Ein
Programm kann sich auch selbst löschen. Diese Methode eignet sich jedoch nur begrenzt zur
Vertuschung.
;****************************************
;*Datei löscht sich selbst *
;****************************************

DATEN SEGMENT

MELDUNG DB "Dieses Programm hat sich selbst gelöscht","$"


DATEINAME DB "SUID.EXE",0

DATEN ENDS

STAPEL SEGMENT BYTE STACK

DW 128 DUP (?)

STAPEL ENDS

CODE SEGMENT

ASSUME CS:CODE,DS:DATEN,ES:NOTHING,SS:STAPEL

START: MOV AX, DATEN


MOV DS, AX
MOV DX, OFFSET MELDUNG
MOV AH, 9H
INT 21H

MOV AH, 41H


MOV DX, OFFSET DATEINAME
INT 21H

MOV AH, 4CH


INT 21H

CODE ENDS

END START

Dieser Quellcode muss, wie man am Programm sieht, als SUID.ASM abgespeichert werden.

Die Löschung wird durch die Funktion 41H (Delete Directory Entry) bewirkt.

peschko@aol.com 9
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Assembler II – DOS Teil 2


ASSEMBLER – Weitere Eigenschaften von Dateien

peschko@aol.com 1
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

1. Operatoren, Befehle und Anweisungen

Die Grundlagen sind nun weitestgehend erledigt. Wir könnten nun alle Tore und Türen zur ultimativen
Programmierung aufstoßen. Ja, ja... wären da nicht noch folgende Probleme:

Es fehlt eine umfassende Referenz der

ü Prozessorbefehle
ü Operatoren
ü Assembler – Anweisungen
ü DOS – Interrupte

Ein Operator wirkt immer auf einen oder mehrere Operanden. Beim Ausdruck

3–2

ist das Minuszeichen der Operator. Der Ausdruck besitzt den Wert 1. Weitere Operatoren in
Assembler sind:

DUP, FAR, NEAR, XOR….

Ein Prozessorbefehl ist ein Mnemonic. Ein Ausdruck, der den Prozessor aktiv werden lässt:

MOV, JE, INT....

Eine Assembler – Anweisung gibt dem Assembler Anweisungen in welcher Weise die
Assemblierung des Quellcodes zu erfolgen hat.

INCLUDE, GOTO, .MODEL, ....

Da ich überhaupt keine Lust habe hier einen einige MB großen Upload mit Operatoren,
Prozessorbefehle und Anweisungen zu machen, werde ich hier einen Buchtipp geben:

Die meisten Autoren geben leider nicht genau an für welchen Assembler ihr Buch geschrieben ist.
Es gibt verschiedene Hersteller von Assemblern sowie unterschiedliche Versionsnummern. In dieser
Hinsicht herrscht ein babylonisches Sprachengewirr unter den Assemblerkonventionen.
Eine angenehme Ausnahme macht da die:

Assembler Referenz
von
Oliver Müller

Verlag: Franzis

Dieses Buch hat zwar auch fast 600 Seiten, ist aber trotzdem keine "Schwarte". Es enthält eigentlich
alle Informationen die man braucht ( incl. Beispielen ).

peschko@aol.com 2
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

2. Einige Dateien wird es hart treffen !

In diesem Tutorial möchte ich die allseits beliebte ;-) Funktion "Find First" von MS DOS vorstellen.
Manchmal hat man das Problem, dass man etwas sucht aber einfach nicht weiß wie es heißt und ob
es so etwas überhaupt gibt. Wenn sich dieses Problem auf Dateien bezieht, kann die Funktion "Find
First" eine wertvolle Hilfestellung geben.
Ich denke ein Beispiel veranschaulicht die Aufgabenstellung:

Es soll ein Programm erstellt werden, das Textdateien mit beliebigem Namen findet. Dieses
Programm soll die erste gefundene Textdatei löschen. Diese Textdatei soll auch gefunden und
gelöscht werden, wenn sie die Dateiattribute "versteckt" und "schreibgeschützt" besitzt.

Zuerst das Programm, dann die Erklärung:

.MODEL SMALL

.DATA

MELDUNG DB "Programm S_and_D.EXE sucht nach Textdateien",10,13,"$"


NICHTS_DA DB "Keine Textdatei gefunden!",10,13,"$"
NAMENSTEIL_DES_OPFERS DB "*.TXT",0
VOLLZUG DB "Eine Textdatei wurde gelöscht!",10,13,"$"

.STACK

.CODE
.STARTUP

MOV DX, OFFSET MELDUNG ;Dumm-User informieren.


MOV AH, 09H
INT 21H

MOV AH, 4EH ;Erstes Opfer finden...


MOV DX, OFFSET NAMENSTEIL_DES_OPFERS ;...mit folgendem Namensteil..
MOV CX, 07H ;...auch wenn es sich versteckt.
INT 21H ;Suchen...

JNC DTA_HOLEN ;Springe nach DTA_HOLEN falls ein Opfer


;gefunden wurde...
MOV DX, OFFSET NICHTS_DA ;Melden, dass kein Opfer vorhanden ist...
MOV AH, 09H
INT 21H

JMP ENDE ;..und zum Programmende springen.

DTA_HOLEN:

MOV AH, 2FH ;Die Startadresse der DTA des


INT 21H ;ersten gefundenen Opfers besorgen...->ES:DX

PUSH DS ;Datensegmentadresse auf STACK retten..


MOV AX, ES ;Segmentadresse umladen von ES nach...
MOV DS, AX ;...DS
MOV DX, BX ;Offsetadresse umladen von BX nach DX..
ADD DX, 1EH ;DX soll die Anfangsadresse vom Namens des Opers haben.
MOV AH, 41H ;Opfer soll geloescht werden...
INT 21H ;..ausfuehren..

POP DS ;Datensegmentadresse zurueck holen...


MOV DX, OFFSET VOLLZUG
MOV AH, 09H
INT 21H

ENDE:
.EXIT
END

peschko@aol.com 3
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Das obere Programm hat die einfache Funktion "search and destroy" realisiert.

Hier die Erklärung:


Die Frage ist nun: Was bewirkt "Find First" und welche Registerwerte werden erwartet ?

Hier hat sich die Interruptliste von Ralf Brown als sehr nützlich erwiesen. Hier werden unter anderem die
Interrupts von MS DOS aufgelistet. Da diese Liste sehr umfangreich ist, bietet er auf seiner Homepage auch ein
Programm als Hilfsmittel zur Betrachtung der Liste an.
Suchabfrage

Die Funktion FIND FIRST MATCHING FILE wird über den Wert 4EH im Register AH mittels des Interrupts 21H
aufgerufen. Die Anfangsadresse der Zeichenkette, welche den Namen bzw. Namensteil der zu suchenden Datei
im Arbeitsspeicher darstellt, muss mittels DS:DX adressiert werden. Der Ausdruck "*.TXT",0 bedeutet, dass die
Endung der gesuchten Datei auf jeden Fall TXT sein muss. Der vordere Teil kann beliebig sein. Dies bewirkt,
dass diese Funktion nur Textdateien sucht.

Als Rückgabewert wird das Carry – Flag (CF) zurückgesetzt, wenn eine entsprechende Datei gefunden wurde
( clear if successful ). Sollte keine entsprechende Datei gefunden worden sein, so wird das Carry – Flag gesetzt.

Im Register CX werden die Attribute der Datei als Maske erwartet. Es wird auf die Funktion 4301H zur Erklärung
der Maske verwiesen. Dort finden wir den Aufbau der Maske:

Bitfields for file attributes:


Bit(s) Description (Table 0765)
7 shareable (Novell NetWare)
6 unused
5 archive
4 directory
3 volume label
execute-only (Novell NetWare)
2 system
1 hidden
0 read-only

Hier finden wir den Grund warum wir in das Register CX die Zahl 7 kopieren. Die Zahl 7 ist als binäre
Zahl 00000111. Das bedeutet, dass die gesuchte Datei eine Systemdatei oder versteckt sowie auch
schreibgeschützt sein kann.

Damit ergibt sich:


peschko@aol.com 4
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

MOV AH, 4EH ;Erstes Opfer finden...


MOV DX, OFFSET NAMENSTEIL_DES_OPFERS ;...mit folgendem Namensteil..
MOV CX, 07H ;...auch wenn es sich versteckt.
INT 21H

Sollte diese Suche von Erfolg sein, wird das Carry – Flag NICHT gesetzt (à CF =0). Hierauf reagiert der Befehl
JNC DTA_HOLEN . Jump if Not Carry. Es wird im Fall von CF = 0 zur Marke DTA_HOLEN weiter unten
gesprungen.

Nun interessiert uns natürlich der Name der gefundenen Datei. Den vollständigen Namen benötigen wir, damit wir
die Datei umnieten können. Jede Datei hat einen "Personlanausweis" . In diesem Vorspann steht zum Beispiel
das Datum sowie die Uhrzeit der Erstellung der Datei.. uvm. Ganz am Ende dieses Vorspanns steht auch der
Name der Datei. Dieser Vorspann heißt DISK TRANSFER AREA (DTA). Sein Aufbau ist bei der Funktion
FINDFIRST aufgeführt.
.
. ---all versions, documented fields---
15h BYTE attribute of file found
16h WORD file time (see #1005 at AX=5700h)
18h WORD file date (see #1006 at AX=5700h)
1Ah DWORD file size
1Eh 13 BYTEs ASCIZ filename+extension

Die Frage ist nur: Wie kommen wir an diese DTA ran ?

Zur Umsetzung dieses niederträchtigen Ansinnens bedienen wir uns der Funktion GET DISK TRANSFER AREA
ADDRESS . Und ? Was sehen wir da ?

INT 21 - DOS 2+ - GET DISK TRANSFER AREA ADDRESS


AH = 2Fh
Return: ES:BX -> current DTA
Note: under the FlashTek X-32 DOS extender, the pointer is in ES:EBX
SeeAlso: AH=1Ah

Nach Ausführung dieser Funktion befindet sich die Startadresse der DTA in ES:BX. Wenn wir nun anfangen von
der Adresse ES:BX einzulesen bekommen wir:

00h BYTE drive letter (bits 0-6), remote if bit 7 set


oder so was…das interessiert uns nicht!

Was wir wollen ist aber:

1Eh 13 BYTEs ASCIZ filename+extension

Daraus folgt: Wir müssen zu BX noch 1EH Speicherstellen addieren, damit in BX die Adresse des
Speicherplatzes steht, der den Anfang des Dateinamens enthält.

Jetzt erwartet aber die Funktion Delete Directory Entry die Startadresse des Speicherplatzes mit dem
Namen der zu löschenden Datei in DS:DX. Bevor wir den oben erwähnten Korrekturzug ausführen
verschieben wir ES:BX nach DS:DX.

MOV AX, ES ;Segmentadresse umladen von ES nach...


MOV DS, AX ;...DS
MOV DX, BX ;Offsetadresse umladen von BX nach DX..

Und dann der Korrekturzug:

ADD DX, 1EH

Da wir zum Abschluss den User über seinen Verlust in Kenntnis setzen wollen, müssen wir die Segmentadresse
des Datensegments vor dem Überschreiben durch MOV DS, AX retten. Dies geschieht mittels PUSH DS. Vor
der Ausgabe der Kondolenzmeldung muss dieser Wert mit POP DS wieder nach DS kopiert werden.

3. Das Manipulieren des Änderungsdatums einer Datei

peschko@aol.com 5
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Wird der Inhalt einer Datei geändert und anschließend die Datei wieder abgespeichert, so wird das
Änderungsdatum der Datei aktualisiert. Diese Aktualisierung nimmt das Betriebssystem vor. Wir können das
aber auch selbst in die Hand nehmen !

Das folgende Programm hat eigentlich keinen großen praktischen Nähwert. Es ist vermutlich leichter die
Systemzeit am Computer zurückzustellen, als dieses Programm zu schreiben. Es ermöglicht jedoch einen
wertvollen Einblick in „Zeit- und Datumsmechanik“ einer Datei.

Von Virenprogrammieren wird immer dieses Datum und die Uhrzeit der letzten Änderung gesichert. Nach der
Infektion wird dieses Datum inklusive Uhrzeit wieder hergestellt. Die Datei soll ja nicht als letztes
Änderungsdatum das Datum und die Uhrzeit der Infektion tragen !

Hier das Programm SETDATE.ASM :

--------------------------------------------------------

INCLUDE MACRO.BIB

.MODEL SMALL

.DATA

BEGRUESSUNG DB "Programm ändert das Datum einer Datei!",10,13,"$"


DATUM DB "Neues Datum eingeben: ",10,13,"$"
DATEI DB "TEST.TXT",0
ZAHL DB 0
ZEICHEN DB 0
TAG DW 0
MONAT DW 0
JAHRE DW 0
REGISTER_DX DW 0

.STACK 128

.CODE
.STARTUP

PRINT BEGRUESSUNG

PRINT DATUM

INPUT_INT ZAHL

MOV AL, [ZAHL]

MOV BL, 0AH

MUL BL

PUSH AX

INPUT_INT ZAHL

POP BX

ADD BL, [ZAHL]

MOV [TAG], BX

INPUT_CHAR ZEICHEN

INPUT_INT ZAHL

MOV AL, [ZAHL]

MOV BL, 0AH


peschko@aol.com 6
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

MUL BL

PUSH AX

INPUT_INT ZAHL

POP BX

ADD BL, [ZAHL]

MOV [MONAT], BX

INPUT_CHAR ZEICHEN

INPUT_INT ZAHL

MOV AL, [ZAHL]

MOV BX, 3E8H

MUL BX

PUSH AX

INPUT_INT ZAHL

MOV AL, [ZAHL]

MOV BX, 64H

MUL BX

PUSH AX

INPUT_INT ZAHL

MOV AL, [ZAHL]

MOV BL, 0AH

MUL BL

PUSH AX

INPUT_INT ZAHL

MOV AL, [ZAHL]

POP BX ; Zehner

ADD AX, BX

POP BX ; Hunderter

ADD AX, BX

POP BX ; Tausender

ADD AX, BX

MOV BX, 07BCH

SUB AX, BX

MOV [JAHRE], AX

peschko@aol.com 7
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

SHL [MONAT], 05H

SHL [JAHRE], 09H

MOV DX, 0H

OR DX, [TAG]

OR DX, [MONAT]

OR DX, [JAHRE]

MOV [REGISTER_DX], DX

MOV AH, 3DH

MOV DX, OFFSET DATEI

MOV AL, 0H

INT 21H

MOV BX, AX

MOV AX, 5700H

INT 21H

MOV AX, 5701H

MOV DX, [REGISTER_DX]

INT 21H

MOV AH, 3EH

INT 21H

.EXIT
END

Hier noch die Datei MACRO.BIB :


peschko@aol.com 8
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

--------------------------------------------------------------

PRINT MACRO STRING

MOV DX, OFFSET STRING


MOV AH, 09H
INT 21H

ENDM

INPUT_INT MACRO ZAHL

MOV AH, 01H


INT 21H
MOV AH, 0H
SUB AL, 30H
MOV ZAHL, AL

ENDM

INPUT_CHAR MACRO ZEICHEN

MOV AH, 01H


INT 21H
MOV ZEICHEN, AL
ENDM

---------------------------------------------------------

Die Dateien SETDATE.ASM und MACRO.BIB müssen sich wieder im gleichen Verzeichnis befinden.

Nach dem Assemblieren mit...

C:\asm\Projekt\date>tasm setdate.asm
Turbo Assembler Version 4.0 Copyright (c) 1988, 1993 Borland International

Assembling file: setdate.asm


Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 409k

C:\asm\Projekt\date>tlink setdate.obj
Turbo Link Version 6.00 Copyright (c) 1992, 1993 Borland International

.... kann das Programm SETDATE.EXE gestartet werden. Das Datum muss in der Form TT.MM.JJJJ
eingegeben werden. Zum Beispiel 07.08.1999. Ein Datum vor dem 01.01.1980 ist nicht möglich.

Geändert wird das Änderungsdatum einer Datei mit dem Namen TEST.TXT, welche zuvor im gleichen
Verzeichnis mit dem Editor erstellt werden muss.

Wenden wir uns der Erklärung zu.


peschko@aol.com 9
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Zum Eintragen eines Änderungsdatums einer Datei gibt es folgende DOS Funktion:

Änderungsdatum und Uhrzeit einer Datei holen:

Aufruf: AH = 57H
AL = 00H à Datum und Zeit holen.

BX = Dateinummer (à Handle)

Ergebnis: CF = 0 Alles OK
CX = Uhrzeit
DX = Datum

CF = 1 Fehler

Änderungsdatum und Uhrzeit einer Datei setzen:

Aufruf: AH = 57H
AL = 01H à Datum und Zeit setzen.

BX = Dateinummer (à Handle)

Ergebnis: CF = 0 Alles OK
CX = neue Uhrzeit
DX = neues Datum

CF = 1 Fehler

Wenn wir nun ein neues Datum eingeben wollen müssen wir dies über die Tastatur tun. Hierfür gibt es
die DOS Funktion :

Lesen eines an der Tastatur eingegebenen Zeichens und Anzeige:

Aufruf: AH = 01H

Ergebnis: AL = ASCII Wert der betätigten Taste !!!!!

1. Problem:

Nun, wenn wir die Taste „1“ auf der Tastatur drücken ist AL nicht etwa 1 ( AL = 0000 0001 ), sondern
es befindet sich in AL der ASCII Code der Ziffer 1. Da dies die hexadezimale Zahl 31 ist, befindet sich
in AL nun folgendes Bitmuster à AL = 0001 1111. Damit in AL wirklich eine 1 steht muss jetzt vom
Inhalt des Registers AL noch hexdezimal 30 abgezogen werden.

Genauer gesagt: Es kann mit dieser Funktion z. B. gar nicht die Zahl 12 eingelesen werden. Man kann
lediglich beim ersten Aufruf der Funktion die Ziffer 1 und beim zweiten Aufruf der Funktion die Ziffer 2
mittels ihres jeweiligen ASCII Codes einlesen. Danach müssen die beiden Ziffern wieder zur Zahl 12
zusammengebaut werden.

2. Problem:
peschko@aol.com 10
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Die Funktion 57H erwartet das neue Datum im Register DX. Dabei gilt folgende Aufteilung:

Register DX:

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

0 0 1 0 0 1 1 1 1 0 0 1 0 1 0 1

Im grünen Bereich wird die


Zahl für den jeweiligen Tag
des Datum gespeichert.
à 0 bis 31.

Im roten Bereich wird die


Zahl für den jeweiligen Monat
des Datum gespeichert.
à 1 bis 12

Im blauen Bereich wird


die Anzahl der Jahre seit
1980 gespeichert !

Dies bedeutet : Wenn wir das Datum 21.12.1999 setzen wollen, muss dazu obiges Bitmuster im
Register DX stehen.

Die Frage ist nur: Wie bekommen wir dieses Bitmuster mittels der Tastatur da rein ???

INPUT_INT ZAHL
MOV AL, [ZAHL]

Zunächst wird das Makro INPUT_INT aufgerufen. Innerhalb des Makros geschieht nun folgendes:

Mit
MOV AH, 01H
wird ein Zeichen von der Tastatur eingelesen. Der ASCII Code des Zeichens landet in AL. Danach wird mit
SUB AL, 30H
aus dem ASCII Code der Wert der Ziffer extrahiert. Da AL nur der niedrige Teil von AX ist, wird noch mit
MOV AH, 0H
nach dem Ausführen der Funktion 01H das Register AH auf Null gesetzt.
Wenn die Taste „2“ gedrückt wurde gilt: AX = 00 02
Dieses Ergebnis wird mittels
MOV ZAHL, AL
von AL in die Variable ZAHL kopiert. Der Befehl MUL verlangt aber einen Operanden in AL. Deshalb wird mit
MOV AL, [ZAHL]
Die Zahl (unnötigerweise) wieder nach AL zurückkopiert.

Die Ziffer “2” stellt aber die Zehnerstelle der Zahl 21 dar. Deshalb wird nun nach BL die Zahl 10 geschrieben.
MOV BL, 0AH
Anschließend wird der Inhalt von AL (à 2) mittel des Befehls
MUL BL
mit 10 multipliziert.

peschko@aol.com 11
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Nun befindet sich im Register AL die Zahl 20. Diese Zahl wird mit
PUSH AX
auf dem Stack abgelegt.

Jetzt lesen wir mit erneutem Aufruf des Makros


INPUT_INT ZAHL
Die Zahl „1“ nach AL ein. Diese Ziffer muss nicht mehr mit einem Faktor multipliziert werden , da sie
die Einerstelle der Zahl 21 darstellt. Mit
POP BX
Wird nun die Zahl 20 vom Stack zurück in das Register BL geholt. Anschließend wird mit
ADD BL, [ZAHL]
zu dem Inhalt des Registers BL (à 20) der Inhalt der Variable ZAHL (à 1) addiert.
Das Ergebnis landet in BL.
Nun befindet sich in BL das Bitmuster BL = 0001 0101, was der Zahl 21 entspricht.
Zum Abschluss wird diese Zahl noch mit
MOV [TAG], BX
in der Variablen TAG abgelegt.
Nach der Eingabe des Tages folgt die Eingabe eines Punktes. Dieser wird mittels des Makros
INPUT_CHAR ZEICHEN
zwar eingelesen aber nicht ausgewertet, da er uns nicht interessiert.

Zum Einlesen des Monats wiederholen sich nun diese Arbeitsschritte.

In ähnlicher Weise wird in einem dritten Arbeitsschritt zunächst die Tausenderstelle der Jahreszahl
eingelesen und mit der Zahl 1000 multipliziert bevor sie auf dem Stack zwischengelagert wird.

Die gleichen Arbeitschritte fallen für die Hunderter-, Zehner- und Einerstelle der Jahreszahl an.

1999 à 1 * 1000 = 1000


+ 9 * 100 = 900
+ 9 * 10 = 90
+ 9= 9

Anschließend befindet sich im Register AX die binäre Jahreszahl. Für Computer hat die Geburt Jesu
keinerlei Bedeutung. Für Computer ist die Erfindung von MS DOS bedeutungsvoll. Für Computer
leben wir also sozusagen im Jahre 20 nach Gates.

Aus diesem Grund muss nun von der Jahrezahl immer 1980 abgezogen werden. Dies passiert mittels
MOV BX, 07BCH
SUB AX, BX

Nun verfügen wir über die nötigen Daten, um das Register DX füttern zu können. Das Problem ist,
dass die einzelnen Daten in einer bestimmten Abfolge im Register DX stehen müssen.

peschko@aol.com 12
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Die Daten stehen jetzt noch in den Variablenfelder TAG, MONAT, JAHRE:

0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1

0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0

0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1

Man kann erkennen, dass die Zahl im Feld MONAT um 5 Stellen nach links verschoben werden muss,
damit sie ihre richtige Position erreicht.

Die geschieht mit dem Befehl


SHL [MONAT], 05H

Auch die Anzahl der Jahre muss verschoben werden. Hier sind 9 Stellen erforderlich.
SHL [JAHRE], 09H

Danach sehen die Speicherplätze folgendermaßen aus:

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0

Danach wird jeder Speicherplatz mit dem DX Register ODER verknüpft. Dies geschieht durch die
Befehle
OR DX, [TAG]
OR DX, [MONAT]
OR DX, [JAHRE]

Dies bewirkt, dass ein Bit an einer bestimmten Stelle im DX Register dann gesetzt wird, wenn
mindestens ein Bit an der gleichen Stelle in einem der drei Variablenfelder gesetzt wurde.

Daraus folgt für das Register DX:

Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 1 0 0 1 1 1 1 0 0 1 0 1 0 1

Dieser Wert wird im Speicherplatz REGISTER_DX geparkt.

peschko@aol.com 13
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

Man kann jetzt aber noch nicht die Funktion AX = 5701H aufrufen. Denn...

1. Wir brauchen noch ein Datei- Handle.


2. Wir haben keinen Wert für die Uhrzeit im CX Register.

Das Datei – Handle besorgen wir uns durch das Öffnen der Datei. Was die Uhrzeit betrifft
übernehmen wir einfach die alte Uhrzeit der Datei durch Aufruf der Funktion AX = 5700H.

Jetzt kopieren wir das gewünschte Datum in das Register DX und rufen die Funktion AX = 5701H auf.

Zum Abschluss muss die geöffnete Datei wieder geschlossen werden.

4. Manipulationen von Dateieigenschaften mittels DTA

Im letzten Programm dieses Kapitels sollen die Namen sowie die Dateiattribute zweier Dateien
ausgetauscht werden.

Zu diesem Zweck assemblieren wir das folgende Listing XCHANGE.ASM und legen im gleichen
Verzeichnis mittels des Windows Explorers zwei Textdateien mit den Namen ERSTE_DATEI.TXT und
ZWEITE_DATEI.TXT an. In die erste Datei schreiben wir „Dies ist Datei 1“und in die zweite Datei den
Text “Dies ist Datei 2“.
Bei der Datei ERSTE_DATEI.TXT klicken wir alle Dateiattribute ( versteckt, schreibgeschützt, Archiv )
an. Bei der Datei ZWEITE_DATEI.TXT deaktivieren wir alle Eigenschaften.

Nach Ausführen des Programms wurde nicht der Inhalt der Dateien ausgetauscht ! Es wurden
lediglich die „Personalausweise“ der Dateien ausgetauscht. Der Text „Dies ist Datei 1“ steht immer
noch in der ersten Datei. Die erste Datei hat jetzt jedoch den Namen ZWEITE_DATEI.TXT und besitzt
auch deren Attribute.

.MODEL SMALL

.DATA

START DB "Programm vertauscht Identität zweier Dateien !",10,13,"$"


DATEI_1 DB "ERSTE*.TXT",0
DATEI_2 DB "ZWEITE*.TXT",0
MERKER_1 DB "DUMMI_1.DAT",0
MERKER_2 DB "DUMMI_2.DAT",0
FEHLER_1 DB "Erste Datei nicht gefunden !",10,13,"$"
FEHLER_2 DB "Zweite Datei nicht gefunden !",10,13,"$"
VOLLZUG DB "Dateinamen wurden in den Puffer geschrieben !",10,13,"$"
NAME_1 DB 13 DUP (0)
NAME_2 DB 13 DUP (0)

.STACK 128

.CODE
.STARTUP

MOV AH, 09H


MOV DX, OFFSET START
INT 21H

MOV AH, 4EH


MOV CX, 07H
MOV DX, OFFSET DATEI_1
INT 21H

JNC WEITER_1
peschko@aol.com 14
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

MOV AH, 09H


MOV DX, OFFSET FEHLER_1
INT 21H

JMP ENDE

WEITER_1:

MOV AH, 2FH


INT 21H

ADD BX, 1EH

PUSH DS
PUSH DS
MOV AX, ES
MOV DS, AX
MOV SI, BX

POP ES
MOV DI, OFFSET NAME_1

MOV CX, 0DH


REP MOVSB

MOV AX, DS
MOV ES, AX
POP DS

MOV AH, 4EH


MOV CX, 07H
MOV DX, OFFSET DATEI_2
INT 21H

JNC WEITER_2

MOV AH, 09H


MOV DX, OFFSET FEHLER_2
INT 21H

JMP ENDE

WEITER_2:

MOV AH, 2FH


INT 21H

ADD BX,1EH

PUSH DS
PUSH DS
MOV AX, ES
MOV DS, AX
MOV SI, BX

POP ES
MOV DI, OFFSET NAME_2

MOV CX, 0DH


REP MOVSB
peschko@aol.com 15
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

MOV AX, DS
MOV ES, AX
POP DS

MOV AH, 09H


MOV DX, OFFSET VOLLZUG
INT 21H

MOV AX, 4300H


MOV DX, OFFSET NAME_1
INT 21H

MOV BX, CX

MOV AX, 4300H


MOV DX, OFFSET NAME_2
INT 21H

MOV AX, 4301H


MOV DX, OFFSET NAME_1
INT 21H

MOV CX, BX

MOV AX, 4301H


MOV DX, OFFSET NAME_2
INT 21H

MOV DX, OFFSET NAME_1


PUSH ES
MOV AX, DS
MOV ES, AX
MOV DI, OFFSET MERKER_1
MOV AH, 56H
INT 21H
POP ES

MOV DX, OFFSET NAME_2


PUSH ES
MOV AX, DS
MOV ES, AX
MOV DI, OFFSET MERKER_2
MOV AH, 56H
INT 21H
POP ES

MOV DX, OFFSET MERKER_1


PUSH ES
MOV AX, DS
MOV ES, AX
MOV DI, OFFSET NAME_2
MOV AH, 56H
INT 21H
POP ES

MOV DX, OFFSET MERKER_2


PUSH ES
MOV AX, DS
MOV ES, AX
MOV DI, OFFSET NAME_1
peschko@aol.com 16
Assembler – DOS Teil 2 (Beta 1)
Copyright 2000 Thomas Peschko

MOV AH, 56H


INT 21H
POP ES

ENDE:
.EXIT
END

peschko@aol.com 17

Das könnte Ihnen auch gefallen