Assemblerkurs, Teil 1: Überblick

Einleitung

Im Normalfall ist die Assemblerprogrammierung heute nicht mehr ganz zeitgemäß und kommerziell sicher nur noch in Spezialfällen rentabel. Als Kunde hat man sich schließlich an ständig zunehmende Programmgrößen gewöhnt, ja erwartet sie sogar. Wir Portfolio-Anwender wollen uns freilich eine derartige Verschwendungssucht nicht leisten und hier bietet sich die Assemblerprogrammierung zur Erstellung effizienter Programme an. Dieser Kurs soll zunächst eine allgemeine Einführung in diese "Programmiersprache" geben und später portfoliospezifische Lösungen vorstellen. Da viele Leser bereits mit Pascal oder Basic vertraut sind, werden gewisse Programmiererfahrungen vorausgesetzt. Andererseits wird sich sicher die eine oder andere Stelle finden, die nicht jeder kritischen Prüfung standhält.

Doch jetzt zur Sache. Bekanntlich tut im Portfolio ein Intel-Prozessor namens 8088 seinen Dienst. Das Faszinierende an dieser 1979 mit dem 8086 (16-Bit-Datenbus, sonst wie 8088) eingeführten Architektur ist, daß sie uns bis in heutige PCs verfolgt. Wenn man aus einem Pentium-PC das Letzte herausholen möchte, ist deshalb auch heute das Erlernen der 8086-Maschinensprache durchaus noch aktuell.

Maschinensprache und Assembler

Jedes ausführbare Programm besteht letztlich aus einer Aneinanderreihung von Maschinenbefehlen, wobei ein Maschinenbefehl je nach Zahl der Operanden zwischen einem und sechs Bytes lang ist. Man kann ein Programm also als Folge von Byte-Werten (0...255) darstellen, vorzugsweise im hexadezimalen Zahlenformat, weil man dann mit 2 Ziffern pro Byte auskommt. Folgende 15 Bytes sind z.B. der Maschinencode für ein Programm, das einen kurzen Soundeffekt auf dem Portfolio erzeugt:

    BA 20 80 31 C0 EE 48 75 FC B8 80 4C EE CD 21

In den Zeiten als die Programme noch kurz, Disketten Luxus und Mailboxen die Domäne von Hackern waren, wurden Listings ja gern in so einer Form gedruckt. Zur Programmentwicklung ist diese Darstellung jedoch denkbar ungeeignet. Um sich nicht für jeden Maschinenbefehl eine Zahl merken zu müssen, bedient man sich einer anderen Schreibweise. Für alle Maschinenbefehle des Prozessors existieren eingängige Abkürzungen, die meist aus drei Buchstaben bestehen und Mnemonics genannt werden. Ein Assemblerquelltext besteht hauptsächlich aus diesen Mnemonics und den zum jeweiligen Maschinenbefehl gehörenden Parametern. Dabei beginnt man für jeden Befehl eine neue Zeile, läßt links Platz für Sprungmarken und fügt eventuell rechts nach einem Strichpunkt einen Kommentar an. Obiges Beispiel stellt sich damit wie folgt dar:

            mov dx,8020h  ; Portadresse
            xor ax,ax     ; Zähler auf 0
    Marke:  out dx,al     ; Ton erzeugen
            dec ax        ; Zähler -1
            jnz Marke     ; Schleife
            mov ax,4c80h
            out dx,al     ; Ton aus
            int 21h       ; beenden

Unser Handwerkszeug

Dieser Quelltext muß nun in zwei Schritten mit einem Assembler und einem Linker in ein lauffähiges Programm übersetzt werden. Man kann diese zwei Schritte mit einem Batchprogramm zusammenfassen. Die bekanntesten Assembler sind MASM von Microsoft (zugehöriger Linker: LINK), TASM (Linker: TLINK) von Borland sowie der Sharewareassembler A86. Ich selbst benutze Borlands Turbo Assembler, jedoch scheint mir A86 den einfachsten Einstieg zu ermöglichen, denn dieser kann ohne externen Linker obigen Beispielquelltext direkt in ein .com-Programm übersetzen. Zudem ist er mit 23kB (Version 3.22) mit Abstand der kleinste Vertreter seiner Gattung und läuft damit sogar direkt auf dem Portfolio. A86.COM befand sich auf der Begleitdiskette zur ersten Ausgabe der Pofoinfo, ist aber auch auf der Club-CD, in der Club-Mailbox und in vielen Shareware-Archiven vorhanden. Turbo-Pascal (6.0ff) wartet mit einem integrierten Assembler auf. Allerdings bettet es den damit erzeugten Code in ein unhandliches EXE-Programm ein. Wer noch keines dieser Programme besitzt, kann zum Experimentieren vorerst auf DEBUG zurückgreifen.
Neben einem Assembler sollten zur Grundausstattung aber auch Nachschlagewerke zu den Themen Prozessorarchitektur, Befehlssatz und Betriebssystem gehören, denn eine ausführliche Behandlung im Rahmen dieses Kurses ist kaum möglich. Eine große Hilfe ist hier eine Online-Referenz, wie z.B. das Shareware-Programm HelpPC für DOS von David Jurgens, das alle genannten Themenkomplexe abdeckt (Quelle z.B.: ftp://ftp.simtel.net/pub/simtelnet/msdos/info/helppc21.zip, Größe ca. 255K)

Das erste Programm

Um unser Beispielprogramm zu erzeugen gibt es nun folgende Möglichkeiten:

1. Verwendung von DEBUG.COM

Das Dienstprogramm Debug gehört zum Lieferumfang von MSDOS und eignet sich vor allem zur Fehlersuche in Maschinenprogrammen. Es ermöglicht aber auch die Eingabe von Bytefolgen und sogar Mnemonics. Trotzdem haben wir es nicht mit einem ausgewachsenen Assembler zu tun, da z.B. keine symbolischen Adressen verwendet werden dürfen, wie oben mit "Marke" geschehen.

Die wichtigsten Befehle, die in Debug immer hinter einem Minuszeichen als Prompt eingegeben werden, lauten:
    ?      Hilfe (nur neuere Versionen)
    e Adr  Eingabe einer Bytefolge
    a      Assemble
    u      Unassemble
    nName  Dateiname festlegen
    r cx   Dateilänge festlegen
    w      Write
Weiterführende Erläuterungen zu Debug sowie eine ältere Version (knapp 10KB), die auch auf dem PF funktioniert, finden sich auf der Club-CD. Unser Programm können wir damit bereits eingeben, und alle Debug-Neulinge sollten dies jetzt einmal versuchen. Zur Orientierung diene dabei das folgende Bildschirmprotokoll:

    C:\>debug ton.com
    File not found
    -a
    0F38:0100 mov dx,8020
    0F38:0103 xor ax,ax
    0F38:0105 out dx,al
    0F38:0106 dec ax
    0F38:0107 jnz 105
    0F38:0109 mov ax,4c80
    0F38:010C out dx,al
    0F38:010D int 21
    0F38:010F
    -r cx
    CX 0000
    :f
    -w
    Writing 0000F bytes
    -q

Fertig! Anstelle der Sprungmarke "Marke" mußte hier eine der Speicheradressen eingesetzt werden, die vor jedem Befehl am linken Rand erscheinen. Nach dem Verlassen von Debug sollte das Programm TON.COM mit einer Länge von 15 Bytes im aktuellen Directory zu finden sein. Noch etwas schneller gelangt man ans Ziel, wenn man die 15 Bytes mit Hilfe des E-Kommandos von Debug direkt im Hexcode eingibt. Hierbei ist zu beachten, daß nach den zwei Zeichen jedes Bytes Space betätigt werden muß, nach dem letzten Byte stattdessen Enter. Ach ja: Dieses Programm läuft nur auf dem Portfolio und könnte einen normalen PC zum Absturz bringen.

2. Assemblierung mit A86

Vor der Anwendung eines Assemblers muß man den Quelltext mit einem normalen ASCII-Editor in eine Datei schreiben, die man gewöhnlich mit der Endung .ASM versieht. Zur Demonstration tippe man hierzu unser Beispiel (in der Form wie es vor "Unser Handwerkszeug" steht) ab und nenne es TON.ASM. Der Rest ist wirklich kinderleicht:

    C:\>a86 ton.asm
    A86 macro assembler, V3.22  Copyright 1990 Eric Isaacson
    Sources:
    ton.asm
    Object: ton.COM
    Symbols: ton.SYM

Das Resultat ist unser gewünschtes Programm TON.COM. Die Symbollistendatei TON.SYM benötigen wir nicht und können sie auch löschen. Wer sich den generierten Code genauer ansieht, wird feststellen, daß das vierte Byte von der mit Debug erzeugten Version abweicht (33h statt 31h). Das liegt daran, daß sich manche Assemblerbefehle unterschiedlich codieren lassen. Der Autor des A86 verwendet bewußt ungewöhnliche Codierungen, um feststellen zu können, ob ein Programm mit seinem Assembler erzeugt wurde. Diese Spuren lassen sich aber leicht verwischen, indem man den Code z.B. mit Debug erst disassembliert und dann wieder assembliert.

3. Das Borland-Gespann TASM und TLINK

Der klassische Weg vom Quelltext zum Code führt über die sogenannte Objektdatei. Sie wird vom Assembler aus dem Quelltext erzeugt und enthält einen Mix aus Symbolen und Code. Erst durch das anschließende Linken gelangt man zum ausführbaren Programm. Dieses Verfahren ist zwar vielseitiger (man kann z.B. mehrere Objektdateien zusammenlinken), für den Einsteiger allerdings erst einmal umständlicher als die direkte Methode von A86. Damit aber nicht genug: Der Quelltext bedarf noch ein paar Ergänzungen:

    .model tiny
    .code
    ORG 100h
    Start:  mov dx,8020h  ; Portadresse
            xor ax,ax     ; Zähler auf 0
    Marke:  out dx,al     ; Ton erzeugen
            dec ax        ; Zähler -1
            jnz Marke     ; Schleife
            mov ax,4c80h
            out dx,al     ; Ton aus
            int 21h       ; beenden
    END Start

Die hinzugekommenen Assemblerdirektiven machen Angaben über das Speichermodell, den Beginn des Codesegments und die Einsprungadresse. Die hier verwendeten Minimaleinstellungen dürften für jedes Portfolioprogramm ausreichend sein und können jedesmal so übernommen werden. Hat man TON.ASM wie oben ergänzt, kann die Assemblierung folgen (hier auf dem Desktop-PC):

    C:\>tasm ton.asm
    Turbo Assembler  Version 2.0  Copyright (c) 1988, 1990 Borland International

    Assembling file:   ton.asm
    Error messages:    None
    Warning messages:  None
    Passes:            1
    Remaining memory:  474k

Die erzeugte Objektdatei muß jetzt gelinkt werden. Die Option /t gibt an, daß ein .COM-Programm erstellt werden soll:

    C:\>tlink /t ton.obj
    Turbo Link  Version 3.0 Copyright (c) 1987, 1990 Borland International

Das fertige TON.COM ist übrigens identisch zu der mit A86 erzeugten Version (was nicht selbstverständlich ist). Die zusätzlich entstandenen Dateien TON.OBJ und TON.MAP können gelöscht werden.

4. Microsoft MASM und LINK

Das umständlichste - pardon professionellste - Werkzeug stammt, wie sollte es anders sein, aus dem Hause Microsoft. Die hier verwendete MASM-Version 1.25 ist allerdings schon leicht in die Jahre gekommen, so daß das Beispiel eventuell nicht mit aktuelleren Versionen nachvollziehbar ist. Trotzdem hier der angepaßte Quelltext:

    code    SEGMENT
            ASSUME CS:code
            ORG 100h
    Start:  mov dx,8020h  ; Portadresse
            xor ax,ax     ; Zähler auf 0
    Marke:  out dx,al     ; Ton erzeugen
            dec ax        ; Zähler -1
            jnz Marke     ; Schleife
            mov ax,4c80h
            out dx,al     ; Ton aus
            int 21h       ; beenden
    code    ENDS
    END Start

Bis zum Endprodukt sind jetzt drei Schritte erforderlich. Zunächst wie beim Borland-Produkt die Assemblierung und das Linken. LINK erzeugt allerdings eine ausführbare Datei im .EXE-Format (783 Bytes), die danach noch ins COM-Format konvertiert werden muß:

    C:\>masm ton.asm
    The Microsoft MACRO Assembler , Version 1.25
     Copyright (C) Microsoft Corp 1981,82,83

    Object filename [ton.OBJ]:
    Source listing  [NUL.LST]:
    Cross reference [NUL.CRF]:

    Warning Severe
    Errors  Errors
    0       0

    C:\>link ton.obj

       Microsoft Object Linker V2.01 (Large)
    (C) Copyright 1982, 1983 by Microsoft Inc.

    Run File [TON.EXE]:
    List File [NUL.MAP]:
    Libraries [.LIB]:
    Warning: No STACK segment

    There was 1 error detected.

    C:\>exe2bin ton.exe ton.com

Die Warnung können wir ignorieren, und die Dateien TON.OBJ, TON.MAP, TON.EXE löschen. Das Resultat ist wieder genau die selbe 15 Bytes lange Datei TON.COM, die auch schon die beiden anderen Assembler geliefert haben.

5. Der integrierte Assembler von Turbo Pascal

Mit der Anweisung ASM lassen sich Assemblerbefehle auch in ein Pascalprogramm einbauen. Dabei ist zu beachten, daß Labels mit dem Klammeraffen beginnen müssen und Kommentare in geschweifte Klammern gestellt werden müssen:

    begin
      ASM
            mov dx,8020h {Portadresse  }
            xor ax,ax    {Zähler auf 0 }
    @Marke: out dx,al    {Ton erzeugen }
            dec ax       {Zähler -1    }
            jnz @Marke   {Schleife     }
            mov ax,4c80h
            out dx,al    {Ton aus      }
            int 21h      {beenden      }
      end;
    end.

Kompiliert mit TP 6.0 ergibt dieser Quelltext ein 1488 Bytes langes .EXE-Programm. Da der Befehl int 21h das Programm vorzeitig beendet, sollte man diese Zeile besser weglassen. Es ist aber auch möglich (z.B. mit DEBUG), unsere 15 Bytes aus dem .EXE Programm herauszuschneiden und als .COM-Programm zu speichern. Der betreffende Abschnitt reicht übrigens vom 16. bis zum 30. der 1488 Bytes.

Wie geht's weiter?

Wer selbst Assemblerprogramme für den Portfolio entwickeln will, kommt nicht umhin, sich ein wenig mit der Architektur (insbes. Registersatz) des 8086 vertraut zu machen. Außerdem benötigen wir einen gewissen Vorrat an Befehlen. Zum Glück ist man hier mit einem guten Dutzend schon recht gut gewappnet. Ein weiterer Schwerpunkt ist die Ein- und Ausgabe, sowohl über das Betriebssystem als auch durch direkte Hardwarezugriffe. Aus Platzgründen können diese Themen leider erst in den kommenden Heften behandelt werden.
Fragen und Anmerkungen bitte an .
Eventuelle Korrekturen oder Ergänzungen werden zu finden sein auf der WWW-Seite:
http://leute.server.de/peichl/pf.htm
 



Zurück zur Portfolio-Seite
Zum 2. Kursteil
Zum Hauptmenü