Asembler do architektury 80x86, czyli popularnych "PCtów", jest skomplikowany i pełen niekonsekwencji.
Wynika to z tego, że każdy nowy procesor wprowadzaj±c różne ulepszenia musiał pozostać kompatybilny z poprzednikami. W procesorach 80286 jest około 250 rozkazów, w 80486 już ok. 350 natomiast w najnowszym procesorze – Pentium 4 – ok. 580 (w procesorach firmy AMD jest ich ponad 620).
Sprawę asemblera dla x86 komplikuje obecno¶ć dwóch składni: Intela i AT&T (istniej± automatyczne translatory), oraz to, że praktycznie w każdym systemie operacyjnym, w każdym języku programowania i kompilatorze szczegóły wykorzystania tego języka wygl±daj± inaczej.
Tryby pracy procesora
Kod może działać w trzech trybach pracy:
real-mode (tryb rzeczywisty) emuluj±c stare procesory 8086;
protected-mode (tryb chroniony) wykorzystuj± pełne możliwo¶ci danego modelu procesora;
virtual 86 (wirtualny 86) - tworzony jest wirtualny procesor 8086 w trybie protected, ów wirtualny procesor nie ma pełnej władzy nad sprzętem.
Tryb wirtualny 86, pocz±wszy od Pentium 4, stał się atrybutem trybu chronionego. W praktyce ten tryb służy do uruchamiana starych programów DOS-owych – system Windows sam rozpoznaje typ aplikacji i ustawia odpowiedni tryb pracy, w systemie Linux do tego celu wykorzystywany jest emulator DOS – dosemu.
Ze względu na tryb adresowania mówi się o kodzie 16-bitowym (najczę¶ciej wykorzystywanym w programach DOS-owych), w którym dostęp jest do pierwszego megabajta pamięci. W kodzie 32-bitowym tryb adresowania umożliwia dostęp do czterech gigabajtów pamięci.
Istnieje jednak pewien bł±d w procesorach 80386 i nowszych, który pozwala – podczas pracy w trybie chronionym – na tak± manipulacj± strukturami wewnętrznymi procesora, że po przeł±czeniu procesora w tryb rzeczywisty istnieje możliwo¶ć dostępu do 4GB pamięci. Mówi się wówczas, że procesor pracuje w trybie unreal lub voodoo. Ponieważ ten trik został wykorzystany przez producentów BIOS firma Intel nie może poprawić tego błędu.
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 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.
S± też dostępne rejestry segmentów:
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±:
Pod tym linkiem znajduje się bardzo duży przykład pod Turbo Assembler (kompilator Borlanda dla DOS).
Poniżej dwa małe przykłady pod Linuksa. Pierwszy można skompilować as z binutils (lub samym gcc je¶li ma on rozszerzenie .s), drugi programem nasm. Linkowanie w obu przypadkach gcc lub ręcznie.
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, 100
_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
.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 $100, %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