Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateC
C sharpCalculatoareCorel drawDot netExcelFox pro
FrontpageHardwareHtmlInternetJavaLinux
MatlabMs dosPascalPhpPower pointRetele calculatoare
SqlTutorialsWebdesignWindowsWordXml

Structurarea programelor - Definirea si initializarea datelor

calculatoare



+ Font mai mare | - Font mai mic



Structurarea programelor - Definirea si initializarea datelor

Operatori

Capitolul curent si cel urmator sunt dedicate limbajului de asamblare. Pe langa instructiunile propriu-zise, un program ASM poate cuprinde directive, prin intermediul carora se definesc date, etichete si proceduri, se structureaza segmente, se definesc si se utilizeaza macroinstructiuni, se controleaza in general procesul de asamblare etc. Directivele nu reprezinta instructiuni, ci comenzi catre asamblor, efectul lor manifestandu-se exclusiv in faza de asamblare.



1 Segmentare. Directive pentru definirea segmentelor

Un modul de program in limbajul de asamblare poate conduce la:

. o portiune dintr-un segment;

. un segment;

. portiuni de segmente diferite;

. mai multe segmente.

Mai multe module obiect se leaga prin operatia de link-editare.

1.1 Directivele SEGMENT si ENDS

Indiferent de modul de dezvoltare al unui program ASM, atat instructiunile, cat si datele trebuie sa se gaseasca in interiorul unui segment. Directiva SEGMENT controleaza:

. numele segmentului;

. alinierea;

. combinarea cu alte segmente;

. continuitatea (adiacenta) segmentelor.

Forma generala a directivei SEGMENT este:

nume SEGMENT [tip_aliniere] [tip_combinare] ['nume_clasa']

nume ENDS

Parametrii inclusi in paranteze drepte sunt optionali. Daca sunt prezenti, acestia trebuie sa fie in ordinea specificata. Semnificatia parametrilor este:

. tip_aliniere - specifica la ce limita va fi relocat segmentul in memorie; poate avea una din formele:

. PARA (implicit) - aliniere la paragraf (segmentul fizic, adica adresa pe 20 de biti, va fi relocat la prima adresa absoluta divizibila prin 16);

. BYTE - fara aliniere (segmentul se va reloca la urmatorul octet liber);

. WORD - aliniere la cuvant (segmentul se va reloca la urmatorul octet liber aflat la adresa para);

. DWORD - aliniere la dublu-cuvant (segmentul se va reloca la prima adresa divizibila cu 4);

. PAGE - aliniere la pagina (segmentul se va reloca la prima adresa absoluta divizibila prin 256).

Sa consideram urmatoarele definitii de segmente:

DAT1 SEGMEMT BYTE

X1 db 7 dup (?)

DAT1 ENDS

DAT2 SEGMENT WORD

x21 dw 300 dup (?)

x22 dw ?

DAT2 ENDS

DAT3 SEGMENT PARA

x3 db 5 dup (?)

DAT3 ENDS

Daca prima adresa disponibila este 1000H, cele trei segmente vor fi relocate la urmatoarele adrese fizice:

DAT1 1000:0 - 1000:6

DAT2 1000:8 - 1000:261

DAT3 1027:0 - 1027:4

Adresele de segment se obtin prin trunchierea la primele patru cifre ale adresei fizice de inceput, iar

offset-urile se calculeaza corespunzator. Offset-urile la executie difera in general de offset-urile la asamblare. Offset-ul la asamblare pentru x21 este 0, dar la executie este 8.

Operatorul OFFSET, care furnizeaza deplasamentul unei variabile sau unei etichete in cadrul unui segment, va produce offset-ul de la executie. Instructiunea:

mov bx, OFFSET x21

va incarca in BX valoarea 8.

Daca dorim ca offset-ul la asamblare sa coincida cu offset-ul la executie, putem folosi tipul de aliniere PARA (ultima cifra a adresei fizice de inceput a segmentului este 0).

. tip_combinare - specifica daca si cum se va combina segmentul respectiv cu alte segmente, la operatia

de link-editare; poate fi:

. necombinabil (implicit) - nu se scrie nimic;

. PUBLIC - segmentul curent va fi concatenat cu alte segmente cu acelasi nume si cu atributul PUBLIC. Aceste segmente pot fi in alte module de program. Se va forma, deci, un singur segment final cu numele respectiv, cu o unica adresa de inceput; lungimea unui segment cu atributul PUBLIC este suma lungimilor segmentelor cu acelasi nume.

. COMMON - specifica faptul ca segmentul curent si toate segmentele cu acelasi nume si tipul COMMON se vor suprapune in memorie (incep la aceeasi adresa fizica); lungimea unui segment COMMON este cea mai mare lungime a segmentelor componente;

. STACK - marcheaza segmentul curent ca reprezentand stiva programului; daca sunt mai multe segmente cu tipul STACK, ele vor ti tratate ca la PUBLIC; urmatorul exemplu este o definire si o initializare explicita a unui segment de stiva:

stiva SEGMENT STACK

db 256 dup (?)

stiva_sp label WORD

stiva ENDS

cod SEGMENT

mov ax, stiva

mov ss, ax

mov sp, OFFSET stiva_sp

cod ENDS

. AT EXPRESIE - specifica faptul ca segmentul va fi plasat la o adresa fizica absoluta de memorie; urmatorul exemplu ilustreaza definirea explicita a tabelei de vectori de intreruperi:

INT_TABLE SEGMEMT AT 0

dd PROC_NIV_0

dd PROC_NIV_1

IMT_TABLE ENDS

Aceste forme se folosesc cand programul este dezvoltat pentru echipamente dedicate. In cazul unui calculator de tip IBM-PC, exista functii DOS pentru accesul la tabela de intreruperi.

. 'nume_clasa' - specifica un nume de clasa pentru segment, extinzand astfel numele segmentului; de exemplu, daca segmentul are si atributul 'nume_clasa', atunci atributele COMMON sau PUBLIC vor actiona numai asupra segmentelor cu acelasi nume si acelasi nume de clasa; ca nume de clase, se folosesc de obicei 'code', 'data' si 'stack'.

Sa consideram doua module ASM, asamblate distinct. In primul modul, avem urmatoarele definitii de segmente:

dseg1 SEGMENT PARA PUBLIC 'data'

db 100 dup (?)

dseg1 ENDS

dseg2 SEGMENT PARA COMMON

db 50 dup (?)

dseg2 ENDS

cseg SEGMENT PARA PUBLIC 'code'

db 20 dup (?)

cseg ENDS

In al doilea modul, exista urmatoarele definitii:

dseg1 SEGMENT PARA PUBLIC 'data'

db 30 dup (?)

dseg1 ENDS

dseg2 SEGMENT PARA COMMON

db 200 dup (?)

dseg2 ENDS

cseg SEGMENT PARA PUBLIC 'code'

db 70 dup (?)

cseg ENDS

Imaginea segmentelor rezultate in urma link-editarii si a relocarii este cea din Figura 1.

1.2 Directiva ASSUME

Directiva ASSUME realizeaza o conexiune simbolica (logica) intre definirea instructiunilor si a datelor in segmente logice (cuprinse intre SEGMENT si ENDS) la momentul asamblarii si accesul, la executie, la instructiuni si date prin registrele de segment. Are forma generala:

ASSUME reg_seg:expresie,

in care reg_seg este un registru de segment, iar expresie poate fi;

. un nume de segment;

. un nume de grup de segmente;

. SEG nume segment;

. NOTHING.

Figura 1 Combinarea segmentelor este controlata de directiva SEGMENT

In aceeasi directiva ASSUME se pot asocia mai multe registre de segment, de exemplu:

ASSUME CS:CSEG, DS:DSEG1, ES:DSEG2, SS:SSEG

Directiva ASSUME nu ne scuteste de incarcarea registrelor de segment cu adresele de segment. Daca un nume de segment nu apare intr-o directiva ASSUME, datele definite in acel segment pot fi accesate numai prin prefixe de segment. Sa consideram urmatorul exemplu:

dseg SEGMENT

BETA dw 5

dseg ENDS

cseg SEGMENT

ASSUME cs:cseg, es:dseg

mov ax, dseg

mov es, ax

mov bx, BETA ; se va genera o instructiune MOV,

; cu adresare prin registrul ES

; conform asocierii ES:dseg

cseg ENDS

Daca nu s-ar fi asociat registrul ES cu segmentul dseg prin directiva ASSUME, accesul la variabila BETA trebuia scris in forma:

mov bx, es:BETA

In formatul intern al instructiunii (in codificare), trebuie sa apara prin ce registru de segment se calculeaza adresa fizica. Daca simbolul care apare intr-o instructiune (de exemplu, BETA), face parte dintr-un segment care este asociat printr-o directiva ASSUME, cu un registru de segment, atunci se va codifica intern accesul prin acel registru de segment (in cazul de fata, prin ES). Codificarea registrului de segment se face:

. implicit - datele sunt accesate in toate modurile de adresare prin registrul DS, cu exceptia celor care implica registrul BP, caz in care se foloseste SS, iar instructiunile sunt adresate totdeauna prin CS;

. explicit - prin utilizarea prefixelor de segment.

1.3 Directiva GROUP

Directiva GROUP serveste la gruparea mai multor segmente (inclusiv cu nume si atribute diferite), a caror lungime totala nu depaseste 64 KO, sub acelasi nume. Are forma generala:

nume_grup GROUP nume_seg1, nume_seg2,

Aceasta directiva reprezinta o alta modalitate de combinare a mai multor segmente, pe langa cea oferita de atributul PUBLIC.

Numele de grup se poate folosi in directive ASSUME, la initializarea unor registre de segment sau la calculul unui offset. Sa consideram urmatorul exemplu:

d1 SEGMENT

y dw 20

d1 ENDS

d2 SEGMENT

x db 2

d2 ENDS

data GROUP d1, d2

Putem scrie instructiuni de forma:

mov ax, data

mov ds, ax

mov ax, OFFSET x

mov ax, OFFSET data:x

sau directive de forma:

ASSUME ds:data

Expresia OFFSET x furnizeaza offset-ul variabilei x in cadrul segmentului d1, iar expresia OFFSET data:x produce offset-ul variabilei x in cadrul grupului de segmente data.

2 Definirea simplificata a segmentelor

Aceasta modalitate de definire este introdusa in variantele recente ale asambloarelor. Avantajul major este faptul ca se respecta acelasi format (structura a programului obiect) ca si la programele dezvoltate in limbaj de nivel inalt. Concret, daca utilizam definirea simplificata, se vor genera segmente cu nume si atribute identice cu cele generate de compilatoarele de limbaje de nivel inalt. Toate directivele simplificate incep cu un punct.

2.1 Modele de memorie

Directiva pentru specificarea modelului de memorie are forma generala:

.model tip

in care tip poate fi tiny, small, medium, large sau huge. Semnificatia acestor tipuri este:

. tiny - toate segmentele (date, cod, stiva) se pot genera intr-un spatiu de 64KO si formeaza un singur grup de segmente. Se foloseste la programele de tip COM. Toate salturile, apelurile si definitiile de proceduri sunt implicit de tip NEAR;

. small - datele si stiva sunt grupate intr-un singur segment, iar codul in alt segment. Fiecare din acestea nu trebuie sa depaseasca 64KO. Toate salturile, apelurile si definitiile de proceduri sunt implicit de tip NEAR;

. medium - datele si stiva sunt grupate intr-un singur segment (cel mult egal cu 64KO), dar codul poate fi in mai multe segmente separate (nu se grupeaza), deci poate depasi 64KO. Salturile si apelurile sunt implicit tip FAR, iar definitiile de proceduri sunt implicit de tip far.

. compact - codul generat ocupa cel mult 64KO (se grupeaza), dar datele si stiva sunt in segmente separate (pot depasi 64KO). Apelurile si salturile sunt implicit de tip NEAR. Se utilizeaza adrese complete (segment si offset) atunci cand se acceseaza date definite in alte segmente.

. large - atat datele, cat si codul generat pot depasi 64KO.

. huge - este asemanator modelului large, dar structurile de date pot depasi 64KO; se utilizeaza adrese complete normalizate in care offset-ul este redus la minim (in domeniul 0 - 15), ceea ce face ca o adresa fizica sa fie descrisa de o unica pereche (segment, offset). La modelele compact si large, o structura compacta de date (de tip tablou) nu poate depasi limitele unui segment fizic (64KO); la modelul huge, aceasta restrictie dispare.

Folosirea directivelor simplificate prezinta si avantajul ca nu mai sunt necesare directive ASSUME: asocierile intre segmentele generate si registrele de segment sunt implicite.

Se utilizeaza urmatoarea terminologie:

. modele de date mici: small, compact;

. modele de cod mic: small, medium;

. modele de date mari: medium, large, huge;

. modele de cod mare: compact, large, huge.

2.2 Directive simplificate de definire a segmentelor

Formele generale ale acestor directive sunt:

. .stack dimensiune

. .code [nume]

. .data

. .data?  ; date neinitializate in l.n.i

. .fardata [nume]  ; segmente de date utilizate prin

. .fardata? [nume]  ; adrese complete in l.n.i

. .const  ; definire de constante in l.n.i

Daca parametrul nume lipseste, se atribuie nume implicite segmentelor generate, dupa cum urmeaza:

. pentru .code TEXT (la modele de cod mic) sau nume_fisier_sursa_TEXT (la modele de cod mare);

. pentru .data?  _BSS;

. pentru .data  DATA;

. pentru .const  CONST

. pentru .stack  STACK

. pentru .fardata _FAR_DATA

. pentru .fardata?  _FAR_BSS

Se genereaza grupul de segmente DGROUP. Acesta cuprinde:

. toate segmentele, la modelul de date tiny;

. DATA, _BSS, CONST, STACK, in rest.

Se considera ca exista o directiva ASSUME implicita (care nu mai trebuie, deci, scrisa in textul sursa), de forma:

. la modelele small si compact:

ASSUME cs:TEXT, ds:DGROUP, ss:DGROUP

. la modelele large si huge:

ASSUME cs:nume_TEXT, ds:DGROUP, ss:DGROUP

; nume este numele din directiva .code

; sau numele fisierului sursa

. la modelul de memorie tiny:

ASSUME cs:DGROUP, ds:DGROUP, cg:DGROUP

Daca se folosesc directivele .fardata si / sau .fardata?, atunci segmentele respective trebuie gestionate explicit prin directive ASSUME.

Adresele de inceput ale segmentelor si ale grupurilor de segmente definite cu directivele simplificate sunt disponibile prin simbolurile globale:

@data, @data?, @fardata, @fardata?, dgroup.

Daca in acelasi text sursa (model de cod mare) se folosesc mai multe directive .code cu nume diferite, atunci, pana la intalnirea unei alte directive de segment, este ca si cum s-ar fi scris o directiva ASSUME CS: cu numele respectiv.

Segmentele generate au numele de clasa 'STACK', 'CODE', 'DATA', 'BSS', 'FAR_BSS', 'FAR_DATA'. Segmentul CONST are numele de clasa DATA.

Tipurile de combinare sunt:

. PUBLIC (la .code, .data, .data? si .const);

. STACK (la .stack);

. fara combinare (la .fardata si .fardata?).

Tipul de aliniere este PARA (pentru .stack, .fardata si .fardata?) si WORD (pentru celelalte).

Sa consideram fisierul sursa exemplu.asm, cu continutul de mai jos:

.model large

.data

.const

.stack 256

.data?

.fardata

.fardata?

.code

end

Fisierul listing generat cuprinde (intre altele), urmatoarele informatii:

SymbolName Type Value

??DATE Text '06/02/96'

??FILENAME Text 'exemplu'

??TIME Text '15:21:50'

??VERSIOM Number 0200

@CODE  Text EXEMPLU_TEXT

@CODESIZE Text 1

@CPU Text 0101H

@CURSEG Text EXEMPLU_TEXT

@DATA Text DGROUP

@DATASIZE Text 1

@FARDATA Text FAR_DATA

@FARDATA? Text FAR_BSS

@FILENAME Text EXEMPLU

@MODEL Text 5

@WORDSIZE Text 2

Groups & Segments Bit Size Align Combine Class

DGROUP Group

CONST 16 0000 Word Public DATA

STACK 16 0100 Para Stack STACK

_BSS 16 0000 Word Public BSS

_DATA 16 0000 Word Public DATA

EXEMPLU_TEXT 16 0000 Word Public CODE

FAR_BSS 16 0000 Para none FAR_BSS

FAR_DATA 16 0000 Para none FAR_DATA

Listingul releva o serie de simboluri predefinite si valorile asociate. Se remarca adresele de inceput ale segmentelor, cel mai important fiind simbolul @DATA care are aceeasi valoare cu adresa grupului DGROUP. Partea a doua a listingului descrie segmentele si grupurile de segmente.

Initializarea registrelor DS si ES la inceputul unui program principal se poate face cu simbolul @DATA sau cu simbolul DGROUP. Macroinstructiunea init_ds_es realizeaza operatia:

mov ax, dgroup

mov ds, ax

mov es, ax

3 Directive pentru legarea modulelor

Cand programul dezvoltat se compune din mai multe module asamblate separat, este necesara specificarea simbolurilor care sunt definite intr-un modul si utilizate in alte module. Aceste simboluri sunt nume de variabile, etichete sau nume de proceduri. In mod normal, un simbol este vizibil doar in modulul in care este definit (simbol local). Simbolurile care sunt vizibile in mai multe module de program se numesc simboluri globale.

Se folosesc urmatoarele notiuni:

. simboluri publice - sunt simboluri care sunt definite intr-un modul curent de program si folosite (eventual) si in alte module; aceste simboluri se declara ca simboluri publice in modulul in care sunt definite;

. simboluri externe - sunt simboluri care sunt folosite intr-unul din modulele in care nu sunt definite; aceste simboluri se declara ca simboluri externe in modulele in care sunt folosite.

Se observa ca un simbol global trebuie declarat ca simbol public in modulul in care este definit si ca simbol extern in modulele in care este folosit, altele decat cel in care este definit.

Declaratia unui simbol ca simbol public, respectiv extern se face cu directivele PUBLIC, respectiv EXTRN.

Directiva PUBLIC are forma generala:

PUBLIC nume, nume,

Simbolurile declarate ca publice pot fi variabile, etichete, nume de proceduri sau constante numerice simbolice.

Directiva EXTRN are forma generala:

EXTRN nume:tip, nume:tip,

in care tip precizeaza tipul simbolului. Acesta poate ti:

. BYTE, WORD, DWORD sau QWORD, in cazul in care simbolul este o variabila;

. NEAR sau FAR, in cazul in care simbolul este o eticheta sau un nume de procedura;

. ABS, in cazul in care simbolul este o constanta numerica simbolica. Sa consideram doua module de program, M1.ASM si M2.ASM:

; Modul M1.ASM

.model large

extrn var1:word, output:far

public var2

.data

var2 dw 7

.code

start:

mov ax, dgroup

mov ds, ax

add var1, 3

call output

end start

; Modul M2.ASM

.model large

public varl, output

extrn var2:word

.data

var 1 dw5

.code

output proc far

add var2, 1

retf

output endp

end

in acest exemplu, accesul la simbolurile globale este facilitat de faptul ca atat var1, cat si var2 sunt in acelasi segment de date, fiind accesibile prin registrul DS, initializat cu grupul de segmente DGROUP.

Daca nu am fi folosit directive simplificate, accesul la var1 si var2 ar fi fost mai complicat, deoarece nu s-ar fi cunoscut segmentul in care acestea sunt definite. In asemenea situatii, se foloseste operatorul SEG, care produce adresa de segment a simbolului:

mov ax, SEG var1

mov es, ax

add es:var1, 3

Directiva END

Aceasta directiva marcheaza sfarsitul logic al unui modul de program si e obligatorie in toate modulele. Tot ce se gaseste in fisierul sursa dupa aceasta directiva este ignorat la asamblare. Forma generala este:

END [punct_de_start]

in care punct_de_start este o eticheta sau un nume de procedura care marcheaza punctul in care se va da controlul dupa incarcarea programului in memorie. Intr-o aplicatie compusa din mai multe module si care se constituie intr-un program executabil, un singur modul trebuie sa aiba parametru in directiva END.

4 Contoare de locatii si directiva ORG

Contoarele de locatii controleaza procesul de asamblare, aratand la ce offset in cadrul segmentului curent se vor asambla instructiunea sau datele urmatoare. Un contor de locatii poate fi accesat explicit prin simbolul $.

La prima utilizare a unui nume de segment, contorul de locatii este initializat cu zero. Daca se revine intr-un segment care a mai fost utilizat, contorul de locatii revine la ultima valoare utilizata in cadrul acelui segment, ca in exemplul urmator:

.data ; $ = 0

db 5 dup(?)

.code

.data

db 7 ; $ = 5

Contoarele de locatii sunt utile la calculul automat al unor deplasamente sau dimensiuni. In exemplul urmator, se defineste un tablou de cuvinte TAB si o variabila LNG care contine numarul de cuvinte din tablou:

TAB dw 1, 2, 3, 4, 5, 6, 7, 8, 9

LNG dw ($ - TAB)/2

Expresia $ - TAB reprezinta numarul de octeti de la adresa TAB pana la adresa curenta. Impartind acest numar la 2, se deduce numarul de elemente din tablou. Putem acum adauga sau elimina date din tabloul TAB fara a actualiza lungimea LNG: asamblorul va calcula corect numarul de elemente din tablou.

Alte exemple de utilizare pot fi:

JMP $ ; Ciclu infinit: $ este chiar adresa

; instructiunii JMP

JMP $ + 5 ; salt relativ

Directiva ORG (Origin - Initializeaza contorul de locatii)

Aceasta directiva modifica explicit contorul de locatii curent, avand forma generala:

ORG expresie

De exemplu:

ORG $ + 5 ; Sare 5 octeti la asamblare

ORG 100H ; Asambleaza la offset-ul absolut 100H

Definirea si initializarea datelor

Asamblorul recunoaste trei categorii sintactice de baza:

. constante;

. variabile;

. etichete (inclusiv nume de proceduri).

Constantele pot fi absolute (numerice) sau simbolice. Constantele simbolice reprezinta nume generice asociate unor valori numerice. Variabilele identifica datele (un spatiu de memorie rezervat), iar etichetele identifica codul (program sau procedura).

Instructiuni de tipul MOV, ADD, AND etc. folosesc variabile si constante, iar instructiunile JMP si CALL folosesc etichete.

Variabilele si etichetele au asociate atribute, cum ar fi segmentul in care sunt definite, offset-ul la care sunt definite in interiorul segmentului etc.

1 Constante

Constantele numerice absolute pot ti:

. constante binare - se utilizeaza sufixul B sau b;

. constante octale - se utilizeaza sufixele O, Q, o sau q;

. constante zecimale - fara sufix sau cu sufixele D sau d;

. constante hexazecimale - se utilizeaza sufixele H sau h si prefixul 0 daca prima cifra este mai mare ca 9; pentru cifrele peste 9 se utilizeaza simbolurile AF sau af.

. constante ASCII - se scriu unul sau mai multe caractere ASCII intre semnul apostrof sau ghilimele;

lata exemple de constante absolute: 123, 123D, 10001100B, 177q, 0AAH, 3fh, 'a', 'AB'.

Constantele simbolice se definesc cu directiva EQU, in forma generala:

nume EQU expresie

De exemplu, liniile de program:

CR EQU 0DH

LF EQU 0AH

definesc constantele simbolice CR si LF, cu valorile 0DH si 0AH. Putem folosi aceste constante simbolice in orice context in care este permisa folosirea unei constante numerice.

Definirea si utilizarea variabilelor

Pentru definirea variabilelor, se utilizeaza directivele DB, DW, DD, DQ sau DT, care au fost introduse in Capitolul 1. Forma generala a unei definitii de date este:

nume directiva lista_de_valori

in care nume este identificatorul asociat definitiei respective, iar lista_de_valori este lista valorilor initiale, care poate cuprinde:

. constante numerice (absolute sau simbolice);

. simbolul ?

. o adresa, adica un nume de variabila sau de eticheta; se foloseste la DW si DD;

. un sirASCII;

. operatorul DUP (expresie), in care expresie poate fi:

. constanta numerica;

. lista de valori;

. simbolul ?

. operatorul DUP.

Semnificatia simbolului ? este locatie neinitializata, iar cea a operatorului DUP, repetarea de un numar de ori a expresiei din paranteza.

Sa consideram definitiile urmatoare:

var a db 2 dup (0, 3 dup (1))

var_ b db 1, 2, 3, ?, ?, ?

adr_ n dw var_a

adr_ f dd var_b

prima definitie este echivalenta cu:

var_a db 0, 1, 1, 1, 0, 1, 1, 1

Atributele datelor detinite sunt:

. segment - cel curent;

. offset - cel curent;

. tip - 1, 2, 4, 8, 10, dupa cum s-a utilizat ca directiva de baza DB, DW, DD, DQ sau DT.

Variabilele pot fi utilizate ca:

. variabile simple - var_a, var_b;

. variabile indexate - var_a [bx], var_b [2], ALFA [si][bx].

In cazul variabilelor indexate, trebuie tinut seama de tipul variabilei de baza. De exemplu, in urma definitiei tabloului:

B dw 10 dup(?)

expresiile indexate corecte din punct de vedere logic, care acceseaza elementele lui B, sunt B[0], B[2] etc. Din punct de vedere sintactic, putem scrie si:

mov ax, B[1]

dar aceasta forma va incarca in AL partea high a primului cuvant din tablou si in AH partea low a celui de-al doilea cuvant din tabloul B. Daca acest lucru s-a urmarit, perfect!

In cazul accesarii variabilelor prin expresii anonime, poate aparea necesitatea operatorului PTR. Instructiunile:

lea bx, B

inc byte ptr [bx][4]

inc word ptr [bx][4]

incrementeaza octetul, respectiv cuvantul de la adresa BX +

6 Definirea etichetelor. Directiva LABEL

Etichetele sunt folosite pentru specificarea punctelor tinta la instructiuni de salt sau apel sau pentru o specificare alternativa a datelor. In ambele cazuri, numele etichetei devine un nume simbolic asociat adresei curente de memorie. Atributele etichetelor sunt: segment, offset si tip. Pana acum am intalnit doua modalitati de definire a etichetelor:

. prin nume urmat de caracterul: - se defineste o eticheta de tip near;

. prin directiva PROC - numele procedurii este interpretat ca o eticheta cu tipul derivat din tipul procedurii;

O alta posibilitate este directiva LABEL, care are forma generala:

nume LABEL tip

Daca ceea ce urmeaza reprezinta instructiuni (cod), tipul etichetei va fi, de regula, NEAR sau FAR si eticheta va fi folosita ca punct tinta in instructiuni de timp JMP / CALL. Daca ceea ce urmeaza reprezinta definitii de date, tipul etichetei va fi, de regula, BYTE, WORD, DWORD etc.

Atributele NEAR sau FAR pot aparea numai daca eticheta este definita intr-un segment asociat cu registrul CS printr-o directiva ASSUME (explicita sau implicita).

Directiva LABEL este utila atunci cand dorim sa accesam variabile in moduri diferite. De exemplu, in urma definitiei:

ALFAB label BYTE

ALFAW dw 1234H

o instructiune de forma inc ALFAB va incrementa octetul mai putin semnificativ al cuvantului, iar una de tip inc ALFAW va incrementa intreg cuvantul; un efect similar se poate obtine prin operatorul PTR.

Similar, definitia:

adr_proc label dword

proc_off dw ?

proc_seg dw ?

permite definirea explicita a segmentului si a offset-ului, dar si accesul la nivel de dublu-cuvant, de exemplu intr-o instructiune de apel indirect inter-segment

7 Definirea structurilor. Operatii specifice

Structurile reprezinta colectii de date (campuri sau membri) plasate succesiv in memorie, grupate sub un unic nume sintactic. Dimensiunea unei structuri este suma dimensiunilor campurilor componente. Forma generala a definitiei unei structuri este:

nume_structura STRUC

nume_membru definitie_date

nume structura ENDS

Prin aceasta se defineste tipul structurii (sablonul) fara a se rezerva spatiu de memorie. Definitiile de date pot cuprinde directive DB, DW etc. cu valori initiale asociate, ?, DUP etc., exact ca orice definitie de date. Numele membrilor structurii trebuie sa fie distincte, chiar daca apartin unor structuri distincte. lata un exemplu de definitie de tip de structura:

ALFA STRUC

a db ?

b dw 1111H

c dd 1234

d db ?

ALFA ENDS

O structura definita este interpretata ca un tip nou de date, care poate apoi participa la definitii concrete de date, cu rezervare de spatiu de memorie. De exemplu, putem defini o structura de tipul ALFA, cu numele x:

x ALFA <, , 777, 5>

in care ALFA este tipul de date, x este numele variabilei, iar intre paranteze unghiulare se trec valorile initiale ale campurilor structurii x. Prezenta virgulelor din lista de valori initiale precizeaza ca primii doi membri sunt initializati cu valorile implicite de la definitia tipului ALFA, al treilea membru este initializat cu valoarea 777, iar al patrulea cu valoarea 5. Astfel, definitia variabilei x este echivalenta cu secventa de definitii:

a db ?

b dw 1111H

c dd 777

d db 5

Principalul avantaj al structurilor este accesul la membri intr-o forma asemanatoare limbajelor de nivel inalt. De exemplu, pentru a incarca in SI campul b al structurii x, putem scrie:

mov si, x.b

Accesul la membrii structurii x poate fi deci detaliat in:

. x.a - variabila de tip byte

. x.b - variabila de tip word

. x.c - variabila de tip dword

. x.d - variabila de tip byte

Putem acum defini un tablou de 5 structuri de tip ALFA:

y ALFA 5 dup <, , , 10>

in care primii trei membri sunt initializati cu valorile de la definitia tipului structurii, iar al patrulea cu valoarea 10. Au acum sens expresii de forma: y[0].a, y[SI], y[8].c, y[BX].d etc. De fapt, toate aceste expresii se evalueaza din adresa de inceput a variabilei y la care se adauga un deplasament corespunzator. Expresia y[11].d este echivalenta cu y + 11 + deplasamentul campului d fata in cadrul tipului ALFA.

Se observa ca gestiunea deplasamentelor cade in sarcina utilizatorului. Daca dorim sa accesam campul c al celei de-a doua structuri din tabloul y, nu putem scrie y[1].c, asa cum am scrie intr-un limbaj de nivel inalt, ci y[8].c. Limbajul de asamblare ne pune la dispozitie operatorul TYPE, care intoarce numarul de octeti ocupat de o structura. Pentru a generaliza accesul la al i-lea element al tabloului y (i cuprins intre 0 si 4), putem scrie y[i*TYPE ALFA].c.

Problema se complica daca indicele i este cunoscut de-abia la momentul executiei (fiind memorat intr-un registru, de exemplu). O expresie de forma:

mov a1, y [SI*TYPE ALFA].d

are sens numai la procesoarele care accepta adresare cu factor de scala (80386 si peste) si numai daca factorul de scala este 1, 2, 4, sau 8. In caz contrar, deplasamentul trebuie calculat explicit prin operatii aritmetice.

Cu toate aceste limitari, structurile sunt intens utilizate atunci cand se lucreaza cu anumite sabloane fixe, cum ar fi, de exemplu, imaginea stivei la intrarea intr-o procedura sau transmiterea prin referinta a unei zone de date. Sa consideram o inregistrare logica descrisa prin campuri:

. campul NUME - sir ASCII de 30 de caractere

. campul PRENUME - sir ASCII de 20 de caractere

. campul COD - intreg pe 4 octeti

Dorim sa transmitem unei proceduri far cu numele PRELUCRARE o asemenea inregistrare. Cel mai simplu mod este de a transmite adresa (near sau far) a zonei de memorie in care este stocata inregistrarea, de exemplu printr-un registru sau o pereche de registre; pentru accesul comod la campuri definim structura:

PERSOANA STRUC

NUME db 30 dup (?)

PRENUME db 20 dup (?)

COD dd ?

PERSOANA ENDS

Definitia concreta a datelor si secventa de apel a procedurii este:

.data

om PERSOANA <'Ionescu', 'Ion', 77206>

adr_om dd om ; Pointer la om

.code

les di, adr_om

call PRELUCRARE

Se pune acum problema accesului la campurile inregistrarii, din interiorul procedurii. Beneficiem acum de structura PERSOANA. Ca sa incarcam adresa campului PRENUME vom scrie:

lea bx, es:[di].PRENUME

Perechea ES:DI contine adresa structurii om, iar expresia es:[di].PRENUME reprezinta deplasamentul campului PRENUME. In acest moment, in ES:BX avem adresa sirului de caractere de tip PRENUME. Similar se procedeaza si pentru celelalte campuri: ca sa preluam in DX:AX campul COD, putem scrie:

mov ax, word ptr es:[di].COD

mov dx, word ptr es:[di].COD + 2

Este evident ca toate secventele de mai sus se puteau inlocui cu deplasamente calculate explicit de catre programator. Incarcarea campului COD ar fi putut fi scrisa:

mov ax, word ptr es:[di + 50]

mov dx, word ptr es:[di + 52]

dar forma cu nume de campuri este mult mai clara si mai comoda. In plus, noi putem gresi la calculul unui deplasament, dar asamblorul nu.

Aceasta tehnica este benefica si din punctul de vedere al intretinerii unui program. Daca se modifica ulterior inregistrarea PERSOANA si se adauga inca un camp intre PRENUME si COD, toate deplasamentele calculate explicit trebuie modificate. In varianta cu structura, trebuie doar sa schimbam definitia structurii, restul fiind facut automat la asamblare.

8 Definirea inregistrarilor. Operatii specifice

Inregistrarile (RECORD) corespund de fapt unor structuri impachetate din limbajele de nivel inalt. Concret, o inregistrare este o definitie de campuri de biti de lungime maxima 8 sau 16. Din punct de vedere sintactic, definitia unei inregistrari este similara cu cea a unei structuri, forma generala fiind:

nume_inregistrare RECORD nume_camp:valoare,

sau, cu initializare implicita:

nume RECORD nume_camp:valoare = expresie,

Valorile care apar asociate cu numele de campuri dau de fapt numarul de biti pe care se memoreaza campul respectiv. In varianta cu initializare, expresia care apare dupa semnul egal se evalueaza la o cantitate care se reprezinta pe numarul de biti asociat campului respectiv. La fel ca la structuri, numele campurilor trebuie sa fie distincte, chiar daca apartin unor inregistrari diferite.

Sa consideram un exemplu:

BETA record X:7, Y:4, Z:5

Prin care se defineste un sablon de 16 biti, grupati in campurile X, Y si Z (vezi Figura 2)

Figura 2 Definitia unei inregistrari

La fel ca la structuri, putem acum defini variabile de tipul inregistrarii BETA:

VAL BETA <5, 2, 7>

in care valorile dintre parantezele unghiulare initializeaza cele trei campuri de biti. Se observa ca aceasta definitie este echivalenta cu definitia explicita:

VAL dw 0000101001000111B

Exista doi operatori specifici inregistrarilor: MASK si WIDTH. Operatorul MASK primeste un nume de camp si furnizeaza o masca cu bitii 1 pe pozitia campului respectiv si 0 in rest. Astfel, expresiile MASK X si MASK Y sunt echivalente cu constantele binare 1111111000000000B si 0000000111100000B.

Asemenea expresii pot fi utilizate pentru selectarea campurilor respective, ca in exemplele de mai jos:

AND AX, MASK Y ; Filtreaza campul Y

AND BX, NOT MASK Z ; Forteaza campul Z la 0

Operatorul WIDTH primeste un nume de inregistrare sau un nume de camp dintr-o inregistrare, intorcand numarul de biti ai inregistrarii sau ai campului respectiv. De exemplu, secventa:

MOV AL, WIDTH BETA ; AL <--- 16

MOV BL, WIDTH Y ; BL <--- 4

va incarca in AL valoarea 16 si in BL valoarea

Daca se utilizeaza un nume de camp intr-o instructiune de tip MOV, se va incarca un contor de deplasare, util pentru a deplasa campul respectiv pe pozitiile cele mai putin semnificative.

Sa presupunem ca dorim sa testam valoarea numerica a campului Y din inregistrarea VAL. Nu este suficient sa-l izolam si sa folosim o instructiune de comparatie. Inainte de comparatie, campul Y trebuie adus pe pozitiile cele mai din dreapta. Realizam aceste operatii prin secventa:

MOV AX, VAL  ; Preluare inregistrare

AND AX, MASK Y ; Selectare camp Y

MOV CL, WIDTH Y ; Incarcare in CL a valorii 4

SHR AX, CL ; Deplasare camp Y la dreapta

9 Operatori in limbajul de asamblare

Limbajul de asamblare dispune de un set de operatori cu care se pot forma expresii aritmetice si logice. Aceste expresii sunt evaluate la momentul asamblarii, producand, de fapt, constante numerice. Este esential sa deosebim instructiunile executabile (codul masina) de operatiile care se fac la momentul asamblarii.

9.1 Operatori aritmetici si logici

Operatorii aritmetici sunt: +, -, *, /, MOD, SHL si SHR. Primii patru au semnificatii evidente si opereaza numai cu cantitati intregi. Operatorul MOD produce restul la impartire, iar SHL si SHR provoaca deplasare la stanga sau la dreapta.

De exemplu, instructiunea:

mov ax, 1 SHL 3 ; 1 deplasat la stanga cu 3 biti

este echivalenta cu:

mov ax, 100B

Similar, in directiva:

COUNT dw ($ - TAB)/2

se va evalua la momentul asamblarii expresia ($- TAB)/2, adica diferenta dintre contorul curent de locatii si adresa TAB, impartita la 2.

Operatorii logici sunt NOT, AND, OR si XOR, cu semnificatii evidente. Toti acesti operatori accepta drept operanzi constante intregi; operatiile respective se fac la nivel de bit. Ei nu trebuie confundati cu instructiunile executabile cu acelasi nume. In instructiunea:

and al, (1 SHL 3 ) OR (1 SHL 5)

registrul AL va fi incarcat cu constanta 101000B.

9.2 Operatori relationali

Operatorii relationali sunt EQ, NE, LT, LE, GT, GE, cu semnificatii evidente. Acestia intorc valori logice, codificate ca 0 sau ca secvente de 1 pe un numar corespunzator de biti. in urma definitiilor:

x db 1 EQ 2

y dw 1 NE 2

z dd 1 LT 2

variabilele x, y si z vor fi initializate cu constantele 0, 0FFFFH, respectiv 0FFFFFFFFH.

Operatorii relationali sunt utilizati in special in directivele de asamblare conditionata.

9.3 Operatorul de atribuire =

Operatorul de atribuire = defineste constante simbolice, fiind similar cu directiva EQU, dar permite redefinirea simbolurilor utilizate. O definire de forma:

N EQU 1

N EQU 2

este semnalata ca eroare, dar secventa:

N = 1

N = 2

este corecta.

Acest operator este utilizat in special in definitiile de macroinstructiuni.

9.4 Operatori care intorc valori

Acesti operatori se aplica unor entitati ale programului in limbaj de asamblare (variabile, etichete), intorcand valori asociate acestora.

. Operatorul SEG

Se aplica atat variabilelor, cat si etichetelor si furnizeaza adresa de segment asociata variabilei respective. Daca var este o variabila, atunci putem scrie secventa:

mov ax, SEG var

mov ds, ax

. Operatorul OFFSET

Este similar cu SEG, furnizand offset-ul asociat variabilei sau etichetei:

mov bx, OFFSET var

. Operatorul THIS

Acest operator creeaza un operand care are asociate o adresa de segment si un offset identice cu contorul curent de locatii. Forma generala a operandului este:

THIS tip

in care tip poate fi BYTE, WORD, DWORD, QWORD sau TBYTE pentru definitii de date, respectiv NEAR sau FAR pentru etichete. Operatorul THIS se utilizeaza de obicei cu directiva EQU. De exemplu, definitia constantei simbolice:

ALFA EQU THIS WORD

este echivalenta cu definitia unei etichete:

ALFA LABEL WORD

. Operatorul TYPE

Se aplica variabilelor si etichetelor, intorcand tipul acestora. Pentru variabile intoarce valorile 1, 2, 4, 8 sau 10 pentru variabile simple (definite cu directivele DB, DW, DD, DQ respectiv DT), iar pentru structuri intoarce numarul de octeti pe care este memorata structura respectiva. Pentru etichete, intoarce tipul etichetei (NEAR sau FAR).

. Operatorul LENGTH

Se aplica numai variabilelor si intoarce numarul de elemente definite in variabila respectiva. De exemplu, in definitia:

A DW 100 dup (?)

expresia LENGTH A are valoarea 100.

. Operatorul SIZE

Se aplica numai variabilelor si intoarce dimensiunea in octeti a variabilei respective. In definitia de mai sus, expresia SIZE A are valoarea 200.

Pentru o variabila oarecare var, are loc totdeauna identitatea:

SIZE var = (LENGTH var) * (TYPE var)

Acesti operatori sunt utili la prelucrarea tablourilor. Secventa urmatoare nu depinde de tipul de baza al tabloului TAB si nici de dimensiunea sa:

.data

TAB dd 100 dup (?)

.code

mov cx, LENGTH TAB

lea si, TAB

bucla:

add si, TYPE TAB

loop bucla

Daca inlocuim acum tipul de baza al tabloului TAB cu o structura de tip PERSOANA (definita in 7), modificand si dimensiunea:

PERSOANA STRUC

. . . . .

PERSOANA ENDS

.data

TAB PERSOAMA 50 dup <,,,>

partea de program nu trebuie modificata, deoarece asamblorul va evalua corect expresiile LENGTH TAB si TYPE TAB.

9.5 Operatorul PTR

Acest operator se aplica atat variabilelor, cat si etichetelor, avand ca efect schimbarea tipului variabilei sau al etichetei respective. Este obligatoriu in cazul in care se folosesc referinte anonime la memorie, din care nu se poate deduce tipul operandului:

add word ptr [bx], 2

call dword ptr [bx]

call near ptr proc

10 Directive de asamblare conditionata

Permit ignorarea unor portiuni din textul sursa, functie de o conditie care se poate evalua la asamblare. Directiva de baza este IF, cu forma generala:

IF expresia

[ELSE]

ENDIF

Daca expresia din directiva IF este adevarata (adica este diferita de 0), se asambleaza portiunea de program dintre IF si ELSE si se ignora portiunea dintre ELSE si ENDIF. Daca expresia din IF este falsa, se asambleaza portiunea de program dintre ELSE si ENDIF si se ignora portiunea dintre IF si ELSE. Ramura ELSE este optionala.

Aceste operatii se petrec la momentul asamblarii, permitand intretinerea comoda a unui program de dimensiuni mari sau chiar mentinerea in acelasi text sursa a mai multor variante de program executabil. Sa presupunem ca dorim un program care sa poata fi configurat rapid pentru modele de date "mici', respectiv "mari'. Aceasta presupune ca orice secventa de instructiuni care defineste, citeste sau scrie o adresa sa fie inclusa in directive de asamblare conditionata:

FALSE  equ 0

TRUE equ NOT FALSE

DATE_MARI equ TRUE

DATE_MICI equ NOT DATE_MARI

.data

X dw 100 dup (?)

IF DATE_MARI

ADR_X dd X

ELSE

ADR_X dw X

ENDIF

.code

IF DATE_MARI

lds si, ADR_X

ELSE

mov si, ADR_X

ENDIF

; Prelucrare TABLOU

Exista si directivele IFDEF / ENDIF si IFNDEF / ENDIF, care testeaza daca un simbol este definit sau nu, si directivele IFB / ENDIF (If Blank), respectiv IFNB / ENDIF (If Not Blank), care testeaza daca un simbol este vid sau nevid. Acestea din urma vor fi utilizate la definirea macroinstructiunilor.

In expresiile care apar in directivele de tip IF se utilizeaza de obicei operatori logici si relationali.

Sa consideram un exemplu de program sursa multifunctional. Se doreste scrierea unei secvente care sa calculeze in acumulator suma elementelor unui tablou A (de octeti sau de cuvinte), definit in segmentul curent de date, ca suma pe octeti si pe cuvinte, indiferent de tipul si de dimensiunea tabloului. Tipul sumei este dictat de constantele simbolice SUM_B si SUM_W.

FALSE equ 0

TRUE equ NOT FALSE

SUM_B equ TRUE

SUM_W egu NOT SUM_B

.code

if SUM_B

mov cx, SIZE A ; Numar de iteratii

else

mov cx, (SIZE A)/2

endif

xor bx, bx ; Indice initial

mov ax, bx ; Suma initiala

bucla:

if SUM_B

add al, byte ptr A [bx] ; Suma pe octet

inc bx ; Actualizare adresa

else

add ax, word ptr A [bx] ; Suma pe cuvant

inc bx ; Actualizare adresa

inc bx

endif

loop bucla ; Bucla

if SUM_W AND (TYPE A EQ 1) AND ((SIZE A) MOD 2) EQ 1

push ax

mov al, byte ptr A [bx] ; Un eventual

cbw  ; ultim

mov dx, ax ; octet

pop ax

add ax, dx

endif

Se observa ca nu putem initializa contorul CX cu expresia LENGTH A, deoarece numarul de iteratii este dictat de modul de calcul al sumei, si nu de tipul tabloului. Ca atare, luam dimensiunea in octeti a tabloului, pe care o impartim la 2 in cazul sumei pe cuvant. Calculul sumei si actualizarea adresei sunt evidente.

Apare o problema speciala in urmatoarea situatie: se cere suma pe cuvant, iar tabloul, definit ca tablou de octeti, are un numar impar de elemente. In aceasta situatie, este evident ca ultimul octet nu este considerat in bucla de sumare. Ca atare, el este adaugat explicit la sfarsit.

De remarcat formularea situatiei de mai sus in termenii conditiei logice de la ultima directiva IF:

. SUM_W codifica cererea de calcul a sumei la nivel de cuvant;

. (TYPE A EQ 1) codifica situatia in care tabloul A este definit la nivel de octet;

. ((SIZE A) MOD 2) codifica situatia in care tabloul are un numar impar de octeti.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1584
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved