V pořadí již devátém článku o použití assembleru v operačním systému Linux dokončíme popis makrosystému GNU Assembleru. Minule jsme se seznámili se způsobem vytvoření jednoduchých maker s případnými parametry, ovšem ve skutečnosti GNU Assembler nabízí programátorům i další možnosti, například zápis podmínek, výrazů, vkládání souborů (include) atd.

Obsah

1. Použití assembleru v Linuxu: makra v GNU Assembleru (dokončení)

2. Využití direktivy .print při ladění maker

3. První demonstrační příklad – použití direktivy .print

4. Volání makra z jiného makra

5. Druhý demonstrační příklad – ukázka volání makra z jiného makra

6. Výrazy v direktivách

7. Třetí demonstrační příklad – vytvoření vysokoúrovňových rozhodovacích konstrukcí pomocí maker

8. Použití direktivy pro zápis podmínek – .if a .endif

9. Porovnání číselných hodnot a řetězců

10. Čtvrtý demonstrační příklad – porovnání řetězců

11. Direktiva .include

12. Pátý demonstrační příklad – použití direktivy .include

13. Repositář s demonstračními příklady

14. Odkazy na Internetu

1. Použití assembleru v Linuxu: makra v GNU Assembleru (dokončení)

V předchozím článku jsme se seznámili se způsobem tvorby jednoduchých maker v GNU Assembleru. Připomeňme si, že makra je možné použít například ve chvíli, kdy se v kódu opakuje nějaká sekvence instrukcí; typicky se jedná o volání knihovní či systémové funkce s přípravou parametrů. Díky uživatelsky definovaným makrům lze volání knihovních i systémových funkcí zapisovat způsobem, který se podobá zápisu používaném ve vyšších programovacích jazycích. Velmi jednoduchým příkladem může být makro pojmenované exit, které slouží k ukončení aktuálního procesu. Zápis makra vypadá následovně:

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm

Použití tohoto makra (bez parametrů) je ještě jednodušší:

_start:
        ...
        ... vlastni kod programu
        ...
        exit                         # ukonceni aplikace

Ještě si připomeňme způsob vytvoření nepatrně složitějšího makra s parametry:

# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm

Použití tohoto makra je následující:

writeMessage message1,message1len

Povšimněte si, že jsme se skutečně nepatrně přiblížili k zápisu podobnému vyšším programovacím jazykům.

Makrosystém GNU Assembleru je ovšem ve skutečnosti mnohem sofistikovanější a nabízí programátorům i další možnosti, nejenom „pouhou“ expanzi maker provedenou před samotným překladem. S některými dalšími možnostmi se seznámíme v dnešním článku. Jedná se například o použití výrazů, podmínkových konstrukcí typu if-then-else či direktivy pojmenované .print, která může být užitečná například při ladění maker (výhodnější je však sledování expandovaného kódu). Taktéž si ukážeme, co se stane ve chvíli, kdy se v jednom makru volá jiné makro – tuto funkcionalitu GNU Assembler samozřejmě taktéž podporuje. Poslední potenciálně velmi užitečnou direktivou je direktiva nazvaná .include, která umožňuje vložení jednoho zdrojového kódu do kódu jiného, podobně jako céčkovská direktiva #include. S využitím této direktivy je možné si vytvořit sadu maker snadno použitelných v celém projektu (jen tak je možné si udržet pořádek v rozsáhlejších projektech).

2. Využití direktivy .print při ladění maker

Při ladění maker je v mnoha případech důležité se přesvědčit o tom, že skutečně došlo k expanzi makra popř. že assembler při zpracování zdrojového kódu překládaného programu došel na určitý řádek. Jedno z možných a velmi jednoduše použitelných řešení spočívá v použití direktivy .print. Této direktivě se předá řetězec, který se při zpracování direktivy jednoduše vytiskne na standardní výstup:

.print "Hello world!"

Užitečnost této direktivy se ještě zvyšuje díky tomu, že uvnitř makra (ale nikde jinde!) lze do řetězce přidat i symbol \@, s nímž jsme se seznámili minule. Připomeňme si, že tento symbol se používá jako počitadlo volání maker a je možné ho použít mj. i pro vytváření unikátních názvů návěští. I tento zápis je tedy legální:

.print "Aktuální hodnota počitadla maker: \@"

3. První demonstrační příklad – použití direktivy .print

Podívejme se nyní na způsob použití výše popsané direktivy .print, a to v demonstračním příkladu, s nímž jsme se již seznámili minule. V tomto příkladu je deklarováno makro writeMessageRepeatedly, ve kterém jsme vytvářeli unikátní názvy návěští pomocí počitadla \@. Bylo by tedy vhodné se přesvědčit o tom, jak přesně tato návěští vypadají, což je přesný úkol právě pro direktivu .print. Upravené makro vypadá následovně:

# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm

Upravené makro zařadíme do zdrojového kódu demonstračního příkladu:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - pouziti direktivy .print pro vypis pocitadla
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix


# Linux kernel system call table
sys_exit   = 1
sys_write  = 4

# Dalsi konstanty pouzite v programu - standardni streamy
std_input  = 0
std_output = 1



#-----------------------------------------------------------------------------

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm



#-----------------------------------------------------------------------------
.section .data
message1:
        .string "Hello world\n"
message1len = $ - message1         # delka prvni zpravy

message2:
        .string "Vitejte na mojefedora.cz\n"
message2len = $ - message2         # delka druhe zpravy

message3:
        .string "Assembler je fajn\n"
message3len = $ - message3         # delka druhe zpravy



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        .global _start               # tento symbol ma byt dostupny i linkeru

_start:
        writeMessageRepeatedly message1,message1len,10
        writeMessageRepeatedly message2,message2len,2
        writeMessageRepeatedly message3,message3len,7
        exit                         # ukonceni aplikace



# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80
        ret

Nejzajímavější bude sledovat, co se bude dít při překladu:

as -g --32 print_directive.s -o print_directive.o
 
Declaring label loop0
Declaring label loop1
Declaring label loop2

Vidíme, že jak počitadlo volání maker \@, tak i direktiva .print skutečně pracují korektně.

Výpis expandovaných maker se získá jednoduše přepínačem -alm kombinovaném s přepínačem -g (viz též předchozí část tohoto seriálu):

as -alm -g --32 print_directive.s -o print_directive.o

Výsledek expanze:

GAS LISTING print_directive.s                   page 1


   1                    # asmsyntax=as
   2                    
   3                    # Ukazka pouziti maker v GNU Assembleru - pouziti direktivy .print pro vypis pocitadla
   4                    # - pouzita je "Intel" syntaxe.
   5                    #
   6                    # Autor: Pavel Tisnovsky
   7                    
   8                    .intel_syntax noprefix
   9                    
  10                    
  11                    # Linux kernel system call table
  12                    sys_exit   = 1
  13                    sys_write  = 4
  14                    
  15                    # Dalsi konstanty pouzite v programu - standardni streamy
  16                    std_input  = 0
  17                    std_output = 1
  18                    
  19                    
  20                    
  21                    #-----------------------------------------------------------------------------
  22                    
  23                    # Deklarace makra pro ukonceni aplikace
  24                    .macro exit
  25                            mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
  26                            mov   ebx, 0                 # exit code = 0
  27                            int   0x80                   # volani Linuxoveho kernelu
  28                    .endm
  29                    
  30                    
  31                    
  32                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  33                    .macro writeMessage message,messageLength
  34                            mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
  35                            mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
  36                            call  write_message          # vytisknout zpravu "Zero flag not set"
  37                    .endm
  38                    
  39                    
  40                    
  41                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  42                    .macro writeMessageRepeatedly message,messageLength,count
  43                            mov   ebp, \count            # nastaveni pocitadla
  44                            .print "Declaring label loop\@"
  45                    loop\@:                              # lokalni navesti (unikatni)
  46                            mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
  47                            mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
  48                            call  write_message          # vytisknout zpravu "Zero flag not set"
  49                            dec   ebp                    # snizeni hodnoty pocitadla
  50                            jnz   loop\@                 # opakovani smycky
  51                    .endm
  52                    
  53                    
  54                    
  55                    #-----------------------------------------------------------------------------
  56                    .section .data
  57                    message1:


GAS LISTING print_directive.s                   page 2


  58 0000 48656C6C              .string "Hello world\n"
  58      6F20776F 
  58      726C640A 
  58      00
  59                    message1len = $ - message1         # delka prvni zpravy
  60                    
  61                    message2:
  62 000d 56697465              .string "Vitejte na mojefedora.cz\n"
  62      6A746520 
  62      6E61206D 
  62      6F6A6566 
  62      65646F72 
  63                    message2len = $ - message2         # delka druhe zpravy
  64                    
  65                    message3:
  66 0027 41737365              .string "Assembler je fajn\n"
  66      6D626C65 
  66      72206A65 
  66      2066616A 
  66      6E0A00
  67                    message3len = $ - message3         # delka druhe zpravy
  68                    
  69                    
  70                    
  71                    #-----------------------------------------------------------------------------
  72                    .section .bss
  73                    
  74                    
  75                    
  76                    #-----------------------------------------------------------------------------
  77                    .section .text
  78                            .global _start               # tento symbol ma byt dostupny i linkeru
  79                    
  80                    _start:
  81                            writeMessageRepeatedly message1,message1len,10
  81 0000 BD0A0000      >  mov ebp,10
  81      00
  81                    >  .print "Declaring label loop0"
  81                    > loop0:
  81 0005 B9000000      >  mov ecx,offset message1
  81      00
  81 000a BA0D0000      >  mov edx,message1len
  81      00
  81 000f E83D0000      >  call write_message
  81      00
  81 0014 4D            >  dec ebp
  81 0015 75EE          >  jnz loop0
  82                            writeMessageRepeatedly message2,message2len,2
  82 0017 BD020000      >  mov ebp,2
  82      00
  82                    >  .print "Declaring label loop1"
  82                    > loop1:
  82 001c B90D0000      >  mov ecx,offset message2
  82      00
  82 0021 BA1A0000      >  mov edx,message2len
  82      00
  82 0026 E8260000      >  call write_message


GAS LISTING print_directive.s                   page 3


  82      00
  82 002b 4D            >  dec ebp
  82 002c 75EE          >  jnz loop1
  83                            writeMessageRepeatedly message3,message3len,7
  83 002e BD070000      >  mov ebp,7
  83      00
  83                    >  .print "Declaring label loop2"
  83                    > loop2:
  83 0033 B9270000      >  mov ecx,offset message3
  83      00
  83 0038 BA130000      >  mov edx,message3len
  83      00
  83 003d E80F0000      >  call write_message
  83      00
  83 0042 4D            >  dec ebp
  83 0043 75EE          >  jnz loop2
  84                            exit                         # ukonceni aplikace
  84 0045 B8010000      >  mov eax,sys_exit
  84      00
  84 004a BB000000      >  mov ebx,0
  84      00
  84 004f CD80          >  int 0x80
  85                    
  86                    
  87                    
  88                    # Podprogram pro vytisteni zpravy na standardni vystup
  89                    # Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
  90                    write_message:
  91 0051 B8040000              mov   eax, sys_write         # cislo syscallu pro funkci "write"
  91      00
  92 0056 BB010000              mov   ebx, std_output        # standardni vystup
  92      00
  93 005b CD80                  int   0x80
  94 005d C3                    ret
  95                    

Povšimněte si především toho, že expandované makro se pozná jednoduše podle znaku > na příslušném řádku.

4. Volání makra z jiného makra

Při používání maker se velmi často dostaneme do situace, kdy je zapotřebí z jednoho makra volat jiné makro. To samozřejmě GNU Assembler podporuje, protože „volání makra“ není nic jiného, než textová expanze jeho těla. Pro příklad nemusíme chodit daleko, stačí se podívat na předchozí demonstrační příklad, v němž jsou deklarována dvě makra, která používají shodnou sekvenci instrukcí:

# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm

Povšimněte si, že v makru writeMessageRepeatedly vlastně můžeme volat makro writeMessage, takže úprava bude jednoduchá a výsledné makro writeMessageRepeatedly bude kratší a přehlednější:

# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        writeMessage \message, \messageLength
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm

5. Druhý demonstrační příklad – ukázka volání makra z jiného makra

V dnešním druhém demonstračním příkladu je použito upravené makro writeMessageRepeatedly. Zajímavý není ani tak zápis zdrojového kódu uvedený pod tímto odstavcem, ale způsob, jakým je zpracováno počitadlo \@ a co se bude dít při expanzi:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - volani makra z jineho makra
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix


# Linux kernel system call table
sys_exit   = 1
sys_write  = 4

# Dalsi konstanty pouzite v programu - standardni streamy
std_input  = 0
std_output = 1



#-----------------------------------------------------------------------------

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        writeMessage \message, \messageLength
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm



#-----------------------------------------------------------------------------
.section .data
message1:
        .string "Hello world\n"
message1len = $ - message1         # delka prvni zpravy

message2:
        .string "Vitejte na mojefedora.cz\n"
message2len = $ - message2         # delka druhe zpravy

message3:
        .string "Assembler je fajn\n"
message3len = $ - message3         # delka druhe zpravy



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        .global _start               # tento symbol ma byt dostupny i linkeru

_start:
        writeMessageRepeatedly message1,message1len,10
        writeMessageRepeatedly message2,message2len,2
        writeMessageRepeatedly message3,message3len,7
        exit                         # ukonceni aplikace



# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80
        ret

Při překladu se stane zajímavá věc – direktiva .print vypíše názvy vytvořených unikátních návěští, jejichž hodnoty se liší o dvojku a nikoli o jedničku. Je tomu tak z toho důvodu, že \@ počítá jakoukoli expanzi makra, tedy i makra zanořeného do jiného makra:

Declaring label loop0
Declaring label loop2
Declaring label loop4

Opět se podívejme na výsledek expanze maker:

GAS LISTING nested_macros.s                     page 1


   1                    # asmsyntax=as
   2                    
   3                    # Ukazka pouziti maker v GNU Assembleru - volani makra z jineho makra
   4                    # - pouzita je "Intel" syntaxe.
   5                    #
   6                    # Autor: Pavel Tisnovsky
   7                    
   8                    .intel_syntax noprefix
   9                    
  10                    
  11                    # Linux kernel system call table
  12                    sys_exit   = 1
  13                    sys_write  = 4
  14                    
  15                    # Dalsi konstanty pouzite v programu - standardni streamy
  16                    std_input  = 0
  17                    std_output = 1
  18                    
  19                    
  20                    
  21                    #-----------------------------------------------------------------------------
  22                    
  23                    # Deklarace makra pro ukonceni aplikace
  24                    .macro exit
  25                            mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
  26                            mov   ebx, 0                 # exit code = 0
  27                            int   0x80                   # volani Linuxoveho kernelu
  28                    .endm
  29                    
  30                    
  31                    
  32                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  33                    .macro writeMessage message,messageLength
  34                            mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
  35                            mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
  36                            call  write_message          # vytisknout zpravu "Zero flag not set"
  37                    .endm
  38                    
  39                    
  40                    
  41                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  42                    .macro writeMessageRepeatedly message,messageLength,count
  43                            mov   ebp, \count            # nastaveni pocitadla
  44                            .print "Declaring label loop\@"
  45                    loop\@:                              # lokalni navesti (unikatni)
  46                            writeMessage \message, \messageLength
  47                            dec   ebp                    # snizeni hodnoty pocitadla
  48                            jnz   loop\@                 # opakovani smycky
  49                    .endm
  50                    
  51                    
  52                    
  53                    #-----------------------------------------------------------------------------
  54                    .section .data
  55                    message1:
  56 0000 48656C6C              .string "Hello world\n"
  56      6F20776F 


GAS LISTING nested_macros.s                   page 2


  56      726C640A 
  56      00
  57                    message1len = $ - message1         # delka prvni zpravy
  58                    
  59                    message2:
  60 000d 56697465              .string "Vitejte na mojefedora.cz\n"
  60      6A746520 
  60      6E61206D 
  60      6F6A6566 
  60      65646F72 
  61                    message2len = $ - message2         # delka druhe zpravy
  62                    
  63                    message3:
  64 0027 41737365              .string "Assembler je fajn\n"
  64      6D626C65 
  64      72206A65 
  64      2066616A 
  64      6E0A00
  65                    message3len = $ - message3         # delka druhe zpravy
  66                    
  67                    
  68                    
  69                    #-----------------------------------------------------------------------------
  70                    .section .bss
  71                    
  72                    
  73                    
  74                    #-----------------------------------------------------------------------------
  75                    .section .text
  76                            .global _start               # tento symbol ma byt dostupny i linkeru
  77                    
  78                    _start:
  79                            writeMessageRepeatedly message1,message1len,10
  79 0000 BD0A0000      >  mov ebp,10
  79      00
  79                    >  .print "Declaring label loop0"
  79                    > loop0:
  79                    >  writeMessage message1,message1len
  79 0005 B9000000      >>  mov ecx,offset message1
  79      00
  79 000a BA0D0000      >>  mov edx,message1len
  79      00
  79 000f E83D0000      >>  call write_message
  79      00
  79 0014 4D            >  dec ebp
  79 0015 75EE          >  jnz loop0
  80                            writeMessageRepeatedly message2,message2len,2
  80 0017 BD020000      >  mov ebp,2
  80      00
  80                    >  .print "Declaring label loop2"
  80                    > loop2:
  80                    >  writeMessage message2,message2len
  80 001c B90D0000      >>  mov ecx,offset message2
  80      00
  80 0021 BA1A0000      >>  mov edx,message2len
  80      00
  80 0026 E8260000      >>  call write_message


GAS LISTING nested_macros.s                   page 3


  80      00
  80 002b 4D            >  dec ebp
  80 002c 75EE          >  jnz loop2
  81                            writeMessageRepeatedly message3,message3len,7
  81 002e BD070000      >  mov ebp,7
  81      00
  81                    >  .print "Declaring label loop4"
  81                    > loop4:
  81                    >  writeMessage message3,message3len
  81 0033 B9270000      >>  mov ecx,offset message3
  81      00
  81 0038 BA130000      >>  mov edx,message3len
  81      00
  81 003d E80F0000      >>  call write_message
  81      00
  81 0042 4D            >  dec ebp
  81 0043 75EE          >  jnz loop4
  82                            exit                         # ukonceni aplikace
  82 0045 B8010000      >  mov eax,sys_exit
  82      00
  82 004a BB000000      >  mov ebx,0
  82      00
  82 004f CD80          >  int 0x80
  83                    
  84                    
  85                    
  86                    # Podprogram pro vytisteni zpravy na standardni vystup
  87                    # Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
  88                    write_message:
  89 0051 B8040000              mov   eax, sys_write         # cislo syscallu pro funkci "write"
  89      00
  90 0056 BB010000              mov   ebx, std_output        # standardni vystup
  90      00
  91 005b CD80                  int   0x80
  92 005d C3                    ret
  93                    

Zajímavé jsou například následující řádky, z nichž je patrné, že makro volané z jiného makra poznáme snadno podle většího počtu znaků >:

  81                    > loop4:
  81                    >  writeMessage message3,message3len
  81 0033 B9270000      >>  mov ecx,offset message3
  81      00
  81 0038 BA130000      >>  mov edx,message3len
  81      00
  81 003d E80F0000      >>  call write_message
  81      00
  81 0042 4D            >  dec ebp

6. Výrazy v direktivách

Při vytváření konstant či maker lze v GNU Assembleru používat výrazy, v nichž se vyskytují numerické konstanty. Tyto výrazy lze použít například při výpočtu adresy (při překladu), což je až překvapivě častá operace. Zápis výrazů do značné míry odpovídá zvyklostem známým z céčka (až na dvě výjimky), liší se však priority operátorů, což je naznačeno v následující tabulce:

Priorita Operátory
nejvyšší * / % << >>
střední | & ^ (XOR) ! (OR NOT – rozdílné od céčka!)
nižší + – == <> (nerovnost, rozdílné od céčka!) < <= > >=
nejnižší && ||

Navíc ještě existují dva unární operátory, tj. operátory, u nichž se jediný operand zapisuje za operátor:

Operátor Význam
dvojkový doplněk
~ bitová negace

7. Třetí demonstrační příklad – vytvoření vysokoúrovňových rozhodovacích konstrukcí pomocí maker

V GNU Assembleru je možné napsat i relativně sofistikovaná makra, která programátorům nabízí obdobu rozhodovacích konstrukcí typu if-then-else. Tato makra typicky převedou konstrukci s podmínkou na sekvenci instrukcí cmp, jump(podmínka), jump(negace-podmínky), což může vést ke zjednodušení zápisu celého programu (vlastně se tak nepatrně přiblížíme k vysokoúrovňovým programovacím jazykům). V takovém případě je však nutné generovat vhodné názvy návěští, což lze řešit s využitím výrazů v direktivách, například takto:

.set _if_label_counter, 0

.macro make_label counter
 xxx\counter:
.endm

.macro jump_to_label counter
        jz xxx\counter
.endm

# Makro pro oznaceni zacatku podminky
.macro _if_equal
        .set _if_label_counter, _if_label_counter+1
        jump_to_label _if_label_counter
.endm

.macro _endif
        make_label _if_label_counter
.endm

Povšimněte si, že zde není možné použít počitadlo \@, a to z toho prostého důvodu, že by počitadlo bylo zvyšováno při každém volání makra, tedy ihned po _if_equal by již makro _endif použilo jinou hodnotu. Navíc se jedná o globální počitadlo, nad nímž nemáme žádnou kontrolu.

Použití nově nadefinovaných „rozhodovacích konstrukcí“:

_start:
        writeMessage message1,message1len

        cmp eax, eax
        _if_equal
            writeMessage message2,message2len
        _endif

        cmp eax, eax
        _if_equal
            writeMessage message2,message2len
        _endif

        writeMessage message3,message3len
        exit

Tato makra i jejich volání jsou součástí dalšího demonstračního příkladu, jehož úplný zdrojový kód vypadá následovně:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - if-then atd.
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix

# Linux kernel system call table
sys_exit   = 1
sys_write  = 4

# Dalsi konstanty pouzite v programu - standardni streamy
std_input  = 0
std_output = 1



#-----------------------------------------------------------------------------

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm


.set _if_label_counter, 0

.macro make_label counter
 xxx\counter:
.endm

.macro jump_to_label counter
        jz xxx\counter
.endm

# Makro pro oznaceni zacatku podminky
.macro _if_equal
        .set _if_label_counter, _if_label_counter+1
        jump_to_label _if_label_counter
.endm

.macro _endif
        make_label _if_label_counter
.endm



#-----------------------------------------------------------------------------
.section .data
message1:
        .string "Hello world\n"
message1len = $ - message1         # delka prvni zpravy

message2:
        .string "Vitejte na mojefedora.cz\n"
message2len = $ - message2         # delka druhe zpravy

message3:
        .string "Assembler je fajn\n"
message3len = $ - message3         # delka druhe zpravy



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        .global _start               # tento symbol ma byt dostupny i linkeru

_start:
        writeMessage message1,message1len
        cmp eax, eax
        _if_equal
        writeMessage message2,message2len
        _endif

        cmp eax, eax
        _if_equal
        writeMessage message2,message2len
        _endif

        writeMessage message3,message3len
        exit                         # ukonceni aplikace



# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80
        ret

Poznámka: zkuste upravit nějakou podmínku například následovně:

mov ebx, eax
inc ebx
cmp eax, ebx

8. Použití direktivy pro zápis podmínek – .if a .endif

V souvislosti s výrazy a konstantami (ať již numerickými či textovými) je vhodné se zmínit o podpoře pro podmíněný překlad. Makrosystém GNU Assembleru totiž podporuje tvorbu podmínek typu if-then-else, přičemž v podmínce je možné testovat buď hodnotu nějakého řetězce či provést porovnání dvou numerických výrazů. Při zápisu podmínek je nutné si uvědomit, že celý makrosystém slouží k „úpravám“ zdrojového kódu před jeho překladem. Všechny podmínky se tedy vyhodnotí již při překladu (resp. těsně před ním), což znamená, že se v žádném případě nejedná o podmínky vyhodnocované v době běhu programu.

Základní podmínky se zapisují direktivami .if, .else a .endif, což je patrné i z následujícího velmi jednoduchého příkladu. Samozřejmě je nutné, aby se všechny výrazy použité v podmínce vyhodnotily již při překladu (viz též předchozí kapitolu):

# asmsyntax=as

x=42
y=10

.if x > y
    .print "vetsi"
.else
    .print "mensi"
.endif

.if x % 2 == 0
    .print "suda"
.else
    .print "licha"
.endif

Po spuštění GNU Assembleru získáme na standardním výstupu dvě zprávy:

as test.s
 
vetsi
suda

Podobným způsobem lze zařídit podmíněný překlad některých částí kódu:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - direktivy .if a .endif
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix

printFirstMessage  = 1
printSecondMessage = 0
printThirdMessage  = 1


# Linux kernel system call table
sys_exit   = 1
sys_write  = 4

# Dalsi konstanty pouzite v programu - standardni streamy
std_input  = 0
std_output = 1



#-----------------------------------------------------------------------------

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        writeMessage \message, \messageLength
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm



#-----------------------------------------------------------------------------
.section .data
message1:
        .string "Hello world\n"
message1len = $ - message1         # delka prvni zpravy

message2:
        .string "Vitejte na mojefedora.cz\n"
message2len = $ - message2         # delka druhe zpravy

message3:
        .string "Assembler je fajn\n"
message3len = $ - message3         # delka druhe zpravy



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        .global _start               # tento symbol ma byt dostupny i linkeru

_start:
        .if printFirstMessage
        writeMessageRepeatedly message1,message1len,10
        .endif

        .if printSecondMessage
        writeMessageRepeatedly message2,message2len,2
        .endif

        .if printThirdMessage
        writeMessageRepeatedly message3,message3len,7
        .endif

        exit                         # ukonceni aplikace



# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80
        ret

Po spuštění získáme následující výstup:

Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Assembler je fajn
Assembler je fajn
Assembler je fajn
Assembler je fajn
Assembler je fajn
Assembler je fajn
Assembler je fajn

Expanze maker:

GAS LISTING if_endif.s                   page 1


   1                    # asmsyntax=as
   2                    
   3                    # Ukazka pouziti maker v GNU Assembleru - volani makra z jineho makra
   4                    # - pouzita je "Intel" syntaxe.
   5                    #
   6                    # Autor: Pavel Tisnovsky
   7                    
   8                    .intel_syntax noprefix
   9                    
  10                    printFirstMessage  = 1
  11                    printSecondMessage = 0
  12                    printThirdMessage  = 1
  13                    
  14                    
  15                    # Linux kernel system call table
  16                    sys_exit   = 1
  17                    sys_write  = 4
  18                    
  19                    # Dalsi konstanty pouzite v programu - standardni streamy
  20                    std_input  = 0
  21                    std_output = 1
  22                    
  23                    
  24                    
  25                    #-----------------------------------------------------------------------------
  26                    
  27                    # Deklarace makra pro ukonceni aplikace
  28                    .macro exit
  29                            mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
  30                            mov   ebx, 0                 # exit code = 0
  31                            int   0x80                   # volani Linuxoveho kernelu
  32                    .endm
  33                    
  34                    
  35                    
  36                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  37                    .macro writeMessage message,messageLength
  38                            mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
  39                            mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
  40                            call  write_message          # vytisknout zpravu "Zero flag not set"
  41                    .endm
  42                    
  43                    
  44                    
  45                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  46                    .macro writeMessageRepeatedly message,messageLength,count
  47                            mov   ebp, \count            # nastaveni pocitadla
  48                            .print "Declaring label loop\@"
  49                    loop\@:                              # lokalni navesti (unikatni)
  50                            writeMessage \message, \messageLength
  51                            dec   ebp                    # snizeni hodnoty pocitadla
  52                            jnz   loop\@                 # opakovani smycky
  53                    .endm
  54                    
  55                    
  56                    
  57                    #-----------------------------------------------------------------------------


GAS LISTING if_endif.s                         page 2


  58                    .section .data
  59                    message1:
  60 0000 48656C6C              .string "Hello world\n"
  60      6F20776F 
  60      726C640A 
  60      00
  61                    message1len = $ - message1         # delka prvni zpravy
  62                    
  63                    message2:
  64 000d 56697465              .string "Vitejte na mojefedora.cz\n"
  64      6A746520 
  64      6E61206D 
  64      6F6A6566 
  64      65646F72 
  65                    message2len = $ - message2         # delka druhe zpravy
  66                    
  67                    message3:
  68 0027 41737365              .string "Assembler je fajn\n"
  68      6D626C65 
  68      72206A65 
  68      2066616A 
  68      6E0A00
  69                    message3len = $ - message3         # delka druhe zpravy
  70                    
  71                    
  72                    
  73                    #-----------------------------------------------------------------------------
  74                    .section .bss
  75                    
  76                    
  77                    
  78                    #-----------------------------------------------------------------------------
  79                    .section .text
  80                            .global _start               # tento symbol ma byt dostupny i linkeru
  81                    
  82                    _start:
  83                            .if printFirstMessage
  84                            writeMessageRepeatedly message1,message1len,10
  84 0000 BD0A0000      >  mov ebp,10
  84      00
  84                    >  .print "Declaring label loop0"
  84                    > loop0:
  84                    >  writeMessage message1,message1len
  84 0005 B9000000      >>  mov ecx,offset message1
  84      00
  84 000a BA0D0000      >>  mov edx,message1len
  84      00
  84 000f E8260000      >>  call write_message
  84      00
  84 0014 4D            >  dec ebp
  84 0015 75EE          >  jnz loop0
  85                            .endif
  86                    
  87                            .if printSecondMessage
  88                            writeMessageRepeatedly message2,message2len,2
  89                            .endif
  90                    


GAS LISTING if_endif.s                         page 3


  91                            .if printThirdMessage
  92                            writeMessageRepeatedly message3,message3len,7
  92 0017 BD070000      >  mov ebp,7
  92      00
  92                    >  .print "Declaring label loop2"
  92                    > loop2:
  92                    >  writeMessage message3,message3len
  92 001c B9270000      >>  mov ecx,offset message3
  92      00
  92 0021 BA130000      >>  mov edx,message3len
  92      00
  92 0026 E80F0000      >>  call write_message
  92      00
  92 002b 4D            >  dec ebp
  92 002c 75EE          >  jnz loop2
  93                            .endif
  94                    
  95                            exit                         # ukonceni aplikace
  95 002e B8010000      >  mov eax,sys_exit
  95      00
  95 0033 BB000000      >  mov ebx,0
  95      00
  95 0038 CD80          >  int 0x80
  96                    
  97                    
  98                    
  99                    # Podprogram pro vytisteni zpravy na standardni vystup
 100                    # Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
 101                    write_message:
 102 003a B8040000              mov   eax, sys_write         # cislo syscallu pro funkci "write"
 102      00
 103 003f BB010000              mov   ebx, std_output        # standardni vystup
 103      00
 104 0044 CD80                  int   0x80
 105 0046 C3                    ret
 106                    

9. Porovnání číselných hodnot a řetězců

Ve skutečnosti existuje hned několik dalších variant direktivy typu .if, které se od sebe odlišují podle toho, jaký typ podmínky se testuje – zda se například testuje hodnota řetězce či zda se porovnávají dvě numerické konstanty. Podívejme se na následující tabulku, kde jsou jednotlivé direktivy pro přehlednost vypsány:

# Direktiva Význam direktivy
1 .if test na výsledek výrazu
2 .ifne alias pro předchozí direktivu
3 .ifdef test, zda symbol existuje
4 .ifndef test, zda symbol neexistuje
5 .ifnotdef alias pro předchozí direktivu
6 .ifc porovnání dvou řetězců na rovnost
7 .ifeqs alias pro předchozí direktivu (EQual Strings)
8 .ifnc porovnání dvou řetězců na nerovnost
9 .ifnes alias pro předchozí direktivu (Not Equal Strings))
10 .ifeq test výrazu na nulu
11 .ifge test výrazu na kladné číslo či nulu
12 .ifgt test výrazu na kladné číslo
13 .ifle test výrazu na záporné číslo či nulu
14 .iflt test výrazu na záporné číslo

Vidíme, že z názvů direktiv lze snadno poznat, jaká podmínka (či její negace) se makrosystémem GNU Debuggeru testuje.

10. Čtvrtý demonstrační příklad – porovnání řetězců

Použití direktiv pro porovnání řetězců si ukažme na velmi jednoduchém (umělém) příkladu:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - direktivy pro porovnani retezcu
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix



.print ".ifc"

.ifc "Hello", "World"
    .print "Hello == World"
.else
    .print "Hello != World"
.endif

.ifc "Hello", "Hello"
    .print "Hello == Hello"
.else
    .print "Hello != Hello"
.endif

.ifc "", ""
    .print "empty strings are equal"
.else
    .print "empty strings are not equal"
.endif



.print "\n.ifeqs"

.ifeqs "Hello", "World"
    .print "Hello == World"
.else
    .print "Hello != World"
.endif

.ifeqs "Hello", "Hello"
    .print "Hello == Hello"
.else
    .print "Hello != Hello"
.endif

.ifeqs "", ""
    .print "empty strings are equal"
.else
    .print "empty strings are not equal"
.endif



.print "\n.ifnes"

.ifnes "Hello", "World"
    .print "Hello != World"
.else
    .print "Hello == World"
.endif

.ifnes "Hello", "Hello"
    .print "Hello != Hello"
.else
    .print "Hello == Hello"
.endif

.ifnes "", ""
    .print "empty strings are not equal"
.else
    .print "empty strings are equal"
.endif

Při překladu se na standardní výstup vypíšou následující zprávy:

.ifc
Hello != World
Hello == Hello
empty strings are equal

.ifeqs
Hello != World
Hello == Hello
empty strings are equal

.ifnes
Hello != World
Hello == Hello
empty strings are equal

11. Direktiva .include

Při psaní rozsáhlejších projektů se velmi často programátor dostane do situace, kdy je nutné projekt rozdělit do většího množství souborů, které se samostatně překládají a následně linkují. Ovšem mnoho maker by bylo vhodné využít v dalších souborech a sdílet je tak v rámci celého projektu. Vzhledem k tomu, že linkování je až posledním krokem při překladu a sestavování programu, je nutné pro sdílení maker použít odlišnou techniku. Ta spočívá v použití direktivy .include, která má stejný význam jako direktiva #include v programovacích jazycích C a C++: do souboru, v němž se .include použije se při překladu vloží jiný specifikovaný soubor a až teprve poté dojde k případné expanzi maker a následně k překladu (ve skutečnosti lze všechny tři fáze provádět v „pipeline“, čímž se překlad urychlí a navíc se ušetří značná část kapacity operační paměti).

12. Pátý demonstrační příklad – použití direktivy .include

Podívejme se na praktické použití direktivy .include. Předchozí příklady rozdělíme do tří souborů. V prvním z nich bude pouze makro exit:

# asmsyntax=as

# Makro pro ukonceni procesu v Linuxu.
#
# Autor: Pavel Tisnovsky

sys_exit   = 1

# Deklarace makra pro ukonceni aplikace
.macro exit
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
.endm

Ve druhém souboru jsou deklarována makra pro tisk zprávy a pro opakovaný tisk zprávy:

# asmsyntax=as

# Makro pro tisk zpravy na standardni vystup.
#
# Autor: Pavel Tisnovsky

# Linux kernel system call table
sys_write  = 4
std_output = 1


# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessage message,messageLength
        mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
        mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
        call  write_message          # vytisknout zpravu "Zero flag not set"
.endm



# Deklarace makra pro vytisteni zpravy na standardni vystup
.macro writeMessageRepeatedly message,messageLength,count
        mov   ebp, \count            # nastaveni pocitadla
        .print "Declaring label loop\@"
loop\@:                              # lokalni navesti (unikatni)
        writeMessage \message, \messageLength
        dec   ebp                    # snizeni hodnoty pocitadla
        jnz   loop\@                 # opakovani smycky
.endm



# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
write_message:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80
        ret

Třetí soubor použije první dva soubory, protože jsou do něj vloženy direktivou .include:

# asmsyntax=as

# Ukazka pouziti maker v GNU Assembleru - direktiva .include
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky

.intel_syntax noprefix

# Nacteni definice makra pro ukonceni aplikace
.include "exit.s"

# Nacteni maker pro (opakovany) tisk zpravy i prislusne subrutiny
.include "writeMessage.s"



#-----------------------------------------------------------------------------
.section .data
message1:
        .string "Hello world\n"
message1len = $ - message1         # delka prvni zpravy

message2:
        .string "Vitejte na mojefedora.cz\n"
message2len = $ - message2         # delka druhe zpravy

message3:
        .string "Assembler je fajn\n"
message3len = $ - message3         # delka druhe zpravy



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        .global _start               # tento symbol ma byt dostupny i linkeru

_start:
        writeMessageRepeatedly message1,message1len,10
        writeMessageRepeatedly message2,message2len,2
        writeMessageRepeatedly message3,message3len,7
        exit                         # ukonceni aplikace

O tom, že je vložení souborů a expanze maker provedena korektně, se přesvědčíme na formátovaném listingu:

GAS LISTING include.s                   page 1


   1                    # asmsyntax=as
   2                    
   3                    # Ukazka pouziti maker v GNU Assembleru - direktiva .include
   4                    # - pouzita je "Intel" syntaxe.
   5                    #
   6                    # Autor: Pavel Tisnovsky
   7                    
   8                    .intel_syntax noprefix
   9                    
  10                    # Nacteni definice makra pro ukonceni aplikace
  11                    .include "exit.s"
   1                    # asmsyntax=as
   2                    
   3                    # Makro pro ukonceni procesu v Linuxu.
   4                    #
   5                    # Autor: Pavel Tisnovsky
   6                    
   7                    sys_exit   = 1
   8                    
   9                    # Deklarace makra pro ukonceni aplikace
  10                    .macro exit
  11                            mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
  12                            mov   ebx, 0                 # exit code = 0
  13                            int   0x80                   # volani Linuxoveho kernelu
  14                    .endm
  15                    
  12                    
  13                    # Nacteni maker pro (opakovany) tisk zpravy i prislusne subrutiny
  14                    .include "writeMessage.s"
   1                    # asmsyntax=as
   2                    
   3                    # Makro pro tisk zpravy na standardni vystup.
   4                    #
   5                    # Autor: Pavel Tisnovsky
   6                    
   7                    # Linux kernel system call table
   8                    sys_write  = 4
   9                    std_output = 1
  10                    
  11                    
  12                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  13                    .macro writeMessage message,messageLength
  14                            mov   ecx, offset \message   # adresa retezce, ktery se ma vytisknout
  15                            mov   edx, \messageLength    # pocet znaku, ktere se maji vytisknout
  16                            call  write_message          # vytisknout zpravu "Zero flag not set"
  17                    .endm
  18                    
  19                    
  20                    
  21                    # Deklarace makra pro vytisteni zpravy na standardni vystup
  22                    .macro writeMessageRepeatedly message,messageLength,count
  23                            mov   ebp, \count            # nastaveni pocitadla
  24                            .print "Declaring label loop\@"
  25                    loop\@:                              # lokalni navesti (unikatni)
  26                            writeMessage \message, \messageLength
  27                            dec   ebp                    # snizeni hodnoty pocitadla
  28                            jnz   loop\@                 # opakovani smycky


GAS LISTING include.s                         page 2


  29                    .endm
  30                    
  31                    
  32                    
  33                    # Podprogram pro vytisteni zpravy na standardni vystup
  34                    # Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
  35                    write_message:
  36 0000 B8040000              mov   eax, sys_write         # cislo syscallu pro funkci "write"
  36      00
  37 0005 BB010000              mov   ebx, std_output        # standardni vystup
  37      00
  38 000a CD80                  int   0x80
  39 000c C3                    ret
  40                    
  15                    
  16                    
  17                    
  18                    #-----------------------------------------------------------------------------
  19                    .section .data
  20                    message1:
  21 0000 48656C6C              .string "Hello world\n"
  21      6F20776F 
  21      726C640A 
  21      00
  22                    message1len = $ - message1         # delka prvni zpravy
  23                    
  24                    message2:
  25 000d 56697465              .string "Vitejte na mojefedora.cz\n"
  25      6A746520 
  25      6E61206D 
  25      6F6A6566 
  25      65646F72 
  26                    message2len = $ - message2         # delka druhe zpravy
  27                    
  28                    message3:
  29 0027 41737365              .string "Assembler je fajn\n"
  29      6D626C65 
  29      72206A65 
  29      2066616A 
  29      6E0A00
  30                    message3len = $ - message3         # delka druhe zpravy
  31                    
  32                    
  33                    
  34                    #-----------------------------------------------------------------------------
  35                    .section .bss
  36                    
  37                    
  38                    
  39                    #-----------------------------------------------------------------------------
  40                    .section .text
  41                            .global _start               # tento symbol ma byt dostupny i linkeru
  42                    
  43                    _start:
  44                            writeMessageRepeatedly message1,message1len,10
  44 000d BD0A0000      >  mov ebp,10
  44      00


GAS LISTING include.s                         page 3


  44                    >  .print "Declaring label loop0"
  44                    > loop0:
  44                    >  writeMessage message1,message1len
  44 0012 B9000000      >>  mov ecx,offset message1
  44      00
  44 0017 BA0D0000      >>  mov edx,message1len
  44      00
  44 001c E8DFFFFF      >>  call write_message
  44      FF
  44 0021 4D            >  dec ebp
  44 0022 75EE          >  jnz loop0
  45                            writeMessageRepeatedly message2,message2len,2
  45 0024 BD020000      >  mov ebp,2
  45      00
  45                    >  .print "Declaring label loop2"
  45                    > loop2:
  45                    >  writeMessage message2,message2len
  45 0029 B90D0000      >>  mov ecx,offset message2
  45      00
  45 002e BA1A0000      >>  mov edx,message2len
  45      00
  45 0033 E8C8FFFF      >>  call write_message
  45      FF
  45 0038 4D            >  dec ebp
  45 0039 75EE          >  jnz loop2
  46                            writeMessageRepeatedly message3,message3len,7
  46 003b BD070000      >  mov ebp,7
  46      00
  46                    >  .print "Declaring label loop4"
  46                    > loop4:
  46                    >  writeMessage message3,message3len
  46 0040 B9270000      >>  mov ecx,offset message3
  46      00
  46 0045 BA130000      >>  mov edx,message3len
  46      00
  46 004a E8B1FFFF      >>  call write_message
  46      FF
  46 004f 4D            >  dec ebp
  46 0050 75EE          >  jnz loop4
  47                            exit                         # ukonceni aplikace
  47 0052 B8010000      >  mov eax,sys_exit
  47      00
  47 0057 BB000000      >  mov ebx,0
  47      00
  47 005c CD80          >  int 0x80
  48                    

13. Repositář s demonstračními příklady

Všechny dnes popisované demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, společně s podpůrnými skripty určenými pro jejich překlad či naopak pro disassembling, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny příklady jsou určeny pro GNU Assembler a používají Intel syntaxi. Následuje tabulka s odkazy na zdrojové kódy příkladů i na již zmíněné podpůrné skripty:

První demonstrační příklad – použití direktivy .print

# Soubor Popis Odkaz do repositáře
1 print_directive.s program pro GNU Assembler https://github.com/tisnik/presentations/blob/master/assembler/23_print_directive/print_directive.s
2 assemble skript pro překlad na procesorech i386 https://github.com/tisnik/presentations/blob/master/assembler/23_print_directive/assemble
3 assemble_list skript pro překlad a vygenerování listingu https://github.com/tisnik/presentations/blob/master/assembler/23_print_directive/assemble_list
4 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/23_print_directive/disassemble
5 print_directive.list výsledek skriptu assemble.list https://github.com/tisnik/presentations/blob/master/assembler/23_print_directive/print_directive.list

Druhý demonstrační příklad – ukázka volání makra z jiného makra

# Soubor Popis Odkaz do repositáře
1 nested_macros.s program pro GNU Assembler https://github.com/tisnik/presentations/blob/master/assembler/24_nested_macros/nested_macros.s
2 assemble skript pro překlad na procesorech i386 https://github.com/tisnik/presentations/blob/master/assembler/24_nested_macros/assemble
3 assemble_list skript pro překlad a vygenerování listingu https://github.com/tisnik/presentations/blob/master/assembler/24_nested_macros/assemble_list
4 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/24_nested_macros/disassemble
5 nested_macros.list výsledek skriptu assemble.list https://github.com/tisnik/presentations/blob/master/assembler/24_nested_macros/nested_macros.list

Třetí demonstrační příklad – direktivy použité pro zápis podmínek

# Soubor Popis Odkaz do repositáře
1 if_endif.s program pro GNU Assembler https://github.com/tisnik/presentations/blob/master/assembler/25_if_endif/if_endif.s
2 assemble skript pro překlad na procesorech i386 https://github.com/tisnik/presentations/blob/master/assembler/25_if_endif/assemble
3 assemble_list skript pro překlad a vygenerování listingu https://github.com/tisnik/presentations/blob/master/assembler/25_if_endif/assemble_list
4 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/25_if_endif/disassemble
5 if_endif.list výsledek skriptu assemble.list https://github.com/tisnik/presentations/blob/master/assembler/25_if_endif/if_endif.list

Čtvrtý demonstrační příklad – porovnání řetězců

# Soubor Popis Odkaz do repositáře
1 string_comparisons.s program pro GNU Assembler https://github.com/tisnik/presentations/blob/master/assembler/26_string_comparisons/string_comparisons.s
2 assemble skript pro překlad na procesorech i386 https://github.com/tisnik/presentations/blob/master/assembler/26_string_comparisons/assemble
3 assemble_list skript pro překlad a vygenerování listingu https://github.com/tisnik/presentations/blob/master/assembler/26_string_comparisons/assemble_list
4 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/26_string_comparisons/disassemble
5 string_comparisons.list výsledek skriptu assemble.list https://github.com/tisnik/presentations/blob/master/assembler/26_string_comparisons/string_comparisons.list

Pátý demonstrační příklad – použití direktivy .include

# Soubor Popis Odkaz do repositáře
1 include.s program pro GNU Assembler https://github.com/tisnik/presentations/blob/master/assembler/27_include/include.s
2 exit.s program pro GNU Assembler, který se vkládá do prvního souboru https://github.com/tisnik/presentations/blob/master/assembler/27_include/exit.s
3 writeMessage.s program pro GNU Assembler, který se vkládá do prvního souboru https://github.com/tisnik/presentations/blob/master/assembler/27_include/writeMessage.s
4 assemble skript pro překlad na procesorech i386 https://github.com/tisnik/presentations/blob/master/assembler/27_include/assemble
4 assemble_list skript pro překlad a vygenerování listingu https://github.com/tisnik/presentations/blob/master/assembler/27_include/assemble_list
5 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/27_include/disassemble
6 include.list výsledek skriptu assemble.list https://github.com/tisnik/presentations/blob/master/assembler/27_include/include.list

14. Odkazy na Internetu

  1. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnuasm.html
  2. The GNU Assembler – macros
    http://tigcc.ticalc.org/doc/gnuasm.html#SEC109
  3. ARM subroutines & program stack
    http://www.toves.org/books/armsub/
  4. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/
  5. Calling subroutines
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm
  6. ARM Assembly Language Programming
    http://peter-cockerell.net/aalp/html/frames.html
  7. ASM Flags
    http://www.cavestory.org/guides/csasm/guide/asm_flags.html
  8. Status Register
    https://en.wikipedia.org/wiki/Status_register
  9. Intel x86 JUMP quick reference
    http://unixwiz.net/techtips/x86-jumps.html
  10. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/developerworks/library/l-gas-nasm/index.html
  11. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/grygarek/asm/asmlinux.html
  12. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  13. Why Learn Assembly Language?
    http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language
  14. Is Assembly still relevant?
    http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant
  15. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html
  16. Assembly language today
    http://beust.com/weblog/2004/06/23/assembly-language-today/
  17. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz
  18. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/linux/prog/asm.html
  19. AT&T Syntax versus Intel Syntax
    https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html
  20. Linux Assembly website
    http://asm.sourceforge.net/
  21. Using Assembly Language in Linux
    http://asm.sourceforge.net/articles/linasm.html
  22. vasm
    http://sun.hasenbraten.de/vasm/
  23. vasm – dokumentace
    http://sun.hasenbraten.de/vasm/release/vasm.html
  24. The Yasm Modular Assembler Project
    http://yasm.tortall.net/
  25. 680×0:AsmOne
    http://www.amigacoding.com/index.php/680×0:AsmOne
  26. ASM-One Macro Assembler
    http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler
  27. ASM-One pages
    http://www.theflamearrows.info/documents/asmone.html
  28. Základní informace o ASM-One
    http://www.theflamearrows.info/documents/asminfo.html
  29. Linux Syscall Reference
    http://syscalls.kernelgrok.com/
  30. Programming from the Ground Up Book – Summary
    http://savannah.nongnu.org/projects/pgubook/
  31. IBM System 360/370 Compiler and Historical Documentation
    http://www.edelweb.fr/Simula/
  32. IBM 700/7000 series
    http://en.wikipedia.org/wiki/IBM_700/7000_series
  33. IBM System/360
    http://en.wikipedia.org/wiki/IBM_System/360
  34. IBM System/370
    http://en.wikipedia.org/wiki/IBM_System/370
  35. Mainframe family tree and chronology
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html
  36. 704 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html
  37. 705 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html
  38. The IBM 704
    http://www.columbia.edu/acis/history/704.html
  39. IBM Mainframe album
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html
  40. Osmibitové muzeum
    http://osmi.tarbik.com/
  41. Tesla PMI-80
    http://osmi.tarbik.com/cssr/pmi80.html
  42. PMI-80
    http://en.wikipedia.org/wiki/PMI-80
  43. PMI-80
    http://www.old-computers.com/museum/computer.asp?st=1&c=1016
  44. The 6502 overflow flag explained mathematically
    http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
  45. X86 Opcode and Instruction Reference
    http://ref.x86asm.net/coder32.html