FAQ der Newsgroup de.comp.lang.assembler (d.c.l.a.)

Wie fange ich am besten an?

Es gibt keinen Königsweg und noch nicht einmal eine "Bibel" für Assembler. Am besten schnappt man sich ein Tutorial oder ein Lehrbuch und ackert es von vorne nach hinten durch. Die dort aufgezeigten Wege hat man widerstandslos zu akzeptieren. Es ist eigentlich egal, ob man Assembler unter DOS, Windows oder Linux oder gar für den eigenen Mikroprozessor lernt, wichtig ist nur, dass man den einmal eingeschlagenen Weg nicht nur aus Frust verlässt - und Frust wird kommen ;-)

Für Windows ist wohl der einfachste Einstieg eine Nasm-GCC-Kombination:

Man hole sich MingW und installiere es mit dem Installer:
http://sourceforge.net/project/showfiles.php?group_id=2435

Beim Download ist empfehlenswert, sich nicht nur den Installer zu holen, sondern auch das neueste Win32-API-Paket:

Screenshot der MingW-Downloadseite

Die Installation geschieht mit MinGW-5.1.4.exe. Dieses Programm holt sich weitere Dateien aus dem Internet. Beim Installieren ist das Häkchen bei g++ compiler zu setzen, alles andere kann wie vorgeschlagen akzeptiert werden:

Screenshot der MingW-Installation

Das heruntergeladene Win32-API-Paket (w32api-X.XX-mingw32-dev.tar.gz) muss zweimal entpackt werden. Beim ersten Entpacken entsteht nur eine TAR-Datei. Diese muss auch noch entpackt werden. Viele Entpackprogramme meistern diese zweifache Entpackung in einem Schritt. Es entstehen zwei Verzeichnisse: include und lib. Diese Verzeichnisse werden gnadenlos über die gleichlautenden Verzeichnisse und vorhandenen Dateien im MingW-Verzeichnis drüberkopiert.

Dann hole man sich Nasm (Win32 binaries), entpacke es und kopiere nasm.exe in das bin-Verzeichnis von MingW:
http://sourceforge.net/project/showfiles.php?group_id=6208

Nun erstelle man eine Textdatei mit dem Namen hallo.nasm:

NASM & GCC
; Name:         hallo.nasm
; Assemblieren: nasm.exe -fwin32 hallo.nasm
; Linken:       g++.exe -o hallo.exe hallo.obj
; Ergebnis:     hallo.exe

BITS 32
EXTERN _printf

SECTION .data
str: db "Hallo Welt",0
fmt: db "%s",10,0

SECTION .text
GLOBAL _main
_main:

  push str         ; Zeichenkette
  push fmt         ; Format
  call _printf     ; C-Funktion: printf (fmt,str)
  add esp, (4 * 2) ; cdecl: Pop zwei DWORD-Pushes = 2*4 Bytes

  mov eax, 0       ; Exitcode
  ret              ; Ende

Folgende drei Konsolenanweisungen kompilieren hallo.nasm zu hallo.obj, linken es zusammen mit der C-Bibliothek von GCC zum Programm hallo.exe und führen das Programm (hallo.exe) aus:

c:\mingw\bin\nasm.exe -f win32 hallo.nasm
c:\mingw\bin\g++.exe -o hallo.exe hallo.obj
hallo.exe

Vorteile dieser Kombination sind:

  • NASM und GCC sind plattformunabhängige Programme, d.h. sie existieren für Windows und für Linux. Wer zwischen diesen Betriebssystemen wechseln will, muss sich nicht mühsam in die Grundlagen neuer Programme einarbeiten
  • Mit dem MingW-GCC steht auch ein funktionierender und moderner C++-Compiler zur Verfügung. Die Win32- und C-Bibliotheken sind vorhanden und einkompiliert, d.h. man muss sich meistens nicht um LIB-Dateien kümmern.

Mit dieser Kombination lassen sich Assembler- und C-Programme recht mühelos kombinieren:

NASM & GCC
; Name:                 callgcc.nasm
; Assemblieren:         nasm.exe -fwin32 main.nasm

BITS 32
EXTERN _Print

SECTION .text
GLOBAL _main
_main:
    call _Print

    mov eax, 0      ; Exitcode
    ret             ; Ende
------------------------------------------------------------

// Name:                 callee.cpp
// Kompilieren & Linken: g++.exe -o callgcc.exe callgcc.obj callee.cpp
// Ergebnis:             callgcc.exe

#include <windows.h>
extern "C" void Print ()
{
    MessageBox (0, "Hallo Welt", "Titel", 0);
}

Dem Aufruf einer C-Funktion in Assembler muss ein Unterstrich vorangestellt werden. Die Funktion Print im GCC-Modul wird also durch call _Print im NASM-Modul aufgerufen. Diese Technik wird Name Mangling oder Name Decoration genannt. Für GCC gilt dabei folgendes:

Calling conventionextern "C"
GCC-Funktion (_cdecl) _Funktion (nur Unterstrich)
Win32-API (stdcall) _Funktion@0 (Unterstrich, Klammeraffe und Anzahl der Bytes auf dem Stack)

Beim Aufruf von Win32-Funktionen ist das Zählen der Bytes auf dem Stack ziemlich nervig und fehlerträchtig. Ebenso führt das Aufräumen des Stacks nach dem Aufruf einer Funktion nach der cdecl calling convention häufig zu Fehlern. Nachfolgend werden zwei Makros vorgestellt, die das Pushen, die EXTERN-Deklaration, Dekoration und das Aufräumen des Stacks übernehmen:

NASM & GCC
; Name:         hallo.nasm
; Assemblieren: nasm.exe -fwin32 hallo.nasm
; Linken:       g++.exe -o hallo.exe hallo.obj
; Ergebnis:     hallo.exe

BITS 32

%MACRO m_invoke_mangled 1-*     ; Für Win32-API-Funktionen (stdcall)
    %assign args (%0-1)         ; Anzahl der Argumente
    %assign stack 4 * args      ; jedes Argument 4 Bytes
    EXTERN _%1@%[stack]
    %if %0 > 1
        %rep %0-1               ; Argumente auf den Stack
            %rotate -1
            push %1
        %endrep
        %rotate -1
    %endif
    call _%1@%[stack]           ; mit Unterstrich und Stack-Bytes (gemangelt)
%ENDMACRO

%MACRO m_ccall_mangled 1-*      ; Für C-Funktionen (_cdecl)
    EXTERN _%1
    %if %0 > 1
        %rep %0-1               ; Argumente auf den Stack
            %rotate -1
            push %1
        %endrep
        %rotate -1
    %endif
    %assign args (%0-1)         ; Anzahl der Argumente
    %assign stack 4 * args      ; jedes Argument 4 Bytes
    call _%1                    ; mit Unterstrich (gemangelt)
    %if %0 > 1
       add esp, stack           ; Stack aufräumen
    %endif
%ENDMACRO

SECTION .data
    MsgWin32 db "Hallo Welt von WriteFile",10,0
    len      EQU $-MsgWin32                     ; WriteFile: strlen (MsgWin32)
    FmtC     db "%s",10,0
    MsgC     db "Hallo Welt von printf",0
    dummy    dd 0                               ; WriteFile
    hStdOut  dd 0                               ; WriteFile

SECTION .text
GLOBAL _main
_main:

    m_invoke_mangled GetStdHandle, -11          ; StdOut
    mov [hStdOut], eax
    m_invoke_mangled WriteFile, dword [hStdOut], MsgWin32, len, dummy, 0
    m_ccall_mangled printf, FmtC, MsgC

    mov eax, 0                                  ; Exitcode
    ret                                         ; Ende

Ralph 'rkhb' Bauer Juni 2009