Asembler to program który tworzy kod maszynowy na podstawie kodu źródłowego, który został zdefiniowany w niskopoziomowym języku programowania, czyli bazującym na podstawowych operacjach procesora. W asemblerach jedno polecenie odpowiada zasadniczo jednemu rozkazowi procesora. Język ten powstał na bazie mnemoników, kilkuliterowych skrótów zrozumiałych dla człowieka. Mnemoniki zostały zdefiniowane w oparciu o odpowiednie kody binarne operacji dla danego procesora, dając tym samym wysoki stopień kontroli programistom nad działaniem procesora i wygodniejszy sposób programowania.
Każdy program napisany w języku asemblera składa się z pojedynczych instrukcji, które można
podzielić na grupy w zależności od wykonywanych przez dany rozkaz operacji. Wyróżnia się:
Rozkazy przesyłania danych – odpowiedzialne za odpowiednie
przemieszczanie danych pomiędzy
rejestrami a komórkami pamięci, biorąc pod uwagę jakiś warunek, bądź przenosząc je bezwarunkowo,
Rozkazy arytmetyki binarnej – dotyczące podstawowych operacji
arytmetycznych wykonywanych na
procesorze jak dodawanie, odejmowanie, mnożenie, dzielenie, negacja itd.,
Rozkazy arytmetyki dziesiętnej – rozkazy arytmetyczne, dotyczące
operacji na danych w postaci kodu
BCD,
Rozkazy logiczne - dotyczące logiki wykonywanych operacji
(AND,OR,XOR
,NOT,TEST),
Rozkazy przesunięć logicznych i arytmetycznych – operacje
odpowiedzialne ze modyfikacje zmian
położenia poszczególnych bitów w danych,
Rozkazy operujące na poszczególnych bitach i bajtach –rozkazy
modyfikujące i ustawiające odpowiednie wartości w bicie, bądź bajcie,
Rozkazy sterujące przesyłaniem danych – definiujące skoki w
odpowiednie miejsca pamięci przy danym warunku, bądź też bezwarunkowo,
Rozkazy łańcuchowe – dotyczące operacji na łańcuchach, bądź bajtów
zawierających odpowiednie łańcuchy,
Rozkazy sterujące flagami – dotyczące modyfikacji w rejestrze
flag,
Rozkazy rejestrów segmentowych - operujące na rejestrach
segmentowych:DS,ES,
FS,SS i
GS
Rozkazy mieszane i Rozkazy systemowe - dotyczące specjalnych
rejestrów systemowych.
Najpopularniejsze instrukcje z grup opisanych powyżej przedstawiono w poniższej tabeli:
Nazwa instrukcji | Opis instrukcji |
MOV | Prześlij, kopiuj |
PUSH | Odłóż na stos |
POP | Zdejmij ze stosu |
PUSHA/PUSHAD | Umieszczenie na stosie wszystkich rejestrów powszechnego stosowania |
POPA/POPAD | Zdejmowanie ze stosu wszystkich rejestrów powszechnego stosowania |
IN | Odczyt z portu wejściowego |
OUT | Umieszczenie w porcie wyjściowym |
ADD | Dodawanie całkowite |
ADC | Dodawanie całkowite z przeniesieniem |
SUB | Odejmowanie |
SBB | Odejmowanie całkowite z pożyczką |
IMUL | Mnożenie ze znakiem |
MUL | Mnożenie bez znaku |
IDIV | Dzielenie ze znakiem |
DIV | Dzielenie bez znaku |
INC | Zwiększanie o 1(inkrementacja) |
DEC | Zmniejszenie o 1(dekrementacja) |
NEG | Negacja |
CMP | Porównanie |
AND | Iloczyn logiczny |
OR | Suma logiczna |
XOR | Alternatywa wykluczająca |
NOT | Negacja logiczna |
TEST | Testowanie lub porównywanie logiczne bajtu lub słowa |
SAR | Przesunięcie arytmetyczne w prawo |
SHR | Przesunięcie logiczne w prawo |
SAL | Przesunięcie arytmetyczne w lewo |
SHL | Przesunięcie logiczne w lewo |
ROR | Obrót w prawo |
ROL | Obrót w lewo |
RCR | Obrót w prawo z wykorzystaniem flagi przeniesienia, CF |
RCL | Obrót w lewo z wykorzystaniem flagi przeniesienia, CF |
JMP | Skok bezwarunkowy |
JE | Skok warunkowy jeśli równe |
JZ | Skok warunkowy jeśli zero |
JNE | Skok warunkowy jeśli nie równe |
JNZ | Skok warunkowy jeśli nie zero |
JA/JNBE | Skok, jeśli powyżej/jeśli nie poniżej lub równe |
JAE/JNB | Skok, jeśli powyżej lub równe/jeśli nie poniżej |
JB/JNAE | Skok, jeśli poniżej/jeśli nie powyżej lub równe |
JBE/JNA | Skok, jeśli poniżej lub równe /jeśli nie powyżej |
JG/JNLE | Skok, jeśli większe/jeśli nie mniejsze lub równe |
JL/JNGE | Skok, jeśli mniejsze/jeśli nie większe lub równe |
JLE/JNG | Skok, jeśli mniejsze lub równe/jeśli nie większe |
LOOP | Skok z licznikiem w rejestrze ECX |
CALL | Wywołaj procedurę |
RET | Bliski powrót z procedury |
Uruchomienie pojedynczego programu oznacza przetwarzanie kolejnych instrukcji w postaci zer i jedynek które mogą być umieszczane w pamięci , jak i także w specjalnych komórkach zlokalizowanych w pamięci procesora - tzw. rejestrach. Podobnie jak w przypadku instrukcji do procesora można podzielić je na kilka grup pod względem funkcji do jakich są przeznaczone.
Grupa z której korzysta prawie każdy program, w mniejszym bądź większym stopniu. Są one
wykorzystywane w działaniach arytmetycznych podczas odwoływania się do komórek pamięci, a także
pełnią rolę pomocniczą. W systemie Windows większość operacji opiera się o 32-bitowe rejestry
ogólnego przeznaczenia:
EAX - tzw. akumulator, stosowany w większości operacji arytmetycznych; jest on wykorzystywany najczęściej i do niego najczęściej zwracane są wyniki operacji funkcji Windows API,
EBX - rejestr pomocniczy, często wskaźnik do danych w segmencie DS,
ECX - rejestr wykorzystywany jako licznik pętli oraz do wykonywania rozkazów operujących na większych ilościach danych,
EDX - rejestr wykorzystywany w operacjach arytmetycznych z reguły w parze EAX, określa także numer portu dla rozkazów operujących na urządzeniach zewnętrznych,
ESI - rejestr 32-bitowy, pomocniczy wskaźnik do danych w segmencie DS, określa źródło dla instrukcji operujących na większych ilościach danych,
EDI – rejestr 32-bitowy, pomocniczy wskaźnik do danych(często w segmencie ES), określa cel dla instrukcji operujących na większych ilościach danych,
ESP - rejestr 32-bitowy, pomocniczy w operacjach dotyczących stosu, szczególnie w prologach procedur(domyślnie odwołuje się do segmentu SS).
Wskaźnik do aktualnie wykonywanej instrukcji w bieżącym segmencie kodu CS, jest przechowywany w 32-bitowym rejestrze EIP. Wykonanie dowolnej instrukcji powoduje każdorazowo automatyczną zmianę wartości tego rejestru. Modyfikacja rejestru EIP wynika wyłącznie z aktualnie wykonywanego programu i niemożliwa jest bezpośrednia ingerencja w jego zawartość.
Rejestry segmentowe są 16-bitowe. Pierwotnie rejestry te zawierały adres segmentu pamięci w którym przechowywane były kod, dane i stos.
Na dzień dzisiejszy, przy płaskim modelu pamięci są jedynie ważne dla programistów systemowych, gdyż ich wartość nie jest zmieniana przez standardowe, typowe aplikacje w czasie całego życia programu tj. od momentu uruchomienia do jego zakończenia. Odpowiednie rejestry segmentowe to:
CS - 16-bitowy selektor określający segment kodu,
DS -16-bitowy selektor określający segment danych,
ES – 16-bitowy selektor określający dodatkowy segment danych,
SS –16 bitowy selektor określający segment stosu,
FS- 16 bitowy selektor określający obsługę wyjątków,
GS-16 bitowy selektor określający segment wykorzystywane przez system.
Rejestr EFLAGS pełni rolę rejestru wskazującego na wszelkie zmiany związane z aktualnym stanem procesora. Jest rejestrem 16-bitowym, znanym już od procesora 8086. Istnieje umowny podział rejestru na 3 części. Pierwsza, tzw. część statusowa dotyczy bitów 0,2,4,6,7,11 czyli znaczników cf, pf, af, zf ,sf i of. Wspomagają one operacje arytmetyczne, informując o związanych z wynikiem danego działania stanach. Znacznik cf dotyczy przeniesienia lub pożyczki dla liczb bez znaku, af dla liczb BCD i of dla liczb ze znakiem. Poza tym znacznik cf jest wykorzystywany przez operacje przesunięć i obrotów oraz instrukcji dodawania z przeniesieniem i odejmowania z pożyczką. Druga część rejestru to znacznik df, który odpowiada za wspomaganie działań na łańcuchach. Trzecia część rejestru określana jest jako systemowa i składa się z pozostałych bitów. Sterują one pracą procesora, nadzorują pracę krokową oraz decydują o wielu aspektach związaną z pracą w trybie chronionym.
W każdym z programów na samym początku należy określić typ instrukcji procesora, dla którego jest pisany. Przykładowo dla podanych niżej procesorów, określa się je następująco:
Procesor Intel 80386 - dyrektywa .386
Procesor Intel 80486 - dyrektywa .486
Procesor Intel 80586 - dyrektywa .586
Każdy z wymienionych procesorów pozwala na korzystanie przez program z jego instrukcji oraz instrukcji procesorów modeli wcześniejszych. Na przykład zdefiniowanie dyrektywy .586 pozwoli na używanie instrukcji zarówno procesora 80486 jak i 80386 .