In acest tutorial destinat imbajului de asamblare pentru procesoare din familia Intel 8086 sunt prezentate procedurile. Acestea permit reutilizarea codului si reducerea dimensiunii acestuia insa implica un efort suplimentar generat de trimiterea parametrilor si apel.
Atentie: La procesoarele 80×86 urmatoarea instructiune de executat este determinata de continutul registrelor CS:IP (registrul asociat segmentului de cod CS si pointerul la instructiune IP) ce reprezinta adresa fizica a segmentului de cod si offset-ul in cadrul acestuia.
Instructiunile de salt neconditionat (Ex. JMP) permit salturi in program insa intoarcere la punctul de salt este gestionata in intregime de utilizator care trebuie fie sa indice punctul respectiv printr-o eticheta care ulterior sa fie de o instructiune de salt, fie sa salveze continutul registrului IP.
Apelurile de proceduri, reprezinta salturi la secvente de cod (reutilizabile) declarate de utilizator in corpul procedurii, care permit reintoarcerea la punctul de start. Acest lucru este posibil prin implementarea instructiunilor CALL (apel procedura) si RET (iesire din procedura si intoarcere in programul apelator).
Diferenta dintre CALL si JMP este data de faptul ca prima instructiune salveaza pe stiva inainte de a face saltul adresa instructiunii urmatoare.
Tipuri de CALL
- near; salveaza pe stiva valoarea pe 16 biti din IP si face salt la prima instructiune din corpul procedurii; are forma interna pe 3 bytes: 1 byte codul operatiei (E8h), 2 bytes reprezentand distanta ca numar de bytes pana la codul procedurii;
- far; salveaza pe stiva valoarea din CS si apoi din IP; are forma interna pe 5 bytes: 1 byte codul operatiei (9Ah), 2 bytes offset si 2 bytes adresa segemntului.
Structura unei proceduri in assembler
Sintaxa declararii unei proceduri:
Nume_procedura PROC [FAR | NEAR] ………….. [Nume_procedura] ENDP
Structura generala a unei proceduri scrisa in limbaje de asamblare este:
Nume_procedura PROC [FAR | NEAR] |
Salvare registre |
Bloc prelucrare date cu referirea parametrilor formali |
Stocare rezultate |
Restaurarea registrelor |
RET [nr_octeti] |
[Nume_procedura] ENDP |
Apelul si iesirea din proceduri
Apelul unei proceduri se face prin instructiunea CALL:
- sintaxa: CALL operand
- aceasta salveaza pe stiva adresa instructiunii urmatoare (daca saltul este de tip intersegment – far se salveaza inainte si adresa din CS) si executa salt la locatia indicata de operand;
- tipul operandului este:
Tip operand | Descriere |
CALL nume_procedura | Numele procedurii apelate reprezinta in esenta o eticheta. |
CALL eticheta | Procesorul presupune in aceasta situati ca eticheta este locala si atunci face un salt de tip NEAR. Aceasta trebuie sa fie la o distanta cuprinsa in intervalul [-32768,32676] de bytes. A se vedea exemplul de mai jos. |
CALL FAR PTR eticheta | Eticheta este in alt segment. Inlocuieste CS si IP cu segmentul si offsetul etichetei. |
CALL registru sau variabila | Continutul din registru sau variabila este copiat in IP dupa ce acesta a fost salvat pe stiva. Deci variabilasau registrul respectiv reprezinta de fapt un pointer near (tine doar offsetul unei proceduri sau etichete) |
CALL WORD PTR variabila | Continutul variabilei reprezinta un offset si are loc un salt de tip NEAR. De obicei variabila este reprezentata de o adresare indirecta cu registru index (SI,DI), [SI] sau registru de baza (BX). [BX] si atunci trebuie specificat cati bytes trebuie cititi (trebuie indicat procesorului tipul de salt – near, se citesc 2 octeti; far se citesc 4 octeti). De vazut exemplul cu apeluri indirecte de proceduri. |
CALL DWORD PTR adresa | Continutul variabilei reprezinta un segment + offset si are loc un salt de tip FAR. De obicei variabila este reprezentata de o adresare indirecta cu registru index (SI,DI), [SI] sau registru de baza (BX). [BX]. De vazut exemplul cu apeluri indirecte de proceduri. |
- instructiunea CALL se traduce prin secventele de instructiuni:
pentru NEAR
PUSH IP ; salvare adresa instructiunii urmatoare lui CALL
JMP adresa_salt ;adresa_salt reprezinta un offset si este indicata de operand
JMP adresa_salt ;adresa_salt reprezinta un offset si este indicata de operand
Imaginea stivei dupa CALL si la pozitionarea pe prima instructiune din corpul procedurii | ||
Stiva creste pe directia ↑! Adresele scad | ||
SP → | adresa( IP ) de intors | |
pentru FAR
PUSH CS ;salvare adresa segment de cod curent
PUSH IP ; salvare adresa instructiunii urmatoare lui CALL
JMP adresa_salt ;adresa_salt reprezinta un sehment+offset si este indicata de operand
Imaginea stivei dupa CALL si la pozitionarea pe prima instructiune din corpul procedurii |
||
Stiva creste pe directia ↑ ! Adresele scad |
||
SP → |
adresa offset ( IP ) de intors |
|
SP +2 → |
Adresa segment (CS) de intors |
|
Iesirea din proceduri se realizeaza prin instructiunea RET:
-
sintaxa: RET [valoare numerica]
-
aceasta scoate de pe stiva adresa instructiunii urmatoare (daca saltul este de tip intersegment – far se restaureaza dupa IP si CS salvat anterior) si initializeaza registrele care controleaza executarea instructiunilor, IP si CS (ambele daca procedura a fost de tip FAR);
-
daca se da si valoarea numerica optionala are loc curatarea stivei prin modificarea pozitiei curente din stiva (indicata de SP) adunand la SP atatia bytes cati indica valoarea numerica;
-
instructiunea RET se traduce prin secventele de instructiuni:
pentru NEAR
POP IP ;reinitializeaza registrul IP cu valoarea salvata la intrarea in procedura
;cum acest registru contine offsetul instructiunii de executat, programul se ;reia de la instructiunea urmatoare saltului
[add SP, valoare_numerica] ;instructiune optionala care se executa doar daca se da ;instructiunea RET + valoare numerica
pentru FAR
POP IP
POP CS ;reinitializeaza registrul CS cu adresa segmentului de cod
[add SP,valoare_numerica]
-
instructiunea stie ce iesire sa faca in functie de modul in care a fost declarata procedura sau mai exact in functie de tipul apelului FAR sau NEAR; la asamblare fiecare instructiune de tip return este inlocuita cu RETN sau RETF (instructiunile pot fi utilizate – a se vedea exemplul urmator) care reprezinta forma explicita a instructiunii de iesire.
Realizarea unei proceduri fara PROC si ENDP
Exemplul urmator subliniaza modul de interpretare interna a procedurii asemenea unei secvente de cod la care se realizeaza salturi si de la care programul stie sa se intoarca gestionand adresa punctului de start.
Procedura realizeaza suma a doua numere de tip word ale caror valori sunt puse pe stiva iar rezultatul este returnat in registrul CX.
.model small .286 .stack 100h .data a dw 5 b dw 3 s dw ? .code mov AX,@data mov DS,AX push a ;pun valoarea lui a pe stiva push b; ;pun valoarea lui b pe stiva call NEAR PTR start_procedura ;apel explicit de procedura NEAR mov s, CX ;pun rezultatul in s mov AX, 4c00h int 21h start_procedura: ;eticheta ce indica inceputul procedurii push BP ;salvez valoarea din BP mov BP,SP ;initializez BP cu valoarea lui SP pentru a-l utiliza ;ca reper in citirea datelor de pe stiva push AX ;salvez valoarea lui AX mov AX,[BP+6] ;copiez in AX valoarea lui a primita pe stiva ;a se vedea figura de mai jos care descrie stiva in acest moment add AX,[BP+4] ;adun in AX valoarea lui b primita pe stiva mov CX,AX ;pun rezultatul in CX pop AX ;restaurez AX mov SP,BP ;copiez in SP valoarea din BP ;aduc SP la pozitia reperului pop BP ;restaurez SP retn ;return de tip NEAR – scote doar IP de pe stiva end
Imaginea stivei la momentul adunarii celor doua valori de pe stiva | |||
Pozitie in stiva relativa fata de | |||
BP |
SP |
Stiva creste pe directia ↑ ! Adresele scad |
|
BP-2→ |
SP → |
valoare din AX |
|
BP→ |
SP + 2 → |
valoare din BP |
|
BP+2→ |
SP + 4 → |
adresa offset ( IP ) de intors |
|
BP+4→ |
SP + 6 → |
valoare b |
|
BP+6→ |
SP + 8 → |
valoare a |
|
Acelasi program in limbaj de asamblare se scrie utilizand directivele PROC si ENDP:
.model small .286 .stack 100h .data a dw 5 b dw 3 s dw ? .code mov AX,@data mov DS,AX push a push b call suma mov s, CX mov AX, 4c00h int 21h suma PROC push BP mov BP,SP push AX mov AX,[BP+6] add AX,[BP+4] mov CX,AX pop AX mov SP,BP pop BP ret ENDP end
Proceduri de tip FAR
Procedurile FAR sunt proceduri care se gasesc in alt segment decat cel curent. Pentru a exemplifica o astfel de situatie se definesc explicit segmentele si se renunta la definirea simplificata a unui program utilizand directivele .model .code .stack .data. De exemplu, se scrie programul assembler care aduna doua numere de tip double trimitand parametrii prin referinta pe stiva.
Variabile SEGMENT ;declar un segment numit Variabile in care declar ;datele a dd 1234677 b dd 3456123 sum dd ? Variabile ENDS ;sfarsit segment de date Stiva SEGMENT ;declar segment numit Stiva in care rezerv 512 octeti ;pentru a-i utiliza pe post de stiva dw 100h dup(?) baza label word ;declar o variabila simbolica de tip word pentru a ;lua offset-ul in stiva Stiva ENDS ;sfarsit segment de stiva Principal SEGMENT ;declar segment numit Parincipal in care scriu codul ;programului principal ASSUME CS:Principal,DS:Variabile,SS:Stiva ;se realizeaza asocieri logice intre registrele de ;segment si segmentele utilizate – NU inseamna ca ;sunt si initializate registrele start: mov AX, Variabile ;initializez DS mov DS,AX mov AX,Stiva ;initializez SS mov SS,AX mov SP,offset baza ;initializez SP mov AX,offset a ;pun pe stiva adresa lui a push AX mov AX,offset b ;pun pe stiva offset b push AX mov AX,offset sum ;pun pe stiva offset sum push AX call FAR PTR suma ;apel procedura FAR mov AX,4c00h int 21h Principal ENDS ;sfarsit segment de cod principal Procedura SEGMENT ;definesc un alt segment de cod in care scriu ;procedura ASSUME CS:Procedura ;asociere logica suma PROC FAR ;definesc procedura push BP ;salvez BP mov BP,SP ;initializez BP cu valoarea lui SP push AX ;salvez AX mov SI,[BP+10] ;copiez in SI offset a mov DI,[BP+8] ;copiez in DI offset b mov AX,[SI] ;copiez in AX cuvant inferior din a add AX,[DI] ;adun la AX cuvant inferior din b push SI ;salvez continut SI – adica affset a mov SI,[BP+6] ;copiez in SI offset sum ;a se vedea figura urmatoare ce descrie stiva in acest punct mov [SI],AX ;copiez in sum suma cuvintelor inferioare pop SI ;restaurez SI – adica offset a mov AX,[Si+2] ;adun cu carry cuvinte superioare din a si b adc AX,[DI+2] mov SI,[BP+6] mov [SI+2],AX ;copiez in partea superioara a sum rezultatul pop AX ;restaurez AX mov SP,BP pop BP ret 6 ;curat stiva stergand logic cele 3 offset-uri suma ENDP Procedura ENDS end start
Imaginea stivei la momentul copierii in sum a rezultatului adunarii cuvintelor inferioare din a si b | |||
Pozitie in stiva relativa fata de | |||
BP |
SP |
||
BP-4→ |
SP → |
valoare din SI |
Stiva creste pe directia ↑ ! Adresele scad |
BP-2→ |
SP + 2 → |
valoare din AX |
|
BP→ |
SP + 4 → |
valoare din BP |
|
BP+2→ |
SP + 6 → |
adresa offset ( IP ) de intors |
|
BP+4→ |
SP + 8 → |
adresa segment (CS) de intors |
|
BP+6→ |
SP + 10 → |
offset sum |
|
BP+8→ |
SP + 12 → |
offset b |
|
BP+10→ |
SP + 14 → |
offset a |
|
Reguli la scriere procedurilor:
- secventele de cod ce compun corpul procedurii realizeaza operatii cu caracter general;
- utilizarea registrelor in proceduri implica salvarea continutului acestora pe stiva inainte de efectuarea prelucrarilor si restaurarea lor de pe stiva inainte de a iesi; pentru acest lucru se utilizeaza instructiunile PUSH (pune pe stiva) si POP (scoate de pe stiva). De exemplu:
ProcTest PROC push AX push BX push CX ………… Pop CX Pop BX Pop AX ret ENDP
Atentie: Restaurarea registrelor cu POP se face in ordine inversa salvarii cu PUSH.
- citirea datelor de pe stiva se realizeaza numai cu registrul BP
Apeluri indirecte de proceduri
Se realizeaza prin intermediul pointerilor la functii. De exemplu programul in limbaj de asamblare care aduna doua numere de tip word prin intermediul unei proceduri.
.model small .286 .stack 100h .data pointer_functie dw ? ;pointer la functie de tip NEAR pointer_functie_far DD ? ;pointer la functie de tip FAR a dw 5 b dw 3 s dw ? dif dw ? .code mov AX,@data mov DS,AX mov AX, offset suma ;incarc in AX offset proecdura mov pointer_functie,AX ;initializez pointerul la procedura mov AX,a ;trimit parametrii prin registrii mov BX,b call pointer_functie ;apelez procedura NEAR mov s,CX ; incarc pointerul cu offsetul si adresa segmentului mov WORD PTR pointer_functie_far,offset diferenta mov WORD PTR pointer_functie_far+2,seg diferenta mov AX,a ;trimit parametrii prin registrii call pointer_functie_far ;apelez procedura FAR ; sau call FAR PTR diferenta ;apel prin nume mov dif,CX mov AX, 4c00h int 21h suma PROC add AX,BX mov CX,AX ret ENDP diferenta PROC FAR ;declar explicit procedura de tip FAR sub AX,BX mov CX,AX ret ENDP end
In exemplul anterior procesorul stie ce tip de procedura sa apeleze pentru ca pointerii sunt declarati ca fiind de tip word, respectiv, double, ceea ce inseamna ca poate sa contina doar offsetul procedurii apelate (in cazul near) sau adresa completa, segment de cod + offset (pentru far).
In caz ca exista un vector de variabile de tip pointeri la functii, la apelul procedurii trebuie indicat explicit tipul acesteia prin WORD PTR (pentru NEAR) sau DWORD PTR (pentru FAR).
Daca in exemplul anterior incarc in SI offsetul de inceput al variabilei pointer_functie (simulez utilizarea unui vector) si atunci apelurile echivalente sunt:
call WORD PTR [SI] ;apel de tip near pentru suma call DWORD PTR [SI+2] ;apel de tip far pentru diferenta
Daca se da apelul call [SI] atunci implicit se considera ca apelul este de tip NEAR si se vor citi 2 octeti de la adresa [SI] reprezentand offsetul saltului. De aceea este important (mai ales in cazul salturilor de tip FAR) sa se indice explicit tipul saltului prin WORD sau DWORD.
Transmiterea parametrilor de intrare in proceduri
Pentru a exemplifica fiecare metoda, se realizeaza un program care verifica daca elementele de tip student (definit prin nume, varsta si numar matricol) ale unui vector au campul varsta cu valori cuprinse intr-un interval definit.
Se realizeaza utilizand:
- variabile declarate in memorie in segmentul de date; situatia este echivalenta in limbajele de nivel inalt cu utilizarea variabilelor declarate global in interiorul subprogramelor; prima varianta a programului implementeaza modelul small de definire a segmentelor si a tipului de memorie.
.model small .286 .stack 100h .data student struc ;definesc structura de date nume db 10 dup (?) varsta db ? nr_matricol dw ? ends dimStudent equ size student ;dimensiune student 13 octeti s1 student <'Popescu',22,1234> s2 student <'Ionescu',21,1235> mes_eroare db 'Depasire limite!','$' mes_corect db 'Date corecte!','$' lim_inf db 14 lim_sup db 89 .code Validare PROC ;procedura valideaza doar varsta studentului s1 push BP mov BP,SP push aX xor AX,AX mov AL,lim_inf ;copiez in AL valoarea limitei inferioare mov AH,lim_sup ;copiez in AH valoarea limitei superioare cmp AL,s1.varsta ;compar varsta cu limita inferioara ja eroare ;daca varsta e mai mica, afisez mesaj cmp AH,s1.varsta ;compar varsta cu limita superioara jb eroare ;daca varsta e mai mare,afisez mesaj mov AH,09h ;afisez mesaj date corecte lea DX,mes_corect int 21h jmp final eroare: mov AH,09h ;afisez mesaj eroare lea DX,mes_eroare int 21h final: pop AX mov SP,BP pop BP ret ;termin procedura ENDP start: ;indic de unde incep instructiunile programului mov AX,@data mov DS,AX call validare mov AX,4c00h int 21h end start ;indic terminarea programului dar si de unde incepe ;executia codului
A doua versiune a programului assembler utilizeaza segmente definite de programator.
Date SEGMENT ;segmentul de date student struc nume db 10 dup (?) varsta db ? nr_matricol dw ? ends dimStudent equ size student s1 student <'Popescu',22,1234> s2 student <'Ionescu',21,1235> mes_eroare db 'Depasire limite!','$' mes_corect db 'Date corecte!','$' lim_inf db 14 lim_sup db 89 Date ENDS Stiva SEGMENT ;segmentul aferent stivei dw 100 dup (?) ;rezerv 100 de cuvinte pentru stiva varf label word ;definesc o eticheta pentru a extrage adresa bazei stivei Stiva ENDS Main SEGMENT ;segmentul aferent codului ASSUME CS:Main,DS:Date,SS:Stiva ;fac asocieri logice Validare PROC ;procedura valideaza doar varsta studentului s1 push BP mov BP,SP push aX xor AX,AX mov AL,lim_inf ;copiez in AL valoarea limitei inferioare mov AH,lim_sup ;copiez in AH valoarea limitei superioare cmp AL,s1.varsta ;compar varsta cu limita inferioara ja eroare ;daca varsta e mai mica, afisez mesaj cmp AH,s1.varsta ;compar varsta cu limita superioara jb eroare ;daca varsta e mai mare,afisez mesaj mov AH,09h ;afisez mesaj date corecte lea DX,mes_corect int 21h jmp final eroare: mov AH,09h ;afisez mesaj eroare lea DX,mes_eroare int 21h final: pop AX mov SP,BP pop BP ret ;termin procedura ENDP start: ;indic de unde incep instructiunile programului mov AX,Date ;incarc in DS adresa segmentului de date mov DS,AX mov AX,Stiva ;incarc in SS adresa segmentului de stiva mov SS,AX mov AX,varf ;initializez SP cu offsetul curent in stiva mov SP,AX call validare ;apelez procedura mov AX,4c00h int 21h Main ENDS end start ;indic terminarea programului dar si de unde incepe ;executia codului
Marele dezavantaj al acestei metode de transmitere a parametrilor este diminuarea caracterului de generalitate pe care procedura trebuie sa-l aiba, deoarece este conditionata de existenta variabilelor (in cazul anterior lim_inf, lim_sup si s1) definite in segmentul de date.
- registrele pentru a transmite parametrii de intrare; consider mesajele de avertizare ca fiind declarate global si atunci trebuie doar sa trimit in procedura limitele intervalului si valoarea de verificat; pentru a realiza acest lucru folosesc AX pentru limitele intervalului (lim_inf in AL si lim_sup in AH) si CL pentru valoarea de verificat; se observa ca din toate datele studentului intereseaza doar varsta si atunci trimit doar aceasta data.
.model small .286 .stack 100h .data student struc nume db 10 dup (?) varsta db ? nr_matricol dw ? ends dimStudent equ size student ;dimensiune student 13 octeti s1 student <'Popescu',22,1234> mes_eroare db 'Depasire limite!','$' mes_corect db 'Date corecte!','$' lim_inf db 14 lim_sup db 89 .code Validare PROC ;procedura valideaza varsta oricarui student, aceasta ;fiind trimisa prin CL push BP mov BP,SP push AX ;salvez valoarea din AX, poate mai trebuie push CX; cmp AL,CL ;compar varsta cu limita inferioara ja eroare ;daca varsta e mai mica, afisez mesaj cmp AH,CL ;compar varsta cu limita superioara jb eroare ;daca varsta e mai mare,afisez mesaj mov AH,09h ;afisez mesaj date corecte lea DX,mes_corect int 21h jmp final eroare: mov AH,09h ;afisez mesaj eroare lea DX,mes_eroare int 21h final: pop CX pop AX mov SP,BP pop BP ret ;termin procedura ENDP start: ;indic de unde incep instructiunile programului mov AX,@data mov DS,AX mov AL,lim_inf mov AH,lim_sup xor CX,CX mov CL,s1.varsta call validare mov AX,4c00h int 21h end start
Dezavantajul metodei deriva din numarul limitat de registre si din faptul ca instructiunile salvare/restaurare a valorilor registrelor pe/de pe stiva consuma resurse (de exemplu daca valoarea parametrului de intrare ce a fost trimisa prin intermediul registrului AX este necesara in procedura dupa efectuarea altor prelucrari trebuie sa ne asiguram ca nu o pierdem salvand-o pe stiva).
- stiva pe post de zona de transfer a datelor catre proceduri; utilizarea stivei in acest sens se face in doua moduri in functie de semnificatia valorilor de pe stiva: adresa variabilei (referinta) sau valoarea ei; indiferent de modul ales, secventa se rezuma la a salva valorile respective pe stiva (prin instructiunea PUSH) inainte de a apela procedura si la a le accesa in procedura prin intermediul registrului BP; primul program exemplifica transmiterea parametrilor pe stiva prin valoare, iar al doilea transmite referinta variabilelor de intrare.
.model small .286 .stack 100h .data student struc nume db 10 dup (?) varsta db ? nr_matricol dw ? ends dimStudent equ size student ;dimensiune student 13 octeti s1 student <'Popescu',22,1234> mes_eroare db 'Depasire limite!','$' mes_corect db 'Date corecte!','$' lim_inf db 14 lim_sup db 89 .code Validare PROC ;procedura valideaza varsta oricarui student, aceasta ;fiind trimisa prin stiva push BP mov BP,SP push AX push CX ;* a se vedea figura stivei de mai jos mov AL,[BP+6] mov AH,[BP+7] mov CL,[BP+4] cmp AL,CL ;compar varsta cu limita inferioara ja eroare ;daca varsta e mai mica, afisez mesaj cmp AH,CL ;compar varsta cu limita superioara jb eroare ;daca varsta e mai mare,afisez mesaj mov AH,09h ;afisez mesaj date corecte lea DX,mes_corect int 21h jmp final eroare: mov AH,09h ;afisez mesaj eroare lea DX,mes_eroare int 21h final: pop CX pop AX mov SP,BP pop BP ret 4 ;termin procedura si golesc stiva ENDP start: ;indic de unde incep instructiunile programului mov AX,@data mov DS,AX mov AL,lim_inf ;salvez pe stiva limitele intervalului mov AH,lim_sup push AX xor CX,CX mov CL,s1.varsta ;salvez pe stiva varsta studentului push CX call validare ;apelez procedura mov AX,4c00h int 21h end start
In cazul acestui program, la momentul * stiva este descrisa de figura urmatoare:
Imaginea stivei la momentul incarcarii de pe stiva a valorilor parametrilor de intrare | |||
Pozitie in stiva relativa fata de | |||
BP |
SP |
||
BP-4→ |
SP → |
valoare din CX |
Stiva creste pe directia ↑ ! Adresele scad |
BP-2→ |
SP + 2 → |
valoare din AX |
|
BP→ |
SP + 4 → |
valoare din BP |
|
BP+2→ |
SP + 6 → |
adresa offset ( IP ) de intors |
|
BP+4→ |
SP + 8 → |
valoarea varsta student; doar primul octet. |
|
BP+6→ |
SP + 10 → |
limite interval; sunt scrise pe un cuvant in forma:[[lim_sup][lim_inf]] |
|
Atentie ! In cazul trimiterii parametrilor de tip sir de caractere este indicat sa se utilizeze metoda prin referinta deoarece punerea unui sir pe stiva este o operatie ineficienta ce consuma spatiu si cicluri masina. Din acest motiv, s-a trimis doar valoarea varstei studentului pe stiva si nu intreaga structura.
Al doilea exemplu de program assembler descrie trimiterea referintelor pe stiva.
.model small .286 .stack 100h .data student struc nume db 10 dup (?) varsta db ? nr_matricol dw ? ends dimStudent equ size student ;dimensiune student 13 octeti s1 student <'Popescu',22,1234> mes_eroare db 'Depasire limite!','$' mes_corect db 'Date corecte!','$' lim_inf db 14 lim_sup db 89 .code Validare PROC ;procedura valideaza varsta oricarui student, acesta ;este trimis prin stiva utilizand referinta sa push BP mov BP,SP push AX push CX push SI push DI ;* a se vedea figura stivei de mai jos mov SI,[BP+8] mov AL,[SI] mov SI,[BP+6] mov AH,[SI] mov SI,[BP+4] cmp AL,[SI].varsta ;compar varsta cu limita inferioara ja eroare ;daca varsta e mai mica, afisez mesaj cmp AH,[SI].varsta ;compar varsta cu limita superioara jb eroare ;daca varsta e mai mare,afisez mesaj mov AH,09h ;afisez mesaj date corecte lea DX,mes_corect int 21h jmp final eroare: mov AH,09h ;afisez mesaj eroare lea DX,mes_eroare int 21h final: pop DI pop SI pop CX pop AX mov SP,BP pop BP ret 6 ;termin procedura si golesc stiva ENDP start: ;indic de unde incep instructiunile programului mov AX,@data mov DS,AX mov AX,offset lim_inf push AX mov AX,offset lim_sup push AX mov AX, offset s1 push AX call validare mov AX,4c00h int 21h end start
Imaginea stivei la momentul incarcarii de pe stiva a valorilor parametrilor de intrare | |||
Pozitie in stiva relativa fata de | |||
BP |
SP |
||
BP-8→ |
SP → |
valoarea din DI |
|
BP-6→ |
SP + 2 → |
valoarea din SI |
|
BP-4→ |
SP + 4 → |
valoare din CX |
Stiva creste pe directia ↑ ! Adresele scad |
BP-2→ |
SP + 6 → |
valoare din AX |
|
BP→ |
SP + 8 → |
valoare din BP |
|
BP+2→ |
SP + 10 → |
adresa offset ( IP ) de intors |
|
BP+4→ |
SP + 12 → |
adresa offset student |
|
BP+6→ |
SP + 14 → |
adresa offset a lim_sup |
|
BP+8→ |
SP + 16 → |
Adresa offset a lim_inf |
|
Atentie ! In cazul trimiterii parametrilor prin referinta pe stiva orice modificare a valorii lor utilizand o adresare indirecta (bazata sau indexata) are loc in segmentul de date si valoarea se pastreaza si dupa iesirea din procedura.
Transmiterea parametrilor de iesire din proceduri
Intoarcerea rezultatelor din proceduri se realizeaza prin intermediul registrelor AX, BX, CX, DX. De exemplu, programul care aduna doua numere de tip double trimise prin valoare returneaza suma in registrele CX (cuvantul inferior) si DX (cuvantul superior).
Utilizarea procedurilor din fisiere incluse
[urmeaza]
MACRODEFINITII
[urmeaza]
[urmeaza]