E-kurs programowania w asemblerze pod Windows



I.Czym jest asembler?

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.

II.Podstawowe instrukcje asemblera.

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 instrukcjiOpis instrukcji
MOVPrześlij, kopiuj
PUSHOdłóż na stos
POPZdejmij ze stosu
PUSHA/PUSHADUmieszczenie na stosie wszystkich rejestrów powszechnego stosowania
POPA/POPADZdejmowanie ze stosu wszystkich rejestrów powszechnego stosowania
INOdczyt z portu wejściowego
OUTUmieszczenie w porcie wyjściowym
ADDDodawanie całkowite
ADCDodawanie całkowite z przeniesieniem
SUBOdejmowanie
SBBOdejmowanie całkowite z pożyczką
IMULMnożenie ze znakiem
MULMnożenie bez znaku
IDIVDzielenie ze znakiem
DIVDzielenie bez znaku
INCZwiększanie o 1(inkrementacja)
DECZmniejszenie o 1(dekrementacja)
NEGNegacja
CMPPorównanie
ANDIloczyn logiczny
ORSuma logiczna
XORAlternatywa wykluczająca
NOTNegacja logiczna
TESTTestowanie lub porównywanie logiczne bajtu lub słowa
SARPrzesunięcie arytmetyczne w prawo
SHRPrzesunięcie logiczne w prawo
SALPrzesunięcie arytmetyczne w lewo
SHLPrzesunięcie logiczne w lewo
RORObrót w prawo
ROLObrót w lewo
RCRObrót w prawo z wykorzystaniem flagi przeniesienia, CF
RCLObrót w lewo z wykorzystaniem flagi przeniesienia, CF
JMPSkok bezwarunkowy
JESkok warunkowy jeśli równe
JZSkok warunkowy jeśli zero
JNESkok warunkowy jeśli nie równe
JNZSkok warunkowy jeśli nie zero
JA/JNBESkok, jeśli powyżej/jeśli nie poniżej lub równe
JAE/JNBSkok, jeśli powyżej lub równe/jeśli nie poniżej
JB/JNAESkok, jeśli poniżej/jeśli nie powyżej lub równe
JBE/JNASkok, jeśli poniżej lub równe /jeśli nie powyżej
JG/JNLESkok, jeśli większe/jeśli nie mniejsze lub równe
JL/JNGESkok, jeśli mniejsze/jeśli nie większe lub równe
JLE/JNGSkok, jeśli mniejsze lub równe/jeśli nie większe
LOOPSkok z licznikiem w rejestrze ECX
CALLWywołaj procedurę
RETBliski powrót z procedury

III.Rejestry procesora.

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.

a)Rejestry ogólnego przeznaczenia

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).

b)Rejestr EIP

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ść.

c)Rejestry segmentowe

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.

d)Rejestr EFLAGS

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.

IV.Rodzaje instrukcji procesora

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 .



Jacek Gajda , e-mail: jack9872@op.pl