Documente Academic
Documente Profesional
Documente Cultură
SIPOTEANU IONELA
1. Limbajul de asamblare pentru microprocesorul 8086/8088
1.1. Generalităţi cu privire la instrucţiuni
1.2. Generalităţi privind programul de asamblare pentru 8086
1.3. Declararea datelor în limbajul de asamblare 8086/8088
1.4. Definirea şi utilizarea segmentelor în limbajul de asamblare
1.5. Utilizarea grupurilor de segmente
1.6. Definirea simplificată a segmentelor
1.7. Declararea şi utilizarea procedurilor în limbajul de asamblare
1.8. Moduri de adresare specifice 80386/80486
1.9. Reguli pentru determinarea registrului de segment implicit
1.10. Moduri de adresare specifice 80386/80486
1.11. Formatul instrucţiunilor în limbajul de asamblare
2. Repertoriul de instrucţiuni al microprocesorului 8086/8088
2.1. Instrucţiuni pentru transferul datelor
2.2. Instrucţiuni pentru transferul valorilor indicatorilor de condiţie
2.3. Instrucţiuni pentru poziţionarea indicatorilor de condiţie
2. Repertoriul de instrucţiuni al microprocesorului 8086/8088
2.1. Instrucţiuni pentru transferul datelor
2.2. Instrucţiuni pentru transferul valorilor indicatorilor de condiţie
2.3. Instrucţiuni pentru poziţionarea indicatorilor de condiţie
2.4. Instrucţiuni aritmetice
2.5. Instrucţiuni logice
2.6. Instrucţiuni pe şiruri de caractere
2.7. Instrucţiuni de salt
2.8. Instrucţiunea INT
2.9. Instrucţiuni de intrare/ieşire
2.10. Instrucţiuni specifice 80286/386/486
2.11. Utilizarea 80286/386/486 în mod protejat
Capitolul 1
Limbajul de asamblare pentru
microprocesorul 8086/8088
1.1Generalităţi cu privire la instrucţiuni
În momentul de faţă se poate lucra cu doua programe asamblor pentru familia ’86:MASM
produs al firmei Microsoft şi TASM produs al firmei Borland. Versiunile apărute în ultimi ani
ale acestor programe sunt compatibile între ele. Din acest motiv cele ce urmează se referă la
ambele produse.
Ceea ce aduce nou limbajul de asamblare pentru familia ’86 este modul de tratare
al segmentelor şi existenţa noţiunii de tip de dată. Dacă tratarea segmentelor şi de aici
tratarea specifică a mecanismului de adresare apare ca o condiţie naturală din structura şi
funcţionarea microprocesorului, existenţa tipurilor de date este mai puţin aşteptată întru-un
limbaj de asamblare.
De asemenea asamblorul este cel care trebuie să decidă dacă o instrucţiune de
reîntoarcere dintr-o procedură(ret), trebuie să actualizeze numai registrul IP(reîntoarcere în
acelaşi segment de cod) sau trebuie să actualizeze şi registrul CS(reîntoarcere în alt segment
de cod). Pentru ca asamblorul să poată să trateze în mod corect astfel de instrucţiuni trebuie
să deţină informaţiile corespunzătoare.
Spre deosebire de anumite limbaje de nivel superior pentru care se pot face
verificări ale corectitudinii accesului la resurse în faza de execuţie, pe baza informaţiilor
referitoare la tipurile de date, programul asamblor nu poate să efectueze decât verificări de
tip sintactic referitor la corespondenţele de tip de date.
Ca pentru orice limbaj de programare şi pentru limbajul de asamblare se poate
specifica alfabetul, sintaxa propoziţiilor ce pot să fie definite în acest limbaj şi semantica
acestor propoziţii. Alfabetul utilizat este alfabetul latin la care adaugă cifrele, semnele
operaţiilor aritmetice( +,-,*,/), o serie de caractere de delimitare sau punctuaţie( . , ; () [ ]) şi
alte caractere speciale( _ @ $ ? ).
Propoziţiile ce pot fi scrise în acest limbaj sunt: propoziţii ce descriu instrucţiuni,
propoziţii care descriu date şi pseudoinstrucţiuni.
1.3 Declararea datelor în limbajul
de asamblare 8086/8088
Pentru a putea urmări modul în care sunt iniţializate datele conform declaraţiilor de
date trebuie să facem câteva precizări. Şi anume datele de tip adresă sunt memorate “inversat”.
Adică, la adresa mai mică este memorat octetul mai puţin semnificativ al valorii, respectiv la
adresa mai mare se memorează octetul mai semnificativ. Aceeaşi regulă se aplică şi pentru
structurile mai complexe. Şi anume pentru o dată de tip pointer, care ocupă 4 octeţi, în primii
doi octeţi se va memora valoarea adresei relative în segment, respectiv în următorii doi octeţi se
memorează valoarea segmentului. Fiecare dintre aceste valori este memorată ca o valoare de
tip adresă.
Regula de inversare se respectă şi pentru celelalte tipuri de date. Să considerăm câteva
exemple. În figura 1.1 este prezentată o porţiune din listingul obţinut în urma unei asamblări.
Fiecare linie din program este numerotată şi numerele respective formează cea mai din stânga
coloană. A doua coloană de numere reprezintă adresele relative la care vor fi memorate datele
respective. Urmează valorile cu care se face iniţializarea. Cele trei coloane pe care le-am descris
conţin textul furnizat de către programul asamblor. Restul textului care apare în listing este cel
scris de către programator.
Conţinutul zonei de memorie rezervate pentru declaraţiile de date anterioare este
prezentat în Declaraţia
pentru ab1 iniţializează 6 octeţi cu valorile 1, 2, 3 şi codurile ASCII corespunzătoare
caracterelor a, b şi c. Urmează doi octeţi pentru care nu a fost specificat un nume. Aceşti
octeţi pot însă să fie referiţi pornind de la numele ab1. Declaraţia pentru ab3 rezervă 60 de
octeţi iniţializaţi în ordine cu valorile: 4,4,4,4,4,7,4,4,4,4,4,7,.... Pentru ab4 se vor rezerva 4
octeţi. De remarcat că în listing valorile apar scrise ca valori reprezentate pe doi, respectiv
patru octeţi. Din exemplu rezultă şi modul în care pot să fie iniţializate zone de date cu coduri
ASCII. Astfel, utilizând date de tip octet, se pot iniţializa şiruri de octeţi cu valorile ASCII
corespunzătoare. Pentru datele de tip pereche de octeţi lungimea şirului de caractere utilizat
pentru iniţializare poate să fie unu sau doi. Astfel, dacă iniţializarea se face utilizând un singur
caracter acesta va ocupa octetul mai puţin semnificativ, iar octetul mai semnificativ este
iniţializat cu 0. Dacă se utilizează un şir de caractere format din două caractere, iniţializarea
se va face conform convenţiei: primul caracter va iniţializa octetul mai puţin semnificativ, al
doilea caracter va iniţializa octetul mai semnificativ, al doilea caracter va iniţializa octetul mai
semnificativ.
Valoarea curentă a contorului program poate să fie referită cu ajutorul simbolului
$ sau al expresiei this <tip> . Să considerăm de exemplu următoarele declaraţii de date:
2 0000 55 6E 20 text db ‘un sir’
73 69 72
3 0006 06 lungime_sir db $ - text
4 0007 07 tot_lungime db this byte – text
Valoarea cu care se iniţializează variabila lungime_sir este egală cu numărul de
octeţi rezervaţi pentru variabila text. Valoarea cu care se iniţializează variabila tot_lungime
este egală cu numărul de octeţi rezervaţi între adresa text şi adresa curentă.
Limbajul permite definirea şi utilizarea unor tipuri de date definite de către
utilizator. Mecanismele oferite în această direcţie sunt structura şi iniţializarea. Să
considerăm de exemplu următoarea definiţie de structură:
2 *000 salfa struc
3 *000 01*(??) unu db ?
4 *001 01*(00) doi db 0
5 *002 01*(05) trei db 5
6 *003 01*(0001) patru dw doi ; valoare 1
7 *005 01*(00000001) cinci dd doi ; valori 1,0
8 *009 salfa ends
Spre deosebire de declaraţiile de date această declaraţie nu are ca efect o
rezervare de memorie, dar poate să fie utilizată pentru rezervarea unor zone de memorie
structurate conform acestei definiţii, de exemplu:
10 0000 05*(01*(??) 00 05 + utiliz1 salfa 5 dup (<>)
11 0001 00000001)
12 002D 01 02 0001 + utiliz2 salfa <1,2,3>
13 00000001
În primul caz se copiază de 5 ori structura salfa, fără modificarea valorilor iniţiale
ale câmpurilor din structură. A doua utilizare a structurii salfa se face cu modificarea valorilor
iniţiale ala câmpurilor unu, doi şi trei.
O înregistrare permite asocierea de nume unor câmpuri de biţi dintr-un octet sau
dintr-o pereche de octeţi. Să considerăm de exemplu următoarea declaraţie:
ralfa record xa1:3, xa2:6, xa3:7
Cu ajutorul acestei declaraţii s-a definit un tip nou de date numită ralfa, care
poate să fie utilizată pentru rezervarea de memorie similar tipurilor elementare (db, dw, etc).
Să considerăm de exemplu următoarea utilizare:
0A*(1FC3) utiliz3 ralfa 10 dup (<0,63,67>)
Se iniţializează în acest caz 10 x 2 octeţi, fiecare pereche de octeţi fiind iniţializată
cu valoarea: 63 x 2 ** 7 + 67(memorarea datelor de tip adresă se face memorând la adresa
mai mică octetul mai puţin semnificativ al valorii).
În definirea formei unei înregistrări se poate face şi iniţializarea cu valori a
câmpurilor, existând şi posibilitatea de a modifica la utilizarea definiţiei aceste valori. Să
considerăm de exemplu următoarea definiţie:
rbeta record xb1:8 = ‘A’, xb2:4 = 3,xb3:4
0014 413F utiliz4 rbeta <,3,15>
În acest caz s-a definit o pereche de octeţi care conţin respectiv valorile: ’A’ şi 3 x
2 ** 4 + 15
Numele fiecărui câmp din înregistrare are asociată o valoare egală cu numărul de
deplasări dreapta necesare pentru a se aduce câmpul respectiv în biţii cei mai puţin
semnificativi din octetul sau perechea de octeţi care formează înregistrarea. Expresia: WIDTH
<nume de câmp> are ca valoare numărul de biţi din câmpul respectiv, iar expresia: MASK
<nume de câmp> are ca valoare masca necesar[ pentru selecţia câmpului respectiv.
Limbajul permite definirea şi utilizarea unor tipuri de date definite de către
utilizator. Mecanismele oferite în această direcţie sunt structura şi iniţializarea. Să
considerăm de exemplu următoarea definiţie de structură:
2 *000 salfa struc
3 *000 01*(??) unu db ?
4 *001 01*(00) doi db 0
5 *002 01*(05) trei db 5
6 *003 01*(0001) patru dw doi ; valoare 1
7 *005 01*(00000001) cinci dd doi ; valori 1,0
8 *009 salfa ends
Spre deosebire de declaraţiile de date această declaraţie nu are ca efect o
rezervare de memorie, dar poate să fie utilizată pentru rezervarea unor zone de memorie
structurate conform acestei definiţii, de exemplu:
10 0000 05*(01*(??) 00 05 + utiliz1 salfa 5 dup (<>)
11 0001 00000001)
12 002D 01 02 0001 + utiliz2 salfa <1,2,3>
13 00000001
În primul caz se copiază de 5 ori structura salfa, fără modificarea valorilor iniţiale
ale câmpurilor din structură. A doua utilizare a structurii salfa se face cu modificarea valorilor
iniţiale ala câmpurilor unu, doi şi trei.
O înregistrare permite asocierea de nume unor câmpuri de biţi dintr-un octet sau
dintr-o pereche de octeţi. Să considerăm de exemplu următoarea declaraţie:
ralfa record xa1:3, xa2:6, xa3:7
Referirile la date se pot face cu ajutorul instrucţiunilor cu respectarea tipului de
date considerat. Pentru a ilustra referirile de date vom utiliza o instrucţiune şi anume
instrucţiunea MOV. Forma generală pentru această instrucţiune este:
mov <destinaţie>, <sursă>
Ca efect valoarea corespunzătoare sursei se copiază în destinaţia specificată în
instrucţiune. Sursa şi destinaţia trebuie să aibă acelaşi tip.
Fie de exemplu următoarea secvenţă de instrucţiuni:
a1 db 1,2,3
a2 dw 2
…
mov a1,a1 ;referire corectă
mov bx,a2 ;referire corectă
mov ax,a1 ;referire incorectă nu se respectă
;corespondenţa tipurilor
În cazul în care este necesar accesul la o dată utilizând alt tip decât cel cu care
data respectivă a fost definită se poate folosi o soluţie de forma:
a1w label word
a1b db 1,2,3
În acest caz au fost asociate două nume (a1w şi a1b) pentru aceeaşi adresă de
memorie şi sunt corecte referirile:
mov bx,a1w ;acces de tip WORD
mov b1,a1b ;acces de tip BYTE
În general pseudoinstrucţiunea LABEL este de forma:
<nume> LABEL <atribut>
unde <nume> este numele declarat: valoarea asociată acestui nume este adresa de memorie
la care se va găsi în faza de execuţie a programului prima rezervare de memorie care
urmează după această declaraţie(rezervare care poate să fie datorată unei instrucţiuni sau
unei declaraţii de date).
<atribut> poate să fie:
-FAR,NEAR- caz în care numele respective poate să fie utilizat pentru referiri din instrucţiuni
de salt sau de apel de procedură darn u poate să fie utilizat pentru referiri de tip date.
-BYTE,WORD,DWORD, un nume de înregistrare sau structură.
Să considerăm şi următoarea declaraţie:
a1w label word
a1b db 100 dup (?)
Instrucţiunea:
…. A1 0006 R mov ax,a1w(6)
este echivalentă cu
…. A1 0006 R mov ax,a1w+6
iar instrucţiunea:
….8A 1E 0006 R mov b1,a1b(6)
este echivalentă cu:
….8A 1E 0006 R mov b1,a1b+6
Execuţia instrucţiunii:
mov al,byte ptr b1w
are ca efect încărcarea în registrul AL a primului octet din cei rezervaţi pentru b1w. Ca efect
al execuţiei instrucţiunii:
mov ax,word ptr b1b
se încarcă în registrul ax primii doi octeţi rezervaţi pentru a1b.
Se observă că utilizând expresii de forma this <tip> se pot utiliza nume diferite
pentru aceeaşi zonă de date,fiecare nume având propriul său tip.
Dacă se doreşte obţinerea adresei relative şi respectiv a valorii de segment pentru
o dată se pot utiliza operatorii speciali:OFFSET şi SEG.
Se observă că variabilele alfa_ptr şi altfel conţin amândouă aceeaşi valoare
(adresă fizică a variabilei alfa).
Referirile la elementele unei structuri se fac pe baza adresei structurii şi a numelui
câmpului referit. Să considerăm de exemplu următoarea declaraţie de structură:
alfa struc
unu dw 123
doi db 5 dup(’abc’)
alfa ends
Să considerăm şi o utilizare a acestei declaraţii:
utiliz1 alfa <utiliz1>
Se observă că în acest caz în primul câmp al înregistrării s-a memorat adresa
relativă în segment a primului octet din înregistrare.
Fie instrucţiunea: mov ax,utiliz1.unu
Ca efect în registrul ax se va încărca valoarea primului cuvânt din utilizarea
structurii utiliz1: În secvenţa:
mov bx,offset utiliz1
mov ax,word ptr[bx].doi
în registrul ax se încarcă, primii doi octeţi din câmpul numit doi din structura utiliz1 (valoarea
6162H).
Referirile la elementele unei înregistrări se fac utilizând expresii care conţin
numele câmpurilor şi operatorii MASK şi WIDTH. Să considerăm de exemplu următoarea
declaraţie de înregistrare:
beta record x1:3, x2:6, x3:7
utiliz_beta beta <2,5,67>
Fie secvenţa de instrucţiuni:
mov ax,utiliz_beta
and ax,MASK x2
mov c1,x2
shr ax,c1
;deplasare dreapta
;cu (c1) biţi
este echivalentă cu următoarea secvenţă:
mov ax,utiliz_beta
and ax,0001111110000000b
mov c1,7
shr ax,c1
;deplasare dreapta
;cu (c1) biţi
şi are ca efect selectarea câmpului x2 din cei 16 biţi care formează înregistrarea, urmată de
aducerea biţilor care formează acest câmp pe poziţiile cele mai puţin semnificative .
1.4 Definirea şi utilizarea segmentelor
în limbajul de asamblare
mov ax,alfa
se va folosi registrul de segment ES. Se observă că se pot face definiri de date şi în cadrul
segmentului de cod. În instrucţiunile care urmează să se execute după instrucţiunile
mov ax,cs
mov ds,ax
Datorită mecanismului specific de adresare(utilizând registru segment) execuţia unei instrucţiuni de apel sau de
reîntoarcere din procedură se poate face în cadrul aceluiaşi segment sau între două segmente diferite. În primul caz
ceea ce trebuie să se salveze/refacă în/din stivă este numai conţinutul registrului IP în timp ce în al doilea caz
trebuie să se salveze/refacă în/din stivă şi conţinutul registrului CS. Corespunzător,pentru o instrucţiune de tip apel
de procedură,respective reîntoarcere din procedură se pot genera coduri diferite,în funcţie de contextual de
utilizare. Pentru a informa asamblorul în legătură cu modul în care trebuie să genereze cod pentru acest tip de
instrucţiuni,limbajul de asamblare permite definirea procedurilor sub forma generală:
<nume> proc [<atribut>]
<corp procedura>
<nume> endp
unde
<nume> este numele prin care se vor face apeluri la procedură. Acest nume are asociată ca valoare adresa primei
instrucţiuni din procedură. Ca orice adresă această valoare are două componente:adresa de segment şi adresa
relativă în segment. În funcţie de valoarea atributului sunt semnificative ambele componente sau numai adresa
relativă în segment
<atributul> poate să fie FAR sau NEAR. Dacă atributul lipseşte se consideră pentru el valoarea implicită NEAR.
Atributul NEAR indică faptul că apelurile la această procedură se fac fără a schimba conţinutul registrului de
segment de cod. Atributul FAR indică faptul că apelul acestei proceduri se poate face şi cu schimbarea conţinutului
registrului de segment de cod(în stivă se va salva şi adresa de segment pentru a permite reintroducerea corectă în
punctual de apel).
Să considerăm de exemplu următoarea procedură:
PROCEDURA proc far
…….
alfa:
……..
ret
PROCEDURA endp
Orice instrucţiune de apel pentru PROCEDURA are ca effect salvarea în stivă a
conţinuturilor registrului CS şi apoi a conţinutului registrului IP. La execuţia instrucţiunii de
reîntoarcere din procedură se face refacerea corespunzătoare a registrelor IP şi CS. Să
considerăm acum însă o instrucţiune de apel de procedură care se referă la numele alfa. O
astfel de instrucţiune poate să apară în acelaşi segment cu cel în care este definită procedura.
În acest caz instrucţiunea va fi tratată ca o instrucţiune de apel de procedură pentru care în
stivă trebuie să se salveze numai registrul IP. Revenirea din procedură se face cu ajutorul
instrucţiunii ret, care în acest caz se va executa incorect,deoarece contextual instrucţiunii
este FAR. Soluţiile posibile pentru această problemă pot să fie:
1.
PROCEDURA proc far
…..
alfa proc far
……
ret
alfa endp
PROCEDURA endp
2.
PROCEDURA proc far
…..
alfa label far
…….
ret
PROCEDURA endp
BX implică DS
BP implică SS
SI implică DS
DI implică DS
Microprocesorul 80386 reprezintă un pas înainte revoluţionar faţă de fraţii săi mai
mici 8088/8086/80186/80286.După iniţializare,80386 funcţionează în modul real,în care se
comportă ca un 8086 foarte rapid. Însă nu acesta este modul său “natural” de lucru;în modul
protejat microprocesorul oferă moduri de adresare mai flexibile,un spaţiu de adrese mult mai
mare,împreună cu o serie de facilităţi de multiprograme. O parte dintre aceste posibilităţi
sunt accesibile şi în modul real de funcţionare,dar expoatarea lor este posibilă(deocamdată)
numai din limbajul de asamblare.
Toate instrucţiunile care operează asupra unor date reprezentate pe un
cuvânt(16biţi) pot opera şi asupra unor date reprezentate pe un dublucuvânt(32 de
biţi).Fiecare dintre segmentele de date sau de cod care compun un program pentru 80386 are
asociat un atribut care specifică dimensiunea implicată a adreselor folosite pentru referirea la
datele sau subprogramele definite în acel segment,şi deasemenea dimensiunea implicată a
operanzilor instrucţiunilor care apar în acel segment. O declaraţie de forma
SEG segment USE16
Înseamnă că instrucţiunile care apar în segmental SEG utilizează implicit operanzi
pe 16 biţi,iar referirile la simbolurile definite în acest segment se fac implicit folosind adrese
efective exprimate pe 16 biţi .În mod similar,declaraţia
SEG segment USE32
Înseamnă că instrucţiunile care apar în segmentul SEG utilizează implicit operanzi pe 32 biţi,iar
referirile la simbolurile definite în acest segment se fac implicit folosind adrese efective
exprimate pe 32 biţi. Dacă o instrucţiune plasată într-un segment care are atributul USE16 face
referire la un operand reprezentat pe un 32 de biţi,asamblorul codifică instrucţiunea precedată
de un prefix cu valoare 66h,indicând inversarea valorii implicite pentru dimensiunea operanzilor;
similar,o instrucţiune plasată într-un segment cu atributul USE32 în care se referă in operand
reprezentat pe 16 biţi va fi codificată de asamblor cu un prefix 66h. Dacă o instrucţiune plasată
într-un segment cu atributul USE16 foloseşte un mod de adresare pe 32 de biţi specific
microprocesorului 80386,asamblorul o va codifica precedată de prefixul 67h,indicând inversarea
valorii implicite pentru dimensiunea unei adrese efective, reciproc,o instrucţiune plasată într-un
segment cu atributul USE32 în care se foloseşte un mod de adresare pe 16 biţi va fi codificată
precedată de prefixul 67h.În mod real de funcţionare(cel folosit în sistemul de operare MS-DOS)
toate segmentele de cod trebuie să aibă atributul USE16 pentru a putea fi executate. În modul
protejat de funcţionare se pot folosi atât segmente de cod USE16 cât şi segmente de cod USE32.
1.11Formatul instrucţiunilor in limbajul de asamblare
unde <registru> este un registru de 16 biţi,<operand> este o expresie care are ca valoare o
adresă de memorie, iar <adresa> este o expresie de tip dword.
Ca efect al execuţiei instrucţiunii lea se încarcă în registru adresa relativă corespunzătoare
operandului care apare în instrucţiune.
Ca efect al execuţiei instrucţiunii lds (les) se încarcă în registrul DS (ES) şi în registrul
specificat în instrucţiune, adresa aflată în memorie la adresa referită în instrucţiune. Şi
anume, cei patru octeţi aflaţi în memorie la adresa respectivă sunt interpretaţi ca o valoare
de tip pointer, deci valoarea conţinută în primii doi octeţi (offset-ul ) se încarcă în registrul
specificat în instrucţiune iar valoarea conţinută în următorii doi octeţi ( segment ) se încarcă
în registrul DS (ES).
Să considerăm următoarele date de declaraţii:
mov bx,offset b2
şi
lea dx, b2
sunt aceleaşi. Dacă însă examinăm aceste instrucţiuni cu ajutorul unui program de depanare
în momentul în care programul este încărcat în memorie, vom obţine o secvenţă care poate
să arate în modul următor:
se observă că instrucţiunea lea dx, b2 a fost înlocuită cu o instrucţiune mov. Ambele
instrucţiuni mov utilizează în mod implicit registrul ds. Analizând codul obţinut se poate
constata că instrucţiunea:
determină adresa relativă a variabilei b2 în cadrul segmentului din care face parte, iar
instrucţiunea:
lea dx, b2
determină adresa relativă a variabilei b2 faţă de registrul de segment prin intermediul căruia
se poate referi această variabilă.
Să considerăm şi următoarea secvenţă de instrucţiuni:
7 6 5 4 3 2 1 0
SF ZF AF PF CF
Structura cuvântului care se transferă în cazul instrucţiunilor PUSHF şi POPF este:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF DF IF TF SF ZF AF PF CF
“ unitate_disc: \sub1\sub2\...
\subn\nume.exe”,0
numele programului şi îl copiază într-o zonă. Se ştie că lungimea unui astfel de şir este de
maximum 128 de caractere. Şirul se găseşte în zona buffer, numele programului se copiază în
zona nume.
Instrucţiunile de salt şi apel de procedură între segmente se pot executa indirect numai prin
intermediul memoriei, utilizând date de tip dublu cuvânt. Instrucţiunile de salt condiţionat sunt
toate instrucţiuni cu adresare autorelative. În cazul în care se doreşte efectuarea unui salt
condiţionat pe o distanţă mai mare decât 128 de octeţi, atunci trebuie să se utilizeze două
instrucţiuni de salt. Şi anume o instrucţiune de salt condiţionat şi o instrucţiune de salt
necondiţionat.
Pentru o instrucţiune de apel de procedură între două segmente se va salva în stivă adresa
segmentului din care face parte instrucţiunea de salt şi apoi adresa relativă: a instrucţiunii
care urmează textual după instrucţiunea de apel. În cazul instrucţiunilor de reîntoarcere din
procedură se poate utiliza şi forma:
RET <n>
unde <n> este numărul de octeţi cu care se descarcă stiva după ce se preia adresa de
revenire. Această este utilă pentru cazul în care argumentele procedurii se transmit prin
intermediul stivei, pentru a descărca stiva de valorile argumentelor.
Instrucţiunile de salt şi apel de procedură între segmente se pot executa indirect
numai prin intermediul memoriei, utilizând date de tip dublu cuvânt (DD). Instrucţiunile de
salt condiţionat sunt toate instrucţiuni cu adresare autorelative. În cazul în care se doreşte
efectuarea unui salt condiţionat pe o distanţă mai mare decât 128 de octeţi, atunci trebuie să
se utilizeze două instrucţiuni de salt. Şi anume o instrucţiune de salt condiţionat şi o
instrucţiune de salt necondiţionat
Să considerăm de exemplu problema ordonării a două numere fără semn
reprezentate pe 16 biţi. Cele două numere se găsesc la adresele unu şi doi. În urma execuţiei
următoarei secvenţe de program la dresele de memorie unu şi doi se vor găsi valorile în
ordine descrescătoare.
mov ax,unu
cmp ax,doi
jbe gata
xchg ax,doi
mov unu,ax
gata:
Dacă numerele sunt reprezentate cu semn secvenţa se modifică în modul următor:
mov ax,unu
cmp ax,doi
jle gata
xchg ax,doi
mov unu,ax
gata:
Instrucţiunile pentru controlul ciclurilor permit o programare uşoară a structurilor de
control de tip ciclu cu testul la sfârşit:
LOOP adresa ; se decrementează CX şi dacă
; (CX) <> 0 se execută saltul
LOOPEZ adresa ; se decrementează CX şi dacă (CX) <> 0 şi
; ZF = 1 se execută saltul
LOOPNZ adresa ; se decrementează CX şi dacă (CX) <> 0 şi
; ZF = 0 se execută saltul
iar:
scasb
loopez iar
dec di
2.8 Instrucţiunea INT
Instrucţiunea INT permite generarea unor întreruperi software. Forma generală pentru instrucţiunea INT este:
INT <număr>
unde <număr> este numărul nivelului de întrerupere asociat semnalului de întrerupere “generat”. Sunt posibile
256 nivele de întrerupere. Efectul instrucţiunii INT constă din salvarea în stivă a indicatorilor de condiţie, a
conţinutului registrelor CS şi IP, anularea indicatorilor de condiţie TF şi IF şi efectuarea saltului la adresa
memorată în cei patru octeţi de la adresa specifică nivelului de întrerupere. Adresa asociată unui nivel de
întrerupere se obţine înmulţind cu 4 numărul semnalului de întrerupere. Adresa de segment pentru adresa
asociată este zero. În general legătura cu sistemul de operare ( BIOS,DOS ) se realizează utilizând instrucţiuni
de tip INT. Reîntoarcerea din procedurile de tratare a întreruperilor se face cu ajutorul instrucţiunii IRET ( care
reface nu numai adresa segmentului şi adresa în segment, ci şi starea indicatorilor de condiţie ).Pentru 8086 o
serie de nivele de întrerupere sunt asociate unor cauze specificate:
nivel 0 - depăşire la împărţire ( divide overflow )
nivel 1 - întrerupere pentru execuţia pas cu pas (TF = 1)
nivel 2 - întrerupere nemascabilă
nivel 3 - întrerupere “scurtă” (break point)
nivel 4 - depăşire superioară sau inferioară
Instrucţiunea INTO generează o întrerupere pe nivelul 4 dacă indicatorul OF este 1 (adică s-a înregistrat apariţia
unei depăşiri)
Instrucţiunea IRET este o instrucţiune de reîntoarcere dintr-o procedură de tratare a întreruperilor (se fac IP,CS şi
indicatorii de condiţie).
Să considerăm de exemplu situaţia, în care se doreşte efectuarea unei operaţii de
împărţire pentru care rezultatul este mai mare decât destinaţia (de exemplu se doreşte
împărţirea numărului 12345678H la numărul 10H). În acest caz trebuie să se ia precauţii
deosebite pentru a nu se produce o întrerupere nedorită pe nivel 0. Considerăm că
deîmpărţitul este un număr N reprezentat pe 32 de biţi de forma Y1Y0 cu Y1 şi Y0 numere
reprezentate pe 16 biţi. Fie X împărţitorul un număr reprezentat pe 16 biţi. Rezultatele sunt
de forma Q1Q0 cu R0 restul împărţirii.Q1,Q0 şi R0 sunt numere reprezentate pe 16 biţi. Între
aceste valori există următoarele relaţii:
mov ah,0
int 1AH
; apel BIOS pentru citire ceas
Instrucţiuni logice
Instrucţiunile ROL,ROR,RCL,SHL,SAR,SHR acceptă ca al doilea operand o valoare
imediată(nu neapărat 1 sau CL). Următoarele instrucţiuni sunt definite pentru 80386:
BSF r16, <sursa> ; Bit Scan Forward
BSR r16, <sursa> ; Bit Scan Reverse
Caută primul (BSF) sau ultimul (BSR) bit 1 din operandul <sursa> şi încarcă r16 cu indicele
acestuia. Dacă toţi biţii din <sursa> au valoarea 0, atunci ZF este setat.
BT <dest>,reg / im ; Bit Test
BTC <dest>,reg / im ; Bit Test abd Complement
BTS <dest>,reg / im ; Bit Test and Set
BTR <dest>, reg / im ; Bit Test and Reset
Încarcă în CF bitul din <dest> al cărui indice este dat de valoarea imediată im sau de conţinutul
registrului reg. După această operaţie, bitul respectiv poate fi complementat (BTC), forţat la 1
(BTS) sau la 0 (BTR).
SETcc <dest> ; SET byte
Încarcă octetul <dest> cu valoarea 1 dacă este îndeplinită condiţia cc (aceleaşi mnemonice ca
la instrucţiunile de salt). În caz contrar,încarcă octetul cu valoarea 0.
SHLD <dest>,r16,im8 ; Shift Left Double
SHLD <dest>,r16,CL
SHRD <dest>,r16,im8 ; Shift Right Double
SHRD <dest>,r16,CL
Operandul <dest> (16 biţi) este deplasat spre stânga (SHLD) sau spre dreapta (SHRD) cu im8
sau CL poziţii; în poziţiile eliberate se introduc biţi din r16 începând cu bitul 0 (SHLD) sau cu
bitul 15 (SHRD).
Instrucţiuni pentru controlul execuţiei
Instrucţiunea BOUND poate fi folosită pentru verificarea încadrării valorii
conţinute de un registru de 16 biţi între două limite.
BOUND r16, <tablim>
Operandul <tablim> trebuie să fie o zonă de două cuvinte conţinând limita
inferioară şi cea superioară admise. Comparaţia se face considerându-se valorile ca numere
cu semn. Dacă valoarea registrului testat nu se încadrează între limitele date, atunci se
execută INT 5.
Pentru intrarea într-un subprogram se foloseşte instrucţiunea ENTER , care are ca
efect crearea unei înregistrări de activare (operanzii <zona_locală> şi <nivel_imbricare> sunt
valori imediate pe 16 biţi, fără semn). Pentru procesorul 80386,
instrucţiunile de salt condiţionat acceptă şi un deplasament reprezentat pe 16/32 de biţi (în
funcţie de atributul „dimensiunea adresei” al segmentului de cod curent), nu numai pe 8 biţi
ca la 8086.
2.11Utilizarea microprocesoarelor 80286/80386/80486
în mod protejat