Asembler x86
Z Wikipedii
Asembler x86 to język programowania z rodziny asemblerów do komputerów klasy PC, które posiadają architekturę głównego procesora zgodną z x86.
Język ten jest pełen sprzeczności ze względu na to, że każdy nowy procesor wprowadzający różne ulepszenia musi jednocześnie pozostawać kompatybilny z poprzednikami. W procesorach 80286 jest około 250 rozkazów, w 80486 już ok. 350, natomiast w procesorze Pentium 4 – ok. 580 (w procesorach firmy AMD jest ich ponad 620).
Język jest dodatkowo komplikowany przez obecność dwóch składni: Intela i AT&T (istnieją automatyczne translatory), oraz przez fakt, że możliwości i zasady wykorzystania tego języka są mocno zależne od systemu operacyjnego oraz kompilatora, różne są także sposoby komunikacji asemblera z innymi językami programowania.
Spis treści |
[edytuj] Rejestry
32-bitowe rejestry ogólnego przeznaczenia to:
- EAX – Accumulator (akumulator)
- EBX – Base Register (rejestr bazowy)
- ECX – Counter Register (rejestr licznikowy)
- EDX – Data Register
- ESP – Stack Pointer (wskaźnik wierzchołka stosu)
- EBP – Base Pointer
- ESI – Source Index
- EDI – Destination Index
Możliwy jest też dostęp do ich 16-bitowych mniej znaczących części – AX, BX, CX, DX, SP, BP, SI, DI, a w przypadku czterech pierwszych także do młodszego (Low) i starszego bajtu (High) – odpowiednio AL, AH, BL, BH, CL, CH, DL, DH.
W procesorach 64-bitowych do rejestrów o długości 64 bitów odwołuje się poprzez nazwę z przedrostkiem R zamiast E - np. RAX, RBX.
Są też dostępne rejestry segmentów, określające położenie segmentów pamięci w przestrzeni adresowej:
- CS – Code Segment (segment kodu)
- DS – Data Segment (segment danych)
- ES – Extra Segment (dodatkowy segment danych)
- SS – Stack Segment (segment stosu)
- FS – dodatkowy rejestr segmentu
- GS – dodatkowy rejestr segmentu
Ponadto istnieją:
- EFLAGS – rejestr flag procesora, składający się z pojedynczych bitów określających stan procesora
- EIP – wskaźnik adresowy na aktualnie wykonywaną instrukcję. Za jego pomocą procesor realizuje m.in. skoki, pętle, przejścia do podprogramów.
- rejestry kontrolne procesora CRn (n – numer rejestru)
- rejestry debugera DRn (n – numer rejestru);
- rejestry koprocesora arytmetycznego:
- osiem rejestrów stosu koprocesora, oznaczanych w zależności od kompilatora jako ST0... ST7, 0... 7 lub ST(0)... ST(7);
- rejestr stanu koprocesora;
- rejestr stanu stosu koprocesora;
- rejestr sterujący koprocesora
[edytuj] Konwencje
W składni i metodach programowania za pomocą asemblera x86 wykorzystuje się kilka ogólnie przyjętych (bądź sprzętowo narzuconych) konwencji, m.in.:
- operand docelowy instrukcji jest podawany jako pierwszy z operandów, np. instrukcja:
mov ax, bx
- spowoduje wpisanie zawartości rejestru BX do rejestru AX.
- kolejność bajtów w procesorach zgodnych z x86 to little-endian (mniej znaczący bajt pierwszy).
[edytuj] Przykłady
Poniżej dwa przykłady pod Linuksa. Pierwszy można skompilować przy użyciu nasm, drugi można skompilować asemblerem z binutils (lub samym gcc jeśli ma on rozszerzenie .s). Linkowanie w obu przypadkach gcc lub ręcznie.
Kompilacja pierwszego: nasm -f elf -o beer.o beer.asm && gcc -s -o beer beer.o
Kompilacja drugiego: gcc -s -o beer beer.s
[edytuj] Przykład składni Intela – program "99 bottles of beer"
global main extern printf section .data beer db "%d bottles of beer on the wall, %d bottles of beer." db 0x0a db "Take one down and pass it around, %d bottles of beer." db 0x0a db 0 main: mov ecx, 99 _loop: dec ecx push ecx push ecx inc ecx push ecx push ecx push beer call printf add esp,16 pop ecx or ecx, ecx jne _loop xor eax,eax ret
[edytuj] Przykład składni AT&T – ten sam program "99 bottles of beer"
.section .rodata .beer: .ascii "%d bottles of Beer on the wall, %d bottles of Beer.\n" .asciz "Take one down and pass it around, %d bottles of Beer.\n" .text .global main main: mov $99, %ecx loop: dec %ecx push %ecx push %ecx inc %ecx push %ecx push %ecx pushl $.beer call printf add $16,%esp pop %ecx or %ecx, %ecx jne loop xorl %eax,%eax ret