Sunteți pe pagina 1din 79

INDICATORI DE CONTROL SI SEMNALE

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

Resursele cu care operează instrucţiunile microprocesorului sunt : memoria,


registrele, indicatorii de condiţie şi porturile de intrare-ieşire.
Memoria. Microprocesorul 8086 are posibilitatea să adreseze un spaţiu de
memorie de 1M octeţi. Adresarea acestui spaţiu de memorie se face cu ajutorul a 20 de linii
de adresă. Microprocesorul “vede” memoria organizată în segmente de 64K adresabile pe
baza unui registru de segment. Un segment poate începe de la orice adresă de memorie care
este multiplu de 16. Segmentele se pot suprapune,total sau parţial,pot fi adiacente sau
disjuncte. La un moment dat sunt active patru segmente,corespunzătoare acelor registre
segment. Adresarea fizică pentru o celulă de memorie se poate obţine însumând adresa de
segment înmulţită cu 16 cu adresa relativa în segment. Rezultă în acest mod o adresă
reprezentată pe 20 biţi. Pentru datele reprezentate pe 16 biţi,deci pe 2 octeţi,octetul mai
puţin semnificativ este memorat la adresa mai mică din pereche iar octetul mai semnificativ
este memorat la adresa mai mare din pereche.
Pentru specificarea unei adrese se utilizează notaţia: <adresa1>:<adresa2>, unde
<adresa1> reprezintă adresa segmentului corespunzător adresei respective iar <adresa2>
reprezintă adresa relativă în segment pentru adresa respectivă. Evident aceeaşi adresă fizică
poate să fie reprezentată prin mai multe construcţii diferite de tipul de mai sus,în funcţie de
modul în care se constituie cele două componente ale adresei.
Actualizarea conţinutului registrelor segment se face explicit prin execuţia unor
instrucţiuni,deci controlul poziţiei segmentelor în memorie este la dispoziţia programelor.
Registrele microprocesorului pot să fie clasificate din punct de vedere al rolului
pe care îl au în execuţia instrucţiunilor în 5 grupuri:
1.Grupul registrelor generale AX,BX,CX,DX
2.Grupul registrelor indicatoare de adresă:SP,BP
3.Grupul registrelor index:SI,DI
4.Grupul registrelor de segment:CS,DS,ES,SS
5.Registrul adresei instrucţiunii curente:IP.
Registrele generale(AX,BX,CX,DX) sunt utilizate în instrucţiunile aritmetrice şi
logice. Majoritatea instrucţiunilor aritmetice utilizează în acelaşi mod toate registrele. Există
însă instrucţiuni aritmetice pentru care anumite registre generale au semnificaţii speciale.
Astfel,registrul AX constituind registrul implicit pentru anumite instrucţiuni şi este denumit
de obicei registru acumulator,registrul BX este utilizabil ca registru de bază în instrucţiunile
care folosesc posibil pentru anumite instrucţiuni,iar registrul DX este utilizat în operaţiile de
înmulţire şi de asemenea poate să conţină adresa unui port pentru instrucţiunile de I/E.
Toate cele 4 registre generale pot să fie utilizate şi ca perechi de registre de 8 biţi.
De exemplu,registrul AX este format din registrele AH şi AL. Registrul AH reprezintă octetul
mai semnificativ al registrului AX,iar AL reprezintă octetul mai puţin semnificativ al registrului
AX.
Registrele indicatoare de adresă(SP,BP) sunt registre care conţin adrese relative
în segmentul stivă curent.
Registrul SP are semnificaţia de adresă curentă a vârfului stivei. De obicei registrul BP
este utilizat pentru a permite accesul la informaţia conţinută în stivă fără a o extrage din
stivă. Registrul BP poate să fie utilizat şi pentru adresarea în cadrul altor segmente.
Registrele SP şi BP pot să fie utilizate în anumite instrucţiuni aritmetice şi logice ca registre de
16 biţi.
Registrele index(SI,DI) sunt utilizate în general pentru adresarea indexată,
conţinând adrese relative în segmentul de date curent. Segmentul implicit utilizat în adresare
poate să fie modificat prin utilizarea în instrucţiuni a unor prefixe speciale.
Registrele index sunt utilizate ca registre index implicite în instrucţiuni de transfer sau
prelucrare pe şiruri de octeţi. În acest ultim caz registrul SI conţine adresa relativă curentă a
şirului sursă în cadrul segmentului de date curent,iar DI conţine adresa relativă curentă a
şirului de destinaţie in cadrul segmentului de date suplimentar. Şi registrele index sunt
utilizabile în instrucţiuni de transfer de date şi în instrucţiunile aritmetice şi logice ca registre
pe 16 biţi.
Registrele de segment(CS,DS,ES,SS) conţin adresa segmentului de
program(CS),adresa segmentului de date curent(DS),adresa segmentului de date
suplimentar(ES) şi adresa segmentului de stivă(SS). Instrucţiunea care urmează să se execute
se găseşte în segmentul a cărui adresă se află în registrul CS,la adresa relativă în segment
conţinută în IP.
Conţinutul registrului DS defineşte segmentul de date curent. Toate referirile la datele din
memorie cu excepţia celor care utilizează registrele BP,SP sau registrul DI în instrucţiunile
pentru şiruri de octeţi,utilizează în mod implicit segmentul referit de registrul DS.
Conţinutul registrului SS defineşte segmentul curent al stivei. Toate referirile la
datele din memorie care utilizează in mod implicit sau explicit registrele SP sau BP sunt
considerate ca implicând registrul stivei.
Conţinutul registrului ES defineşte un segment de date suplimentar. Referirile la
date în instrucţiunile pe şiruri de octeţi care utilizează registrul DI sunt considerate că
utilizează în mod implicit segmentul referit de registrul ES.
Indicatorii de condiţie sunt utilizaţi pentru a memora informaţii referitoare la
rezultatul unor operaţii aritmetice sau logice(AF,CF,OF,PF,SF,ZF) şi pentru memorarea unor
informaţii de control pentru microprocesor(DF,IF,TF).
Indicatorul AF(Auxiliary Carry) este unu dacă în execuţia unei instrucţiuni care
poziţionează acest indicator a apărut un transport din rangul 3 spre rangul 4 sau a fost
efectuat un împrumut din rangul 4 spre rangul 3. Acest indicator este utilizat pentru
implementarea aritmeticii pentru numere zecimale codificate binar.
Indicatorul CF(Carry) este unu dacă în execuţia unei instrucţiuni care poziţionează
acest indicator a apărut un transport sau s-a făcut un împrumut în rangul cel mai
semnificativ. De asemenea instrucţiunile de rotire a conţinutului unui registru pot să
poziţioneze acest indicator.
Indicatorul OF(Overflew) este unu dacă în execuţia unei instrucţiuni aritmetice cu
semn a apărut o depăşire , adică s-a obţinut un rezultat care nu poate să fie memorat corect
în destinaţia stabilită de către instrucţiune.
Indicatorul SF(Sign) este unu dacă din execuţia unei instrucţiuni care poziţionează
acest indicator s-a obţinut un rezultat pentru care bitul cel mai semnificativ este unu.
Indicatorul PF(Parity) este unu dacă din execuţia unei instrucţiuni care
poziţionează acest indicator s-a obţinut un rezultat pentru care octetul mai puţin semnificativ
are un număr par de biţi cu valoare unu.
Indicatorul ZF(Zero) este unu dacă in execuţia unei instrucţiuni care poziţionează
acest indicator s-a obţinut rezultatul zero.
Indicatorul DF(Direction) indică direcţia de parcurgere a şirurilor de octeţi în cazul
instrucţiunilor pe şiruri de octeţi. Valoarea zero a acestui indicator indică parcurgerea
şirurilor de la adrese mici spre adrese mari.
Indicatorul IF(Interrupt) controlează acceptarea semnalelor de întrerupere
externă. Dacă indicatorul IF este unu este activă acceptarea semnalelor de întrerupere
externă. Indicatorul nu are influenţă în cazul semnalului de întrerupere nemascabilă.
Indicatorul TF(Trace) este utilizat pentru controlul execuţiei instrucţiunilor în
regim pas cu pas în scopul depanării programelor. Dacă acest indicator este unu, după
execuţia fiecărei instrucţiuni se va genera un semnal de întrerupere intern (pe nivelul 1).
Execuţia secvenţei de tratare acestei întreruperi se face cu indicatorul TF având valoarea
zero.

1.2 Generalităţi privind programul asamblor pentru 8086

Î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

Forma generala pentru o declaraţie de date este:


[<nume>] <tip> <lista expresii> [<coment.>]
sau
[<nume>] <tip> <factor> DUP (<lista expresii>) [<coment.>]
unde
<nume> este numele prin care va fi referită data. Acest nume are asociat un tip şi
o valoare. Tipul rezultă din tipul datei iar valoarea la care se va găsi în memorie în timpul
execuţiei programului primul octet rezervat pentru data etichetată cu numele respectiv. Un
nume este format din maximum 31 de litere, cifre şi caractere speciale (_,?,$,@) din care
primul caracter este o literă sau un caracter special (_,?,$,@,.). Limbajul de asamblare
utilizează nume rezervate care nu pot să fie utilizate ca etichete. Aceste nume rezervate sunt:
numele instrucţiunilor, pseudoinstrucţiunilor, operatorilor,registrelor şi numele $ şi ?.
<lista expresii> reprezintă lista unor expresii cu ale căror valori se vor iniţializa
zonele de date rezervate pentru declaraţia respectivă;
<factor> este un număr care indică de câte ori se repetă lista de expresii care
urmează în paranteză.
Expresiile utilizate pentru iniţializarea datelor sunt expresii care pot să fie evaluate în
momentul asamblării programului. Expresiile utilizează ca operanzi constante numerice,
alfanumerice şi nume pentru care asamblorul asociază valori(nume de date,etichete de
instrucţiuni,etc). Constantele numerice pot să fie întregi sau reale. În cazul constantelor
întregi reprezentarea se poate face în baza 2,8 10 sau 16. Baza în care se reprezintă o
constantă este specificată prin una din literele: B, Q, D, H, care urmează după cifrele care
formează constanta. Dacă după cifrele care reprezintă constanta nu se specifică baza,
înseamnă că s-a specificat o constantă în baza 10. Pentru constantele reale se poate utiliza şi
formatul cu un exponent. În cazul constantelor alfanumerice şirurile de caractere
corespunzătoare se scriu între ghilimele (“) sau între caractere apostrof (’). Pentru fiecare
caracter dintr-un sir de caractere valoarea corespunzătoare este codul ASCII al caracterului
respectiv.
În expresii se pot utiliza operatori aritmetici(+,-,*,/,MOD), operatori de deplasare
(SHR, SHL), operatori logici(NOT, AND, OR, XOR), operatori relaţionari (EQ, NE, LT, GT, GE),
cu convenţia că adevărat se reprezintă ca 1 respectiv fals se reprezintă ca 0),operatori de
conversie de tip (PTR, SHORT, LOW, HIGH,SEG, OFFSET, LENGTH, SIZE,MASK).Ordinea de
prioritate a operatorilor este:
1.LENGTH,SIZE,WIDTH,MASK;
2.PTR, OFFSET,SEG;
3.HIGH, LOW;
4.+, -(operatori unari);
5.*, /, MOD, SHL, SHR;
6.+, -(operatori binari);
7.EQ, NE, LT, LE, GT, GE;
8.NOT;
9.AND;
10.OR, XOR;
11.SHORT.
Tipurile de date ce pot să fie utilizate sunt:
DB date de tip octet (BYTE)
DW date de tip cuvânt sau adresă(WORD)
DD date de tip pointer(DWORD)
DQ date de tip 8 octeţi(QWORD)
DT date de tp 10 octeţi (TBYTE)

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

La un moment dat microprocesorul poate să facă acces la patru segmente logice:


Segmentul de cod – accesibil prin CS
Segmentul de date curent – accesibil prin DS
Segmentul de date suplimentar – accesibil prin ES
Segmentul de stivă – accesibil prin SS
Aceste segmente logice pot să corespundă la patru segmente fizice distincte dar
pot să existe şi coincidenţe şi/sau suprapuneri parţiale între aceste segmente.
Definirea unui segment se face sub forma:
<nume> SEGMENT [<tip aliniere>] [<tip>] [<clasa>]

<corpul segmetului>

<nume> ENDS
unde <nume> este numele asociat segmentului, care nu poate să fie utilizat cu altă semnificaţie în
program. Acestui nume i se se asociază o valoare şi anume adresa de segment (16 biţi)
corespunzătoare poziţiei segmentului în memorie în faza de execuţie a programului (această valoare se
obţine împărţind cu 16 sau deplasând la dreapta cu 4 poziţii adresa de 20 de biţi corespunzătoare
adresei fizice). Corespunzător secvenţei de instrucţiuni:
MOV AX, <nume>
MOV DS,AX
are ca efect încărcarea în registrul DS a adresei de bază a segmentului <nume>.
Următoarele referiri la memorie care implică registrul DS trebuie să se refere la date din cadrul
acestui segment (asamblorul nu poate să verifice corectitudinea unor astfel de referiri). De
asemenea asamblorul nu are posibilitatea să verifice că în momentul în care se executa o
instrucţiune care presupune referirea la o variabilă aflată într-un anumit segment conţinutului
registrului de segment este cel corect;
<tip aliniere> poate să fie: PARA,BYTE,WORD sau PAGE indicând tipul adresei de început
a segmentului. Şi anume: PARA/BYTE/WORD/PAGE/ indică faptul că adresa de început a zonei de
memorie rezervată segmentului este divizibilă cu 16/1/2/256. Valoarea adresei de segment utilizată
se obţine prin trunchierea acestei adrese.
<tip> este o informaţie pentru editorul de legături. Această informaţie indică raportul
între acest segment şi segmente definite în alte module obiect. Tipul poate să fie:
PUBLIC – dacă într-un alt modul obiect, cu acre se leagă modul obiect curent este conţinut un
segment cu acelaşi nume, atunci se va face o concatenare a celor două segmente, obţinându-se un
unic segment;
COMMON – dacă în alt modul obiect cu care se leagă modulul obiect curent, este conţinut un
segment cu acelaşi nume, atunci se va face o suprapunere a celor două segmente (cele doua
segmente încep la aceeaşi adresă).
AT <expresie> - segmentul va fi încărcat în memorie la adresa segment reprezentată de
valoarea expresiei;
STACK- segmentul curent este segmentul stivă în faza de execuţie a programului.
MEMORY – segmentul curent va fi aşezat în memorie în spaţiul disponibil rămas după aşezarea
celorlalte segmente în memorie.
<clasa> este un nume cuprins între ghilimele simple. Rolul acestui element este de a
permite stabilirea modului în care se aşează în memorie segmentele care compun un program.
Dacă pentru un segment nu se specifică clasa, se consideră în mod implicit şirul de caractere
vid.
Două segmente cu aceeaşi clasă vor fi aşezate în memorie la adrese succesive. Exemple:
1.code1 segment

code1 ends
data1 segment

data1 ends
2.code1 segment

data1 segment

data1 ends
code1 ends
Exemplul 2 are acelaşi efect ca şi exemplul 1 şi anume se definesc două segmente distincte.
3 .stiva segment stack
dw 100 dup(0) ; iniţializare cu 0 a unei zone de 100 x 2 octeţi
varf_stiva label word ; asociere nume pentru această adresă
stiva ends
initializare_stiva segment
assume cs:initializare_stiva, ss:stiva
mov ax,stiva
mov ss,ax ; actualizare registru segment stiva
mov sp,offset varf_stiva ; actualizare indicator vârf stiva

initializare_stiva ends
Având în vedere că într-un program pot să existe mai multe segmente este
necesar ca asamblorul să ”ştie” în fiecare moment care sunt cele patru segmente logice
curente pentru a putea să genereze corect codurile instrucţiunilor. De asemenea asamblorul
trebuie să ”ştie” ce registru de segment se poate utiliza pentru a realiza accesul la datele
referite. Această informaţie este transmisă asamblorului cu ajutorul pseudoinstrucţiunii
ASSUME. Forma generală pentru această pseudoinstrucţiune este:
ASSUME {<registru_s> : <segment>}
Unde <registru_s> este numele unui registru segment iar <segment> este numele
segmentului care va fi adresat de registrul segment respective. Adresa acestui segment
trebuie să fie încărcată explicit în registrul segment. De remarcat faptul că ASSUME este o
pseudoinstrucşiune pentru care nu se generează cod, rolul ei fiind numai de a informa
asamblorul care este intenţia programatorului. Asamblorul nu are nici un mechanism prin
care să verifice dacă conţinutul registrului de segment este cel promis în pseudoinstrucţiunea
ASSUME.
În acest exemplu trebuie să remarcăm mai întâi modul în care se iniţializează
registrele segment (în aceste registre nu se pot încărca valori constante). Pentru
instrucţiunea:

mov ax,alfa

asamblorul “ştie” că instrucţiunea utilizează ca registru de bază registrul DS,deoarece


simbolul alfa a fost definit în cadrul segmentului data1 şi adresările în acest segment se fac
conform pseudoinstrucţiunii ASSUME, cu ajutorul registrului DS. Pentru instrucţiunea
mov ax,gama

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

segmentul de date coincide cu segmentul de cod. Se observă că dacă se doreşte utilizarea


pentru adresare a unui anumit registru de segment,diferit de cel implicit,, acest fapt se va
specifica explicit în instrucţiune.
1.5 Utilizarea grupurilor de segmente

Un grup de segmente poate să fie adresat utilizând un singur registru de segment


(cu condiţia ca suma lungimilor segmentelor să nu depăşească 64K) utilizând
pseudoinstrucţiunea GROUP. Forma generală a acestei pseudoinstrucţiuni este:
<nume> GROUP <lista nume segmente>
unde <nume> este numele grupului de segmente şi va fi utilizat pentru a
determina adresa de segment utilizată pentru referirea în cadrul grupului (segmentele care
formează grupul vor fi memorate concatenate fiind adresate cu acelaşi registru de segment).
Utilizând pseudoinstrucţiunea GROUP, acelaşi registru de segment poate să fie folosit pentru
adresarea datelor şi procedurilor aflate în segmente diferite.
• Adresa relativă asociată unei informaţii conţinute într-un grup este calculată faţă
de începutul grupului şi nu faţă de începutul segmentului din care face parte
1.6 Definirea simplificata a
segmentelor

Începând de la versiunea S.O a asamblorului MASM a fost un nou mecanism


pentru declararea segmentelor într-o forma simplificată. Acest tip de declarare este mai
simplu de utilizat şi este de preferat mai ales în cazul în care se leagă modele scrise în
limbajul de asamblare cu module scrise în alte limbaje. Produsele firmei Borland (TASM) au
preluat şi ele aceste mecanisme.
Pentru a utiliza acest mod de definire trebuie să se stabilească întâi care este
modelul de memorie pentru care este scris programul respectiv. În ciuda titlului,acest model
nu are o semnificaţie hardware ci una software,precizând modul în care se utilizează
segmentele în cadrul programul respective. Există următoarele modele de memorie:
1.Tiny - toate segmentele de date,segmentul de cod şi cel de stivă fac parte din
acelaşi grup.
2.Small - toate datele încap într-un singur segment care poate să fie separat de
segmentul de cod.
3.Medium - toate datele încap în 64K,dar codul poate să depăşească 64K.În acest
caz pentru cod se utilizează apeluri de tip far în timp ce pentru date se consideră adrese
relative.
4.Compact - codul se încadrează în 64K, dar datele pot să ocupe mai mult de
64K(există însă restricţia că o structură de date nu poate să depăşească 64K).
5.Large -atât datele cât şi codul pot să depăşească 64K (rămâne limitarea
referitoare la structurile de date).
6.Huge - atât cât şi codul pot să depăşească 64K.Spre deosebire de modelul Large este
înlăturată problema structurilor de date care în acest caz nu pot să depăşească 64K.
Segment:adresa relativă dar adresa relativă este mai mică decât 16.În acest fel
pentru o adresă fizică dată există o singură adresă de tip huge. Faţă de avantajul important al
existenţei structurilor de date ce ocupă peste 64K există în acest caz dezavantajul calculelor
necesare pentru a aduce fiecare adresă calculată la forma huge. În cazul implementării
Borland(TASM) există şi modelul TPascal care specifică de fapt modelul Large precizând că se vor
utiliza convenţiile de apel şi transmitere de argumente pentru subprograme conforme cu
convenţiile limbajului TurboPascal. Declararea modelului trebuie să preceadă alte
pseudoinstrucţiuni sau instrucţiuni ce simplifică referiri la segmente. Declararea modelului se
face cu ajutorul unei pseudoinstrucţiuni de forma:
MODEL tip model
Unde tip model poate să fie Tiny, Small, Medium,Compact,Large,Huge sau TPascal.
După stabilirea modelului,începutul listei propoziţiilor care fac parte dint-un segment se face cu
una dintre următoarele pseudoinstrucţiuni:
STACK[dimensiune]
CODE
DATA
DATA?
FARDATA[NUME]
FARDATA?[ NUME]
CONST
Pseudoinstrucţiunea STACK defineşte segmental de stivă precizând în mod
opţional ţi dimensiunea acestuia. Dacă nu se specifică dimensiunea segmentului de stivă se
consideră implicit valoarea 512.Pseudoinstrucţiunea CODE specifică faptul că textul care
urmează face parte din segmental de cod cu numele specificat în pseudoinstrucţiune. Dacă
acest nume lipseşte(numele este semnificativ numai pentru modelele Medium,Large şi Huge)
se va utilizeze un nume implicit(TEXT). Trecerea la alt segment de cod sau de date se face la
întâlnirea unei alte pseudoinstrucţiuni end încheie textul ce descrie ultimul segment iniţial.
Toate propoziţiile care urmează unei pseudoinstrucţiuni de tip CODE şi care utilizează acelaşi
nume sunt considerate ca făcând parte din acelaşi segment.
Declaraţiile de date care urmează după pseudoinstrucţiuni DATA vor face parte
dintr-un segment de date neiniţializate. Împreună cu segmental de date neiniţializate, cu
segmental de constante şi cu segmental de stivă,acest segment face parte dintr-un grup cu
numele DGROUP. Declaraţiile de date care urmează după pseudoinctrucţiuni DATA? vor face
parte dintr-un segment de date neiniţializate.
Pseudoinstrucţiunea FARDATA indică faptul că textul care urmează conţine
declaraţii de date care vor face parte dintr-un segment la care referirile se fac prin
intermediul unor adrese complete(segment şi adresă relativă). Dacă se utilizează un nume
acesta va avea asociată ca valoare adresa de segment a segmentului relative.
Pseudoinstrucţiunea FARDATA? are un efect similar,dar se referă la date neiniţializate.
Pseudoinstrucţiunea CONST este utilizată numai în cazul în care se face legătura cu alte
limbaje. Textul care urmează după pseudoinstrucţiunea CONST trebuie să conţină numai
declaraţii de date iniţializate. Se consideră că valorile respective mu se modifică prin execuţia
programului.
O parte dintre aceste segmente se grupează într-un grup numit DGROUP în modul
următor:
- dacă modelul este Tiny în DGROUP intră toate segmentele
- dacă modelul nu este Tiny în DGROUP intră segmentele descrise prin
pseudoinstrucţiunile DATA, DATA?,CONST,STACK.
Utilizarea formei simplificate de declarare a segmentelor nu scuteşte programul
de sarcina specificării modului de utilizare a registrelor de segment şi nici de asigurarea
conţinutului registrelor de segment conform pseudoinstrucţiunilor assume în vigoare. Dacă
nu este furnizată nici o pseudoinstrucţiune assume se consideră că pentru modelele de
memorie Small sau Compact există următoarea pseudoinstrucţiune:
assume cs:_TEXT, ds:DGROUP, ss:DGROUP
respective pseudoinstrucţiunea:
assume cs:nume_TEXT, ds:DGROUP, ss:DGROUP
pentru celelalte modele.
1.7 Declararea şi utilizarea procedurilor în limbajul de asamblare

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

În acest caz numele alfa are asociat atributul FAR.


1.8 Moduri de adresare în limbajul de asamblare 8086/8088

Pentru exemplificarea modurilor de adresare să considerăm următorul segment


de date:
assume ds: data
2 0000 data segment
3 0000 01 ab db 1
4 0001 05*(4142) aw dw 5 dup (‘AB’)
5 000B data ends
În vederea preluării operanzilor microprocesorului “ştie” să utilizeze următoarele moduri de
adresare:
Adresarea imediată. În instrucţiune apare valoarea operandului.
… B0 05 mov al,5
… B8 0005 mov ax,5
… B8 0001r mov ax,offset aw; valoarea operandului
; este adresa relativa
; in segmentul data
… B8 0000s mov ax,data ; valoarea operandului
; este adresa de segment
; pentru data
Adresarea la registere Operandul referit se găseşte într-un registru
… 8B C3 mov ax,bx
… 88 16 0000r mov ab,dl
Adresarea directă. În instrucţiune apare adresa operandului. Calculul adresei
fizice rezultă pe baza adresei de segment implicată în instrucţiune şi a adresei relative
conţinute în instrucţiune.
… A1 00001r mov ax,aw
… A1 0001r mov ax,word ptr ab+1
… A3 0003r mov aw+2,ax
… A3 0003r mov aw(2),ax
Adresarea bazată. În instrucţiune apare registrul de bază utilizat. Calculul adresei
fizice rezultă pe baza adresei de segment implicată în instrucţiune,adresa relativă în cadrul
segmentului fiind sume dintre conţinutul registrului de bază şi o adresă relativă conţinută în
instrucţiune.Ca registre de bază se pot utiliza registrele BX şi BP. În mod implicit, dacă se
utilizează ca registru de bază registrul BX, atunci se consideră ca registru de segment registrul
DS, dacă se utilizează ca registru de bază registrul BP,atunci se consideră ca registru de
segment registrul SS. De obicei adresarea bazată se utilizează pentru a referi elementele unei
structuri pentru care adresa de început este conţinută în registrul de bază,iar adresa relativă
în cadrul structurii apare explicit în instrucţiune.
;
; pregătire registru de bază utilizat
;
… BB 0001r mov bx,offset aw
;
; adresa referita: ds:bx
;
… 8B 07 mov ax,[bx]
… 8B 47 02 mov ax,[bx+2]
Adresarea bazată poate să fie interpretată şi ca o adresare indexată. Să
considerăm de exemplu următoarea secvenţă de instrucţiuni:
;
; pregătire registru de bază utilizat
;
… BD 0002 mov bp,2
… 3E: 8B 86 0001r mov ax,aw[bp] ; registrul de segment
; este ds
Adresare indexată. În instrucţiune apare registrul indexat utilizat. Calculul adresei
fizice rezultă pe baza adresei de segment implicată în instrucţiune,adresa relativă în cadrul
segmentului fiind sume dintre conţinutul registrului index şi o adresă relativă conţinută în
instrucţiune.Ca register index xe pot utiliza SI şi DI. Registrul de segment considerat implicit
este registrul DS. Adresarea indexată se utilizează pentru a referi elementele unui vector. În
acest caz în registrul index se încarcă adresa relativă în vector,iar adresa relativă în segment a
vectorului apare direct în instrucţiune.
;
; pregătire registru index utilizat
;
… BE 0005 mov si,5
;
; adresare indexată
;
… 8B 84 0001r mov ax,aw[si]
Adresarea bazată şi indexată. În instrucţiune apar registrele de bază şi index
utilizate pentru calculul adresei operandului. Calculul adresei fizice rezultă pe baza adresei de
segment implicată în instrucţiune, adresa relativă în cadrul segmentului fiind suma dintre
conţinutul registrului index,al registrului de bază şi o adresă relativă conţinută în
instrucţiune. Registrul de bază este cel care dictează registrul de segment considerat
implicit. Acest tip de adresare este util pentru a realiza accesul la structuri de date create în
stivă sau accesul la elementele unui vector de înregistrări. În acest ultim caz în registrul de
bază se încarcă adresa de început a vectorului,în registrul index se încarcă adresa relativă în
vector a înregistrării referite,iar în instrucţiune apare adresa relativă(fixă) a elementului
referit din înregistrare.
; forme echivalente
… 8B 81 0001r mov ax,aw[bx][di]
… 8B 81 0001r mov ax,aw[bx+di]
… 8B 81 0001r mov ax,[bx+di+offset aw]
Să considerăm şi următoarele declaraţii de date:
= 0064 lungime equ 100
= 01F4 număr_elemente equ 500
*000 dlista struc
*000 01*(????) catre dw ?
*002 01*(????) de_la dw ?
*004 01*(60*(??)) inf db lungime-inf dup(?)
*064 dlista ends
0000 01F4*(64*(??)) lista dlista număr_elemente dup(<>)
A fost definită o listă de elemente,fiecare element fiind format dintr-o legătură la
elemental următor (camp catre), o legătură la elemental anterior (câmpul de_la) şi o zonă de
date (câmpul inf).Secvenţa de instrucţiuni care iniţializează legăturile ”înapoi” în listă este:
;
; legătura la primul element
;
… BB 0000r mov bx,offset lista
… C7 47 02 0000 mov [bx].de_la,0
… BE 0064 mov si,lungime
… B9 01F3 mov cx,numar_elemente-1
; au mai ramas
;
; legăturile celorlalte elemente
;
… iar:
… 89 58 02 mov [bx][si].de_la, bx
… 03 DE add bx,si ; actualizare
… E2 F9 loop iar ; mai sunt
1.9Reguli pentru determinarea registrului de segment implicit

Regulile se referă la cazurile în care în instrucţiuni nu apar referiri explicite la


register segment şi de asemenea nu apar nici nume de date din care să se poată deduce
registrul de segment care trebuie să fie considerat.
1. pentru registrele de bază

BX implică DS
BP implică SS

2. pentru registrele index

SI implică DS
DI implică DS

În cazul în care în instrucţiune apare atât un registru de bază cât şi un registru


index se va aplica regula corespunzătoare registrului de bază.Să considerăm de exemplu
următor segmental de date:
Exemplul 1
1.10Moduri de adresare specifice microprocesoare 80386/80486

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

În limbajul de asamblare o instrucţiune se poate reprezenta pe o linie care are


maximum 128 de caractere şi care are forma generală:
[<eticheta>:] [<operaţie> [operanzi>] [<comentarii>]]
unde <eticheta> este un nume format din maximum 31 de litere,cifre şi caractere
speciale(_,?,$,@)din care primul caracter este o literă sau un caracter special. Limbajul de
asamblare utilizează nume rezervate care nu pot să fie utilizate ca etichetă. Aceste nume
rezervate sunt:numele instrucţiunilor şi numele $ sau ?.Orice etichetă are asociată o valoare
şi anume adresa relativă în cadrul segmentului din care face parte a primului octet din
instrucţiunea etichetată;
<operaţie> este mnemonica corespunzătoare instrucţiunii
<operanzi> este un câmp a cărui existenţă şi formă dependent de tipul instrucţiunii
Capitolul 2
Repertoriul de instrucţiuni al
microprocesorului 8086/ 8088

2.1. Instrucţiuni pentru transferul datelor

În această categorie se încadrează următoarele tipuri de instrucţiuni:


instrucţiuni de transfer re tip „clasic”
instrucţiuni de transfer pentru adrese
instrucţiuni de transfer pentru indicatori de condiţie
În categoria instrucţiunilor de transfer de tip „clasic” se încadrează următoarele instrucţiuni:
MOV <destinaţie>,<sursa>
PUSH <sursa>
POP <destinaţie>
XCHG <destinaţie>,<sursa>
XLAT
Efectul instrucţiunii MOV este de a transfera valoarea desemnată de <sursa> la <destinaţie>.

Instrucţiunile PUSH şi POP sunt instrucţiuni de lucru cu stiva.


. Să considerăm de exemplu următoarele instrucţiuni:
push ds ; se decrementează registrul sp (indicatorul
; vârfului stivei) cu 2 şi se memorează
; conţinutul registrului ds.
pop es ; se preia valoarea memorată în vârful stivei în
; registrul es şi se incrementează registrul sp
; (indicatorul vârfului stivei) cu 2
O secvenţă cu efect echivalent este:
mov ax,ds
mov es,ax
A doua secvenţă este de preferat dacă există un registru general disponibil.
Alte exemple:
Push dataw ; se decrementează registrul sp (indicatorul
; vârfului stivei) cu 2 şi se memorează
; valoarea aflată în memorie la adresa dataw
push dataw [bx] ; se decrementează registrul sp (indicatorul
; vârfului stivei) cu 2 şi se memorează
; valoarea aflată în memorie
; la adresa dataw + (bx)
push dataw[bx][di] ; se decrementează registrul sp
; (indicatorul vârfului stivei)
; cu 2 şi se memorează valoarea aflată
; dataw + (bx) + (di)
Încărcarea registrului cs prin intermediul stivei se poate face numai executând o
instrucţiune de reîntoarcere din procedură în context far. Accesul la informaţiile memorate
în stivă se face fără descărcarea stivei,utilizând adresarea bazată în modul următor:
;
; secvenţa de memorare informaţii în stivă
;
mov bp,sp ; „baza stivei”
push ax ; SP = BP - 2
push bx ; SP = BP - 4
push cx ; SP = BP - 6
……
;
; secvenţa de acces la informaţiile din stivă
;
mov ax, [bp-2] ; prima informaţie introdusă
mov bx, [bp-4] ; a doua informaţie introdusă
mov cx, [bp-6] ; ultima informaţie introdusă
….
add sp,6 ; descarcă stiva
Instrucţiunea XCHG are ca efect schimbarea conţinutului sursei cu destinaţia. Să
considerăm ca exemplu următoarele instrucţiuni:
xchg ax,bx ; se schimbă între ele conţinuturile
; registrelor ax şi bx
xchg al,byte ptr x ; se schimbă conţinutul registrului al
; cu cel al octetului aflat în memorie
; la adresa x
Instrucţiunea XLAT converteşte conţinutul registrului al utilizând o tabelă a cărui
adresă de început este conţinută în registrul bx. Conţinutul registrului al este interpretat cu
adresă relativă în tabelă. Rezultatul conversiei este dat de valoarea octetului aflat în tabelă la
adresa relativă conţinută în al. Să considarăm de exemplu procedura care determină codul
ASCII corespunzător unei cifre în reprezentarea numerelor în baza 16 (având deci o valoare
mai mică decât 16):
;
; conversie binar-> ASCII
;
conv proc near
mov bx,offset tabc
xlat
ret
conv endp
tabc label byte
db ‘0123456789ABCDEF’
Se observă că instrucţiunea xlat are forma generală:
xlat
sau
xlat <adresa>
Utilizarea referii la o adresă în instrucţiunea xlat este necesară pentru a permite
asamblorului să determine registrul segment care trebuie să fie utilizat în execuţia
instrucţiunii ( evident registrul bx va conţine numai adresa relativă în cadrul segmentului în
care este memorată, a tabelei de conversie ).
Instrucţiunile de transfer pentru adrese sunt:

LEA <registru>, <operand>


LDS <registru>, <adresa>
LES <registru>, <adresa>

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:

De remarcat faptul că instrucţiunea


mov bx,offset aa
se execută mai rapid decât instrucţiunea:
lea bx,aa
Spre deosebire de utilizarea unei instrucţiuni de forma:
mov bx,offset………
instrucţiunea lea permite şi utilizarea unei indexări pentru încărcarea într-un
registru a unei adrese relative în segment. De exemplu instrucţiunile:

mov ax,offset datab


şi
lea ax,datab

sunt echivalente, dar instrucţiunea:


lea ax,datab [bx]
nu are un echivalent direct care să utilizeze o instrucţiune mov.
Utilizarea instrucţiunii lea devine indispensabilă atunci când se
grupeazăsegmentele cu ajutorul pseudoinstrucţiunii GROUP.
Să considerăm de exemplu următoarele declaraţii de date :

Se observă că în listing adresele referite de instrucţiunile:

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:

mov bx, offset b2

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:

BD 0300 mov bp,300h


8D 56 02 lea dx,2[bp]

Ca efect în registrul dx se va încărca valoarea 302H.


2.2 Instrucţiuni pentru transferul valorilor indicatorilor de condiţie

Pentru transferul indicatorilor de condiţie sunt disponibile următoarele


instrucţiuni:
SAHF - încarcă indicatorii de condiţie conform conţinutului registrului AH
LAHF – memorează valorile indicatorilor de condiţie în registrul AH
PUSHF – salvează în stivă valorile indicatorilor de condiţie
POPF – reface sin stivă valorile indicatorului de con

Structura octetului care se transferă în cazul instrucţiunilor LAHF şi SAHF este:

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

Să considerăm de exemplu secvenţa de instrucţiuni:


pushf
pop ax
or ax,100h ; poziţionarea indicatorului
TF
push ax
popf
nop
Ca efect al poziţionării indicatorului TF după execuţia instrucţiunii nop se va genera o
întrerupere pe nivel 1. Dacă se doreşte ca în continuare (la ieşirea din procedura de tratare a
întreruperi ) să se lucreze în regim de funcţionare normală ( fără întreruperi după execuţia
instrucţiunii ) atunci în procedura de tratare a întreruperii trebuie să se modifice valoarea
salvată în stivă.
2.3 Instrucţiuni pentru poziţionarea indicatorilor de condiţie

În această categorie se încadrează instrucţiunile:


CLC – pune pe zero valoarea indicatorului CF
STC – pune pe unu valoarea indicatorului CF
CMC – complementează valoarea indicatorului CF
CLD - pune pe zero valoarea indicatorului DF
STD - pune pe unu valoarea indicatorului DF
CLI - dezactivează sistemul de întreruperi ( IF = 0)
STI - activează sistemul de întreruperi ( IF = 1)
Se observă că nu există instrucţiuni speciale pentru toţi indicatorii de condiţie.
Indicatorii AF,OF,PF,SF,ZF pot să fie poziţionaţi prin execuţia unor instrucţiuni aritmetice sau
logice. Să considerăm de exemplu următoarea secvenţă de instrucţiuni:
mov ax,7fffh
add ax,1
Ca efect al execuţiei acestei secvenţe indicatorul OF este poziţionat pe 1 (s-a
obţinut prin adunare a două numere pozitive un număr pozitiv mai mare decât cel mai mare
număr pozitiv care poate să fie memorat în registrul ax).
Desigur indicatorii de condiţie pot să fie modificaţi simplu şi cu ajutorul instrucţiunilor SAHF
şi POPF.
2.4Instrucţiuni aritmetice

Aceeaşi configuraţie binară poate să fie interpretată ca un număr cu semn sau


fără semn. Astfel, pentru valori reprezentate pe 8 biţi, valoarea 80H poate să fie considerată
ca reprezentând numărul 128 dacă este interpretată ca un număr fără semn sau poate să fie
considerată ca reprezentând valoarea-128 dacă este interpretată ca un număr cu semn.
Microprocesorul poate executa cele 4 operaţii aritmetice pe 8 sau 16 biţi considerând
numerele reprezentate cu semn în complement faţă de 2 sau fără semn. De asemenea se pot
executa operaţii aritmetice( adunare,scădere ) şi asupra numerelor reprezentate în binar
codificat zecimal, în format împachetat ( 2 cifre pe octet ) sau despachetat ( o cifră pe octet ),
Înmulţirea şi împărţirea se pot executa şi asupra numerelor în BCD despachetat.
Microprocesorul are instrucţiuni aritmetice deferite pentru numere cu semn şi
respectiv numere fără semn numai pentru înmulţire şi împărţire. Pentru celelalte operaţii
aritmetice se utilizează aceleaşi instrucţiuni, indiferent dacă este vorba de numere cu semn
sau fără semn. Rămâne în sarcina programatorului să interpreteze rezultatele obţinute
pentru a identifica de exemplu erorile de depăşire. Să considerăm de exemplu că se adună
numerele 7FH şi 3, rezultatul obţinut ( 82H ) este corect dacă se consideră că s-au adunat
două numere fără semn ( 127 + 3 = 130 );dacă însă se consideră că s-au adunat două numere
cu semn, atunci se constată faptul că rezultatul obţinut este negativ, deci incorect.
2.5 Instructiuni logice

Instrucţiunile de înmulţire şi împărţire sunt cele mai lente instrucţiuni


executate de către microprocesorul 8088/8086. Din acest motiv ori de câte ori este posibil
este de preferat înlocuirea unor operaţii de înmulţire/împărţire cu valori constante prin
secvenţe echivalente care să folosească numai operaţii de adunare şi deplasare. De
exemplu pentru secvenţa:
xor ah,ah
shl ax,1
sunt necesare 4 cicluri maşină,în timp ce secvenţa:
mov bl,2
mul bl

necesită între 74 şi 81 de cicluri.


Prezentăm în continuare câteva exemple tipice care realizează operaţii de înmulţire,
respective împărţire, cu valori constante
Secvenţa care realizează înmulţirea cu 10 a unui număr conţinut în registrul ax:

shl ax,1 ;*2
mov bx,ax
shl ax,1 ;*4
shl ax,1 ;*8
add ax,bx ; * 10
Secvanţa care realizează împărţirea cu 512 a unui număr conţinut în registrul ax:
shr ax,1 ;/ 2
xchg ah,al ;
cbw ; / 512
Secvenţa pentru împărţirea cu 16 a unui număr reprezentat pe 32 de biţi:
mem32 add 500000
………
mov cx,4
iar:
shr word ptr mem32[2],1 ; deplasare în carry
rcr word ptr mem32[0],1 ; rotire cu carry
loop iar
Modul de interpretare al operanzilor este foarte important în cazul
comparaţiei a două numere..
Daca dacă numerele sunt fără semn:
CF = 0 şi ZF = 0 => d > s
CF = 0 şi ZF = 1 => d = s
CF = 1 şi ZF = 0 => d<s
Pentru numere cu semn:
SF = OF => d > s
ZF = 1 => d = s
SF = OF => d < s
2.6 Instrucţiuni pe şiruri de caractere

Pentru toate instrucţiunile pe şiruri de caractere se consideră că şirul sursă (dacă


există ) este conţinut în segmentul curent de date ( a cărui adresă de început este conţinută
în DS ), iar adresa relativă a şirului în segment este conţinută în SI. Şirul destinaţie(dacă
există) este conţinut în segmental de date extern ( a cărui adresă de început este conţinută în
ES) iar adresa relativă în segment este conţinută în DI. Pentru şirul sursă se poate considera şi
alt registru de segment utilizând un prefix de registru adecvat. Indicatorul DF indică sensul de
parcurgere în memorie al şirurilor, cu alte cuvinte modul în care se actualizează registrele SI
şi DI după execuţia operaţiei. Şi anume dacă indicatorul DF are valoarea zero atunci se
consideră că şirurile se vor parcurge de la adrese mici spre adrese mari. Actualizarea se face
prin incrementare ( decrementare pentru DF = 1 ), cu unu sau cu doi după cum se executa o
operaţie care implică un octet sau un cuvânt ( doi octeţi ),
Operaţiile pe şiruri de caractere pot să fie precedate de un prefix care indică
condiţiile în care se repetă execuţia operaţiei ( evident datorită actualizării conţinutului
registrelor DI şi SI repetarea se efectuează în legătură cu alte informaţii ).
Operaţiile elementare pe şiruri de caractere sunt:
MOVSB(MOVSW) – transferă un octet ( o pereche de octeţi ) între sursă şi destinaţie;
CMPSB(CMPSW) – poziţionează indicatorii de condiţie conform diferenţei între sursă şi
destinaţie;
SCASB(SCASW) – poziţionează indicatorii de condiţie conform diferenţei între conţinutul
registrului AL (AX) şi destinaţie
LODSB(LODSW) – încarcă un octet( doi octeţi ) din sursă în registrul AL( AX )
STOSB ( STOSW ) – memorează conţinutul registrului AL (AX) în destinaţie
Pentru repetarea operaţiilor pe şiruri de caractere se pot utiliza următoarele prefixe:
REP,REPZ,REPE – se repetă operaţia până când conţinutul registrului CX devine zero sau până
când indicatorul ZF devine 0.Indicatorul ZF este poziţionat numi de către instrucţiunile
SCASB( SCASW ) şi CMPSB( CMPSW ). După fiecare execuţie a operaţiei elementare conţinutul
registrului CX este decrementat.
REPNZ,REPNE – se repetă operaţia până când conţinutul registrului CX devine zero sau până
când indicatorul ZF devine 1. Indicatorul ZF este poziţionat numai de către instrucţiunile SCASB
( SCASW ) şi CMPSB (CMPSW ). După fiecare execuţie a operaţiei elementare conţinutul registrului
CX este decrementat.
De observat faptul că tipul prefixului este semnificativ numai pentru operaţiile
scasb( scasw ) şi cpmsb (cmpsw ).Pentru toate celelalte instrucţiuni toate prefixele au aceeaşi
semnificaţie ca şi prefixul rep.Ca exemplu să considerăm că au fost declarate următoarele
segmente de date:
Să considerăm şi o secvenţă de instrucţiuni care caută într-un şir de caractere de
forma:

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

Să considerăm şi o secvenţă care copiază argumentele cu care este apelat un


program. Conform structurii prefixului unui program încărcat în memorie linia de apel care a
produs execuţia programului respectiv este copiată într-un vector care începe la adresa
relativă 81H în cadrul prefixului,iar numărul de octeţi din linia de apel este conţinut într-un
octet aflat la adresa relativă 80H. Din textul liniei de apel s-au eliminat numele programului
şi orice comenzi de redirectare. Zona în care se copiază se numeşte buffer, adresa de
segment pentru această zonă se găseşte în registrul de segment es.
2.7 Instructiunile de salt

Limbajul se asamblare pentru 8086 conţine următoarele tipuri de instrucţiuni se salt:


- apeluri de procedură CALL
- reîntoarcere din procedură RET
- salturi condiţionate şi necondiţionate
- instrucţiuni de ciclare LOOP
- întreruperi software INT
Instrucţiunile de apel şi reîntoarcere din procedură şi instrucţiunile de salt se pot executa în
cadrul aceluiaşi segment sau între două segmente. Aceste instrucţiuni pot să utilizeze adresarea directă
sau indirectă. În acest caz mecanismul de calcul al adresei operandului ( adresa la care se va executa
saltul ) este cel cunoscut de la referirile la variabile, putând fi conţinut într-un registru, indexat,bazat.
Dacă se execută saltul între două segmente,atunci se utilizează drese formate din cele două
componente: adresa de segment şi adresa în segment.
În cazul instrucţiunilor de salt necondiţionat în cadrul aceluiaşi segment există o formă de
instrucţiune scurtă, instrucţiunea de salt relativ în domeniul [128,127]
de octeţi faţă de instrucţiunea următoare celei de salt. Dacă eticheta la care se face referire se găseşte
textual înainte de instrucţiunea de salt atunci asamblorul “ştie” să genereze corect o instrucţiune scurtă
(dacă se poate ). În cazul în care instrucţiunea de salt este necesară specificarea explicită a formei
scurte. De exemplu:
alfa:
mov ax,5
….
jmp alfa
…..
jmp short beta
….
beta:

nstrucţiunile de salt şi apel de procedură în cadrul aceluiaşi segment se pot executa şi


indirect utilizând orice registru sau prin intermediul memoriei. De exemplu pentru instrucţiunea:
call bx
adresa de salt este conţinută în registrul bx. Pentru instrucţiunea:
call [bx]
adresa de salt este conţinută la adresa conţinută în registrul bx.

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

Să considerăm de exemplu secvenţa care determină adresa primului octet cu


valoare diferită de zero dint-o zonă de memorie a cărei adresă de început se transmite în
registrul DI, iar adresa de sfârşit se transmite în registrul CX. Rezultatul rămâne în DI.

sub cx,di ; determină numărul de octeţi


inc cx
mov al,0 ; valoare de comparaţie

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:

Deci şi valorile Q0 şi R0 se obţin printr-o operaţie de împărţire „obişnuită”. Desigur, operaţia de


împărţire trebuie să se execute în acest mod numai dacă rezultatul operaţiei poate să fie
corect (împărţitorul este diferit de zero) şi dacă rezultatul nu se poate obţine prin execuţia
unei operaţii de împărţire „obişnuită”.
2.9 Instrucţiuni de intrare/ieşire

Accesul de interfeţe corespunzătoare echipamentelor periferice se realizează prin


intermediul unor instrucţiuni speciale. Pentru majoritatea resurselor externe
microprocesorului modul de programare este atât de complicat încât este de preferat ca
programatorul să se bazeze pe ceea ce oferă sistemul de operare. Fiecare interfaţă are
asociate un număr de coduri specifice numite porturi. Atribuirea codurilor pentru fiecare
interfaţă se face la proiectarea sistemului de calcul. Comunicaţia între microprocesor şi
mediul exterior reprezentat de interfeţele de intrare/ieşire se face prin execuţia unor
instrucţiuni care accesează aceste porturi. Un port este o valoare reprezentată pe 8 sau 16
biţi.
Citirea unor date sau a unor informaţii de stare se face prin intermediul unor
instrucţiuni de forma:
in al,port
in al,dx
Prima formă poate să fie utilizată numai dacă portul are o valoare mai mică decât
256. Efectul execuţiei unei astfel de instrucţiuni constă din transmiterea conţinutului
registrului în instrucţiune. Să considerăm de exemplu modul în care se realizează sunetele în
sistemele compatibile IBM.
Transmiterea unor date sau a unor comenzi se face prin intermediul unor instrucţiuni
de forma:
out port,al
aut dx,al
Prima formă poate să fie utilizată numai dacă portul are o valoare mai mică decât
256. Efectul execuţiei unei astfel de instrucţiuni constă din transmiterea conţinutului registrului
în instrucţiune. Să considerăm de exemplu modul în care se realizează sunetele în sistemele
compatibile IBM
Realizarea sunetelor de bază pe contorul programabil 8253 controlat de circuitul
8255. Contorul programabil 8253 este utilizat în sistemele compatibile IBM pentru realizarea a
trei funcţii, fiecare funcţie fiind realizată cu ajutorul unui contor:
contorul 0 este realizat pentru ceasul de timp real. Programarea sa iniţială este
realizată la pornirea sistemului şi este se face astfel încât generează 18.2 întreruperi pe secundă
contorul 1 este utilizat pentru realizarea „refreshului” memoriei dinamice
contorul 2 este conectat la generatorul de tonuri programabile sub controlul
circuitului 8255

Procedura beep prezentată în continuare produce un semnal ce frecvenţa de 100Hz.


Realizarea unei anumite note muzicale înseamnă de fapt generarea unui sunet
cu o frecvenţă dată pentru o perioadă dată de timp. Să considerăm de exemplu o procedură
care interpretează o secvenţă de note muzicale memorate într-un şir de forma:

Frecv 1 Frec 2 Frec 3….…..…..….

Se observă că toate notele muzicale au aceeaşi durată. Pentru a obţine o notă


muzicală cu o durată mai mare se va repeta de un număr corespunzător de ori frecvenţa
respectivă.

În cadrul exemplului anterior întârzierile au fost realizate cu ajutorul ceasului de


timp real. Un apel de forma:

mov ah,0
int 1AH
; apel BIOS pentru citire ceas

are ca rezultat (obţinut în registrele CX şi DX) valoarea contorului de întreruperi


menţinut de procedura pentru tratarea întreruperilor generate de ceasul de timp real.
2.10 Instrucţiuni specifice microprocesoarelor 80286/80386/80486

Generalităţi despre instrucţiunile pe 32 de biţi ale lui ‘386


Toate instrucţiunile care acceptă operanzi reprezentaţi pe 16 biţi pot lucra şi cu
operanzi reprezentaţi pe 32 de biţi;alegerea între 16 şi 32 de biţi în ceea ce priveşte
dimensiunea operanzilor se face astfel:
a) dacă procesorul lucrează în mod real, atunci dimensiunea implicită a
operanzilor este de 16 biţi. Prezenţa unui prefix cu valoarea 66H înaintea instrucţiunii
specifică utilizarea operanzilor pa 32 de biţi
b) dacă procesorul lucrează în mod protejat, atunci dimensiunea implicită a
operanzilor depinde de bitul B din descriptorul segmentului de cod curent. Prefixul 66H are
ca efect inversarea efectului acestui bit.

Instrucţiuni pentru transferul datelor


Instrucţiunea PUSH a fost extinsă pentru a accepta ca operand sursa o valoare
imediată pe 16 biţi. Pentru salvarea/restaurarea registrelor AX,BX,CX,DX,SI,DI,BP în/din stivă
există instrucţiunile PUSHA şi respectiv POPA. Pentru 80386, există instrucţiunile LFS,LGS,LSS
care funcţionează în acelaşi mod ca LDS,LES, cu diferenţa că registrul de segment implicat
este FS,GS sau SS.
Instrucţiunile MOVSX şi MOVZX transferă conţinutul unei surse pe 8 biţi într-un registru de
16/32 de biţi cu umplerea spaţiului rămas cu zerouri (MOVZX) sau prin replicarea bitului de
semn (MOVSX). Pentru 80386/486, instrucţiunea MOV are o serie de forme speciale pentru
transferul datelor în/din registrele speciale de control ale microprocesorului:
MOV <destinaţie>,CRn ; n = 0,2,3
MOV CRn,<sursa> ; n = 0,2,3
MOV <destinaţie>,DRn ; n = 0,1,2,3,6,7
MOV DRn,<sursa> ; n = 0,1,2,3,6,7
MOV <destinaţie>,TRn ; n = 6,7
MOV TRn,<sursa> ; n = 6,7
Operanzii <destinaţie> şi <sursă> trebuie să fie registre generale de 32 de biţi.
Adresele diferitelor tabele de descriptori folosite în modul protejat pot fi
încărcate/citite cu ajutorul instrucţiunilor
LGDT <sursa> ; tabela globală de segmente
LLDT <sursa> ; tabela locală de segmente
LTR <sursa> ; descrierea taskului curent
SGDT <dest> ; tabela globală de segmente
SLDT <dest> ; tabela locală de segmente
STR <dest> ; descrierea taskului current
Operanzii <sursa> şi <dest> sunt surse de memorie de 6 octeţi.
Instrucţiuni aritmetice
Instrucţiunea IMUL are o formă cu trei operanzi:
IMUL <destineţie>,<sursa>,<val.imediată>. Efectul acestei instrucţiuni constă în înmulţirea
valorii operandului <sursa> (16 biţi) cu <valoarea imediată> şi depunerea rezultatului în
operandul <destinaţie> (obligatoriu un registru de 16 biţi).

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

Microprocesorul 8086 are patru registre de segment, cu ajutorul cărora adresele


efective pe 16 biţi manipulate de programe pot fi mapate pe patru zone(potenţial distincte)
din spaţiul de adrese de 1 Moctet. Spre deosebire de 8086, microprocesorul 80286 poate
adresa 4 Gocteţi; dar aceste spaţii imense de adrese sunt accesibile numai atunci când
microprocesorul funcţionează în mod protejat. În modul real de funcţionare (cel utilizat
pentru rularea sistemului de operare MS-DOS), 80286 şi 80386 se comportă ca un 8086
foarte rapid şi deci sunt limitate la acelaşi spaţiu de adrese de 1 Moctet.
Pe lângă posibilitatea de adresa memorii mult mai întinse, şi deci de a rula
programe mult mai mari, modul protejat de funcţionare al acestor microprocesoare mai
oferă mijloace eficiente de multiasking (programare multiproces), facilităţile necesare
implementării unui mecanism avansat de memorie virtuală, metode de protecţie între
programele care coexistă în memorie şi de control al accesului la resursele sistemului.
După RESET,80286 şi membri mai tineri ai familiei(cu excepţia lui 80376)
operează în modul real. O secvenţă de iniţializare poate construi o serie de tabele de
descriptori, urmată de trecerea microprocesorului în mod protejat prin setarea unui bit într-
un registru special de stare a maşinii(80386 şi 80486 pot reveni în modul real prin resetarea
acestui bit).
Cei 16 biţi ai unui registru de segment sunt împărţiţi în trei zone, ilustrate în figura
2.2:
Biţii 0 şi 1 definesc nivelul de privilegiu cerut(Requested Privilege Level, RPL) de taskul
curent pentru accesul la segment; nivelul cel mai privilegiat este 0. Operaţiile referitoare la
datele din segment se efectuează la nivelul de privilegiu specificat de RPL, care poate fi mai
slab decât nivelul de privilegiu la care rulează taskul curent
Bitul 2 (Table Indicator,TI) precizează la care tabelă de descriptori se face referire: tabela de
descriptori globală (bit 2 = 0), comună tuturor taskurilor din sistem, sau tabela de descriptori
locală(bit 2 = 1), specifică taskul curent.
Biţii 15..3 conţin indicele în tabelă al descriptorului segmentului cerut (de la 0 la 8191 = 213).

Intrarea numărului zero în tabela globală de segmente corespunde selectorului


nul(0000h). Procesorul permite încărcarea unui selector nul într-un registru segment însă
orice încercare de a folosi un astfel de selector pentru calculul unei adrese liniare se soldează
cu o excepţie de tip 13 (General Protection).
Fiecare task (proces) care rulează în mod protejat poate folosi segmente aflate
într-un spaţiu de adrese virtuale (perechea selector:offset), împărţit în două subspaţii:
spaţiul segmentelor globale (care pot fi folosite de orice task) şi spaţiul segmentelor
locale(specifice taskului curent).
Fiecare task are asociat un nivel de privilegiu, de la 3 (cel mai puţin privilegiat)
până la 0 (cel mai privilegiat);un nivel similar de privilegiu este codificat în cel mai puţin
semnificativi 2 biţi ai fiecărui selector de segment. Nivelul de privilegiu la care se execută
instrucţiunile dintr-un segment de cod este cel mai puţin privilegiat dintre nivelul de
privilegiu al taskului şi cel al selectorului segmentului de cod. Un task utilizator va rula la
nivelul de privilegiu 3 (cel mai slab);instrucţiunile care schimbă nivelul de privilegiu nu pot fi
efectuate de un astfel de task. Dacă programul utilizator are nevoie de un serviciu oferit de
sistemul de operare,el poate face un apel sistem printr-un mecanism numit poartă de apel,
care izolează programul utilizator de sistemul de operare
Doi biţi din registrul indicatorilor de condiţii controlează posibilitatea taskului
curent de a executa instrucţiuni de intrare/ieşire; modificarea acestor biţi nu este posibilă
decât la cel mai înalt nivel de privilegiu. În acest fel, un program utilizator (cu nivelul de
privilegiu 3) nu are nici o şansă să provoace daune sistemului – greşeli de programare nu se
răsfrâng decât asupra programului vinovat.
Structura unui descriptor de segment
În tabelele de segmente se pot găsi două categorii de intrări:descriptori de
segment şi descriptori de porţi(porţiile sunt un mecanism prin care un task “utilizator” poate
face apel la serviciile unui task “supervizor” mai privilegiat etc.). formatul unui descriptor de
segment este arătat în figura 2.3 în care s-au folosit următoarele notaţii:
Limita 15..0 – cei mai puţin semnificativi 16 biţi ai limitei segmentului;
Limita 19..16 – cei mai semnificativi 8 biţi ai limitei segmentului. Limita este
folosită de procesor pentru a testa validarea acceselor la datele conţinute de segment, şi are
valori între 0 şi 1 Moctet – 1 dacă bitul G este 0, respectiv între 0 şi 4 Gocteţi -1 dacă bitul G
este 1.
Adresa de bază 15..0, 23..16, 31..24 – prin concatenarea acestor câmpuri se
obţine adresa de bază a segmentului (32 de biţi).
P-dacă acest bit este 0 segmentul este marcat ’absent’ şi încercarea de referire a
datelor din segment provoacă o excepţie de tip 11 (segment absent). Această facilitate poate
fi folosită de un sistem de operare pentru implementarea unui mecanism de memorie
virtuală.
DPL – Descriptor Privilege Level, Nivelul de privilegiu al descriptorului.
T – este 1 pentru descriptorii segmentelor programelor de aplicaţie,0 pentru
descriptorii folosiţi de sistem şi pentru descriptorii de porţi.
Tip – pentru descriptorii de segmente al programelor de aplicaţie, cei patru biţi al
câmpului. Tip pot fi interpretaţi astfel:
bit 11 - 0 -> segment de date
1 -> segment de cod
bit 10 - 0 -> segment de date: creşte spre adrese mari
segment de cod: segment obişnuit
1 -> segment de date: creşte spre adrese mici
segmente de cod: segment ’conform’
bit 9 - 0 -> segment de date: nemodificabil(read only)
segment de cod: se permite numai execuţia
1 -> segment de date: modificabil (read/write)
segment de cod: se permite şi citirea
bit 8 - 0 -> segmentul nu a fost accesat
-> segment a fost accesat
B – dacă bitul este 0, atunci dimensiunea implicită a operanzilor şi a adreselor
efective de 16 biţi; dacă este 1, atunci dimensiunea implicită a adreselor efective şi a
operanzilor este 32 de biţi.
A – valoarea 1 indică faptul că segmentul poate fi folosit de către programele de
sistem.
Pentru procesorul 80286, ultimul cuvânt din descriptorului de segment trebuie să
aibă valoarea zero, ceea ce înseamnă că pentru toate segmentele adresa de bază are numai
24 biţi, iar lungimea este de cel mult 1 Moctet.
Trecerea din mod real în mod protejat şi invers
La punerea sub tensiune, sau după o reiniţializare, microprocesorul 80286 începe
să funcţioneze în mod real de adresare. Pentru a trece microprocesorul în mod protejat sunt
de parcurs mai multe etape:
construirea tabelelor de descriptori de segmente care vor fi folosite în mod protejat (tabela
globală de segmente şi tabela de descriptori pentru tratarea întreruperilor);
încărcarea adreselor acestor tabele în registrele speciale destinate ale microprocesorului,
folosind instrucţiunilor:
LGT Desc ; încarcă registrul GDTR cu adresa şi lungimea tabelei globale de segmente
LIDT Desc ; încarcă adesa şi lungimea tabelei de descriptori pentru întreruperiîn IDTR
LLDT Desc ; încarcă în LDTR adresa şi lungimea tabelei locale de segmente
În toate aceste cazuri Desc este eticheta unei zone de memorie de 6 octeţi, din
care primii doi conţin lungimea tabelei care va fi încărcată, iar următorii patru conţin adresa
liniară a tabelei. Acestea sunt singurele instrucţiuni ale microprocesorului 80286 pentru care
programatorul furnizează o adresă liniară completă de 32 biţi, utilizată ca atare de procesor,
fără nici o prelucrare.
printr-o instrucţiune
MOV CR0, Sursa sau LMSW Sursa
Se forţează la 1 bitul cel mai puţin semnificativ din registrul CR0 (registrul de
control 0), trecând procesorul în modul protejat de adresare. Deoarece după execuţia acestei
instrucţiuni intră în vigoare alte norme de calcul ale adresei, următoarea instrucţiune
executată trebuie să fie un salt FAR, pentru a asigura încărcarea registrului CS cu un selector
corect.
Odată trecut în mod protejat, microprocesorul 80286 nu mai poate reveni în mod
real decât prin reiniţializare, în timp ce pentru 80386 şi 80486 este suficientă forţarea la 0 a
aceluiaşi bit cel mai puţin semnificativ din registrul CR0. Programul din figura 2.4 ilustrează
tehnicile folosite pentru intrarea şi ieşirea din modul protejat.
TITLE utilizarea microprocesorului 80286/80386 în modul protejat
PAGE 72,132
.286p
; Programul demonstrează trecerea microprocesorului 80286/80386 din mod real în mod ;
protejat şi înapoi în mod real.
; Programul se execută pe un microcalculator IBM PC-AT sau compatibil, sub sistemul de
operare MS-DOS. După trecerea microprocesorului în mod protejat, programul trece întregul
ecran alfanumeric pe video invers, apoi revine în mod real pentru a transmite controlul
sistemului de operare. Se presupune că monitorul este CGA sau EGA.
; După asamblare programul trebuie convertit în format .COM.
Instrucţiunea următoare este deja în coada de instrucţiuni, deci pentru adresarea ei nu mai
este nevoie de acces la memorie. Însă ea trebuie neapărat să fie o instrucţiune de salt FAR cu
adresare directă (16 + 16 biţi) pentru a încărca în CS un descriptor correct de segment.
JMPFAR Cont_2,CS_Cod ; coada de instrucţiuni
Deja suntem în mod protejat. Prima noastră grijă este să punem valori corecte în
registrele de segment (DS şi SS).
Pentru a funcţiona şi pe calculatoare construite cu microprocesorul 80286 (pentru
care nu există nici un mecanism direct de revenire în mod real), programul procedează astfel
pentru a readuce unitatea centrală în mod real:
plasează adresa punctului de revenire într-o variabilă aflată la adresa fixă 0040:0067h;
depune într-un octet din memoria CMOS un cod de comandă care precizează că după
reiniţializarea procesorului execuţia conţinută de la adresa conţinută de variabila mai sus
menţionată;
execută o secvenţă care are ca efect reiniţializarea maşinii.
Bibliografie

Buburuzan,Adrian, Practica hardware,Univ.


“Al.I.Cuza”,Iasi, 2005;
Oniga, Stefan, ASM_curs,Baia Mare, 2002
Lungu, Vasile, Procesoare Intel,Programare in
limba de asamblare,Ed Teora,2005

S-ar putea să vă placă și