Cwiczenie 2 - Wprowadzenie skoków i pętli



W ćwiczeniu tym postaram się przybliżyć wam tzw skoki oraz pętle, które pozwalają na tworzenie bardziej skąplikowanych programów. Przekazanie sterowania lub rozgałęzienie to sposób zmiany kolejności wykonywania instrukcji. Wszystkie języki programistyczne zawierają rozkazy pozwalające na wykonanie takiej czynności. Polecenia rozgałęzienia można podzielić na dwie kategorie:

-Rozgałęzienie bezwarunkowe- program przechodzi do nowego położenia we wszystkich wypadkach. Do wskaźnika rozkazów zostaje załadowana nowa wartość, co powoduje kontynuację pracy od nowego adresu. Dobrym przykładem jest polecenieJMP, które omówimy w dalszej części tego ćwiczenia.

-Rozgałęzienie warunkowe- program przechodzi do nowego położenia, jeśli został spełniony określony warunek. Intel zapewni szeroki zakres rozkazów rozgałęzienia warunkowego; rozkazy te ułatwiają tworzenie złożonych struktur logicznych. Procesor interpretuje warunek fałszu lub prawdy na podstawie zawartości rejestrów CX i Flags



Polecenie JMP

Polecenie JMP nakazuje procesorowi kontynuację wykonywania programu od innego miejsca. To miejsce musi być identyfikowane przez etykiete, która jest przekształcana przez asembler na postać adresu. Jeżeli skok jest wykonywany do etykiety w aktualnym segmencie, to przesunięcie etykiety zostanie załadowane do rejestru IP. W przypadku skoku do innego segmentu także adres tego segmentu jest umieszczany w rejestrze CS. Zakładając, iż miejsce_docelowe to etykieta lub 32-bitowy adrs typu segment-przesunięcie, to polecenie JMP ma trzy formaty:

JMP SHORT miejsce_docelowe
JMP NEAR PTR miejsce_locelowe
JMP FAR PTR miejsce_docelowe

Polecenie JMP umożliwia skok do etykiety w aktualnej procedurze, skok do innej proce­dury, do innego segmentu, do adresu znajdującego się poza aktualnym programem oraz do dowolnego miejsca w pamięci RAM lub ROM. Choć taki sposób programowania nie jest zalecany, to czasami jest to niezbędne w czasie pracy nad oprogramowaniem syste­mowym. Poniżej pokazano kilka przykładów użycia polecenia JMP:

jmp LI ; NEAR - miejsce docelowe w aktualnym segmencie
jmp near ptr LI ; NEAR - miejsce docelowe w aktualnym segmencie
jmp short nextval ; SHORT - skok do miejsca w zakresie od -128 do 127 bajtów
jmp far ptr error_rtn ; FAR - skok do innego segmentu


Przed operandem miejsca docelowego można umieścić jeden z poniższych operatorów:

SHORT — skok do etykiety w zakresie od -128 do 127 bajtów od adresu kolejnego rozkazu. Do rejestru IP jest dodawana 8-bitowa liczba całkowita ze znakiem o nazwie przemieszczenie. NEAR PTR — skok do etykiety w dowolnym miejscu w aktualnym segmencie kodu. W rejestrze IP umieszczana jest 16-bitowa wartość przemieszczenia.
FAR PTR — skok do etykiety w innym segmencie. Adres segmentu etykiety jest umieszczany w rejestrze CS, a jej przesunięcie w rejestrze IP.

Operator SHORT jest szczególnie przydatny w czasie kodowania skoków do przodu, ponieważ asembler nie zna adresu docelowego aż do momentu zasemblowania tej części programu. Proszę przyjrzeć się poniższemu kodowi:

labell: jmp short label2 : tutaj należy użyć SHORT
labe!2: jmp labell ; automatycznie skok SHORT


Operator NEAR PTR stanowi dla asemblera informację, że docelowa etykieta znajduje się w tym samym segmencie kodu — takie założenie jest zwykle przyjmowane. Jeżeli skok jest wykonywany do etykiety poza aktualnym segmentem, to należy użyć operatora FAR PTR. Poniżej przedstawiono przykłady użycia obu operatorów z etykietą exi t jako miejscem docelowym skoku:jmp near ptr exit
jmp far ptr exit


Skoki warunkowe

Dostępne są trzy typy poleceń skuku warunkowego. Pierwszy typ wykonuje ogólne porównania i nie ma nic wspólnego z wartościami ze znakiem lub bez znaku. Drugi typ służy wyłącznie do porównywania operandów bez znaku. Trzeci typ poleceń wykonuje porównanie operandów ze znakiem.

Polecenia skoku dla ogólnych operacji porównania


Mnemonik Opis Znacznik lub rejestr
JZ Skok, jeśli zero ZF=1
JE Skok, jeśli równe ZF=1
JNZ Skok, jeśli nie zero ZF=0
JNE Skok, jeśli nie równe ZF=0
JC Skok, jeśli przeniesienie CF=1
JNC Skok, jeśli nie ma przeniesienia CF=0
JCXZ Skok, jeśli CX = 0 CX=0
JECXZ Skok, jeśli ECX = 0 ECX=0
JP Skok, jeśli parzystość PF=1
JNP Skok, jeśli nieparzystość PF=0


Porównanie operandów bez znaku

Mnemonik Opis Znaczniki
JA Skok, jeśli op1 jest większy od op2 CF=0 i ZF=0
JNBE Skok, jeśli op1 nie jest mniejszy lub równy op2 CF=0 i ZF=0
JAE Skok, jeśli op1 jest większy lub równy op2 CF=0
JNB Skok, jeśli op1 nie jest mniejszy odop2 CF=0
JB Skok, jeśli op1 jest mniejszy od op2 CF=1
JNAE Skok, jeśli op1 nie jest większy lub równy op2 CF=1
JBE Skok, jeśli op1 jest mniejszy lub równy op2 CF=1 lub ZF=1
JNA Skok, jeśli op1 nie jest większy od op2 CF=1 lub ZF=1


Polecenia skoku dla porównania liczb ze znakiem

Mnemonik Opis Znaczniki
JG Skok, jeśli op1 jest większy od op2 SF=OF i ZF=0
JNLE Skok, jeśli op1 nie jest mniejszy lub równy op2 SF=OF i ZF=0
JGE Skok, jeśli op1 jest większy lub równy op2 SF=OF
JNL Skok, jeśli op1 nie jest mniejszy od op2 SF=OF
JL Skok, jeśli op1 jest mniejszy od op2 SF<>OF
JNGE Skok, jeśli op1 nie jest większy lub równy op2 SF<>OF
JLE Skok, jeśli op1 jest mniejszy lub równy op2 ZF=1 lub SF<>OF
JNG Skok, jeśli op1 nie jest większy od op2 ZF=1 lub SF<>OF
JS Skok, jeśli wartość ze znakiem (op1 jest ujemny) SF=1
JNS Skok, jeśli wartość bez znaku (op1 jest dodatni) SF=0
JO Skok, jeśli przepełnienie OF=1
JNO Skok, jeśli brak przepełnienia OF=0


Polecenie CMP


Kolejnym bardzo przydatnympoleceniem przy tworzeniu struktur logicznych jest CMP. Polecenie CMP (porównanie) wykonuje niejawne odejmowanie operandu źródłowego od operandu docelowego, przy czym żaden operand nie jest modyfikowany. Wynik operacji jest odzwierciedlony przez stan rejestru Flags. Dozwolone są następujące kombinacje operandów, gdzie pierwszy operand jest zawsze argumentem docelowym, a drugi argumentem źródłowym:

CMP reg, reg
CMP reg, mem
CMP reg, immed
CMP mem, reg
CMP mem, immed

Nie jest możliwe użycie rejestrów segmentów. Operacja CMD ma wpływ na znaczniki: Overflow, Sign, Zero, Parity, Carry i Auxiliary Carry.

Wartości znaczników


Polecenie CMP dla operandów bez znaku powoduje ustawienie znaczników Zero i Carry:

Wynik CMP CF ZF
operand docelowy < operand źródłowy 1 0
operand docelowy = operand źródłowy 0 1
operand docelowy > operand źródłowy 0 0


W przypadku operandów ze znacznikiem ustawiane są znaczniki Zero, Sign i Overflow:

Wynik CMP ZF SF, OF
operand docelowy < operand źródłowy ? SF<>OF
operand docelowy = operand źródłowy 1 ?
operand docelowy >operand źródłowy 0 SF=OF


Skoro mamy już za sobą wprowadzenie pod względem teoretycznym czas zobaczyć jak zastosować powyższą teorie w praktyce. Napiszemy zatem 2 przykładowe programy wykorzystujące skoki warunkowe ogólnych operacji porównania. Ale zanim zobaczymy te bardziej zaawansowane pętle, zobaczmy co zrobi program który będzie wykorzystywał jedynie instrukcję skoku bezwarunkowego JMP.

.model small ; model pamięci: 1 segment programu i 1 segment danych
.386 ;dostępny zbiór rozkazów procesora 80386
.data ;początek segmentu danych
.stack l00h

;segment stosu o zadeklarowanym rozmiarze

.code

;początek segmentu z kodem programu

.startup

;makroinstrukcja generująca sekwencję

; inicjującą rejestry segmentowe DS i SS

start: mov ah,2 ;ustawienie etykiety a nastepnie wpisanie cyfry 2 do rejestru ah
mov dl,'A' ;wyświetlenie litery A na ekran
int 21h ;wywołanie procedury systemowej przerwania
jmp start ;skok do etykiety start
.exit

;makroinstrukcja generująca sekwencję końcową programu

end ;koniec programu źródłowego


Powyższy program ma na celu wyświetlenie na ekranie litery A. Jednakże ponieważ nie podaliśmy żadnego warunku zatrzymania, program ten będzie wkółko wyświetlał literę A i nastąpi jego zapętlenie. Przerwanie go umożliwia nam polecenie INT 21h oznaczające przerwanie programu w debugerze poprzez wciśnięcie klawiszy CTRL+BREAK.


Spróbujmy zatem napisać program, który wyświetli nam 4 razy napis hello world. Pominiemy już początek i koniec programu gdyż on pozostanie u nas bez zmian (patrz ćwiczenie 1), przedstawimy tylko instrukcje zawarte pomiędzy komendami .startup i .exit.

mov cx,4 ustawienie wartości w rejestrze CX na 4, będzie on służył jako licznik w naszej pętli
poczatek: etykieta
mov dx, offset tekst ;wprowadzenie parametrów wywołania
mov ah, 09h ;procedury wyświetlenia komunikatu
JCXZ koniec warunek przejścia do etykiety KONIEC, jeżeli rejestr CX będzie równy 0
jmp poczatek instrukcja skoku do etykiety poczatek, zostanie wykonana jedynie jeśli warunek JCXZ będzie fałszywy
koniec: etykieta


Teraz napiszemy program wykonujący dokładnie tę samą czynność co powyższy tym razem jednak z wykorzystaniem polecenia CMP i polecenia skoku JZ.

XOR al,al Szybkie kasowanie rejestru poprzez odjęcie wartości znajdujących się w tym rejestrze
mov cl,4 ustawienie rejestru cl na wartość 4
poczatek: etykieta
mov dx, offset tekst ;wprowadzenie parametrów wywołania
mov ah, 09h ;procedury wyświetlenia komunikatu
inc al zwiększenie rejestru al domyślnie o 1
CMP al, cl porównanie wartości rejestru al z ustawionym przez nas uprzednio rejestrem cl
JZ Koniec jeżeli z porównania rejestrów otrzymamy wynik 0, przejdziemy do etykiety koniec
jmp poczatek instrukcja skoku do etykiety poczatek, zostanie wykonana jedynie jeśli warunek JZ będzie fałszywy
koniec: etykieta



Copyright by Murathag