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
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ů
12. Pátý demonstrační příklad – použití direktivy .include
13. Repositář s demonstračními příklady
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
<strong>.print "Declaring label loop\@"</strong>
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:
<strong>as -g --32 print_directive.s -o print_directive.o</strong>
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:
<strong>as test.s</strong>
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
14. Odkazy na Internetu
- The GNU Assembler Tutorial
http://tigcc.ticalc.org/doc/gnuasm.html - The GNU Assembler - macros
http://tigcc.ticalc.org/doc/gnuasm.html#SEC109 - ARM subroutines & program stack
http://www.toves.org/books/armsub/ - Generating Mixed Source and Assembly List using GCC
http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/ - Calling subroutines
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm - ARM Assembly Language Programming
http://peter-cockerell.net/aalp/html/frames.html - ASM Flags
http://www.cavestory.org/guides/csasm/guide/asm_flags.html - Status Register
https://en.wikipedia.org/wiki/Status_register - Intel x86 JUMP quick reference
http://unixwiz.net/techtips/x86-jumps.html - Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/library/l-gas-nasm/index.html - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Is it worthwhile to learn x86 assembly language today?
https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1 - Why Learn Assembly Language?
http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language - Is Assembly still relevant?
http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant - Why Learning Assembly Language Is Still a Good Idea
http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html - Assembly language today
http://beust.com/weblog/2004/06/23/assembly-language-today/ - Assembler: Význam assembleru dnes
http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz - Assembler pod Linuxem
http://phoenix.inf.upol.cz/linux/prog/asm.html - AT&T Syntax versus Intel Syntax
https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html - Linux Assembly website
http://asm.sourceforge.net/ - Using Assembly Language in Linux
http://asm.sourceforge.net/articles/linasm.html - vasm
http://sun.hasenbraten.de/vasm/ - vasm – dokumentace
http://sun.hasenbraten.de/vasm/release/vasm.html - The Yasm Modular Assembler Project
http://yasm.tortall.net/ - 680x0:AsmOne
http://www.amigacoding.com/index.php/680x0:AsmOne - ASM-One Macro Assembler
http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler - ASM-One pages
http://www.theflamearrows.info/documents/asmone.html - Základní informace o ASM-One
http://www.theflamearrows.info/documents/asminfo.html - Linux Syscall Reference
http://syscalls.kernelgrok.com/ - Programming from the Ground Up Book - Summary
http://savannah.nongnu.org/projects/pgubook/ - IBM System 360/370 Compiler and Historical Documentation
http://www.edelweb.fr/Simula/ - IBM 700/7000 series
http://en.wikipedia.org/wiki/IBM_700/7000_series - IBM System/360
http://en.wikipedia.org/wiki/IBM_System/360 - IBM System/370
http://en.wikipedia.org/wiki/IBM_System/370 - Mainframe family tree and chronology
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html - 704 Data Processing System
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html - 705 Data Processing System
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html - The IBM 704
http://www.columbia.edu/acis/history/704.html - IBM Mainframe album
http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html - Osmibitové muzeum
http://osmi.tarbik.com/ - Tesla PMI-80
http://osmi.tarbik.com/cssr/pmi80.html - PMI-80
http://en.wikipedia.org/wiki/PMI-80 - PMI-80
http://www.old-computers.com/museum/computer.asp?st=1&c=1016 - The 6502 overflow flag explained mathematically
http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html - X86 Opcode and Instruction Reference
http://ref.x86asm.net/coder32.html