Die folgende hello.asm Datei erklärt in den Kommentaren das Wesentliche, um den Quellcode zu verstehen. Unter Mac OS X kann man den Code mit folgenden Befehlen übersetzen, linken und ausführen.

Kompilierung

$ nasm -f macho hello.asm
$ ld -static -o hello -e teststart hello.o
$ ./hello

Assembler Code nasm

; nasm -f macho hello.asm
; ld -static -o hello -e teststart hello.o

section .data
    testmsg db "hello, world!", 0xa     ; String mit Zeilenumbruch
    testlen equ $-testmsg               ; Laenge des Strings in Bytes


section .text
global teststart                        ; Macht die Hauptfunktion extern sichtbar

teststart:
    ; TEXT AUSGEBEN MIT SYSTEMCALL WRITE
    ; write(int fildes, const void *buf, size_t nbyte);
    
    ; Argumente in umgekehrter Reihenfolge auf den Stack legen
    ; Hinweis der Stack waechst von oben nach unten also von 0xFFFF... nach 0x...0000
    push dword testlen                  ; nbyte     - Laenge des Strings                           
    push dword testmsg                  ; *buf      - Pointer/Adresse auf den String
    push dword 1                        ; fildes    - Filedescriptor. In welche Datei so geschrieben
                                        ;             werden. 1 = stdout

    ; SYSTEMCALL WRITE AUSFUEHREN
    mov eax, 0x4                        ; Systemcall write(...) hat Nr. 4
    sub esp, 4                          ; Mac OS X (BSD) braucht 4 Bytes weitern Platz auf dem Stack
    int 0x80                            ; Interrupt 0x80 löst einen Kernel/Systemcall Sprung aus

    ; STACK AUFRAEUMEN - Argumente werde nicht mehr gebraucht
    add esp, 16                         ; 3 Argumente * 4 Bytes (dword) gross + 4 Bytes extra
                                        ; Platz = 16 Bytes

    ; PROGRAMM BEENDEN
    ; exit(int status) 
    push dword 0                        ; Exitstatus 0 (kein Fehler) als Argument auf den Stack legen

    ; SYSTEMCALL EXIT AUSFUEHREN
    mov eax, 0x1                        ; Systemcall exit(...) hat Nr. 1
    sub esp, 4                          ; Mac OS X (BSD) braucht 4 Bytes weitern Platz auf dem Stack
    int 0x80                            ; Systemcall machen

Beschreibung

    testmsg db "hello, world!", 0xa ; String mit Zeilenumbruch

Mit der Pseudo-Instruktion db kann man NASM anweisen, Bytes in die .o Datei zu schreiben. Da die ASCII Charakter genau 1 Byte (8 Bit) groß sind, verwendet man den Pseudo – Befehl db. Pseudo-Befehl heißt es deswegen, da nicht der Prozessor diesen Befehl ausführen wird, sondern es „nur“ eine Anweisung an den Assembler ist, was er mit den nachstehenden Daten machen soll. Statt alle Charakter Bytes in hexadezimaler Schreibweise mit Kommata aufzuführen, kann man diese auch in Anführungszeichen setzen. Das Byte/Zeichen 0xa für den Zeilenumbruch, lässt sich aber weiterhin einfacher in Hex-Schreibweise hinzufügen.

Mit testmsg benennen wir die Adresse des ersten Bytes/Zeichen der Zeichenkette im Speicher. Also in diesem Fall heißt die Adresse von ‚h‘ (von hello, world!) testmsg. Mit testmsg kann man jetzt immer auf den Anfang von „hello, world!“ zugreifen. testmsg + 1 ist dann das ‚e‘, testmsg + 2 das ‚l‘ usw. Was wir an dieser Stelle aber noch nicht wissen ist, wo hört "hello, world!",0xa auf? Das machen wir in der nächsten Zeile.

    testlen equ $-testmsg ; Laenge des Strings in Bytes

equ ist auch ein Pseudo-Befehl. Mit ihm kann man einen Wert definieren. C-Programmierer kennen das von der #define Präprosessor-Anweisung. equ (vielleicht von equal) ordnet einem Bezeichner einen Wert zu. Ein Bezeichner ist dabei einfach der Name für etwas. Z. B.: x = 3. Hier wäre x ein Bezeichner. Mit testlen equ 14 könnte man z. B. statt 14 auch überall testlen schreiben.

Zwar hat unser hello, world! 13 sichtbare und ein unsichtbares Zeichen (den Zeilenumbruch), also insgesamt 14 Zeichen, aber würde man einen sehr langen Text ausgeben wollen, wäre es wünschenswert, nicht jedes mal die Länge per Hand nachzuzählen oder auch bei Textänderungen müsste man den Wert ständig anpassen.

NASM erlaubt es statt einen festen Wert bei einem equ Befehl, einen mathematischen Ausdruck (Expression) anzugeben. Z. B. testlen equ 13 + 1 oder man könnte auch einfach einen anderen Bezeichner angeben z. B. testlen equ testmsg. Wie etwas weiter oben bereits erklärt enthält testmsg die Adresse von hello, world. Da wir noch nicht genau wissen, an welcher Stelle sich genau „hello, world“ befindet, nehmen wir einfach mal an, es ist Adresse 10000 (könnte ja sein), dann hätte testlen jetzt den Wert 1000.

Das Token $ enthält die Adresse/Position an dem es Aufgerufen wurde. Hier ein Beispiel: Wir haben eben gesagt, testmsg hat die Adresse 1000, dann folgen 14 Bytes (hello, world …), dann kommt zwar testlen equ etc. aber das erzeugt keine Daten in der Datei, so dass $ auf 1014 zeigt. Somit ist der Ausdruck testlen equ $ - testmsg das Gleiche wie testlen equ 1014 - 1000 und das ist testlen equ 14. Angenommen die Adressen sind andere, würde sich zwar die beiden Werte um einen Offset verschieben z. B. 50, aber 1064 – 1050 sind immer noch 14. So können wir die Textlänger bequem berechnen lassen.